当前位置: 首页 > news >正文

open cv像素的操作

本文涉及:
Mat图像矩阵的坐标关系解释 BGR颜色模型矩阵的解释
at函数直接访问像素值的解释 at模板函数的具体用法
关于存储类型名称ucharVec3b以及其他Vec向量元素类型的详细解释
使用指针访问像素值的解释 以及.ptr模板函数的使用
基于.at方法的各种像素操作 图像像素的遍历赋值
基于.at方法的单通道和三通道图像的像素反差处理
利用.at方与minmax函数的配合,对单通道的图片进行灰度值最大化(图像提亮)灰度值最小化(图像调暗)
利用bitwise_not接口函数(API单通道图像和三通道图像进行反差处理
利用convertTo接口函数对Mat矩阵的类型进行转换
利如RectAPI设定图像操作ROI区域,对图像进行分区域的处理


图像的像素

对图像的像素进行操作,我们可以实现 空间增强, 反色等广泛的目的.让我们先来看一下内存空间中图像矩阵,也就是Mat的矩阵数值部分是怎么存储的:

Mat图像矩阵(图片类型为单通道灰度时)
在这里插入图片描述
如果图像是一幅灰度图像,他就像这样, 从左到右,从上到下,依次是矩阵的每一行每一列,这时候矩阵M(i,j)的值自然就是当前点的灰度值了

Mat图像矩阵(图片类型为三通道BGR时)
在这里插入图片描述
对于彩色图像而言,由于它的像素分量并不是一个,所以在它的每一列的像素点中有分为了多个通道(channel),从这张常见的RGB图像矩阵中我们可以清楚的看出内存中矩阵是如何存储多通道的图像的,这里要注意的是: 在RGB色彩模型中,Opencv下的每一个子列(图中黑框部分)的颜色分量顺序分别为BGR,正好和RGB相颠倒(BGR即第一个分量是蓝色,第二个是绿色,第三个是红色)

清楚了图像在内存中的存储方式,我们也就可以来进行像素值的操作了


像素操作

.at函数直接访问像素值:

利用Matat函数可以访问元素.因为Mat可以接受任何类型的元素,所以at函数被实现成一个模板函数,在调用时必须指定图像元素的类型

Mat.at函数的标准格式:

Mat.at<存储类型名称>(行,列)[通道数]=需要的BGR颜色分量值

Mat.at函数的具体用法:

🔥图像为灰度单通道时存储类型为uchar(0-255)且不需要输入通道数,直接修改像素值

Mat src = imread("D:/实验台/机器视觉/测试图片/白人女.jpg",IMREAD_GRAYSCALE);//读取为单通道灰度图像
	
src.at<uchar>(254,254)= 255; 

在这里插入图片描述


🔥图像为RGB三通道时存储类型为Vec3b(uchar类型的,长度为3的vector向量),此时需要使用.at函数逐通道进行像素值修改

src.at<Vec3b>(255, 255)[0] = 0;//修改B通道数据
src.at<Vec3b>(255, 255)[1] = 0;//修改G通道数据
src.at<Vec3b>(255, 255)[2] = 255;//修改R通道数据

在这里插入图片描述


🔥特殊情况: 图像为RGB三通道时存储类型为Vec3b(uchar类型的,长度为3的vector向量),但此时未指明需要修改的通道数就对目标坐标的像素值进行修改

src.at<Vec3b>(255, 255) = 255;

在这里插入图片描述
我们发现,此时虽然我们未指定需要修改的通道数,但是此函数此时默认把BGR三通道中的第一个通道B设置为了我们此前设定的值,且将GR两通道全部置为0


关于存储类型名称(uchar和Vec3b)

由于在OpenCV中, 使用imread读取到的Mat图像数据都是用uchar类型的数据存储,所以对于灰度图,每个像素点的数据都是uchar类型的数据,对于RGB三通道的图像,每个点的数据都是一个Vec3b类型的数据(代表3个8位的数值 也就是3个uchar

此外,还有针对其它元素类型的向量 :

浮点数3维向量类型 Vec3f
整型数3维向量类型 Vec3i
短整型数3维向量类型 Vec3s
双精度数3维向量类型 Vec3d

使用Mat的模板子类Mat_,直接通过operator运算符重载来访问元素

//🔥适用于灰度单通道图像
Mat_<uchar>im(src);
im(255, 255) = 0;

//🔥适用于BGR三通道图像
Mat_<Vec3b>im(src);
im(255, 255)[0]= 0;
im(255, 255)[1] = 0;
im(255, 255)[2] = 255;

在这里插入图片描述
如果使用Mat_类,那么就可以在Mat变量声明时就确定元素的类型,再次访问元素时不再需要指定元素类型,即使得代码简洁又减少了程序出错的可能性.

总结:🎯
.at方法适合对像素点进行随机访问,当涉及像素的遍历时,考虑处理的性能,应该使用更高效的方法.


使用指针访问像素值:

首先要确定Opencv中的rowscols在图像中的坐标关系:

在这里插入图片描述

指针访问像素值的语法:

 uchar * DataPtr=image.ptr<uchar>(Data Of Rows)[Data Of Cols];

拆分解释:uchar *为指向某像素点坐标的指针类型,DataPtr为指针名,image为指针取值的目标图像(实际上是Mat矩阵),.ptr<>中的类型符是.ptr这个函数返回的指针类型,.ptr<uchar>意为用uchar类型的指针指向image这个Mat矩阵中 (Data Of Rows) [Data Of Cols] 处的像素值的地址.

先看一个Mat数据类型指针ptr的使用案例,对ptr这个指针进行分析:

 Mat image = Mat(400, 600, CV_8UC1); //使用Mat指令新建一个宽(cols)400,长600(rows),图像深度为1的灰度图
  
 uchar * MatPtrA = image.ptr<uchar>(0); 
 uchar * MatPtrB = image.ptr<uchar>(1);
 uchar * MatPtrC = image.ptr<uchar>(0)[1];

其中:
MatPtrA是指向image第一行第一个元素的指针
MatPtrB是指向image第二行第一个元素的指针
MatPtrC是指向image第一行第二个元素的指针

由此我们可以得出结论:
image.ptr<uchar>()[]这个函数返回的是一个二维的数组指针,其有两个传入参数,其后方第一个传入参数表示Mat矩阵中的rows(宽),第二个传入参数表示Mat矩阵中的cols(长),通过这个二维指针两个传入参数的配合定位,我们能得到某个Mat矩阵中任何坐标位置的像素的地址,就可以利用这个指针快速的对图片进行遍历修改像素值.

使用.ptr二维指针对Mat矩阵中指定位置的像素点进行修改:

Mat image = Mat(500, 600, CV_8UC1);
image = Scalar(255);
//🔥建立一个列数为500,行数为600的纯白色的单通道图片image

 image.ptr<uchar>(255)[260] = 0;
//🔥以指针的形式,对image这个Mat矩阵第255列,第260行的像素点赋值为0.


运行结果:
在这里插入图片描述
位于第255列,260行的像素点已被赋值为0

使用.ptr二维指针和for循环对 单通道 图像像素进行遍历修改:
在这里插入图片描述
运行结果:
在这里插入图片描述

使用.ptr二维指针和for循环对 多通道图像像素进行遍历修改:

上述使用ptr指针修改的都单通道的Mat矩阵类型(灰度图),至于多通道的图像,若想达到和处理灰度图时一样的效果,我们需要两种方法:

方法一:
我们需要在 将Mat矩阵的宽高信息赋值给rowcol时,将col的数值变更为image.cols乘以image这个Mat矩阵的通道数(一般为3)

这是因为当Mat矩阵为3通道的矩阵时,矩阵每一行中需要对其中的3个颜色分量都进行处理,所以要把Mat矩阵的行数*3才能保证遍历到所有像素(否则只会遍历cols/3个像素),如图:
在这里插入图片描述

int row = image.rows;//500(行 高)
int col = image.cols*image.channels(); //600(列 宽)

方法二:
由于单通道矩阵的用char存储,而3通道的矩阵可以使用Vec3b存储,所以我们可以不对Mat矩阵的行数进行乘以通道数的处理,而是在调用Mat指针.ptr的函数时将其返回的指针类型变更为Vec3b型.

//代码...
Vec3b* pt = image.ptr<Vec3b>(j);
//代码...

这两种方法的运行结果:
在这里插入图片描述


总结:🎯
opencv中遍历像素的方法主要有两种:利用.at模板函数对像素直接进行访问和利用.ptr二维指针对Mat矩阵中的像素进行地址访问以改变其像素值.
除了这两种方法之外,我们还可以使用以下方法对像素进行操作: 用迭代器iterator遍历像素


像素的操作实际运用示例

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <math.h>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
	Mat src = imread("D:/实验台/机器视觉/测试图片/白人女.png");
	imshow("原图", src);
	if (src.empty())
	{
		cout << "无法正常加载图片" << endl;
		return -1;
	}

	Mat dst;
	🔥使用.create函数使用已有的Mat矩阵 赋值新建的Mat矩阵(dst) 其大小和深度信息
 	dst.create(src.size(), src.type());//dst create 后的通道数和src是一致的 都为3

使用.at模板函数对alone这个Mat矩阵进行像素操作,遍历其每个像素并取反赋值使其具有反差(负片)效果

	Mat alone;
	
	cvtColor(src, alone, CV_BGR2GRAY);
	//🔥之所以cvtColor函数转换alone的深度为(单通道 灰度)是因为以下像素值取反算法中只对Mat矩阵的一个通道的像
	//素值进行了取反.

	🔥使用.at模板函数对alone这个Mat矩阵进行像素操作,遍历其每个像素并取反赋值使其具有反差(负片)效果
	int height = alone.rows;//获取图像行数
	int width = alone.cols;//获取图像列数

	for (int row = 0; row < height; row++)//遍历图像各个像素点的像素值
	{
		for (int col = 0; col < width; col++)
		{
			int gray = alone.at<uchar>(row, col);//读取一个gray像素点的像素值
			alone.at<uchar>(row, col) = 255 - gray;//🔥逐像素值取反赋值
		}
	}
	imshow("单通道反差处理1", alone);//反差之后有负片效果

在这里插入图片描述


各种像素操作示例

基于.at方法的各种像素操作:

使用.at函数遍历Mat矩阵中像素的值并对其进行操作,分通道数的(单通道图像或多通道)对灰度图像或彩色图像进行反差等处理(也就是使用算法实现bitwise_notAPI的功能)

	dst.create(src.size(), src.type());//赋值新建Mat矩阵dst的大小和类型属性,备用(大小和通道和src原图一致)
	
	//🔥使用三个变量获取src原图矩阵的宽高和通道数信息,备用
	height = src.rows;//高信息
	width = src.cols;//宽信息
	int tds = src.channels();//通道数信息
 
   //🔥以之前获取的原图src的大小和类型信息作为参数,利用for循环将src的每个位置的像素的信息赋值给需要的地方
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{

像素操作: 使单通道的图像的像素反转

	if (tds == 1)//🔥通道判断(使单通道颜色反转)
	{
		int gray = alone.at<uchar>(row, col) ;//🔥先获取gray这个目标图像(row,col)位置的像素的像素值
		alone.at<uchar>(row, col) = 255 - gray;//🔥再使用.at方法使用255这个像素值顶值减去gray当前位置的像素值,使图像反转
	}

在这里插入图片描述
像素操作: 使三通道的图像的像素反转

		else if(tds==3)//通道判断(使3通道图片色彩反转)
		{
			🔥先获取原图,每个通道,某个位置的像素的值,并分别赋值给int变量 [b g r]

			int b = src.at<Vec3b>(row, col)[0];
			//获取row行col列 0通道的像素值 ,vec3b可用来读取 3通道的rgb图像像素值
			int g = src.at<Vec3b>(row, col)[1];
			//获取row行col列1通道的像素值
			int r  = src.at<Vec3b>(row, col)[2];
			//获取row行col列2通道的像素值

			🔥图像反转:255减去原值所有通道某点坐标像素值的算法,并将得到的像素差值赋值给dst这个新建矩阵
			
			dst.at<Vec3b>(row, col)[0]=255-b;
			//反转row行col列0通道的像素值
			dst.at<Vec3b>(row, col)[1]=255-g;
			//反转row行col列1通道的像素值
			dst.at<Vec3b>(row, col)[2]=255-r;
			//反转row行col列2通道的像素值

在这里插入图片描述

			🔥 扩展:改变矩阵某个通道的值(修改dst矩阵row行col列0通道的像素值为58)

			dst.at<Vec3b>(row, col)[0] = 58

			🔥 扩展: 矩阵赋值时,保持或清零某个通道的值

			dst.at<Vec3b>(row, col)[0] = b;
			//保持row行col列0通道的像素值
			dst.at<Vec3b>(row, col)[1] = g;
			//保持row行col列1通道的像素值
			dst.at<Vec3b>(row, col)[2] = 0;
			//清零row行col列2通道的像素值

像素操作: 利用.at方与minmax函数的配合,对单通道的图片进行灰度值最大化(图像提亮)灰度值最小化(图像调暗)

			alone.at<uchar>(row, col) = min(r, max(b, g));
			//🔥取rgb中的最小值作为灰度值 灰度处理后使用此函数 图片会更暗沉

			alone.at<uchar>(row, col)= max(r, max(g, b));
			//🔥取rgb中的最大值作为灰度值 灰度处理后使用此函数 图片会更亮白
		}
	}
}

灰度值最大化(图像提亮):
在这里插入图片描述
灰度值最小化(图像调暗):

在这里插入图片描述


像素操作: 利用bitwise_not接口函数(API单通道图像和三通道图像进行反差处理)

bitwise_not(src, dst);//🔥使用bitwise_not函数对三通道的图像dst进行反差处理
imshow("使用bitwise_not函数对三通道图像进行反差处理", dst);

Mat dstgray;
cvtColor(src, dstgray, CV_BGR2GRAY);
bitwise_not(dstgray, dstgray);//🔥使用bitwise_not函数对单通道的图像dstgray进行反差处理
imshow("使用bitwise_not函数对单通道图像进行反差处理", dstgray);

在这里插入图片描述


像素操作: 使用convertTo接口函数对Mat矩阵的类型进行转换(后期会涉及,所以不进不详细解释)

Mat dst32f;
src.convertTo(dst32f, CV_32F);

像素操作:利用RectAPI设定图像操作ROI区域,对图像进行分区域的处理

Mat img=src.clone();

Rect r(0,0,250, 54);
//🔥第一个0为x坐标轴 左右 第二个0为y坐标轴 上下 第三个500为图片宽度 第四个54为图片的高度
Mat smallimg = img(r);

在这里插入图片描述


来自:

OpenCV对像素的操作

相关文章:

  • 一个简单的Spring Web Service示例
  • open cv图片混合
  • C++第五章习题
  • open cv对比度与亮度调节
  • POJ 1905
  • open cv绘制形状与文字
  • open cv均值 中值 高斯 双边高斯 滤波及模糊
  • C语言运算符优先级
  • open cv膨胀与腐蚀
  • 浅谈外链因何而存在?
  • open cv形态学操作
  • 微软发布Surface平板电脑 再度挑战苹果
  • open cv提取水平线与垂直线
  • 用VBScript实现Zip解压缩目录中的所有文件
  • open cv图像上采样与下采样
  • 【译】JS基础算法脚本:字符串结尾
  • express + mock 让前后台并行开发
  • js数组之filter
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Linux下的乱码问题
  • Python连接Oracle
  • sessionStorage和localStorage
  • SpiderData 2019年2月13日 DApp数据排行榜
  • 多线程事务回滚
  • 诡异!React stopPropagation失灵
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 前端学习笔记之观察者模式
  • 提醒我喝水chrome插件开发指南
  • 微信小程序--------语音识别(前端自己也能玩)
  • 为视图添加丝滑的水波纹
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • 小程序开发中的那些坑
  • 新书推荐|Windows黑客编程技术详解
  • 一个完整Java Web项目背后的密码
  • zabbix3.2监控linux磁盘IO
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​软考-高级-系统架构设计师教程(清华第2版)【第20章 系统架构设计师论文写作要点(P717~728)-思维导图】​
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • ​油烟净化器电源安全,保障健康餐饮生活
  • (1)SpringCloud 整合Python
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (ZT)出版业改革:该死的死,该生的生
  • (ZT)薛涌:谈贫说富
  • (二)JAVA使用POI操作excel
  • (二)PySpark3:SparkSQL编程
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (分布式缓存)Redis哨兵
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (附源码)计算机毕业设计SSM保险客户管理系统