OpenCV Android 开发实战 -- 学习笔记 -- Mat像素操作 【第三章】
3.1 像素读写
# 概述
- Mat作为图像容器,数据部分存储图像的像素数据,可以通过相关API获取图像像素数据
- 根据Mat的类型与通道数目,开辟适当大小的内存空间
- 然后通过get()方法循环实现每个像素点的读取
- 接着再通过put()方法修改与Mat对应的数据部分即可
- 默认情况下,imread方式将Mat对象类型加载为CV_8UC3
# 准备代码 [获取基本数据]
[code] 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数组,数组长度取决于图像通道数
- 代码 [效率低 内存需求最小]
[code] 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 每个像素的通道数目之和
- 然后循环修改数据
- 代码 [速度提高,内存需求增大]
[code] 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 通道数目
- 代码 [修改速度最快 内存消耗最高]
[code] 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<Mat> mv)
m:输入多通道图像
mv:分离后的多个单通道图像,mv长度与m通道数一致
- 通道合并 merge()
merge(List<Mat> mv, Mat dst)
mv:多个待合并的单通道图像
dst:合并之后的多通道图像
- merge() split() 方法都来自Core模块
Core模块主要包含一些Mat操作与基础矩阵书写功能
-代码 -- Mat对象的分离与合并
[code] List<Mat> 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中相同位置的像素点才参与计算均值与方差
· 应用 -- 根据均值将图像二值化
- 标准方差越小,说明图像各个像素的差异越小,
此时的二值图像可能为无效图像或者空白图像
- 效果:提取或者过滤质量不高的扫描或者打印图像
· 代码 -- 计算均值与方差
[code] //加载图像 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,反之越低。
- 图像对比度只要用来描述图像颜色与亮度之间的差异感知。
对比度越大,图像的每个像素与周围的差异性也就越大,整个图像的细节就越显著
- 乘除法 扩大/缩小 图像像素之间差值
加减法 调整图像亮度
- 代码实现
[code] //加载图像 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图像
27cc6
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()
- OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co
- Android学习开发笔记之SQLite数据库操作
- OpenCV学习笔记——存取像素值操作汇总core
- OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年
- OpenCV学习笔记 cv.Mat 与 .txt 文件数据的读写操作
- 【opencv学习笔记.1】操作像素画圆
- OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年
- Android开发学习笔记之窗口操作
- openCV学习笔记(四):图像遍历和像素操作
- [OpenCV学习笔记][Mat数据类型和操作]
- 我的OpenCV学习笔记(三):利用操作像素完成简单的图像处理:加入椒盐噪声、图像翻转、改变对比度、图像锐化
- opencv学习笔记 二 操作像素
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 ApplicationContext和BeanFactory区别
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.2 相关Java基础知识 类装载器 反射机制
- android开发学习笔记——sqlite操作
- OpenCV学习笔记 存取像素值操作汇总core
- oracle开发之<<SQL Cookbook>>学习笔记整理:第三章 操作多个表
- 【OpenCV学习笔记】一.操作像素
- 《Spring 3.x 企业应用开发实战》学习笔记 第三章 IoC容器概述 3.5 Bean的生命周期
- OpenCV学习笔记 cv.Mat 与 .txt 文件数据的读写操作