一.基本操作
1.1 创建
//创建且赋初值
cv::Mat matDes(nHEIGHT,nWID,CV_8UC1, cv::Scalar(0));//创建 row(高),col(宽)
cv::Mat matDes = cv::Mat::zeros(nHEIGHT,nWID,CV_8UC1);//ones matlab方法
//矩阵间初值
matDes=cv::Mat(nImgH,nImgW,CV_8UC1,cv::Scalar(255));
//创建矩阵(不赋初值)
m_Mat.create(nRow, 1000, m_MatImg.type());//, cv::Scalar(0)
//数组方式
int sz[2] = { 3, 3 };
cv::Mat M2(2, sz, CV_8UC1, cv::Scalar::all(0));//2维
Mat灰度乘以系数(增强)
mat*=n;//方式一
img.convertTo(img, CV_32F, 0.5);//方式二
cv::Mat scalar = cv::Mat::ones(img.size(), CV_32F) * 0.5;//方式三
cv::multiply(img, scalar, img);
1.2 Mat读写
读
cv::imread(sPath,cv::IMREAD_GRAYSCALE|cv::IMREAD_ANYDEPTH);读灰度图//
cv::imread(sPath);读三通道图//
保存
std::vector <int> compression_params;
compression_params.push_back(259);
if(0 == m_nImgType){
compression_params.push_back(1);//1-无压缩
}
else if(1 == m_nImgType){
compression_params.push_back(5);//5-COMPRESSION_LZW
}
cv::imwrite(sPath.toStdString(),mat,compression_params);//默认LZW压缩替换现有文件,.toLocal8Bit().data()可支持中文路径
1.3 遍历Mat
OpenCV Mat遍历的方法
a.通过Mat的ptr指针的[]操作符号,按照数组的方式遍历—>效率最高。
b. 通过迭代器 cv::MatIterator_进行访问—>效率次之,但是是安全的。
c. 不通过ptr指针,直接通过Mat(.at)进行访问—>效率最低, 不适合遍历操作,常用于随机位置的访问。
一般图像行与行之间往往存储是不连续的(ROI裁剪的Mat),但是有些图像可以是连续的(Mat的clone)
裁剪Mat(不连续)存到连续uchar数组/void*连续内存
1.rect裁剪后先clone,再memcpy(成功)或Mat::data.reshape(0,1)(失败)给data赋值。
猜测Mat(rect)后为const,不支持直接memcpy(条纹)、reshape(致崩)、Mat::data赋值给数组(条纹)等操作,需先clone,或直接用为其它Mat的Data。
2.指针逐个遍历,直接Data不行(猜测new后转为数组),用局部变量指针da或Data[]可以。
```cpp
unsigned char* data = new unsigned char[nByte]
unsigned char* da = data;
uchar* ptr = matFile.ptr<uchar>(h);
*da++ = ptr[w];
//data[w+h*nCols] = ptr[w];
a.Mat的ptr指针(先取行,再取列)
int nRows = matSrc.rows,nCols = matSrc.cols;
if(matSrc.isContinuous())
{
nCols *= nRows;
nRows = 1;
}
int nMatType = matSrc.type();
if(0 == nMatType){//CV_8UC1
uchar* pDataSrc = NULL;
for (int i = 0; i< nRows; i++)
{
pDataSrc = matSrc.ptr<uchar>(i);
if(pDataSrc ==NULL) continue;
for (int j = 0; j < nCols; j++)
{
pDataSrc[j]=cv::saturate_cast<uchar>(m_nMax-pDataSrc[j]);//
//auto c = matSrc.data[i*nCols+j];
//matSrc.data[i*nCols+j]=255;
}
}
}
else if(2 == nMatType){//CV_16UC1
ushort* pDataSrc = NULL;
for (int i = 0; i< nRows; i++)
{
pDataSrc = matSrc.ptr<ushort>(i);
if(pDataSrc ==NULL) continue;
for (int j = 0; j < nCols; j++)
{
pDataSrc[j]=cv::saturate_cast<ushort>(m_nMax-pDataSrc[j]);//
}
}
}
c.逐个点(读Mat值用float时,值异常偏大),暂一般用double
X.at<double>(i, j) = X.at<double>(i, j);//uchar,ushort,
mat.at<Vec3b>(row, col)[0] = 255; //修改点(row, col)的B通道数据(Vec3b相当于vector<uchar,3>)
mat.at<Vec3b>(row, col); //三通道,返回 <Vec3b>,即返回一个 uchar 数组,长度为 3
mat.at<Vec3b>(row, col)[1] = mat.at<uchar>(row, col)*k;//变小变形
1.4 Mat的部分赋值剪贴
cv::Mat DesROIMat =matDes(cv::Rect(nX2,nY2,nWid,nHeight)); //范围时 宽,高
matSrc.copyTo(DesROIMat);//深复制? 位数不一致会导致copy失败
1.5 判断Mat有效性/是否加载成功
matFile.empty()
nullptr==matFile.data
1.6 16位Mat转8位
void oeThread_LS::Mat16ToMat8(cv::Mat &mat16,cv::Mat &mat8)
{
Mat tmp;
mat8 = Mat::zeros(mat16.size(), CV_8U);
normalize(mat16, tmp, 0, 255, NORM_MINMAX);
convertScaleAbs(tmp, mat8);
}
1.7 Mat旋转90度-逆时针
//顺时针90度+沿y轴翻转
transpose(matFileCut, matCopy);//顺时针90度+沿y轴翻转
//flip(matCopy, matCopy, 0);//逆时针90度
//flip(matCopy, matCopy, 1);//顺时针90度
//flip(matFile, matCopy,-1);//顺时针90度+沿y轴翻转 / 逆时针90度+沿x轴翻转
//沿y轴翻转
transpose(matFileCut, matCopy);//顺时针90度+沿y轴翻转
flip(matCopy, matCopy, 1);//顺时针90度
1.8 Mat的缩放
int nRow = matSrc.rows*fScale;
int nCol = matSrc.cols*fScale;
cv::Mat matDes(nRow,nCol,CV_8UC3,cv::Scalar(0));//先分配内存,否则无效Mat致崩
cv::resize(matSrc,matDes,matDes.size());
1.9
二.常见问题记录
2.1 Mat致崩
如cv::resize范围不合理致崩,但不明确定位报错。
2.2 cv::imread读取异常
A.Debug下无带d的cv库。
B.读、写16位tif得到图片全白(读取图片类型、默认灰度值,文件后缀保存时一致)。
C.|与||,按位或与逻辑或,结果分别为二进制数和bool,此处用前者|。
D.读取中文路径,sPath.toLocal8Bit().toStdString(),cv::imwrite类似。
cv::Mat matSrc=cv::imread(sJpgPath.toStdString(),cv::IMREAD_GRAYSCALE|cv::IMREAD_ANYDEPTH);
//0 pow(256,matSrc.type())-1
cv::Mat matDes(nHEIGHT,nWID,matSrc.type(),cv::Scalar(0));//CV_8UC1创建 0黑255白 row,col
cv::Mat DesROIMat =matDes(cv::Rect(nX2,nY2,nWid,nHeight));//越界致崩
matSrc.copyTo(DesROIMat);//深复制? Mat的部分赋值(剪贴)
QString sDesPath = m_sPathOut + "/"+fileInfo.baseName()+'.'+fileInfo.suffix();
cv::imwrite(sDesPath.toStdString(),matDes);//无.jpg后缀会致崩
2.3 CV_32FC类型mat多线程中cv::imread/imshow异常
致崩ucrtbase.dll报错
opencv笔记(三十一)——Mat 矩阵数据类型转换convertTo
dstmat.convertTo(aa, CV_8U, 255);//测得仍然会致崩
信号槽参数(cv::Rect)时ucrtbase.dll致崩。
三.加载BigTif
修改opencv位限制长度。
BigTif右键不能看到具体属性信息,也看不到缩略图。
Debug BigTif8可打开,BigTif16可打开
Debug | Release |
---|---|
BigTif8可打开 | BigTif8可打开 |
BigTif16可打开 | BigTif16不可打开,借助相关库 |
四.Mat传参
4.1 Qt信号槽需先注册Mat
#include <QMetaType>
qRegisterMetaType< cv::Mat >("cv::Mat&");//()内cv::Mat&或cv::Mat皆可
传cv::Mat&正常。
传cv::Mat异常
void RunProcess::Rotate(cv::Mat& ImageSrc)
{
cv::imwrite(QString("D:/1.tif").toStdString(),ImageSrc);
transpose(ImageSrc,ImageSrc);//顺时针90度+沿y轴翻转
flip(ImageSrc,ImageSrc,0);//逆时针90度
flip(ImageSrc,ImageSrc,1);//顺时针90度
cv::imwrite(QString("D:/2.tif").toStdString(),ImageSrc);
}
4.2 传入Mat修改
传cv::Mat&/cv::Mat失败,return cv::Mat成功。
猜测
1.形参image为临时副本,只读不修改。
2.warpAffine造成。
cv::Mat RunProcess::Rotate(cv::Mat image, int degree)
{
cv::Mat M,dst= image.clone();//猜测该函数结束后会释放
int h = image.rows;
int w = image.cols;
M = getRotationMatrix2D(cv::Point2f(w / 2, h / 2), degree, 1.0);//定义变换矩阵M 45
double cos = abs(M.at<double>(0, 0)); //求cos值
double sin = abs(M.at<double>(0, 1)); //求sin值
int nw = cos * w + sin * h; //计算新的长、宽
int nh = sin * w + cos * h;
M.at<double>(0, 2) += (nw / 2 - w / 2); //计算新的中心
M.at<double>(1, 2) += (nh / 2 - h / 2);
warpAffine(image, dst,M,cv::Size(nw, nh), cv::INTER_LINEAR, 0, cv::Scalar(255));//,255,255
image = dst.clone();
return dst;
}
//传cv::Mat &image 成功
void RunProcess::Rotate3(cv::Mat &image, int degree)
{
cv::Mat M;//dst,
int h = image.rows;
int w = image.cols;
M = getRotationMatrix2D(cv::Point2f(w / 2, h / 2), degree, 1.0);//定义变换矩阵M 45
double cos = abs(M.at<double>(0, 0)); //求cos值
double sin = abs(M.at<double>(0, 1)); //求sin值
int nw = cos * w + sin * h; //计算新的长、宽
int nh = sin * w + cos * h;
M.at<double>(0, 2) += (nw / 2 - w / 2); //计算新的中心
M.at<double>(1, 2) += (nh / 2 - h / 2);
warpAffine(image, image, M, cv::Size(nw, nh), cv::INTER_LINEAR, 0, cv::Scalar(0));//,255,255
}