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

时间:2011-04-05 10:13:52

标签: image image-processing opencv mathematical-optimization

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

10 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:3)

实际上在我发布上面的代码后,我写了一个更好,非常快的一个.. 上面的代码甚至会受到640x480图片的影响.. 我优化了它,现在即使1600x1200 pic也非常快。 这是代码:

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;
        }
    }
}

答案 2 :(得分:3)

以下列表是类似于Matlab&#34; imregionalmax&#34;的功能。它最多查找 nLocMax 阈值以上的局部最大值,其中找到的局部最大值至少为 minDistBtwLocMax 像素。它返回找到的实际局部最大值的实际数量。请注意,它使用OpenCV的 minMaxLoc 来查找全局最大值。它是&#34; opencv-self-contained&#34;除了(易于实现)函数 vdist ,它计算点(r,c)和(row,col)之间的(欧几里德)距离。

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

&#13;
&#13;
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;
}
&#13;
&#13;
&#13;

答案 3 :(得分:2)

要回答的第一个问题是您认为“本地”是什么。答案可能是方形窗口(比如3x3或5x5)或某个半径的圆形窗口。然后,您可以使用以每个像素为中心的窗口扫描整个图像,并在窗口中选择最高值。

有关如何在OpenCV中访问像素值的信息,请参阅this

答案 4 :(得分:1)

  

这是一种非常快速的方法。它存储创建了最大值的矢量   点。

vector <Point> GetLocalMaxima(const cv::Mat Src,int MatchingSize, int Threshold, int GaussKernel  )
{  
  vector <Point> vMaxLoc(0); 

  if ((MatchingSize % 2 == 0) || (GaussKernel % 2 == 0)) // MatchingSize and GaussKernel have to be "odd" and > 0
  {
    return vMaxLoc;
  }

  vMaxLoc.reserve(100); // Reserve place for fast access 
  Mat ProcessImg = Src.clone();
  int W = Src.cols;
  int H = Src.rows;
  int SearchWidth  = W - MatchingSize;
  int SearchHeight = H - MatchingSize;
  int MatchingSquareCenter = MatchingSize/2;

  if(GaussKernel > 1) // If You need a smoothing
  {
    GaussianBlur(ProcessImg,ProcessImg,Size(GaussKernel,GaussKernel),0,0,4);
  }
  uchar* pProcess = (uchar *) ProcessImg.data; // The pointer to image Data 

  int Shift = MatchingSquareCenter * ( W + 1);
  int k = 0;

  for(int y=0; y < SearchHeight; ++y)
  { 
    int m = k + Shift;
    for(int x=0;x < SearchWidth ; ++x)
    {
      if (pProcess[m++] >= Threshold)
      {
        Point LocMax;
        Mat mROI(ProcessImg, Rect(x,y,MatchingSize,MatchingSize));
        minMaxLoc(mROI,NULL,NULL,NULL,&LocMax);
        if (LocMax.x == MatchingSquareCenter && LocMax.y == MatchingSquareCenter)
        { 
          vMaxLoc.push_back(Point( x+LocMax.x,y + LocMax.y )); 
          // imshow("W1",mROI);cvWaitKey(0); //For gebug              
        }
      }
    }
    k += W;
  }
  return vMaxLoc; 
}

答案 5 :(得分:1)

找到一个简单的解决方案。

在这个例子中,如果你试图找到一个matchTemplate函数的2个结果,彼此之间的距离最小。

    cv::Mat result;
    matchTemplate(search, target, result, CV_TM_SQDIFF_NORMED);
    float score1;
    cv::Point displacement1 = MinMax(result, score1);
    cv::circle(result, cv::Point(displacement1.x+result.cols/2 , displacement1.y+result.rows/2), 10, cv::Scalar(0), CV_FILLED, 8, 0);
    float score2;
    cv::Point displacement2 = MinMax(result, score2);

,其中

cv::Point MinMax(cv::Mat &result, float &score)
{
    double minVal, maxVal;
    cv::Point  minLoc, maxLoc, matchLoc;

    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
    matchLoc.x = minLoc.x - result.cols/2;
    matchLoc.y = minLoc.y - result.rows/2;
    return minVal;
}

过程是:

  1. 使用minMaxLoc查找全局最小值
  2. 使用minima作为半径之间的最小距离
  3. 在全局最小值周围绘制一个填充的白色圆圈
  4. 寻找另一个最低要求
  5. 可以将得分相互比较以确定例如匹配的确定性,

答案 6 :(得分:1)

这是一个简单的伎俩。这个想法是用一个在中心有一个洞的内核扩张。在扩张操作之后,每个像素被其最大邻居替换(在该示例中使用5乘5邻域),排除原始像素。

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

答案 7 :(得分:0)

我想你想用

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

在你的形象上运作

答案 8 :(得分:0)

您可以遍历每个像素并测试它是否是局部最大值。我就是这样做的。 输入假定为CV_32FC1类型

#include <vector>//std::vector
#include <algorithm>//std::sort
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"

//structure for maximal values including position
struct SRegionalMaxPoint
{
    SRegionalMaxPoint():
        values(-FLT_MAX),
        row(-1),
        col(-1)
    {}
    float values;
    int row;
    int col;
    //ascending order
    bool operator()(const SRegionalMaxPoint& a, const SRegionalMaxPoint& b)
    {   
        return a.values < b.values;
    }   
};

//checks if pixel is local max
bool isRegionalMax(const float* im_ptr, const int& cols )
{
    float center = *im_ptr;
    bool is_regional_max = true;
    im_ptr -= (cols + 1);
    for (int ii = 0; ii < 3; ++ii, im_ptr+= (cols-3))
    {
        for (int jj = 0; jj < 3; ++jj, im_ptr++)
        {
            if (ii != 1 || jj != 1)
            {
                is_regional_max &= (center > *im_ptr);
            }
        }
    }
    return is_regional_max;
}

void imregionalmax(
    const cv::Mat& input, 
    std::vector<SRegionalMaxPoint>& buffer)
{
    //find local max - top maxima
    static const int margin = 1;
    const int rows = input.rows;
    const int cols = input.cols;
    for (int i = margin; i < rows - margin; ++i)
    {
        const float* im_ptr = input.ptr<float>(i, margin);
        for (int j = margin; j < cols - margin; ++j, im_ptr++)
        {
            //Check if pixel is local maximum
            if ( isRegionalMax(im_ptr, cols ) )
            {
                cv::Rect roi = cv::Rect(j - margin, i - margin, 3, 3);
                cv::Mat subMat = input(roi);

                float val = *im_ptr;
                //replace smallest value in buffer
                if ( val > buffer[0].values )
                {
                    buffer[0].values = val;
                    buffer[0].row    = i;
                    buffer[0].col    = j;
                    std::sort(buffer.begin(), buffer.end(), SRegionalMaxPoint());
                }

            }
        }
    }

}

为了测试代码,你可以试试这个:

cv::Mat temp = cv::Mat::zeros(15, 15, CV_32FC1);
temp.at<float>(7, 7) = 1;
temp.at<float>(3, 5) = 6;
temp.at<float>(8, 10) = 4;
temp.at<float>(11, 13) = 7;
temp.at<float>(10, 3) = 8;
temp.at<float>(7, 13) = 3;

vector<SRegionalMaxPoint> buffer_(5);
imregionalmax(temp, buffer_);

cv::Mat debug;
cv::cvtColor(temp, debug, cv::COLOR_GRAY2BGR);
for (auto it = buffer_.begin(); it != buffer_.end(); ++it)
{
    circle(debug, cv::Point(it->col, it->row), 1, cv::Scalar(0, 255, 0));
}

此解决方案不考虑平台,因此它与matlab的imregionalmax()不完全相同

答案 9 :(得分:0)

要查找不仅仅是全局的最小值和最大值,请尝试使用201中的此函数:

http://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.peak_local_max

您也可以参数化峰之间的最小距离。和更多。要找到最小值,请使用取反的值(不过要注意数组类型,[self logMessage:@"Got authorization tokens. Access token: %@", authState.lastTokenResponse.accessToken]; // This part was added for me [self makeRequest:authState.lastTokenResponse.accessToken]; 可以解决问题)。