计算机视觉 OpenCV Android | Mat像素操作
本文目录
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 pixelsint 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
为起始元素
,
提交一个data长度
的数据,即一律px的一律channel
;
上述三种方法:
- 第一种方法由于频繁访问
JNI调用(*!!!* |get())
而效率低下,但是内存(*!!!* |局部变量data的长度)
需求最小; - 第二种方法每次读取一行,相比第一种方法
速度有所提高
,但是内存使用添加
; - 第三种方法一次读取Mat中的一律像素数据,在内存中循环
修改速度最快
,通过JNI调用OpenCV底层C++方法次数最少
,因此效率
也是最高
的,但是对于高分辨率图像,这种方式显然内存消耗过多
,容易导致OOM问题
。
所以Android开发者在使用OpenCV的时候,
需要注意应根据项目需求
,
选择第二种
或者者第三种方法
实现像素读写
,第一种方法
只适用于随机一些像素读写
的场合。
2. 图像通道与均值方差计算
图像中通道数目的多少
可以通过Mat对象channels()
进行查询获取
。- 对于
多通道
的图像,Mat提供的API方法可以把它分为多个单通道
的图像;
同样对于多个单通道
的图像,也可以组合
成一个多通道
的图像。 - OpenCV还提供了
计算
图像每个通道像素平均值
与标准方差
的API方法,
通过它们可以计算得到图像的像素平均值与方差
,
根据平均值
可以实现基于平均值的二值图像分割
,
根据标准方差
可以找到空白图像
或者者无效图像
。
2.1 图像通道分离与合并
图像通道数
通过Mat的channels()
获取之后,
假如通道数目大于1,
那么根据需要调用split方法
即可以实现通道分离
,
通过merge方法
即可以实现通道合并
,
这两个方法的详细解释具体如下:
split(Mat m, List<Mat> mv) // 通道分离
m
:表示输入多通道
图像。mv
:表示分离
之后个单通道
图像,mv的长度与m的通道数目一致。merge(List<Mat> mv, Mat dst) // 通道合并
mv
:表示多个待合并
的单通道
图像。dst
:表示合并之后
生成的多通道
图像。
上面两个方法都来自Core板块
,Core板块
主要包含少量Mat操作
与基础矩阵数学功能
。
一个简单的多通道的Mat对象其分离与合并的代码演示如下:
public void channelsAndPixels() {// Mat src = Imgcodecs.imread(fileUri.getPath());// if(src.empty()){// return;// } //******* Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.lena); Mat ori = new Mat(); Mat src = new Mat(); Utils.bitmapToMat(bitmap, ori); Imgproc.cvtColor(ori, src, Imgproc.COLOR_RGBA2BGR); //******* List<Mat> mv = new ArrayList<>(); Core.split(src, mv); for(Mat m : mv) { int pv = 0; int channels = m.channels();//channels = 1,毕竟都调用了split()了// //下面这行用来测试channels的值// Toast.makeText(this,"The m.channels is" + channels,Toast.LENGTH_SHORT).show(); 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]&0xff; pv = 255-pv; data[i] = (byte)pv; } m.put(0, 0, data); } Core.merge(mv, src); Bitmap bm = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888); Mat dst = new Mat(); Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2RGBA); Utils.matToBitmap(dst, bm); ImageView iv = (ImageView)this.findViewById(R.id.chapter3_imageView); iv.setImageBitmap(bm); dst.release(); src.release(); }
上面的代码实现了对多通道图像
分离之后取反
,
而后再合并
,
最后通过Android ImageView组件显示结果
,
如此便是图像通道分离与合并
的基本用法
;
2.2 .均值与标准方差计算与应用
接下来的内容是关于图像Mat像素数据的简单统计
,计算均值与方差
。
对给定的一组数据计算其
均值μ
与标准方差stddev
的公式如下:其中,
n
表示数组的长度
、xi
表示数组第i个元素的值
。其中,
n
表示数组长度
、μ
表示均值
、1
表示自由度
。根据上述公式,
可以读取每个像素点的值
,计算
每个通道像素的均值与标准方差
,
OpenCV Core板块中已经实现了这类API
,具体解释如下:
meanStdDev(Mat src, MatOfDouble mean, MatOfDouble stddev)
src
:表示输入Mat图像。mean
:表示计算出各个通道的均值
,数组长度与通道数目一致。stddev
:表示计算出各个通道的标准方差
,数组长度与通道数目一致。meanStdDev(Mat src, MatOfDouble mean, MatOfDouble stddev, Mat mask)
本方法实现的功能同上,
不同的是这里多了一个Mat型参数 mask
;
表示只有当mask中对应位置的像素值不等于零
时,src中相同位置的像素点
才参加计算均值与标准方差
。
完整的基于均值实现图像二值分割的代码如下:
// 加载图像Mat src = Imgcodecs.imread(fileUri.getPath());if(src.empty()){ return;}// 转为灰度图像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]);// 读取像素数组int 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);
最终得到的gray
就是二值图像
,转换为Bitmap对象
之后,通过ImageView显示
就可。
- 另外,
关于计算得到的标准方差
,如上面的代码中假设stddev[0]的值小于5
,那么基本上图像可以看成是无效图像
或者者空白图像
,
由于标准方差越小
则说明图像各个像素的差异越小
,图像本身携带的有效信息越少
;- 在图像解决中,可以利用这个结论来
提取和过滤质量不高的扫描或者者打印图像
。
3. 算术操作与调整图像的亮度和比照度
- OpenCV的
Core
板块支持Mat对象的加、减、乘、除算术运算
,
这些算术运算都处于Mat对象层次
,
可以在任意两个Mat之间
实现上述算术操作,以得到结果
。
3.1 算术操作API的详情
OpenCV中
Mat的加、减、乘、除运算
,
既可以在两个Mat对象之间
,
也可以在Mat对象与Scalar之间
进行。Mat对象之间的加、减、乘、除运算最常用的方法
如下: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
均为Mat对象
的时候,
它们的大小与类型
必需一致
,
默认的输出
图像类型与输入
图像类型一致
。
下面是一个简单的算术运算的例子,使用加法,将两个Mat对象的叠加结果输出:
// 输入图像src1Mat src = Imgcodecs.imread(fileUri.getPath());if(src.empty()){ return;}// 输入图像src2Mat moon = Mat.zeros(src.rows(), src.cols(), src.type());int cx = src.cols() - 60;int cy = 60;Imgproc.circle(moon, new Point(cx, cy), 50, new Scalar(90,95,234), -1, 8, 0);// 加法运算Mat dst = new Mat();Core.add(src, moon, dst);
3.2 调整图像的亮度和比照度
图像的
亮度和比照度
是图像
的两个基本属性
,
对RGB色彩图像
来说,亮度越高
,像素点对应的RGB值
应该越大
,越接近255
;
反之亮度越低
,其像素点对应的RGB值
应该越小
,越接近0
。
所以在RGB色彩空间
中,调整图像亮度
可以简单地通过对图像进行加法与减法操作
来实现。图像比照度
主要是用来形容图像颜色与亮度之间的差异感知,比照度越大
,图像的每个像素与附近的差异性
也就越大
,整个图像的细节
就越明显
;
反之亦然。
通过对图像进行乘法或者者除法操作
来扩大或者者缩小图像像素之间的差值
,便可调整图像比照度
。
加减法
只能使各个通道值保持差值(差距)去变大变小;
而乘除法
能放大缩小差值;
基于Mat与Scalar
的算术
操作,实现图像亮度
或者者比照度调整
的代码实现如下:
// 输入图像src1Mat src = Imgcodecs.imread(fileUri.getPath());if(src.empty()){ return;}// 调整亮度Mat dst1 = new Mat();Core.add(src, new Scalar(b,b,b), dst1);// 调整比照度Mat dst2 = new Mat();Core.multiply(dst1, new Scalar(c, c, c), dst2);//至dst2,图像的两个度已经调整完毕,就差个转化类型而已// 转换为Bitmap,显示Bitmap bm = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);Mat result = new Mat();Imgproc.cvtColor(dst2, result, Imgproc.COLOR_BGR2RGBA);Utils.matToBitmap(result, bm);
- 上述代码中,
b
表示亮度参数
,c
表示比照度参数
;
其中,b
的取值为负数
时,表示调低亮度
;为正数
时,表示调高亮度
;c
的取值是浮点数
,使用经验值范围
一般为0~3.0
,c
的取值小于1
时,表示降低比照度
,大于1
时表示提升比照度
。
4. 基于权重的图像叠加
- 对图像进行
简单的相加
方法有时候并不能满足需要
,
这时可以通过参数
来调整输入图像
在最终叠加之后的图像中所占的权重比
,
以实现基于权重方式的、更加灵活的图像调整方法
。
Core板块中已经实现了这样的API函数,方法名称与各个参数的解释具体如下:
addWeighted(Mat src1, double alpha, Mat src2, double beta, double gamma, Mat dst)
src1
:表示输入
的第一个
Mat对象。alpha
:表示混合时候第一个
Mat对象所占的权重
大小。src2
:表示输入
的第二个
Mat对象。beta
:表示混合时候第二个
Mat对象所占的权重
大小。gamma
:表示混合之后能否进行亮度校对
(提升或者降低)。dst
:表示输出
权重叠加之后的Mat对象。最常见
的情况下,
在进行两个图像叠加的时候
,权重调整需要满足的条件为alpha + beta = 1.0
,通常alpha = beta = 0.5
,
表示混合叠加后的图像中原来两副图像的像素比值各占一半
,这些都是对于正常图像
来说的。假设
src2
是全黑色背景图像
,
那么这种叠
加效果就是让图像src1
变得更加暗
,比照度
变得更加低
;
在src2为黑色背景图像时,我们把alpha
值调整为1.5
,beta
值为-0.5
,
这样最终的叠加结果就是图像的比照度得到了提升
;
当alpha=1
时候,则输出原图
。假如
gamma
不是默认值0
,而是一个正整数
的时候,那么这时就会提升图像的亮度
,
所以这种方式就成为调整图像亮度与比照度
的另外一种方式
,而且它比上一节中提到的方法更简洁、实用
,只要一次调用
即可以得到图像亮度与比照度调整后输出的图像
。
这种方法的公式化形容如下:
dst=src1*alpha+src2*beta+gamma
其中,
假如src2
是纯黑色的背景图像,
则gamma
大小决定了图像的亮度
,alpha
大小决定了图像的比照度
(由于src2纯黑色背景则基本无比照度,所以该由src1决定得多),alpha+beta=1
。
基于权重叠加的图像亮度与比照度调整
的完整代码实现如下:
// 加载图像Mat src = Imgcodecs.imread(fileUri.getPath());if(src.empty()){ return;}// create black imageMat black = Mat.zeros(src.size(), src.type());Mat dst = new Mat();// 像素混合 - 基于权重Core.addWeighted(src, alpha, black, 1.0-alpha, gamma, dst);// 转换为Bitmap,显示Bitmap bm = Bitmap.createBitmap(src.cols(), src.rows(), Bitmap.Config.ARGB_8888);Mat result = new Mat();Imgproc.cvtColor(dst, result, Imgproc.COLOR_BGR2RGBA);Utils.matToBitmap(result, bm);
其中,两个参数alpha和gamma
分别表示比照度与亮度调整的幅度
,这里的默认值分别为1.5
和30
。
完整代码可以参考文末作者的GitHub;
5. Mat的其余各种像素操作
OpenCV除了支持图像的算术操作
之外,还支持图像的逻辑操作、平方、取LOG、归一化值范围
等操作,
这些操作在解决复杂场景的图像
与二值
或者者灰度图像分析
的时候非常有用。
图像逻辑操作相关的API与参数说明具体如下:
bitwise_not(Mat src, Mat dst) // 取反操作
src
:输入图像。dst
:取反之后的图像。取反操作
对二值图像
来说是一个常见操作
,
有时候我们需要先进行取反操作
,而后再对图像进行更好地分析
。bitwise_and(Mat src1, Mat src2, Mat dst) // 与操作
src
:输入图像一。src2
:输入图像二。dst
:与操作结果。
与操作对两张图像混合之后的输出图像
有降低混合图像亮度
的效果,
会让输出的像素小于等于对应位置的任意一张输入图像的像素值
。
(因唯两个高值像素相与得高值像素,
高值与低值、低值与低值的结果都是低值,
于是三分之二的运算都是降低亮度的操作)
bitwise_or(Mat src1, Mat src2, Mat dst) // 或者操作
src1
:输入图像一。src2
:输入图像二。dst
:或者操作结果。
或者操作对两张图像混合之后的输出图像
有强化混合图像亮度
的效果,
会让输出的像素大于等于对应位置的任意一张输入图像的像素值。
(其了解同与操作相反)
bitwise_xor(Mat src1, Mat src2, Mat dst) // 异或者操作
src1
:输入图像一。src2
:输入图像二。dst
:或者操作结果。
异或者操作
可以看作是对输入图像的叠加取反效果
。
下面创立两个Mat对象,
而后对它们完成位运算——逻辑与、或者、非,
得到的结果将拼接
为一张大Mat对象显示,
完整的代码演示如下:
// 创立图像Mat src1 = Mat.zeros(400, 400, CvType.CV_8UC3);Mat src2 = new Mat(400, 400, CvType.CV_8UC3);src2.setTo(new Scalar(255, 255, 255));// ROI区域定义Rect rect = new Rect();rect.x=100;rect.y=100;rect.width = 200;rect.height = 200;// 绘制矩形Imgproc.rectangle(src1, rect.tl(), rect.br(), new Scalar(0, 255, 0), -1);rect.x=10;rect.y=10;Imgproc.rectangle(src2, rect.tl(), rect.br(), new Scalar(255, 255, 0), -1);// 逻辑运算Mat dst1 = new Mat();Mat dst2 = new Mat();Mat dst3 = new Mat();Core.bitwise_and(src1, src2, dst1);Core.bitwise_or(src1, src2, dst2);Core.bitwise_xor(src1, src2, dst3);// 输出结果Mat dst = Mat.zeros(400, 1200, CvType.CV_8UC3);rect.x=0;rect.y=0;rect.width=400;rect.height=400;dst1.copyTo(dst.submat(rect));rect.x=400;dst2.copyTo(dst.submat(rect));rect.x=800;dst3.copyTo(dst.submat(rect));// 释放内存dst1.release();dst2.release();dst3.release();// 转换为Bitmap,显示Bitmap bm = Bitmap.createBitmap(dst.cols(), dst.rows(), Bitmap.Config.ARGB_8888);Mat result = new Mat();Imgproc.cvtColor(dst, result, Imgproc.COLOR_BGR2RGBA);Utils.matToBitmap(result, bm);// showImageView iv = (ImageView)this.findViewById(R.id.chapter3_imageView);iv.setImageBitmap(bm);
如上代码前文字所述,
三个输出图像分别以x = 0, 400, 800为Mat矩阵左上角点拼接
到结果Mat矩阵dst中:
- 除了逻辑操作之外,
还有两个重要且常见的像素操作是归一化
与线性绝对值放缩变换
,
其中归一化
是把数据re-scale到指定的范围内,线性绝对值放缩
是把任意范围的像素值变化到0~255的CV_8U的图像像素值。
相关API解释如下:
convertScaleAbs(Mat src, Mat dst) //线性绝对值放缩变换
src
:表示输入图像。dst
:表示输出图像。
默认情况下会对输入Mat对象数据求得绝对值,并将其转换为CV_8UC1类型
的输出数据dst
。
normalize(Mat src, Mat dst, double alpha, double beta, int norm_type, int dtype, Mat mask)
src
:表示输入图像。dst
:表示输出图像。alpha
:表示归一化到指定范围的低值。beta
:表示归一化到指定范围的高值。dtype
:表示输出的dst图像类型,默认为-1,表示类型与输入图像src相同。mask
:表示遮罩层,默认为Mat类型。归一化
在图像解决中是经常需要用到的方法,
比方对浮点数进行计算
得到输出数据
,将数据归一化到0~255后即可以作为彩色图像输出
,得到输出结果。
(数据 只需经过 归一化 即可以变成 彩色图像 输出,划重点!!!!!!)
下面简单演示一下如何创立一个0~1的浮点数图像,
而后将其归一化到0~255,
代码实现如下:
// 创立随机浮点数图像Mat src = Mat.zeros(400, 400, CvType.CV_32FC3);float[] data = new float[400*400*3];Random random = new Random();for(int i=0; i<data.length; i++) { data[i] = (float)random.nextGaussian();}src.put(0, 0, data);// 将值归一化到0~255之间Mat dst = new Mat();Core.normalize(src, dst, 0, 255, Core.NORM_MINMAX, -1, new Mat());// 类型转换Mat dst8u = new Mat();dst.convertTo(dst8u, CvType.CV_8UC3);
上述代码将创立一张大小为400×400
的高斯噪声图像
,
其中归一化方法
选择的是最小与最大值归一化方法(NORM_MINMAX=32)
,
这种方法的数学表示如下:
- 图解:
如图所示,
( x - min / max - min )
必然是一个[0,1]
的实数!- 另外,
你会发现公式中,不加alpha对( 0 , 255 )
这个范围的归一(即以上题境)没有什么影响,
这是由于( x - min / max - min )
光乘以(beta - alpha)
不加最后的alpha
只能归一到范围( 0 , beta )
;
加上 最后的alpha
才能归一到( alpha , beta )
;
其中,x
表示src
的像素值,min、max
表示src
中像素的最小值与最大值
,
对 src 各个通道
完成上述计算就可得到最终的归一化结果
。
若计算图像的结果有正负值
,那么在显示之前
会调用convertScaleAbs()
来对负值求取绝对值图像
,
在后面的图像滤波与梯度计算中会用到该方法。
此外,Core中图像常见的操作还有对Mat做平方与取对数
,这些操作都与实际应用场合有肯定的关系,而且使用与参数都比较简单,书中这里没再做过多的说明。
关于相关API的更多说明,我们可以查看对应的OpenCV帮助文档。
参考资料
- 《OpenCV Android 开发实战》(贾志刚 著)
- 关于本书作者的GitHub项目
- 基于作者GitHub维护的APP
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 计算机视觉 OpenCV Android | Mat像素操作