opencv 基于边缘的形状匹配

opencv基于梯度的形状模板匹配,公式就是这个干货 | OpenCV实现边缘模板匹配算法

创建模板

  1. Canny得到边缘图像。
  2. 模板图像和边缘图像的padding,确保旋转的时候,图像都在。
auto PaddingTempl = [](Mat& mInput, Rect& rectRoi)
    {
    
    
        int nDiagonal = sqrt(pow(mInput.cols, 2) + pow(mInput.rows, 2)) + 1;
        if ((nDiagonal - mInput.cols) % 2 != 0) {
    
     nDiagonal++; }
        Size paddingSize(nDiagonal, nDiagonal);
        Mat paddingImg = mInput;
        rectRoi = Rect(Point((paddingSize.width - mInput.cols) * 0.5, (paddingSize.height - mInput.rows) * 0.5), Size(mInput.cols, mInput.rows));
        copyMakeBorder(paddingImg, mInput, rectRoi.tl().y, paddingSize.height - rectRoi.br().y, rectRoi.tl().x, paddingSize.width - rectRoi.br().x, BORDER_CONSTANT, Scalar::all(0));
        return;
    };
  1. 旋转padding后的模板图片,Sobel图像,根据大佬ImageShop,可以对Gx,Gy图像归一化拿到 NormGx,NormGy图像
void NormalizedSobel(const Mat mInput, Mat& mGx, Mat& mGy)
{
    
    
    Sobel(mInput, mGx, CV_32F, 1, 0);
    Sobel(mInput, mGy, CV_32F, 0, 1);
	float* pGx = mGx.ptr<float>(0);
	float* pGy = mGy.ptr<float>(0);
	float fG = 0;
	for(int row = 0; row < mGx.rows; row++)
	{
    
    
    	pGx = mGx.ptr<float>(row);
    	pGy = mGy.ptr<float>(row);
		for(int col = 0; col < mGx.cols; col++)
    	{
    
    
        	if(pGx[col]!=0 || pGy[col]!=0)
        	{
    
    
            	fG = 1./sqrtf(powf(pGx[col],2) + powf(pGy[col],2));
            	pGx[col] *=fG;
            	pGy[col] *=fG;
        	}
    	}
	}  
}
  1. 旋转padding后的边缘图像并遍历,根据其像素值为1的点,向NormGx,NormGy图像提取相应的梯度值。
for (int row = 0; row < mRotateEdge.rows; row++)
{
    
    
	pGx     = mGx.ptr<float>(row);
	pGy     = mGy.ptr<float>(row);
    pEdge   = mRotateEdge.ptr<uchar>(row);
    for (int col = 0; col < mRotateEdge.cols; col++)
    {
    
    
		float fGx   = pGx[col];
		float fGy   = pGy[col];
		uchar uEdge = pEdge[col];
		if (uEdge == 1 && fabs(fGx) >= 1e-6 && fabs(fGy) >= 1e-6)
		{
    
    
			templates[nId].m_vstGradientInfo.emplace_back(sGradient(fGx, fGy, Point(col, row)));
            templates[nId].m_mNormGx.ptr<float>(row)[col] = fGx;
            templates[nId].m_mNormGy.ptr<float>(row)[col] = fGy;
            templates[nId].m_nCoordsNums++;
       	}
     }
 }

二 匹配

  1. 将目标图像padding,padding后的目标图像与模板卷积后,就是的原始目标图像的大小。这样在卷积的过程中,就不用判断位置是否越界,速度也会快点。
void PaddingSrc(Mat& mSrc, Size sTempl, Rect& rectRoi)
{
    
    
	Size paddingSize(mSrc.cols + sTempl.width - 1, mSrc.rows + sTempl.height - 1);
	rectRoi = Rect(sTempl.width * 0.5, sTempl.height * 0.5, mSrc.cols, mSrc.rows);
	Mat paddingImg = mSrc.clone();
	copyMakeBorder(paddingImg, mSrc, rectRoi.tl().y, paddingSize.height - 		rectRoi.br().y, rectRoi.tl().x, paddingSize.width - rectRoi.br().x, 	BORDER_CONSTANT, Scalar::all(0));
  1. 在高层的时候,还是用dft更快。比起NCC,卷两次就可以了。最后把两次卷积相加,再除点数。
  	Size size(mGx.cols - templ->m_mNormGx.cols + 1, mGx.rows - templ->m_mNormGx.rows + 1);
    mResult = Mat::zeros(size, CV_32F);
    Mat mCorrGx = Mat::zeros(size, CV_32F);
    Mat mCorrGy = Mat::zeros(size, CV_32F);
    ConvDFT(mGx, templ->m_mNormGx, mCorrGx);
    ConvDFT(mGy, templ->m_mNormGy, mCorrGy);
    mResult = (mCorrGx + mCorrGy) / templ->m_nCoordsNums;
  1. 有了候选点,在下一层进行局部匹配。这时候就可以用上贪婪度加快速度。局部search的范围,我是根据金字塔层数计算的:2 + 2 * nLevel,最底层就是3x3。
void EdgeShapeMatch::Match(const Mat mGx, const Mat mGy, const double dMinScore, const int nSearchSize,const double dGreediness, const sTemplate* templ,double& dMax, Point& pMax, Mat& mResult)
{
    
    
    int nCoordsNums = templ->m_vstGradientInfo.size();
    double dNormMinScore = dMinScore / nCoordsNums;
    double dNormGreediness = ((1 - dGreediness * dMinScore) / (1 - dGreediness)) / nCoordsNums;
    double dMinScore_1 = dMinScore - 1;
    double dPartialScore = 0, dPartialSum = 0;
    int nCoords = 0, curX = 0, curY = 0;
    dMax = 0;
    for (int row = 0; row < nSearchSize + 1; row++)
    {
    
    
        for (int col = 0; col < nSearchSize + 1; col++)
        {
    
    
            dPartialSum = 0;
            for (int n = 0; n < nCoordsNums; n++)
            {
    
    
                nCoords = n + 1;
                curX = col + templ->m_vstGradientInfo[n].m_pPosition.x;
                curY = row + templ->m_vstGradientInfo[n].m_pPosition.y;
                dPartialSum += mGx.ptr<float>(curY)[curX] * templ->m_vstGradientInfo[n].m_fGx
                    + mGy.ptr<float>(curY)[curX] * templ->m_vstGradientInfo[n].m_fGy;
                dPartialScore = dPartialSum / nCoords;
                if (dPartialScore < min(dMinScore_1 + dNormGreediness * nCoords, dNormMinScore * nCoords))
                {
    
    
                    break;
                }
            }
            if (dPartialScore >= dMax)
            {
    
    
                dMax = dPartialScore;
                pMax.x = col;
                pMax.y = row;
            }
            mResult.ptr<float>(row)[col] = dPartialScore;
        }
    }
}

三 测试结果

680x480, [-180,180], 4level, 14.7186ms在这里插入图片描述
830x832, [-180,180], 4level, 31.43ms在这里插入图片描述
646x492, [-180,180], 4level, 9.30ms在这里插入图片描述
exe测试程序

猜你喜欢

转载自blog.csdn.net/weixin_43493903/article/details/132125104