本文目录
1. 像素读写
2. 图像通道与均值方差计算
3. 算术操作与调整图像的亮度和对比度
4. 基于权重的图像叠加
5. Mat的其他各种像素操作
1. 像素读写
- Mat作为
图像容器
,其数据部分存储了图像的像素数据
,我们可以通过相关的API来获取图像数据部分
; - 在获取图像数据的时候,知道
Mat的类型
与通道数目
关重要,
根据Mat的类型
与通道数目
,开辟适当大小的内存空间
,
然后通过get方法
就可以循环实现每个像素点值的读取、修改
,
然后再通过put方法修改与Mat对应的数据部分
。
常见的Mat的像素读写get与put方法支持
如下表:

- 默认情况下,
imread
方式将Mat对象类型
加载为CV_8UC3
,
本系列笔记跟随原著默认
提到的加载图像文件均为Mat对象、类型均为CV_8UC3
、通道顺序均为BGR
。 - 上表中所列举的是当前OpenCV支持的读取图像的方法;
使用时若需要将像素值写入到Mat对象
中,使用与每个get
方法相对应的put
方法即可。 - 根据
开辟
的缓存区域data数组的大小
,
读写像素既可以每次从Mat中读取一个
像素点数据,
或者可以每次从Mat中读取一行
像素数据,
还可以一次从Mat中读取全部
像素数据。
下面演示对Mat对象中的每个像素点的值都进行取反操作
,并且分别用这三种方法实现像素操作
。
- 首先要将图像
加载为Mat对象
,
然后获取图像的宽、高以及通道数channels(特别注意这三个值,接下来一直用到,尤其channels)
:
Mat src = Imgcodecs.imread(fileUri.getPath());
if(src.empty()){
return;
}
int channels = src.channels();
int width = src.cols();
int height = src.rows();
接下来便可以通过方才所述三种方式读取像素数据、修改、写入
并比较它们的执行时间
。
1.1.从Mat中每次读取一个像素点数据
对于CV_8UC3
的Mat类型
来说,对应的数据类型
是byte
;
则先初始化byte数组data
,用来存取每次读取出来的一个像素点的所有通道值
,数组的长度
取决于图像通道数目
。
完整代码如下:
byte[] data = new byte[channels];
int b=0, g=0, r=0;
for(int row=0; row<height; row++) {
for(int col=0; col<width; col++) {
// 读取
src.get(row, col, data);//!!!!!!!!!!!!!!!!!!!!!!!读取一个px
b = data[0]&0xff;
g = data[1]&0xff;
r = data[2]&0xff;
// 修改
b = 255 - b;
g = 255 - g;
r = 255 - r;
// 写入
data[0] = (byte)b;
data[1] = (byte)g;
data[2] = (byte)r;
src.put(row, col, data);
}
}
补充诠释:
- 一个px有多个通道;
- 一个通道配给它一个数组元素;
- 1.2中逐行读取时的一个列(某行中的某个列其实就是一个数组元素而已)不是px,
而只是某个px的一个channel而已;- 1.3 同理
- 即1.2 以及1.3 中,data的一个元素,不是px,而只是某个px的一个channel而已;
1.2 从Mat中每次读取一行像素数据
首先需要定义每一行像素数据数组的长度
,这里为图像宽度
乘以每个像素的通道数目
。
接着循环修改每一行的数据
;
这里get方法
的第二个参数 col = 0
的意思是从每一行的第一列开始获取像素数据
。
完整代码如下:
// each row data
byte[] data = new byte[channels*width];//channels 是一个px的通道数;width是一个行的px的个数;
// loop
int b=0, g=0, r=0;
int pv = 0;
for(int row=0; row<height; row++) {
src.get(row, 0, data);
/*get一整行的px数据,存进data;形象地说,是以 位置是(row, 0)的第一个px的第一个channel为起始元素,获取一个data长度的数据;
数据一个元素(channel)一个元素(channel)地存进数组data, 每个元素是某个px的一个channel;*/
for(int col=0; col<data.length; col++) {//行中循环列,处理内容:修改一整行的数据
// 读取
pv = data[col]&0xff;
// 修改
pv = 255 - pv;
data[col] = (byte)pv;
}
// 至此,data蓄满一行修改好的px(channel)数据
// 写入
src.put(row, 0, data);
}
关于代码的补充诠释:
byte[] data = new byte[channels*width];
中:
channels 是一个px的通道数;
width是一个行的px的个数;for(int row=0; row<height; row++)
:外层 for 循环行;src.get(row, 0, data);
get一整行的px数据,存进data;
形象地说,
是以 位置是(row, 0)
即第一个px
的第一个channel
为起始元素
,
获取一个data长度
的数据;
数据一个元素(channel)
一个元素(channel)
地存进数组data
,
每个元素
是某个px
的一个channel
;for(int col=0; col<data.length; col++)
次层 for ,
行中循环列,处理内容:修改一整行的数据;- 次层for执行完毕,data蓄满一行修改好的px(channel)数据;
src.put(row, 0, data)
:数组对象引用赋给行首,交付整行数据;
形象地说,
是以 位置是(row, 0)
的第一个px
的第一个channel
为起始元素
,
提交一个data长度
的数据,即一整行;
1.3 从Mat中一次读取全部像素数据
- 首先定义
数组长度
,这里为图像宽度×图像高度×通道数目
,
然后一次性获取全部
像素数据,
即get
的前面两个参数row=0、col=0
,表示从第一个像素的第一个channel
开始读取。
完整代码如下:
// all pixels
int pv = 0;
byte[] data = new byte[channels*width*height];
src.get(0, 0, data);
for(int i=0; i<data.length; i++) {
pv = data[i]&0xff;
pv = 255-pv;
data[i] = (byte)pv;
}
src.put(0, 0, data);
关于代码的补充诠释(参考1.2的补充,不难理解):
src.get(0, 0, data);
get全部的px数据,存进data;
形象地说,
是以 位置是(0, 0)
即第一个px
的第一个channel
为起始元素
,
获取一个data长度
的数据;
数据一个元素(channel)
一个元素(channel)
地存进数组data
,
每个元素
是某个px
的一个channel
;src.put(0, 0, data)
:数组对象引用赋给行首,交付全部数据;
形象地说,
是以 位置是(0, 0)
的第一个px
的第一个channel
为