当前位置: 首页 > article >正文

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

Mat类及常用数据结构

  • 一、Mat类简介
    • 1.1、矩阵头
    • 1.2、矩阵的数据类型
    • 1.3、Mat的子类
  • 二、矩阵数据的存储
    • 2.1、单通道
    • 2.2、多通道
  • 三、创建矩阵的方法
    • 3.1、静态方法创建
    • 3.2、构造方法创建
    • 3.3、读取图像文件创建
    • 3.4、克隆创建
  • 四、获取矩阵信息
  • 五、矩阵相关操作
    • 5.1、获取/修改像素值
    • 5.2、批量获取/修改像素值
  • 六、常用数据结构
    • 6.1、Point类(点)
    • 6.2、Rect类(矩形)
    • 6.3、Size类(尺寸)
    • 6.4、Scalar类(颜色)
    • 6.5、示例

一、Mat类简介

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

1.1、矩阵头

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

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);}
}
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_8U08位无符号整数0~255
CV_8S18位有符号整数-128~127
CV_16U216位无符号整数0~65535
CV_16S316位有符号整数-32768~32767
CV_32S432位有符号整数-2147483~2147483647
CV_32F532位浮点数-
CV_64F664位浮点数-

1.3、Mat的子类

Mat类可以存储哥哥不同的数据类型。根据数据类型的不同,Mat类又派生出多个子类。
在这里插入图片描述

二、矩阵数据的存储

2.1、单通道

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

11列的灰度值 12列的灰度值 13列的灰度值...
21列的灰度值 22列的灰度值 23列的灰度值...
31列的灰度值 32列的灰度值 33列的灰度值...
...

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

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());}
}
1通道,所以同一行1个值表示1个像素点
[  0,   0,   0;0,   0,   0;0,   0,   0]

2.2、多通道

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

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());}
}
3通道,所以13个值表示一个像素点第一行	         		第二行          	   第三行 
[  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、静态方法创建

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,其余值为0Mat m3 = Mat.eye(3, 3, CvType.CV_8UC1);System.out.println(m3.dump());//需要注意的是,如果通道数大于1,则onces()创建的Mat类只有第一个通道的值为1Mat m4 = Mat.ones(3, 3, CvType.CV_8UC3);System.out.println(m4.dump());}
}
[  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种方法大多类似,下面介绍最常用的几种:

//构造方式创建
//方法一
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、读取图像文件创建

//读取文件创建
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、克隆创建

Mat src = Mat.ones(2, 2, CvType.CV_8UC1);
Mat dst - src.clone();

四、获取矩阵信息

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());}
}
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)
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();//将第二行第二列值改为9m.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]);}
}
[  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的上限,修改后代码如下:

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();//将第二行第二列值改为9m.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]);}
}
[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,同时第三个参数数组足够大,则可以用于批量获取或修改数据。

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] + ",");}}
}
[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。

Point(double x, double y)
x:点的x坐标
y:点的y坐标

6.2、Rect类(矩形)

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

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

6.3、Size类(尺寸)

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

Size(double width, double height)
width:宽度
height:高度

6.4、Scalar类(颜色)

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

Scalar(double v0)
通常用于构造灰度图像
v0:灰度值Scalar(double v0, double v1, double v2);
通常用于三通道图像v0:B值
v1:G值
v2:RScalar(double v0, double v1, double v2, double v3);
通常用于四通道图像v0:B值
v1:G值
v2:R值
v3:透明度值

6.5、示例

public class BaseStructure {static {OpenCV.loadLocally(); // 自动下载并加载本地库}public static void main(String[] args) {//创建PointPoint p = new Point(10, 20);System.out.println(p);System.out.println(p.x + "," + p.y);System.out.println();//创建RectRect 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();//创建SizeSize s = new Size(50, 100);System.out.println(s);System.out.println(s.width + "," + s.height);System.out.println();//创建ScalarScalar red = new Scalar(0, 0, 255);System.out.println(red);System.out.println();}
}
{10.0, 20.0}
10.0,20.0{10, 20, 50x100}
10,20,50,10050x100
50.0,100.0[0.0, 0.0, 255.0, 0.0]
http://www.lryc.cn/news/2400611.html

相关文章:

  • 深入解析FutureTask:原理与实战
  • 每天总结一个html标签——Audio音频标签
  • 使用 React Native 开发鸿蒙(HarmonyOS)运动健康类应用的系统化准备工作
  • web3-Remix部署智能合约到“荷兰式”拍卖及以太坊gas费机制细讲
  • 网络编程及原理(一)
  • superior哥AI系列第9期:高效训练与部署:从实验室到生产环境
  • 【Linux】进程 信号保存 信号处理 OS用户态/内核态
  • [ Qt ] | 与系统相关的操作(一):鼠标相关事件
  • stm32使用hal库模拟spi模式3
  • 安装 Nginx
  • Vue-1-前端框架Vue基础入门之一
  • OurBMC技术委员会2025年二季度例会顺利召开
  • postman自动化测试
  • 力扣热题100之二叉树的直径
  • 数字人技术的核心:AI与动作捕捉的双引擎驱动(210)
  • c++ 命名规则
  • GRU 参数梯度推导与梯度消失分析
  • 针对KG的神经符号集成综述 两篇
  • RabbitMQ和MQTT区别与应用
  • Vue跨层级通信
  • docker常见命令行用法
  • Axure设计案例:滑动拼图解锁
  • MySQL权限详解
  • 基于BP神经网络的语音特征信号分类
  • 解决fastadmin、uniapp打包上线H5项目路由冲突问题
  • web3-区块链的交互性以及编程的角度看待智能合约
  • 数据结构(7)—— 二叉树(1)
  • ROS1和ROS2的区别autoware.ai和autoware.universe的区别
  • 如何使用 Docker 部署grafana和loki收集vllm日志?
  • Kafka入门- 基础命令操作指南