opencv 如何检测金字塔顶视图图像的高光点

lnxxn5zx  于 2023-03-30  发布在  其他
关注(0)|答案(2)|浏览(127)

正如您在示例图像中看到的,这是SEM捕获的表面图像。我想做的是尽可能准确地检测每个金字塔的峰值。

我试过角点检测方法,结果峰点没有被识别为角点。有没有其他方法适合这样的任务?

params = dict(maxCorners=500,
              qualityLevel=0.01,
              minDistance=5,
              blockSize=10,
              useHarrisDetector=False)
corners = cv2.goodFeaturesToTrack(sharp_img, **params)
bvjxkvbb

bvjxkvbb1#

方法:模板匹配,使用一个金字塔的尖端补丁。
我通过number of false positives and false negatives, the balance of which can be adjusted判断(目测)结果,并对模板匹配的分数设置阈值。
我使用了几个金字塔尖的平均值,大小为40 x40(“半径”20),但一个模板就足够了。要么用照片编辑器进行裁剪,要么编写代码来选择一些点,收集和平均裁剪。
我试过fastNlMeansDenoising。它没有明显的好处。模板匹配已经是一种低通滤波器。
我还取消了对整个输入图像的gammaMap。只有(im * np.float32(1/255)) ** 2.2。这对结果有明显的好处。
我还应用了Sobel/Scharr滤波器。它似乎给予了更好的结果。我将X和Y方向的响应和幅度组成3通道图像进行进一步处理。

给你个建议

您还可以使用copyMakeBorder来获取图像边缘附近的提示......由于那里的图像内容较少,因此无论如何您可能都不会在那里获得任何命中。
总之,代码的核心是这样的:

scores = cv.matchTemplate(haystack, template, cv.TM_CCOEFF)

现在你需要从中刮取局部最大值。这是非最大值抑制:

localmax = cv.dilate(scores, None, iterations=radius) # or 2*radius
localmax = (scores == localmax)

levelmask = (scores >= 200)

candidates = levelmask & localmax

(nlabels, labels, stats, centroids) = cv.connectedComponentsWithStats(candidates.astype(np.uint8), connectivity=8)
print(nlabels-1, "found")

centroids为您提供模板的每个匹配示例的左上角。添加(radius, radius)以获得模板的中心。
剩下的只是画画:

for label, pt in zip(range(1, nlabels), centroids[1:]):
    ipt = pt.round().astype(int)
    bestscore = scores[labels == label].max()
    ...

这是结果。我看到至少一个假阳性,和假阴性,但那些金字塔小于补丁半径。
这可以通过在Scharr滤波(梯度)和直接输入(水平)两者上测试模板匹配分数来改进。

bfhwhh0e

bfhwhh0e2#

我尝试了非常简单的方法:
“看起来越靠近金字塔顶部越亮,所以……不能从每个像素位置爬上金字塔吗?”
我用C++实现的非常非常直接(:不是好代码)。

//Start from (x,y) --> move to brighter position.
//When reached to local brightest position, vote to the position.
void Work(
    int x, int y,  //Start position
    const cv::Mat &ProcTgtImg, //Gray Scale Pyramids image
    cv::Mat &FootPrintBuff, //This is a storage to avoid duplicate work.
    cv::Mat &Votes, //Voting Buffer
    int r   //local region size(radius)
)
{
    static std::vector< cv::Vec2i > Route;  //Why "static" is, to reduce memory allocation cost.

    auto UpdateWork = [&]( cv::Vec2i ResultPos )->void{
        for( const auto &P : Route ){   FootPrintBuff.at<cv::Vec2i>( P[1],P[0] ) = ResultPos;   }
        ++Votes.at<unsigned short>( ResultPos[1], ResultPos[0] );
    };

    Route.clear();
    while( true )
    {
        Route.emplace_back( x,y );

        {//If current position(x,y) is already processed...
            const auto &FP = FootPrintBuff.at<cv::Vec2i>(y,x);
            if( FP[0] >= 0 )
            {
                UpdateWork( FP );
                return;
            }
        }

        //Find the position has local max pixel value
        cv::Vec2i MaxValPos(x,y);
        {// * Perhaps circle would be better for the shape of the region, but square is used here for simplicity.
            int top = std::max( 0, y-r );
            int bottom = std::min( ProcTgtImg.rows-1, y+r );
            int left = std::max( 0, x-r );
            int right = std::min( ProcTgtImg.cols-1, x+r );
        
            unsigned char MaxVal = 0;
            for( int yy=top; yy<=bottom; ++yy )
            {
                const unsigned char *p = ProcTgtImg.ptr<unsigned char>( yy, left );
                for( int xx=left; xx<=right; ++xx, ++p )
                {
                    if( MaxVal < *p )
                    {
                        MaxVal = *p;
                        MaxValPos = cv::Vec2i(xx,yy);
                    }
                }
            }
        }
        //If current position has local max value, Vote to current position.
        if( x==MaxValPos[0] && y==MaxValPos[1] )
        {
            UpdateWork( MaxValPos );
            return;
        }
        //Change the current position to the local max position.
        x = MaxValPos[0];
        y = MaxValPos[1];
    }
}

//
int main()
{
    //Load image as GrayScale
    cv::Mat SrcImg = cv::imread( "Pyramids.png", cv::IMREAD_GRAYSCALE );
    if( SrcImg.empty() )return 0;
    cv::imshow( "SrcImg", SrcImg );
    
    //Process
    cv::Mat Votes = cv::Mat::zeros( SrcImg.size(), CV_16U );
    {
        cv::Mat ProcTgtImg;
        cv::medianBlur( SrcImg, ProcTgtImg, 5 );

        cv::Mat FootPrintBuff( SrcImg.size(), CV_32SC2 );
        FootPrintBuff = cv::Scalar(-1,-1);

        for( int y=0; y<ProcTgtImg.rows; ++y )
        {
            for( int x=0; x<ProcTgtImg.cols; ++x )
            {
                Work( x,y, ProcTgtImg, FootPrintBuff, Votes, 32 );
            }
        }
    }

    {//Show Result
        cv::Mat Visualize( SrcImg.size(), CV_8UC3 );
        cv::cvtColor( SrcImg, Visualize, cv::COLOR_GRAY2BGR );
        Visualize *= 0.45;

        double MaxVal;  //Max Vote value is just used to change the draw circle size
        cv::minMaxLoc( Votes, NULL, &MaxVal );

        for( int y=0; y<Votes.rows; ++y )
        {
            const unsigned short *v = Votes.ptr<unsigned short>(y);
            for( int x=0; x<Votes.cols; ++x, ++v )
            {
                if( *v )
                {   cv::circle( Visualize, cv::Point(x,y), 2+int(10*(*v)/MaxVal), cv::Scalar(64,64,255), 2 );   }
            }
        }
        cv::imshow( "Visualize", Visualize );
    }
    cv::waitKey();
    return 0;
}

这就是结果。
我不能说这个结果是非常好的,但一些顶部发现。
圆圈越大表示投票越多。

相关问题