it-swarm.com.ru

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

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

17
Durin

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

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

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

1
fabrizioM

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

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

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);
    }
}
5
killogre

Следующий листинг - это функция, похожая на «imregionalmax» Матлаба. Выглядит максимально nLocMax локальные максимумы выше пороггде найденные локальные максимумы не меньше minDistBtwLocMax пикселей друг от друга. Возвращает фактическое количество найденных локальных максимумов. Обратите внимание, что он использует OpenCV minMaxLoc найти глобальные максимумы. Это «opencv-автономный» за исключением (легко реализуемой) функции 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;
}

3
Marcelo

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

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

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

2
peakxu

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

В этом примере, если вы пытаетесь найти 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
Mich

Вот простой трюк. Идея состоит в том, чтобы расширить ядро ​​с отверстием в центре. После операции расширения каждый пиксель заменяется максимальным количеством его соседей (в данном примере используется соседство 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
eitanrich

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

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

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

0
DomTomCat

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

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
Dasdranagon

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