open cv像素的操作
本文涉及:
Mat
图像矩阵的坐标关系解释 BGR颜色模型矩阵的解释
at
函数直接访问像素值的解释 at
模板函数的具体用法
关于存储类型名称uchar
和Vec3b
以及其他Vec向量元素类型的详细解释
使用指针访问像素值的解释 以及.ptr
模板函数的使用
基于.at
方法的各种像素操作 图像像素的遍历赋值
基于.at
方法的单通道和三通道图像的像素反差处理
利用.at
方与min
和max
函数的配合,对单通道的图片进行灰度值最大化(图像提亮)和灰度值最小化(图像调暗)
利用bitwise_not
接口函数(API
对单通道图像和三通道图像进行反差处理
利用convertTo
接口函数对Mat矩阵的类型进行转换
利如Rect
API设定图像操作ROI
区域,对图像进行分区域的处理
图像的像素
对图像的像素进行操作,我们可以实现 空间增强, 反色等广泛的目的.让我们先来看一下内存空间中图像矩阵,也就是Mat的矩阵数值部分是怎么存储的:
Mat图像矩阵(图片类型为单通道灰度时)
如果图像是一幅灰度图像,他就像这样, 从左到右,从上到下,依次是矩阵的每一行每一列,这时候矩阵M(i,j)的值自然就是当前点的灰度值了
Mat图像矩阵(图片类型为三通道BGR时)
对于彩色图像而言,由于它的像素分量并不是一个,所以在它的每一列的像素点中有分为了多个通道(channel),从这张常见的RGB图像矩阵中我们可以清楚的看出内存中矩阵是如何存储多通道的图像的,这里要注意的是: 在RGB色彩模型中,Opencv下的每一个子列(图中黑框部分)的颜色分量顺序分别为BGR,正好和RGB相颠倒(BGR即第一个分量是蓝色,第二个是绿色,第三个是红色)
清楚了图像在内存中的存储方式,我们也就可以来进行像素值的操作了
像素操作
.at函数直接访问像素值:
利用Mat
的at
函数可以访问元素.因为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中的rows
和cols
在图像中的坐标关系:
指针访问像素值的语法:
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矩阵的宽高信息赋值给row
和col
时,将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_not
API的功能)
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
方与min
和max
函数的配合,对单通道的图片进行灰度值最大化(图像提亮)和灰度值最小化(图像调暗)
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);
像素操作:利用Rect
API设定图像操作ROI
区域,对图像进行分区域的处理
Mat img=src.clone();
Rect r(0,0,250, 54);
//🔥第一个0为x坐标轴 左右 第二个0为y坐标轴 上下 第三个500为图片宽度 第四个54为图片的高度
Mat smallimg = img(r);
来自:
OpenCV对像素的操作