【opencv4.3.0教程】08之图像掩膜(Mask)操作与执行时间

2023-12-11 20:32

本文主要是介绍【opencv4.3.0教程】08之图像掩膜(Mask)操作与执行时间,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、前言

二、温故知新——像素基本操作

1、获取像素指针

2、像素范围处理

3、读写像素

三、图像掩膜操作

1、怎么理解掩膜Mask

2、掩膜实现

3、API-filter2D

四、执行时间


一、前言

图像操作其实就是对像素进行操作,这些操作不仅仅是像前面那些基础操作一样简单,只有获取值啊,简单赋值啊之类的。但是像素操作可不止有这么简单。

从今天这节内容开始,我们来一起聊一些更高级的像素操作,我们会讲一些原理,并讲解对应的API,通过实战让大家能够对内容有更深刻的认识。

二、温故知新——像素基本操作

前面我们讲了几个像素基本操作:

获取像素指针:用于后续读取某像素的信息及修改像素。通过像素指针来访问像素。

控制像素范围:将求得的像素值规范到0-255之间。

读写像素,利用像素指针获取像素值及修改像素值。

1、获取像素指针

获取像素指针是可以获得一个指向像素的指针,我们可以使用指针来访问像素值,修改像素值。包括获取灰度图像像素指针和彩色图像像素指针。

获取方式如下:

	//灰度图像src_gray.at<uchar>(y, x); //行在前,列在后,y表示行,x表示列src_gray.at<uchar>(Point(x, y));//彩色图像Vec3b BGR = src.at<Vec3b>(row, col);int B = BGR.val[0];int G = BGR.val[1];int R = BGR.val[2];Scalar BGR1 = src.at<Vec3b>(Point(col, row));

 

2、像素范围处理

像素范围处理功能会将所有的输入值都控制在0-255之间,小于0的返回0,大于255的返回255,其他不变。具体API如下:

saturate_cast<uchar>(number);

3、读写像素

读写像素是最基本的像素操作,一个是用于获取像素值,一个是用于修改某个位置的像素值,上面获取像素指针的代码同时也读取了像素,写像素代码如下:

//灰度图像
image.at<uchar>(y, x) = 123;  //彩色图像
image.at<Vec3b>(y,x)[0]=0; // blue
image.at<Vec3b>(y,x)[1]=0; // green
image.at<Vec3b>(y,x)[2]=255; // red

 

三、图像掩膜操作

1、怎么理解掩膜Mask

首先我们先来看一下定义:

掩膜操作是指根据掩膜矩阵(也称作核kernel)重新计算图像中每个像素的值。

我们举个例子来说明一下,然后我们再来解释:

图像与掩膜矩阵

比如上面的这个图中,我们左边是图像,右边是我们的掩膜矩阵,定义说,我们要根据掩膜矩阵重新计算图像中每个像素的值。计算方式如下:

从左边图中找到黄色背景区域(从原图中找到和掩膜矩阵大小相同的区域),对应位置相乘,然后求和:

5 * 0 + 5 * (-1) + 5 * 0 + 5 * (-1) + 5 * 5 + 5 * (-1) + 5 * 0 + 5 * (-1) + 5 * 0 = 5

掩膜运算

通过计算,我们就会得到右面的图像,左边的3×3的像素,经过计算,会得到右边1×1的像素。然后,我们计算

然后,我们计算下一个像素点,也就是左边的区域往后移动一个区域,一直到最后一个区域结束:

我们会发现,计算过后,图像变小了(最外面有一圈没有计算)。这个问题,我们先留在这里,以后再说。

由于图像所有都是5,五个中心减去四个相邻,最后结果还是5,所以上面这个经过掩膜操作并没有什么太明显的变化。

如果换个矩阵就不一样啦:

通过上面的介绍,我想,我们可以对掩膜有个更加深刻的了解了,掩膜可以通过邻近像素来修改自身像素值。

 

 

2、掩膜实现

了解了具体的原理,我们接下来通过代码来自己实现一下掩膜操作吧!

核心代码就是使用掩膜中心位置的值的五倍,减去上下左右四个位置的值:

	for (int i = 1; i < src_gray.rows-1; i++){for (int j = 1; j < src_gray.cols-1; j++){src_new.at<uchar>(i, j) =src_gray.at<uchar>(i, j) * 5 -   //中心src_gray.at<uchar>(i - 1, j) -   //上src_gray.at<uchar>(i + 1, j) -   //下src_gray.at<uchar>(i, j - 1) -   //左src_gray.at<uchar>(i, j + 1);    //右}}

全部代码如下:

#include<iostream>
#include<opencv2\opencv.hpp>using namespace std;
using namespace cv;int main() {Mat src = imread("./image/sign.png"); if (!src.data){cout << "ERROR : could not load image.\n";return -1;}Mat src_gray;cvtColor(src, src_gray, COLOR_BGR2GRAY);imshow("src_gray", src_gray);Mat src_new = Mat(Size(src_gray.cols,src_gray.rows),CV_8UC1);for (int i = 1; i < src_gray.rows-1; i++){for (int j = 1; j < src_gray.cols-1; j++){src_new.at<uchar>(i, j) =src_gray.at<uchar>(i, j) * 5 -   //中心src_gray.at<uchar>(i - 1, j) -   //上src_gray.at<uchar>(i + 1, j) -   //下src_gray.at<uchar>(i, j - 1) -   //左src_gray.at<uchar>(i, j + 1);    //右}}imshow("new src_gray", src_new);waitKey(0);return 0;
}

执行结果如下:

大家就能发现,图像中文字边界的位置的发生了明显变化。

3、API-filter2D

我们自己实现了我们的掩膜操作,在opencv中,我们提供了专门的API来实现掩膜操作:

void filter2D( InputArray src, OutputArray dst, int ddepth,                            InputArray kernel, Point anchor = Point(-1,-1),                            double delta = 0, int borderType = BORDER_DEFAULT 
);

函数参数含义如下:

(1)InputArray类型的src ,输入图像。

(2)OutputArray类型的dst ,输出图像,图像的大小、通道数和输入图像相同。

(3)int类型的ddepth,目标图像的所需深度。

(4)InputArray类型的kernel,卷积核(或者更确切地说是相关核)是一种单通道浮点矩阵;如果要将不同的核应用于不同的通道,请使用split将图像分割成不同的颜色平面,并分别对其进行处理。。

(5)Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。。

(6)double类型的delta,在将筛选的像素存储到dst中之前添加到这些像素的可选值。

(7)int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT。

在一般使用中,我们只涉及到前面四个参数,

对于第五个参数,我们都是默认中心为锚点,

对于第六个参数,一般来说我们很少会设置一个额外的值去调整像素值,所以也是默认为0的。

对于第七个参数,因为是刚开始,我们先不展开说明,因为后续我们还会讲到,在这里,我们先采用默认,让我们把重心放在前四个参数上面。

如下面这个例子:

filter2D( src, dst, src.depth(), kernel );

我们来使用一个完整的例子来说明一下:

 

执行结果如下:

我们能够发现,我们的文字出现了白色的边界。

如果我们再调整一下kernel,我们就可以得到很多类型的图像:

大家也可以自己尝试设置自己的kernel,一般来说有个原则就是尽量核所有数值加起来不要太大。一般都是让求得的值为1。

 

四、执行时间

我们留意到一个比较重要的内容,就是我们操作像素,是要遍历所有像素的,这是很耗时间的操作。特别是了解深度学习的同学,当做深度学习图像检测要遍历图像做训练时,运算量是极大的,所以我们需要获取执行时间,来分析算法的优劣,进行算法效率比较。

在opencv中,我们提供了专门的API来获取执行时间,全部功能代码如下:

	double t = (double)getTickCount();// do something ...t = ((double)getTickCount() - t) / getTickFrequency();cout << "消耗的时间为: " << t << endl;

这其中涉及到两个API:

	getTickCount();getTickFrequency();

第一个是:函数返回特定事件(例如,当机器打开时)后的刻度数。它可用于初始化RNG或通过读取函数调用前后的计时计数来测量函数执行时间。

第二个是:函数返回每秒的刻度数。

我们通过第一个执行得到运行前和运行后的刻度数,相减后得到运行过程中的刻度时长,然后除以每秒的刻度数,就能得到代码以秒为单位的运行时长了。

举个例子:

	double t = (double)getTickCount();// do something ...Mat src_new;Mat kernel = (Mat_<char>(3, 3) << 1, -4, 1, -1, 7, -1, 1, -4, 1);filter2D(src, src_new, src.depth(), kernel);imshow("src_new: 1, -4, 1, -1, 7, -1, 1, -4, 1", src_new);t = ((double)getTickCount() - t) / getTickFrequency();cout << "消耗的时间为: " << t << endl;

执行结果如下:

到这里我们就说完啦,大家多做练习哦!

这篇关于【opencv4.3.0教程】08之图像掩膜(Mask)操作与执行时间的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/482021

相关文章

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

Java实现远程执行Shell指令

《Java实现远程执行Shell指令》文章介绍使用JSch在SpringBoot项目中实现远程Shell操作,涵盖环境配置、依赖引入及工具类编写,详解分号和双与号执行多指令的区别... 目录软硬件环境说明编写执行Shell指令的工具类总结jsch(Java Secure Channel)是SSH2的一个纯J

sysmain服务可以禁用吗? 电脑sysmain服务关闭后的影响与操作指南

《sysmain服务可以禁用吗?电脑sysmain服务关闭后的影响与操作指南》在Windows系统中,SysMain服务(原名Superfetch)作为一个旨在提升系统性能的关键组件,一直备受用户关... 在使用 Windows 系统时,有时候真有点像在「开盲盒」。全新安装系统后的「默认设置」,往往并不尽编

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Python自动化处理PDF文档的操作完整指南

《Python自动化处理PDF文档的操作完整指南》在办公自动化中,PDF文档处理是一项常见需求,本文将介绍如何使用Python实现PDF文档的自动化处理,感兴趣的小伙伴可以跟随小编一起学习一下... 目录使用pymupdf读写PDF文件基本概念安装pymupdf提取文本内容提取图像添加水印使用pdfplum

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

Python从Word文档中提取图片并生成PPT的操作代码

《Python从Word文档中提取图片并生成PPT的操作代码》在日常办公场景中,我们经常需要从Word文档中提取图片,并将这些图片整理到PowerPoint幻灯片中,手动完成这一任务既耗时又容易出错,... 目录引言背景与需求解决方案概述代码解析代码核心逻辑说明总结引言在日常办公场景中,我们经常需要从 W

Java Scanner类解析与实战教程

《JavaScanner类解析与实战教程》JavaScanner类(java.util包)是文本输入解析工具,支持基本类型和字符串读取,基于Readable接口与正则分隔符实现,适用于控制台、文件输... 目录一、核心设计与工作原理1.底层依赖2.解析机制A.核心逻辑基于分隔符(delimiter)和模式匹