S0.4 二值图与阈值化

目录

  • 二值图的定义
  • 二值图的应用
  • 阈值化
  • 二值化/阈值化方法
    • 1,无脑简单判断

      • opencv3函数threshold()实现
    • 2,Otsu算法(大律法或最大类间方差法)
      • OpenCV3 纯代码实现大津法
      • OpenCV3 threshold算法调用Otsu阈值化
      • 改进版本
      • OpenCV3函数adaptiveThreshold实现自适应阈值

二值图的定义

二值图是一种特殊的灰度图,即每个像素点要么是白(0),要么是黑(255)

无论是灰度图还是二值图都是用阈值化的知识。

二值图的应用

图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。

阈值化

二值化只是阈值化的一个特例。

阈值化就是设定一个阈值,使超过或者低于该阈值的像素变成某一个给定值。

二值化/阈值化方法

1,无脑简单判断

通过搜索灰度图每个像素,判断灰度值是否大于127 [(255+0)/2 = 127.5]

//二值化 一

#include<opencv2/highgui/highgui.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>

using namespace cv;

int main()
{
    Mat srcImage = imread("images/favorite/Lena.jpg", 0);
    Mat dstImage;

    srcImage.copyTo(dstImage);

    int rows = srcImage.rows;
    int cols = srcImage.cols * srcImage.channels();

    for(int i = 0; i < rows; i++)
    {
        uchar* data = dstImage.ptr<uchar>(i);
        for(int j = 0; j < cols; j++)
        {
            if(data[j] > 127)
                data[j] = 255;
            else
                data[j] = 0;
        }

    }
    imshow("Lena", dstImage);

    waitKey(30000);
    return 0;
}

opencv3函数threshold()实现

threshold是阈值化方法,也可以大材小用一下做二值化。

在imgproc.hpp中可以看到形参

//看函数参数
//CV_EXPORTS_W 就是double

CV_EXPORTS_W double threshold(
    InputArray src,   //第一个参数表示输入图像,必须为单通道灰度图。
    OutputArray dst,  //第二个参数表示输出图像,为单通道黑白图。
    double thresh,    //第三个参数表示阈值,例如127
    double maxval,    //第四个参数表示最大值,例如255
    int type          //第五个参数表示运算方法。
);

在OpenCV的imgproc\types_c.h中可以找到运算方法的定义。

/** Threshold types */
enum
{
    CV_THRESH_BINARY      =0,  /**< value = value > threshold ? max_value : 0       */
    CV_THRESH_BINARY_INV  =1,  /**< value = value > threshold ? 0 : max_value       */
    CV_THRESH_TRUNC       =2,  /**< value = value > threshold ? threshold : value   */
    CV_THRESH_TOZERO      =3,  /**< value = value > threshold ? value : 0           */
    CV_THRESH_TOZERO_INV  =4,  /**< value = value > threshold ? 0 : value           */
    CV_THRESH_MASK        =7,
    CV_THRESH_OTSU        =8, /**< use Otsu algorithm to choose the optimal threshold value;
                                 combine the flag with one of the above CV_THRESH_* values */
    CV_THRESH_TRIANGLE    =16  /**< use Triangle algorithm to choose the optimal threshold value;
                                 combine the flag with one of the above CV_THRESH_* values, but not
                                 with CV_THRESH_OTSU */
};

实例:

#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/core/core.hpp>

using namespace cv;

int main()
{
    Mat srcImage = imread("images/favorite/Lena.jpg", 0);
    Mat dstImage;

    threshold(srcImage, dstImage, 127, 255, 0);

    imshow("Lena", dstImage);

    waitKey(30000);
    return 0;
}

2,Otsu算法(大律法或最大类间方差法)

大津法由大津(日本学者,名叫OTSU)于1979年提出,对图像Image,记t为前景与背景的分割阈值,前景点数占图像比例为w0,平均灰度为u0;背景点数占图像比例为w1,平均灰度为u1。图像的总平均灰度为:\(u=w0*u0+w1*u1\)。

我们的目标是从最小灰度值到最大灰度值找到一个合适的分隔值t(即阈值),遍历\(t\),当\(t\)使得值\(g=w0*(u0-u)^2+w1*(u1-u)^2\)最大时t即为分割的最佳阈值。对大津法可作如下理解:该式实际上就是类间方差值,阈值t分割出的前景和背景两部分构成了整幅图像,而前景取值u0,概率为 w0,背景取值u1,概率为w1,总均值为u,根据方差的定义即得该式。

因方差是灰度分布均匀性的一种度量,方差值越大,说明构成图像的两部分差别越大, 当部分目标错分为背景或部分背景错分为目标都会导致两部分差别变小,因此使类间方差最大的分割意味着错分概率最小。

直接应用大津法计算量较大,因此我们在实现时采用了等价的公式\(g=w0*w1*(u0-u1)^2\)。(把u代入g得到)

OpenCV3 纯代码实现大津法

#include <opencv2/opencv.hpp>
#include <iostream>
#include <cstdio>

using namespace std;
using namespace cv;

int main()
{
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst;

    int best_t = 1;
    long long best_g = 0;

    for(int t = 1; t < 255; t++)
    {
        int u0 = 0;
        int u1 = 0;

        int num_of_u0 = 0;
        int num_of_u1 = 0;
        long long sum_of_u0 = 0;
        long long sum_of_u1 = 0;

        for (int i = 0; i < src.rows; i++)
        {
            for (int j = 0; j < src.cols; j++)
            {
                int pixel = src.at<uchar>(i, j);
                if(pixel >= t)
                {
                    num_of_u0++;
                    sum_of_u0+=pixel;
                }
                else
                {
                    num_of_u1++;
                    sum_of_u1+=pixel;
                }
            }
        }

        if(num_of_u0 == 0 || num_of_u1 == 0)
            continue;

        u0 = sum_of_u0/num_of_u0;
        u1 = sum_of_u1/num_of_u1;

        long long g = num_of_u0*num_of_u1/(src.rows*src.cols)*(u1-u0)*(u1-u0);
//        long long g = num_of_u0/(src.rows*src.cols)*num_of_u1/(src.rows*src.cols)*(u1-u0)*(u1-u0);精度太低,全是0
//        long long g = num_of_u0*num_of_u1*(u1-u0)*(u1-u0);精度太高,容易产生误差

        if(g > best_g)
        {
            best_g = g;
            best_t = t;
        }
    }

    printf("best_g:%d\nbest_t:%d\n", best_g, best_t);

    threshold(src, dst, best_t, 255, 0);

    imshow("Lena0", dst);

    waitKey(0);
    return 0;
}

当然你可以基于直方图,这样只需要遍历0-255像素就ok了。

OpenCV3 threshold算法调用Otsu阈值化

#include <opencv2/opencv.hpp>
#include <iostream>
#include <cstdio>

using namespace std;
using namespace cv;

int main()
{
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst;

    threshold(src, dst, 100, 127, THRESH_OTSU);

    imshow("Lena0", dst);
    waitKey(0);

    return 0;
}

经过实验我们可以得到:当thresholdType为THRESH_OTSU时,thresh参数没有用了。

改进版本

OTSU 算法可以说是自适应计算单阈值(用来转换灰度图像为二值图像)的简单高效方法。下面的代码最早由 Ryan Dibble提供,此后经过多人Joerg.Schulenburg, R.Z.Liu 等修改,补正。

算法对输入的灰度图像的直方图进行分析,将直方图分成两个部分,使得两部分之间的距离最大。划分点就是求得的阈值。

OpenCV3函数adaptiveThreshold实现自适应阈值

在imgproc.hpp中找到adaptiveThreshold的形参

CV_EXPORTS_W void adaptiveThreshold(
    InputArray src,                //输入图像
    OutputArray dst,               //输出图像
    double maxValue,               //最大值,一般为255
    int adaptiveMethod,            //0或1
    int thresholdType,
    int blockSize,                 //bxb区域,必须奇数
    double C                       //减去的常量
);
adaptiveMethod参数 效果
ADAPTIVE_THRESH_MEAN_C bxb区域平均值-C
ADAPTIVE_THRESH_GAUSSIAN_C bxb区域高斯加权平均值-C
#include <opencv2/opencv.hpp>
#include <iostream>
#include <cstdio>

using namespace std;
using namespace cv;

int main()
{
    Mat src = imread("images/favorite/Lena.jpg", 0);
    Mat dst0, dst1;

    adaptiveThreshold(src, dst0, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 41, 0.0);
    adaptiveThreshold(src, dst1, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 41, 0.0);

    imshow("src", src);
    imshow("mean", dst0);
    imshow("gaussian", dst1);

    waitKey(0);
    return 0;
}

原文地址:https://www.cnblogs.com/juicebox/p/9688932.html

时间: 2024-09-29 11:19:08

S0.4 二值图与阈值化的相关文章

用 Python 通过马尔可夫随机场(MRF)与 Ising Model 进行二值图降噪

前言 这个降噪的模型来自 Christopher M. Bishop 的 Pattern Recognition And Machine Learning (就是神书 PRML……),问题是如何对一个添加了一定椒盐噪声(Salt-and-pepper Noise)(假设噪声比例不超过 10%)的二值图(Binary Image)去噪. 原图 添加 10% 椒盐噪声的图 建模 下文中的数学表示: yi:噪声图中的像素 xi:原图中的像素,对应噪声图中的 yi 既然噪声图是从原图添加噪声而来,我们拥

使用OpenCV查找二值图中最大连通区域

http://blog.csdn.net/shaoxiaohu1/article/details/40272875 使用OpenCV查找二值图中最大连通区域 标签: OpenCVfindCoutours 2014-10-19 22:31 2802人阅读 评论(0) 收藏 举报  分类: 图像与OpenCV(15)  版权声明:本文为shaoxiaohu原创文章,欢迎转载,请注明出处,谢谢. 上一篇博文中介绍了matlab查找最大连通区域的方法,OpenCV函数中也有类似的函数与之对应,findC

C语言实现将彩色BMP位图转化为二值图

CTF做了图片的隐写题,还没有形成系统的认识,先来总结一下BMP图的组成,并通过将彩色图转为二值图的例子加深下理解. 只写了位图二进制文件的格式和代码实现,至于诸如RGB色彩和调色板是什么的一些概念就不啰嗦了. BMP位图文件格式 BMP文件由文件头.位图信息头.调色板和图形数据四部分组成,真彩色图是没有调色板的.每部分的具体结构在代码中具体列出并解释. 结构体的对齐 定义文件头部各结构体时要注意对齐的问题,至于什么是结构体对齐,请看这篇博文,写的很详细http://www.cnblogs.co

c语言实现灰度图转换为二值图

将上篇得到的灰度图转换为二值图,读取像素数据,低于某一值置0,否则设置为255,为得到更好的效果不同图片应采用不同的值 1 /* 2 2015年6月2日11:16:22 3 灰度图转换为二值图 4 blog:http://www.cnblogs.com/wd1001/ 5 */ 6 #include<stdio.h> 7 #include<malloc.h> 8 #include<stdlib.h> 9 /* 10 位图头结构 11 */ 12 #pragma pack

zw&#183;准专利&#183;高保真二值图细部切分算法

zw·准专利·高保真二值图细部切分算法     高保真二值图细部切分算法,是中国字体协会项目的衍生作品.     说准专利算法,是因为对于图像算法的标准不了解,虽然报过专利,但不是这方面的,需要咨询专业的专利顾问.     原型是用opencv+python实现的,因为Halcon,对于协会的设计师,门槛太高,所以,特意设计了一套opencv+python的live-cd,解压即可,无需配置. 中国传统书法,有很多飞白.泼墨的手法,产生了很多小孔.孤点,从图像学角度,这些都是细小的感染区. 传统

Kinect 2.0 + OpenCV 显示深度数据、骨架信息、手势状态和人物二值图

1.前言 Kinect 2.0实测比第一代性能提升非常多! 本来想简单地找个教程复制黏贴一下,居然还没有人写过C++版的Kinect 2.0教程,自己摸索了一下,现在把结果拿出来和大家分享. 实现的功能是:深度数据(Depth Data),骨架信息(Body Data),手势状态(Hand State)和人物二值图(就是图1的那个东西,微软官方称法是Body Index Data)的提取和显示. 效果如下: 图1 骨架信息,人物二值图和手势状态 图2 深度信息 2.安装 Kinect 2.0的安

opencv二值图反色处理

反色处理指的是:如果原先图像的背景是白色,而目标是黑色的话:经过反色处理后,背景变为白色,目标变为黑色. 在opencv中,对于二值图的反色处理有两种方法: 之前处理好的二值图的定义为:Mat  binaryImg; 1.直接使用opencv中的函数: //! inverts each bit of array (dst = ~src) CV_EXPORTS_W void bitwise_not(InputArray src, OutputArray dst, InputArray mask=n

自己写的二值图的轮廓图算法

在线PS随便画了一个四边形如图: 代码: #include "stdafx.h" #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/video/background_segm.hpp> #include "iostream&

OpenCV二值图求最大连通区域算法(广度优先算法 BFS)

#include <iostream> #include <opencv2\opencv.hpp> #include <vector> #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <windows.h> #include <math.h> #include <queue> using namespace std;usin