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

news2025/7/24 10:16:23

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,其余值为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());
    }
}
[  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();

        //将第二行第二列值改为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]);
    }
}
[  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();

        //将第二行第二列值改为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]);
    }
}
[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) {
        //创建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();
    }
}
{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]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2400611.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每天总结一个html标签——Audio音频标签

Audio标签 文章目录 Audio标签一、audio标签的定义与介绍1. 定义介绍2. 语法3. 支持的格式4.文本提示 二、audio标签的HTML属性1. autoplay2. loop3. muted4. preload 三、audio标签的常用DOM属性四、audio标签的常用事件四、默认样式五、自定义样式1. 示例2. 代码 六、播放 m3…

web3-Remix部署智能合约到“荷兰式”拍卖及以太坊gas费机制细讲

web3-Remix部署智能合约到“荷兰式”拍卖及以太坊gas费机制细讲 一、使用Remix演示智能合约部署 智能合约的代码编写一般都是在Remix上&#xff0c;Remix的好处的话就是可以在浏览器中快速开发和部署合约&#xff0c;无需在本地安装任何程序&#xff0c;十分适合新手。 对应…

网络编程及原理(一)

目录 一 . 独立模式与网络互联 二 . 局域网 —— LAN &#xff08;1&#xff09;基于网线直连 &#xff08;2&#xff09;基于集线器组建 &#xff08;3&#xff09;基于交换机组建 &#xff08;4&#xff09;基于交换机和路由器组建 三 . 广域网 —— WAN 四 …

【Linux】进程 信号保存 信号处理 OS用户态/内核态

&#x1f33b;个人主页&#xff1a;路飞雪吖~ &#x1f320;专栏&#xff1a;Linux 目录 一、信号保存 ✨进程如何完成对信号的保存&#xff1f; ✨在内核中的表示 ✨sigset_t ✨信号操作函数 &#x1fa84;sigprocmask --- 获取或设置当前进程的 block表 &#x1fa84;s…

[ Qt ] | 与系统相关的操作(一):鼠标相关事件

目录 信号和事件的关系 (leaveEvent和enterEvent) 实现通过事件获取鼠标进入和鼠标离开 (mousePressEvent) 实现通过事件获得鼠标点击的位置 (mouseReleaseEvent) 前一个的基础上添加鼠标释放事件 (mouseDoubleClickEvent) 鼠标双击事件 鼠标移动事件 鼠标滚轮事件 …

stm32使用hal库模拟spi模式3

因为网上模拟spi模拟的都是模式0&#xff0c;很少有模式3的。 模式3的时序图&#xff0c;在clk的下降沿切换电平状态&#xff0c;在上升沿采样&#xff0c; SCK空闲为高电平 初始化cs&#xff0c;clk&#xff0c;miso&#xff0c;mosi四个io。miso配置为输入&#xff0c;cs、c…

OurBMC技术委员会2025年二季度例会顺利召开

5月28日&#xff0c;OurBMC社区技术委员会二季度例会顺利召开。本次会议采用线上线下结合的方式&#xff0c;各委员在会上听取了OurBMC社区二季度工作总结汇报&#xff0c;规划了2025年三季度的重点工作。 会上&#xff0c;技术委员会主席李煜汇报了社区2025年二季度主要工作及…

postman自动化测试

目录 一、相关知识 1.网络协议 2.接口测试 3.编写测试用例 4.系统架构 二、如何请求 1.get请求 ​编辑2.post请求 3.用环境变量请求 4.Postman测试沙箱 一、相关知识 1.网络协议 规定数据信息发送与解析的方式。 网络传输协议 https相比http&#xff0c;信息在网…

力扣热题100之二叉树的直径

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 代码 方法&#xff1a;递归 计算二叉树的直径可以理解…

数字人技术的核心:AI与动作捕捉的双引擎驱动(210)

**摘要&#xff1a;**数字人技术从静态建模迈向动态交互&#xff0c;AI与动作捕捉技术的深度融合推动其智能化发展。尽管面临表情僵硬、动作脱节、交互机械等技术瓶颈&#xff0c;但通过多模态融合技术、轻量化动捕方案等创新&#xff0c;数字人正逐步实现自然交互与情感表达。…

针对KG的神经符号集成综述 两篇

帖子最后有五篇综述的总结。 综述1 24年TKDD 系统性地概述了神经符号知识图谱推理领域的进展、技术和挑战。首先介绍了知识图谱&#xff08;KGs&#xff09;和符号逻辑的基本概念&#xff0c;知识图谱被视为表示、存储和有效管理知识的关键工具&#xff0c;它将现实世界的知识…

RabbitMQ和MQTT区别与应用

RabbitMQ与MQTT深度解析&#xff1a;协议、代理、差异与应用场景 I. 引言 消息队列与物联网通信的重要性 在现代分布式系统和物联网&#xff08;IoT&#xff09;生态中&#xff0c;高效、可靠的通信机制是构建稳健、可扩展应用的核心。消息队列&#xff08;Message Queues&am…

Axure设计案例:滑动拼图解锁

设计以直观易懂的操作方式为核心&#xff0c;只需通过简单的滑动动作&#xff0c;将拼图块精准移动至指定位置&#xff0c;即可完成解锁。这种操作模式既符合用户的日常操作习惯&#xff0c;在视觉呈现上&#xff0c;我们精心设计拼图图案&#xff0c;融入生动有趣的元素&#…

MySQL权限详解

在MySQL中&#xff0c;权限管理是保障数据安全和合理使用的重要手段。MySQL提供了丰富的权限控制机制&#xff0c;允许管理员对不同用户授予不同级别的操作权限。本文将会对MySQL中的权限管理&#xff0c;以及内核如何实现权限控制进行介绍。 一、权限级别 MySQL 的权限是分层…

解决fastadmin、uniapp打包上线H5项目路由冲突问题

FastAdmin 基于 ThinkPHP&#xff0c;默认采用 URL 路由模式&#xff08;如 /index.php/module/controller/action&#xff09;&#xff0c;且前端资源通常部署在公共目录&#xff08;如 public/&#xff09;下。Uniapp 的历史模式需要将所有前端路由请求重定向到 index.html&a…

web3-区块链的交互性以及编程的角度看待智能合约

web3-区块链的交互性以及编程的角度看待智能合约 跨链交互性 交互性 用户在某一区块链生态上拥有的资产和储备 ​ 目标&#xff1a;使用户能够把资产和储备移动到另一个区块链生态上 可组合性 使在某一区块链的DAPP能调用另一个区块链上的DAPP 如果全世界都在用以太坊就…

数据结构(7)—— 二叉树(1)

目录 前言 一、 树概念及结构 1.1树的概念 1.2树的相关概念 1.3数的表示 1.二叉树表示 2.孩子兄弟表示法 3.动态数组存储 1.4树的实际应用 二、二叉树概念及结构 2.1概念 2.2特殊的二叉树 1.满二叉树 2. 完全二叉树 2.3二叉树的性质 2.4二叉树的存储结构 1.顺序存储 2.链式存储…

如何使用 Docker 部署grafana和loki收集vllm日志?

环境: Ubuntu20.04 grafana loki 3.4.1 问题描述: 如何使用 Docker 部署grafana和loki收集vllm日志? 解决方案: 1.创建一个名为 loki 的目录。将 loki 设为当前工作目录: mkdir loki cd loki2.将以下命令复制并粘贴到您的命令行中,以将 loki-local-config.yaml …

Kafka入门- 基础命令操作指南

基础命令 主题 参数含义–bootstrap-server连接的Broker主机名称以及端口号–topic操作的topic–create创建主题–delete删除主题–alter修改主题–list查看所有主题–describe查看主题的详细描述–partitions设置分区数–replication-factor设置分区副本–config更新系统默认…

目标检测我来惹1 R-CNN

目标检测算法&#xff1a; 识别图像中有哪些物体和位置 目标检测算法原理&#xff1a; 记住算法的识别流程、解决问题用到的关键技术 目标检测算法分类&#xff1a; 两阶段&#xff1a;先区域推荐ROI&#xff0c;再目标分类 region proposalCNN提取分类的目标检测框架 RC…