opencv|基于openCV的形状模版匹配

初衷 果然halcon用顺手了,人就变懒了,正好有项目需要自己写个形状匹配的程序,就拿来练练手,程序不是很复杂,速度上感觉和halcon里面find_scaled_shape_model还是有差距,目前也不知道如何进一步改进,暂时就先这样了。大佬们如果有什么改进的想法可以评论一下,这样才能不断进步。
思路 主要的想法还是基于散点的重合度,虽然openCV自带matchTemplate和matchShape函数,但其内部是进行遍历像素矩计算,由于检测图中会有干扰边缘的存在,所以这些函数对于形状匹配(局部像素点集匹配)来说是不适合的。当然这些函数运行速度还是非常快的,目前我也不知道用什么办法可以达到同数量级的运算速度。
算法实现过程中主要遇到了几个问题:
1.生成旋转模版的问题 openCV没有自带的旋转函数,需要通过getRotationMatrix2D生成旋转矩阵,再用warpAffine进行仿射变换。但是warpAffine生成的图形会自动进行裁剪,会带来一些问题,因此需要稍微修改(详情见imrotate函数)。
2.遍历边缘点还是进行卷积的问题 由于openCV和halcon对于取像素点的操作都特别慢,所以对比之下只能采取用filter2D进行遍历,不得不说openCV自带的硬件加速库还是高效。
3.遍历运算速度过慢 openCV一些函数的源代码在内部还是进行了hal库的调用,用户没有办法修改,因此没有办法在函数内部进行算法修改。尝试用ppl库进行并行加速,效果还可以。
程序使用说明 (1)优先修改金字塔层数PyrLevel,层数越大,搜索速度越快,主函数中有缩放后的边缘图片,根据边缘是否清晰来修改PyrLevel和Canny函数的阈值,保证待检测图和模版图片的边缘足够清晰完整。
(2)如果找不到,适当减小minScore,越接近0,搜索相似容忍度越大,但运行速度同时也越慢。
函数片段 【opencv|基于openCV的形状模版匹配】具体代码工程见
https://download.csdn.net/download/sillykog/10727775

/************************************************* Function:// CreateScaledShapeModel Description:// 创建模版集 Input:// Template:模版图片的边缘图 PyrLevel:金字塔缩小层数 AngleStart,AngleExtent,AngleStep:旋转角度候选参数 ScaleMin,ScaleMax,ScaleStep:缩放比例候选参数 Output:// pModelImageSet,pModelPointSet,pScaleSet,pAngleSet:模版集指针 Return://无 Others:// *************************************************/ void CreateScaledShapeModel(Mat Template, int PyrLevel, int AngleStart, int AngleExtent, int AngleStep, float ScaleMin, float ScaleMax, float ScaleStep, \ vector* pModelImageSet, vector* pModelPointSet, vector* pScaleSet, vector* pAngleSet) { vector ModelImageSet; vector ModelPointSet; vector AngleSet; vector 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; } //模版生成 for (int level = 0; level <= PyrLevel; level++) { Mat pyrmodelImage = Template; for (int i = 0; i < level; i++) { pyrDown(pyrmodelImage, pyrmodelImage); } //缩放 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); //旋转 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("旋转", rotateImage); //imwrite("旋转.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(Point(i, j)) != 0) pointNum++; } } ModelPointSet.push_back(pointNum); rotateImage.release(); } scaleImage.release(); } } *pModelImageSet = ModelImageSet; *pModelPointSet = ModelPointSet; *pAngleSet = AngleSet; *pScaleSet = ScaleSet; }/************************************************* Function:// FindScaledShapeModel Description:// 在一张图片中搜索与模版相似的图形 Input:// Image:待检测图片 ModelImageSet,ModelPointSet,ScaleSet,AngleSet:模版集 PyrLevel:金字塔缩小层数 MinScore:筛选相似度阈值 Output:// pRow,pCol,pScale,pAngle,pScore:输出匹配到的元素参数集合的指针 Return://无 Others:// 使用该函数前需要先调用CreateScaledShapeModel *************************************************/ void FindScaledShapeModel(Mat Image, vector ModelImageSet, vector ModelPointSet, vector ScaleSet, vector AngleSet, int PyrLevel, float MinScore,\ vector* pRow, vector * pCol, vector* pScale, vector* pAngle, vector* pScore) { mutex mt; Mat modelImage = ModelImageSet[0]; vector Row; vector Col; vector Scale; vector Angle; vector Score; bool findFlag = false; //金字塔分层匹配 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("糊化原图", blurImage); //moveWindow("糊化原图", 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("模版", tempmodelImage); resizeWindow("模版", tempmodelImage.rows, tempmodelImage.cols); moveWindow("模版", blurImage.cols,0); waitKey(10); */ //double start = static_cast(getTickCount()); filter2D(tempblurImage, scoreImage, scoreImage.depth(), tempmodelImage); //double time = ((double)getTickCount() - start) / getTickFrequency(); //cout << "所用时间为:" << time << "秒" << 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(idx_max[0], idx_max[1]) = 0; max_score = v_max / temppointNum; //cout << "第" << level << "层" << "第" << k + 1 << "个成绩:" << 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("缩放图位置", blurImage); //imshow("原图位置", 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])= 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(0, 2) += (widthNew - img.cols) / 2; r.at(1, 2) += (heightNew - img.rows) / 2; warpAffine(img, newIm, r, Size(widthNew, heightNew), INTER_LINEAR, BORDER_REPLICATE); }

    推荐阅读