【OpenCV】Mat详解

在OpenCV中,cv::Mat是用于存储图像、矩阵等多维数据的核心数据结构,替代了早期的IplImage(需手动管理内存),其设计的核心目标是自动内存管理高效数据操作。下面详细介绍其组成原理及使用方法。

一、cv::Mat的组成原理

cv::Mat的结构由两部分组成:矩阵头(Matrix Header)数据指针(Data Pointer),二者分离的设计使其既能高效传递,又能避免冗余内存占用。

1. 矩阵头(Matrix Header)

矩阵头是一个轻量级结构体,存储了数据的元信息,不直接存储像素数据。核心成员包括:

  • rows:行数(图像的高度,单位为像素)。
  • cols:列数(图像的宽度,单位为像素)。
  • size():返回cv::Size(cols, rows),便捷表示尺寸。
  • type():数据类型,由位深度通道数组成(如CV_8UC3表示8位无符号整数,3通道)。
    • 位深度:如8U(8位无符号)、16S(16位有符号)、32F(32位浮点)等。
    • 通道数:单通道(灰度图)、3通道(RGB/BGR)、4通道(带Alpha通道)等。
  • channels():返回通道数(由type()推导,如CV_8UC3的通道数为3)。
  • step:行步长(每行数据的总字节数,包括像素数据和可能的填充字节,用于快速定位某一行的起始地址)。
  • refcount:引用计数器(用于内存自动释放,记录当前有多少个Mat对象共享同一块数据)。
2. 数据指针(Data Pointer)

数据指针(uchar* data)指向存储实际像素数据的内存区域。数据在内存中的排列方式由通道数决定:

  • 单通道(灰度图):按行存储,每行像素依次排列(如[p0, p1, p2, ..., p_cols-1])。
  • 多通道:每个像素的通道数据连续存储(如3通道图像的一个像素为[B, G, R],按B0, G0, R0, B1, G1, R1, ...排列,OpenCV默认通道顺序为BGR而非RGB)。
3. 内存管理机制:引用计数

cv::Mat通过引用计数实现高效内存管理:

  • 当复制Mat对象(如Mat B = A)时,仅复制矩阵头,数据指针指向同一块内存,引用计数refcount加1。
  • Mat对象销毁时,引用计数减1;当refcount为0时,自动释放数据内存,避免内存泄漏。
  • 若需深拷贝(独立数据),需使用clone()copyTo()方法(如Mat C = A.clone())。

二、cv::Mat的使用方法

1. 创建cv::Mat对象

常用创建方式包括:

(1)通过构造函数创建

指定行数、列数、数据类型:

// 创建3行2列的8位无符号单通道矩阵(初始值随机)
cv::Mat mat1(3, 2, CV_8UC1);// 创建3行2列的32位浮点3通道矩阵(初始值随机)
cv::Mat mat2(3, 2, CV_32FC3);// 用cv::Size指定尺寸(宽x高)
cv::Mat mat3(cv::Size(200, 100), CV_8UC3); // 宽200,高100(rows=100, cols=200)
(2)创建并初始化特殊矩阵

使用zeros()ones()eye()(单位矩阵):

// 创建3x3的8位无符号单通道零矩阵
cv::Mat zeros_mat = cv::Mat::zeros(3, 3, CV_8UC1);// 创建2x4的32位浮点3通道全1矩阵
cv::Mat ones_mat = cv::Mat::ones(2, 4, CV_32FC3);// 创建5x5的64位浮点单通道单位矩阵
cv::Mat eye_mat = cv::Mat::eye(5, 5, CV_64FC1);
(3)从已有数据创建

将外部数组数据包装为Mat(不复制数据,仅共享内存):

float data[] = {1.0f, 2.0f, 3.0f, 4.0f};
// 创建2行2列的32位浮点单通道矩阵,数据指向data数组
cv::Mat mat_from_data(2, 2, CV_32FC1, data);
(4)从图像文件读取

使用cv::imread读取图像,返回Mat对象:

// 读取彩色图像(默认3通道BGR)
cv::Mat img_color = cv::imread("image.jpg");// 读取灰度图(单通道)
cv::Mat img_gray = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
2. 访问cv::Mat的属性

通过成员函数或成员变量获取元信息:

cv::Mat img = cv::imread("image.jpg");
int rows = img.rows;         // 图像高度(行数)
int cols = img.cols;         // 图像宽度(列数)
cv::Size size = img.size();  // 尺寸(cols, rows)
int channels = img.channels(); // 通道数(如3)
int type = img.type();       // 数据类型(如CV_8UC3)
size_t step = img.step;      // 行步长(每行字节数)
3. 访问像素数据

根据场景选择不同方法(效率和便捷性权衡):

(1)at<T>()方法(便捷,适合单像素访问)

需指定数据类型T(与type()匹配),语法:mat.at<T>(row, col)(单通道)或mat.at<T>(row, col)[channel](多通道)。

cv::Mat img = cv::imread("image.jpg"); // CV_8UC3类型// 访问(10, 20)处的像素(行10,列20)
cv::Vec3b pixel = img.at<cv::Vec3b>(10, 20); // Vec3b对应8UC3(3个uchar)
uchar blue = pixel[0];   // B通道
uchar green = pixel[1]; // G通道
uchar red = pixel[2];    // R通道// 修改像素值
img.at<cv::Vec3b>(10, 20) = cv::Vec3b(255, 0, 0); // 改为蓝色
  • 常用类型对应:CV_8UC1ucharCV_32FC1floatCV_8UC3cv::Vec3bCV_32FC3cv::Vec3f
(2)行指针ptr<T>()(高效,适合遍历行)

获取某一行的起始指针,通过指针遍历像素(效率高于at<T>()):

cv::Mat img = cv::imread("image.jpg"); // 8UC3
for (int i = 0; i < img.rows; ++i) {// 获取第i行的指针(uchar*,因8UC3每个像素3字节)uchar* row_ptr = img.ptr<uchar>(i);for (int j = 0; j < img.cols; ++j) {// 计算当前像素的起始位置(每行j列的像素:j*通道数)int pos = j * 3;uchar b = row_ptr[pos];     // Buchar g = row_ptr[pos + 1]; // Guchar r = row_ptr[pos + 2]; // R// 修改为灰度(简单平均)row_ptr[pos] = row_ptr[pos + 1] = row_ptr[pos + 2] = (b + g + r) / 3;}
}
(3)迭代器(安全,适合复杂遍历)

使用cv::MatIterator_<T>遍历,自动处理边界:

cv::Mat img = cv::imread("image.jpg");
// 3通道迭代器
cv::MatIterator_<cv::Vec3b> it = img.begin<cv::Vec3b>();
cv::MatIterator_<cv::Vec3b> it_end = img.end<cv::Vec3b>();
for (; it != it_end; ++it) {// 每个迭代器指向一个像素(Vec3b)(*it)[0] = 0; // 将B通道置0
}
4. 常用操作
  • 通道分离与合并:用split()merge()处理多通道图像:

    cv::Mat img = cv::imread("image.jpg"); // BGR
    std::vector<cv::Mat> channels;
    cv::split(img, channels); // 分离为3个单通道(B, G, R)
    channels[2] = cv::Mat::zeros(img.size(), CV_8UC1); // 将R通道置0
    cv::Mat img_no_red;
    cv::merge(channels, img_no_red); // 合并回3通道
    
  • ROI(感兴趣区域):提取子矩阵(共享原数据,需深拷贝时用clone()):

    cv::Mat img = cv::imread("image.jpg");
    // 提取从(10, 20)开始,宽100、高50的区域(行范围[10,10+50),列范围[20,20+100))
    cv::Mat roi = img(cv::Rect(20, 10, 100, 50)); // Rect(x, y, width, height)
    roi.setTo(cv::Scalar(0, 255, 0)); // 直接修改ROI,原图像也会变化
    
  • 保存图像:用cv::imwrite

    cv::Mat img = cv::imread("image.jpg");
    cv::imwrite("output.jpg", img); // 保存为JPG
    

三、注意事项

  1. 数据类型匹配:访问像素时,at<T>()ptr<T>()T必须与Mat::type()匹配(如CV_8UC3对应cv::Vec3b),否则会导致内存访问错误。
  2. 引用计数与深拷贝:默认复制为浅拷贝(共享数据),需独立数据时用clone()copyTo()
  3. 通道顺序:OpenCV默认图像通道为BGR(而非RGB),处理时需注意转换(可通过cv::cvtColor(img, img_rgb, cv::COLOR_BGR2RGB)转换)。

通过理解cv::Mat的组成和使用方法,可高效处理图像数据,避免内存问题并优化操作性能。

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

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

相关文章

疏老师-python训练营-Day45Tensorboard使用介绍

浙大疏锦行知识点回顾&#xff1a; tensorboard的发展历史和原理tensorboard的常见操作tensorboard在cifar上的实战&#xff1a;MLP和CNN模型 效果展示如下&#xff0c;很适合拿去组会汇报撑页数&#xff1a; 作业&#xff1a;对resnet18在cifar10上采用微调策略下&#xff0c;…

算法详细讲解:基础算法 - 离散化/区间合并

离散化 讲解 这里的离散化特指整数有序离散化。整个值域跨度很大&#xff0c;但是值非常稀疏的情况。 问题背景 我们有一个无限长的数轴&#xff0c;初始时每个位置上的值都是0。我们需要进行两种操作&#xff1a; 修改操作&#xff1a;在某个位置 x 上增加一个值 c。查询…

SpringBoot 实现在线查看内存对象拓扑图 —— 给 JVM 装上“透视眼”

0. 你将获得什么 一个可嵌入任何 Spring Boot 应用的内存对象拓扑服务&#xff1a;访问 /memviz.html 就能在浏览器看见对象图。 支持按类/包名过滤、按对象大小高亮、点击节点看详情。 线上可用&#xff1a;默认只在你点击“生成快照”时才工作&#xff1b;日常零开销。 1.…

STM32 HAL驱动MPU6050传感器

STM32 HAL驱动MPU6050传感器 项目概述 本项目实现了基于STM32 HAL库的MPU6050传感器驱动&#xff0c;可以读取加速度计和陀螺仪数据。项目使用I2C接口与MPU6050通信&#xff0c;并通过UART接口输出数据。 项目仓库地址&#xff1a;STM32_Sensor_Drives 硬件连接 MPU6050 I2…

flex-wrap子元素是否换行

flex-wrap设置子元素是否换行&#xff0c;默认情况下&#xff0c;项目都排在一条线&#xff08;又称”轴线”&#xff09;上。flex-wrap属性定义&#xff0c;flex布局中默认是不换行的。1、div的宽度是600px&#xff0c;每个span的宽度是150px&#xff0c;总共有5个&#xff0c…

RabbitMQ面试精讲 Day 21:Spring AMQP核心组件详解

【RabbitMQ面试精讲 Day 21】Spring AMQP核心组件详解 开篇 欢迎来到"RabbitMQ面试精讲"系列第21天&#xff01;今天我们将深入探讨Spring AMQP的核心组件&#xff0c;这是Java开发者集成RabbitMQ最常用的框架。掌握Spring AMQP不仅能提升开发效率&#xff0c;更是…

Flink TableAPI 按分钟统计数据量

一、环境版本环境版本Flink1.17.0Kafka2.12MySQL5.7.33二、MySQL建表脚本 create table user_log (id int auto_increment comment 主键primary key,uid int not null comment 用户id,event int not null comment 用户行为,logtime bigint null comment 日志时…

18.13 《3倍效率提升!Hugging Face datasets.map高级技巧实战指南》

3倍效率提升!Hugging Face datasets.map高级技巧实战指南 实战项目:使用 datasets.map 进行高级数据处理 在大模型训练过程中,数据预处理的质量直接决定了模型最终的表现。Hugging Face Datasets 库提供的 datasets.map 方法是处理复杂数据场景的瑞士军刀,本章将深入解析…

实体店获客新引擎:数据大集网如何破解传统门店引流难题

在商业竞争日益激烈的当下&#xff0c;实体店的生存与发展正面临前所未有的挑战。无论是街边的小型便利店&#xff0c;还是大型购物中心的连锁品牌&#xff0c;都在为"如何吸引顾客进店"而绞尽脑汁。传统广告投放效果不佳、线下流量持续萎缩、客户转化率难以提升………

LeetCode 分类刷题:2302. 统计得分小于 K 的子数组数目

题目一个数组的 分数 定义为数组之和 乘以 数组的长度。比方说&#xff0c;[1, 2, 3, 4, 5] 的分数为 (1 2 3 4 5) * 5 75 。给你一个正整数数组 nums 和一个整数 k &#xff0c;请你返回 nums 中分数 严格小于 k 的 非空整数子数组数目。子数组 是数组中的一个连续元素序…

TDengine IDMP 基本功能(1.界面布局和操作)

UI 布局和操作说明 TDengine IDMP 的用户界面&#xff08;UI&#xff09;设计旨在提供直观、易用的操作体验。下面介绍 UI 的主要区域和典型操作&#xff1a; 主要区域 IDMP 的用户界面是完全基于浏览器的。登录后的典型 UI 界面具有几个区域&#xff1a; 主菜单&#xff1a;AI…

QT(概述、基础函数、界面类、信号和槽)

一、概述1、QTQT是一个c的第三方库&#xff0c;是专门用来进行界面编程的一个库 1. QT本身实现了多种软件&#xff1a; 2. ubuntu系统中所有界面都是QT做的 3. 最新版本的QQ也是QT做的 4. 嵌入式编程中&#xff0c;几乎所有的上位机&#xff0c;都可以使用QT来做 QT本身除了实现…

【从零开始java学习|第六篇】运算符的使用与注意事项

目录 一、算术运算符 1. 基本算术运算符&#xff08;二元&#xff09; 2. 自增 / 自减运算符&#xff08;一元&#xff09; 二、类型转换&#xff08;隐式与强制&#xff09; 1. 隐式转换&#xff08;自动类型转换&#xff09; ​编辑 2. 强制转换&#xff08;显式类型转…

shellgpt

一、介绍 官网&#xff1a;https://github.com/TheR1D/shell_gpt ShellGPT&#xff08;shell_gpt&#xff09; 是一款把 GPT 系列大模型能力直接搬到终端 的开源命令行生产力工具。用日常英语或中文描述需求&#xff0c;就能帮你 生成、解释甚至自动执行 Shell 命令&#xff…

geoserver sql视图调用Postgis自定义函数问题记录

一、问题描述&#xff1a;geoserver sql视图调用Postgis自定义函数对点图层增加一条记录时&#xff0c;返回结果主键自增ID加了2&#xff0c;但表中数据只增加一条记录。 但在pgAdmin中直接写SQL调用Postgis自定义函数对点图层增加一条记录时&#xff0c;返回结果主键自增ID只加…

#T1224. 最大子矩阵

题目传送 题目描述 已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵&#xff0c;你的任务是找到最大的非空(大小至少是11)子矩阵。 比如&#xff0c;如下44的矩阵 0 -2 -7 09 2 -6 2 -4 1 -4 1-1 8 0 -2的最大子矩阵是 9 2-4 1-1 8这…

2025年大模型安全岗的面试汇总(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 1. Transformer核心机制及其对LLM突破的基石作用 2. LLM能力边界评估框架设计 3. 模型层级安全风险分析 …

《关于省级政务云服务费支出预算标准的规定》豫财预〔2024〕106号解读

《关于省级政务云服务费支出预算标准的规定》豫财预〔2024〕106号文件由河南省财政厅编制经省政府同意后于2024年12月3日印发执行&#xff0c;规定作为省级政务云服务费支出预算编制和审核的依据&#xff0c;旨在加强省级部门预算管理&#xff0c;规范政务云服务费支出预算编制…

使用HalconDotNet实现异步多相机采集与实时处理

文章目录 一、核心功能与原理 功能目标: 工作原理: 关键机制: 二、完整C#实现代码 三、关键实现解析 1. 零拷贝图像传输 2. 动态帧率控制 3. HALCON并行优化 4. 异常隔离机制 四、高级优化策略 1. 硬件加速配置 2. 内存池管理 3. 实时性保障 一、核心功能与原理 功能目标:…

《疯狂Java讲义(第3版)》学习笔记ch4

ch4流程控制与数组1.switch语句后的expression表达式的数据类型只能是byte、short、char、int四种证书类型。2.建议不要在循环体内修改循环变量&#xff08;也叫循环计数器&#xff09;的值&#xff0c;否则会增加程序出错的可能性。3.定义数组推荐语法格式&#xff1a;type[] …