图像二值化方法及适用场景分析(OTSU Trangle 自适应阈值分割)

2023-05-16

图像二值化

    • 应用场景
    • 二值图像定义
    • 阈值获取的方法
      • 手动阈值法
      • 自动阈值法
        • 灰度均值法
        • 基于直方图均值法
          • OTSU
          • Triangle
        • 自适应均值阈值分割方法
    • 总结
      • 参考文献

应用场景

二值图像处理与分析在机器视觉与机器人视觉中非常重要,涉及到非常多的图像处理相关的知识,常见的二值图像分析包括轮廓分析、对象测量、轮廓匹配与识别、形态学处理与分割、各种形状检测与拟合、投影与逻辑操作、轮廓特征提取与编码等。二值化方法是一种应用广泛的图像分割方法,恰当的二值化结果对于 文档图像分析、OCR以及医学图像中对 DNA 点阵图像分析等起着至关重要的作用。

通常可以分为全局二值化和局部二值化方法两类,前者将一个固定的阈值应用于整幅图像,简单易行,但在光照不均匀的条件难以应用,如 Otsu 法;后者则针对图像的不同部分采用不同的阈值来解决光照问题,其阈值实际是一个随像素变化的曲面。本文将着重介绍如何从一幅图像中获取有目的性的二值图像。

二值图像定义

二值图像就是只有黑白两种颜色表示的图像,在数字上用0 表示黑色(0),1表示白色(255) 。在实际场景中,二值图像的获得一般需要经过如下过程。

灰度化
二值化
彩色图像
灰度图像
二值图像

从灰度图像到二值图像,本质上是对数据的二分类分割,所以很多数据处理的方法都可以使用,但是图像是特殊类型的数据,它有很多限制条件,决定了只有一些合适的方法才会取得比较好的效果。这些算法的最主要的一个任务就是寻找合理的分割阈值T。

阈值获取的方法

二值化分割可分为手动阈值分割和自动阈值分割,前者是根据整幅图像的特征进行分析进而确定,目前常用的方法包括手动阈值法及自动阈值法,

  • 手动阈值分割
  • 自动阈值分割
    • 基于灰度图均值的自动分割
    • 基于直方图的自动分割
      • OTSU(直方图出现双峰)
      • Triangle(直方图出现单峰)
    • 自适应阈值分割

下面以下图为例,对上述几种阈值分割方法进行分析和对比。
图一  输入图像

手动阈值法

该阈值T需要人为给定,取值范围为0-255,常规的定义为灰度图像上某点像素值 P(x, y) > T ? 255 : 0,除此之外,还有几种变式如下:

  • P(x, y) > T ? 0: 255,表示像素值大于阈值时,为0,否则为255;
  • P(x, y) > T ? T : 0 ,表示像素值大于阈值时,为0,否则为255;
  • P(x, y) > T ? T : P(x, y) ,表示像素值大于阈值时,为阈值,否则为原像素;
  • P(x, y) > T ? P(x, y) : 0 ,表示像素值大于阈值时,为原像素,否则为0;
  • P(x, y) > T ? 0 : P(x, y) ,表示像素值大于阈值时,为0,否则为原像素;

图解表示为:
在这里插入图片描述
在OPENCV中有相应的API可以使用,可以根据任务需求,选择不同的type值。

函数原型:
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type) 
参数含义: 
InputArray src    -输入原图像,需为灰度图像 
OutputArray dst   -输出图像 
double thresh     -阈值大小 (即阈值T)
double maxval     -最大值 (一般指定为255,也可指定为其他数值)
int type          -阈值模式(很重要,它决定这个函数的变式,0为常规型的,1-4均为变式) 

其中,type可以取0-4中的任一个值,对应的含义如下: 
0: THRESH_BINARY -当前点值大于阈值时,取Maxval,否则设置为01: THRESH_BINARY_INV -当前点值大于阈值时,设置为0,否则设置为Maxval; 
2: THRESH_TRUNC -当前点值大于阈值时,设置为阈值,否则不改变; 
3: THRESH_TOZERO -当前点值大于阈值时,不改变,否则设置为04: THRESH_TOZERO_INV -当前点值大于阈值时,设置为0,否则不改变。 

设阈值T = 120 ,type分别取0-4,可以得到以下结果:
1) type = 0
在这里插入图片描述

2)type = 1
在这里插入图片描述
3)type = 2
在这里插入图片描述
4) type = 3
在这里插入图片描述
5) type = 4
在这里插入图片描述

总结:
要使用手动阈值分割法获取理想的二值图,很关键的两个点是Ttype,需要多次尝试,以经验加枚举的方式挨个测试,最终确定一个合适的阈值。但此阈值仅适用于这一特定的场景,光照等因素的改变可能导致阈值不再适用。所以,此方法适用与场景单一、固定的场合,如工业车间、流水线等机器人视觉上。

自动阈值法

可以看出手阈值法需要多次尝试且应用场景单一,局限性较大。自动阈值分割方法可以根据环境进行阈值调整,适应性也更广。自动阈值分割法包括基于灰度均值的自动分割、基于直方图的自动分割和自适应阈值分割。下面分别对这几种方法进行分析:

灰度均值法

此方法较为简单,即将灰度图像的像素均值作为阈值T,得到的图像如下图所示,关键代码为:

	Mat src = imread("E:/images/aaa.jpg");
	Mat gray, gray_mean;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	meanStdDev(gray, gray_mean, mat_stddev);
    double m;
    m = mat_mean.at<double>(0, 0);
	binary = Mat::zeros(src.size(), CV_8UC1);
	int height = gray.rows;
	int width = gray.cols;
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			int pv = gray.at<uchar>(row, col);
			if (pv > m) {
				binary.at<uchar>(row, col) = 255;
			}
			else {
				binary.at<uchar>(row, col) = 0;
			}
		}
	}
	imshow("binary", binary);
	waitKey(0);
	return 0;
}

在这里插入图片描述

基于直方图均值法

此方法是根据直方图的特征进行阈值的选择和求取,在此之前,我们先获取下灰度图像的直方图分布图如图所示,关键代码为如下,这个过程在使用直方图均值法是不需要的,为了说明问题,单独显示下直方图:

       // 本段代码,在二值化中不需要,opencv中API中已包括,只是为了说明问题
	    Mat src = imread("E:/images/aaa.jpg");
	    Mat gray, gray_mean;
	    cvtColor(src, gray, COLOR_BGR2GRAY);
        //定义变量
        Mat dstHist;
        int dims = 1;
        float hranges[] = {0, 256};
        const float *ranges[] = {hranges}; // 这里需要为const类型
        int size = 256;
        int channels = 0; //计算图像的直方图
        calcHist(&gray, 1, &channels, Mat(), dstHist, dims, &size, ranges);
        Mat dstImage(size, size, CV_8U, Scalar(0)); //获取最大值和最小值
        double minValue = 0;
        double maxValue = 0;
        minMaxLoc(dstHist,&minValue, &maxValue, 0, 0); // 在cv中用的是cvGetMinMaxHistValue //绘制出直方图 //saturate_cast函数的作用即是:当运算完之后,结果为负,则转为0,结果超出255,则为255。
        int hpt = saturate_cast<int>(0.9 * size);
        for(int i = 0; i < 256; i++)
           {
              float binValue = dstHist.at<float>(i); // 注意hist中是float类型 
              int realValue = saturate_cast<int>(binValue * hpt/maxValue);
              line(dstImage,Point(i, size - 1),Point(i, size - realValue),Scalar(255));
           }
        imshow("一维直方图", dstImage);
}

在这里插入图片描述

可以从直方图分布图中看出,灰度多集中在靠近白色的一端,即只有一个明显的峰值,整体图像偏白。在opencv中基于直方图获取阈值并分割的功能,实现方式仍然是使用threshold函数,只是将type类型声明为THRESH_OTSU或THRESH_RRIANGLE即可。

函数原型:
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type) 
参数含义: 
InputArray src    -输入原图像,需为灰度图像 
OutputArray dst   -输出图像 
double thresh     -阈值大小,取 0 
double maxval     -最大值,取 255
int type          -阈值模式

其中,type可以取0-4中的任一个值,对应的含义如下: 
THRESH_BINARY | THRESH_OTSU 使用最大类间差分法获取阈值,然后再用该阈值进行二分 ; 
THRESH_BINARY | THRESH_TRIANGLE 使用三角法获取阈值,然后再用该阈值进行二分 ; 

OTSU算法对直方图有两个峰,中间有明显波谷的直方图对应图像二值化效果比较好,而对于只有一个单峰的直方图对应的图像分割效果没有双峰的好。

OTSU

OTSU的是通过计算类间最大方差来确定分割阈值的阈值选择算法,它的原理是不断地求前景和背景的类件方差:
在这里插入图片描述
在这里插入图片描述
如图所示,在灰度直方图中,先将0,1,2三个灰度作为背景,求取背景类内方差,将3,4,5作为前景,求取前景类内方差。然后根据两个类内方差,求取两个类间的类间方差。如此,依次求 0 与1,2,3,4,5之间,0,1,与2,3,4,5之间,…直到遍历完整个灰度组数。然后找类间方差最大的,则灰度分界也就找到了,此分界即为所要的阈值。在opencv中实现的方法如下:

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

using namespace cv;
using namespace std;

int main()
{
	Mat src = imread("E:/images/aaa.jpg");
	Mat gray, binary;
	cvtColor(src, gray, COLOR_BGR2GRAY);
    double T = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
    cout<<"threshold : %.2f\n"<< T <<endl;
    imshow("binary", binary);
    waitKey(0);
    return 0;
}

此时返回的阈值T = 157,即采用阈值157进行常规的二值分割,其分割的结果如下图所示:
在这里插入图片描述

Triangle

在但是有时候图像的直方图只有一个波峰,这个时候使用TRIANGLE方法寻找阈值是比较好的一个选择。

在这里插入图片描述

OpenCV中TRIANGLE算法使用只需要在 threshold函数的type类型声明THRESH_TRIANGLE即可。程序如下:

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

using namespace cv;
using namespace std;

int main()
{
	Mat src = imread("E:/images/aaa.jpg");
	Mat gray, binary;
	cvtColor(src, gray, COLOR_BGR2GRAY);
    double T = threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);
    cout<<"threshold : %.2f\n"<< T <<endl;
    imshow("binary", binary);
    waitKey(0);
    return 0;
}

此时返回的阈值T = 215,即采用阈值215进行常规的二值分割,其分割的结果如下图所示:
在这里插入图片描述

自适应均值阈值分割方法

OpenCV中的自适应阈值算法主要是基于均值实现,根据计算均值的方法不同分为box-filter模糊均值与高斯模糊均值,其一般步骤为:

灰度化
高斯模糊或box模糊
原图像-均值图像
自适应分割
彩色图像
灰度图像
均值图像
差值图像
二值图像

在opencv中有相关的API可以调用,如下:

void cv::adaptiveThreshold( InputArray src, OutputArray dst, double maxValue, int 
adaptiveMethod, int thresholdType, int blockSize, double C ) 

其中:

参数取值
blockSize取值必须是奇数,如果输入图像较大,取127左右,对于小图像取25左右
C取值多少与效果有很大关系,不能取高,也不能取低,一般取值在10/15/25
adaptiveMethodADAPTIVE_THRESH_GAUSSIAN_C = 1 , ADAPTIVE_THRESH_MEAN_C = 0
thresholdTypeTHRESH_BINARY 二值图像 = 原图 – 均值图像 > -C ? 255 : 0 ,THRESH_BINARY_INV 二值图像 = 原图 – 均值图像 > -C ? 0 : 255
    Mat src = imread("E:/images/aaa.jpg");
    Mat gray, binary;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    adaptiveThreshold(gray, binary, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 25, 10);
    imshow("binary", binary);
    waitKey(0);
    return 0;

运行效果如下:
在这里插入图片描述

总结

在实际二值化应用中,阈值要根据实际场景和目标物特征进行选择,如果对于光照不均匀的场合应该选择自适应阈值分割,对于直方图出现明显双峰的应该选择OTSU,对与直方图出现单峰的应该选择Triangle,双峰还是单峰的分辨,可以看偏黑或偏白的占比,也可尝试运行两种方法,观察效果。

参考文献

[1]: 贾志刚,《OpenCV Android开发实战》

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图像二值化方法及适用场景分析(OTSU Trangle 自适应阈值分割) 的相关文章

随机推荐

  • Visual Studio 2022 C++ CLR 的艰难除 Bug

    请看下面一段代码 xff1a 运行结果 xff1a 这是一个Button xff0c 要用到这段代码是因为字符串出了问题 xff1a 肯定是我写的类出问题了 xff0c 便是我在控制台下测试是正常的 代码 xff1a 运行结果 xff1a
  • UBX 协议报文整理

    UBX 协议报文整理 UBX 协议的报文格式如下 xff1a 帧头 2 byte CLASS ID MESSAGE ID 2 byte 消息长度 2 byte PAYLOAD校验和 2 byte 帧头 由两个字节组成 xff0c 即0xB5
  • 将串口接收的数据绘制成波形图(使用matlab或Visual Scope)

    一 串口通信配置 结合stm32固件库 xff08 或其它类型单片机 xff09 中usart相关的函数 xff0c 配置好串口通信的寄存器 xff0c 确定 xff08 数据位 停止位 波特率等等 xff09 xff0c 本文主要介绍两种
  • linux下makefile、make、Cmake的区别

    Makefile make工具 linux下makeflie和make的用法 makefile与make详解 人们通常利用make工具来自动完成编译工作 这些工作包括 xff1a 如果仅修改了某几个源文件 xff0c 则只重新编译这几个源文
  • 常见的HTTP请求报文头

    目录 AcceptCookieConnectionCache ControlHostRefererUser Agent 参考链接 HTTP请求行 请求头 请求体详解 关于常用的http请求头以及响应头详解 Accept Accept app
  • C++中::和:的意思

    C 43 43 中的 1 类作用域 指明成员函数所属的类 span class token class name M span span class token operator span span class token function
  • Git 基础知识--打Tag、团队协作

    打 Tag 简述 Git 可以给历史中的某一个提交打上标签 tag xff0c 以示重要 人们一般用 tag 功能来标记发布节点 xff08 v1 0 xff09 tag 与 分支很像 xff0c 区别在于 xff1a 轻量标签 tag 是
  • 第七章——VINS系统初始化

    前言 这一章主要内容是讲的VINS系统初始化的事 xff0c 内容上还是比较全面丰满的 xff0c 有一些有疑问的点我之后读了代码会在博客里补上 一句话总结初始化 xff1a 以优化量与观测值构建残差 xff0c 提取优化量构成最小二乘问题
  • GIT系列之标签

    1 标签列表 git tag 在控制台打印出当前仓库的所有标签 git tag l 39 v1 39 搜索符合模式的标签 2 打标签 git标签分为两种类型 xff1a 轻量标签和附注标签 轻量标签是指向提交对象的引用 附注标签则是仓库中的
  • printf重定向(重新定义发送数据的方向)

    1 串口使用 printf 需要对 printf 重定向 也就是需要重定义 fputc xff08 xff09 这个函数 xff08 这个函数是printf的底层函数 xff09 转 int fputc span class token p
  • 解决nvcc找不到的问题/bin/sh:1:nvcc:not found

    这里写自定义目录标题 问题描述方法探索解决方法 问题描述 在执行make指令进行编译的时候 xff0c 遇到问题 34 bin sh 1 nvcc not found 34 xff0c 如图所示 其原因是未找到nvcc xff0c 于是开始
  • GPS的Heading, Course, and Crab Angle不同与区别

    看了老外这个文章终于知道GPS的Heading Course and Crab Angle不同与区别 xff0c 好东西分享给大家 http www chrobotics com library heading course and cra
  • 交换机对数据帧的转发和过滤

    大家好呀 xff0c 我是请假君 xff0c 今天又来和大家一起学习数通了 xff0c 今天要分享的知识是交换机对数据帧的转发和过滤 一 单播帧的转发 xff1a 交换机根据MAC地址表项进行数据帧转发 上图中 xff0c PCA发出数据帧
  • python3运行ros方法 No module named 'rospkg'

    我们安装的ros后 其一般的库文件均基于python2 但是当某个项目必须要用python3时 就会出现如下错误 Traceback most recent call last File 34 home jetbot arm all xAr
  • 二维码识别与定位-方法1-利用ar_track_alvar

    二维码识别作为一种快捷准确的技术已经应用与生活中的购物支付 物体识别及工业AGV导航等领域 xff0c 在OpenCV3 4 4版本上均提供了相应的函数cv QRCodeDetector detectAndDecode 用于二维码的检测和解
  • Jetson xavier NX进行apt update及安装ROS出错记录

    Jetson xavier NX ROS 在Jetson xaiver NX上准备安装ROS xff0c 在执行apt get update时出现如 34 Failed to fetch https mirrors aliyun com u
  • Ubuntu18.04安装ROS2并使用ROS1的bag和cartographer来建图

    目录 概述1 安装ROS21 xff09 安装python3及colcon2 xff09 添加ROS2的源3 xff09 安装eloquent桌面版本4 xff09 修改 bashrc 2 安装cartographer及cartograph
  • Linux获取指定进程CPU和内存占用率

    Linux获取指定进程CPU和内存占用率 创建如下python程序 caclute memory occupied py import psutil import pynvml import time class MonitorProces
  • 【ros2订阅报错】 ros2 forming pointer to reference type ‘const std::shared_ptr<const sensor_msgs::msg::Las

    这里写自定义目录标题 使用ROS2创建发布者 使用ROS2创建发布者 在创建发布者的时候 xff0c 出现 xff0c 如下问题 xff0c 经过修改cmakeList发现并不是cmake版本问题 usr include c 43 43 9
  • 图像二值化方法及适用场景分析(OTSU Trangle 自适应阈值分割)

    图像二值化 应用场景二值图像定义阈值获取的方法手动阈值法自动阈值法灰度均值法基于直方图均值法OTSUTriangle 自适应均值阈值分割方法 总结参考文献 应用场景 二值图像处理与分析在机器视觉与机器人视觉中非常重要 xff0c 涉及到非常