3.1 像素读写
# 概述
- Mat作为图像容器,数据部分存储图像的像素数据,可以通过相关API获取图像像素数据
- 根据Mat的类型与通道数目,开辟适当大小的内存空间
- 然后通过get()方法循环实现每个像素点的读取
- 接着再通过put()方法修改与Mat对应的数据部分即可
- 默认情况下,imread方式将Mat对象类型加载为CV_8UC3
# 准备代码 [获取基本数据]
Mat src = Imgcodecs.imread(fileUri.getPath());
if(src.empty()){
return 0;
}
int channels = src.channels();
int width = src.cols();
int height = src.rows();
# 一次读取一个像素点数据
- CV_8UC3的Mat类型,对应数据类型为byte
- 初始化byte数组,数组长度取决于图像通道数
- 代码 [效率低 内存需求最小]
byte[] data = new byte[channels];
int b = 0, g = 0, r = 0;
for(int rows = 0; row < height; row++){
for(int col = 0; col < width; col++){
//读取
src.get(row,col,data);
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);
}
}
# 一次读取一行像素数据
- 首先定义每一行像素数组长度
数组长度 = 图像宽度 X 每个像素的通道数目之和
- 然后循环修改数据
- 代码 [速度提高,内存需求增大]
byte[] data = new byte[channels*width];
int b = 0, g = 0, r = 0;
int pv = 0;
for(int row = 0; row < height; row++){
//读取
src.get(row,0,data);
//修改
for(int col= 0; col < data.length; col++){
pv = data[col]&0xff;
pv = 255 - pv;
data[col] = (byte)pv;
}
//写入
src.put(row,0,data);
}
# 一次读取全部像素
- 定义:数组长度 = 像素宽度 X 图像高度 X 通道数目
- 代码 [修改速度最快 内存消耗最高]
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);
3.2 图像通道与均值方差计算
# 图像通道分离、合并
- 获取通道数 channels()
- 通道分离 split()
split(Mat m, List
m:输入多通道图像
mv:分离后的多个单通道图像,mv长度与m通道数一致
- 通道合并 merge()
merge(List
mv:多个待合并的单通道图像
dst:合并之后的多通道图像
- merge() split() 方法都来自Core模块
Core模块主要包含一些Mat操作与基础矩阵书写功能
-代码 -- Mat对象的分离与合并
List mv = new ArrayList<>();
Core.split(src,mv);
for(Mat m : mv){
int pv = 0;
int channels = m.chanels();
int width = m.cols();
int height = m.rows();
byte[] data = new byte[channels*width*height];
m.get(0,0,data);
for(int i = 0; i < data.length; i++){
pv = data[i]&0fxx;
pv = 255 - pv;
data[i] = (byte)pv;
}
m.put(0,0,data);
}
Core.merge(mv,src);
# 均值与标准方差计算
· 均值计算
- 像素之和/像素个数
· 方差计算
- 每个像素与均值的差的方差和/像素个数 最后开根
· 实现方法
- meantStdDev(Mat src, MatOfDouble mean, MatOfDouble stddev)
src:输入Mat图像
mean:计算各通道均值,数组长度与通道数目一致
stddev:计算各个通道标准方差,数组长度与通道数目一致
- meantStdDev(Mat src, MatOfDouble mean, MatOfDouble stddev, Mat mask)
src:输入Mat图像
mean:计算各通道均值,数组长度与通道数目一致
stddev:计算各个通道标准方差,数组长度与通道数目一致
mask:只有当mask中相同位置对应的像素值不等于零的时候,src中相同位置的像素点才参与计算均值与方差
· 应用 -- 根据均值将图像二值化
- 标准方差越小,说明图像各个像素的差异越小,
此时的二值图像可能为无效图像或者空白图像
- 效果:提取或者过滤质量不高的扫描或者打印图像
· 代码 -- 计算均值与方差
//加载图像
Mat src = Imgcodecs.imread(fileUri.getPath());
if(src.empty){
return 0;
}
//转灰度图像
Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
//计算均值方差
MatOfDouble means = new MatOfDouble();
MatOfDouble stddevs = new MatOfDouble();
Core.meanStdDev(gray, means, stddevs);
//显示均值方差
double[] mean = means.toArray();
double[] stddev = stddevs.toArray();
Log.i(TAG,"gray image means : " + mean[0]);
Log.i(TAG,"gray image stddev : " + stddev[0]);
//读取像素图像
imt width = gray.cols();
int height = gray.rows();
byte[] data = new byte[width*height];
gray.get(0,0,data);
int pv = 0;
//根据均值二值化
int t = (int) mean[0];
for(int i = 0; i < data.length, i++){
pv = data[i]&0xff;
if(pv > t){
data[i] = (byte)255;
}else{
data[i] = (byte)0;
}
}
gray.put(0,0,data);
3.3 算数操作与调整图像的亮度和对比度
# 算数操作API
- OpenCV的Core模块支持Mat对象的加减乘除算数操作
- 这些操作可以在两个Mat对象之间,也可以Mat对象与Scalar直接进行
- 常用方法
add(Mat src1, Mat src2, Mat dst)
subtract(Mat src1, Mat src2, Mat dst)
multiply(Mat src1, Mat src2, Mat dst);
divide(Mat src1, Mat src2, Mat dst)
src1:输入的第一个Mat图像对象
src2:输入的第二个Mat图像对象
dst:算数操作输出的Mat图像对象
src2 的类型还可以是Scalar类型,这时候表示像素点都与Scalar中的每个向量完成指定的算数运算
- src1 src2 大小类型必须一致,默认输出图像类型与输入图像类型一致
# 调整图像的亮度和对比度
- 图像的亮度和对比度是图像的两个基本属性
- RGB色彩图像:亮度越高,像素点对应RGB值越大,越接近255,反之越低。
- 图像对比度只要用来描述图像颜色与亮度之间的差异感知。
对比度越大,图像的每个像素与周围的差异性也就越大,整个图像的细节就越显著
- 乘除法 扩大/缩小 图像像素之间差值
加减法 调整图像亮度
- 代码实现
//加载图像
Mat src = Imgcodecs.imread(fileUri.getPath());
if(src.empty){
return 0;
}
/*
*调整亮度
*b表示亮度参数 [负数表示调低亮度]
*/
Mat dst1 = new Mat();
Core.add(src, new Scalar(b,b,b), dst1);
/*
*调整对比度
*c表示对比度参数[经验值范围0~3.0] [小于1表示降低对比度]
*/
Mat dst2 = new Mat();
Core.multiply(dst1, new Scalar(c,c,c), dst2);
//转换为Bitmap,显示
Bitmap bm = Bitmap.creatBitmap(src,cols(),crs.rows(),Bitmap.Config.ARGB_8888);
Mat result = new Mat();
Imgproc.cvtColor(dst2, result, Imgproc.COLOR_RGB2RGBA);
Utils.matToBitmap(result, bm);
3.4 基于权重的图像叠加
# 实现函数
· addWeighted(Mat src1, double alpha, Mat src2, double beta, double gama, Mat dst)
- src1:输入的第一个Mat图像
alpha:混合的时候第一个Mat对象所占的权重大小
src2:输入的第二个Mat图像
beta: 混合的时候第二个Mat图像所占的权重大小
gamma:表示混合之后是否进行亮度校正(提升或降低)
dst:输出权重叠加之后的Mat对象
- 常见情况下,权重调整需要满足条件:alpha + beta = 1.0
- 方法公式:dst = src1*alpha + src2*beta + gamma
3.5 Mat的其他各种像素操作
# 概述
- OpenCV除了支持图像算数操作之外,还支持图像逻辑操作、平方、取LOG、归一化等操作
# 图像逻辑操作
- bitwise_not(Mat src, Mat dst) 取反操作
- bitwise_and(Mat src1, Mat src2, Mat dst) 与操作 对两图像混合之后的输出图像有降低图像亮度效果
- bitwise_or(Mat src1, Mat src2, Mat dst) 或操作 对两图像混合之后的输出图像有强化图像亮度效果
- bitwise_xor(Mat src1, Mat src2, Mat dst) 异或操作 对输入图像的叠加取反效果
# 归一化与线性绝对值放缩变换
- convertScaleAbs(Mat src, Mat dst)
- normalize(Mat src, Mat dst, double alpha, double beta, int norm_type, Mat mask)
alpha: 归一化到指定范围的低值
beta:归一化到指定范围的高值
type:dst图像类型,默认为-1,表示类型与输入图像src相同
mask:遮罩层 默认 new Mat()