- cv Mat类型的使用
下载安装好opencv,使用时只需添加如下头文件:
#include<opencv2/opencv.hpp>
另外在工程的属性页中要添加opencv的包含目录与库目录:
在写稍大一些工程的时候,尽量不要直接用using namespace cv; 因为在实践的过程中,发现会出现一些报错,例如错误“ACCESS_MASK”不明确,就是因为opencv的using namespace cv和windows.h中ACCESS_MASK定义冲突,解决的办法就是把using namespace cv注掉, 在使用的时候加上cv::,当然也有其他的方法可以解决,也就是去处理windows.h头文件,具体可以看博客:
https://blog.csdn.net/jacke121/article/details/110121394?spm=1001.2014.3001.5506
2. cv Mat类型的声明
利用矩阵尺寸和类型参数构造Mat:
cv::Mat Matname( int rows, int cols, int type);
cv::Mat pred_uvd_jts_29(29, 3, CV_32FC1);//声明一个29行3列数据类型为32位float单通道的矩阵
其中,float: 4字节,6-7位有效数字 -3.4E-38 到 3.4E38
double: 8字节,15~16位有效数字 -1.7E-308 到 1.7E308
Float 32bits:CvMat数据结构参数:CV_32FC1,CV_32FC2,CV_32FC3,CV_32FC4
Double 64bits:CvMat数据结构参数:CV_64FC1,CV_64FC2,CV_64FC3,CV_64FC4,
要注意,cv Mat类型是有double类型的,参数为CV_64F,注意使用时,深度学习模型的推理结果类型为torch.float32,所以声明的Mat也要为CV_32F,否则类型不对应会导致结果不正确,相差离谱。
声明全0矩阵(cv::Mat::zeros):
cv::Mat pred_xyz_jts_29 = cv::Mat::zeros(29, 3, CV_32FC1);
声明全1矩阵:
cv::Mat mm = cv::Mat::ones(2, 2, CV_8UC3);
cv Mat给其中整行或整列赋值(使用copyTo函数):
把c的第一列赋值给a的第一列:
Mat c = Mat::zeros(3, 5, CV_32F);
Mat a = Mat::ones(3, 6, CV_32F);
c.col(0).copyTo(a.col(0));
//将c的1-5列赋值给a
c.copyTo(a.colRange(1, 6));
//pred_uvd_jts_29的第3列赋值给pred_xyz_jts_29的第3列
pred_uvd_jts_29.col(2).copyTo(pred_xyz_jts_29.col(2));
colRange(start,end),包括左边界,不包括右边界,对rowRange同样适用。
3. cv Mat类型的取值方法
cv Mat类型是支持直接输出的:
//可以直接输出pred_uvd_jts_29矩阵的值
std::cout << "pred_uvd_jts_29 : " << pred_uvd_jts_29 << std::endl;
其他三种取值的方法:
Matname.at<float>(i, j);
Matname.ptr<float>(i)[j];
Matname.data;
cv::Mat camera_root(1, 3, CV_32FC1);
//代码实例
for(int i = 0; i < 29; i++)
{
pred_xy_jts_29_meter.at<float>(i,0) = ((pred_uvd_jts_29.at<float>(i,0) + cx) * input_size / focal_length) * (pred_xyz_jts_29.at<float>(i, 2) * \
depth_factor + pred_camDepth.at<float>(0, 0));
pred_xy_jts_29_meter.at<float>(i,1) = ((pred_uvd_jts_29.at<float>(i,1) + cy) * input_size / focal_length) * (pred_xyz_jts_29.ptr<float>(i)[2] * \
depth_factor + pred_camDepth.ptr<float>(0)[0]);
pred_xyz_jts_29.at<float>(i,0) = pred_xy_jts_29_meter.at<float>(i,0) / depth_factor;
pred_xyz_jts_29.at<float>(i,1) = pred_xy_jts_29_meter.at<float>(i,1) / depth_factor;
}
//camera_root为1行3列,给它的一行三个数赋值
camera_root.at<float>(0,0) = pred_xyz_jts_29.at<float>(0, 0) * depth_factor;
camera_root.at<float>(0,1) = pred_xyz_jts_29.at<float>(0, 1) * depth_factor;
camera_root.at<float>(0,2) = pred_xyz_jts_29.at<float>(0, 2) * depth_factor;
camera_root.at<float>(0,2) += pred_camDepth.at<float>(0, 0);
另外,几种取值的方法在debug和release模式下的时间不同,Debug模式下M.at(i, j)最耗时,用指针取值最快,在Release模式下,三者耗时相差不大,结果如下,感兴趣的小伙伴可以看看:穿越门
这里指针取值就是有一个Mat矩阵,根据Mat矩阵的属性行、列及通道数,将Mat拉成一行进行取值,具体代码如下:
cv::Mat feature = this->output[nodes_name[i]];//feature即得到onnx对应节点的输出
if (nodes_name[i] == "pred_xyz_jts_29")
{
auto* pred_xyz_jts_29 = feature.ptr<float>();//定义一个auto指针类型
//根据Mat的rows、cols及channels属性确定元素个数
size_t pred_xyz_jts_29_out_length = feature.rows * feature.cols * feature.channels();
for (int j = 0; j < pred_xyz_jts_29_out_length; j++)//遍历即可
{
std::cout << "pred_xyz_jts_29 " << j << "," << pred_xyz_jts_29[j] << std::endl;
}
}
- cv Mat类型做归一化操作(减均值、除方差)
我的预处理是先做了一个to_tensor操作,然后减mean,除std
img = im_to_torch(img)
# mean
img[0].add_(-0.406)
img[1].add_(-0.457)
img[2].add_(-0.480)
# std
img[0].div_(0.225)
img[1].div_(0.224)
img[2].div_(0.229)
首先数学上这两个操作可以合并,最后只减一次均值和方差即可,计算合并后:
cv::Mat input_f;
input.convertTo(input_f, CV_32FC3);//input是处理完的U8C3类型要先convertTo转换成浮点型
input_f = input_f - cv::Scalar(103.53, 116.535, 122.4);//用scalar,对应通道减去计算出的均值
vector<float> v_std = {57.375, 57.12, 58.395};
std::vector<cv::Mat> planes(3);//planes是个长度为3的vector,里面每个元素类型都是cv Mat
cv::split(input_f, planes);//三通道的input_f按通道分割,结果存放在事先声明的planes中
cv::Mat planer;
for (int i = 0; i < 3; i++)
{
cv::Mat f;
planes[i].convertTo(f, CV_32FC1, 1.0 / v_std[i] );//除方差
planer.push_back(f.reshape(1,1));//f.reshape(1,1)将矩阵转换成一个列向量,维度(row*col, 1)
}//planer.size():[65536*3]
注意:Mat.convertTo()方法在转换类型的同时可以做一些转换操作,原型是:
src.convertTo(dst, type, scale, shift)
scale是要乘上的比例因子,shift是要加上的类似偏置, 如果scale=1,shift=0,则不进行比例缩放:
dst(i)=src(i) x scale + shift
所以除255的操作可以写成:
image.convertTo(src_f, CV_32F, 1.0/255, 0);//图像大小没有变化,类型从UINT8变为了FLOAT32位
cv::split和cv::merge的原型:
void cv::split(
const cv::Mat& mtx, //输入图像
vector<Mat>& mv // 输出的多通道序列(n个单通道序列)
);
void merge(
const vector<cv::Mat>& mv, // 输入的多通道序列(n个单通道序列)
cv::OutputArray dst // 输出图像,包含mv
);
opencv相关函数可以学习博客:https://www.cnblogs.com/yanghailin/p/12067239.html#3-c-opencv%E7%9B%B4%E6%8E%A5%E5%87%8F%E5%9D%87%E5%80%BC–%E9%99%A4%E6%96%B9%E5%B7%AE
opencv的总结就到这里了,知道的越多,自己不知道的越多,书到用时方恨少,希望自己能不断积累,早日从菜鸡像不那么菜的菜鸡进化,加油喔~~~~~