OpenCV学习笔记:图像二值化

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

本文是我之前在微信公众号上的一篇文章记录。原链接为:# OpenCV学习笔记:图像二值化

图像二值化( Image Binarization)就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程。即将256个亮度等级的灰度图像通过适当的阀值选取而获得仍然可以反映图像整体和局部特征的二值化图像。

由于二值图像数据足够简单,许多视觉算法都依赖二值图像。通过二值图像,能更好地分析物体的形状和轮廓。二值图像也常常用作原始图像的掩模(又称遮罩、蒙版,Mask):它就像一张部分镂空的纸,把我们不感兴趣的区域遮掉。进行二值化有多种方式,其中最常用的就是采用阈值法(Thresholding) 进行二值化。

Opencv中提供了 threshold()adaptiveThreshold()两个函数用于实现图像的二值化。

直接阈值化 - threshold()

下面我们先介绍threshold,它使用全局阈值,即对整个图像中的每一个像素都选用相同的阈值。该函数原型如下:

double threshold(InputArray src,
                 OutputArray dst,
                 double thresh,
                 double maxval,
                 int type);
复制代码
  • src:源图像,只能是CV_8U或者CV_32F位。
  • dst:输出图像 ,与输入图像有相同的大小,数据类型和通道数。
  • thresh:二值化的阈值
  • maxval:dst图像中最大值
  • type:二值化方法类型,如下:
enum ThresholdTypes {
    THRESH_BINARY     = 0,//灰度值大于阀值的为最大值,其他的为0
    THRESH_BINARY_INV = 1,//灰度值大于阀值的为0,其他的为最大值
    THRESH_TRUNC      = 2,//灰度值大于阀值的为阀值,其他的不变
    THRESH_TOZERO     = 3,//灰度值大于阀值的不变,其他的值为0
    THRESH_TOZERO_INV = 4,//灰度值大于阀值的为0,其他的值不变
    THRESH_MASK       = 7,
    THRESH_OTSU       = 8,//大律法自动寻求全局阀值
    THRESH_TRIANGLE   = 16//三角形法自动寻求全局阀值
};
复制代码

看下如下程序:

#include <iostream>
#include <string>
#include <cmath>
#include "opencv2/opencv.hpp"


//使用CommandLineParser对输入的参数进行分析,获取输入的图片路径
std::string GetFileName(int argc,char* argv[])
{
    /*
    argc : the size of argv[]
    argv : the parameters of comdline
    */
    const char* key_map = {
    "{help h usage? || usage information}"
    "{@picture || input picture}"
    };


    /*
	* 函数功能:构造函数
	* 参数:
	*   [in]     argc        main函数中的第一参数,即运行程序中获得指令的个数
	*   [in]     argv        main函数中的第二个参数,即运行程序中指令的内容
	*   [in]     key_map     当启动程序是没有输入任何指令,则使用key_map中默认的指指令。
	* 备注:
	*   key_map中的格式:
	*       "{    s|  123asd       |string parameter}
	*        {    d|  100          |digit parameter }
	*        {   @c|  false        |without camera  }
	*        {    1|  some text    |help            }
	*        {    2|  333          |another help    }"
	*参数or指令名称|指令or参数内容 |指令说明
	*
	*
	*  运行程序输入指令的方式如下:(例如程序名称为extest.exe)
	*  extest.exe -s=123asdd -d=1000 -@c=10
	*  注意:指令名称前面需要加一个“-”,或“--”。当输入指令后面没有参数,默认为true
	*       另外,前面加@的指令可以不输入指令名称,直接设置指令内容即可。
	*       没有输入的指令,则使用key_map中的默认值
	*/
    cv::CommandLineParser parser(argc, argv, key_map);

    //如果没有输入指令,则会使用上面设置的默认指令,则存在‘help’指令
    if (parser.has("help"))
    {
        parser.printMessage();
        exit(0);
    }

    //检查输入的指令是否有有错误,即无法解析
    if (!parser.check())
    {
        parser.printErrors();
        exit(-1);
    }

    std::string fileName = parser.get<std::string>(0);

    return fileName;
}


void onChangeTrackBar(int pos, void *userdata)
{
    cv::Mat dstImage;
    cv::Mat grayImage = *(cv::Mat*)userdata;
    cv::threshold(grayImage, dstImage, 130, 255, pos);
    cv::imshow("grayImage", dstImage);
}


int main(int argc,char* argv[])
{
    std::string fileName = GetFileName(argc, argv);

    cv::Mat srcImage = cv::imread(fileName);
    if (srcImage.empty())
    {
        std::cerr << "failed to read image" << std::endl;
        exit(-1);
    }

    cv::Mat grayImage;
    //将图像转成灰色图
    cv::cvtColor(srcImage, grayImage, cv::COLOR_BGR2GRAY);

    //新建窗口
    cv::namedWindow("grayImage");
    //在窗口上显示图像
    cv::imshow("grayImage", grayImage);
    //在窗口上创建一个trackbar
    cv::createTrackbar("threshold", "grayImage", 0, 4, onChangeTrackBar, (void*)&grayImage);

    cv::imshow("srcImage", srcImage);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}
复制代码

这个程序可以一直滑动滚动条的滑块改变不同的值,而且图像会出现相应的变化,如下图滑块到不同位置的效果:

image-20210620220333161

image-20210620220418590image-20210620220444032image-20210622071901523image-20210620220513975

这里其实是靠createTrackbar事件处理得到的,学过一点计算机的人就可以理解为该函数其实一直运行在后台检测是否有滑块移动这种中断产生,然后当你移动滑块时就会触发中断,这时回调函数就相当于中断服务函数来处理中断(不完全准确但是可以这么理解)。

这个程序就是通过滑动不同的二值化类型来进行图像二值化处理。

自适应阈值化 - adaptiveThreshold()

接下来我们介绍一下另外一个二值化函数adaptiveThreshold()自适应阀值的二值化方法。

void cv::adaptiveThreshold(
	cv::InputArray src, // 输入图像
	cv::OutputArray dst, // 输出图像
	double maxValue, // 二值化最大值
	int adaptiveMethod, // 自适应方法,平均或高斯
	int thresholdType // 阈值化类型
	int blockSize, // 块大小
	double C // 常量
);
复制代码
  • src:输入图像,必须是8位单通道(CV_8UC1)图像;
  • dst:需和原图像尺寸类型一致;
  • maxValue:二值化最大值;
  • adaptiveMethod:用于指定自适应阈值的算法,ADAPTIVE_THRESH_MEAN_C ,ADAPTIVE_THRESH_GAUSSIAN_C
  • thresholdType: 二值化方法类型:必须是THRESH_BINARY或者THRESH_BINARY_INV
  • blockSize:用来计算阈值的象素邻域大小:,一般是3, 5, 7, 的奇数
  • C:与方法有关的参数。对方法 ADAPTIVE_THRESH_MEAN_C 和 ADAPTIVE_THRESH_GAUSSIAN_C, 它是一个从均值或加权均值提取的常数, 有时也可以是小数或负数。

adaptiveThreshold()支持两种自适应方法,即ADAPTIVE_THRESH_MEAN_C(平均)和ADAPTIVE_THRESH_GAUSSIAN_C(高斯)。在两种情况下,自适应阈值T(x, y)。通过计算每个像素周围blockSize * blockSize大小像素块的加权均值并减去常量C得到。其中,blockSize的大小必须为奇数;如果使用平均的方法,则所有像素周围的权值相同;如果使用高斯的方法,则(x,y)周围的像素的权值则根据其到中心点的距离通过高斯方程得到。

看下如下程序进行自适应阀值化效果:

void test_adaptive_threshold()
{
    //must be CV_8UC1
	cv::Mat src = cv::imread("car.png", cv::IMREAD_GRAYSCALE);
	cv::Mat dst_mean, dst_gauss;
 
	int maxVal = 255;
	int blockSize = 55;
	double C = 0;
	cv::adaptiveThreshold(src, dst_mean, maxVal, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, C);
 
    cv::adaptiveThreshold(src, dst_gauss, maxVal, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, blockSize, C);
 

	cv::imshow("dst_mean", dst_mean);
    cv::imshow("dst_gauss", dst_gauss);
	cv::waitKey(0);
 
	return;
}
复制代码

程序执行效果如下:

image-20210622065550560

猜你喜欢

转载自juejin.im/post/7104854376820244493