opencv_usage

保存图片

1
2
3
4
5
cv::Mat m(1080, 1920, CV_8UC3);
unsigned char* m_ptr = m.ptr<uchar>(0);
static int fgq_n = 0;
memcpy((void*)m_ptr, image_blob->cpu_data(), 1080 * 1920 * 3);
cv::imwrite("/home/caros/work/tmp/" + std::to_string(fgq_n++) + ".png", m);

cv::Mat

基础概念

OpenCV:Mat中的step、elemSize、channel和任意内存访问

判断是否连续

isContinuous() 方法可以判断一个 cv::Mat 对象是否在内存中是连续的。

如果是连续的返回 true,如果在每一行的结尾跳过一部分内存地址到达下一行,那么就会返回 false。

所以很显然,1x1 和 1xN 的对象一定是连续的,因为只有一行数据。

使用 cv::Mat::create 创建的对象也是连续的,表示直接开辟了一个连续的内存空间进行对象的创建。

但是,如果从一个 cv::Mat 对象中截取了一部分数据,或者构造数据来自外部存储的数据,那么就不一定是连续的了。

1
2
3
4
5
6
7
8
cv::Mat newM = cv::Mat::zeros(20, 20, CV_32FC1);

cout << newM.isContinuous() << endl;

cv::Mat segM = newM.colRange(10, 15);

cout << segM.isContinuous() << endl;

非连续Mat 如何拷贝

非连续是指行之间的地址是间断的,但是同一行的内存是连续,所以可以按行复制

下面是把非连续的 Mat 复制给vector.data(), 把非连续变成了连续,如果不关注连续还是非连续,可以直接 a_mat.copyTo(b_mat)

1
2
3
4
5
6
7
8
9
10
11
// apollo
void BEVObstacleDetector::Mat2Vec(const cv::Mat &im, float *data) {
ACHECK(nullptr != data);
int rh = im.rows;
int rw = im.cols;
int rc = im.channels();

for (int i = 0; i < rc; ++i) {
cv::extractChannel(im, cv::Mat(rh, rw, CV_32FC1, data + i * rh * rw), i);
}
}

深拷贝与浅拷贝

1
2
3
4
5
6
7
// 申请一块大内存
Mat buffer(1024*1024*256, 5, CV_32F);

// useful 是将要实际使用的行数
// rowRange 只是创建了个 mat 的 head,不会申请新的
handle = buffer.rowRange(0, useful)
// handle 相当于是 0 - useful 行的浅拷贝

cv::cuda::GpuMat

cuda kernel 访问 GpuMat

GpuMat的数据在gpu 上,那么如何在 cuda kernel 中访问 GpuMat 呢?可以直接访问吗,答案是不可以,因为 cuda kernel 不可以传入不带__device__修饰的构造和析构的对象,就是说GpuMat 的构造和析构没有用__device__修饰,所以无法直接传递给kernel

opencv 提供了一个 PtrStepSz 类,其构造和析构函数是用__device__修饰的,所以可以传递给kernel。

另外一个问题是 GpuMat 是如何转化为 PtrStepSz 的,在GpuMat 的成员函数实现了GpuMat 到 PtrStepSz 的类型转换,因此在调用kernel 的时候直接传递GpuMat,而在kernel 的形参中用 PtrStepSz 进行接收

详细说明: https://stackoverflow.com/questions/73171101/how-does-cvcudagpumat-turn-into-cvcudaptrstepsz-when-passed-to-a-kernel


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!