本博客为自己学习时候的笔记,并不深究各API的具体原理及类似API的区别与联系,从实用角度进行整理,方便自己的回忆与复习。课程内容均来自B站。https://www.bilibili.com/video/av29600072
图像的线性混合
线性混合可以由字面意思得出,它是像素点经过线性变化后得出的新的点。
只有两张大小相同、类型相同的图片才可以相互混合!
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的时候图像效果分别如下(高能预警图片可能引起不适&&智贤女神我对不起你):
亮度与对比度
一张图片越亮,它与白色越接近,也就是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则退出循环。