Merge pull request #23922 from vrabaud:imgwarp

Fix imgwarp at borders when transparent. #23922

I believe this is a proper fix to #23562

The PR #23754 overwrites data while that should not be the case with transparent data. The original test is failing because points at the border do not get computed because they do not have 4 neighbors to be computed. Still ,we can approximate their computation with whatever neighbors that are available.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Vincent Rabaud 2023-07-12 14:20:01 +02:00 committed by GitHub
parent 85f0074f23
commit fdfb875208
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 17 deletions

View File

@ -757,6 +757,37 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy,
} }
else else
{ {
if (borderType == BORDER_TRANSPARENT) {
for (; dx < X1; dx++, D += cn) {
if (dx >= dsize.width) continue;
const int sx = XY[dx * 2], sy = XY[dx * 2 + 1];
// If the mapped point is still within bounds, it did not get computed
// because it lacked 4 neighbors. Still, it can be computed with an
// approximate formula. If it is outside, the point is left untouched.
if (sx >= 0 && sx <= ssize.width - 1 && sy >= 0 && sy <= ssize.height - 1) {
const AT* w = wtab + FXY[dx] * 4;
WT w_tot = 0;
if (sx >= 0 && sy >= 0) w_tot += w[0];
if (sy >= 0 && sx < ssize.width - 1) w_tot += w[1];
if (sx >= 0 && sy < ssize.height - 1) w_tot += w[2];
if (sx < ssize.width - 1 && sy < ssize.height - 1) w_tot += w[3];
if (w_tot == 0.f) continue;
const WT w_tot_ini = (WT)w[0] + w[1] + w[2] + w[3];
const T* S = S0 + sy * sstep + sx * cn;
for (int k = 0; k < cn; k++) {
WT t0 = 0;
if (sx >= 0 && sy >= 0) t0 += S[k] * w[0];
if (sy >= 0 && sx < ssize.width - 1) t0 += S[k + cn] * w[1];
if (sx >= 0 && sy < ssize.height - 1) t0 += S[sstep + k] * w[2];
if (sx < ssize.width - 1 && sy < ssize.height - 1) t0 += S[sstep + k + cn] * w[3];
t0 = (WT)(t0 * (float)w_tot_ini / w_tot);
D[k] = castOp(t0);
}
}
}
continue;
}
if( cn == 1 ) if( cn == 1 )
for( ; dx < X1; dx++, D++ ) for( ; dx < X1; dx++, D++ )
{ {
@ -767,12 +798,6 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy,
{ {
D[0] = cval[0]; D[0] = cval[0];
} }
else if (borderType == BORDER_TRANSPARENT)
{
if (sx < ssize.width && sx >= 0 &&
sy < ssize.height && sy >= 0)
D[0] = S0[sy*sstep + sx];
}
else else
{ {
int sx0, sx1, sy0, sy1; int sx0, sx1, sy0, sy1;
@ -814,13 +839,6 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy,
for(int k = 0; k < cn; k++ ) for(int k = 0; k < cn; k++ )
D[k] = cval[k]; D[k] = cval[k];
} }
else if (borderType == BORDER_TRANSPARENT)
{
if (sx < ssize.width && sx >= 0 &&
sy < ssize.height && sy >= 0)
for(int k = 0; k < cn; k++ )
D[k] = S0[sy*sstep + sx*cn + k];
}
else else
{ {
int sx0, sx1, sy0, sy1; int sx0, sx1, sy0, sy1;
@ -837,10 +855,6 @@ static void remapBilinear( const Mat& _src, Mat& _dst, const Mat& _xy,
v2 = S0 + sy1*sstep + sx0*cn; v2 = S0 + sy1*sstep + sx0*cn;
v3 = S0 + sy1*sstep + sx1*cn; v3 = S0 + sy1*sstep + sx1*cn;
} }
else if( borderType == BORDER_TRANSPARENT &&
((unsigned)sx >= (unsigned)(ssize.width-1) ||
(unsigned)sy >= (unsigned)(ssize.height-1)))
continue;
else else
{ {
sx0 = borderInterpolate(sx, ssize.width, borderType); sx0 = borderInterpolate(sx, ssize.width, borderType);

View File

@ -1672,6 +1672,28 @@ TEST(Imgproc_Remap, issue_23562)
remap(src, dst, mapx, mapy, INTER_LINEAR, BORDER_TRANSPARENT); remap(src, dst, mapx, mapy, INTER_LINEAR, BORDER_TRANSPARENT);
ASSERT_EQ(0.0, cvtest::norm(ref, dst, NORM_INF)) << "channels=" << cn; ASSERT_EQ(0.0, cvtest::norm(ref, dst, NORM_INF)) << "channels=" << cn;
} }
mapx = Mat1f({3, 3}, {0, 1, 2, 0, 1, 2, 0, 1, 2});
mapy = Mat1f({3, 3}, {0, 0, 0, 1, 1, 1, 2, 2, 1.5});
for (int cn = 1; cn <= 4; ++cn) {
Mat src = cv::Mat(3, 3, CV_32FC(cn));
Mat dst = 10 * Mat::ones(3, 3, CV_32FC(cn));
for(int y = 0; y < 3; ++y) {
for(int x = 0; x < 3; ++x) {
for(int k = 0; k < cn; ++k) {
src.ptr<float>(y,x)[k] = 10.f * y + x;
}
}
}
Mat ref = src.clone();
for(int k = 0; k < cn; ++k) {
ref.ptr<float>(2,2)[k] = (src.ptr<float>(1, 2)[k] + src.ptr<float>(2, 2)[k]) / 2.f;
}
remap(src, dst, mapx, mapy, INTER_LINEAR, BORDER_TRANSPARENT);
ASSERT_EQ(0.0, cvtest::norm(ref, dst, NORM_INF)) << "channels=" << cn;
}
} }
}} // namespace }} // namespace