C++调用OpenCV实现图像阈值处理

news2024/11/19 3:50:18

1 前言

在计算机视觉技术中,阈值处理是一种非常重要的操作,它是很多高级算法的底层处理逻辑之一。比如在使用OpenCV检测图形时,通常要先对灰度图像进行阈值(二值化)处理,这样就得到了图像的大致轮廓,以便于识别图形。

在阈值处理中,会将图像的每一个像素值与阈值进行比较,如果小于阈值,则将像素值置为0(黑色),若大于或等于阈值,将像素值置为最大值255(白色)。下边我们一起了解一下OpenCV中的三种阈值处理技术:简单阈值处理、自适应阈值处理和Otsu阈值处理

2 简单阈值处理

OpenCV中,简单阈值处理的C++接口原型是:

double cv::threshold( InputArray  src,
		              OutputArray dst,
		              double 	  thresh,
		              double 	  maxval,
		              int 	      type 
	                )		

参数说明:

参数1:待处理的图像,可以是彩色图像或灰度图像,建议使用灰度图像

参数2:阈值处理后的图像

参数3:阈值,一般在125~150之间取一个阈值,效果比较好

参数4:阈值处理采用的最大值

参数5:阈值处理类型,包括以下表格中的5种类型

返回值:处理时采用的阈值

5种简单阈值处理的C++核心代码如下:

    //二值化处理 
	threshold(src, dst, 127, 255, THRESH_BINARY);
	imshow("binary", dst);
	imwrite("binary.jpg", dst);

	//反二值化处理
	threshold(src, dst, 127, 255, THRESH_BINARY_INV);
	imshow("binary-inv", dst);
	imwrite("binary-inv.jpg", dst);

	//截断阈值处理
	threshold(src, dst, 127, 255, THRESH_TRUNC);
	imshow("trunc", dst);
	imwrite("trunc.jpg", dst);

	//低于阈值0处理
	threshold(src, dst, 127, 255, THRESH_TOZERO);
	imshow("tozero", dst);
	imwrite("tozero.jpg", dst);

	//超出阈值0处理
	threshold(src, dst, 127, 255, THRESH_TOZERO_INV);
	imshow("to-zero-inv", dst);
	imwrite("to-zero-inv.jpg", dst);

效果图如下:

第一张图像是灰度图像,其对应的原始彩色图像如下:

后边5幅图像,依次是二值化阈值处理、反二值化阈值处理、截断阈值处理、低于阈值0处理、超出阈值0处理对应的图像。可以看出来,截断阈值处理和低于阈值0处理,效果相对比较好一些,但图像的有些轮廓依然不清晰。

3 自适应阈值处理

在简单阈值处理中,我们使用了一个全局的阈值,细心的读者可能已经发现了,这个值是127。就是说,对一幅图像的所有像素值,都是用这1个阈值来比较和处理的。而实际情况是,一幅图像的不同区域,往往明暗度不一样。

OpenCV提供了一个改进的阈值处理技术,即自适应阈值处理。在这里,内部算法根据一个像素周围的小区域来确定该像素的阈值,因此对同一图像的不同区域会得到不同的阈值。

OpenCV中的自适应阈值处理接口原型:

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

参数说明:

参数1:待处理的图像,必须使用灰度图像

参数2:阈值处理后的图像

参数3:阈值处理采用的最大值

参数4:自适应阈值计算方法,包括2种,如下表所示

参数5:阈值处理类型,仅包括以下2种:THRESH_BINARYTHRESH_BINARY_INV

参数6:一个正方形区域的大小,例如11,就是11 x 11的矩阵区域

参数7:常量,阈值等于均值或加权值减去这个常量值

 自适应阈值处理的C++核心代码如下:

    //自适应阈值
	//阈值是邻近区域的平均值减去常数C
	adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 2);
	imshow("mean-binary", dst);
	imwrite("mean-binary.jpg", dst);

	//自适应阈值
	//阈值是邻域值的高斯加权和减去常数C
	adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, 2);
	imshow("gauss-binary", dst);
	imwrite("gauss-binary.jpg", dst);

效果图如下:

以上4幅图像,依次是原图灰度图像、二值化阈值处理、平均值自适应阈值处理、高斯加权自适应阈值处理的图像。与前边的简单阈值处理相比,自适应阈值处理较好的保留了二值图像的轮廓信息。

4 Otsu阈值处理

在简单阈值处理中,还存在一个问题,示例代码中使用的阈值127,是随机采用的一个数值,并不是通过算法计算得到的。因为实际图像处理时,有的图像阈值选择127可能刚刚好,但有的图像选择这个值并不合适。那怎么办?一个一个去手动尝试吗,可以,但是太耗费时间了。

针对这个问题,OpenCV提供了一个改进的处理技术:Otsu阈值处理。该方法遍历所有可能的阈值,选择一个最合适的阈值。

OpenCV中,Otsu阈值处理的接口,同样是cv::threshold。

double cv::threshold( InputArray  src,
		              OutputArray dst,
		              double 	  thresh,
		              double 	  maxval,
		              int 	      type 
	                )		

与简单阈值处理调用不同的地方是:参数type除了选择5种类型中的一种外,再加上THRESH_OTSU,比如THRESH_BINARY + THRESH_OTSU。其它参数使用一样。

在下边的示例代码中,打开了一幅带有高斯噪声的图像,对该图像使用Otsu阈值处理,核心代码如下:

    //Otsu阈值
	threshold(src, dst, 0, 255, THRESH_BINARY + THRESH_OTSU);
	imshow("otsu-binary", dst);
	imwrite("otsu-binary.jpg", dst);

	//高斯去噪后使用Otsu阈值处理
	GaussianBlur(src, blur, Size(5, 5), 0, 0);
	threshold(blur, dst, 0, 255, THRESH_BINARY + THRESH_OTSU);
	imshow("gaussblur-binary", dst);
	imwrite("gaussblur-binary.jpg", dst);

效果图如下:

以上4幅图像,依次是高斯噪声灰度图像、二值化阈值处理图像、Otsu阈值处理图像、高斯去噪后Otsu阈值处理图像。

5 创建测试项目

创建测试项目、配置开发环境,具体可参考之前文章,这里就不多说了。

Win10+OpenCV4.6.0之开发环境(VS2022)配置入门_来灵的博客-CSDN博客_opencv4.6

这次测试项目名称img_threshold,VS2022种创建好的项目截图:

以下是上边介绍的3种阈值处理总代码,将代码编辑到img_threshold.cpp文件里,代码中有详细注释。

#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;

//简单阈值处理
int SimpleThresholding(const string& filePath)
{
	//打开读取原图 
	Mat src = imread(filePath.c_str(), IMREAD_GRAYSCALE);
	//打开失败
	if (src.empty())
	{
		cout << "打开图片失败" << endl;
		return -1;
	}
	Mat dst;

	//显示原图
	imshow("src", src);

	//二值化处理 
	threshold(src, dst, 127, 255, THRESH_BINARY);
	imshow("binary", dst);
	imwrite("binary.jpg", dst);

	//反二值化处理
	threshold(src, dst, 127, 255, THRESH_BINARY_INV);
	imshow("binary-inv", dst);
	imwrite("binary-inv.jpg", dst);

	//截断阈值处理
	threshold(src, dst, 127, 255, THRESH_TRUNC);
	imshow("trunc", dst);
	imwrite("trunc.jpg", dst);

	//低于阈值0处理
	threshold(src, dst, 127, 255, THRESH_TOZERO);
	imshow("tozero", dst);
	imwrite("tozero.jpg", dst);

	//超出阈值0处理
	threshold(src, dst, 127, 255, THRESH_TOZERO_INV);
	imshow("to-zero-inv", dst);
	imwrite("to-zero-inv.jpg", dst);

	//按下任何键结束程序
	waitKey(0);

	//关闭所有窗口
	destroyAllWindows();
}

//自适应阈值处理
int AdaptiveThresholding(const string& filePath)
{
	//打开读取原图 
	Mat src = imread(filePath.c_str(), IMREAD_GRAYSCALE);
	//打开失败
	if (src.empty())
	{
		cout << "打开图片失败" << endl;
		return -1;
	}
	Mat dst;

	//显示原图
	imshow("src", src);

	//二值化处理 
	threshold(src, dst, 127, 255, THRESH_BINARY);
	imshow("binary", dst);
	imwrite("binary.jpg", dst);

	//自适应阈值
	//阈值是邻近区域的平均值减去常数C
	adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 11, 2);
	imshow("mean-binary", dst);
	imwrite("mean-binary.jpg", dst);

	//自适应阈值
	//阈值是邻域值的高斯加权和减去常数C
	adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, 2);
	imshow("gauss-binary", dst);
	imwrite("gauss-binary.jpg", dst);

	//按下任何键结束程序
	waitKey(0);

	//关闭所有窗口
	destroyAllWindows();
}

//Otsu阈值处理
int OtsuThresholding(const string& filePath)
{
	//打开读取原图 
	Mat src = imread(filePath.c_str(), IMREAD_GRAYSCALE);
	//打开失败
	if (src.empty())
	{
		cout << "打开图片失败" << endl;
		return -1;
	}
	Mat dst, blur;

	//显示原图
	imshow("src", src);

	//二值化处理 
	threshold(src, dst, 127, 255, THRESH_BINARY);
	imshow("binary", dst);
	imwrite("binary.jpg", dst);

	//Otsu阈值
	threshold(src, dst, 0, 255, THRESH_BINARY + THRESH_OTSU);
	imshow("otsu-binary", dst);
	imwrite("otsu-binary.jpg", dst);

	//高斯去噪后使用Otsu阈值处理
	GaussianBlur(src, blur, Size(5, 5), 0, 0);
	threshold(blur, dst, 0, 255, THRESH_BINARY + THRESH_OTSU);
	imshow("gaussblur-binary", dst);
	imwrite("gaussblur-binary.jpg", dst);

	//按下任何键结束程序
	waitKey(0);

	//关闭所有窗口
	destroyAllWindows();
}

int main(int argc, char** argv)
{
	string filePath("");
	cout << "图像阈值处理测试:1 简单阈值处理;2 自适应阈值处理;3 Otsu阈值处理" << endl << endl;
	while (true)
	{
		cout << "请选择1或2或3,开始图像阈值处理" << endl << endl;
		int option;
		cin >> option;
		cout << "请输入原始图片文件" << endl;
		switch (option)
		{
		case 1:
			//输入文件名 flower.jpg
			cin >> filePath;
			SimpleThresholding(filePath);
			break;
		case 2:
			//输入文件名 flower.jpg
			cin >> filePath;
			AdaptiveThresholding(filePath);
			break;
		case 3:
			//输入文件名 gauss-noise.jpg
			cin >> filePath;
			OtsuThresholding(filePath);
			break;
		default:
			cout << "无效输入,请重新输入" << endl;
		}
	}

	return 0;
}

测试项目工程当前目录:

6 总结

本节我们一起探索了下使用OpenCV,如果对图像进行阈值处理,其中包括简单的阈值处理、自适应阈值处理和Otsu阈值处理。用到的OpenCV关键函数是threshold和adaptiveThreshold,其调用过程都比较简单。但阈值处理的作用很重要,是很多高级算法的底层逻辑之一。比如在做图形检测,轮廓识别时,常常先会对图像进行阈值处理。

7 参考

OpenCV: Basic Thresholding Operations

OpenCV: Image Thresholding

opencv(4.5.3)-python(十二)--图像阈值处理 - 腾讯云开发者社区-腾讯云

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77500.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

English Learning - L1 站在高处建立灵魂 2022.12.5

English Learning - L1 站在高处建立灵魂 2022.12.51.1 到底什么是语法1.2 为什么要学习语法口语分广义和狭义讲母语的人为啥不学语法&#xff1f;作为一名二语习得者口语中可不可以没有有语法&#xff1f;1.3 英语&#xff08;听说读写&#xff09;的核心金字塔理论关于词汇量…

免费内网穿透工具测评对比,谁更好用 2

文章目录1. 前言2. 对比内容2.1 http协议功能及操作对比2.1.1 网云穿的http设置2.1.2 Cpolar的http设置2.2 使用感受对比3. 结语1. 前言 上篇文章&#xff0c;笔者对比了网云穿和Cpolar的直观内容&#xff0c;包括网站界面、客户端界面和内网穿透设置界面。总的来说&#xff0…

保姆级教程:手把手教你使用深度学习处理文本

大家好&#xff0c;今天给大家分享使用深度学习处理文本&#xff0c;更多技术干货&#xff0c;后面会陆续分享出来&#xff0c;感兴趣可以持续关注。 文章目录NLP技术历程准备数据标准化词元化Tokenization&#xff08;文本拆分&#xff09;技术提升建立索引表使用TextVectoriz…

开源开放 | 开源知识图谱抽取工具DeepKE发布更新

知识图谱是一种用图模型来描述知识和建模世界万物之间关联关系的大规模语义网络&#xff0c;是大数据时代知识表示的重要方式之一。近年来&#xff0c;知识图谱在辅助语义搜索、支持智能问答、增强推荐计算、提升语言语义理解和大数据分析能力等越来越多的技术领域得到重视&…

极客时间课程笔记:业务安全

业务安全 业务安全体系&#xff1a;对比基础安全&#xff0c;业务安全有哪些不同&#xff1f;业务安全和基础安全在本质上就有很大的不同&#xff1a;在基础安全中&#xff0c;黑客将技术作为核心竞争力&#xff1b;在业务安全中&#xff0c;黑产将资源作为核心竞争力。谁能够…

ADI Blackfin DSP处理器-BF533的开发详解23:SDRAM内存的设计和自检(含源代码)

硬件准备** ADSP-EDU-BF533&#xff1a;BF533开发板 AD-HP530ICE&#xff1a;ADI DSP仿真器 软件准备 Visual DSP软件 硬件链接 功能介绍 ADSP-EDU-BF53x 板卡上采用的 SDRAM 型号为 MT48LC16M16A2,容量为 32Mbyte&#xff0c;采用 16Bit 模式连接ADSP-BF53x。通过配置 EB…

【STM32】详解嵌入式中FLASH闪存的特性和代码示例

一、存储器 我们正常编译生成的二进制文件&#xff0c;需要下载烧录到单片机里面去&#xff0c;这个文件保存在单片机的ROM(read only memory)中&#xff0c;所有可以完成这种特性的存储介质都可以称为ROM。 分类 ROM一般分为四大类 ①PROM&#xff1a;可编程只读存储器&#…

毫米波雷达系列 | 基于前后向空间平滑的MUSIC算法详解

毫米波雷达系列 | 基于前后向空间平滑的MUSIC算法详解 文章目录毫米波雷达系列 | 基于前后向空间平滑的MUSIC算法详解DOA阵列模型MUSIC算法空间平滑算法整体流程仿真代码忙了一阵子的中期和专利&#xff0c;基本上告一段落&#xff0c;简单的写一个比较常见的解相干MUSIC角度估…

阿里高工珍藏版“亿级高并发系统设计手册(2023版)”面面俱到,太全了!

高并发 俗话说&#xff1a;罗马不是一天建成的&#xff0c;系统的设计当然也是如此。 从原来谁都不看好的淘宝到现在的电商巨头&#xff0c;展现的不仅仅是一家互联网巨头的兴起&#xff0c;也是国内互联网行业迎来井喷式发展的历程&#xff0c;网络信号从 2G 发展到现在的 5…

ATtiny13与Proteus仿真-UART信号模拟仿真

UART信号模拟仿真 ATtiny13没有UART模块,因此在调试程序时,使用软件模拟UART信号很有必要。本文将介绍如何如何控制2个GPIO来模拟UART TX和RX信号,并在Proteus仿真。 1、UART信号介绍 UART的信号一般由如下三部分组成: 开始信号数据信号停止信号UART 信号保持高电平。 作…

软件测试概念基础——小记

文章目录1. 什么是软件测试2. 软件测试和软件开发的区别3. 什么是需求4. 需求是软件测试的依据5. 什么是BUG6. 什么是测试用例7. 开发模型瀑布模型螺旋模型增量模型 迭代模型敏捷模型scrum8. 测试模型V模型W模型9. 软件测试的生命周期&#xff08;软件测试的流程&#xff09;10…

Web大学生个人网页作业成品——学生个人爱好展示网站设计与实现(HTML+CSS+JS)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

FlinkCDC部署

文章目录Flink安装job部署1、测试代码2、打包插件3、打包4、测试Flink安装 1、解压 wget -b https://archive.apache.org/dist/flink/flink-1.13.6/flink-1.13.6-bin-scala_2.12.tgz tar -zxf flink-1.13.6-bin-scala_2.12.tgz mv flink-1.13.6 /opt/module/flink2、环境变量…

快手某HR吐槽:职位要求写得很清楚,照着写简历不行吗?有的工作经历不相关,有的工作好几年还写学生会奖学金,这种一秒扔垃圾桶!...

求职时&#xff0c;你的简历是什么样的&#xff1f;能否帮你顺利通过初筛&#xff1f;一位快手的面试官吐槽很多求职者的简历“一塌糊涂”&#xff1a;职位要求已经写得很明白了&#xff0c;就把里面罗列的技术和跟业务相关的项目经验贴上来就好了&#xff0c;有人偏写航空公司…

Vue 不重新打包,动态加载全局配置的实现过程

背景 项目前端采用了 Vue.js &#xff0c;跟传统前端 html 技术不同之处在于&#xff0c;每次打包后都重新生成新的 js 文件&#xff0c;而且不可读&#xff0c;必须全量替换。但最近碰到一个漏洞扫描的问题&#xff0c;系统通过单点登录方式访问时&#xff0c;是不能有登录首…

【MySQL基础】数据库操作语言DML相关操作有那些?

目录 一、什么是DML 二、数据插入insert 三、数据的修改update 四、数据的删除delete 五、delete和truncate有什么不同&#xff1f; 六、DML操作知识构图 七、DML操作练习 &#x1f49f; 创作不易&#xff0c;不妨点赞&#x1f49a;评论❤️收藏&#x1f499;一下 一、什…

【Docker学习教程系列】8-如何将本地的Docker镜像发布到私服?

通过前面的学习&#xff0c;我们已经知道&#xff0c;怎么将本地自己制作的镜像发布到阿里云远程镜像仓库中去。但是在实际工作开发中&#xff0c;一般&#xff0c;我们都是将公司的镜像发布到公司自己搭建的私服镜像仓库中&#xff0c;那么一个私服的镜像仓库怎么搭建&#xf…

Android之Service

一、Service简介&#xff1a; Service是一种后台服务机制&#xff0c;允许在没有用户界面的情况下&#xff0c;使程序能够长时间在后台运行。 Service是四大组件之一&#xff0c;适用于开发无UI界面、长时间后台运行、做一些用时比较长的操作。 二、Service创建&#xff1a;…

Frida - App逆向 JavaScript代码注入 基本语法以及数据类型介绍

Frida - App逆向 JavaScript代码注入 常用语法介绍 文章目录Frida - App逆向 JavaScript代码注入 常用语法介绍前言一、逆向步骤二、重载(Overload) / Frida数据类型1.重载函数介绍2.重载函数常用的类型三、Frida Hook常用代码介绍1.基本代码框架四、注入JavaScript代码运行监听…

功能测试的工作流程

测试工作人员接收到项目需求、验收标准和原型图并对需求进行分析以了解项目的需求。 一、测试计划&#xff1a;个人觉得测试计划应在在详细设计确认后&#xff0c;代码开始编写的时候开始编写。测试计划主要给后面的测试工作的一些指南。 其内容包含&#xff1a; 1、测试团队人…