This article is 1959 words long and is expected to read for 5 minutes
preface
I've been working on Android in recent months. I didn't do the Demo of OpenCV. I just downloaded VS2022 two days ago. I just made a simple Demo of OpenCV image cut into nine squares with the help of the new VS2022.
Realization effect
![](/images/blog/4f4ad12bdff17cd38c4914b13f20377e.jpg)
The right side of the picture is the effect of dividing into 9 pictures. I've seen it< Fun algorithm -- OpenCV Huarong AI automatic problem solving >Old friends should know what I'm going to do. Yes, this is to make another jigsaw puzzle. The area number corresponding to the image is printed with PutText on each image on the right. Now it is for identification.
Realization idea
#
thinking
1
After loading the image, use Resize to scale the image to a square size
2
The image size of each intercepted area is calculated according to the starting position of the image
3
Store the intercepted area into the container of Vector and sort it randomly during the storage process
4
Generate a new canvas and traverse the container to display each image
Core code explanation
Wechat Zhixiang
01
About segmented image containers
At first, I wanted to use map. Later, I felt it was not very good, so I created a structure, that is, the original sequence number position of the segmented image, the image Mat, and the current position.
![](/images/blog/8895fe8e4155f4a49954d3a1be5212de.jpg)
The generated segmented image container is implemented with a SplitMats function.
![](/images/blog/576a460d1e2c83e975276fa5d07c7eac.jpg)
std::vector<CutMat*> MatSet::SplitMats(cv::Mat& img, int cols, int rows) { std::vector<CutMat*> matvts; if (cols == 0 || rows == 0) { std::cout << "The number of rows and columns cannot be 0" << std::endl; return matvts; } matvts.resize(cols * rows); //Calculate the width and height of the average number of cells int width = img.cols / cols; int height = img.rows / rows; //Generate sequence number list std::vector<int> nums = GetVtsPos(cols, rows); //Separate the rectangle according to the rows and columns entered for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { //Calculates the starting X and Y coordinates of the current rectangle int x = col * width; if (x > 0) x++; int y = row * height; if (y > 0) y++; //Calculate the width and height of the intercepted rectangle, and add control that it cannot exceed the boundary of the source image int rwidth = width; if (x + rwidth > img.cols) rwidth = img.cols - x; int rheight = height; if (y + rheight > img.rows) rheight = img.rows - y; //Generate the intercepted rectangle and store the intercepted image in the map cv::Rect rect = cv::Rect(x, y, rwidth, rheight); cv::Mat matrect = img(rect); //The intercepted image needs to judge whether the width and height are consistent. If they are inconsistent, they will be scaled to the same size for display in one image if (rwidth != width || rheight != height) { cv::resize(matrect, matrect, cv::Size(width, height)); } //Sequence number of the current Mat int pos = row * rows + col + 1; CutMat* tmpcurmat = new CutMat(pos, matrect); //Randomly assigned new location tmpcurmat->curPosition = GetRandNum(nums); //Insert into the container according to the randomly sorted position matvts[tmpcurmat->curPosition - 1] = tmpcurmat; } } return matvts; }
In the process of loading, it should be noted that after the average segmentation according to the image size, the final rectangular length should judge whether it exceeds the image edge. If it exceeds, the length should be set to the length to the image edge, and then the same size of the image can be set through Resize.
![](/images/blog/519fe8fd2b8a11847d8ba405ca800b3e.jpg)
02
Solution to image disorder
In the structure defined above, the curPosition attribute is assigned by generating a random position. Considering that the display should be displayed in order according to the curPosition attribute, the traditional method is two ideas:
- Use Map storage. The Key is curPosition. When traversing, find the Key to find the corresponding Map. The time complexity is O1. Use Map space for time.
- The returned containers are sorted according to curPosition again, and the time complexity is On.
Because we only have 9 containers, the above two basic speeds can be ignored. However, even if the random number has been assigned in the generation process, it is also possible to directly specify the storage location at that time. There is no need to use the above two schemes at all.
![](/images/blog/fa4af8aaeaa4ddf790a21dbfbcb2d074.jpg)
Directly set the number of containers by passing in row and column numbers.
![](/images/blog/8f739aec797afccf9567baf157a35aa8.jpg)
Directly modify the subscript value of the container according to the generated specified location.
![](/images/blog/297aaec41f87864ffe71101543692ada.jpg)
In the whole project, a new class of MatSet is created. Drawing and generating images are implemented here. main.cpp is to load images and external calls.
Complete code
MatSet.h
#pragma once #include <opencv2/opencv.hpp> #include <iostream> struct CutMat { public: //Where images should be stored int Position; //Current location of image int curPosition; //image data cv::Mat mat; CutMat(int _pos, cv::Mat _mat) : Position(_pos), curPosition(_pos), mat(_mat) { } }; class MatSet { public: //Generate a segmented image container static std::vector<CutMat*> SplitMats(cv::Mat& img, int cols = 3, int rows = 3); //Displays a jigsaw puzzle image static void DrawPuzzleMat(std::vector<CutMat*>& cutmats, std::vector<std::vector<cv::Point>>& contours, bool iscontours = false, int cols = 3); private: //Generate corresponding position sequence number containers according to rows and columns static std::vector<int> GetVtsPos(int cols = 3, int rows = 3); //Get the current random sequence number static int GetRandNum(std::vector<int>& nums); //Insert profile static void InsertContours(std::vector<std::vector<cv::Point>>& contours, cv::Rect rect); };
MatSet.cpp
#include "MatSet.h" std::vector<CutMat*> MatSet::SplitMats(cv::Mat& img, int cols, int rows) { std::vector<CutMat*> matvts; if (cols == 0 || rows == 0) { std::cout << "The number of rows and columns cannot be 0" << std::endl; return matvts; } //Set the number of containers directly according to rows and columns matvts.resize(cols * rows); //Calculate the width and height of the average number of cells int width = img.cols / cols; int height = img.rows / rows; //Generate sequence number list std::vector<int> nums = GetVtsPos(cols, rows); //Separate the rectangle according to the rows and columns entered for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { //Calculates the starting X and Y coordinates of the current rectangle int x = col * width; if (x > 0) x++; int y = row * height; if (y > 0) y++; //Calculate the width and height of the intercepted rectangle, and add control that it cannot exceed the boundary of the source image int rwidth = width; if (x + rwidth > img.cols) rwidth = img.cols - x; int rheight = height; if (y + rheight > img.rows) rheight = img.rows - y; //Generate the intercepted rectangle and store the intercepted image in the map cv::Rect rect = cv::Rect(x, y, rwidth, rheight); cv::Mat matrect = img(rect); //The intercepted image needs to judge whether the width and height are consistent. If they are inconsistent, they will be scaled to the same size for display in one image if (rwidth != width || rheight != height) { cv::resize(matrect, matrect, cv::Size(width, height)); } //Sequence number of the current Mat int pos = row * rows + col + 1; CutMat* tmpcurmat = new CutMat(pos, matrect); //Randomly assigned new location tmpcurmat->curPosition = GetRandNum(nums); //Insert into the container according to the randomly sorted position matvts[tmpcurmat->curPosition - 1] = tmpcurmat; } } return matvts; } void MatSet::DrawPuzzleMat(std::vector<CutMat*>& cutmats, std::vector<std::vector<cv::Point>>& contours, bool iscontours, int cols) { if (cutmats.empty()) return; int starttop = 20; int startleft = 20; int rectwidth = cutmats[0]->mat.cols; int rectheight = cutmats[0]->mat.rows; int nums = cutmats.size(); //Create image cv::Mat src = cv::Mat(cv::Size(600, 700), CV_8UC3, cv::Scalar(240, 240, 240)); for (int i = 0; i < nums; ++i) { //Calculate the rows and columns of the two-dimensional array in the current order int row = i / cols; int col = i % cols; //Generate corresponding rectangular box cv::Rect rect = cv::Rect(startleft + col * rectwidth, starttop + row * rectheight, rectwidth, rectheight); //Replace the image at the corresponding position cutmats[i]->mat.copyTo(src(rect)); //Contour insertion if (iscontours) InsertContours(contours, rect); } imshow("puzzle", src); } //Generate sequence number container std::vector<int> MatSet::GetVtsPos(int cols, int rows) { std::vector<int> nums; int total = cols * rows; if (total > 0) { for (int i = 1; i <= total; ++i) { nums.push_back(i); } } return nums; } int MatSet::GetRandNum(std::vector<int>& nums) { //Initialize random number seed srand((int)time(0)); //The last assignment in the lower right corner is incorrect, so the remainder is not counted int index = nums.size() == 1 ? 0 : rand() % (nums.size() - 1); //Get return value int resint = nums[index]; //Delete the assigned number from the container nums.erase(nums.begin() + index); return resint; } void MatSet::InsertContours(std::vector<std::vector<cv::Point>>& contours, cv::Rect rect) { std::vector<cv::Point> vetpt; cv::Point pt1 = cv::Point(rect.x, rect.y); vetpt.push_back(pt1); cv::Point pt2 = cv::Point(rect.x + rect.width, rect.y); vetpt.push_back(pt2); cv::Point pt3 = cv::Point(rect.x + rect.width, rect.y + rect.height); vetpt.push_back(pt3); cv::Point pt4 = cv::Point(rect.x, rect.y + rect.height); vetpt.push_back(pt4); contours.push_back(vetpt); }
main.cpp
#pragma once #include <opencv2/opencv.hpp> #include <iostream> #include "MatSet.h" using namespace std; using namespace cv; //Define contour area vector<vector<Point>> contours; int main(int argc, char** argv) { try { Mat src = imread("E:/DCIM/test8.jpg"); if (src.empty()) { cout << "Image loading failed...." << endl; waitKey(0); return -1; } //Set image zoom to 500 * 500 Mat tmpsrc; resize(src, tmpsrc, Size(500, 500)); imshow("src", src); imshow("tmpsrc", tmpsrc); //Get the set after image segmentation vector<CutMat*> vtsmat = MatSet::SplitMats(tmpsrc); //List the serial numbers corresponding to the set after image segmentation for (int i = 0; i < vtsmat.size(); ++i) { Mat tmpmat = vtsmat[i]->mat; string title = to_string(vtsmat[i]->Position); putText(tmpmat, title, Point(tmpmat.cols/2, tmpmat.rows/2), 2, 2, Scalar(0, 0, 255)); } //Draw image MatSet::DrawPuzzleMat(vtsmat, contours, true); cv::waitKey(0); return 0; } catch (const std::exception& ex) { cout << ex.what() << endl; cv::waitKey(0); return -1; } }
The whole Demo is made with VS2022 and OpenCV4.5.4. In C + + with VS2022, the smart prompt feels like VS2019, which is not as powerful as that in c# I said in the previous article. Another problem is that after using OpenCV4.5.4, there are more loading error outputs on the console during operation. Although it does not affect the operation, it looks uncomfortable. The figure is as follows:
![](/images/blog/52447263286ccc618d506e700346c3d3.jpg)
If you have a little partner who knows how to solve the problem, please leave a message. Thank you very much.
finish