前言

  续本专栏第一篇文章,该部分是入门学习笔记的第二部分,建议读者先从第一部分的笔记看起,循序渐进,第一部分有经典的15个例子,第二部分有13个例子

一、OpenCV图像的一些基本操作(day 02)

16. OpenCV中提供的随机数生成

RNG rng(123);	//123表示随机数种子,随机数种子控制随机数的生成,相同种子的RNG对象生成的随机数序列是一样的

rng.uniform(0,512); 	//生成[0,512)范围的随机数

17. 绘制和填充多边形,以五边形为例

Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);

//存储点集合
vector<Point> ps;

//初始化点
ps.push_back(Point(100, 100));
ps.push_back(Point(350, 100));
ps.push_back(Point(450, 280));
ps.push_back(Point(320, 450));
ps.push_back(Point(80, 400));

//绘制多边形,第三个参数表示是否将多边形闭合,第三个参数表示是否绘制闭合的多边形
polylines(canvas, ps, true, Scalar(0, 0, 255), 8, LINE_AA, 0);

//填充多边形
fillPoly(canvas, ps, Scalar(0, 0, 255), LINE_AA, 0);

//绘制轮廓和填充多边形
vector<vector<Point>> pss;	//创建点集合数组
pss.push_back(ps);		
drawContours(canvas, pss, -1, Scalar(0, 0, 255), -1, LINE_AA);	//第三个参数为-1表示绘制所有的点集合,第五个参数为-1表示填充轮廓,大于0绘制多边形

18. 鼠标事件控制绘画,截取ROI区域

Point sp(-1, -1);	//记录开始位置
Point ep(1, 1);		//记录结束位置
Mat tmp;	//保存最原始的图片

//绑定鼠标事件, x, y表示鼠标在canvas里面的位置
static void OnMouse(int event, int x,int y, int flags, void* userdata) {
	if (event == EVENT_LBUTTONDOWN) {	//左键按下,初始化开始坐标位置
		sp.x = x;
		sp.y = y;
		cout << "Start point: " << sp << endl;
	}
	else if (event == EVENT_LBUTTONUP) {	//左键抬起,初始化结束坐标位置
		ep.x = x;
		ep.y = y;
		Rect rect(sp, ep);
		imshow("mouse_event", tmp);
		if (sp.x != ep.x && sp.y != ep.y) {		//当在mouse_event窗口点击且没有移动时,rect为NULL,创建图像矩阵失败
			Mat roi = tmp(rect).clone();
			imshow("ROI", roi);		//截取图像的矩形区域
			sp.x = -1;
			sp.y = -1;
		}
	}
	else if (event == EVENT_MOUSEMOVE) {
		Mat image = tmp.clone();
		ep.x = x;
		ep.y = y;
		if (sp.x > 0 && sp.y > 0) {
			image = tmp.clone();	//每次移动都用原始图像覆盖,只显示当前移动所创建的矩形
			rectangle(image, sp, ep, Scalar(0, 0, 150), 2, LINE_AA, 0);
			imshow("mouse_event", image);
		}
	}
}

//在一个普通函数中设置鼠标事件回调函数
void MouseDrawingDemo() {
    Mat src;
	imshow("mouse_event", src);	//在设置鼠标事件回调函数之前创建窗口
	tmp = src.clone();		//创建原始图像副本
	setMouseCallback("mouse_event", OnMouse, NULL);
}

19. 图像的像素类型转换与归一化操作

Mat src, dst;

cout << src.type() << endl;				//输出图像的像素类型 
this->src.convertTo(dst, CV_32FC3);		//像素类型转换
cout << dst.type() << endl;

/*
	param third:归一化的上限
	param fourth:归一化的下限
	param fifth:归一化的类型
*/
normalize(dst, dst, 1.0, 0, NORM_MINMAX);	//转换为float类型的像素后,进行归一化操作


imshow("src", src);
imshow("normalize_dst", dst);

20. 图像的放缩与插值

图像的放缩:即放大或缩小图像的尺寸

图像的插值:在图像进行放大或者缩小的过程中,图像的像素点会变少或者变多,这时图像的信息就需要通过某种方法来进行获取,插值法就是旨在 通过某些规则/规范/约束,获取这些多出坐标点的像素值,常见的线性插值包括:一阶线性插值(最近邻近插值)、双线性插值核双三次插值

Mat src;
Mat zoomin, zoomout;
int width = src.cols;
int height = src.rows;
imshow("src",src);

//线性插值法
resize(src, zoomin, Size(width / 2, height / 2), 0, 0, INTER_LINEAR);
imshow("zoomin", zoomin);
resize(src, zoomout, Size(1.5 * width, 1.5 * height), 0, 0, INTER_LINEAR);
imshow("zoomout", zoomout);

21. 图像的翻转(flip,镜像操作)

OpenCV提供flip函数,可以实现三种翻转(镜像)操作:x轴、y轴和y = x对称。

Mat src, dst;

imshow("src", src);

flip(src, dst, 0);	//x轴对称翻转
imshow("flip(0)", dst);

flip(src, dst, 1);	//y轴对称翻转
imshow("flip(1)", dst);

flip(src, dst, -1);	//y=x对称翻转 
imshow("flip(-1)", dst);

22. 图像旋转

根据仿射变换矩阵 M 控制图像的旋转方式,仿射变换矩阵M中每个元素如下,其中,x和y表示图像的中心点位置,a = scale*cos(angle),b = scale*sin(angle),angle表示放大系数,angle表示角度

[ a b ( 1 − a ) ⋅ x − b ⋅ y − b a b ⋅ x + ( 1 − a ) ⋅ y ] \begin{bmatrix} a& b& (1-a)\cdot x-b\cdot y\\ -b& a& b\cdot x+(1-a)\cdot y \end{bmatrix} [abba(1a)xbybx+(1a)y]

重点关注第三列的元素:第一行第三列和第二行第三列元素分别表示图像经过旋转和缩放后,图像中心点在x轴和y轴的平移量,这个平移量保证了图像旋转之后,中心点x和y的值不变

	Mat src, dst, M;
	int w = src.cols;
	int h = src.rows;
	/*
		第一步,获取仿射变换矩阵
		param first: 中心点位置
		param second: 旋转角度
		param third: 放大系数
	*/
	M = getRotationMatrix2D(Point(w / 2, h / 2), 45, 1.0);

	double cos = M.at<double>(0, 0);
	double sin = M.at<double>(0, 1);
	
	//求出旋转后图像的宽度和高度
	double nw = w * cos + h * sin;
	double nh = w * sin + h * cos;

	//第二步,修改仿射变换矩阵的偏置值
	M.at<double>(0, 2) += (nw / 2 - w / 2);
	M.at<double>(1, 2) += (nh / 2 - h / 2);

	//第三步,进行仿射变换
	warpAffine(this->src, dst, M, Size(nw, nh));
	imshow("src", this->src);
	imshow("rotate 45°", dst);

23. 读取写入视频流文件

VideoCapture capture(0);		//参数为0,打开默认摄像头
Mat frame;

//获取视频属性
int frame_width = this->video.get(CAP_PROP_FRAME_WIDTH);
int frame_height = this->video.get(CAP_PROP_FRAME_HEIGHT);
int total_count = this->video.get(CAP_PROP_FRAME_COUNT);	//视频总帧数
double fps = this->video.get(CAP_PROP_FPS);	


/*
	创建视频写入对象
	param second: 视频编解码格式
	param last: 控制视频颜色
*/
VideoWriter write_object("D:\\test.mp4", this->video.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_height), true);	//最后一个参数视频颜色

while (1) {
    int flag = waitKey(100/6);		//程序阻塞可以控制帧率		
    if (flag == 27) break;
    this->video.read(frame);		//将视频中一帧图像提取到frame图像矩阵中
    flip(frame, frame, 1);			//图像沿y轴翻转
    write_object.write(frame);		//以帧的形式写入视频流文件
    if (frame.empty()) break;
    imshow("frame", frame);
}

//release视频资源,最好主动释放,因为摄像头属于硬件资源,便于以后多线程控制访问
capture.release();
write_object.release();

24. 图像通道统计,输出直方图

//存储通道分离
vector<Mat> bgr;
split(this->src, bgr);
//定义绘制直方图参数变量
const int channels[1] = { 0 };	
const int bins[1] = { 256 };	//通道取值的个数
float hranges[2] = { 0,255 };	//通道值的取值范围
const float* ranges[1] = { hranges };	//指针变量数组

Mat b_hist;
Mat g_hist;
Mat r_hist;

//计算三个通道的直方图
calcHist(&bgr[0], 1, 0, Mat(), b_hist, 1, bins, ranges);
calcHist(&bgr[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
calcHist(&bgr[2], 1, 0, Mat(), r_hist, 1, bins, ranges);

25. 续24绘制直方图

//直方图的基本参数
int hist_w = 512;
int hist_h = 400;
//cvRound函数对浮点数进行四舍五入
int bin_w = cvRound((double)hist_w / bins[0]);		//直方图横轴每个元素所占像素点大小
Mat HistImage = Mat::zeros(Size(hist_w, hist_h), CV_8UC3);
HistImage = Scalar(255, 255, 255);	//直方图纯白背景

//归一化直方图数据
normalize(b_hist, b_hist, 0, HistImage.rows, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, HistImage.rows, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, HistImage.rows, NORM_MINMAX, -1, Mat());

//绘制直方图曲线
for (int i = 1; i < bins[0]; ++i) {
    Point p1(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1, 0)));
    Point p2(bin_w * (i), hist_h - cvRound(b_hist.at<float>(i, 0)));
    line(HistImage,  p1, p2, Scalar(255, 0, 0), 2, LINE_AA, 0);

    Point p3(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1, 0)));
    Point p4(bin_w * (i), hist_h - cvRound(g_hist.at<float>(i, 0)));
    line(HistImage, p3, p4, Scalar(0, 255, 0), 2, LINE_AA, 0);

    Point p5(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1, 0)));
    Point p6(bin_w * (i), hist_h - cvRound(r_hist.at<float>(i, 0)));
    line(HistImage, p5, p6, Scalar(0, 0, 255), 2, LINE_AA, 0);
}

//显示直方图
imshow("HistImage", HistImage);

26. 图像卷积操作之均值模糊

OpenCV中,对图像进行模糊操作的主要目的是降低图像的噪声,通常采用卷积核的方式。

均值卷积:对卷积核内的元素求平均值代替卷积核覆盖图像范围的中心位置

Mat dst;
/*
	均值卷积(卷积核系数都是同值)操作函数,自定义卷积操作为3*3大小
	param third: 卷积核的大小
	param fourth: -1表示默认改变像素点位置是卷积核的中心位置
*/
blur(this->src, dst, Size(3, 3), Point(-1, -1));
imshow("src", this->src);
imshow("Convolution Blur", dst);

27. 图像卷积操作之高斯模糊

对需要改变卷积核中心位置的像素点进行高斯函数计算,具体的,计算整个核覆盖的像素点高斯函数值,然后求和,得出核的新像素点大小。

Mat	dst;
GaussianBlur(this->src, dst, Size(2, 2), 10);
imshow("src", this->src);
imshow("Gaussian Blur", dst);

28. 图像卷积之高斯双边模糊

对于一张图像,非轮廓的部分进行模糊,轮廓部分进行原有信息保存(像素值差异大的进行保留)

Mat dst;
bilateralFilter(this->src, dst, 0, 100, 10);
imshow("src",src);
imshow("bilateralFilter", dst);

总结

  至此,基于Cpp的OpenCV4.8入门学习笔记到这里就已经结束了,这28个例子非常适合基于Cpp的OpenCV4入门学习。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部