Opencv学习二图片的载入、显示与输出

前言

首先必须明白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编程入门》

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/86557494