Repository: opencv/opencv
File: modules/dnn/src/layers/gridsample_layer.cpp (C++)

Code before:

        Mat Xf = X;
        if (X.depth() != CV_32F) X.convertTo(Xf, CV_32F);
        Mat Gf = G;
        if (G.depth() != CV_32F) G.convertTo(Gf, CV_32F);

        Mat Ytmp({N, C, Hout, Wout}, CV_32F);
        Ytmp.setTo(Scalar(0));

        auto Xptr = (const float*)Xf.data;
        auto Gptr = (const float*)Gf.data;
        auto Yptr = (float*)Ytmp.data;

        size_t xNStride = (size_t)C*H*W;
        size_t xCStride = (size_t)H*W;
        size_t xHStride = (size_t)W;

        size_t gNStride = (size_t)Hout*Wout*2;
        size_t gHStride = (size_t)Wout*2;
        size_t gWStride = 2;

        size_t yNStride = (size_t)C*Hout*Wout;
        size_t yCStride = (size_t)Hout*Wout;
        size_t yHStride = (size_t)Wout;

        auto sampleAt = [&](int n, int c, float x, float y)->float {
            auto fetch = [&](int yy, int xx)->float {
                int px = xx, py = yy;
                if (padding == P_BORDER) {
                    px = int(std::min(float(W - 1), std::max(0.f, float(px))));
                    py = int(std::min(float(H - 1), std::max(0.f, float(py))));
                } else if (padding == P_REFLECTION) {
                    px = int(std::floor(reflectCoord((float)px, W, align_corners) + 0.5f));
                    py = int(std::floor(reflectCoord((float)py, H, align_corners) + 0.5f));
                }
                if (px < 0 || py < 0 || px >= W || py >= H) return 0.f;
                const float* base = Xptr + n*xNStride + c*xCStride + (size_t)py*xHStride + (size_t)px;
                return *base;
            };

            if (mode == M_NEAREST) {
                int px = int(std::floor(x + 0.5f));
                int py = int(std::floor(y + 0.5f));
                return fetch(py, px);
            } else if (mode == M_BILINEAR) {
                int x0 = (int)floorf(x), y0 = (int)floorf(y);
                float dx = x - x0, dy = y - y0;
                float v00 = fetch(y0,   x0);
                float v01 = fetch(y0,   x0+1);
                float v10 = fetch(y0+1, x0);
                float v11 = fetch(y0+1, x0+1);

Review comment (performance):
this code with nested lambda functions is not going to be fast. Please, rewrite this code without lambda functions that are called inside the innermost loop.

Don't use `fetch()` blindly, 'cause it's very slow. Check that the whole 2x2 fetched neighborhood of the pixel is inside the image, in this case simply convert top-left corner to int's (without using `floor`, because of a non-negative number x we always have (int)std::floor(x) == (int)x).

Code after:
                    int y0 = saturate_cast<int>(floorf(yf));
                    float dx = xf - x0, dy = yf - y0;

                    if (x0 >= 0 && y0 >= 0 && x0 + 1 < W && y0 + 1 < H)
                    {
                        const T* p_y0 = baseNC + (size_t)y0 * xHStride;
                        const T* p_y1 = baseNC + (size_t)(y0 + 1) * xHStride;
                        float v00 = (float)p_y0[x0];
                        float v01 = (float)p_y0[x0 + 1];
                        float v10 = (float)p_y1[x0];
                        float v11 = (float)p_y1[x0 + 1];
                        float vx0 = v00 * (1.f - dx) + v01 * dx;
                        float vx1 = v10 * (1.f - dx) + v11 * dx;
                        outv = vx0 * (1.f - dy) + vx1 * dy;
                    }
                    else
                    {
                        float v00 = fetch(baseNC, y0,     x0);
                        float v01 = fetch(baseNC, y0,     x0 + 1);
                        float v10 = fetch(baseNC, y0 + 1, x0);
                        float v11 = fetch(baseNC, y0 + 1, x0 + 1);
                        float vx0 = v00 * (1.f - dx) + v01 * dx;
                        float vx1 = v10 * (1.f - dx) + v11 * dx;
                        outv = vx0 * (1.f - dy) + vx1 * dy;
                    }
                } else {
                    const float alpha = -0.75f;
                    int x1 = saturate_cast<int>(floorf(xf));
                    int y1 = saturate_cast<int>(floorf(yf));
                    float tx = xf - x1, ty = yf - y1;

                    float wx[4], wy[4];
                    getCubicCoeffs(tx, alpha, wx);
                    getCubicCoeffs(ty, alpha, wy);

                    if (x1 >= 1 && y1 >= 1 && x1 + 2 < W && y1 + 2 < H) {
                        const T* p = baseNC + (size_t)(y1 - 1) * xHStride + (x1 - 1);
                        float rowv0 = (float)p[0] * wx[0] + (float)p[1] * wx[1] + (float)p[2] * wx[2] + (float)p[3] * wx[3];
                        p += xHStride;
                        float rowv1 = (float)p[0] * wx[0] + (float)p[1] * wx[1] + (float)p[2] * wx[2] + (float)p[3] * wx[3];
                        p += xHStride;
                        float rowv2 = (float)p[0] * wx[0] + (float)p[1] * wx[1] + (float)p[2] * wx[2] + (float)p[3] * wx[3];
                        p += xHStride;
                        float rowv3 = (float)p[0] * wx[0] + (float)p[1] * wx[1] + (float)p[2] * wx[2] + (float)p[3] * wx[3];
                        outv = rowv0 * wy[0] + rowv1 * wy[1] + rowv2 * wy[2] + rowv3 * wy[3];
                    } else {
                        float rowv0 = fetch(baseNC, y1 - 1, x1 - 1) * wx[0] + fetch(baseNC, y1 - 1, x1    ) * wx[1] +
                                      fetch(baseNC, y1 - 1, x1 + 1) * wx[2] + fetch(baseNC, y1 - 1, x1 + 2) * wx[3];
                        float rowv1 = fetch(baseNC, y1,     x1 - 1) * wx[0] + fetch(baseNC, y1,     x1    ) * wx[1] +
                                      fetch(baseNC, y1,     x1 + 1) * wx[2] + fetch(baseNC, y1,     x1 + 2) * wx[3];
                        float rowv2 = fetch(baseNC, y1 + 1, x1 - 1) * wx[0] + fetch(baseNC, y1 + 1, x1    ) * wx[1] +