使用OpenCV在灰度图像中查找局部最大值

Dur*_*rin 18 opencv image image-processing mathematical-optimization

有人知道如何IPL_DEPTH_8U使用OpenCV 在灰度图像中找到局部最大值吗?HarrisCorner提到了类似的东西,但我实际上对角落不感兴趣......谢谢!

eit*_*ich 6

这是一个简单的技巧。这个想法是用一个在中心包含一个洞的内核来扩张。在扩张操作之后,每个像素都被替换为其邻居中的最大值(在本示例中使用 5 x 5 邻域),不包括原始像素。

Mat1b kernelLM(Size(5, 5), 1u);
kernelLM.at<uchar>(2, 2) = 0u;
Mat imageLM;
dilate(image, imageLM, kernelLM);
Mat1b localMaxima = (image > imageLM);
Run Code Online (Sandbox Code Playgroud)

  • 如果一个邻居具有相同的最大值怎么办? (4认同)

Dog*_*yli 5

实际上,在我发布上面的代码之后,我写了一个更好且非常快的代码。即使对于 640x480 的图片,上面的代码也会受到影响。我对其进行了优化,现在即使对于 1600x1200 的图片也非常快。这是代码:

void localMaxima(cv::Mat src,cv::Mat &dst,int squareSize)
{
if (squareSize==0)
{
    dst = src.clone();
    return;
}

Mat m0;
dst = src.clone();
Point maxLoc(0,0);

//1.Be sure to have at least 3x3 for at least looking at 1 pixel close neighbours
//  Also the window must be <odd>x<odd>
SANITYCHECK(squareSize,3,1);
int sqrCenter = (squareSize-1)/2;

//2.Create the localWindow mask to get things done faster
//  When we find a local maxima we will multiply the subwindow with this MASK
//  So that we will not search for those 0 values again and again
Mat localWindowMask = Mat::zeros(Size(squareSize,squareSize),CV_8U);//boolean
localWindowMask.at<unsigned char>(sqrCenter,sqrCenter)=1;

//3.Find the threshold value to threshold the image
    //this function here returns the peak of histogram of picture
    //the picture is a thresholded picture it will have a lot of zero values in it
    //so that the second boolean variable says :
    //  (boolean) ? "return peak even if it is at 0" : "return peak discarding 0"
int thrshld =  maxUsedValInHistogramData(dst,false);
threshold(dst,m0,thrshld,1,THRESH_BINARY);

//4.Now delete all thresholded values from picture
dst = dst.mul(m0);

//put the src in the middle of the big array
for (int row=sqrCenter;row<dst.size().height-sqrCenter;row++)
    for (int col=sqrCenter;col<dst.size().width-sqrCenter;col++)
    {
        //1.if the value is zero it can not be a local maxima
        if (dst.at<unsigned char>(row,col)==0)
            continue;
        //2.the value at (row,col) is not 0 so it can be a local maxima point
        m0 =  dst.colRange(col-sqrCenter,col+sqrCenter+1).rowRange(row-sqrCenter,row+sqrCenter+1);
        minMaxLoc(m0,NULL,NULL,NULL,&maxLoc);
        //if the maximum location of this subWindow is at center
        //it means we found the local maxima
        //so we should delete the surrounding values which lies in the subWindow area
        //hence we will not try to find if a point is at localMaxima when already found a neighbour was
        if ((maxLoc.x==sqrCenter)&&(maxLoc.y==sqrCenter))
        {
            m0 = m0.mul(localWindowMask);
                            //we can skip the values that we already made 0 by the above function
            col+=sqrCenter;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @DogaSiyli 什么是 SANITYCHECK? (4认同)

kil*_*gre 5

如果像素等于"本地"邻域中的最大值,则该像素被认为是局部最大值.下面的函数用两行代码捕获这个属性.

为了处理'高原'上的像素(值等于它们的邻域),可以使用局部最小属性,因为高原像素等于它们的局部最小值.其余代码过滤掉那些像素.

void non_maxima_suppression(const cv::Mat& image, cv::Mat& mask, bool remove_plateaus) {
    // find pixels that are equal to the local neighborhood not maximum (including 'plateaus')
    cv::dilate(image, mask, cv::Mat());
    cv::compare(image, mask, mask, cv::CMP_GE);

    // optionally filter out pixels that are equal to the local minimum ('plateaus')
    if (remove_plateaus) {
        cv::Mat non_plateau_mask;
        cv::erode(image, non_plateau_mask, cv::Mat());
        cv::compare(image, non_plateau_mask, non_plateau_mask, cv::CMP_GT);
        cv::bitwise_and(mask, non_plateau_mask, mask);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这段代码给出了“连接”的结果,这很奇怪,因为我们正在寻找局部最大值;如果你问我,两个最大值无法连接。即使我增加内核大小。 (2认同)

小智 5

下面的清单是一个类似于 Matlab 的“imregionalmax”的函数。它查找至多nLocMax局部最大值高于阈值,其中找到的局部最大值至少相距 minDistBtwLocMax像素。它返回找到的局部最大值的实际数量。请注意,它使用 OpenCV 的minMaxLoc来查找全局最大值。它是“opencv-self-contained”,除了(易于实现)函数vdist,它计算点 (r,c) 和 (row,col) 之间的(欧几里德)距离。

输入是单通道 CV_32F 矩阵,位置是 nLocMax(行)乘 2(列)CV_32S 矩阵。

int imregionalmax(Mat input, int nLocMax, float threshold, float minDistBtwLocMax, Mat locations)
{
    Mat scratch = input.clone();
    int nFoundLocMax = 0;
    for (int i = 0; i < nLocMax; i++) {
        Point location;
        double maxVal;
        minMaxLoc(scratch, NULL, &maxVal, NULL, &location);
        if (maxVal > threshold) {
            nFoundLocMax += 1;
            int row = location.y;
            int col = location.x;
            locations.at<int>(i,0) = row;
            locations.at<int>(i,1) = col;
            int r0 = (row-minDistBtwLocMax > -1 ? row-minDistBtwLocMax : 0);
            int r1 = (row+minDistBtwLocMax < scratch.rows ? row+minDistBtwLocMax : scratch.rows-1);
            int c0 = (col-minDistBtwLocMax > -1 ? col-minDistBtwLocMax : 0);
            int c1 = (col+minDistBtwLocMax < scratch.cols ? col+minDistBtwLocMax : scratch.cols-1);
            for (int r = r0; r <= r1; r++) {
                for (int c = c0; c <= c1; c++) {
                    if (vdist(Point2DMake(r, c),Point2DMake(row, col)) <= minDistBtwLocMax) {
                        scratch.at<float>(r,c) = 0.0;
                    }
                }
            }
        } else {
            break;
        }
    }
    return nFoundLocMax;
}
Run Code Online (Sandbox Code Playgroud)


fab*_*ioM -2

我想你想使用

MinMaxLoc(arr, mask=NULL)-> (minVal, maxVal, minLoc, maxLoc)
Finds global minimum and maximum in array or subarray
Run Code Online (Sandbox Code Playgroud)

对你的图像起作用

  • 实际上是在寻找全局。我更喜欢本地(区域),如 Matlab 的 imregionmax() 函数。 (7认同)
  • @peakxu 实际上,使用全局 MinMaxLoc 函数结合一些 ROI 来查找局部极值并不是那么容易。分区怎么设置?以滑动窗口的方式?MinMaxLoc 将始终返回最小值和最大值。ROI 中的边界像素可能是 ROI 内的全局最大值,但 ROI 外的下一个像素可能具有更大的值。 (4认同)
  • 您可以使用 cvSetImageROI 和 cvResetImageROI 调用来定义您正在搜索的子区域。那么,法布里奇奥的建议就可以了。 (3认同)