Simple image processing algorithm causes Processing to freeze - image

I've written an algorithm in Processing to do the following:
1. Instantiate a 94 x 2 int array
2. Load a jpg image of dimensions 500 x 500 pixels
3. Iterate over every pixel in the image and determine whether it is black or white then change a variable related to the array
4. Print the contents of the array
For some reason this algorithm freezes immediately. I've put print statements in that show me that it freezes before even attempting to load the image. This is especially confusing to me in light of the fact that I have written another very similar algorithm that executes without complications. The other algorithm reads an image, averages the color of each tile of whatever size is specified, and then prints rectangles over the region that was averaged with the average color, effectively pixelating the image. Both algorithms load an image and examine each of its pixels. The one in question is mostly different in that it doesn't draw anything. I was going to say that it was different for having an array but the pixelation algorithm holds all of the colors in a color array which should take up far more space than the int array.
From looking in my mac's console.app I see that there was originally this error: "java.lang.OutOfMemoryError: GC overhead limit exceeded". From other suggestions/sources on the web I tried bumping the memory allocation from 256mb to 4000mb (doing this felt meaningless because my analysis of the algorithms showed they should be the same complexity but I tried anyways). This did not stop freezing but changed the error to a combination of "JavaNativeFoundation error occurred obtaining Java exception description" and "java.lang.OutOfMemoryError: Java heap space".
Then I tried pointing processing to my local jdk with the hope of utilizing the 64 bit jdk over processing's built in 32 bit jdk. From within Processing.app/Contents I executed the following commands:
mv Java java-old
ln -s /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk Java
Processing would not start after this attempt with the following error populating my console:
"com.apple.xpc.launchd[1]: (org.processing.app.160672[13559]) Service exited with abnormal code: 1"
Below is my code:
First the noncompliant algorithm
int squareSize=50;
int numRows = 10;
int numCols = 10;
PFont myFont;
PImage img;
//33-126
void setup(){
size(500,500);
count();
}
void count(){
ellipseMode(RADIUS);
int[][] asciiArea = new int[94][2];
println("hello?");
img=loadImage("countingPicture.jpg");
println("image loaded");
for(int i=0; i<(500/squareSize); i++){
for(int j=0; j<(500/squareSize); j++){
int currentValue=i+j*numCols;
if(currentValue+33>126){
break;
}
println(i+", "+j);
asciiArea[currentValue][0]=currentValue+33;
asciiArea[currentValue][1]=determineTextArea(i,j,squareSize);
//fill(color(255,0,0));
//ellipse(i*squareSize,j*squareSize,3,3);
}
}
println("done calculating");
displayArrayContents(asciiArea);
}
int determineTextArea(int i, int j, int squareSize){
int textArea = 0;
double n=0.0;
while(n < squareSize*squareSize){
n+=1.0;
int xOffset = (int)(n%((double)squareSize));
int yOffset = (int)(n/((double)squareSize));
color c = img.get(i*squareSize+xOffset, j*squareSize+yOffset);
if(red(c)!=255 || green(c)!=255 || blue(c)!=255){
println(red(c)+" "+green(c)+" "+blue(c));
textArea++;
}
}
return textArea;
}
void displayArrayContents(int[][] arr){
int i=0;
println("\n now arrays");
while(i<94){
println(arr[i][0]+" "+arr[i][1]);
}
}
The pixelation algorithm that works:
PImage img;
int direction = 1;
float signal;
int squareSize = 5;
int wideness = 500;
int highness = 420;
int xDimension = wideness/squareSize;
int yDimension= highness/squareSize;
void setup() {
size(1500, 420);
noFill();
stroke(255);
frameRate(30);
img = loadImage("imageIn.jpg");
color[][] colors = new color[xDimension][yDimension];
for(int drawingNo=0; drawingNo < 3; drawingNo++){
for(int i=0; i<xDimension; i++){
for(int j=0; j<yDimension; j++){
double average = 0;
double n=0.0;
while(n < squareSize*squareSize){
n+=1.0;
int xOffset = (int)(n%((double)squareSize));
int yOffset = (int)(n/((double)squareSize));
color c = img.get(i*squareSize+xOffset, j*squareSize+yOffset);
float cube = red(c)*red(c) + green(c)*green(c) + blue(c)*blue(c);
double grayValue = (int)(sqrt(cube)*(255.0/441.0));
double nAsDouble = (double)n;
average=(grayValue + (n-1.0)*average)/n;
average=(grayValue/n)+((n-1.0)/(n))*average;
}
//average=discretize(average);
println(i+" "+j+" "+average);
colors[i][j]=color((int)average);
fill(colors[i][j]);
if(drawingNo==0){ //stroke(colors[i][j]); }
stroke(210);}
if(drawingNo==1){ stroke(150); }
if(drawingNo==2){ stroke(90); }
//stroke(colors[i][j]);
rect(drawingNo*wideness+i*squareSize,j*squareSize,squareSize,squareSize);
}
}
}
save("imageOut.jpg");
}

You're entering an infinite loop, which makes the println() statements unreliable. Fix the infinite loop, and your print statements will work again.
Look at this while loop:
while(i<94){
println(arr[i][0]+" "+arr[i][1]);
}
When will i ever become >= 94?
You never increment i, so its value is always 0. You can prove this by adding a println() statement inside the while loop:
while(i<94){
println("i: " + i);
println(arr[i][0]+" "+arr[i][1]);
}
You probably wanted to increment i inside the while loop. Or just use a for loop instead.

Related

How to access intensity of all the pixels of Image in openCV C++

For accessing single point, I am using this line of code and it works
int intensity = gray_image.at<uchar>(Point(100, 100));
However when I use this code to access all the pixels in image, it gives memory error,
for (int i = 0; i < gray_image.rows;i++)
{
for (int j = 0; j < gray_image.cols; j++) {
intensity += gray_image.at<uchar>(Point(i, j));
}
}
When I run above code, it does not give compile time error but gives memory exception. Where am I going wrong?
You can just skip the use of Point and do the following.
for (int i = 0; i < gray_image.rows;i++)
{
for (int j = 0; j < gray_image.cols; j++) {
intensity += gray_image.at<uchar>(i, j);
}
}
You're requesting a pixel (j,i) that doesn't exist. This wouldn't have been an error in a square image (where the number of rows = number of columns), but you're using a rectangular image.
The Mat::at function has multiple prototypes, the two that you're concerned with are:
C++: template<typename T> T& Mat::at(int i, int j)
C++: template<typename T> T& Mat::at(Point pt)
The documentation for Mat::at states that Point pt is defined as the Element position specified as Point(j,i), so you've effectively swapped your rows and columns.
The reason this happens is because the image is stored in a 1D array of pixels, and to get a pixel Point (r,c) is translated to p = r * image.cols + c;

Remove object from 2D Array- Processing

I'm creating a simple space invaders game. I'm looking to delete one of the invaders once they are hit by a bullet. The invaders are made up of a 2D array of images and I've tested the collision between the image and the bullet (in an ArrayList) and that works fine. So the game detects a collision, the next step is to delete the correct object that has been hit. I'm a little confused as to how to correctly correspond where the bullet hits to which object it has hit in the 2D array, and then deleting it from the Array and carrying on with the game.
Below is how I created the invader array in setup()
for(int i=0; i<2; i++){
for(int j=0; j<4; j++){
invArray[j][i]= new Taxi(taxiX, taxiY);
taxiX= taxiX+ 100;
}
taxiX=20;
taxiY= taxiY+ 140;
}
I then filled the 2D Array with images in draw()
for(int i=0; i<2; i++){
for(int j=0; j<4; j++){
invArray[j][i].update();
if(invArray[j][i].y>=600){
invArray[j][i].y= 0;
invArray[j][i].render();
}
}
}
You're using arrays which are fixed size.
In theory you might be able to use array helper functions like shorten() and expand(), but you really got watch your counters and array structure.
In practice, for a beginner, I would say this is error prone.
It might be simpler(but hackier) to set the array element of the hit invader to null,
then simply check if the invader is not null before test collisions/rendering/etc.
e.g. in draw():
for(int i=0; i<2; i++){
for(int j=0; j<4; j++){
if(invArray[j][i] != null){
invArray[j][i].update();
if(invArray[j][i].y>=600){
invArray[j][i].y= 0;
invArray[j][i].render();
}
}
}
}
Another option is to use an ArrayList which has a dynamic size.
e.g.
ArrayList<Taxi> invaders = new ArrayList<Taxi>();
In setup you'd do something similar:
for(int i=0; i<2; i++){
for(int j=0; j<4; j++){
invaders.add(new Taxi(taxiX, taxiY));
taxiX= taxiX+ 100;
}
taxiX=20;
taxiY= taxiY+ 140;
}
then in draw():
for(int i = 0 ; i < invaders.size(); i++){
Taxi t = invaders.get(i);
t.update();
if(t.y>=600){
t.y= 0;
t.render();
}
/*
if(YOUR_HIT_CONDITION_HERE){
invaders.remove(t);
}
*/
}
It's a bit tricky to go back and forth between 1D and 2D arrays/indexing at the beginning, but it's not that bad once you get the hand of it.
To convert from 2D x,y to 1D index:
int index = x + y * width;
(where x,y are you counters and width is the width of your grid (number of columns)).
The other way around, 1D index to 2D x,y:
int x = index % width;
int y = index / width;
Try to decouple the hit detection from removing elements from the arraylist, maybe using a flag and removing at the end on the draw loop. Use arraylist.size() as limit of the loop in one part of the code. Maybe that can solve your problem with hit detection, maybe you need a counter.

Matrix Text rain effect in Processing 3.3

I'm working on making a matrix text rain effect in Processing 3.3 as a simple starter project for learning the processing library and Java. My code so far:
class Symbol {
int x, y;
int switchInterval = round(random(2, 50));
float speed;
char value;
Symbol(int x, int y, float speed) {
this.x = x;
this.y = y;
this.speed = speed;
}
//Sets to random symbol based on the Katakana Unicode block
void setToRandomSymbol() {
if(frameCount % switchInterval == 0) {
value = char((int) random(0x30A0, 0x3100));
}
}
//rains the characters down the screen and loops them to the top when they
// reach the bottom of the screen
void rain() {
if(y <= height) {
y += speed;
}else {
y = 0;
}
}
}
Symbol symbol;
class Stream {
int totalSymbols = round(random(5, 30));
Symbol[] symbols = new Symbol[500];
float speed = random(5, 20);
//generates the symbols and adds them to the array, each symbol one symbol
//height above the one previous
void generateSymbols() {
int y = 0;
int x = width / 2;
for (int i = 0; i <= totalSymbols; i++) {
symbols[i] = new Symbol(x, y, speed);
symbols[i].setToRandomSymbol();
y -= symbolSize;
}
}
void render() {
for(Symbol s : symbols) {
fill(0, 255, 70);
s.setToRandomSymbol();
text(s.value, s.x, s.y);
s.rain();
}
}
}
Ok, so that was a lot of code, Let me explain my dilemma. The issue I'm having is that when I run the code I get a NullpointerException at the s.setToRandomSymbol(); method call in the for each loop in the render function. The weird part about this NullPointerException error and the part I'm not understanding is that it's being thrown on a method that doesn't take in any arguments that could be coming back empty, and the method itself is void, so it shouldn't be returning anything, right? Why is this returning Null and what did I do wrong to have it return this way?
First you come up with a random number betwen 5 and 30:
int totalSymbols = round(random(5, 30));
Then you create an array that holds 500 instances of your Symbol class:
Symbol[] symbols = new Symbol[500];
Note that this array holds 500 null values at this point.
Then you add a maximum of 30 instances of Symbol to your array:
for (int i = 0; i <= totalSymbols; i++) {
symbols[i] = new Symbol(x, y, speed);
Note that this array now holds at least 470 null values at this point.
Then you iterate over all 500 indexes:
for(Symbol s : symbols) {
s.setToRandomSymbol();
But remember that at least 470 of these indexes are null, which is why you're getting a NullPointerException.
Some basic debugging would have told you all of this. I would have started by adding a basic println() statement just before you get the error:
for(Symbol s : symbols) {
println("s: " + s);
s.setToRandomSymbol();
This would have showed you that you're iterating over null values.
Anyway, to fix your problem you need to stop iterating over your entire array, or you need to stop making room for indexes you never use.
In the future, please try to narrow your problem down to a MCVE before posting. Note that this much smaller example program shows your error:
String[] array = new String[10];
array[0] = "test";
for(String s : array){
println(s.length());
}

“cracking the coding interview(fifth edition)”: 9.10 box stacking

You have a stack of n boxes, with widths wi, heights hi, and depths
di. The boxes cannot be rotated and can only be stacked on top of one
another if each box in the stack larger than or equal to the box above
it in width, height, and depth. Implement a method to build the
tallest stack possible, where the height of a stack is the sum of the
heights of each box.
I know there are a couple of articles to talk about using dynamic programming to solve it. Since I'd like to do practice in writing recursion code, I wrote the following code:
const int not_possible = 999999;
class box{
public:
int width;
int depth;
int height;
box(int h=not_possible, int d=not_possible, int w=not_possible):
width(w), depth(d), height(h) {}
};
bool check_legal(box lower, box upper){
return (upper.depth<lower.depth) &&
(upper.height<lower.height) &&
(upper.width<lower.width);
}
void highest_stack(const vector<box>& boxes, bool* used, box cur_level, int num_boxes, int height, int& max_height)
{
if(boxes.empty())
return;
bool no_suitable = true;
for(int i = 0; i < num_boxes; ++i){
box cur;
if(!(*(used+i)) && check_legal(cur_level, boxes[i])){
no_suitable = false;
cur = boxes[i];
*(used+i) = true;
highest_stack(boxes, used, cur, num_boxes, height+cur.height, max_height);
*(used+i) = false;
no_suitable = true;
}
}
if(no_suitable){
cout << height << endl; //for debug
if(height > max_height)
max_height = height;
return;
}
}
I've tested it using a lot of examples. For example:
boxes.push_back(box(4,12,32));
boxes.push_back(box(1,2,3));
boxes.push_back(box(2,5,6));
highest_stack(boxes, used, cur, boxes.size(), 0, max_height);
In the function highest_stack, there is one line cout << height << endl; for output. If I comment no_suitable = true;
the output is: 1 2 4; 1 2; 1, 1 4;
if I don't comment no_suitable = true;
the output is: 1 2 4; 2 4; 4; 1 2; 2; 1; 1 4; 0
Both of them can give the correct result which is 7.
My question is:
(1) Can anyone help me verify my solution?
(2) Is there any more elegant recursive code for this problem?
I don't think my code is elegant.
Thanks
I would make a directed graph where the nodes are boxes and the edges go from a box to a box that can be put on top of it. Then I'd use use the longest path algorithm to find the solution.
Design the relation as a Set array of boxes.(Set[]) i.e. each position has a array of boxes.
Initialize each box with an index.
For each box check boxes that can be placed above the current box(box[i]) add it to the set in the set array i.e. set[i].add(box)
Run DFS with the boxes that can be placed above (the role of adjacent)
Maintain a marked[], count[] and boxTo[] arrays of boxes.
Go over the count array and find the largest value.
Traverse the way to the bottom box using the boxTo[] array.

Find local maxima in grayscale image using OpenCV

Does anybody know how to find the local maxima in a grayscale IPL_DEPTH_8U image using OpenCV? HarrisCorner mentions something like that but I'm actually not interested in corners ...
Thanks!
A pixel is considered a local maximum if it is equal to the maximum value in a 'local' neighborhood. The function below captures this property in two lines of code.
To deal with pixels on 'plateaus' (value equal to their neighborhood) one can use the local minimum property, since plateaus pixels are equal to their local minimum. The rest of the code filters out those pixels.
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);
}
}
Here's a simple trick. The idea is to dilate with a kernel that contains a hole in the center. After the dilate operation, each pixel is replaced with the maximum of it's neighbors (using a 5 by 5 neighborhood in this example), excluding the original pixel.
Mat1b kernelLM(Size(5, 5), 1u);
kernelLM.at<uchar>(2, 2) = 0u;
Mat imageLM;
dilate(image, imageLM, kernelLM);
Mat1b localMaxima = (image > imageLM);
Actually after I posted the code above I wrote a better and very very faster one ..
The code above suffers even for a 640x480 picture..
I optimized it and now it is very very fast even for 1600x1200 pic.
Here is the code :
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;
}
}
}
The following listing is a function similar to Matlab's "imregionalmax". It looks for at most nLocMax local maxima above threshold, where the found local maxima are at least minDistBtwLocMax pixels apart. It returns the actual number of local maxima found. Notice that it uses OpenCV's minMaxLoc to find global maxima. It is "opencv-self-contained" except for the (easy to implement) function vdist, which computes the (euclidian) distance between points (r,c) and (row,col).
input is one-channel CV_32F matrix, and locations is nLocMax (rows) by 2 (columns) CV_32S matrix.
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;
}
The first question to answer would be what is "local" in your opinion. The answer may well be a square window (say 3x3 or 5x5) or circular window of a certain radius. You can then scan over the entire image with the window centered at each pixel and pick the highest value in the window.
See this for how to access pixel values in OpenCV.
This is very fast method. It stored founded maxima in a vector of
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;
}
Found a simple solution.
In this example, if you are trying to find 2 results of a matchTemplate function with a minimum distance from each other.
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);
where
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;
}
The process is:
Find global Minimum using minMaxLoc
Draw a filled white circle around global minimum using min distance between minima as radius
Find another minimum
The the scores can be compared to each other to determine, for example, the certainty of the match,
To find more than just the global minimum and maximum try using this function from skimage:
http://scikit-image.org/docs/dev/api/skimage.feature.html#skimage.feature.peak_local_max
You can parameterize the minimum distance between peaks, too. And more. To find minima, use negated values (take care of the array type though, 255-image could do the trick).
You can go over each pixel and test if it is a local maxima. Here is how I would do it.
The input is assumed to be type 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());
}
}
}
}
}
For testing the code you can try this:
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));
}
This solution does not take plateaus into account so it is not exactly the same as matlab's imregionalmax()
I think you want to use the
MinMaxLoc(arr, mask=NULL)-> (minVal, maxVal, minLoc, maxLoc)
Finds global minimum and maximum in array or subarray
function on you image

Resources