Opencv学习笔记(一)higui模块使用

本文详细介绍OpenCV中图像的载入、显示、输出方法,滑动条与鼠标操作的创建及使用技巧,通过实例代码展示如何实现图像基础操作、滑动条控制图像混合以及鼠标绘制矩形。

大纲

1.图像的载入、显示、输出
2.滑动条的创建与使用
3.鼠标操作的使用

一、图像的载入、显示、输出
图像的载入
使用imread()函数,其原型如下:

Mat imread(const String& filename,int flags = 1);

第一个参数即为所要读取图片的地址,第二个参数为读取图像颜色类型,默认颜色为1,其可能取值对应结果如下:

flag=-1(<0)时,原位深度,原通道
flag=0时,8位深度,单通道
flag=1时,8位深度,3通道
flag=2时,原位深度,单通道
flag=3时,原位深度,3通道
flag=4(>=4)时,8位深度,3通道

验证代码如下:

/*
此程序旨在探究imread函数第二个参数取不同值时输出值的不同
*/

# include<opencv2/opencv.hpp>
#include<vector>

using namespace cv;
using namespace std;

void createMat(Mat &temp)
{
	for (int i = 0;i < temp.rows;i++)
	{
		for (int j = 0;j < temp.cols;j++)
		{
			Vec4w& test = temp.at<Vec4w>(i, j);    //因为希望图片为16 位4通道,则不能用只有8位的uchar即Vec4b而得用16位short对应的Vec4w
			test[0] = rand() % 65535;
			test[1] = rand() % 65535;
			test[2] = rand() % 65535;
			test[3] = rand() % 65535;
		}
	}

}


int main()
{ 
	Mat tempImg1(480,640,CV_16UC4) ,tempImg2;
	createMat(tempImg1);
	vector<int> compress_params;
	compress_params.push_back(IMWRITE_PNG_COMPRESSION);
	compress_params.push_back(0);
	imwrite("test.png", tempImg1, compress_params);
	tempImg2 = imread("test.png", 3);
	namedWindow("test", WINDOW_NORMAL);
	imshow("test", tempImg2);
	waitKey(0);


	createMat(tempImg1);

	return 1;
}

图像的显示
使用imshow()函数,其原型如下:

void imshow(const String& winname, InputArray mat)

第一个参数为窗口名的字符串;第二个参数为所要读取的图片,通常情况可认为InputArray类型就是Mat.
其中窗口的创建可以在调用imshow()函数时自行创建,也可以通过namedWindow()函数提前初始化,其原型如下:

void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE)

第一个参数为所要创建的窗口的名字,第二个参数为可选标识,常用标识具体如下:

WINDOW_AUTOSIZE :默认标识,窗口大小自动调整以适应图像,用户无法自主修改
WINDOW_NORMAL:选择此表示,用户可以自主修改窗口的大小

图像的输出
使用imwrite()函数,原型如下:

imwrite( const String& filename, InputArray img,const std::vector<int>& params = std::vector<int>());

第一个参数为图片存储的路径,第二个参数为所要保存的图片,第三个参数为保存时的压缩选项为vectot类型,具体选项如下:

对PNG保存:IMWRITE_PNG_COMPRESSION,取值0~9的压缩级别,值越大压缩效果越好,图片越小,质量越差,默认3
对JPG保存:IMWRITE_JPEG_QUALITY,取值0~100的图片质量,值越大保存效果越好,图片越大,默认95

图片基础操作实例代码如下:

/*
此示例程序通过输出一张AlphaRGB图给出了imwrite,imshow,imread函数的使用方法
*/


#include <opencv2/opencv.hpp>
#include <vector>

using namespace cv;  //命名空间的声明
using namespace std;

void createAlphaMat(Mat& mat)
{
	for (int i = 0;i < mat.rows;i++)
	{
		for (int j = 0;j < mat.cols;j++)
		{
			Vec4b& rgba = mat.at<Vec4b>(i, j);   //定义一个四维向量 rgba=mat[i,j]位置的四维向量,引用
			rgba[0] = UCHAR_MAX;   //每个点第一维赋值为uchar的最大值即255
			rgba[1] = saturate_cast<uchar>((float(mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX);   
			rgba[2] = saturate_cast<uchar>((float(mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX);  //按公式输出每个点二三维的值,用saturate防止溢出
			rgba[3] = saturate_cast<uchar>(0.5 * (rgba[1] + rgba[2]));   //alpha通道赋值


		}
	}
}
int main()
{
	//创建带Alpha通道的Mat
	Mat mat(480, 640, CV_8UC4);  //创建460行,640列,4通道8位无符号字符矩阵
	createAlphaMat(mat);
	vector<int>compression_params;  //定义向量,类似数组
	compression_params.push_back(IMWRITE_PNG_COMPRESSION);  //向量第一个值为枚举类型16,表示png压缩品质
	compression_params.push_back(9);  //第二个值为压缩品质选择9
	//try-catch用于发现运行中的错误,若try中语句发生错误,则执行catch中语句
	try { 
		imwrite("透明Alpha值图.png", mat, compression_params);  //第三个参数为二维vector类型,即png压缩品质:9
		namedWindow("生成的PNG图", WINDOW_NORMAL);
		mat = imread("透明Alpha值图.png",1);
		if (!mat.data)
		{
			cout << "读取图片失败";
			return 1;
		}
		imshow("生成的PNG图", mat);
		fprintf(stdout, "PNG图片文件的alpha数据保存完毕,\n可以在工程目录下查看由imwrite生成的图片\n");
		waitKey(0);
	}
	catch (runtime_error& ex)  //发生runtime_error则执行
	{
		fprintf(stderr, "图像转换成PNG格式发生错误:%s\n", ex.what());  //输出到指定路径,这里为标准错误输出,仍在屏幕
		return 1;
	}
	return 0;

}

其中使用到了Opencv中的Vec模板类,其具体介绍可参照我的另一篇博文:Opencv模板类Vec介绍
二、滑动条的创建与使用
滑动条就是依附在窗口的上的可实施调节程序运行的交互方式,其运用包括滑动条创建函数和回调函数两个部分,前者原型如下:

 createTrackbar(const String& trackbarname, const String& winname,int* value, int count,TrackbarCallback onChange = 0,void* userdata = 0);

第一个参数为滑动条的名字;
第二个参数为所要依附的窗口的名字;
第三个参数为滑动条所处位置的指针,为int*型,这也说明了默认滑动条的位置变化默认是整数变化,这一参数的初始值即给出了滑动条的初始位置。
第四个参数给出了滑动条所能达到的最大位置;
第五个参数为指向回调函数的指针,即用户所希望通过控制滑动条所影响到的函数,每次滑块位置改变时,这一回调函数都会运行一遍。同时这一回调函数有着格式要求,它应该为如下形式:

void 函数名(int,void *)

其中第一个参数为滑块的位置,第二个参数为传递用户数据的无类型指针,具体解释和之后第六个参数有关。
如果这一参数为NULL指针,就表示不需要回调函数,那么回调就只引起value的变化;
第六个参数为传递用户数据的无类型指针,默认值为0,这个值是用户传递给回调函数的值,即回调函数第二个参数的实参,用来处理滑动条事件,如果设置第三个参数滑块位置的话,通常就不需要再将其传递给回调函数,直接在回调函数内使用即可,这一参数也就可以忽略了。
使用实例如下:

/*
此示例程序通过将两张图(不同大小)混合给出了滑动条的使用方法
*/


#include <opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include <stdio.h>

using namespace cv;
#define WINDOW_NAME "线性混合"

const int MaxAlphaValue = 100; //Alpha的最大值
int bar_AlphaValueSlide;  //滑动条对应的变量
double AlphaValue;
double BetaValue;

//声明图像
Mat srcImg_1;  //被混合图像
Mat srcImg_2;  //混合图像
Mat Img_1ROI;
Mat dst_Img;

void Trackbar(int pos, void*)  //滑动条作用所引起的回调函数
{
	static int sum = 1;
	srcImg_1.copyTo(dst_Img);
	Img_1ROI = dst_Img(Rect(0, 0, srcImg_2.cols, srcImg_2.rows));  //在待混合图中圈出混合图大小相等部分
	printf("运行次数加一,共%d次\n", sum);  //每次移动仍然是共1次,回调函数是重新调用,并不累计
	//求出当前滑动条至对应最大值比例,作为Alpha
	printf("当前位置%d\n", pos);
	AlphaValue = (double)bar_AlphaValueSlide / MaxAlphaValue;
	//Beta的值为1减去Alpha
	BetaValue = 1.0 - AlphaValue;
	//混合图片
	addWeighted(srcImg_2, AlphaValue, Img_1ROI, BetaValue, 0.0, Img_1ROI);  
	//本意想在原图上直接混合,但这样会使混合效应累积,形成全混合的的效果
	//未找到有效办法即在原图上混合,又使混合不累积
	//20.7.25留,每次调用函数重新copy一份原图处理不就行了。。。。

	imshow(WINDOW_NAME, dst_Img);
}

int main()
{
	srcImg_1 = imread("E:\\material\\assassin.jpeg");   //混合主体路径
	srcImg_2 = imread("E:\\material\\symbol.png");      //混合副体路径
	if (!srcImg_1.data || !srcImg_2.data)
	{
		printf("读取图片错误,请确认目录文件时是否存在该图片");
		return -1;  //读取错误直接返回-1,退出
	}
	

	bar_AlphaValueSlide = 70;
	namedWindow(WINDOW_NAME, 1);
	char TraceBackName[50];  //轨迹条名字
	sprintf_s(TraceBackName, "透明值%d", MaxAlphaValue);

	printf("运行至create之前\n");
	createTrackbar(TraceBackName, WINDOW_NAME, &bar_AlphaValueSlide, MaxAlphaValue, Trackbar);  //创建滑动条
	printf("运行至Trackbar之前\n");
	Trackbar(bar_AlphaValueSlide, 0);
	printf("运行至Trcackbar之后\n");
	
	//此段代码运行顺序是顺序运行至waitkey(),中途createTrackbar不触发回调函数
	//故若不在其后加上回调函数语句,则初始图片不会显示
	//运行至waitkey之后点击滑动条直接运行回调函数,不输出printf内容
	waitKey(0);
	return 0;
}

三、鼠标操作
鼠标操作和滑动条的使用类似,只不过鼠标操作是通过鼠标点击或者移动来实时对程序造成影响,它同样包括中介函数和回调函数,其中介函数的原型如下:

setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0)

第一个参数为鼠标操作所在的窗口名;
第二个参数为鼠标操作后所要运行的回调函数,同样鼠标每有一次操作,此回调函数要运行一遍,其格式要求如下:

void onMouse(int event,int x,int y,int flags,void* param)

回调函数第一个参数为鼠标事件变量,用于在回调函数中针对不同的鼠标事件包括EVENT_LBUTTONDOWN,EVENT_MOUSEMOVE等等做出不同的调整,具体包括:

#define CV_EVENT_MOUSEMOVE 0 滑动

#define CV_EVENT_LBUTTONDOWN 1 左键点击

#define CV_EVENT_RBUTTONDOWN 2 右键点击

#define CV_EVENT_MBUTTONDOWN 3 中间点击

#define CV_EVENT_LBUTTONUP 4 左键释放

#define CV_EVENT_RBUTTONUP 5 右键释放

#define CV_EVENT_MBUTTONUP 6 中间释放

#define CV_EVENT_LBUTTONDBLCLK 7 左键双击

#define CV_EVENT_RBUTTONDBLCLK 8 右键双击

#define CV_EVENT_MBUTTONDBLCLK 9 中间释放

回调函数第二,三个参数给出了当前鼠标在图像坐标系当中的位置(x,y);
回调函数第四个参数为鼠标拖拽事件变量,具体包括:

#define CV_EVENT_FLAG_LBUTTON 1 左键拖拽

#define CV_EVENT_FLAG_RBUTTON 2 右键拖拽

#define CV_EVENT_FLAG_MBUTTON 4 中间拖拽

#define CV_EVENT_FLAG_CTRLKEY 8 (8~15)按Ctrl不放事件

#define CV_EVENT_FLAG_SHIFTKEY 16 (16~31)按Shift不放事件

#define CV_EVENT_FLAG_ALTKEY 32 (32~39)按Alt不放事件

回调函数第五个参数和中介函数的第三个参数的介绍参见前述滑动条相应参数介绍,大同小异。
鼠标操作运用实例如下:

/*
此示例程序通过在窗口上画矩形给出了鼠标回调的使用方法
*/


#include<opencv2/opencv.hpp>
#include<stdio.h>
using namespace cv;
using namespace std;

#define WINDOW_NAME	"程序窗口"

void MouseCall(int event, int x, int y, int flag, void* paramas); //void*表示一个位置类型的指针,x,y为图像中坐标,event为事件类型,枚举
void DrawRect(Mat& img, Rect box);    //Mat&为对img的引用

Rect _rectangle;
bool drawbox = false;
RNG rng(12345); //设定种子为12345的随机数

int main()
{
	//预设参数
	_rectangle = Rect(-1, -1, 0, 0);
	Mat srcImg(600, 800, CV_8UC3), tempImg;
	srcImg.copyTo(tempImg);  //真实复制,二者地址不同
	srcImg = Scalar::all(255);

	//设置鼠标操作回调函数
	namedWindow(WINDOW_NAME);
	setMouseCallback(WINDOW_NAME, MouseCall, (void*)& srcImg);  //第二个参数为回调函数(指针),第三个为传递给回调函数的参数,这里为原图的指针



	//标识符为真时,进行绘制
	while (1)
	{
		srcImg.copyTo(tempImg);
		if (drawbox)
		{
			DrawRect(tempImg, _rectangle);//绘制标识符为真时,画矩形
		}
		imshow(WINDOW_NAME, tempImg);//注意,这里显示的图片以及上一张画图的图片都是tempImg,即只显示拖动每一刻的矩形,不累计
		if (waitKey(10) == ' ') break;//按下空格键退出循环
	}
	return 0;
}

//回调函数
void MouseCall(int event, int x, int y, int flags, void* param)
{
	Mat& img = *(cv::Mat*) param;  //(Mat*)为指针强制类型转换,*(Mat*)指取内容,并给引用img
	switch (event)
	{
		//鼠标移动
		case EVENT_MOUSEMOVE:
		{
			if (drawbox)
			{
				_rectangle.width = x - _rectangle.x;
				_rectangle.height = y - _rectangle.y;  //由两次坐标差记录画矩形所需要的width和height
			}
		}
		break;
		case EVENT_LBUTTONDOWN:  //只在按下左键的那一刻执行,记录那一刻坐标
		{
			drawbox = true;
			_rectangle = Rect(x, y, 0, 0);
			printf("坐标(%d,%d)\n",x,y);
		}
		break;
		//左键抬起时执行
		case EVENT_LBUTTONUP:
		{
			printf("宽,高(%d,%d)\n", _rectangle.width, _rectangle.height);
			drawbox = false;
			//若左键抬起后(x,y)值比原值小
			//则width,heigth取相反数,即正值,同时将(x,y)定位到抬起的值
			if (_rectangle.width < 0)   //
			{
				_rectangle.x += _rectangle.width;
				_rectangle.width *= -1;
			}
			if (_rectangle.height < 0)
			{
				_rectangle.y += _rectangle.height;
				_rectangle.height *= -1;
			}
			DrawRect(img, _rectangle);//这里画矩形的是srcImg,即在原图中标出最终矩形
			printf("坐标(%d,%d)\n", x, y);
			printf("宽,高(%d,%d)\n", _rectangle.width, _rectangle.height);
		}
		break;
	}
}
//画图函数
void DrawRect(Mat& img, Rect box)
{
	rectangle(img, box.tl(), box.br(), Scalar(rng.uniform(0, 255),
		rng.uniform(0, 255), rng.uniform(0, 255)));  //以(0,255)内均匀随机数作为矩形颜色
}

参考文献
opencv2 使用鼠标绘制矩形并截取和保存矩形区域图像
opencv中imread第二个参数的含义

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值