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

2023-11-03

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(十二)--图像阈值处理 - 腾讯云开发者社区-腾讯云

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

C++调用OpenCV实现图像阈值处理 的相关文章

  • 使用opencv+picamera流IO用树莓派捕获视频

    我使用 Raspberry 来简单地显示一个视频 目前仅此 为此 我必须使用 opencv cv2 我尝试了很多解决方案 但现在我想使用 Picamera 库捕获视频 我将向您展示我的代码 import io import time imp
  • 如何平滑循环列向量

    这是一个 OpenCV2 问题 我有一个矩阵代表closed空间曲线 cv Mat
  • OpenCV 中的 Gabor 内核参数

    我必须在我的应用程序中使用 Gabor 过滤器 但我不知道这个 OpenCV 方法参数值 我想对虹膜进行编码 启动 Gabor 过滤器并获取特征 我想对 12 组 Gabor 参数值执行此操作 然后我想计算 Hamming Dystans
  • OpenCV 2.4 Jpeg 到 PNG(带 alpha 通道)

    我有一个 JPEG 和一个蒙版 我想创建一个具有三个 JPEG 通道的 PNG 并且 Alpha 通道应该是蒙版 如何使用 OpenCV 实现这一目标 Regards std vector
  • 寻找两个框架之间的变换

    我有来自视频源的两个连续帧 并且我使用 FAST 算法检测这两个帧的关键点 我使用平方差之和法 SSD 来匹配关键点 所以基本上我已经匹配了两个框架之间的关键点 现在我想根据匹配的关键点集计算两个帧之间的仿射变换 缩放 旋转 平移 我知道如
  • 为什么opencv videowriter这么慢?

    你好 stackoverflow 社区 我有一个棘手的问题 我需要你的帮助来了解这里发生了什么 我的程序从视频采集卡 Blackmagic 捕获帧 到目前为止 它工作得很好 同时我用 opencv cv imshow 显示捕获的图像 它也工
  • 如何根据图像中的对象大小(以像素为单位)来测量现实世界中的对象大小(例如英寸、厘米等)?

    我计算了物体的大小pixel来自包含对象的图像 我想测量现实世界中物体的大小 有没有办法找出乘数来测量实际尺寸 我目前正在使用python以便实施 通常 您将使用相机获取图像 该相机通过镜头将 3 维场景投影到 2 维传感器上 垂直 高度
  • OpenCV RGB转灰度

    我正在做一个视频监控项目 我看不到从 RGB 到灰度的转换 我为灰色设置了黑色窗口 你能帮我解决这个问题吗 附代码 另外 如何获得当前帧和前一帧之间的差异 多谢 宜兰 include stdafx h include
  • 我可以将 OpenCV 的发布配置与我的应用程序的调试配置一起使用吗?

    我正在编写一个通用 Windows 应用程序 它使用 OpenCV 进行相机校准和标签检测等 我希望能够在我自己的 DLL 处于调试模式时使用发布模式 完全优化的 OpenCV DLL 这可能吗 如果是这样 我如何配置 CMake 来实现它
  • CMake:编译 OpenCV 时未找到 CUDA 库

    我正在 Windows 上使用 CMAKE 编译支持 CUDA 的 OpenCV 3 0 0 当我点击 配置 时 出现如下错误 CMake Error The following variables are used in this pro
  • 使用 ffmpegframerecorder android 保存视频

    我正在尝试保存该视频并收到此错误 我添加了两个jar文件1 javacv 2 javacpp and for 相机预览我用过opencv 我在这里添加代码 公共无效运行 while true Bitmap bmp null synchron
  • 如何创建关键点来计算 SIFT?

    我正在使用 OpenCV Python 我已经使用确定角点cv2 cornerHarris 输出的类型为dst 我需要计算角点的 SIFT 特征 输入到sift compute 必须是以下类型KeyPoint 我不知道如何使用cv2 Key
  • OpenCv SVM 输出文件格式

    我正在实现我自己的 SVM 而不是使用 OpenCV 的 svm 类 如果我愿意 我希望我的 SVM 用于保存其输出的 XML 文件将来可以由 OpenCV 的 SVM 加载和使用 为此我需要做什么 简而言之 OpenCV 使用什么格式来存
  • HOGDescriptor 带有视频来识别物体

    不幸的是 我既是 python 又是 openCV 初学者 所以如果问题很愚蠢 请原谅我 我正在尝试使用cv2 HOGDescriptor识别视频中的物体 我关心的是逐帧识别 即没有跟踪等 这是我正在做的事情 我读了视频 目前是 mpg 通
  • python cv2.Videocapture() 不起作用,cap.isOpened() 返回 false

    cv2 Videocapture 在使用网络摄像头时工作正常 但在尝试从硬盘驱动器读取时显示错误 cap isOpened 返回 false import cv2 import numpy as np background cv2 imre
  • OpenCV 从中心 x,y 绘制矩形

    我想仅使用中心点绘制一个矩形 所以在我的中心点周围几乎是一个矩形 最简单的方法是什么 谢谢 考虑到中心 x y then cv rectangle image cvPoint x w 2 y h 2 cvPoint x w 2 y h 2
  • 如何在 Keras Lambda Layer 中使用 OpenCV 函数?

    我正在尝试使用一个在图像上使用某些 OpenCV 函数的函数 但我得到的数据是张量 我无法将其转换为图像 def image func img img cv2 cvtColor img cv2 COLOR BGR2YUV img cv2 r
  • ndk-build error.opencv2/core/core.hpp:没有这样的文件或目录

    我在 Android 中使用 OpenCV Nonfree 模块时遇到问题 我读了这个教程https sites google com site wghsite technical notes sift surf opencv androi
  • 如何使用 python 将 OpenCV 输出发送到浏览器?

    我有一个带有 open cv 的简单 python 脚本 它接收视频并使用 YOLO 对其进行对象检测 我的问题是 如何将输出作为实时流显示到我的网站 这是Python代码 保存到output avi import cv2 from dar
  • 使用 Get2D 访问 OpenCV 中的 2d 像素值时出现超出范围错误或错误的返回值

    这是一个简单的程序 使用 OpenCV Python 中 导入图像 将其转换为灰度并将其显示在窗口中 然后 当用户单击窗口中的某个位置时 将从该点开始执行洪水填充 此外 当用户单击该点时 程序应打印该位置的原始 2D 像素值 不幸的是 当过

随机推荐

  • STM32学习笔记(4) 高级定时器-两路互补的PWM输出(带死区和刹车控制)

    目录 1 实验目的 2 实验效果 3 理论部分 3 1时钟源 3 2时基单元 3 3输入捕获 4 程序流程 4 1GPIO初始化结构体 4 2时基初始化结构体 4 3输出比较结构体 4 4刹车和死区结构体的初始化 5 程序源码 1 实验目的
  • roblox虚拟世界怎么做服务器,roblox虚拟世界

    游戏简介 roblox虚拟世界是一款像素风格模拟经营游戏 游戏采用简约的画面风格设定 超大的世界地图可以自由的探索 还可以收集丰富的物品资源进行创造 定制一个专属的秩序 游戏特色 1 游戏采用高清品质的画面 大家开自由的畅玩 2 随时随地一
  • 单片机蓝桥杯--数码管显示

    我们先来看一下蓝桥杯板中数码管部分的电路图 对于动态数码管的控制 是需要有段选和位选的 位选是控制数码管哪一位显示 段选是控制该位显示什么数字 由上图可知 当Y6C有效时 P0控制的是数码管的位 当Y7C有效时 可以给P0写入显示数字的断码
  • RedHat Enterprise下如何配置KVM虚拟机的网络网桥

    RedHat Enterprise下如何配置KVM虚拟机的网络网桥 一 网桥的相关创建命令 1 创建网桥设备 brctl addbr
  • 【Github】目标检测组会内容分享

    组会的内容展示为PPT PDF的形式 讲解目标检测领域比较重要的论文 比如R CNN SPP Net等经典论文 资料已经打包好上传到我的github仓库 仓库地址为 https github com biluko Object detect
  • Java后端内部面试题(前一部分)

    面试题 基础篇 1 Java 语言有哪些特点 1 简单易学 有丰富的类库 2 面向对象 Java 最重要的特性 让程序耦合度更低 内聚性更高 2 面向对象和面向过程的区别 面向过程 是分析解决问题的步骤 然后用函数把这些步骤一步一步地实现
  • python 遇到表情代码出错, 用正则表达式去掉表情代码

    gt gt gt aa U0001f60a adwkdkdkkdk 这里 U0001f60a 这是表情的代码 带这个 gt gt gt import re gt gt gt cc re compile r U0001f60a cc 就是需要
  • 小程序授权登录最新解决方案

    小程序授权登录最新解决方案 一 在WXML中设置单击事件 也可以直接放在第一个页面的ONLOAND函数中 用户进入小程序后自动弹出 二 在JS中设置Button的触发代码 三 演示 一 在WXML中设置单击事件 也可以直接放在第一个页面的O
  • SmartForms取消Word编辑器 恢复文本编辑器

    导语 在目前最新版本的 S 4 HANA 1909和2021版本中 SmartForms的文本编辑器改成了嵌套Word 而不是直接修改文本框的形式 这给很多电脑配置不是很高 或者Word兼容性有问题的开发带来了困难 下面说一下如何恢复到文本
  • Ruoyi用户菜单权限

    若依用户菜单权限 后端实现 首先如果想先要实现菜单权限 那么得现在后端添加一个与之对应的权限字段 这样就有权限字段了 前端实现 前端得需要调用这个权限字段的按钮 调用该权限字段的按钮 点击事件 接口请求 若依前端功能 这里得需要在ruoyi
  • page table 改进之 反置页表

    页表是把所有的逻辑页面放到一起 但随着进程的增加 逻辑页面会迅速增长 有多少页面页表就会有多少项 那么不变的是物理内存 为什么不给物理内存编号记录每一块存放了哪个进程呢
  • 【图像处理】图像检索的三种python实现(直方图/OpenCV/哈希法)

    简介 本文介绍了图像检索的三种实现方式 均用python完成 其中前两种基于直方图比较 哈希法基于像素分布 检索方式是 提前导入图片库作为检索范围 给出待检索的图片 将其与图片库中的图片进行比较 得出所有相似度后进行排序 从而检索结果为相似
  • 《互联网程序设计(Java)》——课程笔记10:Http程序设计

    1 概论 HTTP系统包括客户端软件 浏览器 和服务器软件 HTTP服务器 早期的客户端软件 其主要工作可理解为文件下载和文件显示 实际上现代的HTTP客户端比文件下载要复杂得多 它包括网页文件的下载 跨平台的本地显示 参数的传递 动态网页
  • python爬虫Scrapy框架笔记分享12-Splash 的使用

    1 Splash介绍 Splash是一个JavaScript渲染服务 是一个带有HTTP API的轻量级浏览器 同时它对接了Python中的Twisted和QT库 利用它 我们同样可以实现动态渲染页面的抓取 2 安装 2 1 安装docke
  • el-date-picker 实现禁止选择今日以后的日期,以及时间跨度不超过365天,和设置默认选择日期,解决选择当天无效问题

    首先el date picker有 picker options属性 绑定属性 picker options pickerOptions 然后在data中设置需要的数据 data let secondOfDay 1000 60 60 24
  • Unity3D之动画(Animation)的制作

    实例说明 忍者跑酷的player动画制作 这些都是用Sprite做的动画 在prioject面板里的一组sprite里面点击 之后看属性面板的Sprite Editor对这组Sprite进行编辑 下面先编辑一个idle状态的动画 首先选择第
  • 2023HW-8月(10-15)53个0day,1day漏洞汇总含POC、EXP

    点击 仙网攻城狮 关注我们哦 不当想研发的渗透人不是好运维 让我们每天进步一点点 简介 2023HW 8月10 15号0day 1day漏洞汇总 已更新 包含以下漏洞需要自取 链接 https pan baidu com s 1Tr94yV
  • 使用OpenCV的OpenCL(ocl)模块

    参加OpenCV的OpenCL模块 以下称OCL 移植工作已经有2个月了 这里我说移植而不是开发 是因为大部分OCL模块的函数是从已经很成熟的GPU模块直接移植过来的 因此 目前阶段OCL模块所支持的函数接口是GPU模块的一个子集 但由于运
  • 变分推断(variational inference)

    大家对贝叶斯公式应该都很熟悉 P Z X p
  • C++调用OpenCV实现图像阈值处理

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