OpenCV Android 开发实战 -- 学习笔记 -- Mat像素操作 【第三章】

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 mv)
    m:输入多通道图像
    mv:分离后的多个单通道图像,mv长度与m通道数一致
  - 通道合并 merge()
    merge(List mv, Mat dst)
    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()
 

你可能感兴趣的:(OpenCV Android 开发实战 -- 学习笔记 -- Mat像素操作 【第三章】)