Thinking analysis and code
Thought 1: Use the findContours function to set the contours to the most external RETR_EXTERNAL, and find that the result is still all contours. The edge of the bottle is distorted due to the effect of binarization. The edge of the bottle is distorted due to binarization.
The original image | Direct canny to get the graph | Canny diagram after binarization |
---|
Idea 3: First binarization, then close operation, then fill small black area, and then corrosion operation, get a mask that is one circle smaller than the original bottle, and then remove the contour of the picture after Canny (the mask area is black), so maybe it is ok.
Results: It was indeed much better, but there were no results for two pictures. Observation: One was because there was a problem with the selected point of hole filling, and another debug found that there was also a problem with hole filling. For the time being, the big brown bottle is treated differently. Because his otsu binarization results are a little peculiar. The verification results showed that the effect was indeed better than that after the direct template matching canny, but there were still misjudgments. The misjudgments mainly occurred in identifying a part of the larger bottle as a small bottle, so it was necessary to limit from the number of pixels of the bottle, and the rule was that the categories with fewer pixels than the template bottle could not be identified. What to do: Summarize the bottle pixel value in the template graph, and obtain the bottle pixel value of each test graph (the number of pixels using MASK2 here needs to be screened by the connected domain to determine whether it is smaller than the actual bottle). Background color (black or white) starting point for flooding (the background is filled, usually (0,0)) output: filled binary graph
void My_hole_filling(Mat& srcImage, Mat& dstImage,int color,Point &startPoint)
{
// Floodfill from point (0,0
/*int x = startPoint.x; int y = startPoint.y; * /
//srcImage.at<char>(x, y)
if ( color== 255) // Background is white
{
srcImage = ~srcImage;
}
Mat im_floodfill = srcImage.clone();
floodFill(im_floodfill, startPoint, Scalar(255));
/ / 255
// Invert floodfilled image
Mat im_floodfill_inv;
bitwise_not(im_floodfill, im_floodfill_inv);
// Combine the two images to get the foreground. Get prospects
dstImage = (srcImage | im_floodfill_inv);
dstImage = ~dstImage;
}
Copy the code
Function to get the outline of the outside of the bottle
Input: original grayscale canny threshold output: contour masK2
void get_external_Contours_function(Mat& srcImage, Mat& dstImage,Mat& dstmask, int canny_thred)
{
// Fuzzy denoising
blur(srcImage, srcImage, Size(5.5));
Mat mask;
// Otsu binarization
threshold(srcImage, mask, 100.255, THRESH_OTSU);
/ / closed operation
int Abs_offset = 2;
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(Abs_offset * 2 + 1, Abs_offset * 2 + 1), Point(Abs_offset, Abs_offset)); // Return the kernel matrix
morphologyEx(mask, mask, MORPH_CLOSE, element);
// The hole is filled
Point startpoint = Point(40.40);
My_hole_filling(mask, mask, 0, startpoint);
// Shrink the mask one circle
Mat mask2;
Mat element_erode = getStructuringElement(MORPH_ELLIPSE, Size(Abs_offset * 2 + 1, Abs_offset * 2 + 1), Point(Abs_offset, Abs_offset)); // Return the kernel matrix
morphologyEx(mask, mask2, MORPH_DILATE, element_erode);
dstmask = mask2;
//mask2 is our mask
// Canny detection is performed on the gray image after the blur
Canny(srcImage, dstImage, canny_thred, canny_thred * 2.3);
// Set all white pixels in mask2 to black
int height = dstImage.rows;
int width = dstImage.cols;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
if (mask2.at<uchar>(j, i) == 0 && dstImage.at<uchar>(j, i) == 255)
{
dstImage.at<uchar>(j, i) = 0; }}}}Copy the code
Connected domain test code to find the number of pixels of the largest connected domain in MASK2
int main(a)
{
cv::Mat srcMat = imread("D:\ opencv_picture_test\ test\ test\ 10+ spot.jpg".1);
// CV ::Mat srcMat = imread("D: opencv_picture_test ", 1); // CV ::Mat srcMat = imread("D: opencv_picture_test ", 1);
// CV ::Mat srcMat = imread("D: opencv_picture_test ", 1); // CV ::Mat srcMat = imread("D: opencv_picture_test ", 1);
Mat dstMat;
Mat mask2;
int thred = 40;
// Convert to grayscale
cvtColor(srcMat, dstMat, COLOR_BGR2GRAY);
get_external_Contours_function(dstMat, dstMat, mask2, thred);
// Select the largest connected domain and invert mask2
mask2 = 255 - mask2;
Mat lableMat;
Mat statsMat;
Mat centerMat;
int nComp = cv::connectedComponentsWithStats(mask2,
lableMat,
statsMat,
centerMat,
8,
CV_32S);
// Find the one with the most connected pixels and record the number of pixels
int max_pixels = 0;
int max_pixels_label = 0;
if (nComp == 1) max_pixels = statsMat.at<int> (1.4);
else
{
// Find the connected domain flag with the most pixels
vector<int > pixels_nums;
for (int i = 1; i < nComp; i++)
{
pixels_nums.push_back(statsMat.at<int>(i, 4)); // Put the connected domain area into the vector
}
// Find the maximum value and return its position in the vector
auto maxPosition = max_element(pixels_nums.begin(), pixels_nums.end());
max_pixels = *maxPosition;
max_pixels_label = maxPosition - pixels_nums.begin();
}
cout <<"Number of connected domains (counting background)="<< nComp << endl;
cout << "max_pixels = " << max_pixels << endl;
cout << endl;
imshow("die_on_chip", dstMat);
waitKey(0);
return 0;
}
Copy the code
Results:
JPG 0 max_pixels = 14347 D:/opencv_picture_test/ opencv_picture_test D:/opencv_picture_test/ Visual project resize folder/test folder/test image \10+ pit.jpg 1 max_pixels = 13617 JPG 2 Max_Pixels = 13368 D:/opencv_picture_test/ Opencv_picture_test/Opencv_picture_test D:/opencv_picture_test/ Test folder/test folder/test image \10.jpg 3 max_Pixels = 14335 D:/opencv_picture_test/ Test folder/test folder/test image \10_2.jpg 4 max_pixels = 12677 D:/opencv_picture_test/ Test folder/test folder/test image \11.jpg 5 max_Pixels = 11718 D:/opencv_picture_test/ Test folder/test folder/test image \12.jpg 6 max_Pixels = 12413 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \13+ explosion.jpg 7 max_Pixels = 8002 D:/opencv_picture_test/ Test folder \14.jpg 8 max_pixels = 8870 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \2+ texture. JPG 9 max_pixels = 18144 D:/opencv_picture_test/ test folder/test folder/test image \3.jpg 10 max_pixels = 16658 D:/opencv_picture_test/ Test folder/test folder/test image \3_2.jpg 11 max_pixels = 15836 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \4+ thick.jpg 12 max_Pixels = 17636 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \4+ thick bottom 2.jpg 13 max_pixels = 16468 JPG 14 max_pixels = 15504 D:/opencv_picture_test/ visual project resize D:/opencv_picture_test/ JPG 15 max_pixels = 19443 D:/opencv_picture_test/ Opencv_picture_test / 5+ shoulder thinness.jpg 16 max_Pixels = 18623 D:/opencv_picture_test/ Image folder/test folder/test image \5+ bubble.jpg 17 max_Pixels = 19209 JPG 18 max_pixels = 20063 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \5+ explosion D:/opencv_picture_test/ Image folder/test image folder/test image \5+ wrinkles bubble.jpg 19 max_Pixels = 19552 D:/opencv_picture_test/ test folder/test folder/test image \6.jpg 20 max_pixels = 14913 D:/opencv_picture_test/ Test folder/test folder/test image \6_2.jpg 21 max_pixels = 15616 D:/opencv_picture_test/ Test folder/test folder/test image \ 63.jpg 22 max_pixels = 15653 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \7+ thick.jpg 23 max_Pixels = 15158 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \7+ thick background 2.jpg 24 max_pixels = 13383 D:/opencv_picture_test/ Test folder/test folder/test image \8.jpg 25 max_pixels = 14950 D:/opencv_picture_test/ Test folder/test folder/test image \8_2.jpg 26 max_pixels = 15271 D:/opencv_picture_test/ Test folder/test folder/test image \9.jpg 27 max_pixels = 13192
Count the number of pixels per bottle (take the minimum and subtract 2000 as standard)
Judge sequence | The number of pixels |
---|---|
1 | No statistical |
2 | 18144 |
3 | 15836 |
4 | 15504 |
5 | 18623 |
6 | 14913 |
7 | 13383 |
8 | 14950 |
9 | 13192 |
10(A) | 12677 |
11(B) | 11718 |
12 © | 12413 |
13(D) | 8002 |
14(E) | 8870 |
Implementation idea: after obtaining the test graph, get the number of masK2 pixels. Compare to elements in pixels_num_criterion[]. Create a new vector: prepare_template_num and match it with the template whose sequence number is test_mask_pxiels>=pixels_num_criterion[I]-pixels_num_sub. There seems to be something wrong with the code, save a file and check it tomorrow:
// Convert the test graph to a function that matches the template graph
// Input: test graph Canny operator threshold output: outer contour graph
// Return value: the number of bottle pixels in the test graph mask2
int test_covertTo_Outer_contour(Mat& srcImg, Mat& dstImg, int thred)
{
// Here we do batch processing
Mat mask2;
//int thred = 40;
// Convert to grayscale
cvtColor(srcImg, dstImg, COLOR_BGR2GRAY);
get_external_Contours_function(dstImg, dstImg, mask2, thred);
// Select the largest connected domain and invert mask2
mask2 = 255 - mask2;
Mat lableMat;
Mat statsMat;
Mat centerMat;
int nComp = cv::connectedComponentsWithStats(mask2,
lableMat,
statsMat,
centerMat,
8,
CV_32S);
// Find the one with the most connected pixels and record the number of pixels
int max_pixels = 0;
int max_pixels_label = 0;
if (nComp == 1) max_pixels = statsMat.at<int> (1.4);
else
{
// Find the connected domain flag with the most pixels
vector<int > pixels_nums;
/ / 0 is background
for (int i = 1; i < nComp; i++)
{
pixels_nums.push_back(statsMat.at<int>(i, 4)); // Put the connected domain area into the vector
}
// Find the maximum value and return its position in the vector, and then +1 is required for the position in the connected field label
auto maxPosition = max_element(pixels_nums.begin(), pixels_nums.end());
max_pixels = *(maxPosition);
max_pixels_label = (maxPosition - pixels_nums.begin() + 1);
}
return max_pixels;
}
int main(a)
{
// Change the console font color
system("color 02");
/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * [0] the test folder path and templates folder path * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Get the test folder path and template folder path
cv::String path_test = "D:/opencv_picture_test/ opencv_picture_test/";
cv::String path_template = "D:/opencv_picture_test/ visual project resize folder /template folder /template outline /";
cout << "Address obtained successfully" << endl;
/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 【 1 】 to load the template image * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / /
// Create a template vector
vector<Mat>tempMat;
// Insert template elements
Mat srcImage;
std::vector<cv::String> temp_filenames;
cv::glob(path_template, temp_filenames); // OpencV is a very useful function to read the file name in the specified path
for (int i = 0; i < temp_filenames.size(a); i++) { srcImage = cv::imread(temp_filenames[i],0);
tempMat.push_back(srcImage);
cout << temp_filenames[i] << endl;
}
// Get the number of templates
int tempMat_Nums = tempMat.size(a);/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * (2) load testing image * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / /
// Create a test vector
vector<Mat>testMat;
// Insert the test element
std::vector<cv::String> test_filenames;
cv::glob(path_test, test_filenames); // OpencV is a very useful function to read the file name in the specified path
for (int i = 0; i < test_filenames.size(a); i++) { srcImage = cv::imread(test_filenames[i]);
testMat.push_back(srcImage);
//cout << test_filenames[i] << endl;
}
// Get the number of test graphs
int testMat_Nums = testMat.size(a);/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * [3] for each test pattern template matching * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / /
for (int j = 0; j < testMat_Nums; j++) { cout <<"The first"<< j <<"The test of a test picture."<< endl;
Mat resultMat;
Mat CompareMat;
Mat dispMat;
// Convert the test diagram to a type that matches the template diagram
int test_mask_pxiels = 0;
test_mask_pxiels=test_covertTo_Outer_contour(testMat[j], CompareMat, 40);
cout << "test_mask_pxiels" << test_mask_pxiels << endl;
int match_method = TM_CCORR_NORMED; // This parameter is found to be good after trial and error.
// Use each template to match the test graph and find the best match for each result, storing the value in the vector
vector<double>goodval;
vector<Point>goodlock;
int matchnum = 0;
Point matchLoc;
vector<int>prepare_template_num;
cout << "Possible template number" << endl;
for (int i = 0; i <14; i++) {if (test_mask_pxiels >= (pixels_num_criterion[i] - pixels_num_sub))
{
// Import the template number that matches the rule into the vector
prepare_template_num.push_back(i);
cout << i <<"";
}
}
cout << endl;
for (int x = 0; x < prepare_template_num.size(a); x++) { cout << prepare_template_num[x] <<"";
}
cout << endl;
for (int i = 0; i < tempMat_Nums; i++) {// The larger the positive value is, the higher the matching degree will be, and the larger the negative value will be, the greater the difference between the image will be. However, if the image has no obvious features (i.e. the pixel value in the image is close to the average value), the closer the returned value will be to 0.
matchTemplate(CompareMat, tempMat[i], resultMat, match_method);
// Do not normalize, because the best value after normalization of different templates is 1, which cannot be compared
//normalize(resultMat, resultMat, 0, 1, NORM_MINMAX, -1, Mat()); / / normalization
double minVal; double maxVal; Point minLoc; Point maxLoc; // Define the maximum and minimum values and their position variables
minMaxLoc(resultMat, &minVal, &maxVal, &minLoc, &maxLoc, Mat()); // Find the maximum and minimum matching values from the result matrix and determine their positions
// For both methods SQDIFF and SQDIFF_NORMED, smaller values have higher matching results
// For the rest of the method, the larger the value, the better the match
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
{
// Set the likelihood of a template that does not meet the pixel count rule to 1
vector<int>::iterator result = find(prepare_template_num.begin(), prepare_template_num.end(), i); // Check to see if this template wants a template that matches the rule
if (result == prepare_template_num.end()) / / not found
{
minVal = 1;
}
goodlock.push_back(minLoc);
goodval.push_back(minVal);
}
else
{
// Set the probability of a template that does not meet the pixel count rule to 0
vector<int>::iterator result = find(prepare_template_num.begin(), prepare_template_num.end(), i); // Check to see if this template wants a template that matches the rule
if (result == prepare_template_num.end()) / / not found
{
maxVal = 0;
}
goodlock.push_back(maxLoc);
goodval.push_back(maxVal);
}
show_probability(i, maxVal);
//cout << i << " " << maxVal << endl;
}
// Find the best group of goodval
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
{
auto goodPosition = min_element(goodval.begin(), goodval.end());
matchnum = distance(begin(goodval), goodPosition);
}
else
{
auto goodPosition = max_element(goodval.begin(), goodval.end());
matchnum = distance(begin(goodval), goodPosition);
}
show_text(matchnum, test_filenames[j]);
matchLoc = goodlock[matchnum];
testMat[j].copyTo(dispMat);
// Draw a box of the same size as the template around the best match point
rectangle(dispMat, matchLoc, Point(matchLoc.x + tempMat[matchnum].cols, matchLoc.y + tempMat[matchnum].rows), Scalar::all(255), 2.8.0);
namedWindow("testMat", WINDOW_NORMAL);//WINDOW_NORMAL allows users to scale freely
imshow("testMat", dispMat);
waitKey(30);
}
return 0;
}
Copy the code