1 row、col 和 x、y 的关系
- rows:表示图像有多少行
- cols: 表示图像有多少列
- heigh: 高度
- width: 宽度
关系:
rows == heigh == Point.y
cols == width == Point.x
Mat::at(Point(x, y)) == Mat::at(y,x)
2 创建Mat图像
//创建Mat图像(像素值自定义)
Mat MM(5, 5, CV_8UC1, Scalar(128,0,0));// 参数(int rows, int cols, int type, const Scalar& s)
cout << "MM = " << endl << " " << MM << endl;
//创建Mat图像(像素值205)
Mat MC;
MC.create(5, 5, CV_8UC1);
cout << "MC = "<< endl << " " << MC << endl;
//创建Mat图像(像素值单位矩阵)
Mat E = Mat::eye(5, 5, CV_8UC1);
cout << "E = " << endl << " " << E << endl;
//创建Mat图像(像素值全1矩阵)
Mat O = Mat::ones(5, 5, CV_32FC1);
cout << "O = " << endl << " " << O << endl;
//创建Mat图像(像素值全0矩阵)
Mat Z = Mat::zeros(5, 5, CV_64FC1);
cout << "Z = " << endl << " " << Z << endl;
3 读写Mat数据
cv::Mat mat = ...;
mat.at<cv::Vec3b>(0, 3)[3] = 0;
std::cout << mat.rows << std::endl;//行数
std::cout << mat.cols << std::endl;//列数
std::cout << mat.channals() << std::endl;//通道数
4 遍历Mat数据
at 的方式:
int i;
for (int m = 0; m < mat.cols; m++) {
for (int n = 0; n < mat.rows; n++) {
for (int c = 0; c < mat.channels(); c++) {
//Vec3b:表示mat的向量格式,也就是读写的数据格式,这里表示一个像素有3个byte;
i = mat_RGB.at<cv::Vec3b>(n, m)[c];
}
// 若为单通道
// i = mat_RGB.at<cv::Vec3b>(n, m);
}
}
ptr 的方式(效率更高):
for (int i =0; i < img.rows; ++i) {
const int * ptr = img.ptr<int>(i);
for (int j = 0; j < img.cols * img.channels(); ++j) {
int data = ptr[j];
}
}
5 图片 0-1 和 0-255 的转换
// 0~255 -> 0~1
img.convertTo(img, CV_32F, 1.0/255);
// 0~1 -> 0~255
color.convertTo(color, CV_8U, 255);
6 改变 Mat 尺寸
// img1是输入图像,img2是输出图像,后面那个是尺寸
cv::resize(img1, imgc2, cv::Size(500,311));
7 读取 Mat 类型
myMat = imread("C:\someimage.jpg");
int type = myMat.type();
返回的类型是个数字,数字的含义可以在下面表格中查找:
- | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 |
---|---|---|---|---|---|---|---|---|
CV_8U | 0 | 8 | 16 | 24 | 32 | 40 | 48 | 56 |
CV_8S | 1 | 9 | 17 | 25 | 33 | 41 | 49 | 57 |
CV_16U | 2 | 10 | 18 | 26 | 34 | 42 | 50 | 58 |
CV_16S | 3 | 11 | 19 | 27 | 35 | 43 | 51 | 59 |
CV_32S | 4 | 12 | 20 | 28 | 36 | 44 | 52 | 60 |
CV_32F | 5 | 13 | 21 | 29 | 37 | 45 | 53 | 61 |
CV_64F | 6 | 14 | 22 | 30 | 38 | 46 | 54 | 62 |
C1~C8是通道数,CV_8U ~ CV_64F是图像的数据格式;
8 色彩空间的转换
//将RGB格式的图像转换成BGR格式
cvtColor(RGB_image, BGR_image, COLOR_RGB2BGR);
//将BGR格式的图像转换成灰度图
cvtColor(BGR_imgage, gray_image, COLOR_BGR2GRAY);
//将BGR格式的图像转换为BGRA
cvtColor(BGR_imgage, gray_image, COLOR_BGR2BGRA);
- HSV六棱锥
H参数表示色彩信息,即所处的光谱颜色的位置,用一角度量来表示;红绿蓝间隔隔120度,互补色分别相差180度;
S纯度为一比例值,范围从0到1,它表示成所选颜色的纯度和该颜色最大的纯度之间的比率,S=0时,只有灰度;
V表示色彩的明亮程度,范围从0到1;有一点要注意:它和光强度之间并没有直接的联系。
![pic](/pic/020.png)
![pic](/pic/019.png)
![pic](/pic/018.png)
-
I420(YUV420P)
相比于 RGB 格式,I420 所需要的物理内存更小,当然也有一定精度的损失。I420 格式的内存分布如下,y1 - y16 是图像的尺寸(w * h),也就是一副黑白图,u1 - u4 可以计算蓝色像素的分量,v1 - v4 可以计算红色像素的分量,YUV 和 RGB 的转换公式:R = Y + 1.403V G = Y - 0.344U - 0.714V B = Y + 1.770U
y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11 y12 y13 y14 y15 y16 u1 u2 u3 u4 v1 v2 v3 v4 可能会有一个疑问,为什么 U、V 的数量都是 Y 的 1 4 \frac{1}{4} 41 ?
我们首先将 Y 的尺寸 (w * h) 全部划分为 (2*2) 小矩形,每一个小矩形(4像素)分别对应了一个 u、v,所以是 4 被倍关系。从内存分布上看,RGB 占用了 (w * h * 3) 的空间,而 YUV 占用了 (w * h * 1.5) 的空间,缩减了一半。注意:转换时需要划分为小矩形,所以图片的尺寸要求一定是偶数。
-
420SP
420SP 只是在 I420 的基础上改变了 U、V 的分布:y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 y11 y12 y13 y14 y15 y16 u1 v1 u2 v2 u3 v3 u4 v4
9 数据格式的转换
//将[0,1]范围内的浮点表示的图像转换成8bit整数表示的图像
float_image.convertTo(integer_image, CV_8U, 255.0);
convertTo函数定义如下:
/** @brief Converts an array to another data type with optional scaling.
The method converts source pixel values to the target data type. saturate_cast\<\> is applied at
the end to avoid possible overflows:
\f[m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) + \beta )\f]
@param m output matrix; if it does not have a proper size or type before the operation, it is
reallocated.
@param rtype desired output matrix type or, rather, the depth since the number of channels are the
same as the input has; if rtype is negative, the output matrix will have the same type as the input.
@param alpha optional scale factor.
@param beta optional delta added to the scaled values.
*/
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
10 单通道转换为多通道
// 把传入的单通道灰度图转换为3通道RGB
cv::Mat convertTo3Channels(const cv::Mat& binImg)
{
cv::Mat three_channel = cv::Mat::zeros(binImg.rows,binImg.cols,CV_8UC3);
std::vector<cv::Mat> channels;
for (int i=0;i<3;i++)
{
channels.push_back(binImg);
}
merge(channels,three_channel);
return three_channel;
}
11 从多通道中分离单通道
cv::Mat img = ...;
std::vector<cv::Mat> channals[0]);
cv::split(img, channals);
cv::imshow("通道0", channals[0]);
cv::imshow("通道1", channals[1]);
cv::imshow("通道2", channals[2]);
12 Mat、QImage、QPixmap 之间的转换
:::details 头文件
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <imgproc/types_c.h>
#include <QtGui/QImage>
#include <QtWidgets/QFileDialog>
cv::Mat QImage2cvMat(QImage image);
cv::Mat QPixmapcvMat(QPixmap image);
QImage cvMat2QImage(const cv::Mat& mat);
QPixmap cvMat2QPixmap(const cv::Mat& mat);
QImage QPixmapQImage(QPixmap image);
QPixmap QImageQPixmap(QImage image);
:::
:::details c文件
#include "ImaRormConv.h"
cv::Mat QImage2cvMat(QImage image)
{
cv::Mat mat;
switch(image.format())
{
case QImage::Format_ARGB32:
case QImage::Format_RGB32:
case QImage::Format_ARGB32_Premultiplied:
mat = cv::Mat(image.height(), image.width(), CV_8UC4, (void*)image.constBits(), image.bytesPerLine());
break;
case QImage::Format_RGB888:
mat = cv::Mat(image.height(), image.width(), CV_8UC3, (void*)image.constBits(), image.bytesPerLine());
cv::cvtColor(mat, mat, CV_BGR2RGB);
break;
case QImage::Format_Indexed8:
mat = cv::Mat(image.height(), image.width(), CV_8UC1, (void*)image.constBits(), image.bytesPerLine());
break;
}
return mat;
}
cv::Mat QPixmapcvMat(QPixmap image)
{
QImage img = image.toImage();
cv::Mat mat = QImage2cvMat(img);
return mat;
}
QImage cvMat2QImage(const cv::Mat& mat)
{
// 8-bits unsigned, NO. OF CHANNELS = 1
if(mat.type() == CV_8UC1)
{
QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
// Set the color table (used to translate colour indexes to qRgb values)
image.setColorCount(256);
for(int i = 0; i < 256; i++)
{
image.setColor(i, qRgb(i, i, i));
}
// Copy input Mat
uchar *pSrc = mat.data;
for(int row = 0; row < mat.rows; row ++)
{
uchar *pDest = image.scanLine(row);
memcpy(pDest, pSrc, mat.cols);
pSrc += mat.step;
}
return image;
}
// 8-bits unsigned, NO. OF CHANNELS = 3
else if(mat.type() == CV_8UC3)
{
// Copy input Mat
const uchar *pSrc = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
return image.rgbSwapped();
}
else if(mat.type() == CV_8UC4)
{
// Copy input Mat
const uchar *pSrc = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
return image.copy();
}
else
{
return QImage();
}
}
QPixmap cvMat2QPixmap(const cv::Mat& mat)
{
QImage img = cvMat2QImage(mat);
QPixmap pix = QPixmap::fromImage(img);
return pix;
}
QImage QPixmapQImage(QPixmap image)
{
QImage img = image.toImage();
return img;
}
QPixmap QImageQPixmap(QImage image)
{
QPixmap pix = QPixmap::fromImage(image);
return pix;
}
:::
13 Mat 和 Vector 互换
/***************** Mat转vector **********************/
template<typename _Tp>
vector<_Tp> convertMat2Vector(const Mat &mat)
{
return (vector<_Tp>)(mat.reshape(1, 1));//通道数不变,按行转为一行
}
/****************** vector转Mat *********************/
template<typename _Tp>
cv::Mat convertVector2Mat(vector<_Tp> v, int channels, int rows)
{
cv::Mat mat = cv::Mat(v);//将vector变成单列的mat
cv::Mat dest = mat.reshape(channels, rows).clone();//channels是通道数,rows是行数,这里的.clone是防止下面的return出错
return dest;
}