OpenCV(c++) 笔记二:图像的线性混合、亮度与对比度、绘制形状和文字

本博客为自己学习时候的笔记,并不深究各API的具体原理及类似API的区别与联系,从实用角度进行整理,方便自己的回忆与复习。课程内容均来自B站。https://www.bilibili.com/video/av29600072

图像的线性混合

线性混合可以由字面意思得出,它是像素点经过线性变化后得出的新的点。来自视频配套PPT
只有两张大小相同、类型相同的图片才可以相互混合!

	double alpha = 0.5;
	if (src1.rows == src2.rows && src1.cols == src2.cols && src1.type() == src2.type()) {
		addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);
		//multiply(src1, src2, dst, 1.0);
		imshow("blend demo", dst);
	}
	else {
		cout << "could not blend images..." << endl;
		return -1;
	}

其中,src1和src2分别是两张要被混合的图像,通过调用addWeighted函数来实现图像的线性混合。其中addWeithted函数的具体参数如下:addWeighted(图片1,图片1的alpha值,图片2,图片2的alpha值,gamma值,混合后的图像)。通过改变不同的alpha和gamma的值,可以改变某张图片的主要程度,如各位0.5则是正好相互融合的图像。
也有其他的方法可以进行图像的混合修改,效果各异,可以根据需要进行选择。如multiply(src1, src2, dst, 1.0);
当最后一个值为0.02和2的时候图像效果分别如下(高能预警图片可能引起不适&&智贤女神我对不起你):


在这里插入图片描述

在这里插入图片描述


亮度与对比度

来自视频配套PPT
一张图片越亮,它与白色越接近,也就是BGR的值越高。通过改变alpha和beta的参数,就可以模拟亮度的改变。而对比度则是黑白度对比更为强烈的体现。

	int height = src.rows;
	int width = src.cols;
	Mat m1;
	src.convertTo(m1, CV_32F);
	//转换图片的色彩空间,使其可以用Vec3f进行调整,如果用Vec3b则不用这一步
	dst = Mat::zeros(src.size(), src.type());
	//新建一张大小相同、类型相同的全黑色的图片
	float alpha = 1.5;
	float beta = 10;
	//设定alpha和beta的值
	//通过遍历对每一个像素点进行修改
	for (int row = 0; row < height; row++) {
		for (int col = 0; col < width; col++) {
			if (src.channels() == 3) {
				float b = m1.at<Vec3f>(row, col)[0];
				float g = m1.at<Vec3f>(row, col)[1];
				float r = m1.at<Vec3f>(row, col)[2];
			
				dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
				dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
				dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
			}
			else if (src.channels() == 1) {
				float v = src.at<uchar>(row, col);
				dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);
			}
		}
	}

其中,dst.at<uchar>(row, col) = saturate_cast<uchar>(v*alpha + beta);中的saturate_cast函数是用以确保BGR的值不超过0-255的区间。大于255则为255,小于0则为0.


绘制形状和文字

形状和文字的绘制较为简单,用独立自定义的函数分别实现各个功能。

  • 直线需要两个点的坐标
  • 圆需要圆心坐标和半径
  • 椭圆需要椭圆心的坐标和长短轴半径,倾斜的角度,绘制的范围
  • 矩形需要左上角的起始坐标和长宽
  • 多边形需要各个点

其他的如图形线条的颜色,线条的粗细程度,绘制的方法都在代码中进行体现。

void Myline() {
	Point p1 = Point(20, 30);
	Point p2;
	p2.x = 50;
	p2.y = 50;
	Scalar color = Scalar(0, 0, 255);
	line(src, p1, p2, color,1,LINE_AA);
}

void Myrectangle() {
	Rect rect = Rect(50, 50, 50, 50);
	Scalar color = Scalar(255, 0, 0);
	rectangle(src, rect, color, 2, LINE_8);
}

void Myellipse() {
	Scalar color = Scalar(0, 255, 0);
	ellipse(src, Point(src.cols / 2, src.rows / 2),Size(src.cols/4,src.rows/8),90,0,360,color,10,LINE_8);
	//所画图像,中心点位置,Size(竖轴长度,横轴长度),倾斜角度,起始角度,结束角度(画整个或部分),颜色,线宽,线
}

void Mycircle() {
	Scalar color = Scalar(255, 255, 255);
	Point center = Point(100, 100);//中心坐标
	circle(src, center, 150,color,2,LINE_8);
}

void Mypolygon() {
	Point pts[1][5];
	pts[0][0] = Point(100, 100);
	pts[0][1] = Point(100, 200);
	pts[0][2] = Point(200, 200);
	pts[0][3] = Point(200, 100);
	pts[0][4] = Point(100, 100);
	const Point *ppts[] = { pts[0] };
	int npt[] = { 5 };
	Scalar color = Scalar(0, 255, 255);
	
	fillPoly(src, ppts, npt, 1, color, 8);
}

文字的绘制更为简单,只需要调用已经写好的API即可。

putText(src, "Hello opencv", Point(120, 120), CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(12, 255, 30), 4, 8);
//图片,文字,左下角坐标,字体,字体大小,颜色,字体粗细(用的4号),线型(用的8号,如果用AA可以去锯齿)

接下来是一个随机画直线的函数。

	void Randomlinedemo() {
	RNG rng(1000);
	Point pt1;
	Point pt2;
	Mat bg = Mat::zeros(src.size(), src.type());
	namedWindow("random line demo", CV_WINDOW_AUTOSIZE);
	for (int i = 0; i < 100; i++) {
		pt1.x = rng.uniform(0,bg.cols);
		pt2.x = rng.uniform(0, bg.cols);
		pt1.y = rng.uniform(0, bg.rows);
		pt2.y = rng.uniform(0, bg.rows);
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		if (waitKey(50) > 0) {
			break;
		}
		line(bg, pt1, pt2, color, 2, 8);
		imshow("random line demo", bg);
	}
}

RNG类的对象rng随机产生一个0-1000以内的数,这足以覆盖0-255的BGR的所有值。利用uniform函数使其画线的范围不超过图片的边界。如果循环结束则等待的时间超过50ms则退出循环。

发布了14 篇原创文章 · 获赞 11 · 访问量 2536

猜你喜欢

转载自blog.csdn.net/qq_43425914/article/details/97396586