mirror of
https://github.com/opencv/opencv.git
synced 2026-09-12 05:11:04 -05:00
Merge pull request #29554 from mvanhorn/fix/29279-ipp-warpaffine-nearest-bitexact
imgproc: route IPP warpAffine INTER_NEAREST to native kernel for bit-exactness 🤖🤖🤖
This commit is contained in:
@@ -99,14 +99,17 @@ int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int
|
||||
{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}, //32F
|
||||
{{1, 1, 0}, {0, 0, 0}, {1, 1, 0}, {1, 1, 0}}}; //64F
|
||||
#else // IPP_CALLS_ENFORCED is not defined, results are strictly aligned to OpenCV implementation
|
||||
// NEAREST column kept 0 for all types: IPP's iwiWarpAffine rounds source coords at the
|
||||
// half-pixel boundary unlike the native kernel (warpAffineBlocklineNN), so its NN output
|
||||
// is not bit-exact. Forces fallback to native. See https://github.com/opencv/opencv/issues/29279
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3]={{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {1, 0, 0}}, //16U
|
||||
{{1, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0}}, //16S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32F
|
||||
{{1, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0}}}; //64F
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; //64F
|
||||
#endif
|
||||
|
||||
if(impl[CV_TYPE(src_type)][CV_MAT_CN(src_type)-1][interpolation] == 0)
|
||||
|
||||
@@ -1588,6 +1588,70 @@ TEST(Imgproc_Warp, regression_28554)
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgproc_Warp, regression_29279)
|
||||
{
|
||||
// IPP's iwiWarpAffine rounds source coords at the half-pixel boundary unlike the native
|
||||
// kernel (warpAffineBlocklineNN), so its NN output is not bit-exact.
|
||||
// See https://github.com/opencv/opencv/issues/29279
|
||||
const Size srcSize(800, 600);
|
||||
const Size dstSize(900, 900);
|
||||
|
||||
// Gradient values in [1, ~30700]: representable exactly in CV_16S, CV_16U, CV_32F and
|
||||
// CV_64F, so any change in the selected source pixel changes the resulting value.
|
||||
Mat base(srcSize, CV_32SC1);
|
||||
for (int y = 0; y < base.rows; y++)
|
||||
for (int x = 0; x < base.cols; x++)
|
||||
base.at<int>(y, x) = 1 + ((x * 7 + y * 13) % 30000);
|
||||
|
||||
const int channels[] = { 1, 3, 4 };
|
||||
const int nearestDepth[] = { CV_16S, CV_16U, CV_64F }; // formerly IPP-routed for NEAREST
|
||||
const double angles[] = { 0.0, 13.7, 45.0, 90.0 }; // 0 and 90 are no-ops, guard regressions
|
||||
|
||||
for (size_t ci = 0; ci < sizeof(channels) / sizeof(channels[0]); ci++)
|
||||
{
|
||||
const int cn = channels[ci];
|
||||
|
||||
std::vector<Mat> planes(cn);
|
||||
for (int c = 0; c < cn; c++)
|
||||
planes[c] = base + c * 101; // distinct per-channel values, still in range
|
||||
Mat srcInt;
|
||||
merge(planes, srcInt); // CV_32SC(cn)
|
||||
|
||||
Mat srcRef;
|
||||
srcInt.convertTo(srcRef, CV_MAKETYPE(CV_32F, cn));
|
||||
|
||||
for (size_t ai = 0; ai < sizeof(angles) / sizeof(angles[0]); ai++)
|
||||
{
|
||||
const double angle = angles[ai];
|
||||
|
||||
// Same convention as the issue reproducer: rotation about (400, 300) plus a
|
||||
// fractional translation.
|
||||
Mat M = getRotationMatrix2D(Point2f(400.f, 300.f), angle, 1.0);
|
||||
M.at<double>(0, 2) += 37.3;
|
||||
M.at<double>(1, 2) += -12.8;
|
||||
|
||||
Mat dstRef;
|
||||
warpAffine(srcRef, dstRef, M, dstSize, INTER_NEAREST, BORDER_CONSTANT, Scalar::all(0));
|
||||
|
||||
for (size_t di = 0; di < sizeof(nearestDepth) / sizeof(nearestDepth[0]); di++)
|
||||
{
|
||||
const int type = CV_MAKETYPE(nearestDepth[di], cn);
|
||||
Mat src;
|
||||
srcInt.convertTo(src, type);
|
||||
Mat dst;
|
||||
warpAffine(src, dst, M, dstSize, INTER_NEAREST, BORDER_CONSTANT, Scalar::all(0));
|
||||
|
||||
Mat dstF;
|
||||
dst.convertTo(dstF, CV_MAKETYPE(CV_32F, cn));
|
||||
EXPECT_EQ(0.0, cvtest::norm(dstRef, dstF, NORM_INF))
|
||||
<< "INTER_NEAREST warp not bit-exact with native: depth=" << nearestDepth[di]
|
||||
<< " cn=" << cn << " angle=" << angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgproc_GetAffineTransform, singularity)
|
||||
{
|
||||
Point2f A_sample[3];
|
||||
|
||||
Reference in New Issue
Block a user