OpenCV中Mat属性step,size,step1,elemSize,elemSize1

OpenCV中Mat属性step,size,step1,elemSize,elemSize1

参考链接

cv::Mat 中通道和维度不是一个概念

  • data:Mat对象中的一个指针,指向内存中存放矩阵数据的一块内存 (uchar* data)
  • dims:Mat所代表的矩阵的维度,如 3 * 4 的矩阵为 2 维, 3 * 4 * 5 的为3维
  • channels:通道,矩阵中的每一个矩阵元素拥有的值的个数,比如说 3 * 4 矩阵中一共 12 个元素,如果每个元素有三个值,那么就说这个矩阵是 3 通道的,即 channels = 3。常见的是一张彩色图片有红、绿、蓝三个通道。
  • depth:深度,即每一个像素的位数(bits),在opencv的Mat.depth()中得到的是一个 0 – 6 的数字,分别代表不同的位数:enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }; 可见 0和1都代表8位, 2和3都代表16位,4和5代表32位,6代表64位;
例如一个3x 4 x 5的矩阵,dims = 3, channels = 12 的含义如下:
三维矩阵,每个矩阵元素拥有的值的个数为12。

step size step1 elementSize elementSize1

这里注意:

1.step的大小是字节

2.注意下标与维数的对应关系:下标2对应点,1对应线,0对应面

3.矩阵有几维,step[]数组就有几个元素,如3维,则有3个元素,step[0],step[1],step[2].分别对应面,线,点
只要记住,最后一个总是表示点,然后依次向前为线,面...

4.第2,3 点 ,对于size和step1()也一样。

step1(i)和size[]与step[i]原理相同。

5. step1 = step / elemSize1
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

//定义全局坐标系

int main() {
  std::cout << "=====================================================二维矩阵=="
               "============================================="
            << std::endl;
  cv::Mat test_mat(cv::Size(2, 3), CV_8UC3, cv::Scalar(1, 2, 3));
  std::cout << test_mat << std::endl;

  std::cout << "size of uchar is: " << sizeof(uchar) << std::endl;

  std::cout << "[step]: " << test_mat.step << std::endl;
  // step
  // 可以理解为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量,是累计了一行中所
  // 有元素、所有通道、所有通道的elemSize1之后的值;

  std::cout << "[step[0]]: " << test_mat.step[0] << std::endl;
  std::cout << "[step[1]]: " << test_mat.step[1] << std::endl;

  // 矩阵有几维,step[]数组就有几个元素,如3维,则有3个元素,step[0],step[1],step[2].分别对应面,线,点
  // 只要记住,最后一个总是表示点,然后依次向前为线,面...

  std::cout << "[step1[0]]: " << test_mat.step1(0) << std::endl;
  std::cout << "[step1[1]]: " << test_mat.step1(1) << std::endl;

  // step1(i):每一维元素的通道数;
  // 也就是每一维元素的个数

  std::cout << "[elemSize]: " << test_mat.elemSize() << std::endl;
  // 每个元素的大小(单位字节)  ,每个矩阵元素拥有的值的个数为通道数

  std::cout << "[elemSize1]: " << test_mat.elemSize1() << std::endl;
  //每个通道的大小(单位字节)

  std::cout << "=====================================================三维矩阵=="
               "============================================="
            << std::endl;

  int matsize[] = {5, 8, 10};
  cv::Mat test_mat1(3, matsize, CV_16UC3, cv::Scalar(1, 2, 3));

  std::cout << "size of cv_16u is: " << sizeof(ushort) << std::endl;
  std::cout << "dim is: " << test_mat1.dims << std::endl;

  std::cout << "[step]: " << test_mat1.step << std::endl;
  // step
  // 可以理解为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量,是累计了一行中所
  // 有元素、所有通道、所有通道的elemSize1之后的值;

  std::cout << "[step[0]]: " << test_mat1.step[0] << std::endl;
  std::cout << "[step[1]]: " << test_mat1.step[1] << std::endl;
  std::cout << "[step[2]]: " << test_mat1.step[2] << std::endl;

  // 矩阵有几维,step[]数组就有几个元素,如3维,则有3个元素,step[0],step[1],step[2].分别对应面,线,点
  // 只要记住,最后一个总是表示点,然后依次向前为线,面...

  std::cout << "[step1[0]]: " << test_mat1.step1(0) << std::endl;
  std::cout << "[step1[1]]: " << test_mat1.step1(1) << std::endl;
  std::cout << "[step1[2]]: " << test_mat1.step1(2) << std::endl;

  // step1(i):每一维元素的通道数;
  // 也就是每一维元素的个数

  std::cout << "[elemSize]: " << test_mat1.elemSize() << std::endl;
  // 每个元素的大小(单位字节)  ,每个矩阵元素拥有的值的个数为通道数  2xchannel

  std::cout << "[elemSize1]: " << test_mat1.elemSize1() << std::endl;
  //每个通道的大小(单位字节)  2
}

输出

=====================================================二维矩阵===============================================
[  1,   2,   3,   1,   2,   3;
   1,   2,   3,   1,   2,   3;
   1,   2,   3,   1,   2,   3]
size of uchar is: 1
[step]: 6
[step[0]]: 6
[step[1]]: 3
[step1[0]]: 6
[step1[1]]: 3
[elemSize]: 3
[elemSize1]: 1
=====================================================三维矩阵===============================================
size of cv_16u is: 2
dim is: 3
[step]: 0
[step[0]]: 480
[step[1]]: 60
[step[2]]: 6
[step1[0]]: 240
[step1[1]]: 30
[step1[2]]: 3
[elemSize]: 6
[elemSize1]: 2
<think>嗯,用户问的是在Android中如何将OpenCVMat转换成ByteArray。我需要先回顾一下OpenCV在Android中的使用情况。OpenCVMat对象是用来存储图像数据的,而ByteArray在Android中通常是指byte[]数组,用户可能需要这个数组来进行其他操作,比如传输、保存或者处理。 首先,用户可能已经有一个Mat对象,想要获取其数据。Mat类有一个方法叫做get(),但可能需要正确的参数来获取数据。或者,可能需要将Mat转换为Bitmap,然后再转换为ByteArray。不过用户明确提到使用OpenCVMat转ByteArray,可能更希望直接转换,而不是通过Bitmap中间步骤。 接下来,我需要确认Mat的数据类型和通道数。例如,如果是8UC4(RGBA)、8UC3(RGB或BGR),或者8UC1(灰度)。不同的格式会影响如何提取数据。Mat对象有一个put()方法,可以将数据存入ByteBuffer或者byte数组。或者,使用Mat的dataAddr()获取数据指针,然后将数据复制到byte数组中。 另外,要注意Mat的连续性。如果Mat是连续的,那么数据在内存中是连续存储的,可以直接复制;否则可能需要先调整。可以使用Mat.isContinuous()检查,或者用Mat.reshape()来确保连续性。 然后,考虑如何将Mat的数据复制到byte数组中。在Java中,可能需要使用ByteBuffer,通过Mat.get()方法将数据放入ByteBuffer,然后转换为byte数组。或者直接使用Mat.toArray()方法?不过OpenCVMat在Java中的API可能没有toArray(),可能需要其他方式。 例如,如果Mat是8UC1,每个像素一个字节,那么数据的大小就是rows*cols*1。对于8UC3,则是rows*cols*3。因此,创建一个byte数组,大小为total() * elemSize(),然后使用get方法将数据存入数组。 还需要处理可能的异常情况,比如Mat为空或者数据类型不是8U的类型,这时候需要提醒用户检查输入。 另外,用户可能希望示例代码,这样他们可以直接应用到自己的项目中。需要给出具体的步骤,比如检查Mat类型,创建byte数组,使用get方法填充数组,并处理连续性问题。 可能还需要解释不同颜色空间的转换,比如如果Mat是BGR格式,而用户需要RGB,可能需要额外的处理,但用户的问题主要是转换到ByteArray,而不是颜色空间的转换。 最后,提醒用户注意内存管理和性能问题,比如处理大图像时的内存分配,或者使用更高效的方法如直接访问数据地址,但在Java中可能受限制,可能需要使用JNI或者其他方法。不过大部分情况下,使用Mat.get()方法已经足够。</think>在Android中将OpenCV的`Mat`对象转换为`byte[]`数组需要理解图像数据的内存布局,以下是详细实现方法和注意事项: --- ### 一、核心转换原理 1. **数据结构对应关系** $$ \text{Mat} \rightarrow \text{连续内存块} \rightarrow \text{byte[]} $$ - `Mat`对象包含维度、类型和数据指针 - `byte[]`需要与Mat的像素数据完全对应 2. **关键参数计算** - 总字节数 = 行数 $\times$ 每行字节数(stride) $$ \text{byteSize} = \text{mat.rows()} \times \text{mat.cols()} \times \text{CvType通道数转换系数} $$ --- ### 二、标准转换代码 ```java // 检查Mat有效性 if (mat.empty()) { throw new IllegalArgumentException("输入Mat为空"); } // 创建目标数组 int byteCount = mat.rows() * mat.cols() * mat.channels(); byte[] byteArray = new byte[byteCount]; // 执行数据拷贝(自动处理连续性问题) mat.get(0, 0, byteArray); ``` --- ### 三、不同数据类型的处理 | Mat类型 | 说明 | 转换注意事项 | |-------------------|---------------------|----------------------------| | `CV_8UC1` (灰度图) | 1通道,每像素1字节 | 直接对应byte数组 | | `CV_8UC3` (BGR) | 3通道,每像素3字节 | 注意颜色顺序(BGR而非RGB) | | `CV_8UC4` (BGRA) | 4通道带透明度 | 需要处理alpha通道 | | `CV_32F`等浮点类型 | 需特殊处理 | 需要类型转换(可能丢失精度) | --- ### 四、性能优化技巧 1. **内存连续性检测** ```java if (!mat.isContinuous()) { mat = mat.clone(); // 保证内存连续 } ``` 2. **直接访问数据地址(Native层优化)** ```java ByteBuffer buffer = mat.getNativeObjAddr(); byte[] byteArray = new byte[buffer.remaining()]; buffer.get(byteArray); ``` 3. **使用预分配内存池** ```java // 复用byte数组 private byte[] mReusableBuffer; if (mReusableBuffer == null || mReusableBuffer.length != byteCount) { mReusableBuffer = new byte[byteCount]; } mat.get(0, 0, mReusableBuffer); ``` --- ### 五、典型问题排查 1. **数组大小不符** - 现象:`ArrayIndexOutOfBoundsException` - 检查:`mat.channels()`是否匹配实际数据通道数 2. **颜色异常** - 现象:图像显示颜色错误 - 检查:是否混淆BGR与RGB顺序,使用`Imgproc.cvtColor()`转换颜色空间 3. **数据错位** - 现象:图像出现斜纹或错位 - 处理:验证`mat.step1()`与数组索引计算是否一致 --- ### 六、应用场景示例 ```java // 人脸检测后获取ROI区域数据 Mat faceROI = new Mat(frame, rect); byte[] faceData = new byte[faceROI.total() * faceROI.channels()]; faceROI.get(0, 0, faceData); // 传输到网络服务 networkService.sendImageData(faceData, faceROI.cols(), faceROI.rows(), faceROI.type()); ``` --- ### 七、进阶扩展 1. **与Bitmap互转** ```java // Mat转Bitmap Bitmap bmp = Bitmap.createBitmap(mat.cols(), mat.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(mat, bmp); // Bitmap转byte[] ByteBuffer buffer = ByteBuffer.allocate(bmp.getByteCount()); bmp.copyPixelsToBuffer(buffer); byte[] bitmapBytes = buffer.array(); ``` 2. **JNI层直接访问**(性能关键场景) ```cpp // native方法声明 JNIEXPORT jbyteArray JNICALL Java_com_example_MatConverter_convertMatToByteArray( JNIEnv *env, jobject thiz, jlong matAddr) { Mat* mat = (Mat*) matAddr; jbyteArray result = env->NewByteArray(mat->total() * mat->elemSize()); env->SetByteArrayRegion(result, 0, mat->total() * mat->elemSize(), (jbyte*) mat->data); return result; } ``` --- **注意事项**: - 对于YUV420等特殊格式,需要手动处理平面数据排列 - 大尺寸图像转换时注意OOM问题,建议分块处理 - 推荐使用OpenCV 4.x+的改进API `Mat.copyTo()`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值