user1131662 - 1 year ago 103

C Question

I'm trying to binarise a picture, firstly of course having it prepared(grayscaling)

My method is to find the maximum and minimum values of grayscale, then find the middle value(which is my threshold) and then, iterating over all the pixels I compare the current one with a threshold and if the grayscale is larger than the threshold, I put 0 in a matrix, or for the others I put 1.

But now I'm facing the problem. In common I'm binarising images with white background, so my algorithm is further based on this feature. But when I meet an image with black background everything collapses, but I still can see the number clearly(now 0's and 1's switch places)

How can i solve this problem, make my program more common?

Maybe I'd better look for another ways of binarization/

P.S. I looked for an understandable explanation of Otsu threshold method, but it seems either I'm not prepared for this way of difficulty or I find very complicated explanations every time, but I can't write it in C. If anyone could hrlp here, it'd be wonderful.

Sorry for not answering the questions, just didn't see them

Firstly - the code

`for (int y=1;y<Source->Picture->Height;y++)`

for (int x=1;x<Source->Picture->Width;x++)

{

unsigned green = GetGValue(Source->Canvas->Pixels[x][y]);

unsigned red = GetRValue(Source->Canvas->Pixels[x][y]);

unsigned blue = GetBValue(Source->Canvas->Pixels[x][y]);

threshold = (0.2125*red+0.7154*green+0.0721*blue);

if (min>threshold)

min=threshold;

if (max<threshold)

max = threshold;

}

middle = (max+min)/2;

Then iterating through the image

`if (threshold<middle)`

{

picture[x][y]=1;

fprintf( fo,"1");

} else {

picture[x][y]=0;

fprintf( fo,"0");

}

}

fprintf( fo,"\n");

}

fclose(fo);

So I get a file, something like this

000000000

000001000

000001000

000011000

000101000

000001000

000001000

000001000

000000000

Here you can see an example of one.

Then I can interpolate it, or do something else (recognize), depending on zero's and one's.

But if I switch the colors, the numbers won't be the same. So the recognition will not work. I wonder if there's an algoritm that can help me out.

Answer Source

I've never heard of Otsu's method, but I understand some of the wikipedia page so I'll try to simplify that.

```
1 Count how many pixels are at each level of darkness.
2 "Guess" a threshold.
3 Calculate the variance of the counts of darkness less than the threshold
4 Calculate the variance of the counts of darkness greater than the threshold
5 If the variance of the darker side is greater, guess a darker threshold,
else guess a higher threshold.
Do this like a binary search so that it ends.
6 Turn all pixels darker than threshold black, the rest white.
```

Otsu's method is actually "maximizing inter-class variance", but I don't understand that part of the math.

The concept of variance, is "how far apart are the values from each other." A low variance means everything is similar. A high variance means the values are far apart. The variance of a rainbow is very high, lots of colors. The variance of the background of stackoverflow is 0, since it's all perfectly white, with no other colors. Variance is calculated more or less like this

```
double variance(unsigned int* counts, int size, int threshold, bool above) {
//this is a quick trick to turn the "upper" into lower, save myself code
if (above) return variance(counts, size-threshold, size-threshold, false);
//first we calculate the average value
unsigned long long atotal=0;
unsigned long long acount=0;
for(int i=0; i<threshold; ++i) {
atotal += counts[i]*i //number of px times value
acount += counts[i];
}
//finish calculating average
double average = double(atotal)/count;
//next we calculate the variance
double vtotal=0;
for(int i=0; i<threshold; ++i) {
//to do so we get each values's difference from the average
double t = std::abs(i-average);
//and square it (I hate mathmaticians)
vtotal += counts[i]*t*t;
}
//and return the average of those squared values.
return vtotal/count;
}
```