前言
首先必须明白Mat类是什么,我看了几天的云里雾里的博客后想明白了,基础数据结构或类必须弄明白。之前学python就是因为没有数据类型才弄不明白,全是调用接口、调用接口等。
Mat类是保存矩阵数据的数据结构,当然也可以用来保存图片、视频这种图像。Mat类的初始化很多种方法,之后专门写一篇怎么使用Mat矩阵的学习总结。
显示一张图片的知识夹在上一篇文章的角落里。主要是
Mat src=imread("E:/File/face.jpg");/*我素材都放这里了,之后就不说了*/
学习Opencv时,会经常看见im-开头的API,意思是image-。有些老版本里常量写成CV_LOAD_IMAGE-开头的枚举常量,现在CV_开头的除了本来就很短的之外,都删除了,而像前面这个则改成了IMREAD-开头,所以知道这个规则后,有一些老的demo中未定义的常量可以猜猜。说不定就只差一个CV_就运行成功了呢!
- 以下的显示对输出对视频也一样,其实都是看做一帧帧的图片再进行操作的
图片的载入
Mat imread(const string& filename,int flags=1);
(1) 第一个参数是需要填入的文件名(路径名),支持以下几种类型输入:
- Windows位图:*.bmp,*dib
- JPEG文件: *jpeg,*jpg,*jpe
- JPEG 2000文件:*.jp2
- PNG图片:*.png
- 便携文件格式:*.pbm,pgm,.ppm
- Sun rasters光栅文件:*.sr,*ras
- TIFF文件:.tiff,.tif
(2)第二个参数,可以在imgcodecs.hpp中找到其的枚举。
enum ImreadModes {
IMREAD_UNCHANGED = -1, /*不用*/
IMREAD_GRAYSCALE = 0, /*返回单通道灰度图像*/
IMREAD_COLOR = 1, /*返回3通道彩色图像,如果本来是灰的也没用*/
IMREAD_ANYDEPTH = 2, /*返回8位图像,或者具有相应深度时返回16位、32位图像*/
IMREAD_ANYCOLOR = 4, /*以任何可能的颜色格式读取*/
IMREAD_LOAD_GDAL = 8, /*如果设置,要用gda驱动程序加载图像*/
IMREAD_REDUCED_GRAYSCALE_2 = 16, /*返回大小只有一半的单通道灰度图像*/
IMREAD_REDUCED_COLOR_2 = 17, /*返回大小只有一半的3通道彩色图像*/
IMREAD_REDUCED_GRAYSCALE_4 = 32, /*返回大小只有4分之1的单通道灰度图像*/
IMREAD_REDUCED_COLOR_4 = 33, /*返回大小只有4分之1的3通道彩色图像*/
IMREAD_REDUCED_GRAYSCALE_8 = 64, /*返回大小只有1/8的单通道灰度图像*/
IMREAD_REDUCED_COLOR_8 = 65, /*返回大小只有1/8的3通道彩色图像*/
IMREAD_IGNORE_ORIENTATION = 128 /*如果设置,不要根据EXIF的方向标志旋转图像*/
};
创建窗口
void namedWindow(const string& winname,int flag-WINDOW_AUTOSIZE);
一般显示图片时,这个函数是没用的,imshow()函数本身就会自己创建窗口并显示图像。但有些特殊情况,需要先用这个函数创建个窗口处理,确定窗口大小。
enum WindowFlags {
WINDOW_NORMAL = 0x00000000, /*可以自由改变窗口大小,无比例约束*/
WINDOW_AUTOSIZE = 0x00000001, /*大小由显示的图像限制*/
WINDOW_OPENGL = 0x00001000, /*支持OpenGL的窗口*/
WINDOW_FULLSCREEN = 1, /*全屏*/
WINDOW_FREERATIO = 0x00000100, /**/
WINDOW_KEEPRATIO = 0x00000000, /**/
WINDOW_GUI_EXPANDED=0x00000000, /**/
WINDOW_GUI_NORMAL = 0x00000010, /**/
};
- namedWindow()函数的winname必须与某一要显示的窗口名相同
图片的显示
void imshow(const string& winname,InputArray mat);
imshow()指定在winname窗口中显示图像。如果窗口是WINDOW_AUTOSIZE标志创建的,那么显示原图像大小。否则将图像进行缩放来适应窗口。而imshow()函数缩放图像,取决于图像的深度:
- 如果载入8-bit unsigned,就按原样显示
- 如果载入16-bit unsigned 或32-bit integer,用像素值除以256。也就是说,值范围是[0,255X256]映射到[0,255]
- 如果载入32-bit floating-point,像素值便乘以255,。也就是说,值范围是[0,1]映射到[0,255]
另外,窗口如果设定WINDOW_OPENGL标志,那么imshow还支持ogl::Buffer、ogl::Texture2D作为输入…
输出图片到文件
bool imwrite(const string& filename,InputArray img,const vector<int>& params=vector<int>());
重点是第三个参数,表示为特定格式保存的参数编码。有默认值vector(),如果需要的话,要注意:
- 对于JPEG图片,这个参数表示从0到100的图片质量IMWRITE_JPEG_QUALITY,默认95
- 对于PNG图片,这个参数表示IMWRITE_PNG_COMPRESSION,从0到9。较高的值意味着更小的尺寸和更长的压缩时间,默认值是3
- 对于PPM,PGM,或PBM的图片,这个参数表示一个二进制格式表示IMWRITE_PXM_BINARY,取值0或1,默认1
下面写一个示例:
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
void createAlphaMat(Mat& mat) {
//saturate_cast<uchar>()确保数据不超过[0,255]范围
for (int i = 0; i < mat.rows; ++i) {
for (int j = 0; j < mat.cols; ++j) {
Vec4b& bgra = mat.at<Vec4b>(i, j);
bgra[0] = UCHAR_MAX;
bgra[1] = saturate_cast<uchar>(
((float)(mat.cols - j) / (float)(mat.cols))*UCHAR_MAX
);
bgra[2]= saturate_cast<uchar>(
((float)(mat.rows - i)/ (float)(mat.rows))*UCHAR_MAX
);
bgra[3] = saturate_cast<uchar>(0.5*(bgra[1] + bgra[2]));
}
}
}
int main() {
//创建带Alpha通道的Mat
Mat mat(480, 640, CV_8UC4);//8-bit 4通道,等同于 CV_8UC(4)
createAlphaMat(mat);
vector<int> compression_params;
compression_params.push_back(IMWRITE_PNG_COMPRESSION);
compression_params.push_back(9);
try {
imwrite("E:/File/透明Alpha值图.png", mat, compression_params);
imshow("生成的PNG图", mat);
fprintf(stdout, "PNG图片文件的Alpha数据保存完毕!\n在E:/File目录下查看\n");
waitKey(0);
}
catch (runtime_error& ex) {
fprintf(stderr, "图像转换成PNG格式发生错误:%s\n", ex.what());
system("pause");
}
destroyAllWindows();
}
图片的混合输出
基于公式:w(x)=(1-a)Xf(x)+aXg(x)
void addWeighted(InputArray src1, //要混合的图片1
double alpha, //对比度,src1占比
InputArray src2,//要混合的图片2
double beta,//beta=1-alpha,src2占比
double gamma, //亮度
OutputArray dst, //输出混合后的图片
int dtype = -1);//
addweighted函数使用非常苛刻,如果是想混合两张一样大的图片的话,有时需要用到resize:
resize(src1,src1,Size(320,240));
一般还要在使用前判断图片大小、类型是否一致
if(src1.rows==src2.rows&&src1.cols==src2.cols&&src1.type()==src2.type()){
addWeighted(src1, alpha, src2, 1 - alpha, gamma, dst);
}
但是,如果是需要不同尺寸混合的话,就没这么严格了。原理是,从src1上划定一块区域(假设src1大小大于src2),这块区域(ROI)大小与src2一样,混合时,用src2去替换掉src1上的ROI区域,于是,混合的目的就达到了。
//利用ROI(Region of interest),获取将要混合的矩形大小
Mat img_ROI=src1(Rect(20,40,src2.cols,src2.rows));
//利用addWeighted混合
addWeighted(src2,0.6,img_ROI,0.4,0.,img_ROI);
//输出
imshow("混合后结果",src1);
- 如果要混合的不是完整的src2的话,就可以用一个中间变量代替,选取src2中间的区域,再进行混合操作
最后还有一个边角料是,ROI的选取方法。
用Rect(x1,y2,width,height)取的话,参数是左上角坐标和宽高。
用Range(r1,r2,)取的话(取左不取右),写成
img_ROI=src1(Range(350,350+src2.rows),Range(800,800+src2.cols));//高、宽(Opencv里这种顺序总是乱七八糟的)
参考:《Opencv3编程入门》