Why is the operation `S0 >> 4` performed when the `resize` function takes `u8` input and a scalar for the V-direction? Since `int32` would not overflow in the scalar case, why is this operation still necessary?

template<>
struct VResizeLinear<uchar, int, short, FixedPtCast<int, uchar, INTER_RESIZE_COEF_BITS*2>, VResizeLinearVec_32s8u>
{
typedef uchar value_type;
typedef int buf_type;
typedef short alpha_type;

void operator()(const buf_type** src, value_type* dst, const alpha_type* beta, int width ) const
{
    alpha_type b0 = beta[0], b1 = beta[1];
    const buf_type *S0 = src[0], *S1 = src[1];
    VResizeLinearVec_32s8u vecOp;

    int x = vecOp(src, dst, beta, width);
    #if CV_ENABLE_UNROLLED
    for( ; x <= width - 4; x += 4 )
    {
        dst[x+0] = uchar(( ((b0 * (S0[x+0] >> 4)) >> 16) + ((b1 * (S1[x+0] >> 4)) >> 16) + 2)>>2);
        dst[x+1] = uchar(( ((b0 * (S0[x+1] >> 4)) >> 16) + ((b1 * (S1[x+1] >> 4)) >> 16) + 2)>>2);
        dst[x+2] = uchar(( ((b0 * (S0[x+2] >> 4)) >> 16) + ((b1 * (S1[x+2] >> 4)) >> 16) + 2)>>2);
        dst[x+3] = uchar(( ((b0 * (S0[x+3] >> 4)) >> 16) + ((b1 * (S1[x+3] >> 4)) >> 16) + 2)>>2);
    }
    #endif
    for( ; x < width; x++ )
        dst[x] = uchar(( ((b0 * (S0[x] >> 4)) >> 16) + ((b1 * (S1[x] >> 4)) >> 16) + 2)>>2);
}

};
Why is the operation S0 >> 4 performed when the resize function takes u8 input and a scalar for the V-direction? Since int32 would not overflow in the scalar case, why is this operation still necessary?

where is this code from? what’s the context?

and what is your understanding of fixed point arithmetic?

source code: opencv/modules/imgproc/src/resize.cpp at 5.x · opencv/opencv · GitHub
I might be misunderstanding something, but I would have thought that using floating-point arithmetic would yield higher precision than fixed-point arithmetic for processing u8 inputs. However, looking at the OpenCV source code, I see that fixed-point arithmetic is used instead. I would like to know the reason for this—thanks

fixed point arithmetic is integer arithmetic. this is usually cheaper than floating point arithmetic. it also leaves the floating point units free to do other things that really need floating point arithmetic.

as to what invokes this kernel, I can’t follow the execution backwards, nor can I trace where the operands come from or what they mean or what value ranges they have.

You should try having an AI follow the code and figure it out.

I followed the code backwards as you suggested and looked into what S0/S1 represent and why the >> 4 is there.

S0 and S1 are the int32 horizontal interpolation intermediates produced before the vertical interpolation. The scalar vertical path then does:

((b0 * (S0[x] >> 4)) >> 16)

I found that the >> 4 can actually affect the final 8-bit result, rather than only being an overflow-prevention step.

For example, I constructed a small CV_8UC1 resize case where:

S0 = S1 = 1026
b0 = 2045
b1 = 3

With the current calculation:

1026 >> 4 = 64

(2045 * 64) >> 16 = 1
(3 * 64) >> 16 = 0

(1 + 0 + 2) >> 2 = 0

If the full int32 intermediate is preserved and the corresponding scaling is done later:

(2045 * 1026) >> 20 = 2
(3 * 1026) >> 20 = 0

(2 + 0 + 2) >> 2 = 1

So the early >> 4 can change the output from 1 to 0.

I also checked the history and found that the >> 4 was already present in the SIMD VResizeLinearVec_32s8u implementation before the scalar implementation was added in bd394012e8, whose purpose was to eliminate discrepancies between SSE and non-SSE bilinear resize.

So I think the remaining question is not simply whether fixed-point arithmetic is being used for performance, but whether this particular early precision reduction is intentional and part of the bit-exact resize algorithm.

Do you know whether there is a reference/test that defines the expected result in a case where preserving the int32 intermediate gives a different pixel?