Найти локальные максимумы на изображении в градациях серого с помощью OpenCV - PullRequest
18 голосов
/ 05 апреля 2011

Кто-нибудь знает, как найти локальные максимумы в изображении в градациях серого IPL_DEPTH_8U, используя OpenCV?HarrisCorner упоминает что-то подобное, но на самом деле меня не интересуют углы ... Спасибо!

Ответы [ 10 ]

5 голосов
/ 09 января 2014

Пиксель считается локальным максимумом, если он равен максимальному значению в «локальной» окрестности.Приведенная ниже функция фиксирует это свойство в двух строках кода.

Для работы с пикселями на «плато» (значение равно их окрестности) можно использовать свойство локального минимума, поскольку пиксели плато равны их локальному минимуму,Остальная часть кода отфильтровывает эти пиксели.

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);
    }
}
3 голосов
/ 10 декабря 2014

Следующий листинг является функцией, аналогичной «imregionalmax» Матлаба.Он ищет не более nLocMax локальных максимумов выше порога , где найденные локальные максимумы находятся на расстоянии не менее minDistBtwLocMax пикселейВозвращает фактическое количество найденных локальных максимумов.Обратите внимание, что он использует OpenCV minMaxLoc , чтобы найти глобальные максимумы.Он является "opencv-автономным", за исключением (простой в реализации) функции vdist , которая вычисляет (евклидово) расстояние между точками (r, c) и (row, col).

input - это одноканальная матрица CV_32F, а location - это 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;
}
3 голосов
/ 18 ноября 2012

На самом деле после того, как я разместил код выше, я написал лучший и очень быстрый. Код выше страдает даже для изображения 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;
        }
    }
}
2 голосов
/ 05 апреля 2011

Первый вопрос, на который нужно ответить, будет, по вашему мнению, «местным».Ответом может быть квадратное окно (скажем, 3x3 или 5x5) или круглое окно определенного радиуса.Затем вы можете отсканировать все изображение с окном, центрированным по каждому пикселю, и выбрать самое высокое значение в окне.

См. this для получения доступа к значениям пикселей в OpenCV.

1 голос
/ 07 марта 2017

Вот простой трюк.Идея состоит в том, чтобы расширить ядро ​​с отверстием в центре.После операции расширения каждый пиксель заменяется максимумом его соседей (в данном примере используется соседство 5 на 5), исключая исходный пиксель.

Mat1b kernelLM(Size(5, 5), 1u);
kernelLM.at<uchar>(2, 2) = 0u;
Mat imageLM;
dilate(image, imageLM, kernelLM);
Mat1b localMaxima = (image > imageLM);
1 голос
/ 24 июля 2015

Нашли простое решение.

В этом примере, если вы пытаетесь найти 2 результата функции matchTemplate с минимальным расстоянием друг от друга.

    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. Нарисуйте заполненный белый круг вокруг глобального минимума, используя минимальное расстояние между минимумами в качестве радиуса
  3. Найти другой минимум

Баллы можно сравнить друг с другом, чтобы определить, например, достоверность совпадения,

1 голос
/ 22 февраля 2013

Это очень быстрый метод.Он хранит найденные максимумы в векторе Points.

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; 
}
0 голосов
/ 12 февраля 2019

Чтобы найти больше, чем просто глобальный минимум и максимум, попробуйте использовать эту функцию из skimage:

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

Вы также можете параметрировать минимальное расстояние между пиками.И больше.Чтобы найти минимумы, используйте отрицательные значения (позаботьтесь о типе массива, хотя 255-image может помочь).

0 голосов
/ 09 марта 2015

Вы можете просмотреть каждый пиксель и проверить, является ли он локальным максимумом. Вот как бы я это сделал. Предполагается, что вход имеет тип 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));
}

Это решение не учитывает плато, поэтому оно не совсем то же самое, что и imregionalmax () в matlab *)

0 голосов
/ 05 апреля 2011

Я думаю, что вы хотите использовать

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

функция на вашем изображении

...