二值化方法,中阈值二值化

时间:2012-01-13 17:02:08

标签: c++ c algorithm image

我正试图将图片二值化,首先当然是准备好了(灰度) 我的方法是找到灰度的最大值和最小值,然后找到中间值(这是我的阈值),然后迭代所有像素,我将当前值与阈值进行比较,如果灰度大于阈值,我把0放在一个矩阵中,或者放在其他放的1中。 但现在我正面临这个问题。通常我是用白色背景二值化图像,所以我的算法进一步基于这个特征。但是当我遇到黑色背景的图像时,一切都会崩溃,但我仍然可以清楚地看到数字(现在是0和1的开关位置) 我怎样才能解决这个问题,让我的程序更常见? 也许我最好找另一种二值化方式/

P.S。我找了一个可以理解的Otsu阈值方法的解释,但是我似乎没有为这种困难做好准备,或者我每次都找到非常复杂的解释,但是我不能用C写出来。如果有人能在这里找到,这太棒了。

很抱歉没有回答问题,只是没有看到他们 首先 - 代码

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;

然后遍历图像

        if (threshold<middle)
        {
            picture[x][y]=1;
            fprintf( fo,"1");
        } else {
            picture[x][y]=0;
            fprintf( fo,"0");
        }
    }
    fprintf( fo,"\n");
}
fclose(fo);  

所以我得到一个像这样的文件

  

000000000
  000001000
  000001000
  000011000
  000101000
  000001000
  000001000
  000001000
  000000000

在这里你可以看到一个例子。 enter image description here

然后我可以插入它,或者做其他事情(识别),这取决于零和一个。 但是,如果我切换颜色,数字将不相同。所以这种认可是行不通的。我想知道是否有一个可以帮助我的算法。

4 个答案:

答案 0 :(得分:1)

我会用另一种方法解决这个问题:

  • 计算图像的灰度值的累积直方图。
  • 用作此累积的像素值的阈值 达到图像总像素的一半。

算法如下:

  int bin [256];
  foreach pixel in image
       bin[pixelvalue]++;  
  endfor  // this computes the histogram of the image

  int thresholdCount = ImageWidth * ImageSize / 2;
  int count = 0;
  for int i = 0 to 255
    count = count + bin[i];
    if( count > thresholdCount)
         threshold = i;
         break; // we are done
    endif
  endfor

此算法不会计算累积直方图本身,而是使用图像直方图来执行我之前所说的。

答案 1 :(得分:1)

如果您的算法适用于白色背景但黑色背景无效,您只需要检测何时有黑色背景并反转值。如果假设背景值更常见,则可以简单地计算结果中的1和0的数量;如果0更大,则反转结果。

答案 2 :(得分:1)

我从来没有听说过Otsu的方法,但是我理解了一些维基百科页面,所以我会尝试简化它。

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的方法实际上是“最大化类间方差”,但我不理解那部分数学。

方差的概念是“相互之间的价值相差多远”。低差异意味着一切都很相似。高差异意味着价值相差很大。彩虹的变化非常高,颜色很多。 stackoverflow背景的方差为0,因为它都是完全白色的,没有其他颜色。差异计算或多或少像这样计算

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;
}

答案 3 :(得分:1)

您应该使用所有点的中位数作为阈值,而不是使用min和max的均值。一般来说,第k个百分位数(k =你想要的黑色点的百分比)更合适。

另一种解决方案是将数据集中到两个集群中。