数字图像处理——大津法实现图像二值化

来源:互联网 发布:2016淘宝直通车 编辑:程序博客网 时间:2024/05/17 06:46

图像二值化

二值图像,图像中只有两种颜色的信息,通常是黑色和白色,是将普通图像二值化后得到的图像 。图像二值化的作用是为了方便提取图像中的信息。二值图像在进行计算机识别时可以增加识别效率。比如 需要计算水面悬浮物的数量 就可以将一定面积的水拍成图片后二值化:黑色为水 白色为悬浮物然后通过计算机进行图像扫描 如果是黑色 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;}

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

0 0
原创粉丝点击