OpenCV——Mat类及常用数据结构

Mat类及常用数据结构

一、Mat类简介

Mat是矩阵类(Matrix)的缩写,Mat由矩阵头(Header)和数据两部分组成。

1.1、矩阵头

矩阵头中包含了矩阵尺寸、存储方法,存储地址等信息。

java 复制代码
public class MatDemo {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        Mat m = Mat.zeros(2, 3, CvType.CV_8UC1);
        //打印矩阵信息
        System.out.println(m);
    }
}
java 复制代码
Mat [ 2*3*CV_8UC1, isCont=true, isSubmat=false, nativeObj=0x600003f8f060, dataAddr=0x7f7c923071c0 ]
  • 23CV_8UC1:矩阵尺寸是2*3,数据类型是CV_8UC1
  • isCont=true:是否连续存储
  • isSubmat=false:是否为子矩阵(子矩阵指矩阵的一个子区域,子矩阵可以像矩阵一样进行处理和保存,但是对子矩阵的任何修改都会同时影响原来的矩阵)
  • nativeObj=0x600003f8f060:本地对象地址
  • dataAddr=0x7f7c923071c0:存储的图片的地址

1.2、矩阵的数据类型

以CV_8UC3为例:

  • CV表示OpenCV
  • 下划线后由四部分组成:
    • 第一部分表示数据位数,8表示8位,16表示16位,32表示32位,64表示64位
    • 第二部分表示数据类型,U代表无符号整型,S代表有符号整型,F代表浮点类型
    • 第三部分C代表通道
    • 第四部分为通道数:1表示1通道,2表示2通道,3表示3通道
图像深度 数字值 具体描述 取值范围
CV_8U 0 8位无符号整数 0~255
CV_8S 1 8位有符号整数 -128~127
CV_16U 2 16位无符号整数 0~65535
CV_16S 3 16位有符号整数 -32768~32767
CV_32S 4 32位有符号整数 -2147483~2147483647
CV_32F 5 32位浮点数 -
CV_64F 6 64位浮点数 -

1.3、Mat的子类

Mat类可以存储哥哥不同的数据类型。根据数据类型的不同,Mat类又派生出多个子类。

二、矩阵数据的存储

2.1、单通道

Mat类存储图像数据时,可以看做按照栅格扫描顺序存储的数组。单通道的灰度图中数据的排列顺序如下:

java 复制代码
1行1列的灰度值 1行2列的灰度值 1行3列的灰度值...
2行1列的灰度值 2行2列的灰度值 2行3列的灰度值...
3行1列的灰度值 3行2列的灰度值 3行3列的灰度值...
...

举例,3*3大小8位1通道的灰度图存储如下

java 复制代码
public class MatDemo {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        Mat m = Mat.zeros(3, 3, CvType.CV_8UC1);
        //打印矩阵内部存储
        System.out.println(m.dump());
    }
}
java 复制代码
1通道,所以同一行1个值表示1个像素点
[  0,   0,   0;
   0,   0,   0;
   0,   0,   0]

2.2、多通道

灰度图因为只有一个通道,所以相对简单,3个通道的RGB彩色图像在OpenCV中颜色是按B(蓝色)、G(绿色)、R(红色)的顺序排列的。3*3大小8位3通道的存储如下:

java 复制代码
public class MatDemo {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        Mat m = Mat.zeros(3, 3, CvType.CV_8UC3);
        //打印矩阵内部存储
        System.out.println(m.dump());
    }
}
java 复制代码
3通道,所以1行3个值表示一个像素点
	  第一行	         		第二行          	   第三行 
[  0(B),0(G),0(R),		0,   0,   0,   		0,   0,   0;    第一列
   0,   0,   0,   		0,   0,   0,   		0,   0,   0;	第二列
   0,   0,   0,   		0,   0,   0,   		0,   0,   0]	第三列

三、创建矩阵的方法

3.1、静态方法创建

java 复制代码
public class CreateMat {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //创建2*3全为0的矩阵
        Mat m1 = Mat.zeros(2, 3, CvType.CV_8UC1);
        System.out.println(m1.dump());

        //创建2*3全为1的矩阵
        Mat m2 = Mat.ones(2, 3, CvType.CV_8UC1);
        System.out.println(m2.dump());

        //创建3*3的矩阵,当行号等于列号时值为1,其余值为0
        Mat m3 = Mat.eye(3, 3, CvType.CV_8UC1);
        System.out.println(m3.dump());

        //需要注意的是,如果通道数大于1,则onces()创建的Mat类只有第一个通道的值为1
        Mat m4 = Mat.ones(3, 3, CvType.CV_8UC3);
        System.out.println(m4.dump());
    }
}
java 复制代码
[  0,   0,   0;
   0,   0,   0]
[  1,   1,   1;
   1,   1,   1]
[  1,   0,   0;
   0,   1,   0;
   0,   0,   1]
[  1,   0,   0,   1,   0,   0,   1,   0,   0;
   1,   0,   0,   1,   0,   0,   1,   0,   0;
   1,   0,   0,   1,   0,   0,   1,   0,   0]

3.2、构造方法创建

这14种方法大多类似,下面介绍最常用的几种:

java 复制代码
//构造方式创建
//方法一
Mat mat1 = new Mat();
mat1.create(3, 3, CvType.CV_8UC1);

//方法二
Mat src = Mat.ones(2, 2, CvType.CV_8UC1);
Mat dst = new Mat();
src.copyTo(dst);//将src复制到dst

//方法三
//大小为100*100,8位3通道蓝色
Mat mat = new Mat(100, 100, CvType.CV_8UC3, new Scalar(255, 0, 0));

3.3、读取图像文件创建

java 复制代码
//读取文件创建
Mat fish = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/fish.png");
//在频幕显示图像
HighGui.imshow("fish", fish);
//按任意键退出
HighGui.waitKey();

3.4、克隆创建

java 复制代码
Mat src = Mat.ones(2, 2, CvType.CV_8UC1);
Mat dst - src.clone();

四、获取矩阵信息

java 复制代码
public class GetMatInfo {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        Mat fish = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/fish.png");

        //获取矩阵头
        System.out.println(fish);

        //获取矩阵行数
        System.out.println(fish.rows());

        //获取矩阵列数
        System.out.println(fish.cols());

        //获取矩阵维度,2*3为二维,3*3*5为3维
        System.out.println(fish.dims());

        //获取矩阵通道数
        System.out.println(fish.channels());

        //获取矩阵的深度
        System.out.println(fish.depth());

        //获取矩阵的尺寸
        System.out.println(fish.size());

        //获取矩阵的数据类型
        System.out.println(fish.type());

        //获取矩阵元素个数,等于行*列
        System.out.println(fish.total());

        //获取矩阵数据
        //System.out.println(fish.dump());
    }
}
java 复制代码
Mat [ 750*998*CV_8UC3, isCont=true, isSubmat=false, nativeObj=0x6000025045a0, dataAddr=0x7fbf66a4b000 ]
750
998
2
3
0
998x750
16
748500

五、矩阵相关操作

5.1、获取/修改像素值

获取像素值使用get方法:

  • int Mat.get(int row, int col, byte[] data)
  • int Mat.get(int row, int col, short[] data)
  • int Mat.get(int row, int col, int[] data)
  • int Mat.get(int row, int col, float[] data)
  • int Mat.get(int row, int col, double[] data)

注意:当row和col为0时表示第一行第一列。Java中的byte类型取值范围为-128~127,如果矩阵数据类型为CV_8U,则二者取值范围并不一致。

修改像素值使用put方法:

  • int Mat.put(int row, int col, byte[] data)
  • int Mat.put(int row, int col, short[] data)
  • int Mat.put(int row, int col, int[] data)
  • int Mat.put(int row, int col, float[] data)
  • int Mat.put(int row, int col, double[] data)
java 复制代码
public class MatPixel {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        Mat m = Mat.eye(3, 3, CvType.CV_8UC1);

        //输出修改前的矩阵数据
        System.out.println(m.dump());
        System.out.println();

        //将第二行第二列值改为9
        m.put(1, 1, 9);

        //输出修改后的矩阵数据
        System.out.println(m.dump());
        System.out.println();

        //获取第二行第二列的像素值,存储在data数组中
        byte[] data = new byte[1];//单通道,所以数组长度为1即可
        m.get(1, 1, data);

        //输出像素值
        System.out.println(data[0]);
    }
}
java 复制代码
[  1,   0,   0;
   0,   1,   0;
   0,   0,   1]

[  1,   0,   0;
   0,   9,   0;
   0,   0,   1]

9

上述程序有个潜在问题,矩阵m的数据类型和data数组的数据类型并不匹配。如果put(1,1, 255),则超过了byte的上限,修改后代码如下:

java 复制代码
public class MatPixel {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        Mat m = Mat.eye(3, 3, CvType.CV_32SC1);//修改为32位整型

        //输出修改前的矩阵数据
        System.out.println(m.dump());
        System.out.println();

        //将第二行第二列值改为9
        m.put(1, 1, 255);

        //输出修改后的矩阵数据
        System.out.println(m.dump());
        System.out.println();

        //获取第二行第二列的像素值,存储在data数组中
        int[] data = new int[1];//单通道,所以数组长度为1即可
        m.get(1, 1, data);

        //输出像素值
        System.out.println(data[0]);
    }
}
java 复制代码
[1, 0, 0;
 0, 1, 0;
 0, 0, 1]

[1, 0, 0;
 0, 255, 0;
 0, 0, 1]

255

看上去问题得到了解决,但是实际上还存在一个问题,矩阵的数据类型为CV_32SC1,不是希望的CV_8UC1。要彻底解决这个问题,需要调用Mat类的convertTo()方法。

5.2、批量获取/修改像素值

如果将get/put方法的行号和列号都置为0,同时第三个参数数组足够大,则可以用于批量获取或修改数据。

java 复制代码
public class MatPixel {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        Mat m = Mat.eye(2, 2, CvType.CV_32SC3);

        //将矩阵数据存放在数据data中
        int[] data = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 128, 200, 255};
        //批量修改矩阵数据
        m.put(0, 0, data);

        //查看矩阵数据
        System.out.println(m.dump());
        System.out.println();

        //转化矩阵数据类型
        Mat mat = new Mat();
        m.convertTo(mat, CvType.CV_8SC3);

        //获取矩阵的所有数据
        int[] i = new int[12];
        //获取所有数据
        m.get(0, 0, i);

        //查看mat的矩阵头
        System.out.println(mat);
        System.out.println();

        //查看矩阵数据
        System.out.println(mat.dump());
        System.out.println();

        //查看数组i的数据
        for (int n = 0; n < i.length; n++) {
            System.out.print(i[n] + ",");
        }
    }
}
java 复制代码
[1, 2, 3, 4, 5, 6;
 7, 8, 9, 128, 200, 255]

Mat [ 2*2*CV_8SC3, isCont=true, isSubmat=false, nativeObj=0x600002436d00, dataAddr=0x7fc463805040 ]

[  1,   2,   3,   4,   5,   6;
   7,   8,   9, 127, 127, 127]

1,2,3,4,5,6,7,8,9,128,200,255,

六、常用数据结构

6.1、Point类(点)

Point类用于表示二维坐标系中的一个点。成员变量为x,y。

java 复制代码
Point(double x, double y)
x:点的x坐标
y:点的y坐标

6.2、Rect类(矩形)

Rect类用于表示一个矩形,成员变量为x,y,width和height。

java 复制代码
Rect(int x, int y, int width, int height)
x:左上角顶点x坐标
y:左上角顶点y坐标
width:矩形宽度
height:矩形高度

6.3、Size类(尺寸)

Size类用于表示尺寸,成员变量为width,height。

java 复制代码
Size(double width, double height)
width:宽度
height:高度

6.4、Scalar类(颜色)

Scalar表示具有4个元素的数组,在OpenCV中被用来传递颜色值,如RGB的色彩值。Scalar表示颜色时的顺序是B(蓝色)、G(绿色)、R(红色),如果是四通道,则在后面跟透明度。

java 复制代码
Scalar(double v0)
通常用于构造灰度图像
v0:灰度值

Scalar(double v0, double v1, double v2);
通常用于三通道图像

v0:B值
v1:G值
v2:R值

Scalar(double v0, double v1, double v2, double v3);
通常用于四通道图像

v0:B值
v1:G值
v2:R值
v3:透明度值

6.5、示例

java 复制代码
public class BaseStructure {

    static {
        OpenCV.loadLocally(); // 自动下载并加载本地库
    }

    public static void main(String[] args) {
        //创建Point
        Point p = new Point(10, 20);
        System.out.println(p);
        System.out.println(p.x + "," + p.y);
        System.out.println();

        //创建Rect
        Rect r = new Rect(10, 20, 50, 100);
        System.out.println(r);
        System.out.println(r.x + "," + r.y + "," + r.width + "," + r.height);
        System.out.println();

        //创建Size
        Size s = new Size(50, 100);
        System.out.println(s);
        System.out.println(s.width + "," + s.height);
        System.out.println();

        //创建Scalar
        Scalar red = new Scalar(0, 0, 255);
        System.out.println(red);
        System.out.println();
    }
}
java 复制代码
{10.0, 20.0}
10.0,20.0

{10, 20, 50x100}
10,20,50,100

50x100
50.0,100.0

[0.0, 0.0, 255.0, 0.0]
相关推荐
曦月逸霜30 分钟前
第34次CCF-CSP认证真题解析(目标300分做法)
数据结构·c++·算法
jndingxin2 小时前
OpenCV CUDA模块图像处理------创建一个模板匹配(Template Matching)对象函数createTemplateMatching()
图像处理·人工智能·opencv
贩卖纯净水.2 小时前
Webpack的基本使用 - babel
前端·webpack·node.js
贩卖纯净水.4 小时前
Webpack依赖
前端·webpack·node.js
笑口常开xpr4 小时前
数 据 结 构 进 阶:哨 兵 位 的 头 结 点 如 何 简 化 链 表 操 作
数据结构·链表·哨兵位的头节点
@我漫长的孤独流浪5 小时前
数据结构测试模拟题(4)
数据结构·c++·算法
新知图书8 小时前
OpenCV为图像添加边框
人工智能·opencv·计算机视觉
YGGP9 小时前
吃透 Golang 基础:数据结构之 Map
开发语言·数据结构·golang
weixin_4196583110 小时前
数据结构之栈
数据结构