2017-04-09 17:45:40 Carry_zwxb 阅读数 6067

图像二值化

二值图像,图像中只有两种颜色的信息,通常是黑色和白色,是将普通图像二值化后得到的图像 。图像二值化的作用是为了方便提取图像中的信
息。二值图像在进行计算机识别时可以增加识别效率。
比如 需要计算水面悬浮物的数量 就可以将一定面积的水拍成图片后二值化:
黑色为水 白色为悬浮物
然后通过计算机进行图像扫描 
如果是黑色 0 就继续扫描
如果是白色 1就改变变量 通过连续算法 得出一个悬浮物
二值化就是就是将一幅图像的所有像素点按照256灰阶分类,每个像素点表示一个灰阶,然后我们将高于某一灰阶像素(阈值)全部显示成白色,低于某一灰阶(阈值)的像素点显示成黑色。这样就完成了对一幅图像二值化处理。
    根据阈值选取的不同,二值化的算法分为固定阈值和自适应阈值。 比较常用的二值化方法则有:双峰法、P参数法、迭代法和OTSU法等。

OTSU法(大津法)

对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记假设图像的背景较暗,并且图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:
      ω0=N0/ M×N (1)
      ω1=N1/ M×N (2)
      N0+N1=M×N (3)
      ω01=1    (4)
      μ=ω0011 (5)
      g=ω00-μ)^211-μ)^2 (6)
将式(5)代入式(6),得到等价公式:
      g=ω0ω101)^2    (7) 这就是类间方差
采用遍历的方法得到使类间方差g最大的阈值T,即为所求。(遍历灰度级(0-255),得到是的g取最大值的灰度级就是阈值T。)

大津法求阈值代码:

//大津法求阈值
int otsu(const IplImage *src_image)  
{
    double sum = 0.0;
    double w0 = 0.0;
    double w1 = 0.0;
    double u0_temp = 0.0;
    double u1_temp = 0.0;
    double u0 = 0.0;
    double u1 = 0.0;
    double delta_temp = 0.0;
    double delta_max = 0.0;

    //src_image灰度级  
    int pixel_count[256] = { 0 };
    float pixel_pro[256] = { 0 };
    int threshold = 0;
    uchar* data = (uchar*)src_image->imageData;//src->imageData是指向一片数据区的地址
    //统计每个灰度级中像素的个数  
    for (int i = 0; i < src_image->height; i++)
    {
        for (int j = 0; j < src_image->width; j++)
        {
            pixel_count[(int)data[i * src_image->width + j]]++;//每个灰度级的像素数目
            sum += (int)data[i * src_image->width + j];//灰度之和
        }
    }
    cout << "平均灰度:" << sum / (src_image->height * src_image->width) << endl;
    //计算每个灰度级的像素数目占整幅图像的比例  
    for (int i = 0; i < 256; i++)
    {
        pixel_pro[i] = (float)pixel_count[i] / (src_image->height * src_image->width);
    }
    //遍历灰度级[0,255],寻找合适的threshold  
    for (int i = 0; i < 256; i++)
    {
        w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;
        for (int j = 0; j < 256; j++)
        {
            if (j <= i)   //背景部分  
            {
                w0 += pixel_pro[j];//背景像素比例
                u0_temp += j * pixel_pro[j];
            }
            else   //前景部分  
            {
                w1 += pixel_pro[j];//前景像素比例
                u1_temp += j * pixel_pro[j];
            }
        }
        u0 = u0_temp / w0;//背景像素点的平均灰度
        u1 = u1_temp / w1;//前景像素点的平均灰度
        //http://blog.163.com/yuyang_tech/blog/static/216050083201302113341762/
        delta_temp = (float)(w0 *w1* pow((u0 - u1), 2));//类间方差 g=w0*w1*(u0-u1)^2
        //当类间方差delta_temp最大时,对应的i就是阈值T
        if (delta_temp > delta_max)
        {
            delta_max = delta_temp;
            threshold = i;
        }
    }
    return threshold;
}

全部代码:

//图像的二值化   
#include <opencv2/opencv.hpp>  
using namespace std;

//大津法求阈值
int otsu(const IplImage *src_image)  
{
    double sum = 0.0;
    double w0 = 0.0;
    double w1 = 0.0;
    double u0_temp = 0.0;
    double u1_temp = 0.0;
    double u0 = 0.0;
    double u1 = 0.0;
    double delta_temp = 0.0;
    double delta_max = 0.0;

    //src_image灰度级  
    int pixel_count[256] = { 0 };
    float pixel_pro[256] = { 0 };
    int threshold = 0;
    uchar* data = (uchar*)src_image->imageData;//src->imageData是指向一片数据区的地址
    //统计每个灰度级中像素的个数  
    for (int i = 0; i < src_image->height; i++)
    {
        for (int j = 0; j < src_image->width; j++)
        {
            pixel_count[(int)data[i * src_image->width + j]]++;//每个灰度级的像素数目
            sum += (int)data[i * src_image->width + j];//灰度之和
        }
    }
    cout << "平均灰度:" << sum / (src_image->height * src_image->width) << endl;
    //计算每个灰度级的像素数目占整幅图像的比例  
    for (int i = 0; i < 256; i++)
    {
        pixel_pro[i] = (float)pixel_count[i] / (src_image->height * src_image->width);
    }
    //遍历灰度级[0,255],寻找合适的threshold
    for (int i = 0; i < 256; i++)
    {
        w0 = w1 = u0_temp = u1_temp = u0 = u1 = delta_temp = 0;
        for (int j = 0; j < 256; j++)
        {
            if (j <= i)   //背景部分  
            {
                w0 += pixel_pro[j];//背景像素比例
                u0_temp += j * pixel_pro[j];
            }
            else   //前景部分  
            {
                w1 += pixel_pro[j];//前景像素比例
                u1_temp += j * pixel_pro[j];
            }
        }
        u0 = u0_temp / w0;//背景像素点的平均灰度
        u1 = u1_temp / w1;//前景像素点的平均灰度
        //http://blog.163.com/yuyang_tech/blog/static/216050083201302113341762/
        delta_temp = (float)(w0 *w1* pow((u0 - u1), 2));//类间方差 g=w0*w1*(u0-u1)^2
        //当类间方差delta_temp最大时,对应的i就是阈值T
        if (delta_temp > delta_max)
        {
            delta_max = delta_temp;
            threshold = i;
        }
    }
    return threshold;
}


int main(int argc, char** argv)
{
    const char *pstrWindowsBinaryTitle = "二值图";
    const char *pstrWindowsSrcTitle = "原图";
    IplImage *g_pGrayImage = NULL;//灰度图
    IplImage *g_pBinaryImage = NULL;//二值图片
    // 从文件中加载原图  
    IplImage *pSrcImage = cvLoadImage("qrcode.jpg", CV_LOAD_IMAGE_UNCHANGED);
    if (pSrcImage ==NULL) {
        cout << "Can not load images" << endl;
        return -1;
    }
    // 转为灰度图  
    g_pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
    cvCvtColor(pSrcImage, g_pGrayImage, CV_BGR2GRAY);

    // 创建二值图  
    g_pBinaryImage = cvCreateImage(cvGetSize(g_pGrayImage), IPL_DEPTH_8U, 1);
    //大律法求的阈值
    int pos = otsu(g_pGrayImage);
    // 转为二值图  
    cvThreshold(g_pGrayImage, g_pBinaryImage, pos, 255, CV_THRESH_BINARY);//pos 是阈值
    // 显示原图  
    cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);
    cvShowImage(pstrWindowsSrcTitle, pSrcImage);
    // 创建二值图窗口  
    cvNamedWindow(pstrWindowsBinaryTitle, CV_WINDOW_AUTOSIZE);
    // 显示二值图  
    cvShowImage(pstrWindowsBinaryTitle, g_pBinaryImage);
    cout << "阈值是:"<<pos << endl;
    cvWaitKey(0);
    cvDestroyWindow(pstrWindowsSrcTitle);
    cvDestroyWindow(pstrWindowsBinaryTitle);
    cvReleaseImage(&pSrcImage);
    cvReleaseImage(&g_pGrayImage);
    cvReleaseImage(&g_pBinaryImage);
    return 0;
}

运行结果:
原图:
这里写图片描述
二值图:
这里写图片描述
这里写图片描述

2018-02-11 23:31:26 qq_34445388 阅读数 1401

大津法(OTSU)是一种确定图像二值化分割阈值的算法,由日本学者大津于1979年提出。从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大
     ω0=N0/ M×N (1)
原理:

对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记为g。

假设图像的背景较暗,并且图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:
 
      ω1=N1/ M×N (2)
      N0+N1=M×N (3)
      ω0+ω1=1    (4)
      μ=ω0*μ0+ω1*μ1 (5)
      g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6)
将式(5)代入式(6),得到等价公式:
      g=ω0ω1(μ0-μ1)^2    (7) 这就是类间方差
采用遍历的方法得到使类间方差g最大的阈值T,即为所求。
C#代码
/大津法/
/*最大类间方差法是由日本学者大进展之于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU。
* 它是按图像的灰度特性,将图像分成背景和目标两部分。背景和目标之间的类间方差越大,说明构成图像的两部分
* 的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致两部分差别变小。因此,使类间方差最大的分
* 割意味着错分概率最小。对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图
* 像的比例记为\omega_0 ,,其平均灰度\mu_0;背景像素点数占整幅图像的比例为\omega_1,其平均灰度为\mu_1。
* 图像的总平均灰度记为\mu,类间方差记为g。假设图像的背景较暗,并且图像的大小为M*N,图像中像素的灰度值小
* 于阈值T的像素个数记作N_0,像素灰度大于阈值T的像素个数记作N_1,则有:*/
public static int GetOSTUThreshold(int[] HistGram)
{
int X, Y, Amount = 0;
int PixelBack = 0, PixelFore = 0, PixelIntegralBack = 0, PixelIntegralFore = 0, PixelIntegral = 0;
double OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma; // 类间方差;
int MinValue, MaxValue;
int Threshold = 0;

        for (MinValue = 0; MinValue < 256 && HistGram[MinValue] == 0; MinValue++) ;//获取最小灰度的值
        for (MaxValue = 255; MaxValue > MinValue && HistGram[MinValue] == 0; MaxValue--) ;//获取最大灰度的值
        if (MaxValue == MinValue) 
            return MaxValue;          // 图像中只有一个颜色             
        if (MinValue + 1 == MaxValue) 
            return MinValue;      // 图像中只有二个颜色

        for (Y = MinValue; Y <= MaxValue; Y++)
        {
            Amount += HistGram[Y];        //  像素总数
        }

        PixelIntegral = 0;
        for (Y = MinValue; Y <= MaxValue; Y++)
        {
            PixelIntegral += HistGram[Y] * Y;//灰度值总数
        }
        SigmaB = -1;
        for (Y = MinValue; Y < MaxValue; Y++)
        {
            PixelBack = PixelBack + HistGram[Y];    //前景像素点数
            PixelFore = Amount - PixelBack;         //背景像素点数
            OmegaBack = (double)PixelBack / Amount;//前景像素百分比
            OmegaFore = (double)PixelFore / Amount;//背景像素百分比
            PixelIntegralBack += HistGram[Y] * Y;  //前景灰度值
            PixelIntegralFore = PixelIntegral - PixelIntegralBack;//背景灰度值
            MicroBack = (double)PixelIntegralBack / PixelBack;//前景灰度百分比
            MicroFore = (double)PixelIntegralFore / PixelFore;//背景灰度百分比
            Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);//g
            if (Sigma > SigmaB)//遍历最大的类间方差g
            {
                SigmaB = Sigma;
                Threshold = Y;
            }
        }
        return Threshold;
    }
2018-08-18 09:54:24 u012366767 阅读数 2795

    大津法(OTSU法)是由大津于1979 年提出的,对图像I,记T为前景与背景的分割阈值,前景点数占图像比例为w0,平均灰度为u0;背景点数占图像比例为w1,平均灰度为u1。从最小灰度值到最大灰度值遍历T,当T使得方差值δ=w0*w1*(u0-u1)*(u0-u1)最大时,T即为分割的最佳阈值。

  • 测试程序
clear; clc;
I=imread('rice.tif');
subplot(1, 3, 1)
imshow(I);
xlabel('(a) 原始图像');
level = graythresh(I);      %使用MATLAB 函数计算阈值
BW = im2bw(I, level);     
subplot(1, 3, 2)
imshow(BW);
xlabel('(b) graythresh');
disp(['graythresh 计算灰度阈值:', num2str(level*255)]);
T = Otsu(double(I));     %使用大津法计算阈值
disp(['大津法计算灰度阈值:', num2str(T)])
BW = im2bw(I, T/255);
%阈值分割
subplot(1, 3, 3)
imshow(BW);
xlabel('(c) 大津法');
  • 测试结果

  •  源码
function ThreshValue = Otsu(Imag)
% 大津法计算阈值
% 输入:
%    Imag:二维数组,数值表示灰度;
% 输出:
%    ThreshValue:阈值
iMax = max(Imag(:));              % 最大值
iMin = min(Imag(:));               % 最小值
T = iMin:iMax;                        % 灰度值范围
Tval = zeros(size(T));               % 方差
[iRow, iCol] = size(Imag);        % 数据维度大小
imagSize = iRow*iCol;            % 像素点数量
% 遍历灰度值,计算方差
for i = 1 : length(T)
    TK = T(i);
    iFg = 0;          % 前景
    iBg = 0;          % 背景
    FgSum = 0;    % 前景总数
    BgSum = 0;    % 背景总数
    for j = 1 : iRow
        for k = 1 : iCol
            temp = Imag(j, k);
            if temp > TK
                iFg = iFg + 1;      % 前景像素点统计
                FgSum = FgSum + temp;
            else
                iBg = iBg + 1;      % 背景像素点统计
                BgSum = BgSum + temp;
            end
        end
    end
    w0 = iFg/imagSize;      % 前景比例
    w1 = iBg/imagSize;     % 背景比例
    u0 = FgSum/iFg;         % 前景灰度平均值
    u1 = BgSum/iBg;        % 背景灰度平均值
    Tval(i) = w0*w1*(u0 - u1)*(u0 - u1);     % 计算方差
end
[~, flag] = max(Tval);             % 最大值下标
ThreshValue = T(flag);

 

2018-05-27 13:06:13 Reason_Lee 阅读数 4819

关于大津阈值法的定义这里不再赘述,直接给出实现代码:

%image_graythresh.m

function level =image_graythresh(I)

%该函数作用:使用大津阈值法找出图像分割的阈值

[M,N] = size(I);

%预定义划分的两组,分别为C1、C2。预定义level,用于存放不同划分时的类间方差

C1 = zeros(1,M*N);

C2 = zeros(1,M*N);

level =zeros(1,M*N);

I = double(I);         %将原图像uint8型转换为double型

%求出原图像灰度最大、最小值

min_value =min(min(I));                                              

max_value =max(max(I));

%定义C1、C2、level的下标,同时起统计个数的作用

columns1 = 1;

columns2 = 1;

columns3 = 1;

%开始遍历

for k =min_value:max_value - 1

    for i = 1:M

        for j = 1:N

            if I(i,j)<=k

                C1(columns1)  = I(i,j);        %得分组C1

                columns1 = columns1 + 1;

            else

                C2(columns2)  = I(i,j);        %得到分组C2

                columns2 = columns2 + 1;

            end

        end

    end

       %得到C1、C2的概率

    posibility1 = (columns1-1) / (M*N);

    posibility2 = 1 - posibility1;

       %得到C1、C2的均值

%由于预定义个数大于实际个数,因而求均值时不记录多余的零

    ave1 = sum(C1)/numel(find(C1~=0));        

    ave2 = sum(C2)/numel(find(C2~=0));        

       %得到类间方差,存放在leve中

    std = posibility1*posibility2*(ave1 -ave2)^2;

    level(columns3) = std;

    columns3 = columns3 + 1;

    columns1 = 1;

    columns2 = 1;

    C1 = zeros(1,M*N);

    C2 = zeros(1,M*N);

end

%得到最大类间方差下标从而得到阈值

[~, i] =max(level);

level = min_value +i - 1;

%对阈值做归一化处理

level = level/255;

end

%DIP_exp4_1.m

%该m文件调用Matlab自带函数graythresh以及自编函数image_graythresh对图像进行阈值分割

clc;clear;closeall;

I =imread('cameraman.tif');

level =graythresh(I);            %得到大津阈值法阈值(Matlab自带函数)

J = im2bw(I,level);              %实现图像二值化,即非黑即白

level1 =image_graythresh(I);              %得到大津阈值法阈值(自编函数)

K =im2bw(I,level1);                   

subplot(221),imshow(I);title('原图像');

subplot(222),imshow(J);title('大津法阈值分割后图像');

subplot(223),imshow(I);title('原图像');

subplot(224),imshow(K);title('自编大津法阈值分割后图像');

运行DIP_exp4_1.m文件得结果如下:

 

2018-03-19 10:15:39 qq_34445388 阅读数 1616

下面是前面我弄的C#版本大津法二值化化转化为C语言的。
/***************************************************************
* 河南科技大学一队
*
* 函数名称:void GetHistGram(uint8_t Image[Height][Width])
* 功能说明:获取图像的灰度信息
* 参数说明:
* 函数返回:void
* 修改时间:2018年3月7日
* 备 注:
***************************************************************/
void GetHistGram(uint8_t Image[Height][Width])
{
int X,Y;
for (Y = 0; Y < 256; Y++)
{
HistGram[Y] = 0; //初始化灰度直方图
}
for (Y = 0; Y < Image_Height; Y++)
{
for (X = 0; X < Image_Width; X++)
{
HistGram[Image[Y][X]]++; //统计每个灰度值的个数信息
}
}
}
/***************************************************************
* 河南科技大学一队
*
* 函数名称:uint8_t OSTUThreshold()
* 功能说明:大津法获取图像阈值
* 参数说明:
* 函数返回:uint8_t 阈值
* 修改时间:2018年3月7日
* 备 注:
***************************************************************/
uint8_t OSTUThreshold()
{
int16_t Y;
uint32_t Amount = 0;
uint32_t PixelBack = 0;
uint32_t PixelIntegralBack = 0;
uint32_t PixelIntegral = 0;
int32_t PixelIntegralFore = 0;
int32_t PixelFore = 0;
double OmegaBack, OmegaFore, MicroBack, MicroFore, SigmaB, Sigma; // 类间方差;
int16_t MinValue, MaxValue;
uint8_t Threshold = 0;

for (MinValue = 0; MinValue < 256 && HistGram[MinValue] == 0; MinValue++) ;        //获取最小灰度的值
for (MaxValue = 255; MaxValue > MinValue && HistGram[MinValue] == 0; MaxValue--) ; //获取最大灰度的值

if (MaxValue == MinValue) 
{
    return MaxValue;          // 图像中只有一个颜色    
}
if (MinValue + 1 == MaxValue) 
{
    return MinValue;      // 图像中只有二个颜色
}

for (Y = MinValue; Y <= MaxValue; Y++)
{
    Amount += HistGram[Y];        //  像素总数
}

PixelIntegral = 0;
for (Y = MinValue; Y <= MaxValue; Y++)
{
    PixelIntegral += HistGram[Y] * Y;//灰度值总数
}
SigmaB = -1;
for (Y = MinValue; Y < MaxValue; Y++)
{
    PixelBack = PixelBack + HistGram[Y];    //前景像素点数
    PixelFore = Amount - PixelBack;         //背景像素点数
    OmegaBack = (double)PixelBack / Amount;//前景像素百分比
    OmegaFore = (double)PixelFore / Amount;//背景像素百分比
    PixelIntegralBack += HistGram[Y] * Y;  //前景灰度值
    PixelIntegralFore = PixelIntegral - PixelIntegralBack;//背景灰度值
    MicroBack = (double)PixelIntegralBack / PixelBack;//前景灰度百分比
    MicroFore = (double)PixelIntegralFore / PixelFore;//背景灰度百分比
    Sigma = OmegaBack * OmegaFore * (MicroBack - MicroFore) * (MicroBack - MicroFore);//g
    if (Sigma > SigmaB)//遍历最大的类间方差g
    {
        SigmaB = Sigma;
        Threshold = Y;
    }
}
return Threshold;

}大津法在KV58运行的时间
2ms的运行时间还行吧,可能还需要优化,我还没有成功开启FPU,希望有人分享一下。

没有更多推荐了,返回首页