计算机视觉 OpenCV Android | Mat像素操作

作者 : 开心源码 本文共12671个字,预计阅读时间需要32分钟 发布时间: 2022-05-12 共196人阅读

本文目录

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_8UC3Mat类型来说,对应的数据类型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.5beta值为-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.530
完整代码可以参考文末作者的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像素操作

发表回复