HIGHGUI页面初步学习

图像的输入显示和输出

imread函数

Mat imread(const string&filename ,int flags=1);
//第一个参数为文件名字
//flag默认是1
//flags>0返回3通道的彩色图像 BGR顺序
//flags=0返回灰度图像
//flags=2|4载入无损原图像
//flags<0反馈包含Alpha通道的加载图像
//下图为imread("",0);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5zfpzqF-1658580159739)(C:\Users\正经人\AppData\Roaming\Typora\typora-user-images\image-20220606192802572.png)]

InputArray类型

可以简单当做Mat类型处理

nameWindow()函数

void nameWindow(const string&filename ,int flags=WINDOW_AUTOSIZE);

flags=WINDOW_AUTOSIZE为自动适应窗口,自动调整以适应所显示的图像,不可以手动改变窗口的大小

flags另外的两个可选择的值为WINDOW_NORMAL:可以手动设置窗口大小

WINDOW_OPENGL窗口创建的时候支持OpenGL

|OpenGL是渲染2D、3D矢量图形硬件的一种软件接口。

输出图像到文件imwrite()函数

bool imwrite(const string& filename,InputArray img,const vector<int>& params=vector<int>());

filename “123.jpg”

InputArray img 填写mat类型的数据

const vector& params=vector() 默认的图像编码参数,可以修改图片质量,JPEG为0-100,PNG为0-9

合并显示

将长与宽小于等于image的logo与image重叠在一起

void cvAddWeighted( const CvArr* src1, double alpha,const CvArr* src2, double beta,double gamma, CvArr* dst );

参数1:src1,第一个原数组.
参数2:alpha,第一个数组元素权重

参数3:src2第二个原数组
参数4:beta,第二个数组元素权重
参数5:gamma,图1与图2作和后添加的数值。不要太大,不然图片一片白。总和等于255以上就是纯白色了。

参数6:dst,输出图片

//两张图片拼在一起
	Mat image = imread("tu1.jpg");
	Mat logo = imread("tu2.jpg");
	//输入后显示
	namedWindow("tupian1");
	imshow("tupian1", image);
	namedWindow("tupian2");
	imshow("tupian2", logo);

	//定义一个Mat类型,用于存放图像的ROI
	Mat imageROI;
	//方法一,利用Rect设置ROI位置
	imageROI = image(Rect(0, 0, logo.cols, logo.rows));
	//方法二
	//imageROI=image(Range(350,350+logo.rows),Range(00,800+logo.cols));
	//将logo加到原图上
	
	addWeighted(imageROI, 0.5, logo, 0.3, 0., imageROI);
	namedWindow("hunhe");
	imshow("hunhe", image);
	
	//imwrite("apicture.jpg", image);
	waitKey();
	destroyAllWindows();
	return 0;


opencv 手写输入法_opencv

遇到的BUG

0x00007FF872444FD9 处(位于 Project1.exe 中)有未经处理的异常: Microsoft C++ 异常: cv::Exception,位于内存位置 0x000000F11317EFB8 处。


opencv 手写输入法_opencv 手写输入法_02

解决方法

  1. 将读取文件的路径改为双“ \"
Mat image = imread("D:\\opencv_learn\\Project1\\tu1.jpg");
	Mat logo = imread("D:\\opencv_learn\\Project1\\tu2.jpg");

或将图片放置在工程文件下下方,并直接读取

opencv 手写输入法_回调函数_03

  1. 查看链接器-输入-附加依赖,Debug x64应该为opencv_world***d.lib,查看一下是否填写错误
  2. 查看是否有重复的窗口名,检查下imshow()是不是写错了(经过实验,不同窗口读取相同图片不会产生bug)
//输入后显示
namedWindow("tupian1");
imshow("tupian1", image);
namedWindow("tupian2");
imshow("tupian2", logo);
  1. 图片颜色变换问题、图片通道数问题、图片尺寸问题,我的问题如下面代码所示,将image放在logo的左下角,但由于image的像素比logo大,大的图片不可能作为小的图片的一部分,所以产生了本次错误,再将image和logo切换位置后,可以成功运行。
    大家在写OPENCV的代码时,也要注意Rect后面初始位置的两个参数,如果x+logo.colsimage.cols大,就有可能产生本错误。
//定义一个Mat类型,用于存放图像的ROI
	Mat imageROI;
	//方法一,利用Rect设置ROI位置
	imageROI = logo(Rect(0, 0, image.cols, image.rows));
	//方法二
	//imageROI=image(Range(350,350+logo.rows),Range(00,800+logo.cols));
	//将logo加到原图上
	
	addWeighted(imageROI, 0.5, image, 0.3, 0., imageROI);
	namedWindow("hunhe");
	imshow("hunhe", image);

	//imwrite("apicture.jpg", image);
	waitKey();
	destroyAllWindows();
	return 0;

我们可以看到tupian1比tupian2大


opencv 手写输入法_人工智能_04

滑动条的创建和使用

滑动条是OpenCV动态调节参数中比较好用的工具,依附于窗口存在

创建滑动条:createTrackbar()

createTrackbar()往往会配合一个回调函数使用。

int creatTrackbar(conststring& trackbarname,conststring& winname,int* value,int count,TrackbarCallback onChange=0,void* userdata=0);

  • conststring& trackbarname:轨迹条的名字
  • conststring& winname:窗口的名字,表示轨迹条会依附到哪个窗口
  • int* value:指向整型的指针,表示滑块位置
  • int count:表示滑块可以达到的最大位置的值,滑块最小位置的值始终为0
  • TrackbarCallback onChange=0:回调函数的指针,每次滑块位置改变时,这个函数会进行回调,。函数的原型为void XXX(int,void*);,第一个参数是轨迹条的位置,第二个参数是用户数据,当用户数据指针返回NULL时,证明没有回调函数调用,仅value有变化,即图像的混合程度不变,但是滑动块的位置却变化了
  • void* userdata=0:默认值为0,如value为实参、全局变量,则可不用填写。因为此参数代表用户输入的变量,当value为实参且为全局变量时,改变value,即可对回调函数值产生影响,不需要再输入userdata了
    createTrackbar制定了一个回调函数,当轨迹条位置改变时,调用这个回调函数。回调函数就是一个通过函数指针调用的函数,当这个指针被用来调用所指向的函数时,称其为回调函数。

Example

#include<iostream>
using namespace cv;
using namespace std;
//createTrackbar()滑动条
const int g_nMaxAlpha = 100;//设置透明度最高为100
int g_nAlphaValueSlider;//创建滑动条对应的变量
double g_dAlphaValue;
double g_dBeteValue;

Mat srcimage1;
Mat srcimage2;
Mat dstimage;
Mat srcimage11, srcimage22;
void on_Trackbar(int, void*)
{
	g_dAlphaValue = (double)g_nAlphaValueSlider / g_nMaxAlpha;//求出当前透明值的比例
	g_dBeteValue = 1 - g_dAlphaValue;
	//根据alpha和beta对两张图片进行线性混合
	addWeighted(srcimage1, g_dAlphaValue, srcimage2, g_dBeteValue, 0.0, dstimage);
	imshow("图片", dstimage);
}

int main()
{
    //createTrackbar()滑动条
	//设置图片为相同大小,如果不同,可以使用resize语句
	srcimage1=imread("tu1.jpg");
	srcimage2 = imread("tu2.jpg");
	
	int down_width = 798;
	int down_height = 1280;
	
	resize(srcimage1, srcimage1, Size(down_width, down_height), INTER_LINEAR);
	resize(srcimage2, srcimage2, Size(down_width, down_height), INTER_LINEAR);
	
	if (!srcimage1.data)
	{
		printf("读取第一个照片有错误");
		return -1;
	}
	if (!srcimage2.data)
	{
		printf("读取第二个照片有错误");
		return -1;
	}
	g_nAlphaValueSlider = 70;//赋值为70
	namedWindow("图片");
	//创建滑动条
	char TrackbarName[50];
	sprintf_s(TrackbarName, "透明值 %d", g_nMaxAlpha);//sprintf_s为sprintf的安全升级版本

	createTrackbar(TrackbarName, "图片", &g_nAlphaValueSlider, g_nMaxAlpha, on_Trackbar);
	//结果在回调函数中显示
	on_Trackbar(g_nAlphaValueSlider, 0);

	//按任意键退出
	waitKey(0);
	return 0;

}

获取当前轨迹条的位置:getTrackbarPos()

int getTrackbarPos(conststring& trackbarname,conststring& winname);

  • 第一个参数:轨迹条的名字
  • 第二个参数:轨迹条窗口的名字

鼠标操作

OpenCV中的鼠标操作与滑动条的映射方式类似,均需要用到回调函数。

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

  • 第一个参数,窗口的名字
  • 第二个参数,当鼠标的位置移动(x、y坐标以图像坐标系为参考系)或鼠标左键按下、左键抬起时,被调用的函数指针
  • 第三个参数,洪湖定义的传递到回调函数的参数,默认值为0
cv::Scalar的构造函数是cv::Scalar(v1, v2, v3, v4)
前面的三个参数是依次设置BGR的,和RGB相反,第四个参数设置图片的透明度。

cv::scalar::all(0)将四个值全部设为0.

image.copyTo(imageROI),作用是把image的内容粘贴到imageROI;

#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace cv;
using namespace std;
#define WINDOW_NAME "【程序窗口】"

//全局函数说明
void on_MouseHandle(int event, int x, int y, int flags, void* param);
void DrawRectangle(cv::Mat& imag, cv::Rect box);
void ShowHelpText();
//全局变量说明
Rect g_rectangle;
bool g_bDrawingBox = false;//是否进行绘制
RNG g_rng(12345);

int main()
{
	g_rectangle = Rect(-1, -1, 0, 0);
	Mat srcImage(600, 800, CV_8UC3), tempImage;//CV_8UC3表示的是:8位深度的unsigned int型,C3表示channel 3,3个通道
	srcImage.copyTo(tempImage);
	srcImage = Scalar::all(0);

	//设置回调函数
	namedWindow(WINDOW_NAME);
	setMouseCallback(WINDOW_NAME, on_MouseHandle, (void*)&srcImage);
	while (1)
	{
		srcImage.copyTo(tempImage);//复制原图到临时变量
		if (g_bDrawingBox)DrawRectangle(tempImage, g_rectangle);
		imshow(WINDOW_NAME, tempImage);
		if (waitKey(10) == 27)break;//按下ESC,程序退出
	}
	return  0;
}

void on_MouseHandle(int event, int x, int y, int flags, void* param)
{
	//x与y代表鼠标现在的坐标位置
	Mat& image = *(cv::Mat*)param;
	switch (event)//event为鼠标移动or按下or抬起
	{
		//鼠标移动消息
	case EVENT_MOUSEMOVE:
	{
		if (g_bDrawingBox)//判断是否进行绘制,并将长和宽存到RECT变量中
		{
			g_rectangle.width = x - g_rectangle.x;
			g_rectangle.height = y - g_rectangle.y;
		}
	}
	break;
	//左键按下
	case EVENT_LBUTTONDOWN:
	{
		g_bDrawingBox = true;//之后再拖动,即可改变rectangle的值
		g_rectangle = Rect(x, y, 0, 0);
	}
	break;
	//左键抬起
	case EVENT_LBUTTONUP:
	{
		g_bDrawingBox = false;
		if (g_rectangle.width < 0)
		{
			//将x加一个负值,向左移,并把width的正负反转,在向右画矩形时,实现效果
			//若不操作,原意为在x,y点向左画矩形
			g_rectangle.x += g_rectangle.width;
			g_rectangle.width *= -1;
		}
		if (g_rectangle.height < 0)
		{
			g_rectangle.y += g_rectangle.height;
			g_rectangle.height *= -1;
		}
		//调用函数进行绘制
		DrawRectangle(image,g_rectangle);
	}
	break;
	}
}
void DrawRectangle(cv::Mat& img, cv::Rect box)
{
	rectangle(img, box.tl(), box.br(), Scalar(g_rng.uniform(0, 255), g_rng.uniform(0, 255), g_rng.uniform(0, 255)));//随机颜色
}

opencv 手写输入法_回调函数_05