Merge pull request #24539 from LaurentBerger:blobrecttoimage

Add blobrecttoimage #24539

### Pull Request Readiness Checklist

resolves https://github.com/opencv/opencv/issues/14659

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 #14659
- [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
pull/24723/head
Laurent Berger 1 year ago committed by GitHub
parent fa5ed62a66
commit 3e6dcdc0a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      modules/dnn/include/opencv2/dnn/dnn.hpp
  2. 28
      modules/dnn/misc/python/test/test_dnn.py
  3. 60
      modules/dnn/src/dnn_utils.cpp
  4. 90
      modules/dnn/test/test_misc.cpp

@ -1222,6 +1222,20 @@ CV__DNN_INLINE_NS_BEGIN
CV_PROP_RW DataLayout datalayout; //!< Order of output dimensions. Choose DNN_LAYOUT_NCHW or DNN_LAYOUT_NHWC.
CV_PROP_RW ImagePaddingMode paddingmode; //!< Image padding mode. @see ImagePaddingMode.
CV_PROP_RW Scalar borderValue; //!< Value used in padding mode for padding.
/** @brief Get rectangle coordinates in original image system from rectangle in blob coordinates.
* @param rBlob rect in blob coordinates.
* @param size original input image size.
* @returns rectangle in original image coordinates.
*/
CV_WRAP Rect blobRectToImageRect(const Rect &rBlob, const Size &size);
/** @brief Get rectangle coordinates in original image system from rectangle in blob coordinates.
* @param rBlob rect in blob coordinates.
* @param rImg result rect in image coordinates.
* @param size original input image size.
*/
CV_WRAP void blobRectsToImageRects(const std::vector<Rect> &rBlob, CV_OUT std::vector<Rect>& rImg, const Size& size);
};
/** @brief Creates 4-dimensional blob from image with given params.

@ -127,6 +127,34 @@ class dnn_test(NewOpenCVTests):
targets = cv.dnn.getAvailableTargets(cv.dnn.DNN_BACKEND_OPENCV)
self.assertTrue(cv.dnn.DNN_TARGET_CPU in targets)
def test_blobRectsToImageRects(self):
paramNet = cv.dnn.Image2BlobParams()
paramNet.size = (226, 226)
paramNet.ddepth = cv.CV_32F
paramNet.mean = [0.485, 0.456, 0.406]
paramNet.scalefactor = [0.229, 0.224, 0.225]
paramNet.swapRB = False
paramNet.datalayout = cv.dnn.DNN_LAYOUT_NCHW
paramNet.paddingmode = cv.dnn.DNN_PMODE_LETTERBOX
rBlob = np.zeros(shape=(20, 4), dtype=np.int32)
rImg = paramNet.blobRectsToImageRects(rBlob, (356, 356))
self.assertTrue(type(rImg[0, 0])==np.int32)
self.assertTrue(rImg.shape==(20, 4))
def test_blobRectToImageRect(self):
paramNet = cv.dnn.Image2BlobParams()
paramNet.size = (226, 226)
paramNet.ddepth = cv.CV_32F
paramNet.mean = [0.485, 0.456, 0.406]
paramNet.scalefactor = [0.229, 0.224, 0.225]
paramNet.swapRB = False
paramNet.datalayout = cv.dnn.DNN_LAYOUT_NCHW
paramNet.paddingmode = cv.dnn.DNN_PMODE_LETTERBOX
rBlob = np.zeros(shape=(20, 4), dtype=np.int32)
rImg = paramNet.blobRectToImageRect((0, 0, 0, 0), (356, 356))
self.assertTrue(type(rImg[0])==int)
def test_blobFromImage(self):
np.random.seed(324)

@ -382,6 +382,66 @@ void imagesFromBlob(const cv::Mat& blob_, OutputArrayOfArrays images_)
}
}
Rect Image2BlobParams::blobRectToImageRect(const Rect &r, const Size &oriImage)
{
CV_Assert(!oriImage.empty());
std::vector<Rect> rImg, rBlob;
rBlob.push_back(Rect(r));
rImg.resize(1);
this->blobRectsToImageRects(rBlob, rImg, oriImage);
return Rect(rImg[0]);
}
void Image2BlobParams::blobRectsToImageRects(const std::vector<Rect> &rBlob, std::vector<Rect>& rImg, const Size& imgSize)
{
Size size = this->size;
rImg.resize(rBlob.size());
if (size != imgSize)
{
if (this->paddingmode == DNN_PMODE_CROP_CENTER)
{
float resizeFactor = std::max(size.width / (float)imgSize.width,
size.height / (float)imgSize.height);
for (int i = 0; i < rBlob.size(); i++)
{
rImg[i] = Rect((rBlob[i].x + 0.5 * (imgSize.width * resizeFactor - size.width)) / resizeFactor,
(rBlob[i].y + 0.5 * (imgSize.height * resizeFactor - size.height)) / resizeFactor,
rBlob[i].width / resizeFactor,
rBlob[i].height / resizeFactor);
}
}
else if (this->paddingmode == DNN_PMODE_LETTERBOX)
{
float resizeFactor = std::min(size.width / (float)imgSize.width,
size.height / (float)imgSize.height);
int rh = int(imgSize.height * resizeFactor);
int rw = int(imgSize.width * resizeFactor);
int top = (size.height - rh) / 2;
int left = (size.width - rw) / 2;
for (int i = 0; i < rBlob.size(); i++)
{
rImg[i] = Rect((rBlob[i].x - left) / resizeFactor,
(rBlob[i].y - top) / resizeFactor,
rBlob[i].width / resizeFactor,
rBlob[i].height / resizeFactor);
}
}
else if (this->paddingmode == DNN_PMODE_NULL)
{
for (int i = 0; i < rBlob.size(); i++)
{
rImg[i] = Rect(rBlob[i].x * (float)imgSize.width / size.width,
rBlob[i].y * (float)imgSize.height / size.height,
rBlob[i].width * (float)imgSize.width / size.width,
rBlob[i].height * (float)imgSize.height / size.height);
}
}
else
CV_Error(CV_StsBadArg, "Unknown padding mode");
}
}
CV__DNN_INLINE_NS_END
}} // namespace cv::dnn

@ -13,17 +13,83 @@
namespace opencv_test { namespace {
TEST(blobRectToImageRect, DNN_PMODE_NULL)
{
Size inputSize(50 + (rand() % 100) / 4 * 4, 50 + (rand() % 100) / 4 * 4);
Size imgSize(200 + (rand() % 100) / 4 * 4, 200 + (rand() % 100) / 4 * 4);
Rect rBlob(inputSize.width / 2 - inputSize.width / 4, inputSize.height / 2 - inputSize.height / 4, inputSize.width / 2, inputSize.height / 2);
Image2BlobParams paramNet;
paramNet.scalefactor = Scalar::all(1.f);
paramNet.size = inputSize;
paramNet.ddepth = CV_32F;
paramNet.mean = Scalar();
paramNet.swapRB = false;
paramNet.datalayout = DNN_LAYOUT_NHWC;
paramNet.paddingmode = DNN_PMODE_NULL;
Rect rOri = paramNet.blobRectToImageRect(rBlob, imgSize);
Rect rImg = Rect(rBlob.x * (float)imgSize.width / inputSize.width, rBlob.y * (float)imgSize.height / inputSize.height,
rBlob.width * (float)imgSize.width / inputSize.width, rBlob.height * (float)imgSize.height / inputSize.height);
ASSERT_EQ(rImg, rOri);
}
TEST(blobRectToImageRect, DNN_PMODE_CROP_CENTER)
{
Size inputSize(50 + (rand() % 100) / 4 * 4, 50 + (rand() % 100) / 4 * 4);
Size imgSize(200 + (rand() % 100) / 4 * 4, 200 + (rand() % 100) / 4 * 4);
Rect rBlob(inputSize.width / 2 - inputSize.width / 4, inputSize.height / 2 - inputSize.height / 4, inputSize.width / 2, inputSize.height / 2);
Image2BlobParams paramNet;
paramNet.scalefactor = Scalar::all(1.f);
paramNet.size = inputSize;
paramNet.ddepth = CV_32F;
paramNet.mean = Scalar();
paramNet.swapRB = false;
paramNet.datalayout = DNN_LAYOUT_NHWC;
paramNet.paddingmode = DNN_PMODE_CROP_CENTER;
Rect rOri = paramNet.blobRectToImageRect(rBlob, imgSize);
float resizeFactor = std::max(inputSize.width / (float)imgSize.width,
inputSize.height / (float)imgSize.height);
Rect rImg = Rect((rBlob.x + 0.5 * (imgSize.width * resizeFactor - inputSize.width)) / resizeFactor, (rBlob.y + 0.5 * (imgSize.height * resizeFactor - inputSize.height)) / resizeFactor,
rBlob.width / resizeFactor, rBlob.height / resizeFactor);
ASSERT_EQ(rImg, rOri);
}
TEST(blobRectToImageRect, DNN_PMODE_LETTERBOX)
{
Size inputSize(50 + (rand() % 100) / 4 * 4, 50 + (rand() % 100) / 4 * 4);
Size imgSize(200 + (rand() % 100) / 4 * 4, 200 + (rand() % 100) / 4 * 4);
Rect rBlob(inputSize.width / 2 - inputSize.width / 4, inputSize.height / 2 - inputSize.height / 4, inputSize.width / 2, inputSize.height / 2);
Image2BlobParams paramNet;
paramNet.scalefactor = Scalar::all(1.f);
paramNet.size = inputSize;
paramNet.ddepth = CV_32F;
paramNet.mean = Scalar();
paramNet.swapRB = false;
paramNet.datalayout = DNN_LAYOUT_NHWC;
paramNet.paddingmode = DNN_PMODE_LETTERBOX;
Rect rOri = paramNet.blobRectToImageRect(rBlob, imgSize);
float resizeFactor = std::min(inputSize.width / (float)imgSize.width,
inputSize.height / (float)imgSize.height);
int rh = int(imgSize.height * resizeFactor);
int rw = int(imgSize.width * resizeFactor);
int top = (inputSize.height - rh) / 2;
int left = (inputSize.width - rw) / 2;
Rect rImg = Rect((rBlob.x - left) / resizeFactor, (rBlob.y - top) / resizeFactor, rBlob.width / resizeFactor, rBlob.height / resizeFactor);
ASSERT_EQ(rImg, rOri);
}
TEST(blobFromImage_4ch, Regression)
{
Mat ch[4];
for(int i = 0; i < 4; i++)
ch[i] = Mat::ones(10, 10, CV_8U)*i;
for (int i = 0; i < 4; i++)
ch[i] = Mat::ones(10, 10, CV_8U) * i;
Mat img;
merge(ch, 4, img);
Mat blob = dnn::blobFromImage(img, 1., Size(), Scalar(), false, false);
for(int i = 0; i < 4; i++)
for (int i = 0; i < 4; i++)
{
ch[i] = Mat(img.rows, img.cols, CV_32F, blob.ptr(0, i));
ASSERT_DOUBLE_EQ(cvtest::norm(ch[i], cv::NORM_INF), i);
@ -32,7 +98,7 @@ TEST(blobFromImage_4ch, Regression)
TEST(blobFromImage, allocated)
{
int size[] = {1, 3, 4, 5};
int size[] = { 1, 3, 4, 5 };
Mat img(size[2], size[3], CV_32FC(size[1]));
Mat blob(4, size, CV_32F);
void* blobData = blob.data;
@ -66,8 +132,8 @@ TEST(imagesFromBlob, Regression)
TEST(blobFromImageWithParams_4ch, NHWC_scalar_scale)
{
Mat img(10, 10, CV_8UC4, cv::Scalar(0,1,2,3));
std::vector<double> factorVec = {0.1, 0.2, 0.3, 0.4};
Mat img(10, 10, CV_8UC4, cv::Scalar(0, 1, 2, 3));
std::vector<double> factorVec = { 0.1, 0.2, 0.3, 0.4 };
Scalar scalefactor(factorVec[0], factorVec[1], factorVec[2], factorVec[3]);
@ -77,7 +143,7 @@ TEST(blobFromImageWithParams_4ch, NHWC_scalar_scale)
Mat blob = dnn::blobFromImageWithParams(img, param); // [1, 10, 10, 4]
float* blobPtr = blob.ptr<float>(0);
std::vector<float> targetVec = {(float )factorVec[0] * 0, (float )factorVec[1] * 1, (float )factorVec[2] * 2, (float )factorVec[3] * 3}; // Target Value.
std::vector<float> targetVec = { (float)factorVec[0] * 0, (float)factorVec[1] * 1, (float)factorVec[2] * 2, (float)factorVec[3] * 3 }; // Target Value.
for (int hi = 0; hi < 10; hi++)
{
for (int wi = 0; wi < 10; wi++)
@ -128,16 +194,16 @@ TEST(blobFromImageWithParams_CustomPadding, letter_box)
TEST(blobFromImageWithParams_4ch, letter_box)
{
Mat img(40, 20, CV_8UC4, cv::Scalar(0,1,2,3));
Mat img(40, 20, CV_8UC4, cv::Scalar(0, 1, 2, 3));
// Construct target mat.
Mat targetCh[4];
// The letterbox will add zero at the left and right of output blob.
// After the letterbox, every row data would have same value showing as valVec.
std::vector<uint8_t> valVec = {0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0};
std::vector<uint8_t> valVec = { 0,0,0,0,0, 1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0 };
Mat rowM(1, 20, CV_8UC1, valVec.data());
for(int i = 0; i < 4; i++)
for (int i = 0; i < 4; i++)
{
targetCh[i] = rowM * i;
}
@ -163,7 +229,7 @@ TEST(blobFromImagesWithParams_4ch, multi_image)
param.scalefactor = scalefactor;
param.datalayout = DNN_LAYOUT_NHWC;
Mat blobs = blobFromImagesWithParams(std::vector<Mat> { img, 2*img }, param);
Mat blobs = blobFromImagesWithParams(std::vector<Mat> { img, 2 * img }, param);
vector<Range> ranges;
ranges.push_back(Range(0, 1));
ranges.push_back(Range(0, blobs.size[1]));
@ -173,7 +239,7 @@ TEST(blobFromImagesWithParams_4ch, multi_image)
ranges[0] = Range(1, 2);
Mat blob1 = blobs(ranges);
EXPECT_EQ(0, cvtest::norm(2*blob0, blob1, NORM_INF));
EXPECT_EQ(0, cvtest::norm(2 * blob0, blob1, NORM_INF));
}
TEST(readNet, Regression)

Loading…
Cancel
Save