Merge pull request #30242 from pratham-mcw/inpaint_opt

photo: vectorize cv::inpaint (Telea/NS) inner loops
This commit is contained in:
Abhishek Gola
2026-10-26 14:18:55 +05:30
committed by GitHub
2 changed files with 883 additions and 463 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -183,4 +183,124 @@ TEST_P(Photo_InpaintSmallBorders, regression)
INSTANTIATE_TEST_CASE_P(/*nothing*/, Photo_InpaintSmallBorders, Values(CV_8UC1, CV_8UC3));
typedef testing::TestWithParam<tuple<perf::MatType>> Photo_InpaintNonuniform;
TEST_P(Photo_InpaintNonuniform, regression)
{
const int type = get<0>(GetParam());
const int size = 16;
Mat mask = Mat::zeros(size, size, CV_8U);
circle(mask, Point(size / 2, size / 2), size / 5, Scalar::all(255), -1);
rectangle(mask, Rect(2, size - 6, 4, 4), Scalar::all(255), -1);
Mat img(size, size, type);
if (type == CV_8UC3)
{
for (int y = 0; y < size; y++)
for (int x = 0; x < size; x++)
img.at<Vec3b>(y, x) = Vec3b(
(uchar)((y * 37 + x * 53) % 256),
(uchar)((y * 17 + x * 91 + 40) % 256),
(uchar)((y * 73 + x * 11 + 90) % 256));
}
else
{
const double scale = type == CV_8UC1 ? 255.0 : type == CV_16UC1 ? 65535.0 : 1000.0;
for (int y = 0; y < size; y++)
for (int x = 0; x < size; x++)
{
double v = ((y * 37 + x * 53 + (x * y) % 29) % 211) * scale / 210.0;
if (type == CV_8UC1) img.at<uchar>(y, x) = (uchar)v;
else if (type == CV_16UC1) img.at<ushort>(y, x) = (ushort)v;
else img.at<float>(y, x) = (float)v;
}
}
const int rows[] = {8, 8, 10, 11, 6};
const int cols[] = {8, 7, 3, 4, 8};
Mat dst;
inpaint(img, mask, dst, 3, INPAINT_TELEA);
switch (type)
{
case CV_8UC1:
{
const int expected[] = {123, 134, 113, 133, 149};
for (int k = 0; k < 5; k++) ASSERT_NEAR((int)dst.at<uchar>(rows[k], cols[k]), expected[k], 1);
break;
}
case CV_16UC1:
{
const int expected[] = {31891, 34654, 28621, 34077, 38605};
for (int k = 0; k < 5; k++) ASSERT_NEAR((int)dst.at<ushort>(rows[k], cols[k]), expected[k], 1);
break;
}
case CV_32FC1:
{
const float expected[] = {485.2213f, 527.2879f, 438.2543f, 519.2518f, 588.0099f};
for (int k = 0; k < 5; k++) ASSERT_NEAR(dst.at<float>(rows[k], cols[k]), expected[k], 1e-2);
break;
}
default: // CV_8UC3
{
const Vec3b expected[] = {
Vec3b(148, 135, 127), Vec3b(138, 111, 122), Vec3b(181, 129, 76), Vec3b(150, 95, 104), Vec3b(97, 141, 148)
};
for (int k = 0; k < 5; k++) ASSERT_LE(cvtest::norm(dst.at<Vec3b>(rows[k], cols[k]), expected[k], NORM_INF), 1.);
break;
}
}
inpaint(img, mask, dst, 3, INPAINT_NS);
switch (type)
{
case CV_8UC1:
{
const int expected[] = {141, 159, 115, 146, 129};
for (int k = 0; k < 5; k++) ASSERT_NEAR((int)dst.at<uchar>(rows[k], cols[k]), expected[k], 1);
break;
}
case CV_16UC1:
{
const int expected[] = {36271, 41082, 29646, 37694, 33247};
for (int k = 0; k < 5; k++) ASSERT_NEAR((int)dst.at<ushort>(rows[k], cols[k]), expected[k], 1);
break;
}
case CV_32FC1:
{
const float expected[] = {553.4644f, 626.8738f, 452.3825f, 575.1885f, 507.3150f};
for (int k = 0; k < 5; k++) ASSERT_NEAR(dst.at<float>(rows[k], cols[k]), expected[k], 1e-2);
break;
}
default: // CV_8UC3
{
const Vec3b expected[] = {
Vec3b(118, 123, 112), Vec3b(117, 122, 91), Vec3b(177, 149, 71), Vec3b(151, 102, 101), Vec3b(117, 153, 84)
};
for (int k = 0; k < 5; k++) ASSERT_LE(cvtest::norm(dst.at<Vec3b>(rows[k], cols[k]), expected[k], NORM_INF), 1.);
break;
}
}
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, Photo_InpaintNonuniform, Values(CV_8UC1, CV_16UC1, CV_32FC1, CV_8UC3));
TEST(Photo_InpaintTelea, tiny_2x2_rounding)
{
Mat src = (Mat_<uchar>(2, 2) << 178, 171, 108, 62);
Mat mask = (Mat_<uchar>(2, 2) << 0, 0, 255, 0);
Mat dst;
inpaint(src, mask, dst, 1, INPAINT_TELEA);
EXPECT_NEAR((int)dst.at<uchar>(1, 0), 120.5, 0.5);
EXPECT_EQ(178, (int)dst.at<uchar>(0, 0));
EXPECT_EQ(171, (int)dst.at<uchar>(0, 1));
EXPECT_EQ(62, (int)dst.at<uchar>(1, 1));
inpaint(src, mask, dst, 1, INPAINT_NS);
EXPECT_EQ(120, (int)dst.at<uchar>(1, 0));
EXPECT_EQ(178, (int)dst.at<uchar>(0, 0));
EXPECT_EQ(171, (int)dst.at<uchar>(0, 1));
EXPECT_EQ(62, (int)dst.at<uchar>(1, 1));
}
}} // namespace