OpenCV | 基本矩阵操作

版权声明:文章为博主原创,未经同意禁止转载 https://blog.csdn.net/Hello_Peter_Chan/article/details/82112455

起因

在2013年1月份发行的OpenCV 2.4.4中,对Java的支持也正式发布。同时也有支持Python。

环境配置

这里的环境配置十分简单,在这里,操作系统64位,所以选这个,将里面的opencv_java342.dll负责到你本地下载的JDK的bin目录下,和JDK下的JRE目录下的bin目录下。

我使用的是Eclipse,在Eclipse新建Java工程后将opencv-342.jar加入到路径中,就OK了。

这里可以验证下是否能成功运行OpenCV,代码如下:

package com.monhitul;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;

public class SimpleSample {
  //静态代码块定义,会在程序开始运行时先被调用初始化
  static {
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME); //得保证先执行该语句,用于加载库,才能调用其他操作库的语句,
  }                         //否则会报 java.lang.UnsatisfiedLinkError: org.opencv.core.Mat.n_Mat(IIIDDDD)J 错误

  public static void main(String[] args) {
    System.out.println("Welcome to OpenCV" + Core.VERSION);
    Mat m = new Mat(5,10,CvType.CV_8UC1,new Scalar(0));
    System.out.println("OpenCV Mat: "+m);
    Mat mr1 = m.row(1);
    mr1.setTo(new Scalar(1));
    Mat mc5 = m.col(5);
    mc5.setTo(new Scalar(5));
    System.out.println("OpenCV Mat data:\n"+m.dump());
  }
}

控制台输出是这样的:

这里写图片描述

这样就表示没问题。

基本矩阵操作

大家知道,在计算机视觉中,一幅图像其实就是一个由数字组成的矩阵,这些数字表示的是图像的像素。比如说gray-image,灰度图像,将这些数字按从0(black)到255(white)排列,数字越接近0就是越接近黑,越接近255就是越接近白,其实就是不同程度上的灰,这就是普遍的8-bit images;甚至还可以采用浮点数的方法,黑到白就是0.0到1.0。而color image,彩色图像,它的每个像素点则是由三原色(红绿蓝)结合而成的。

所以,每一个数字图像都是一个像素点矩阵,这个矩阵包含所有像素点的强度值,矩阵的列数对应图像的宽度,矩阵的行数对应图像的高度。

在OpenCV中,Mat类(Matrix的缩写,矩阵)是OpenCV用于处理图像而引入的一个封装类。Mat的type属性表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,命名规则如下:

CV_<bit_depth>{U|S|F}C(<number_of_channels>)

在这里呢,bit_depth表示图像深度,即一个像素点所占的总位数(bit),像是前面提到的灰度图像,就是8位。

U|S|F分别代表unsigned无符号整数,signed有符号整数和float浮点数。

number_of_channels代表的是通道,单通道就是一个像素点只需一个数值表示,只能表示灰度,0为黑色;三通道就是RGB模式,把图像分为红绿蓝三个通道,可以表示彩色,全0表示黑色;四通道就是在RGB基础上加上alpha通道,表示透明度,alpha=0表示全透明。如果number_of_channels未指明,则默认为单通道。

这样的话,我们举个例子,比如说前面的8位灰度图,那就是CV_8UC1,这个还挺好理解的,就不多说了。

熟悉操作

我们可以回到最开始我们验证OpenCV是否正常使用的那个代码。

package com.monhitul;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;

public class SimpleSample {
  //静态代码块定义,会在程序开始运行时先被调用初始化
  static {
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME); //得保证先执行该语句,用于加载库,才能调用其他操作库的语句,
  }                         //否则会报 java.lang.UnsatisfiedLinkError: org.opencv.core.Mat.n_Mat(IIIDDDD)J 错误

  public static void main(String[] args) {
    System.out.println("Welcome to OpenCV" + Core.VERSION);
    Mat m = new Mat(5,10,CvType.CV_8UC1,new Scalar(0));
    System.out.println("OpenCV Mat: "+m);
    Mat mr1 = m.row(1);
    mr1.setTo(new Scalar(1));
    Mat mc5 = m.col(5);
    mc5.setTo(new Scalar(5));
    System.out.println("OpenCV Mat data:\n"+m.dump());
  }
}

这里写图片描述

猜也可以猜出这个代码大致的意思了。我们看看输出结果中Mat的结果,isCont属性告诉我们在表示图像时,次矩阵是否使用了额外的填充,以便在某些平台上可以硬件加速。isSubmat属性是指该矩阵是否是从另一个矩阵创建而来的,以及它是否引用了另一个矩阵的数据。

关于数据初始化的,我们还可以看看下面这个例子:

Mat image = new Mat(new Size(3,3),CvType.CV_8UC3,new Scalar(new double[] {3,6,9}));
System.out.println("OpenCV Mat image data:\n"+image.dump()+"\n");

输出结果为:

这里写图片描述

像素操作

对像素的操作可以有个很直接的方法,就是使用put(row, col, data)方法,代码如下:

for(int i=0;i<image.rows();i++) {
  for(int j=0;j<image.cols();j++) {
    image.put(i, j, new byte[] {2,4,8});
  }
}

在这里byte[]数值中,1代表通道2代表通道绿3代表通道,即顺序是{蓝,绿,黄}。

这样存在的问题也很明显,就算是一幅640×480px的图像,也有307200个像素点,如果是彩色图像则是921600个数值存在于矩阵中,这样必然造成运行时间过久。


我们这个可以看看下面这个代码,实现了过滤通道的功能:

//过滤器,将通道蓝的数值滤去,置为0
public static Mat filter(Mat image) {
  Mat newImage=image;
  //得到图像中的字节数
  int totalBytes = (int)(newImage.total() * newImage.elemSize()); 
  //image.total()得到像素总数,image.elemSize()得到每个元素的大小,单位byte
  System.out.println("totalBytes:"+totalBytes);

  byte buffer[] = new byte[totalBytes];
  //get,put方法分别是将byte转换成Mat,将Mat转换成byte
  for(int i=0;i<totalBytes;i++) {
    if(i%3==0) {  //通道蓝为3的倍数
      buffer[i]=0;  //置为0
    }
  }
  newImage.put(0, 0, buffer);
  System.out.println("put:"+newImage.dump());
  return newImage;
}

然后在主函数中调用:

Mat src = Imgcodecs.imread("D:\\favcion.jpg");  //读入一幅图像
if(src.empty()) {
  System.out.println("empty");
    return;
} else {  //判断确实读入
  System.out.println("Notempty");
  Mat newImage = filter(src); //调用过滤器
    Imgcodecs.imwrite("D:\\newfavcion.jpg", newImage);  //将得到新的图像写入文件中
  }

我们来看看前后的对比。

这里写图片描述

这里写图片描述

明显偏黄,我们可以选择将红的滤去,是这样的:

这里写图片描述

将绿的滤去,这样的:

这里写图片描述

再看看这么一张图:

这里写图片描述

从文件中加载图像

前面代码里刚刚出现的,借助Imgcodecs.imread(name_of_file)可以加载图像文件,而dataAddr()方法可以判断加载图像是否正确。

就像这样:

import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.core;

public Mat openFile(String fileName) throws Exeception{
  Mat newImage = Imgcodecs.imread(fileName);
  if(newImage.dataAddr()==0){
    throw new Exception("Couldn't open file: "+fileName);
  }
  return newImage;
}

而将图像存为文件,前面的代码也有出现,就是使用Imgcodecs.imwrite(name_of_file)

结束语

也欢迎大家关注微信公众号:Monhitul

一个东学西学,学东学西的公众号,大家一起学习,一同进步。

这里写图片描述

猜你喜欢

转载自blog.csdn.net/Hello_Peter_Chan/article/details/82112455