文章目录
前言
续本专栏第一篇文章,该部分是入门学习笔记的第二部分,建议读者先从第一部分的笔记看起,循序渐进,第一部分有经典的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} [a−bba(1−a)⋅x−b⋅yb⋅x+(1−a)⋅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入门学习。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 基于Cpp的OpenCV4.8入门学习笔记(二)
发表评论 取消回复