【OpenCV C++20 学习笔记】操作图片

操作图片

  • 概述
  • 图片的导入和保存
  • 对导入的图片的操作
    • 获取像素值
      • Point类型和图片像素
    • 内存管理和引用计数
    • 一些简便操作
    • 图片可视化
      • 更精确的类型转换

概述

在本专栏的第一篇文章中就介绍了一个用OpenCV处理图片的实例(《图片处理基础》),这篇文章进一步详细介绍OpenCV中处理图片的一些操作。
我这里使用的都是C++20的初始化语法,之前版本的C++可以参考下面这节中不同版本C++语法的对比。

图片的导入和保存

从图片文件中导入图片数据:

Mat img = imread(filename);
Mat imgCpp20 { imread(filename) };	//C++20的初始化语法

如果导入的是jpg格式的图片,那么默认是3通道的图像数据。如果想要以灰度(只有黑白两色)格式导入,可以这样导入:

Mat img = imread(filename, IMREAD_GRAYSCALE);
Mat imgCpp20 { imread(filename, IMREAD_GRAYSCALE) };	//C++20的初始化语法

要将数据保存到图片:

imwrite(filename, img);

对导入的图片的操作

获取像素值

要获取像素的值,必须要知道图片的类型以及颜色通道数量。

关于图片数据的类型,可以参考该合集中的《基本图像容器——Mat》

如果要获取一个单通道灰度图片(即,8UC1类型)中(x, y)坐标上的像素的值,可以使用下面这条语句:

Scalar intensity { img.at<uchar>(y, x) };

**注意这里坐标的表示是(y, x)。**因为在OpenCV中图片都是用矩阵来表示的,而矩阵一般是通过(row, col)的先行后列的模式来定位的,为了统一,OpenCV中坐标的表示也是纵坐标在前、横坐标在后。
在C++中,还可以使用Point来换回传统的坐标表示:

Scalar intensity { img.at<uchar>(Point(x, y) };

如果是3通道的BGR格式的图片,要获取某个像素上每个通道的颜色值,可以使用以下方法:

Vec3b intensity { img.at<Vec3b>(y, x) };
uchar blue { intensity.val[0] };
uchar green { intensity.val[1] };
uchar red { intensity.val[2] };

可以看到,储存单通道的像素值,使用的是Scalar类型;而储存3通道的像素值,使用的是Vec3b类型;3通道中单个通道的颜色值则是uchar类型。
获取像素值的方法也可以用来修改像素值:

img.at<uchar>(y, x) = 128;

Point类型和图片像素

在C++中,用2D或3D的Point类型的数组也可以创建Mat对象,这种Mat矩阵只有1列,每一行对应一个Point对象;而且矩阵的数据类型应该是32FC2或者32FC3,相应的Point对象的类型也应该是Point2f或者Point3f。示例如下:

vector<Point2f> points;
// ... 填充该数组
Mat pointsMat { Mat(points) };

这种矩阵可以从中获取Point对象:

Point2f point { pointsMat.at<Point2f>(i, 0) };

内存管理和引用计数

如该合集的《基本图像容器——Mat》中详细描述的那样,Mat对象只储存指向矩阵数据的指针以及描述矩阵数据的一些信息,所以若干个Mat对象共享同一个矩阵数据是被允许的。下面结合一个比较复杂的例子来讨论这个问题:

vector<Point3f> points;
// ... 填充数组
Mat pointsMat { Mat(points).reshape(1) };	//reshape函数重新设置Mat对象的通道数

上面的例子中pointsMat最终还是一个N3的矩阵,并不是N1的矩阵。因为reshape函数不复制数据,它修改的只是Mat对象中对矩阵的描述。所以矩阵还是原来的N*3的矩阵,只不过在Mat(points)中创建的临时Mat对象将它描述成3通道的矩阵,而pointsMat将其描述成单通道的矩阵。
要想真正的复制数据,则需要用到cv::Mat::copyTo或者cv::Mat::clone函数:

Mat img { imread("image.jpg");
Mat img1 { img.clone() };

**空Mat对象也可以作为函数的输出参数,用来储存计算结果。**这是因为OpenCV中的函数都会调用Mat::create方法来修改输出矩阵。如果输出矩阵是空的,那就为它分配所需要的内存;如果输出矩阵不是空的,而且大小和类型都刚好,那就不会进行任何更改;如果大小和类型不符合需求,就会先释放原有的内存然后重新分配新的内存。示例如下:

Mat img{ imread("image.jpg");
Mat sobelx;
Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0,-1, 5, -1,0, -1, 0);
filter2D(img, sobelx, img.depth(), kernel);	//掩码操作函数,第二个形参为输出的矩阵

一些简便操作

将灰度图片变成黑色图片:

img = Scalar(0); 	//img为储存灰度图片数据的Mat对象

运行结果如下:
灰度图片变成黑色图片

选择兴趣区(ROI):

Rect r(10, 10, 100, 100);
Mat smallImg { img(r) };

定义在<opencv2/imgproc.hpp>模块中的cvtColor函数可以将BGR格式的图片转换成灰度图片:

Mat Img { imread("image.jpg") };
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);

将图片从8UC1格式转换成32FC1格式:

src.convertTo(dst, CV_32F);	//src为原矩阵,dst为转换后的矩阵

图片可视化

在开发过程中能及时看到算法处理的结果是很有帮助的。OpenCV提供了一个简便的图片可视化方法。例如,一个8U格式的图片可以这样展示:

Mat img { imread("image.jpg") };
namedWindow("image", WINDOW_AUTOSIZE);	//可以不用,因为下面的imshow也会自动创建窗口
imshow("image", img);
waitKey();

waitKey();函数开启一个信息传输循环,等待在图片展示窗口上的按键操作,一旦有检测到按键就会停止循环,执行下面的语句。
其他格式的图片需要转换成8U格式的,才能在窗口展示,这就涉及到了类型转换

更精确的类型转换

在该合集的《矩阵上的掩码(mask)操作》中有提到过类型转换的问题。saturate_cast可以采取截断的方法避免信息的丢失,但它只是保证数据落在值域之内,没有进行对应的缩放。下面是一个更精确的类型转换的例子:

Mat img { imread("image.jpg") };
Mat gray;
cvtColor(img, grey, COLOR_BGR2GRAY);
Mat sobelx;
Sobel(grey, sobelx, CV_32F, 1, 0);	//得到一个32F格式的sobelx对象
double minVal, maxVal;
minMaxLoc(sobelx, &minVal, &maxVal); //找到sobelx中的最小值和最大值
Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));	//转换语句
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", draw);
waitKey();

上例中的convertTo语句的最后两个参数是用来将原来的32F格式的值转换成8U格式的。
convertTo的4个参数分别是:

  • 目标矩阵 m m m,储存转换结果
  • 目标格式 r t y p e rtype rtype,转换后的格式
  • α α α
  • β β β
    α α α β β β值,则会用来进行以下运算:
    m ( x , y ) = s a t u r a t e _ c a s t < r t y p e > ( α ( ∗ t h i s ) ( x , y ) + β ) ; m(x,y) = saturate\_cast<rtype>(α(*this)(x,y)+β); m(x,y)=saturate_cast<rtype>(α(this)(x,y)+β);
    可以看出, α α α实际上是一个缩放系数,所以上例将255.0/(maxVal - minVal)作为 α α α。因为255是8U格式的最大值和最小值之间的差,将它除以原始矩阵中的最大值与最小值之间的差,相当于是两个值域的比值。另一方面,β则是缩放后进行偏移量。上例将-minVal * 255.0/(maxVal - minVal)作为偏移量,代表所有的原始值在缩放之后都要向最小值偏移一定的距离。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL injection UNION attacks SQL注入联合查询攻击

通过使用UNION关键字&#xff0c;拼接新的SQL语句从而获得额外的内容&#xff0c;例如 select a,b FROM table1 UNION select c,d FROM table2&#xff0c;可以一次性查询 2行数据&#xff0c;一行是a&#xff0c;b&#xff0c;一行是c&#xff0c;d。 UNION查询必须满足2个条…

实战解读:Llama Guard 3 Prompt Guard

前序研究&#xff1a;实战解读&#xff1a;Llama 3 安全性对抗分析 近日&#xff0c;腾讯朱雀实验室又针对 Llama 3.1 安全性做了进一步解读。 2024年7月23日晚&#xff0c;随着Llama3.1的发布&#xff0c;Meta正式提出了“Llama系统”的概念&#xff0c;通过系统级的安全组件对…

ansible基础讲解和加密文件讲解

ansible最重要的三个文件 /etc/ansible/ansible.cfg #####ansible的配置文件 /etc/ansible/host ##清单文件inventory ansible-navigator.yml ####以yml结尾的文件可以理解为conf结尾的文件&#xff0c;是配置文件&#xff0c;用于设置剧本playbook playbook讲解 以.yml结…

Java泛型理解这一篇就够了

好文推荐&#xff0c;请阅读此文&#xff1a;Java泛型最佳实践 总结&#xff1a; 泛型类 泛型接口 泛型函数 通配符 通配符是为了让Java泛型支持范围限定&#xff0c;这样使得泛型的灵活性提升&#xff0c;同时也让通用性设计有了更多的空间。 <?>&#xff1a;无界…

【SpringBoot】2 项目搭建

创建项目 1&#xff09;确实本地 jdk 版本 打开命令行窗口&#xff1a;快捷键 Windows R&#xff0c;输入 CMD&#xff0c;敲回车 执行命令&#xff1a;java -version 2&#xff09;在项目 clone 的位置创建 Spring Boot 项目&#xff0c;使用 Maven 进行依赖管理&#xff…

Python 机器学习求解 PDE 学习项目——PINN 求解二维 Poisson 方程

本文使用 TensorFlow 1.15 环境搭建深度神经网络&#xff08;PINN&#xff09;求解二维 Poisson 方程: 模型问题 − Δ u f in Ω , u g on Γ : ∂ Ω . \begin{align} -\Delta u & f \quad & \text{in } \Omega,\\ u & g \quad & \text{on } \Gamma:\p…

vue3 vxe-table 点击行,不显示选中状态,加上设置isCurrent: true就可以设置选中行的状态。

1、上个图&#xff0c;要实现这样的&#xff1a; Vxe Table v4.6 官方文档 2、使用 row-config.isCurrent 显示高亮行&#xff0c;当前行是唯一的&#xff1b;用户操作点击选项时会触发事件 current-change <template><div><p><vxe-button click"sel…

【个人记录】pkg可以将Node.js应用打包为可执行文件

背景 之前按客户需求做了一个简易定时任务应用&#xff0c;完成后为方便客户使用需要打包为可执行文件。 pkg工具 pkg 是一个非常流行的工具&#xff0c;它能够将 Node.js 应用打包成独立的可执行文件。它支持多个平台&#xff0c;包括 Windows、macOS 和 Linux。 测试环境…

【SpringBoot】 4 Thymeleaf

官网 https://www.thymeleaf.org/ 介绍 Thymeleaf 是一个适用于 Web 和独立环境的现代服务器端 Java 模板引擎。 模板引擎&#xff1a;为了使用户界面和业务数据分离而产生的&#xff0c;它可以生成特定格式的文档&#xff0c;用于网站的模板引擎会生成一个标准的 html 文档…

Hadoop单机版环境搭建

一 . 案例信息 Hadoop 的安装部署的模式一共有三种&#xff1a; 本地模式&#xff0c;默认的模式&#xff0c;无需运行任何守护进程&#xff08; daemon &#xff09;&#xff0c;所有程序都在单个 JVM 上执行。由 于在本机模式下测试和调试 MapReduce 程序较为方便&#x…

逻辑操作符 、||、!

逻辑操作符为提供逻辑判断的功能&#xff0c;能够构建更复杂的表达式所以有以下三种运算符 &#xff01;&#xff1a;逻辑取反运算符&#xff08;可以改变单个运算符的真假&#xff09;。 &&&#xff1a;逻辑与运算符&#xff0c;就是并且的意思。当两侧均为真的时候…

微信小程序之调查问卷

一、设计思路 1、界面 调查问卷又称调查表&#xff0c;是以问题的形式系统地记载调查内容的一种形式。微信小程序制作的调查问卷&#xff0c;可以在短时间内快速收集反馈信息。具体效果如下所示&#xff1a; 2、思路 此调查问卷采用服务器客户端的方式进行设计&#xff0c;服…

【CodinGame】趣味算法(教学用) CLASH OF CODE -20240726

文章目录 正文字符图形快乐蛇进度条 写在最后END 正文 字符图形 import math import sys# Auto-generated code below aims at helping you parse # the standard input according to the problem statement.zreb int(input())mylist [] for i in range(1, zreb):mylist.app…

Godot入门 03世界构建1.0版

在game场景&#xff0c;删除StaticBody2D节点&#xff0c;添加TileMap节点 添加TileSet图块集 添加TileSet源 拖动图片到图块&#xff0c;自动创建图块 使用橡皮擦擦除。取消橡皮擦后按住Shift创建大型图块。 进入选择模式&#xff0c;TileMap选择绘制&#xff0c;选中图块后在…

一步步教你学会如何安装VMare虚拟机(流程参考图)

前言&#xff1a;一步步教你安装VMare虚拟机&#xff08;此版本为17.5。2版本&#xff09;。 1、安装 2、确认协议 3、选择位置存放 4、选择第二个 5、都不选。 6、都选提供便捷操作 7、点击许可证&#xff0c;将密钥输入&#xff08;可以在网络寻找自己版本的密钥&#xff…

高级及架构师高频面试题-基础型

1、设计模式有哪些原则&#xff08;待解释的更直白&#xff09; 单一职责原则&#xff1a;一个类或方法应只负责一项职责&#xff0c;避免一个类因为多个变化原因而改变。开闭原则&#xff1a;软件实体应对扩展开放&#xff0c;对修改封闭。比如要增加用户类别的时候可以新增一…

【资料分享】2024钉钉杯大数据挑战赛A题思路解析+代码演示

2024第三届钉钉杯大学生大数据挑战赛今天已经开赛&#xff0c;【A题】思路解析代码&#xff0c;资料预览&#xff1a;

Apache Doris + Paimon 快速搭建指南|Lakehouse 使用手册(二)

湖仓一体&#xff08;Data Lakehouse&#xff09;融合了数据仓库的高性能、实时性以及数据湖的低成本、灵活性等优势&#xff0c;帮助用户更加便捷地满足各种数据处理分析的需求。在过去多个版本中&#xff0c;Apache Doris 持续加深与数据湖的融合&#xff0c;已演进出一套成熟…

在Linux、Windows和macOS上释放IP地址并重新获取新IP地址的方法

文章目录 LinuxWindowsmacOS 在Linux、Windows和macOS上释放IP地址并重新获取新IP地址的方法各有不同。以下是针对每种操作系统的详细步骤&#xff1a; Linux 使用DHCP客户端&#xff1a;大多数Linux发行版都使用DHCP&#xff08;动态主机配置协议&#xff09;来自动获取IP地址…

php--高级反序列化

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…