original intention
Sure enough, halcon is easy to use, and people become lazy. There is a project that needs to write a shape matching program, so they use it to practice. The program is not very complex, and the speed feels like find in halcon_ scaled_ shape_ There is still a gap in the model. At present, I don't know how to further improve it. That's it for the time being. If the big guys have any ideas for improvement, they can comment on it, so as to make continuous progress.
thinking
The main idea is based on the coincidence degree of scattered points. Although openCV has its own matchTemplate and matchShape functions, it internally calculates the traversal pixel moment. Because there will be interference edges in the detection image, these functions are not suitable for shape matching (local pixel set matching). Of course, the running speed of these functions is still very fast. At present, I don't know how to achieve the operation speed of the same order of magnitude.
There are several problems in the implementation of the algorithm:
1. Generate rotation template
openCV does not have its own rotation function. You need to generate the rotation matrix through getRotationMatrix2D, and then use warpAffine for affine transformation. However, the graphics generated by warpAffine will be automatically cropped, which will cause some problems, so it needs to be modified slightly (see imrotate function for details).
2. The problem of traversing edge points or convolution
Because openCV and halcon are particularly slow to take pixels, in contrast, they can only traverse with filter2D. It has to be said that the hardware acceleration library provided by openCV is still efficient.
3. Traversal operation speed is too slow
The source code of some openCV functions is internally called by hal library, and the user has no way to modify it, so there is no way to modify the algorithm inside the function. Try parallel acceleration with ppl library, and the effect is OK.
Program instructions
(1) The number of pyramid layers PyrLevel shall be modified first. The larger the number of layers, the faster the search speed. There are scaled edge images in the main function. The thresholds of PyrLevel and Canny functions shall be modified according to whether the edges are clear to ensure that the edges of the image to be detected and the template image are clear and complete.
(2) If it cannot be found, reduce minScore appropriately. The closer it is to 0, the greater the similarity tolerance of the search, but the slower the running speed.
Function fragment
/************************************************* Function: // CreateScaledShapeModel Description: // Create template set Input: // Template:Edge map of template picture PyrLevel:Pyramid reduction layers AngleStart,AngleExtent,AngleStep:Rotation angle candidate parameters ScaleMin,ScaleMax,ScaleStep:Scale candidate parameters Output: // pModelImageSet,pModelPointSet,pScaleSet,pAngleSet:Template set pointer Return: // nothing Others: // *************************************************/ void CreateScaledShapeModel(Mat Template, int PyrLevel, int AngleStart, int AngleExtent, int AngleStep, float ScaleMin, float ScaleMax, float ScaleStep, \ vector<Mat>* pModelImageSet, vector<int>* pModelPointSet, vector<float>* pScaleSet, vector<float>* pAngleSet) { vector<Mat> ModelImageSet; vector<int> ModelPointSet; vector<float> AngleSet; vector<float> ScaleSet; while (ScaleMin <= ScaleMax) { cout << ScaleMax << endl; ScaleSet.push_back(ScaleMax); ScaleMax -= ScaleStep; } while (AngleStart <= AngleExtent) { cout << AngleExtent << endl; AngleSet.push_back(AngleExtent); AngleExtent -= AngleStep; } //Template generation for (int level = 0; level <= PyrLevel; level++) { Mat pyrmodelImage = Template; for (int i = 0; i < level; i++) { pyrDown(pyrmodelImage, pyrmodelImage); } //zoom for (int i = 0; i < ScaleSet.size(); i++) { Mat scaleImage; resize(pyrmodelImage, scaleImage, Size(round(pyrmodelImage.cols*ScaleSet[i]), round(pyrmodelImage.cols*ScaleSet[i])), 0, 0, INTER_LINEAR); //rotate for (int j = 0; j < AngleSet.size(); j++) { Mat rotateImage; imrotate(scaleImage, rotateImage, AngleSet[j]); //threshold(rotateImage, rotateImage, 1, 255, 0); Canny(rotateImage, rotateImage, 50, 100, 3, false); rotateImage /= 255; //imshow("rotation", rotateImage); //imwrite("rotate. jpg", rotateImage); //waitKey(0); ModelImageSet.push_back(rotateImage); int pointNum = 0; for (int i = 0; i < rotateImage.cols; i++) { for (int j = 0; j < rotateImage.rows; j++) { if (rotateImage.at<uchar>(Point(i, j)) != 0) pointNum++; } } ModelPointSet.push_back(pointNum); rotateImage.release(); } scaleImage.release(); } } *pModelImageSet = ModelImageSet; *pModelPointSet = ModelPointSet; *pAngleSet = AngleSet; *pScaleSet = ScaleSet; } /************************************************* Function: // FindScaledShapeModel Description: // Search for graphics similar to the template in a picture Input: // Image:Pictures to be tested ModelImageSet,ModelPointSet,ScaleSet,AngleSet:Template set PyrLevel:Pyramid reduction layers MinScore:Screening similarity threshold Output: // pRow,pCol,pScale,pAngle,pScore:Outputs a pointer to the set of element parameters that match Return: // nothing Others: // You need to call CreateScaledShapeModel before using this function *************************************************/ void FindScaledShapeModel(Mat Image, vector<Mat> ModelImageSet, vector<int> ModelPointSet, vector<float> ScaleSet, vector<float> AngleSet, int PyrLevel, float MinScore,\ vector<int>* pRow, vector<int> * pCol, vector<float>* pScale, vector<float>* pAngle, vector<float>* pScore) { mutex mt; Mat modelImage = ModelImageSet[0]; vector<int> Row; vector<int> Col; vector<float> Scale; vector<float> Angle; vector<float> Score; bool findFlag = false; //Pyramid hierarchical matching for (int level = PyrLevel; !findFlag && level >= PyrLevel; level--) { Mat pyrsrcImage = Image; for (int i = 0; i < level; i++) { pyrDown(pyrsrcImage, pyrsrcImage); } int kernSize = floor(sqrt(min(pyrsrcImage.rows / 100, pyrsrcImage.cols / 100))); Mat kern = Mat::ones(2 * kernSize + 1, 2 * kernSize + 1, CV_8U); Mat blurImage; filter2D(pyrsrcImage, blurImage, pyrsrcImage.depth(), kern); //imshow("gelatinized original", blurImage); //moveWindow("paste original", 0, 0); //waitKey(10); Mat tempblurImage; blurImage.convertTo(tempblurImage, CV_8U); tempblurImage /= 255; int parallelnum = ScaleSet.size() *AngleSet.size(); parallel_for(0, parallelnum, [&](int k) { Mat scoreImage(tempblurImage.size(), CV_16U); Mat tempmodelImage = ModelImageSet.at(ModelImageSet.size()- 1 - k); int temppointNum = ModelPointSet.at(ModelPointSet.size() - 1 - k); float max_score = 0; /*imshow("Template ", tempmodelImage); resizeWindow("Template ", tempmodelImage.rows, tempmodelImage.cols); moveWindow("Template ", blueimage.cols, 0); waitKey(10);*/ //double start = static_cast<double>(getTickCount()); filter2D(tempblurImage, scoreImage, scoreImage.depth(), tempmodelImage); //double time = ((double)getTickCount() - start) / getTickFrequency(); //Cout < < time used is: < < time < < seconds < < endl; mt.lock(); while (1) { double v_min, v_max; int idx_min[2] = { 255,255 }, idx_max[2] = { 255, 255 }; minMaxIdx(scoreImage, &v_min, &v_max, idx_min, idx_max); scoreImage.at<ushort>(idx_max[0], idx_max[1]) = 0; max_score = v_max / temppointNum; //Cout < < level < < layer < < K + 1 < < score: "< max_score < < endl; if (max_score > MinScore) { float scale = ScaleSet[ScaleSet.size() - 1 - (k) / AngleSet.size()]; float angle = AngleSet[AngleSet.size() - 1 - (k) % AngleSet.size()]; //int selectx = (idx_max[1] - (tempmodelImage.cols - 1) / 2)*pow(2, level); //int selecty = (idx_max[0] - (tempmodelImage.rows - 1) / 2)*pow(2, level); //int pyrselectx = idx_max[1] - tempmodelImage.cols / 2; //int pyrselecty = idx_max[0] - tempmodelImage.rows / 2; Row.push_back(idx_max[1] * pow(2, level)); Col.push_back(idx_max[0] * pow(2, level)); Scale.push_back(scale); Angle.push_back(angle); Score.push_back(max_score); //cout << Point(selectx, selecty) << " " << Point(pyrselectx, pyrselecty) << endl; //rectangle(blurImage, Rect(pyrselectx, pyrselecty, tempmodelImage.cols, tempmodelImage.rows), 255, 2, 8); //rectangle(Image, Rect(selectx, selecty, modelImage.cols / scale, modelImage.rows / scale), 255, 2, 8); //imshow("zoom position", blueimage); //imshow("original position", Image); //waitKey(0); //findFlag = true; } else break; } tempmodelImage.release(); scoreImage.release(); mt.unlock(); } ); for (int m = 0; m < Row.size() ; m++) { for (int n = m+1; n < Row.size() ; n++) { if (abs(Row[n] - Row[m])<modelImage.rows*0.3 && abs(Col[n] - Col[m])< modelImage.cols*03) { if (Score[n] < Score[m]) { Row.erase(Row.begin() + n); Col.erase(Col.begin() + n); Scale.erase(Scale.begin() + n); Angle.erase(Angle.begin() + n); Score.erase(Score.begin() + n); n--; } else if (Score[n] >= Score[m]) { swap(Row[m], Row[n]); swap(Col[m], Col[n]); swap(Scale[m], Scale[n]); swap(Angle[m], Angle[n]); swap(Score[m], Score[n]); Row.erase(Row.begin() + n); Col.erase(Col.begin() + n); Scale.erase(Scale.begin() + n); Angle.erase(Angle.begin() + n); Score.erase(Score.begin() + n); n = m ; } } } } *pRow = Row; *pCol = Col; *pScale = Scale; *pAngle = Angle; *pScore = Score; } } void imrotate(Mat& img, Mat& newIm, double angle) { int heightNew = int(img.cols*fabs(sin(angle*3.14 / 180)) + img.rows * fabs(cos(angle*3.14 / 180))); int widthNew = int(img.rows*fabs(sin(angle*3.14 / 180)) + img.cols * fabs(cos(angle*3.14 / 180))); int len = max(img.cols, img.rows); Point2f pt(len / 2., len / 2.); Mat r = getRotationMatrix2D(pt, angle, 1.0); r.at<double>(0, 2) += (widthNew - img.cols) / 2; r.at<double>(1, 2) += (heightNew - img.rows) / 2; warpAffine(img, newIm, r, Size(widthNew, heightNew), INTER_LINEAR, BORDER_REPLICATE); }
--------
Original link: https://blog.csdn.net/sillykog/article/details/83116793