OTSU算法 -- 三通道阈值分割

来源:互联网 发布:太平洋官方软件 编辑:程序博客网 时间:2024/06/04 08:39

otsu 应用

依据像素值的权重,分离图像前景和背景。

otsu 算法确定图像前景和背景阈值

// 关键之处是处理像素值的权重占比// Otsu algorithmint Otsu(const Mat src){    int height = src.rows;    int width  = src.cols;    int size   = height * width;    unsigned char* data = src.data;    // histogram        long histogram[256] = { 0 };    // pixel gray value count    for (int i = 0; i < height; i++)    {        unsigned char* p = data + i * src.step;        for (int j = 0; j < width; j++)        {            histogram[int(*p++)]++;        }    }    /*     * sum0:前景的灰度总和     sum1:背景灰度总和     * cnt0:前景像素的总个数   cnt1:背景像素的总个数     * w0:  前景像素个数占整幅图像像素的比例     * w1:  背景像素个数占整幅图像像素的比例     * u0:  前景的平均灰度     u1: 背景的平均灰度     * variance: 类间方差     */    long sum0 = 0, sum1 = 0;    long cnt0 = 0, cnt1 = 0;    double w0 = 0, w1 = 0;    double u0 = 0, u1 = 0;    double variance = 0;    /*     * u: 整幅图像的总平均灰度     * maxVariance: 最大类间方差     */    int i, j;    double u = 0;    double maxVariance = 0;    // 目标阈值    int threshold = 0;    // 依次遍历每个像素    for (i = 1; i < 256; i++)    {        // 初始化        sum0 = 0;  sum1 = 0;        cnt0 = 0;  cnt1 = 0;        u0 = 0;  u1 = 0;        w0 = 0;  w1 = 0;        // 前景        for (j = 0; j < i; j++)        {            cnt0 += histogram[j];            sum0 += (long)(j * histogram[j]);        }        // u0:前景平均灰度  w0:前景像素点数量占全部像素点的比例        u0 = (double)(sum0 * 1.0 / cnt0);        w0 = (double)(cnt0 * 1.0  / size);        // 背景        for (j = i; j <= 255; j++)        {            cnt1 += histogram[j];            sum1 += (long)(j * histogram[j]);        }        // u1:背景平均灰度  w1:背景像素点数占全部像素点的比例        u1 = (double)(sum1 * 1.0 / cnt1);        w1 = 1 - w0;        // u:图像平均灰度   variancn:类间方差        //u = u0 * w0 + u1 * w1;        variance = w0 * w1 *  (u0 - u1) * (u0 - u1);        if (variance > maxVariance)        {            maxVariance = variance;            threshold = i;        }    }    return (threshold);}

分别根据r,g,b三通道的阈值分离图像,再merge(r,g,b)

  1. 图像分离可采用 opencv split()函数,将Mat格式图像转换为vector格式的图像。
  2. 合并r, g, b则采用相反的操作,使用opencv merge()函数,将vector格式的图像合并成Mat格式的完整图像。
// splite input imagevoid ThresholdByOtsu(const Mat src,  Mat & Dst){    // split Mat src to Vector src_v    vector<Mat>src_v;    split(src, src_v);    imshow("r_src", src_v[0]);    imshow("g_src", src_v[1]);    imshow("b_src", src_v[2]);    // save origin image r, g, b    imwrite((g_kOutputPath + "r_src.tiff"), src_v[0]);    imwrite((g_kOutputPath + "g_src.tiff"), src_v[1]);    imwrite((g_kOutputPath + "b_src.tiff"), src_v[2]);    // get threshold    int threshold_r, threshold_g, threshold_b = 0;    threshold_r = Otsu(src_v[0]);    threshold_g = Otsu(src_v[1]);    threshold_b = Otsu(src_v[2]);    // output r, g, b threshold after otsu.    cout << "after otsu:" << endl;    cout << "------------------" << endl;    cout << "threshold_r:" << threshold_r << endl;    cout << "threshold_g:" << threshold_g << endl;    cout << "threshold_b:" << threshold_b << endl;    // 二值化    vector<Mat>out_v;    threshold(src_v[0], src_v[0], threshold_r, 255, CV_THRESH_BINARY);    threshold(src_v[1], src_v[1], threshold_g, 255, CV_THRESH_BINARY);    threshold(src_v[2], src_v[2], threshold_b, 255, CV_THRESH_BINARY);    // save r, g, b image after otsu    imwrite((g_kOutputPath + "r_otsu.tiff"), src_v[0]);    imwrite((g_kOutputPath + "g_otsu.tiff"), src_v[1]);    imwrite((g_kOutputPath + "b_otsu.tiff.tiff"), src_v[2]);    // merge three chanels image with r_otsu, g_otsu, b_otsu    merge(src_v, Dst);    // clear    src_v.clear();}

Example

输入图像: http://sipi.usc.edu/database/download.php?vol=misc&img=4.2.04
1. 美丽的Lena女士


2. 原图像 R, G, B 三通道分离,依次输出r,g,b单通道图像如下



3. Otsu() 分别计算图像阈值得到r, g, b单通道阈值

channel threshold r 113 g 102 b 162



3. 原图像R,G,B三通道,otsu阈值二值化,依次输出如下图像



4. 将二值化的R,G,B三通道进行merge操作,输出OTSU的最终结果

编译环境

dev version os window10 image library opencv3.2.0 compiler Virtual Studio 2015

More

see more https://github.com/Guguant/anypapers/tree/master/sga