minMaxLoc maximum value confusion - image-processing

I have been using minMaxLoc to compute the maximum value of the data obtained by running a laplacian filter over a grayscale image. My simple intention is to roughly estimate the sharpness. I encountered a confusing situation, which I have discussed here.
The max value obtained from the minMaxLoc function was like 1360, 1456,450 etc for a set of images.
Laplacian(src_gray,dst,ddepth,kernel_size,scale,delta,BORDER_DEFAULT);
minMaxLoc(dst,&min,&estimate,&minLoc,&maxLoc,noArray());//Estimate is the max value
Now I just tried to compute the average to have a better idea of the spread of the sharpness in the image. Note that DST is the Mat variable holding data from the Laplacian.
Size s = dst.size();
rows = s.height;
cols = s.width;
total = 0;
max = 0;
for(int k=0;k<rows;k++)
{
for(int l=0;l<cols;l++)
{
total = total + abs(dst.at<int>(k,l));
}
}
average = total/(rows*cols);
There are 2 baffling results I obtained. The average value I obtained, was not only greater than the max value obtained from minMaxLoc, but also was at times negative, when tried over a set of images. sample Average values where 22567, at times -25678.
The occurrence of negative was even more baffling as am using the abs() to get the absolute value of the laplacian results.
To get a proper understanding, I calculated the max value by myself and then the average values :
Size s = dst.size();
rows = s.height;
cols = s.width;
total = 0;
max = 0;
for(int k=0;k<rows;k++)
{
for(int l=0;l<cols;l++)
{
if(abs(dst.at<int>(k,l))>max)
{
max = abs(dst.at<int>(k,l));
}
total = total + abs(dst.at<int>(k,l));
}
}
average = total/(rows*cols);
surprisingly, I found the max value to be in 8 digits.
This is why I got confused. What is the max value given from the minMaxLoc function? And why is the abs() in total, not working and why am I getting -ve average values.?
Please forgive me if am missing something in the code, but this is slightly confusing me. Thanks for your help in advance.

I think you should use .at< uchar > instead of int (considering image to be grayscale) otherwise the value will overflow!

Typically images have 8 bit images. So chances are, that you are accessing the pixels of your image using the wrong method. And in this case, the values you read from the matrix are wrong.
To check if you are working with a single channel integer matrix use
dst.type() == CV_32SC1 .
To check for a 8 bit matrix use
dst.type() == CV_8SC1 .
If you are actually having such an 8 bit integer matrix, you need to use .at<uchar> to access the pixels.
The reason your total variable is negative even though you only added positive values to it is probably due to an integer overflow. You can avoid this by using a long int for total.

Related

WebGL attempt to access out of range vertices in attribute 2 error

I know this question has been asked quite a bit, but none of the solutions really fit my case. I am looking to add a second type of object to the canvas with the code shown below. I know I didn't provide much but its a quick start. Just ask for more if you think you have a hunch. This code below is in my render function.
So far I have checked that
I have enough vertices in my points array
I have enough normal vectors in my normals array
I have enough texture coordinates in my texCoords array
There are no mismatches between the vectors added when creating my terrain and my propeller.
The terrain renders just fine with the texture, lighting and all but,I am unable to get the propeller to render. I get the error I listed above. I have added multiple objects to canvases before and never run into an error like this.
//----------------------------------------- Draw Terrain ------------------------------------
var i = 0;
for(var row=0-dimension; row<dimension; row+=3){
for(var col=0-dimension; col<dimension; col+=3, i++){
var mv = mult(viewer, mult(translate(row, -1, col), mult(scale[i],rot[i])));
gl.uniformMatrix4fv(modelViewLoc, false, flatten(mv));
gl.uniformMatrix3fv(normalLoc, false, flatten(normalMatrix(mv, true)));
gl.drawArrays( gl.TRIANGLES, 0, index);
}
}
//----------------------------------------- Draw Propeller ------------------------------------
mv = mult(viewer, mult( translate(-2.1, -2.9, -.2), scalem(4,5,5)));
gl.uniformMatrix4fv(modelViewLoc, false, flatten(mv));
gl.uniformMatrix3fv(normalLoc, false, flatten(normalMatrix(mv, true)));
gl.drawArrays( gl.TRIANGLES, propellerStart, points.length);
Is there any way i can use the "Attribute 2" in the error message to track down the variable giving me this issue?
Appreciate the help!
What part don't you understand? The error is clear, whatever buffer you have attached to attribute 2 is not big enough to handle the propellerStart, points.length draw request.
So first thing is figure out which attribute is attribute 2. Do this by printing out your attribute locations. Is your points, normals, or texcoords?
You should already be looking them up somewhere with gl.getAttribLocation so print out those values, find out which one is #2.
Then go look at the size of the buffer you attached to that attribute. To do that somewhere you would have called.
gl.bindBuffer(gl.ARRAY_BUFFER, someBuffer);
gl.vertexAttribPointer(locationForAttribute2, size, type, normalize, stride, offset);
So we know it's someBuffer from the above code. We also need to know size, type, stride, and offset
Somewhere else you filled that buffer with data using
gl.bindBuffer(gl.ARRAEY_BUFFER, someBuffer);
gl.bufferData(gl.ARRAY_BUFFER, someData, ...);
So you need to find the size of someData.
sizeOfBuffer = someData.length * someData.BYTES_PER_ELEMENT
Let's it's a 1000 element Float32Array so it someData.length is 1000 and someData.BYTES_PER_ELEMENT is 4 therefore sizeOfBuffer is 4000.
Using all of that you can now check if your buffer is too small. (note: we already know it's too small since the browser told us so but if you want know how to compute it yourself)
Let's say size is 3, type is gl.FLOAT, stride is 32, offset is 12 (note: I personally never use anything but stride = 0 and offset = 0)
Let's say points.length = 50
numPoints = points.length;
bytesPerElement = size * 4; // because a gl.FLOAT is 4 bytes
realStride = stride === 0 ? bytesPerElement : stride;
bytesNeeded = realStride * (numPoints - 1) + bytesPerElement;
bytesNeeded in this case is (64 * 49) + 12 = 3148
So now we know how many bytes are needed. Does are buffer have enough data? We'll when you called draw you passed in an offset propellerStart. Let's assume it's 900 and there's the offset in the attribute so.
bufferSizeNeeded = offset + propellerStart + bytesNeeded
so bufferSizeNeeded = 12 + 900 + 3148 which is 4060. Since 4060 is > sizeOfBuffer which was 4000 you're going to get the error you got.
In any case the point is really it's up to you to figure out which buffer is used by attribute #2, then go look at why your buffer is too small. Is your offset to drawArrays wrong? Is your stride too big? Is your offset wrong in vertexAttribPointer (it's in number of bytes not number of units). Do you put the wrong size (1,2,3,4). Do you mis-calculate the number of points?

objective c float increment wrong value

I have a query regarding floating value increment in loop.
I have following code
float add = 1.02f;
float counter = 0.0f;
for (int i = 0; i < 20; i++) {
counter += add;
NSLog(#"%f",counter);
}
While executing this loop I am getting following result
1.020000
2.040000
3.060000
4.080000
5.100000
6.120000
7.140000
8.160000
9.180000
10.200001
11.220001
12.240002
13.260002
14.280003
15.300003
16.320004
17.340004
18.360004
19.380005
20.400005
Here is expected result
1.020000
2.040000
3.060000
4.080000
5.100000
6.120000
7.140000
8.160000
9.180000
10.200000
11.220000
12.240000
13.260000
14.280000
15.300000
16.320000
17.340000
18.360000
19.380000
20.400000
Why i am getting some floating point in loop without adding it.
I need to loop more then 1000 times. And I want the value in float variable.
Thanks in advance.
This happens because float cannot represent the values that you have with exact precision. There are two simple ways of fixing this:
Represent the number as 100 times the target value, and use integers - 1.02 becomes 102, 2.04 becomes 204, and so on.
Use NSDecimalNumber to represent your numbers - Unlike float, NSDecimalNumber can represent all your values with full precision.
Here is how to implement the first approach:
int add = 102;
int counter = 0;
for (int i = 0; i < 20; i++) {
counter += add;
NSLog(#"%d.%d", counter/100, counter%100);
}
Here is how to implement the second approach:
NSDecimalNumber add = [NSDecimalNumber decimalNumberWithString:#"1.02"];
NSDecimalNumber counter = [NSDecimalNumber zero];
for (int i = 0; i < 20; i++) {
counter = [counter decimalNumberByAdding:add];
NSLog(#"%#", counter);
}
Why i am getting some floating point in loop without adding it.
Because float is a binary type that doesn't represent decimal values exactly. Rather than trying to explain completely and correctly, let me point you to the well-known paper What Every Computer Scientist Should Know About Floating Point Arithmetic.
Floating point number representations in computers are approximations, they are not exact. Sometimes you end up trying to display a number that can't be exactly represented in the computer's floating point number implementation, so it gives you an approximation. Also you get small arithmetic errors from repeated multiplications, additions, etc. of floating point numbers. The best you can do is to use doubles, which have more precision than floats do. In special circumstances, you could also represent your data in a different format and just change how you display it to the user to fit what they expect. For example, when working with dollars and cents, you could just store a total as a number of cents (which would be only an integer) and then format it to be shown as dollars and cents correctly for the user. There's no floating point rounding issues happening then.
Floating point numbers use four bytes = 32 bits.
1 bit for sign
8 bits for exponent
23 bits for mantissa
Precision: The number of decimal digits precision is calculated via number_of_mantissa_bits * Log10(2). Thus ~7.2 and ~15.9 for single and double precision respectively.
That's why you start to see rounding errors on the 7th digit
Source link.

OpenCV - Image histogram value of pixel

What I am doing is trying to implement an Skin Probability Maps algorithm for skin detection in OpenCV.
I've stuck in a place where I should compare SkinHistValue / NonSkinHistValue probability of each pixel with Theta threshold according to http://www.cse.unsw.edu.au/~icml2002/workshops/MLCV02/MLCV02-Morales.pdf and this tutorial http://www.morethantechnical.com/2013/03/05/skin-detection-with-probability-maps-and-elliptical-boundaries-opencv-wcode/
My problems lies in calculating the coords for hist value:
From the tutorial:
calcHist(&nRGB_frame,1,channels,mask,skin_Histogram,2,histSize,ranges,uniform,accumulate);
calcHist(&nRGB_frame,1,channels,~mask,non_skin_Histogram,2,histSize,ranges,uniform,accumulate);
Calculates the histograms. Than i normalize them.
And after that:
for (int i=0; i<nrgb.rows; i++) {
int gbin = cvRound((nrgb(i)[1] - 0)/range_dist[0] * hist_bins[0]);
int rbin = cvRound((nrgb(i)[2] - low_range[1])/range_dist[1] * hist_bins[1]);
float skin_hist_val = skin_Histogram.at<float>(gbin,rbin);
};
Where nrgb is my image, and im trying to get skin_hist_value for that. But the gbin and rbin are probably calculated wrong and it throws an exception (i run outside of array?) when it comes to
skin_Histogram.at<float>(gbin,rbin);
I have totally no idea how to calculate it correctly. Any help?

How to find an average of N number of x values in the draw function

I'm using open frameworks and opencv to track blobs on a webcam. I'm getting the x value of the blob centroid and tracking it. The problem is, it jumps around allot, I'm wondering if there is a better way to compute the average position over a certain number of frames and use that number it's all being computed in the draw() function.
void testApp::draw(){
ofVec2f centroid = contourFinder.blobs[0].centroid;
int width = ofGetWidth();
float pct = (float)centroid.x / (float)width;
float totFrame = fingerMovie.getTotalNumFrames ();
float gotFrame = totFrame * pct;
}
you should create a loop for N frames, sum all coordinates you get, then divide by N.
I am not experienced with ofx but there must be a function to get next frame.
After loop ends, move camera to the average coordinate and re-initialize the loop.

Zero padding / median filtering

I'm trying to implement median filtering using image j .
I am having trouble with the zero padding as it adds extra zeros to the bottom and far left of the picture.
This is what I have done so far, if you guys can help me out:
Dialog.create("9x9 median filtering");
Dialog.addMessage("9x9 median filtering");
Dialog.show();
setBatchMode(true);
median_filter_9();
setBatchMode("exit and display");
// Produce the 9x9 median image
function median_filter_9()
{
width = getWidth();
height= getHeight();
//if you want to apply this median filter to 16bit
depth = bitDepth();
nBin= pow(2, depth);
//nBin hold max gray intensity value
filteHisto = newArray(nBin);
//filteHisto = newArray(255);
fiveBYFive = newArray(81);
//this is what i used for middle position of array to get median
middlePos = round(81/2);
//-3, -3 will get you position 0,0 of a 9x9 matrix if you start in the middle
for(j=-2;j<width-2;j++){
for(i=-2;i<height-2;i++){
z=0;
for(r=0;r<9;r++){
for(c=0;c<9;c++){
//Extend outside image boundaries using zero padding.
//error here: adds extra to bottom and farleft of picture
if(j+r<0||j+r>=width||i+c<0||i+c>=height){
fiveBYFive[z]=0;
z++;
}else{
v = getPixel(j+r,i+c);
fiveBYFive[z]= v;
z++;
}
}
}
//sort the array to find median
Array.sort(fiveBYFive);
median = fiveBYFive[middlePos];
setPixel(j, i, median);
}
updateDisplay();
}
}
One problem you're seeing at the edges of your image is because you are padding your 9x9 window with zeroes ok, but you still take the median value as the middle of the 81 item window.
So, for example, in the first column of the image, you zero-pad at least 36 elements (more at the top and bottom), which means that you only need to find 4 or 5 more zero pixels in the image to make the median element zero.
The easiest fix is to adjust your median element's index (initialised to 81/2 on each iteration) upward according to how many zeroes you added, or just count how many non-zero pixels you used and then find the median mid-way through that range in your sorted array (taking account of sort order).
In this way, you take the median value of the actual pixels you found and ignore the padded zeroes.
Probably, you missed changing your code from the original 5x5 to 9x9, because the start/end indices are in any case wrong and should be
for(j=-4;j<width;j++){
for(i=-4;i<height;i++){
The other possible source of confusion later is with this line, where it looks like you've confused width and height
if(j+r<0||j+r>=width||i+c<0||i+c>=height)
If j is the column index and i is the row index, it should be
if(j+c<0||j+c>=width||i+r<0||i+r>=height)
Although for a square window this doesn't actually make any difference in practice.

Resources