C/C++ vs2017 OpenCV简单入门

vs2017配置opencv详细教程 vs上配置opencv的详细步骤。 阅读详情

 离职之际,自学一下图像相关的知识点,只是简单学了一点皮毛!


目录

一、 OpenCV环境搭建

二、使用opencv

常用接口说明

使用案例

1. 图像色彩空间转换

2. Mat对象的创建与赋值

3. 图像像素的读写操作

4. 图像像素的算术操作

5. 滚动条操作演示

6. 键盘响应操作

7. OpenCV自带颜色表操作

8. 图像像素的逻辑操作

9. 通道分离与合并

10. 图像色彩空间转换

11. 图像像素值统计 

12. 图像几何形状绘制

13. 随机数与随机颜色 

14. 多边形填充与绘制

15. 鼠标操作与响应

16. 图像像素类型转换与归一化

17. 图像缩放与插值

18. 图像翻转

19. 图像旋转

20. 视频文件摄像头使用

21. 图像卷积操作

22. 高斯模糊 

23. 高斯双边模糊

三、键盘操作小demo

1. 键盘小demo

2. 旋转、缩放、裁剪、平移案例

3. 往图片中写入中文文字

四、总结


一、 OpenCV环境搭建

官网下载链接:Releases - OpenCV

 下载后双击开始解压安装

 

解压完毕 

 进入路径 D:\opencv\opencv\build\x64\vc14\bin

 vs2019及其以上的版本可以选择vc15那个文件夹

 拷贝该路径,设置环境变量

在路径D:\opencv\opencv\build\x64\vc14\lib 会有项目中需要使用到的lib

新建一个VS工程,

右键 属性 - VC++目录 - 包含库目录,添加头文件路径进来

 右键 属性 - VC++目录 - 库目录,添加lib文件路径进行

 右键 属性 - 链接器 - 输入 - 附加依赖项,将opencv_world460d.lib添加进来,如果是release项目,那么就添加opencv_world460.lib

 注意,属性中的“配置”和“平台”需要和界面的保持一致

当然,上面添加目录和库目录的操作,可以将相应的头文件和库拷贝到项目路径,再添加项目路径进来即可!

编写如下代码测试环境:

#include <opencv2/opencv.hpp>

#include <iostream>
#include <stdio.h>


using namespace cv;


void int test1(Mat src) {

    // 设置新的窗体标题,参数一窗体标题,参数二窗体随图片大小
	namedWindow("opencv窗口2", WINDOW_AUTOSIZE);

	Mat output_image;
	/* 设置图片颜色 */
	cvtColor(src, output_image, COLOR_BGRA2RGB);
	imshow("opencv窗口2", output_image);

	/* 保存图片到本地 */
	imwrite("D:/new.png", output_image);

    // 等待
	waitKey(0);
}


int main(void) {

	// 正常:IMREAD_COLOR
	// 灰度:IMREAD_GRAYSCALE
	Mat image = imread("图.jpeg", IMREAD_COLOR);    // 读取一张图片
	if (image.empty()) {        // 判断图片是否读取成功
		printf("path image NULL!\n");
		return -1;
	}

    // 显示图片    参数一是窗体标题,参数二图片对象
	imshow("原图", image);


	test1(image);

    return 0;
}

编译运行,不出意外的话,就会显示两张我们指定路径读取的图片

 左边那一种是修改颜色后的图片,右边是原图!

至此,环境搭建完毕!


二、使用opencv

常用接口说明

1. imread

Mat image = imread("图.jpeg", IMREAD_COLOR);

        解析:读取一张图片。

2. image.empty()

        解析:判断文件是否读取成功,ture表示不成功,false表示成功。

3. imshow

imshow("opencv窗口2", output_image);

        解析:显示一张图片,参数一是窗体名字,参数二是图片对象,也就是Mat对象

4. imwrite

imwrite("D:/new.png", output_image);

        解析:将图像保存到本地。参数一是文件路径名,参数二是图片对象。

5. waitKey

int waitKey(int delay = 0);

        解析:等待,相当于c/c++中的system("pause"); 参数为0,表示一直等待,其他大于零的数,则表示等待对应的秒数。
        

6. destroyAllWindows

void destroyAllWindows();

        解析:销毁所有打开的窗体。

7. cvtColor

void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );

        解析:设置图像的颜色。参数一是输入图像对象,参数二是设置好输出的图像对象,参数三是设置图像颜色枚举(enum ColorConversionCodes)

8. clone

Mat src = image.clone()

        解析:将图像对象克隆赋值,需要另一个Mat对象去接收,相当于深拷贝。

8. copyTo

Mat m2;
image.copyTo(m2)

        解析:将图像对象拷贝给另一个图像对象,相当于深拷贝。

9. zeros

Mat m4 = Mat::zeros(Size(128, 128), CV_8UC3);

        解析:创建空白图像。参数一是图像大小,参数二表示通道数。

10. Scalar

m4 = Scalar(255, 255, 0);

        解析:给图像设置颜色。因为是给三个通道的图像设置颜色,所以这里使用三个参数;如果是给单通道设置颜色,则设置一个参数即可。

11. cols

int width = m4.cols;

        解析:获取图像的宽度。

12. rows

int height = m4.rows;

        解析:获取图像的高度。

13. channels

int channels = m4.channels();

        解析:获取通向的通道数。单通道表示灰色图像,三通道表示彩色图像。

14. add

add(image, m1, dst1);

        解析:图像的像素相加。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相加后输出到参数三。

15. subtract

subtract(image, m2, dst2);

        解析:图像的像素相减。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相减后输出到参数三。

16. multiply

multiply(image, m3, dst3);

        解析:图像的像素相乘。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相乘后输出到参数三。

17. divide

divide(image, m4, dst4);

         解析:图像的像素相除。参数一和参数二是输入图像,参数三是输出图像。也就是将参数一和参数二的像素相除后输出到参数三。

18. waitKey

 int c = waitKey(100);

        解析:等待键盘输入,如果参数设置的毫秒数结束后还没有键盘输入,则默认输入-1,结束等待。

19. applyColorMap 

applyColorMap(image, dst, COLORMAP_COOL);

        解析:设置图像彩色映射。参数一是输入图像,参数二是输出图像,参数三是映射的颜色类型,他是枚举enum ColormapTypes。

        

20. bitwise_and  

bitwise_and(m1, m2, dst1);

        解析:图像像素逻辑与操作。参数一和参数二是输入图像,参数三是输出图像。

21. bitwise_or

bitwise_or(m1, m2, dst2);

        解析:图像像素逻辑或操作。参数一和参数二是输入图像,参数三是输出图像。

22. bitwise_not

bitwise_not(image, dst3);

        解析:图像像素取反操作。参数一是输入图像,参数二是输出图像。

23. bitwise_xor

bitwise_xor(m1, m2, dst4);

        解析:图像像素逻辑异或操作。参数一和参数二是输入图像,参数三是输出图像。

24. split

std::vector<Mat> mv;
split(image, mv);

        解析:通道分离。

25. rectangle

rectangle(dst1, rect, Scalar(0, 255, 255), 2, 8, 0);

        解析:绘制一个矩形。参数一是输出图像,参数二是矩形对象,参数三是颜色,参数四是边框厚度,参数五是反锯齿(可以使用LINE_AA效果更加),参数六不清楚。

        如果是需要填充,则将参数四传入-1。

26. circle

circle(dst1, Point(300, 400), 30, Scalar(255, 0, 255), 2, 8, 0);

        解析:绘制一个圆形。参数一是输出图像,参数二是圆的中心位置,参数三是圆的半径,参数四是颜色,参数五是边框厚度,参数六是反锯齿(可以使用LINE_AA效果更加),参数七不清楚。

        如果是需要填充,则将参数五传入-1。

27. line

line(dst5, Point(100, 130), Point(300, 400), Scalar(255, 0, 0), 2, 8, 0);

        解析:绘制一条线。参数一是输入输出图像, 参数二是第一个点位置,参数三是第二个点位置,参数四是颜色,参数五是线的宽度,参数六是反锯齿,参数七不清楚。

28. RNG

RNG rng((unsigned)time(NULL));    // 用系统时间作为种子初始化rng,要用到time.h库
int b = rng.uniform(0, 255);    // 随机生成 0 - 255

        解析:opencv的随机函数。

29. drawContours

drawContours(canvas, contours, 0, Scalar(255, 0, 0), -1);

        解析:绘制多边形或者填充多边形。参数一是输入输出图像,参数二是std::vector<std::vector<Point>>类型对象,参数三指定绘制容器中指定数据的第几个(-1表示全都绘制),最后一个参数为多边形的边框(-1表示填充)

30. resize

resize(image, zoomin, Size(w / 2, h / 2));

        解析:图像缩放。参数一是输入图像,参数二是输出图像,参数三是重新设置的图像大小。

31. flip

// 上下翻转
flip(image, dst, 0);

// 左右翻转
flip(image, dst, 1);

// 先左右翻转再上下翻转
flip(image, dst, -1);

        解析:图像翻转。参数一是输入图像,参数二是输出图像,参数三0表示上下翻转,1表示左右翻转,-1表示先左右翻转再上下翻转。

使用案例

以下函数的参数都基于此读取的图像对象:

int main(void) {

	// 正常:IMREAD_COLOR
	// 灰度:IMREAD_GRAYSCALE
	Mat image = imread("图.jpeg", IMREAD_COLOR);
	if (image.empty()) {
		printf("path image NULL!\n");
		return -1;
	}

	imshow("原图", image);

	waitKey(0);
	destroyAllWindows();

	return 0;
}

1. 图像色彩空间转换

/* 图像色彩空间转换 */
void colorSpace_Demo(Mat &image) {
	Mat gray, hsv;

	cvtColor(image, hsv, COLOR_BGR2HSV);
	cvtColor(image, gray, COLOR_BGR2GRAY);	// 灰度图像
	
	imshow("HSV", hsv);
	imshow("灰度", gray);

	imwrite("HSV.png", hsv);
	imwrite("GRAY.png", gray);
}

运行截图

2. Mat对象的创建与赋值

/* Mat对象的创建与赋值 */
void mat_creation_demo(Mat &image) {

	Mat m1, m2, m3;
	
	// 克隆
	m1 = image.clone();		// 深拷贝
	// 拷贝
	image.copyTo(m2);		// 深拷贝

	// 赋值
	m3 = image;				// 浅拷贝

	// 创建空白图像
	//Mat m4 = Mat::zeros(Size(8, 8), CV_8UC1);		// 创建一通道
	Mat m4 = Mat::zeros(Size(128, 128), CV_8UC3);	// 最后的数字表示通道数
	// 给通道赋值(相当于设置颜色)
	m4 = Scalar(123, 200, 100);	// 因为是3通道,所以赋三个值;如果是一通道,给一个参数值即可
	//std::cout << m4 << std::endl;

	imshow("m4", m4);

	// 图像的宽
	int width = m4.cols;
	// 图像的高
	int height = m4.rows;
	// 图像的通道数
	int channels = m4.channels();
	printf("width = %d\theight = %d\tchannels = %d\n", width, height, channels);


	// C++11新出的创建空白图像方法
	Mat kernel = (Mat_<char>(3, 3) <<
				  0, -1, 0,
				  -1, 5, -1,
				  0, -1, 0);
	kernel = Scalar(50, 100, 150);
	imshow("kernel", kernel);
}

运行截图

3. 图像像素的读写操作

/* 图像像素的读写操作 */
void pixel_visit_demo(Mat &image) {

	int w = image.cols;		// 获取图像宽
	int h = image.rows;		// 获取图像高
	int dims = image.channels();	// 获取图像通道数

	/* 数组方式 */
	//for (int row = 0; row < h; row++) {
	//	for (int col = 0; col < w; col++) {
	//		// 单通道 - 灰色图像
	//		if (1 == dims) {
	//			// 获取通道中的值
	//			int pv = image.at<uchar>(row, col);
	//			// 还可以对其进行修改
	//			image.at<uchar>(row, col) = 255 - pv;	// 得确保值在0-255区间

	//		} else if (3 == dims) {	// 三通道 - 彩色图像
	//			// 获取通道中的三个值
	//			Vec3b bgr = image.at<Vec3b>(row, col);
	//			// 还可以对其进行修改
	//			image.at<Vec3b>(row, col)[0] = 255 - bgr[0];
	//			image.at<Vec3b>(row, col)[1] = 255 - bgr[1];
	//			image.at<Vec3b>(row, col)[2] = 255 - bgr[2];
	//		}
	//	}
	//}


	/* 指针方式 */
	for (int row = 0; row < h; row++) {
		uchar *current_row = image.ptr<uchar>(row);
		
		for (int col = 0; col < w; col++) {
			if (1 == dims) {
				*current_row++ = 255 - *current_row;
			
			} else if (3 == dims) {
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
			}
		}
	}

	imshow("pixel_visit_demo", image);
}

运行截图:

4. 图像像素的算术操作

/* 图像像素的算术操作 */
void operators_demo(Mat &image) {

	// 加
	Mat dst1;
	Mat m1 = Mat::zeros(image.size(), image.type());
	m1 = Scalar(100, 100, 100);	// 给通道赋值(相当于设置颜色)
	add(image, m1, dst1);
	imshow("图像像素加法操作", dst1);

	// 减
	Mat dst2;
	Mat m2 = Mat::zeros(image.size(), image.type());
	m2 = Scalar(50, 50, 50);
	subtract(image, m2, dst2);
	imshow("图像像素减法操作", dst2);

	// 乘
	Mat dst3;
	Mat m3 = Mat::zeros(image.size(), image.type());
	m3 = Scalar(2, 2, 2);
	multiply(image, m3, dst3);	// 参数一乘以参数二结果给参数三
	imshow("图像像素乘法操作", dst3);

	// 除
	Mat dst4;
	Mat m4 = Mat::zeros(image.size(), image.type());
	m4 = Scalar(5, 5, 5);
	divide(image, m4, dst4);
	imshow("图像像素除法操作", dst4);


	///* 另外一种方式实现加法操作 */
	//Mat dst5 = Mat::zeros(image.size(), image.type());
	//Mat m5 = Mat::zeros(image.size(), image.type());
	//m5 = Scalar(50, 50, 50);

	//int w = image.cols;
	//int h = image.rows;
	//int dims = image.channels();

	//for (int row = 0; row < h; row++) {		
	//	for (int col = 0; col < w; col++) {
	//		if (3 == dims) {
	//			Vec3b p1 = image.at<Vec3b>(row, col);
	//			Vec3b p2 = m5.at<Vec3b>(row, col);
	//			// 加减乘除都是一样的
	//			dst5.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p1[0] + p2[0]);
	//			dst5.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] + p2[1]);
	//			dst5.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);
	//		}
	//		
	//	}
	//}
	//imshow("图像像素加法操作", dst5);
}

运行截图

5. 滚动条操作演示

static void on_track(int b, void *userdata) {

	Mat image = *((Mat*)userdata);
	Mat dst = Mat::zeros(image.size(), image.type());
	Mat m = Mat::zeros(image.size(), image.type());
	m = Scalar(b, b, b);
	add(image, m, dst);
	imshow("亮度调整", dst);
}
/* 滚动条操作演示 */
void tracking_bar_demo(Mat &image) {
	namedWindow("亮度调整", WINDOW_AUTOSIZE);
	int lightness = 0;
	int max_value = 100;
	// 创建一个滚动条
	createTrackbar("Value Bar:", "亮度调整", &lightness, max_value, on_track, (void*)(&image));
	on_track(lightness, &image);
}

运行截图

6. 键盘响应操作

/* 键盘响应操作 */
void key_demo(Mat &image) {
	bool flag = true;

	Mat input;
	image.copyTo(input);

	while (flag) {
		int c = waitKey(100);	// 等待键盘输入,等待100ms
		printf("按键:%d\n", c);

		switch (c) {
			case 27: {			// Esc退出
				flag = false;
			}
			break;

			case 52: {			// 4 向左旋转

			}
			break;

			case 54: {			// 6 向右旋转

			}
			break;

			case 56: {			// 8 放大

			}
			break;

			case 50: {			// 2 缩小
	
			}
			break;

			case 48: {		// 0 复原

			}
			break;

			default:				
				break;
		}

		imshow("原图", image);
	}
}

这个键盘响应,我用来写了一个小demo,通过键盘按键来对图片进行旋转、缩放、平移、翻转和裁剪的操作。

7. OpenCV自带颜色表操作

/* OpenCV自带颜色表操作 */
void color_style_demo(Mat &image) {

	int colorMap[] = {
		COLORMAP_AUTUMN,
		COLORMAP_BONE,
		COLORMAP_CIVIDIS,
		COLORMAP_COOL,
		COLORMAP_DEEPGREEN,
		COLORMAP_HOT,
		COLORMAP_HSV,
		COLORMAP_INFERNO,
		COLORMAP_JET,
		COLORMAP_MAGMA,
		COLORMAP_OCEAN,
		COLORMAP_PARULA,
		COLORMAP_PINK,
		COLORMAP_PLASMA,
		COLORMAP_RAINBOW,
		COLORMAP_SPRING,
		COLORMAP_SUMMER,
		COLORMAP_TURBO,
		COLORMAP_TWILIGHT,
		COLORMAP_TWILIGHT_SHIFTED,
		COLORMAP_VIRIDIS,
		COLORMAP_WINTER,
		DECOMP_NORMAL
	};

	Mat dst;
	int index = 0;

	while (1) {
		int c = waitKey(1000);
		if (27 == c) {
			break;
		}

		applyColorMap(image, dst, colorMap[index % 19]);
		index++;

		imshow("Opencv自带颜色表", dst);
	}
}

数组colorMap中存储的,都是enum ColormapTypes枚举中的数据。

运行截图

8. 图像像素的逻辑操作

/* 图像像素的逻辑操作 */
void bitwise_demo(Mat &image) {

	Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
	Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
	rectangle(m1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);
	rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);
	imshow("m1", m1);
	imshow("m2", m2);
	
	// 逻辑与
	Mat dst1;
	bitwise_and(m1, m2, dst1);
	imshow("逻辑与", dst1);

	// 逻辑或
	Mat dst2;
	bitwise_or(m1, m2, dst2);
	imshow("逻辑或", dst2);

	// 逻辑非
	Mat dst3;
	bitwise_not(image, dst3);
	imshow("逻辑非", dst3);

	// 逻辑异或
	Mat dst4;
	bitwise_xor(m1, m2, dst4);
	imshow("逻辑异或", dst4);
}

运行截图

9. 通道分离与合并

/* 通道分离与合并 */
void channels_demo(Mat &image) {
	std::vector<Mat> mv;
	// 将一个多通道数组划分为多个单通道数组
	split(image, mv);
	imshow("blue", mv.at(0));
	imshow("green", mv.at(1));
	imshow("red", mv.at(2));


	Mat dst;
	//mv.at(1) = 0;
	mv.at(2) = 0;
	// 通道合并
	merge(mv, dst);
	imshow("合并通道颜色", dst);


	int from_to[] = { 0, 2, 1, 1, 2, 0 };	// 将0 和 2通道交换
	// 通道混合
	mixChannels(&image, 1, &dst, 1, from_to, 3);
	imshow("通道混合", dst);
}

运行截图

10. 图像色彩空间转换

/* 图像色彩空间转换 */
void inrange_demo(Mat &image) {
	Mat hsv;
	// 转换位hsv的色彩空间
	cvtColor(image, hsv, COLOR_BGR2HSV);
	Mat mask;
	inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);	// 将草莓扣掉

	Mat redback = Mat::zeros(image.size(), image.type());
	redback = Scalar(40, 40, 200);	// 红色背景图
	bitwise_not(mask, mask);		// 逻辑非
	imshow("mask", mask);
	image.copyTo(redback, mask);	// 将原图草莓图中按照mask里白色区域拷贝到参数一中
	imshow("roi区域提取", redback);
}

运行截图

11. 图像像素值统计 

/* 图像像素值统计 */
void pixel_statistic_demo(Mat &image) {
	double minv, maxv;
	Point minLoc, maxLoc;
	std::vector<Mat> mv;

	split(image, mv);	// 通道分离

	for (int i = 0; i < mv.size(); i++) {
		// 找出通道中的最大值和最小值
		minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat());
		printf("No.channels:%d\tminvalue:%f\tmaxvalue:%f\n", i, minv, maxv);
	}

	Mat mean, stddev;
	// 计算矩阵的均值和标准偏差
	meanStdDev(image, mean, stddev);
	std::cout << "means: " << mean << std::endl;
	std::cout << "stddev:" << stddev << std::endl;
}

运行截图

12. 图像几何形状绘制

/* 图像几何形状绘制 */
void drawing_demo(Mat &image) {
	Rect rect;
	rect.x = 100;
	rect.y = 130;
	rect.width = 200;
	rect.height = 270;


	Mat dst1 = image.clone();
	Mat dst2 = image.clone();

	// 绘制一个矩形
	rectangle(dst1, rect, Scalar(0, 255, 255), 2, 8, 0);
	// 绘制一个圆形
	circle(dst1, Point(300, 400), 30, Scalar(255, 0, 255), 2, 8, 0);
	imshow("绘制演示", dst1);

	// 填充一个矩形
	rectangle(dst2, rect, Scalar(255, 255, 0), -1, 8, 0);
	// 填充一个圆形
	circle(dst2, Point(100, 400), 30, Scalar(0, 255, 0), -1, 8, 0);
	imshow("填充演示", dst2);

	Mat dst5 = image.clone();
	// 绘制线
	line(dst5, Point(100, 130), Point(300, 400), Scalar(255, 0, 0), 2, 8, 0);
	// 绘制椭圆
	RotatedRect rrt;
	rrt.center = Point(200, 200);
	rrt.size = Size(100, 200);
	rrt.angle = 90.0;
	ellipse(dst5, rrt, Scalar(0, 0, 255), 2, 8);	// 如果需要填充,第四个参数传-1
	imshow("线和椭圆", dst5);


	Mat dst3, dst4;
	dst3 = Mat::zeros(image.size(), image.type());
	dst3 = Scalar(100, 180, 200);
	imshow("dst3", dst3);
	// 将两张相同大小,相同类型的图片融合的函数
	addWeighted(image, 0.7, dst3, 0.5, 1, dst4);
	imshow("两种图片融合", dst4);
}

运行截图

13. 随机数与随机颜色 

/* 随机数与随机颜色 */
void random_drawing_demo() {
	Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
	int w = canvas.cols;
	int h = canvas.rows;
	RNG rng((unsigned)time(NULL)); // 用系统时间作为种子初始化rng,要用到time.h库

	while (1) {
		int c = waitKey(10);
		if (27 == c) {
			break;
		}

		canvas = Scalar(0, 0, 0);

		// 获得随机值
		int x1 = rng.uniform(0, w);
		int y1 = rng.uniform(0, h);
		int x2 = rng.uniform(0, w);
		int y2 = rng.uniform(0, h);
		int b = rng.uniform(0, 255);
		int g = rng.uniform(0, 255);
		int r = rng.uniform(0, 255);
		line(canvas, Point(x1, y1), Point(x2, y2), Scalar(b, g, r), 1, LINE_AA, 0);

		imshow("随机数与随机颜色", canvas);
	}
}

运行截图

14. 多边形填充与绘制

/* 多边形填充与绘制 */
void polyline_drawing_demo() {
	Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
	Point p1(100, 100);
	Point p2(350, 120);
	Point p3(300, 350);
	Point p4(450, 500);
	Point p5(50, 200);
	std::vector<Point> pts;
	pts.emplace_back(p1);
	pts.emplace_back(p2);
	pts.emplace_back(p3);
	pts.emplace_back(p4);
	pts.emplace_back(p5);
	// 填充一个多边形
	//fillPoly(canvas, pts, Scalar(255, 0, 255), 8, 0);
	// 绘制一个多边形
	//polylines(canvas, pts, true, Scalar(0, 0, 255), 2, LINE_AA, 0);

	std::vector<std::vector<Point>> contours;
	contours.push_back(pts);
	// 绘制多边形或者填充多边形,参数三指定绘制容器中指定数据的第几个(-1表示全都绘制),最后一个参数为多边形的边框(-1表示填充)
	drawContours(canvas, contours, 0, Scalar(255, 0, 0), -1);
	imshow("多边形填充与绘制",  canvas);
}

运行截图

15. 鼠标操作与响应

Point sp(-1, -1);
Point ep(-1, -1);
Mat temp;
static void on_draw(int event, int x, int y, int flags, void *userdata) {
	Mat image = *((Mat*)userdata);
	if (event == EVENT_LBUTTONDOWN) {	// 鼠标左键按下
		sp.x = x;	// 获取鼠标坐下按下后的位置坐标
		sp.y = y;
		std::cout << "start point:" << sp << std::endl;
	
	} else if (event == EVENT_LBUTTONUP) {
		ep.x = x;	// 获得鼠标松开后的位置坐标
		ep.y = y;
		// 相减得出长和宽
		int dx = ep.x - sp.x;
		int dy = ep.y - sp.y;
		if (dx > 0 && dy > 0 && ep.y < image.size().height && ep.x < image.size().width) {
			Rect box(sp.x, sp.y, dx, dy);
			rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);	// 绘制矩形
			imshow("鼠标绘制", image);
			imshow("ROI区域提取", temp(box));	// 提取矩形中的图像
			
		} else {
			temp.copyTo(image);
			imshow("鼠标绘制", image);
		}
		sp.x = -1;
		sp.y = -1;
	
	} else if (event == EVENT_MOUSEMOVE) {
		if (sp.x > 0 && sp.y > 0) {
			ep.x = x;
			ep.y = y;
			int dx = ep.x - sp.x;
			int dy = ep.y - sp.y;
			if (dx > 0 && dy > 0) {
				temp.copyTo(image);
				Rect box(sp.x, sp.y, dx, dy);
				rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
				imshow("鼠标绘制", image);
			}
		}
	}
}
/* 鼠标操作与响应 */
void mouse_drawing_demo(Mat &image) {
	namedWindow("鼠标绘制", WINDOW_AUTOSIZE);
	// 设置鼠标回调
	setMouseCallback("鼠标绘制", on_draw, (void*)(&image));
	imshow("鼠标绘制", image);
	temp = image.clone();
}

运行截图

16. 图像像素类型转换与归一化

/* 图像像素类型转换与归一化 */
void norm_demo(Mat &image) {
	Mat dst;
	std::cout << image.type() << std::endl;
	image.convertTo(image, CV_32F);
	std::cout << image.type() << std::endl;
	normalize(image, dst, 1.0, 0, NORM_MINMAX);
	std::cout << dst.type() << std::endl;
	imshow("图像数据归一化", dst);
}

运行截图

17. 图像缩放与插值

/* 图像缩放与插值 */
void resize_demo(Mat &image) {
	Mat zoomin, zoomout;
	int h = image.rows;
	int w = image.cols;
	resize(image, zoomin, Size(w / 2, h / 2), 0, 0, INTER_LINEAR);
	imshow("zoomin", zoomin);
	resize(image, zoomout, Size(w*1.5, h*1.5), 0, 0, INTER_LINEAR);
	imshow("zoomout", zoomout);
}

运行截图

18. 图像翻转

/* 图像翻转 */
void flip_demo(Mat &image) {
	Mat dst;

	// 上下翻转
	flip(image, dst, 0);

	// 左右翻转
	//flip(image, dst, 1);

	// 先左右翻转再上下翻转
	//flip(image, dst, -1);

	imshow("图像翻转", dst);
}

运行截图

19. 图像旋转

/* 图像旋转 */
void rotate_demo(Mat &image) {
	Mat dst, M;

	int w = image.cols;
	int h = image.rows;
	M = getRotationMatrix2D(Point2f(w / 2, h / 2), -45, 1.0);

	// 计算旋转后需要多大的窗体才能完全装下旋转后的图像
	double cos = abs(M.at<double>(0, 0));
	double sin = abs(M.at<double>(0, 1));
	int nw = cos * w + sin * h;
	int nh = sin * w + cos * h;
	M.at<double>(0, 2) += (nw / 2 - w / 2);
	M.at<double>(1, 2) += (nh / 2 - h / 2);

	warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 0, 0));

	imshow("图像旋转", dst);
}

运行截图

20. 视频文件摄像头使用

/* 视频文件摄像头使用 */
void video_demo() {
	// VideoCapture capture(0);		// 参数是0,就是读取电脑摄像头
	VideoCapture capture("E:\\Code\\vs2017Code\\OpencvTest2\\OpencvTest2\\win10.mp4");	
	
	// 视频宽
	int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);
	// 视频高
	int frame_hegiht = capture.get(CAP_PROP_FRAME_HEIGHT);
	// 视频总帧率
	int count = capture.get(CAP_PROP_FRAME_COUNT);
	// 视频每秒多少fps(每秒多少帧)
	double fps = capture.get(CAP_PROP_FPS);
	std::cout << "froma_width:" << frame_width << std::endl;
	std::cout << "frame_hegiht:" << frame_hegiht << std::endl;
	std::cout << "count:" << count << std::endl;
	std::cout << "fps:" << fps << std::endl;

	// 定义视频保存对象
	VideoWriter writer("D://test.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_hegiht), true);
	
	Mat frame;
	while (1) {
		capture.read(frame);	// 读取画面
		//flip(frame, frame, 1);	// 左右翻转,读取电脑摄像头时需要翻转,视频不用
		if (frame.empty()) {
			break;
		}
		imshow("frame", frame);

		// 保存视频
		writer.write(frame);

		int c = waitKey(10);
		if (27 == c) {
			break;
		}
	}

	// 释放
	capture.release();
	writer.release();
}

运行截图

21. 图像卷积操作

/* 图像卷积操作 */
void blur_demo(Mat &image) {
	Mat dst;
	blur(image, dst, Size(1, 1), Point(-1, -1));    // // 设置参数三的数值即可
	imshow("图像模糊", dst);    
}

运行截图

22. 高斯模糊 

/* 高斯模糊 */
void gaussian_blur_demo(Mat &image) {
	Mat dst;
	GaussianBlur(image, dst, Size(0, 0), 5);	// 参数三默认0即可,设置参数四实现模糊
	imshow("高斯模糊", dst);
}

运行截图

23. 高斯双边模糊

/* 高斯双边模糊 */
void bifilter_demo(Mat &image) {
	Mat dst;
	bilateralFilter(image, dst, 0, 100, 10);
	imshow("双边模糊", dst);
}

运行截图


三、键盘操作小demo

1. 键盘小demo

对图片操作,旋转,缩放,平移,翻转,裁剪的操作。

#include <opencv2/opencv.hpp>

using namespace cv;


void imageOperate1(Mat &image, Mat input, double angle, double scale, int xOffset, int yOffset, int startRow, int &endRow, int startCol, int &endCol, bool up_down, bool left_right) {

	input.copyTo(image);


	// 图像平移
	Size dst_sz = image.size();
	//定义平移矩阵
	Mat t_mat = Mat::zeros(2, 3, CV_32FC1);
	t_mat.at<float>(0, 0) = 1;
	t_mat.at<float>(0, 2) = xOffset; //水平平移量
	t_mat.at<float>(1, 1) = 1;
	t_mat.at<float>(1, 2) = yOffset; //竖直平移量
	//根据平移矩阵进行仿射变换
	warpAffine(image, image, t_mat, dst_sz);

	// 旋转图像
	Point2f center((image.cols - 1) / 2.0, (image.rows - 1) / 2.0);	// 图像中心位置
	Mat rotation_matix = getRotationMatrix2D(center, angle, 1.0);
	warpAffine(image, image, rotation_matix, image.size());

	// 缩放图像
	int width = int(input.cols * scale);
	int height = int(input.rows * scale);
	resize(image, image, cv::Size(width, height), 0, 0, INTER_LINEAR);


	if (0 == endRow) {		// 发生了缩放操作
		endRow = height;
		endCol = width;
	}
	// 裁剪图像	
	image = image(Range(startRow, endRow), Range(startCol, endCol));

	// 上下翻转
	if (up_down) flip(image, image, 0);

	// 左右翻转
	if (left_right) flip(image, image, 1);
}
/* 键盘响应操作 */
void key_demo1(Mat &image) {
	bool flag = true;
	double scale = 1.0;		// 缩放比例
	double angle = 0;		// 旋转角度
	int xOffset = 0;		// 水平平移量
	int yOffset = 0;		// 竖直平移量
	int startRow = 0;					// 裁剪起始行
	int endRow = image.size().height;	// 裁剪结束行
	int startCol = 0;					// 裁剪起始列
	int endCol = image.size().width;	// 才接结束列
	bool up_down = false;				// 上下翻转
	bool left_right = false;			// 左右翻转

	Mat input;
	image.copyTo(input);

	while (flag) {
		int c = waitKey(100);	// 等待键盘输入,等待100ms
		printf("按键:%d\n", c);

		switch (c) {
			case 27:
			{			// Esc退出
				flag = false;
			}
			break;

			case 52:
			{			// 4 向左旋转
				angle += 5;

				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 54:
			{			// 6 向右旋转
				angle -= 5;

				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 56:
			{			// 8 放大

				scale += 0.1;
				if (scale <= 2.05) {	// 设置最大放大是2
					endRow = 0;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					scale = 2.0;
				}

			}
			break;

			case 50:
			{			// 2 缩小

				scale -= 0.1;
				if (scale >= 0.15) {	// 最小缩小是0.2
					endRow = 0;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					scale = 0.2;
				}

			}
			break;

			case 87:
			case 119:
			{		// wW 图片上移

				yOffset -= 2;

				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 83:
			case 115:
			{		// sS 图片下移

				yOffset += 2;

				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 65:
			case 97:
			{		// aA 图片左移
				xOffset -= 2;

				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 68:
			case 100:
			{		// dD 图片右移
				xOffset += 2;

				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 73:
			{		// I 图片上边复原
				if (startRow > 0) {
					startRow -= 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					startRow = 0;
				}

			}
			break;

			case 105:
			{		// i 图片上边裁剪

				if (startRow < endRow - 1) {
					startRow += 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					startRow = endRow - 1;
				}
			}
			break;

			case 75:
			{		// K 图片下边复原
				if (endRow < input.size().height) {
					endRow += 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					endRow = input.size().height;
				}
			}
			break;

			case 107:
			{		// k 图片下边裁剪
				if (endRow > startRow + 1) {
					endRow -= 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					endRow = startRow + 1;
				}
			}
			break;

			case 74:
			{		// J 图片左边恢复
				if (startCol > 0) {
					startCol -= 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					startCol = 0;
				}
			}
			break;

			case 106:
			{		// j 图片左边裁剪
				if (startCol < endCol - 1) {
					startCol += 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					startCol = endCol - 1;
				}
			}
			break;

			case 76:
			{		// L 图片右边复原
				if (endCol < input.size().width) {
					endCol += 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					endCol = input.size().width;
				}
			}
			break;

			case 108:
			{		// l 图片右边裁剪
				if (endCol > startCol + 1) {
					endCol -= 1;

					imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);

				} else {
					endCol = startCol + 1;
				}
			}
			break;

			case 43:
			{		// + 上下翻转
				up_down = !up_down;
				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 45:
			{		//  - 左右翻转
				left_right = !left_right;
				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			case 48:
			{		// 0 复原
				scale = 1.0;					// 缩放比例
				angle = 0;						// 旋转角度
				xOffset = 0;					// 水平平移量
				yOffset = 0;					// 竖直平移量
				startRow = 0;					// 裁剪起始行
				endRow = input.size().height;	// 裁剪结束行
				startCol = 0;					// 裁剪起始列
				endCol = input.size().width;	// 才接结束列
				up_down = false;				// 上下翻转
				left_right = false;				// 左右翻转
				imageOperate1(image, input, angle, scale, xOffset, yOffset, startRow, endRow, startCol, endCol, up_down, left_right);
			}
			break;

			default:
			break;
		}

		imshow("原图", image);
	}
}


int main(void) {

	Mat image = imread("图.jpeg", IMREAD_COLOR);
	if (image.empty()) {
		printf("path image NULL!\n");
		return -1;
	}

	imshow("原图", image);

	key_demo1(image);

	waitKey(0);
	destroyAllWindows();

	return 0;
}

运行截图

2. 旋转、缩放、裁剪、平移案例

#include <stdio.h>
#include <Windows.h>
#include "opencv2/opencv.hpp"

using namespace cv;


// 图片旋转
Mat imageRotate(Mat input, double angle) {
	angle *= -1;

	// get the center coordinates of the image to create the 2D rotation matrix
	Point2f center((input.cols - 1) / 2.0, (input.rows - 1) / 2.0);
	// using getRotationMatrix2D() to get the rotation matrix
	Mat rotation_matix = getRotationMatrix2D(center, angle, 1.0);

	Mat newImage;
	warpAffine(input, newImage, rotation_matix, input.size());
	//namedWindow("测试图片旋转", WINDOW_FREERATIO);
	//imshow("测试图片旋转", newImage);
	imshow("测试显示图片", newImage);

	return newImage;
}


// 图片缩放
Mat imageZoom(Mat input, float scaleW, float scaleH) {

	//float scaleW = 0.5; // 定义新图像的大小,宽度缩小到80%
	//float scaleH = 0.5;  //定义新图像的大小,高度缩小到80%
	int width = int(input.cols * scaleW);
	//定义想要扩大或者缩小后的宽度,src.cols为原图像的宽度,乘以80%则得到想要的大小,并强制转换成int型
	int height = int(input.rows * scaleH);
	//定义想要扩大或者缩小后的高度,src.cols为原图像的高度,乘以80%则得到想要的大小,并强制转换成int型
	cv::Mat dst;
	resize(input, dst, cv::Size(width, height));//缩放图像

	//namedWindow("测试图片缩放", WINDOW_FREERATIO);
	//imshow("测试图片缩放", dst);
	imshow("测试显示图片", dst);

	return dst;
}


// 图片裁剪
Mat imageCut(Mat input, int startRow, int endRow, int startCol, int endCol) {

	Mat cropped_image = input(Range(startRow, endRow), Range(startCol, endCol));
	//namedWindow("测试裁剪图片", WINDOW_FREERATIO);
	//imshow("测试裁剪图片", cropped_image);
	imshow("测试显示图片", cropped_image);

	return cropped_image;
}


// 图像平移
Mat imageTranslation(Mat srcImage, int xOffset, int yOffset)
{
	Size dst_sz = srcImage.size();
	//定义平移矩阵
	Mat t_mat = Mat::zeros(2, 3, CV_32FC1);
	t_mat.at<float>(0, 0) = 1;
	t_mat.at<float>(0, 2) = xOffset; //水平平移量
	t_mat.at<float>(1, 1) = 1;
	t_mat.at<float>(1, 2) = yOffset; //竖直平移量
	//根据平移矩阵进行仿射变换
	Mat TranslationMat;
	warpAffine(srcImage, TranslationMat, t_mat, dst_sz);

	//namedWindow("测试平移图片", WINDOW_FREERATIO);
	//imshow("测试平移图片", TranslationMat);
	imshow("测试显示图片", TranslationMat);

	return TranslationMat;
}


int main(void) {

	Mat image =imread("123.png");
	//namedWindow("测试显示图片", WINDOW_FREERATIO);	// WINDOW_FREERATIO:可以手动调节大小
	//imshow("测试显示图片", image);
	//waitKey(0);
	

	double angle = 120;
	Mat imgRotate = imageRotate(image, angle);

	waitKey(0);

	float scale = 0.5;
	Mat imgZoom = imageZoom(imgRotate, scale, scale);

	waitKey(0);

	Mat imgCut = imageCut(imgZoom, 50, 80, 200, 300);

	waitKey(0);

	imageTranslation(imgCut, 10, 20);

	waitKey(0);
	destroyAllWindows();

	return 0;
}

3. 往图片中写入中文文字

记录一个类型转换:

    // char* 转 wchar_t*
    wchar_t wcha[255] = { 0 };
    MultiByteToWideChar(CP_ACP, 0, text, strlen(text), wcha, 255);

putTextCN.h

#pragma once
#include <windows.h>
#include <string>
#include <opencv2/opencv.hpp>

using namespace cv;

// 内部调用
static void GetStringSize(HDC hDC, const char* str, int* w, int* h);
// 内部调用
static void putTextZH(Mat dst, const char* str, Point org, Scalar color, int fontSize,
			   const char *fn, bool italic, bool underline, bool lfWeight);

// 外部调用,往图片中写入文字
extern void putTextZh(InputOutputArray     img,
			   const char *text,
			   Point     org,
			   int     fontSize,
			   Scalar     color,
			   const char *font = "Arial",
			   bool italic = false,
			   bool underline = false,
			   bool lfWeight = false);

putTextCN.cpp

#include "putTextCN.h"

static void GetStringSize(HDC hDC, const char* str, int* w, int* h) {
	SIZE size;
	GetTextExtentPoint32A(hDC, str, strlen(str), &size);
	if (w != 0) *w = size.cx;
	if (h != 0) *h = size.cy;
}

static void putTextZH(Mat dst, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline, bool lfWeight) {
	CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));

	int x, y, r, b;
	if (org.x > dst.cols || org.y > dst.rows) return;
	x = org.x < 0 ? -org.x : 0;
	y = org.y < 0 ? -org.y : 0;

	LOGFONTA lf;
	lf.lfHeight = -fontSize;
	lf.lfWidth = 0;
	lf.lfEscapement = 0;
	lf.lfOrientation = 0;
	if (true == lfWeight) {    // 粗体
		lf.lfWeight = 700;
	} else {
		lf.lfWeight = 400;
	}	
	lf.lfItalic = italic;       // 斜体
	lf.lfUnderline = underline; // 下划线
	lf.lfStrikeOut = 0;
	lf.lfCharSet = DEFAULT_CHARSET;
	lf.lfOutPrecision = 0;
	lf.lfClipPrecision = 0;
	lf.lfQuality = PROOF_QUALITY;
	lf.lfPitchAndFamily = 0;
	strcpy_s(lf.lfFaceName, fn);

	HFONT hf = CreateFontIndirectA(&lf);
	HDC hDC = CreateCompatibleDC(0);
	HFONT hOldFont = (HFONT)SelectObject(hDC, hf);

	int strBaseW = 0, strBaseH = 0;
	int singleRow = 0;
	char buf[1 << 12];
	strcpy_s(buf, str);
	char *bufT[1 << 12];  // 这个用于分隔字符串后剩余的字符,可能会超出。
	//处理多行
	{
		int nnh = 0;
		int cw, ch;

		const char* ln = strtok_s(buf, "\n", bufT);
		while (ln != 0) {
			GetStringSize(hDC, ln, &cw, &ch);
			strBaseW = max(strBaseW, cw);
			strBaseH = max(strBaseH, ch);

			ln = strtok_s(0, "\n", bufT);
			nnh++;
		}
		singleRow = strBaseH;
		strBaseH *= nnh;
	}

	if (org.x + strBaseW < 0 || org.y + strBaseH < 0) {
		SelectObject(hDC, hOldFont);
		DeleteObject(hf);
		DeleteObject(hDC);
		return;
	}

	r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
	b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
	org.x = org.x < 0 ? 0 : org.x;
	org.y = org.y < 0 ? 0 : org.y;

	BITMAPINFO bmp = { 0 };
	BITMAPINFOHEADER& bih = bmp.bmiHeader;
	int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));

	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biWidth = strBaseW;
	bih.biHeight = strBaseH;
	bih.biPlanes = 1;
	bih.biBitCount = 24;
	bih.biCompression = BI_RGB;
	bih.biSizeImage = strBaseH * strDrawLineStep;
	bih.biClrUsed = 0;
	bih.biClrImportant = 0;

	void* pDibData = 0;
	HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);

	CV_Assert(pDibData != 0);
	HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);

	//color.val[2], color.val[1], color.val[0]
	SetTextColor(hDC, RGB(255, 255, 255));
	SetBkColor(hDC, 0);
	//SetStretchBltMode(hDC, COLORONCOLOR);

	strcpy_s(buf, str);
	const char* ln = strtok_s(buf, "\n", bufT);
	int outTextY = 0;
	while (ln != 0) {
		TextOutA(hDC, 0, outTextY, ln, strlen(ln));
		outTextY += singleRow;
		ln = strtok_s(0, "\n", bufT);
	}
	uchar* dstData = (uchar*)dst.data;
	int dstStep = dst.step / sizeof(dstData[0]);
	unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
	unsigned char* pStr = (unsigned char*)pDibData + x * 3;
	for (int tty = y; tty <= b; ++tty) {
		unsigned char* subImg = pImg + (tty - y) * dstStep;
		unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
		for (int ttx = x; ttx <= r; ++ttx) {
			for (int n = 0; n < dst.channels(); ++n) {
				double vtxt = subStr[n] / 255.0;
				int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
				subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
			}

			subStr += 3;
			subImg += dst.channels();
		}
	}

	SelectObject(hDC, hOldBmp);
	SelectObject(hDC, hOldFont);
	DeleteObject(hf);
	DeleteObject(hBmp);
	DeleteDC(hDC);
}

extern void putTextZh(InputOutputArray     img,
			   const char *text,
			   Point     org,
			   int     fontSize,
			   Scalar     color,
			   const char  *font,
			   bool italic,
			   bool underline, 
			   bool lfWeight) {

	Mat src = img.getMat();

	putTextZH(src, text, org, color, fontSize, font, italic, underline, lfWeight);
}

main.cpp

#include "putTextCN.h"

/* 绘制文字 */
void putText_demo(Mat &image, char *text) {

	putTextZh(image, text, Point(150, 50), 20, Scalar(0, 0, 0), "宋体");

	imshow("绘制文字", image);
}

int main(void) {

	// 正常:IMREAD_COLOR
	// 灰度:IMREAD_GRAYSCALE
	Mat image = imread("图.jpeg", IMREAD_COLOR);
	if (image.empty()) {
		printf("path image NULL!\n");
		return -1;
	}

	imshow("原图", image);

	char str[] = "测试OpenCV绘制文字";
	putText_demo(image, str);

	waitKey(0);
	destroyAllWindows();

	return 0;
}

运行截图


四、总结

opencv对图片一些简单的操作,我也就只会这些简单的了,也是在B站看视频学习后记录下来的代码,具体其他高深的的操作就不会了!

opencv-c++ 类型说明Point二维点类Point3三维点类Rect矩形类旋转矩形类Range范围类Scalar通道类Size规格类。 阅读详情

相关推荐

VS2017安装及配置OpenCV教程

vs2017安装教程,以及应用C++如何在在VS2017中创建第一个程序,如何创建类,并且如何在主程序中调用类;OpenCV的下载和环境配置教程,以及vs2017中OpenCV配置教程,以及读取图片的程序;

xp_fangfei的博客 1039

计算机视觉与OpenCV算法实战(C++/Python双语教学)

课程目的:OpenCV是应用非常广泛的开源视觉处理库,在图像处理、计算机视觉和自动驾驶中有着非常重要的作用。课程设计特色:(课程当前为第一期)1、C++与Python双语教学Python语言是在计算机视觉中应用最多的一种语言,在工作中,深度学习模型的训练基本上都是使用Python语言编写的训练代码。OpenCV在这个过程中用于图像的预处理(例如图像读取、数据增强)和后处理,还可以用于显示处理的结果,功能强大,使用方便。但是在功能的部署的时候,不管是部署在服务端还是PC端,开发语言基本上用的是C++,所以如何有效的使用OpenCV进行模型或者功能的部署尤为重要。C++语言应用的好坏,在面试中可以看出一个面试者的工程实践能力的强弱,两种语言的开发掌握好了可以使工作如虎添翼。2、全模块讲解我出版了一本图书《学习OpenCV4:基于Python的算法实战》,虽然这本书是写的基于Python的算法实战,但是实际上这本书有详细的介绍算法的C++接口,还有一些C++方向的案例,是以Python为主。图书出版的时候就想双语写作,只是限于篇幅没有成行。本课程不仅采用双语教学,更是对C++的每个模块都做讲解,我们知道,很多的书其实只讲imgproc,如果你翻开一本书图像的形态学运算和图像滤波都是作为独立章节讲解的,那么这本书基本上就可以确定是只是讲解了imgproc模块,但是其他的模块在工作中也有很重要的作用。例如:core模块定义了C++的基本数据结构和基本运算(如四则运算);highgui模块是可视化与交互的模块;feature2d是特征点与特征匹配相关算法所在的模块;ml是机器学习相关的模块;dnn是深度学习相关的模块,可以使用OpenCV进行深度学习模型的部署。这些是很多的书和课程都不会讲的。3、讲解细致本课程会从环境搭建开始讲解,环境搭建尤为重要。从我多年的授课经验总结来看,如果只是给了代码,很多的入门用户环境问题处理不好的话,后面的学习很难进行下去,甚至会丧失学习的信心。4、会讲解C++和Python的开发语法问题是入门用户的一大难关,特别是C++语言。大学只是教授了C语言相关的内容,C++很多同学只懂一点皮毛,所以写代码步履维艰,我们在讲解代码的过程中会顺带讲解C++和Python的内容。我们还会讲解编译相关知识,还有库的装载与链接,这些是学校里不会教的,目前也几乎没有课程讲解。5、讲师经验丰富我讲解过C++和OpenCV的多个课程,广受学员好评。我出版过两本图书《深度学习计算机视觉实战》和《学习OpenCV4》,两本书都是细致入微的讲解,主要针对的就是初学者,所以能够很好的处理课程的难易程度。6、讲义准备充分讲义准备的充分细致,标识清楚明确,重点和疑难点突出。

VS2017+opencv4.2.0环境搭建详细步骤图解

一、下载VS 2017 下载地址 https://docs.microsoft.com/en-us/visualstudio/releasenotes/vs2017-relnotes 点击下载Visual Studio 2017,然后需要登录,没有账号的话就创建一个。 登录之后,选择社区版(Community)下载,因为这个才不用钱。 点击该安装程序,就可以进行安装啦。 二、安装VS 201...

Janway-Win 2108

C++ opencv操作入门教程

C++ opencv操作入门教程

qq_46017342的博客 8052

在VS2017(VC15)上配置opencv4.0.1环境

作为萌新最初听说到opencv,以为是一个集成开发环境IDE。后来因为毕设原因不得不去使用opencv,才了解到OpenCV是一个广受欢迎的开源计算机视觉库 ,它提供了很多函数,实现很多计算机视觉算法,从最基本的滤波到高级物体检测皆有涵盖。换言之,opencv只不过是一个写好的库,方便在编程的时候对视觉数据进行处理。 网上有各式各样的配置方式,但是大部分都没说每一步做法的原因 ,导致opencv...

GoldenBullet的博客 3382

【C++】OpenCV:计算机视觉库介绍及C++环境配置

计算机视觉库介绍及C++环境配置

DevFrank的博客 1万+

VS2017配置opencv教程(超详细!!!)

前言:刚开始是不打算写这篇博客的,但是我最近为了完成对老师布置的区域生长算法,强行要配置一波opencv,因为换了电脑,所以选择了有黑黑主题酷酷的VS2017,但无奈网上的博客关于vs2017配置Opencv不够全(vs2010配置opencv的倒是贼多!),我当时是看了将近10篇文章才配置成功,所以在此我来从一个新入坑的角度来说一下怎么完整的配置一个OPencv!下面就分步来进行说明吧! ...

Chosen One 36万+

Box2D-Lite源码阅读笔记(2)

0.初始化函数 static void InitDemo(int index) { world.Clear(); numBodies = 0; numJoints = 0; bomb = NULL; demoIndex = index; demos[index](bodies, joints); } 在这个初始化函数中,一般会默认执行index=0的函数,这个函数就是一个示例,因为示例比较简单,所以也便于研究,示例效果如下。 就是一个方块。 进入第一个示例函数。(忽略我加的注释)

ARTELE的博客 975

VS code配置C/C++、OpenCV(Windows)

目录一、前言二、编译器、编辑器、链接器与IDE2.1编译器2.2编辑器2.3链接器2.4IDE三、配置环境四、VScode搭建C/C++环境4.1安装VScode+Mingw644.2添加扩展工具4.3C/C++编译与调试五、OpenCV+cmake配置六、测试 一、前言 由于今年疫情影响,很多计划内的事情都泡汤了,鉴于下学期找工作需要,决定自己在家学习C/C++、OpenCV,刷刷题。这时候找一...

CleMints的博客 2万+

python监听海康威视报警服务器

因为我使用海康威视摄像头是自带热成像的所以事件报警会上报两张图片,一张是RGB图像、另一张是热成像图像,因此使用。拿到的就是第一张RGB图像。可以点击测试,会显示。

qq_57018478的博客 1157

SAP BASIS运维之三方SSL证书导入

事情的起因是我司SAP使用的SSL证书即将要到期了(上一个的SAP BASIS也辞职了),需要进行证书的更换。如上图所示,我司的证书有效期到2024年4月30日到期,需要提前预防并更换,防止过期后导致接口和访问出现问题。9.在弹出的框中,选择我们在第5个步骤中导出的转换结果存储地址,并选择导出的转换文件,点打开。12.最后一步也是关键的一步,不然就会无法更新证书,再点击左上角的个人安全环境,点另存为。10.在打开后的弹窗中,输入我们在第4个步骤中设置的密码。6.经过以上步骤后,我们获得了最终的证书文件。

fegda的专栏 1882

VS2017配置OpenCV3.4.1教程(步骤超详细!!)

前言:本学期选了一门软件课设---基于OpenCV的图形处理程序(已经完成了课设任务,出几篇博客记录一下学习历程)。首先第一步就是VS配置OpenCV(VS2017是一直以来用的编程软件),在实验课上老师也教了但是也试了好几遍都没有配置成功。课下自己在找各种配置方法看了大概有10篇左右的博客,在踩了很多坑情况下花了一下午终于配置成功了。 说明:不是博主这个版本的opencv本教程也是实用的...

烬 1万+

AI 大模型应用数据中心的数据清洗工具

1. 背景介绍 在人工智能大模型应用的浪潮中,数据清洗作为数据预处理的重要环节,对于提升模型性能和可靠性具有至关重要的作用。数据中心作为人工智能模型的运行环境,面临着海量数据流和多样化的数据类型,如何高效、准确地进行数据清洗,成为应用大模型的关键问题之一。本文将详细介绍AI大模型应用数据中心的数据清洗工具,包括核心概念、算法原理、具体操作步骤、应用场

SuperAGI2025 1803

如何在VS2017中配置OpenCV

如何在VS2017配置OpenCV 步骤共分为一下5步: 下载OpenCV SDK 与安装 安装VS2015或者VS2017 1、配置包含目录 2、配置库目录 3、配置链接器 4、配置环境变量 5、重启VS2015或VS2017 下载安装OpenCV SDK: 进入OpenCV官网, 点击左上角Library,选择相应的版本下载,作者下载的是OpenCV-3.4.16 Windows版。 下载完成后运行,将文件解压, 建议解压在除C盘外的其他盘,得到opencv文件夹,我们主要利用build目录下的文件

weixin_43623082的博客 9452

uniapp - 解决真机调试报错 request:fail abort statusCode:-1 java.security.cert.CertPathValidatorException: Tr

uni-app,真机调试报错,app端平台,安卓Android,安卓App发布线上报错,打包后接口请求失效了,api接口报错,真机运行安卓app时接口不通且报错,request:failabortstatusCode:-1java.security.cert.CertPathValidatorException:Trustanchorforcertificationpathnotfound.,真机调试,苹果ios真机报错,Uniapp打包到app后报错,uni-app真机调试报错,服务器

🏆 前端领域优质创作者 1万+

从码农到生态建造者:我的一人公司运作方案

华哥·OPC 运营方案 v1.0 - 第一次迭代 🚀,搭建闭环生态系统,赋能 AI 时代的超级个体,更好的做自己,同时照亮别人。

AaronZZH 的博客 994

[opencv +VS2017] opencv、vs2017安装配置,环境搭建

文章目录[opencv] 4.0.1下载安装一、前言二、说明三、下载opencv3.1 获取链接3.2 下载结果四、安装opencv五、配置opencv环境六、vs20176.1 说明 [opencv] 4.0.1下载安装 一、前言       最近有个需求需要用到opencv,奈何过招时,学艺不精,没做好准备,先使用python +op...

贾继康的博客 4421

第十四节《OpenCascade集成Qt》

将 OpenCascade 与 Qt 集成是开发图形界面应用的常见需求。通过将 OpenCascade 的几何建模与 Qt 的图形用户界面(GUI)功能结合,可以创建强大而灵活的 CAD 或建模工具。OpenCascade 提供了与 Qt 的集成接口,允许你在 Qt 应用程序中嵌入 OpenCascade 的视图,执行几何操作,并与用户进行交互。以下是 OpenCascade 与 Qt 集成的详细步骤:首先,确保已安装 OpenCascade 和 Qt。你可以从 OpenCascade 官方网站或通过包管理

yumeiguo的博客 597

VS2017逐步配置OpenCV教程

VS2017逐步配置OpenCV教程 VS2017逐步配置OpenCV教程VS2017逐步配置OpenCV教程一、配置环境二、下载OpenCV安装包1.OpenCV官网下载2.百度网盘下载OpenCV安装包二、解压并安装OpenCV1.选择路径2.配置系统环境变量3.文件转移与系统配置三、VS2017内部配置四、测试 众所周知,OpenCV框架最初出现在C++中,虽然现在python已经封装了大部分opencv的功能包,博主也在python中尝到了配置OpenCV和平时使用的甜头,但是部分应用场景下基于p

CH_monsy的博客 2935

DW3000芯片驱动API介绍

详细介绍DW3000驱动程序API的调用流程说明和中断处理程序流程说明

DT程序员的博客 1518

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

手把手教你用VS2017配置OpenCV

最近学习增强现实技术时需要opencv,早在本科毕设使用过OpenCV 2.4.11做过城市交通指示识别,现在导师要求用OpenCV 3,所以从一个新入坑的角度来讲解一下完整的配置一个OpenCV的过程。

极客园地 6643

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV

OpenCV是大型的Third party 计算机视觉库,在开发中会经常用到,本篇记录一下 在Ubuntu系统上安装和配置OpenCV,并使用C/C++调用OpenCV。前面进行cmake时,已经生成了opencv4.pc配置文件,该文件的安装路径为:/usr/local/lib/pkgconfig/opencv4.pc。我们需要将OpenCV的libs的安装目录添加到动态库加载配置文件中,这样在编译时,才能找到OpenCV的各种库文件,下面创建opencv4.conf。

支持一对一答疑的编程作家朱文伟的博客! 4920

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

使用VS2017和Qt编译OpenCV

作者:billy 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 前言 因为最近项目中用到了工业相机作业,涉及到图像处理无疑就要用到 OpenCV 了,此章节就记录了 OpenCV 从源码到编译,再到应用的过程。 开发环境:Qt 5.12.8, vs2017 Community 软件版本:opencv-4.2.0-vc14_vc15,cmake-3.17.1-win6...

billy的博客 3427

vs2017python配置opencv_[opencv +VS2017] opencv、vs2017安装配置,环境搭建

文章目录[opencv] 3.4.1下载安装一、前言二、说明三、下载opencv3.1 获取链接3.2 下载结果四、安装opencv五、配置opencv环境六、vs20176.1下载6.2 安装6.3 VS2017配置opencv6.3.1 相关文件的配置6.3.2 运行vs20176.3.3 新建空项目1.0 步骤一2.0 步骤二3.0 步骤三6.3.4 进入VS2017配置属性管理器进行相关配...

weixin_30978387的博客 820

OpenCv+VS2017图像处理入门(一)

安装VS2017 Visual Studio官网下载社区版VS2017,安装教程照着网上做即可。 安装OpenCv 官网下载opencv3.3.0 vc14.exe 3.配置OpenCv环境 (1) 计算机环境变量的配置 计算机->系统属性->高级系统设置->高级->环境变量->path->编辑 在下面的窗口中点击”新建“,蓝色区域就是点击新建后输入的内容 D:\softwar

qq_38397581的博客 3万+

vs2017配置cuda和opencv环境

vs2017配置opencv环境网上的资料比较多也比较全,这里就不重点描述了。不过还是贴一个写的比较详细,图文并茂的博客。如下(建议采用临时配置方法,也就是下面博客中的方法二): VS2017配置OpenCV4.0(Win10环境) vs2017配置cuda的资料相对较少,这里重点描述。 首先说碰到的情况。我这里碰到了两种情况:第一种,先装cuda后装vs2017。这样的安装的顺序,一般情况...

SeventhBlue 3688
上一篇: C/C++ plog日志简单用法
下一篇: C/C++ 动态规划 算法
cpp_learners
博客等级 码龄7年 1526粉丝 274原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpp_learners

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值