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

news2024/11/16 22:46:38

操作图片

  • 概述
  • 图片的导入和保存
  • 对导入的图片的操作
    • 获取像素值
      • 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.coloradmin.cn/o/1948849.html

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

相关文章

【LeetCode】141.环形链表、142. 环形链表 II(算法 + 图解)

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;数据结构 &#x1f4da;本系列文章为个人学…

基于PyCharm在Windows系统上远程连接Linux服务器中Docker容器进行Python项目开发与部署

文章目录 摘要项目结构项目开发项目上线参考文章 摘要 本文介绍了如何在Windows 10系统上使用PyCharm专业版2024.1&#xff0c;通过Docker容器在阿里云CentOS 7.9服务器上进行Python项目的开发和生产部署。文章详细阐述了项目结构的搭建、PyCharm的使用技巧、以及如何将开发项…

C#/WinFrom TCP通信+ 网线插拔检测+客服端异常掉线检测

Winfor Tcp通信(服务端) 今天给大家讲一下C# 关于Tcp 通信部分&#xff0c;这一块的教程网上一大堆&#xff0c;不过关于掉网&#xff0c;异常断开连接的这部分到是到是没有多少说明&#xff0c;有方法 不过基本上最多的两种方式&#xff08;1.设置一个超时时间&#xff0c;2.…

Python爬虫技术 第13节 HTML和CSS选择器

在爬虫技术中&#xff0c;解析和提取网页数据是核心部分。HTML 和 CSS 选择器被广泛用于定位网页中的特定元素。下面将详细介绍这些选择器如何在 Python 中使用&#xff0c;特别是在使用像 Beautiful Soup 或 Scrapy 这样的库时。 HTML 选择器 HTML 选择器基于 HTML 元素的属性…

Llama 3.1和xAI超集群加速AI军备竞赛

LLama 3.1 先来看看LLama 3.1 405B的效果&#xff0c;例如输入生成上海印象的四连图&#xff0c;然后一键再生成短视频&#xff0c;整体还是可圈可点。 从下面的各项基准而言&#xff0c;LLama3.1系列在同等量级中均有不俗的表现&#xff0c;尤其是405B已经和闭源的GPT-4o不分…

内网横向:PTHPTKPTT

1.PHT横向 2.PTK横向 3.PTT横向 1.PHT横向&#xff1a; 条件&#xff1a;有管理员的NTLM Hash 并且目标机器开 放445端口 在工作组环境中&#xff1a; Windows Vista 之前的机器&#xff0c;可以使用本地管理员组内用户进行攻击。 WindowsVista 之后的机器&#xff0c;只能是…

【iOS】—— Block总结

Block总结 1. Block的使用规范2. __block修饰符__block修饰符的底层原理 3. Block的类型NSGlobalBlockNSStackBlockNSMallocBlock 4. Block的实现及本质初始化部分调用部分本质 5. Block的捕获与内存管理捕获变量捕获对象内存管理 6. 循环引用什么是循环引用循环引用解决方法1.…

抓包工具Charles

1、抓包的目的 遇到问题需要进行分析 发现bug需要定位 检查数据传输的安全性 接口测试时&#xff0c;开发给的需求文档不详细 在弱网环境下APP的测试 2、Charles是java语言编写的程序&#xff0c;本质是一个代理服务器&#xff0c;通过拦截服务端和客户端的http请求&#xff0…

谷粒商城实战笔记-63-商品服务-API-品牌管理-OSS获取服务端签名

文章目录 一&#xff0c;创建第三方服务模块thrid-party1&#xff0c;创建一个名为gulimall-third-party的模块2&#xff0c;nacos上创建third-party命名空间&#xff0c;用来管理这个服务的所有配置3&#xff0c;配置pom文件4&#xff0c;配置文件5&#xff0c;单元测试6&…

西瓜视频下载助手,支持批量下载视频!

我们每天都在接触海量的视频内容&#xff0c;但想要保存自己喜爱的视频却常常受限于平台的各种限制。因此&#xff0c;今天给大家带来一个超级实用的神器——西瓜视频下载助手。 西瓜视频下载助手&#xff08;电脑&#xff09; 工具支持西瓜视频的下载和今日头条的视频下载&a…

kafka详解及应用场景介绍

Kafka架构 Kafka架构&#xff0c;由多个组件组成&#xff0c;如下图所示&#xff1a; 主要会包含&#xff1a;Topic、生产者、消费者、消费组等组件。 服务代理&#xff08;Broker&#xff09; Broker是Kafka集群中的一个节点&#xff0c;每个节点都是一个独立的Kafka服务器…

如何使用EXCEL访问WinCC中的实时数据实现报表

如果项目已经做好了&#xff0c;不想改动现有项目。那么可以使用 EXCEL 通过 OPC 方式访问 WinCC 项目的数据。预先定义好 EXCEL 表格样式&#xff0c;通过以下方式实现。通过以下步骤打开 EXCEL 中的 VB 编辑器 引用 WinCC 提供的 OPC 客户端 Control 控件: Siemens OPC DAAut…

Godot游戏制作 04平台设计

新建创景&#xff0c;添加AnimatableBody2D节点。 添加Sprite2D节点 拖动图片 剪裁图片&#xff0c;吸附模式&#xff1a;像素吸附 添加CollisionShape2D&#xff0c;设置实际形状为矩形 重命名AnimatableBody2D节点为Platform&#xff0c;保存场景&#xff0c;拖动platform场景…

VirtualBox 安装Centos 7 避坑指南 SSH连不上 镜像失效 静态网络配置等

背景 几乎每次安装Centos 7 时&#xff0c;都会遇到各种各样的问题&#xff0c;毕竟每次安装动辄就是半年几年&#xff0c;几乎都是在换工作时&#xff0c;有了新机器才会倒腾一次&#xff0c;时间久远&#xff0c;就会忘记一些细节&#xff0c;这次整理一下&#xff0c;避免以…

数字图像处理笔记(三) ---- 傅里叶变换的基本原理

系列文章目录 数字图像处理笔记&#xff08;一&#xff09;---- 图像数字化与显示 数字图像处理笔记&#xff08;二&#xff09;---- 像素加图像统计特征 数字图像处理笔记&#xff08;三) ---- 傅里叶变换的基本原理 文章目录 系列文章目录前言一、傅里叶变换二、离散傅里叶变…

Vue3与Element-plus配合 直接修改表格中的一项数据——控制输入框的显示与隐藏

利用控制与隐藏输入框,直接修改表格中的每一项数据。 <!-- 表格模块 --> <div><el-table :data"tablelist" style"width: 100%"><el-table-column align"center" prop"deposit" label"接单押金">&l…

【动态规划】力扣.213. 打家劫舍 II

你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房屋是紧挨着的。同时&#xff0c;相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一…

自动驾驶汽车普及之路

全球范围内自动驾驶汽车的普及正在加速。英国最近通过了 自动驾驶汽车法案 以便在未来几年内实现全自动驾驶和部分自动驾驶汽车安全融入社会。 更多自动驾驶汽车 目前&#xff0c;中国是世界上测试自动驾驶出租车最多的国家。而在美国&#xff0c;各大城市已将“自动驾驶出租车…

人工智能历史:从梦想到现实的变革之路

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

python机器学习8--自然语言处理(2)

1&#xff0e;移除用词 在很多情况下&#xff0c;有一些文章内的英文字符、标点符号分词的结果不符合自己的预期&#xff0c;会出现一些不想要的分词&#xff0c;此时就能通过以下的函数自己设定用词&#xff0c;并且删除。 jieba.analyse.set_stop_words("stop_words.tx…