OpenCV数字图像处理基于C++:基本操作

OpenCV数字图像处理基于C++:基本操作

1、图像的读取、显示和保存示例

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


using namespace cv;
using namespace std;

int main()
{
	Mat image;   //创建一个空图像image
	image = imread("E://2.png");  //读取文件夹中的图像

	//检测图像是否加载成功
	if (image.empty())  //检测image有无数据,无数据 image.empty()返回 真
	{
		cout << "Could not open or find the image" << endl;
		return -1;
	}

	namedWindow("IMAGE");  //创建显示窗口,不加这行代码,也能显示,默认窗口大小不能改变
	imshow("IMAGE", image);  //在窗口显示图像

	imwrite("D:\\1.png", image); //保存图像为png格式,文件名称为1

	waitKey(0);  //暂停,保持图像显示,等待按键结束

	return 0;

}

image-20221005151306320

1.1 imread函数

使用imread()读取图像,imread包含两个参数:imread(图像路径, 图像形式);

其中图像形式有三种:

1.加载彩色图像(默认加载形式)

imread(图像路径, IMREAD_COLOR);或者:imread(图像路径, 1);

2.加载灰度模式图像

imread(图像路径, IMREAD_GRAYSCALE);或者:imread(图像路径, 0);

3.加载图像,包括alpha通道

imread(图像路径,IMREAD_UNCHANGED);或者:imread(图像路径, -1);

1.2 namedWindow函数

功能:namedWindow() 的功能就是新建一个显示窗口,用来显示图像。

namedWindow() 包含两个参数:namedWindow(窗口名称, 窗口形式)

窗口形式常用的两种:

1.显示的图像大小不能改变(默认形式)

namedWindow(窗口名称, WINDOW_AUTOSIZE)

2.图像大小能够调节

namedWindow(窗口名称, WINDOW_NORMAL)

1.3 imshow函数

功能:imshow函数用于显示图像。

imshow() 函数包含两个参数:

imshow(窗口名称,图像名称)

1.4 imwrite函数

功能:imwrite函数用于显示图像。
imwrite() 函数包含两个参数:
imwrite(保存图像名称及格式,图像名称)

2、Mat创建图像(矩阵),获取图像信息,感兴趣区域(Rect)

2.1 创建图像(矩阵):Mat

使用Mat创建图像(矩阵)的常用形式有:
1.创建一个空图像,大小为0

 Mat image1;

2.指定矩阵大小,指定数据类型:

Mat image1(100,100,CV_8U);

这里包含三个参数:矩阵行数,矩阵列数,数据类型;

其中数据类型有很多种,常用的应该有:

CV_8U:8位无符号型(0~255),即灰度图像;

CV_8UC3:三通道8位无符号型,这里三通道指B(蓝)G(绿)R(红),与matlab中的RGB正好相反。

这里创建矩阵时未指定矩阵的值,发现默认值的大小为205.

3.指定矩阵大小,指定数据类型,设置初始值:

Mat image1(100,100,CV_8U, 100);

这里包含四个参数:矩阵行数,矩阵列数,数据类型,初始值;
对于灰度图像:可以直接给出初始值,也可以使用Scalar();

Mat image1(100,100,CV_8U, 100);
Mat image1(100,100,CV_8U, Scalar(100));

对于三通道图像:使用Scalar();

Mat image1(100,100,CV_8UC3, Scalar(100,100,100));

2.2 获取图像信息

获取图像的宽度(列数),高度(行数),尺寸和通道数:

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

using namespace std;
using namespace cv;

int main() {

	Mat image = imread("E:\\2.png",0);
	if (image.empty()) {
		cout << "读取错误!" << endl;
		return -1;
	}
	namedWindow("Display window");
	imshow("Display window", image);		// 显示图像

	cout << "图像的行数为: " << image.rows<<endl;		// 获取图像的高度(行数)
	cout << "图像的列数为: " << image.cols<<endl;		// 获取图像的宽度(列数)
	cout << "图像的通道数为: " << image.channels() << endl;		// 获取图像的通道数(彩色 3; 灰度 1)
	cout << "图像的尺寸为: " << image.size<<endl;		// 获取图像的尺寸(行*列)
	

	waitKey(0);
	return 0;
}

image-20221005155048522

image-20221005155108422

2.3 感兴趣区域

通过Rect()定义一个感兴趣区域:

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

using namespace cv;
using namespace std;

int main()
{
	Mat image1 = imread("E:\\2.png");  //读取图像;
	if (image1.empty())
	{
		cout << "读取错误" << endl;
		return -1;
	}

	imshow("image1", image1);  //显示图像;

	Mat imageROI(image1, Rect(50, 50, 80, 80));  //定义感兴趣区域
	imshow("image2", imageROI);

	waitKey(0);  //暂停,保持图像显示,等待按键结束

	return 0;
}

image-20221005155904720

其中Rect()有四个参数,Rect(a,b,c,d):
a:感兴趣区域列(cols)的起点;
b:感兴趣区域行(rows)的起点;
c:感兴趣区域的列数(cols);
d:感兴趣区域的行数(rows);

3、 单窗口显示多幅图像与贴图

//Mat每个格子内的数据格式---------- - Mat定义
//Mat_<uchar>-------- - CV_8U(0-255)
//Mat<char>---------- - CV_8S(-128-127)
//Nat_<short>-------- - CV_16S(-32768-32767)
//Mat_<ushort>--------CV_16U(0-65535)
//Mat_<int>---------- - CV_32S(-2147483648-2147483647)
//Mat_<float>----------CV_32F(-FLT_MAX…FLT_MAX,INF,NAN)
//Mat_<double>--------CV_64F(-DBL_MAX…DBL_MAX,INF,NAN)

//Mat数据类型和通道对应的type()
//              C1          C2          C3          C4
//CV_8U         0           8           16          24
//CV_8S         1           9           17          25
//CV_16U        2          10           18          26
//CV_16S        3          11           19          27
//CV_32S        4          12           20          28
//CV_32F        5          13           21          29
//CV_64F        6          14           22          30

3.1 单窗口显示多幅图片

为了实现这个效果中,在OpenCV中,可以在一幅黑色背景大图上把各幅图绘制上去,这其中关键是要计算出各幅图绘制在大图中的哪个区域,并对图像按显示区大小进行相应的缩放。

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#include<iostream>

using namespace cv;
using namespace std;


/*******************同时显示多张图片*************************
*srcImages 为要显示的图片的集合;
*imageSize 为要显示的图片的大小;
*最多同时显示12张图片
*************************************************************/
void showManyImages(const vector<Mat>& srcImages, Size imageSize) {
	int nNumImages = srcImages.size();  //获取图片数量
	Size nSizeWindows;
	if (nNumImages > 12) {
		cout << "no more tha 12 images" << endl;
		return;
	}
	//根据图片序列数量来确定分割小窗口的形态
	switch (nNumImages) {
        case 1:nSizeWindows = Size(1, 1); break;
        case 2:nSizeWindows = Size(2, 1); break;
        case 3:
        case 4:nSizeWindows = Size(2, 2); break;
        case 5:
        case 6:nSizeWindows = Size(3, 2); break;
        case 7:
        case 8:nSizeWindows = Size(4, 2); break;
        case 9:nSizeWindows = Size(3, 3); break;
        default:nSizeWindows = Size(4, 3);
	}
	//设置小图像尺寸,间隙,边界
	int nShowImageSize = 200;
	int nSplitLineSize = 15;
	int nAroundLineSize = 50;
	//创建输出图像,图像大小根据输入源来确定
	const int imagesHeight = nShowImageSize * nSizeWindows.width + nAroundLineSize + (nSizeWindows.width - 1) * nSplitLineSize;
	const int imagesWidth = nShowImageSize * nSizeWindows.height + nAroundLineSize + (nSizeWindows.height - 1) * nSplitLineSize;
	cout << imagesWidth << "  " << imagesHeight << endl;
	// 大图像大小
    Mat showWindowsImages(imagesWidth, imagesHeight, CV_8UC3, Scalar(0, 0, 0));
	//提取对应小图像的左上角坐标x,y
	int posX = (showWindowsImages.cols - (nShowImageSize * nSizeWindows.width + (nSizeWindows.width - 1) * nSplitLineSize)) / 2;
	int posY = (showWindowsImages.rows - (nShowImageSize * nSizeWindows.height + (nSizeWindows.height - 1) * nSplitLineSize)) / 2;
	cout << posX << "  " << posY << endl;
	int tempPosX = posX;
	int tempPosY = posY;
	//将每一幅小图像整合成一幅大图像
	for (int i = 0; i < nNumImages; i++) {
		//小图像坐标转换
		if ((i % nSizeWindows.width == 0) && (tempPosX != posX)) {
			tempPosX = posX;
			tempPosY += (nSplitLineSize + nShowImageSize);
		}
		//利用Rect区域将小图像置于大图像的相应区域
		//tempPosX,tempPosY图像坐标起点;nShowImageSize, nShowImageSize图像尺寸大小
		Mat tempImage = showWindowsImages(Rect(tempPosX, tempPosY, nShowImageSize, nShowImageSize));
		cout << tempPosX << " " << tempPosY <<" "<<nShowImageSize<< endl;
		resize(srcImages[i], tempImage, Size(nShowImageSize, nShowImageSize));//利用resize函数实现图像缩放
		tempPosX += (nSplitLineSize + nShowImageSize);
	}//for
	imshow("单窗口显示多图片", showWindowsImages);
}

int main() {
	//图像源输入
	vector<Mat>srcImage(9);
	srcImage[0] = imread("E:\\1.png");
	srcImage[1] = imread("E:\\1.png");
	srcImage[2] = imread("E:\\1.png");
	srcImage[3] = imread("E:\\1.png");
	srcImage[4] = imread("E:\\1.png");
	srcImage[5] = imread("E:\\2.png");
	srcImage[6] = imread("E:\\2.png");
	srcImage[7] = imread("E:\\2.png");
	srcImage[8] = imread("E:\\2.png");
	//判断当前vector读入的正确性
	for (int i = 0; i < srcImage.size(); i++) {
		if (srcImage[i].empty()) {
			cout << "read error" << endl;
			return -1;
		}
	}
	//调用 单窗口显示图像
	showManyImages(srcImage, Size(147, 118));
	waitKey(0);
	system("pause");
	return 0;
}

image-20221005170053924

resize(输入图像, 输出图像, 图像尺寸大小);

3.2 利用copyTo 实现图1贴在图2上

浅拷贝

浅拷贝一般有两种形式:1. 用“=” 2. 用“()”
我们知道变量名实际上是内存地址的指向,通过“=”复制图像,新Mat变量仍指向原图像地址。
浅拷贝对复制后的图像的任何操作实际都是对原图像的操作,所以名浅拷贝,并没有开辟一块空间来创建新的图像。

Mat m2 = m1;
Mat m3(m1);

m2和m3的改变会影响到m1。

深拷贝

深拷贝在opencv C++中有两种操作:1. copyTo() 2. clone()
深拷贝在内存中新开辟空间来存放复制后的图像,对复制后的图像的任何操作不会再影响原图像。
主要是注意.copyTo()和clone()的用法,两者用法有些不同,copyTo()是先声明一个mat名,原图像.copyTo到(新图像)。

int main()
{
	Mat img1 = imread("E:\\1.png");
	Mat img2 = imread("E:\\2.png");
	/*imshow("img1", img1);
	imshow("img2", img2);*/

	// 在img1上创建一个感兴趣的区域
	Mat imgdst = img1(Rect(0, 0, img2.cols, img2.rows));

	 加载图2,转换为灰度图后存入mask
	//Mat mask = imread("E:\\2.png", 0);
	/*imshow("img3", mask);*/
	img2.copyTo(imgdst);

	imshow("贴图",img1);
	waitKey(0);
	return 0;
}

image-20221005192316031

4、窗口销毁与调整窗口大小

4.1 窗口销毁

int main() {
	//图像源输入
	vector<Mat>srcImage(2);
	char szName[50] = "";
	for (int i = 0; i < srcImage.size(); i++)
	{
		sprintf_s(szName, "E:\\%d.png", i + 1);
		srcImage[i] = imread(szName);
		if (srcImage[i].empty()) { //判断当前vector读入的正确性	
			cout << "read " << szName << " error" << endl;
			return -1;
		}
		//调用 单窗口显示图像
		namedWindow(szName, WINDOW_AUTOSIZE);
		imshow(szName, srcImage[i]);//在“窗口1”这个窗口输出图片。
		waitKey(5000);//等待5秒,程序自动退出。改为0,不自动退出。
		destroyWindow(szName);
	}
	//destroyAllWindows();
	cout << "所有的窗口已经销毁了" << endl;
	waitKey(0);
	system("pause");
	return 0;
}

sprintf_s 函数功能:把格式化的指定长度的数据写入某个字符串

4.2 调整窗口大小

int main() {
	vector<Mat>srcImage(1);
	char szName[50] = "";
	int  width = 640, height = 480;

	sprintf_s(szName, "E:\\%d.png", 1);
	srcImage[0] = imread(szName);
	if (srcImage[0].empty()) {
		cout << "read " << szName << " error" << endl;
		return -1;
	}
	cout << szName;
	namedWindow("img", WINDOW_NORMAL);	//新建窗口
	imshow("img", srcImage[0]);		//在窗口显示图片
	resizeWindow("img", width, height);	//调整窗口大小

	waitKey(0);
	system("pause");

	return 0;
}

image-20221005175714305

5、鼠标事件与键盘事件

鼠标事件event主要有以下几种:

enum{
	EVENT_MOUSEMOVE			=0,		//滑动
	EVENT_LBUFFONDOWN		=1,		//左键单击
	EVENT_RBUFFONDOWN		=2,		//右键单击
	EVENT_MBUFFONDOWN		=3,		//中键单击
	EVENT_LBUFFONUP			=4,		//左键放开
	EVENT_RBUFFONUP			=5,		//右键放开
	EVENT_MBUFFONUP			=6,		//中键放开
	EVENT_LBUFFONDBLCLK		=7,		//左键双击
	EVENT_RBUFFONDBLCLK		=8,		//右键双击
	EVENT_MBUFFONDBLCLK		=9		//中键双击
};

鼠标事件标志flags主要有以下几种:

enum{
	EVENT_FLAG_LBUTTON 		=1,		//左键拖拽
	EVENT_FLAG_RBUTTON 		=2,		//左键拖拽
	EVENT_FLAG_MBUTTON 		=4,		//左键拖拽
	EVENT_FLAG_CTRLKEY 		=8,		//按Ctrl
	EVENT_FLAG_SHIFTKEY 	=16,	//按Shift
	EVENT_FLAG_ALTKEY 		=32,	//按Alt
};

5.1 在图片上画线

void on_mouse(int event, int x, int y, int flags, void*)
{
	if (event == EVENT_LBUTTONDOWN)
	{
		previousPoint = Point(x, y);
	}
	else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
	{
		Point pt(x, y);
		line(g_srcImage, previousPoint, pt, Scalar(0, 0, 255), 2, 5, 0);	//画线
		previousPoint = pt;
		imshow("WINDOW", g_srcImage);
	}
}

int main() {
	g_srcImage = imread("E:\\2.png");
	imshow("WINDOW", g_srcImage);
	setMouseCallback("WINDOW", on_mouse, 0);
	waitKey(0);
	system("pause");

	return 0;
}

​ on_mouse(int event, int x, int y, int flags, void*) 其中参数 event 表示鼠标事件,x 表示鼠标事件的 x 坐标,y 表示鼠标事件的 y 坐标,flag 表示鼠标事件的标志,userdata 是可选参数。在上述代码中,on_mouse 就是用来处理鼠标事件的回调函数,当鼠标有动作时,on_mouse 会被系统调用,然后在 on_mouse 中,判断发送了哪种动作,进行相应处理。

image-20221005182710061

5.2 按Esc退出播放视频

int main()
{
	VideoCapture cap("E:\\1.mp4");
	if (!cap.isOpened()) {
		cout << "video open error!\n";
		return -1;
	}

	Mat frame;
	while (1) {
		cap >> frame;	//从cap中读取数据到frame中
		if (frame.empty())
			break;
		imshow("la",frame);
		if (waitKey(30) == 27)	//延时30ms,以正常的速率播放视频,播放期间按下Esc则退出播放
			break;
	}
	return 0;
}

参考:https://zhuanlan.zhihu.com/p/486830242

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值