视频文件及摄像头的使用
这一段其实一开始就已经了解过了,这里算是复习一下
视频的读取(从文件及摄像头)
读取视频文件和摄像头,实际上只是修改cap文件的获取方式而已
void Demo::video_demo(Mat &image) {
VideoCapture cap1(0);
//打开摄像头
VideoCapture cap2("视频文件路径");
//读取视频文件
Mat frame;
//创建一帧
while (true) {
//cap >> frame;
cap1.read(frame);
//读取图像
imshow("video", frame);
if (waitKey(20) == 27)
break;
}
}
在结束视频操作后,一定要将cap释放,养成好习惯
cap1.release();
cap2.release();
视频的保存
视频有许多的清晰度,HD,SD,720P,1080P,4K,这些不同的清晰度对应的每一帧的图像大小也不同,对于我们已经获取的视频文件,可以获取他们对应的视频属性
//frame的意思是帧
int width = cap1.get(CAP_PROP_FRAME_WIDTH);//宽
int height = cap1.get(CAP_PROP_FRAME_HEIGHT);//高
int count = cap1.get(CAP_PROP_FRAME_COUNT);//总帧数
int fps = cap1.get(CAP_PROP_FPS);//每秒帧数
对视频的保存,可以使用VideoWriter函数
VideoWriter writer("保存路径", cap1.get(CAP_PROP_FOURCC), fps, Size(width, height), 1);
//保存路径,编码方式(一般直接获取原视频编码方式),每秒帧数,格式(即尺寸),是否彩色
对视频进行处理,实际上只要对每一帧用之前的图像处理方法即可
图像直方图
图像直方图是用来表现图像中亮度分布的直方图,给出的是图像中某个亮度或者某个范围亮度下共有几个像素,即统计一幅图某个亮度像素数量。
图像直方图由于其计算代价较小,且具有图像平移、旋转、缩放不变性等众多优点,广泛地应用于图像处理的各个领域,特别是灰度图像的阈值分割、基于颜色的图像检索以及图像分类。
但是直方图只能得到他的颜色信息,而不能得到他的位置信息,因此不能具体的对应某一张图片。
void Demo::histogram_demo(Mat &image) {
//绘制一维直方图
//首先运用通道知识,分离三通道(对于RGB图像,灰度图像则不需要分离)
vector<Mat> bgr_plane;
split(image, bgr_plane);
//定义参数变量
const int channels[1] = { 0 };//通道索引,灰度图像可填入[0],彩色图像(BGR)[0]、[1]、[2]代表三个颜色通道;(也要用方括号)
const int bins[1] = { 256 };//总共的灰度级别
float hranges[2] = { 0,255 };//取值范围
const float* ranges[1] = { hranges };
Mat b_hist, g_hist, r_hist;
//计算三个通道的直方图
//calcHist(images,niimage, channels, mask, output, dims, histSize, ranges)
//images:待计算图像,需要用方括号 [img]
//channels:通道索引,灰度图像可填入[0],彩色图像(BGR)[0]、[1]、[2]代表三个颜色通道;(也要用方括号)
//mask:掩膜,计算整个图像,传入None就行,如果要对局部图像做处理,可以定义个mask传入
//histSize:Bin(直方图的柱子)数量,全尺寸用 [256] 就行
//ranges:像素范围,一般设为 [0, 256]
calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);
calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);
//绘制直方图画布
//cvRound():返回跟参数最接近的整数值,即四舍五入
//cvFloor():返回不大于参数的最大整数值,即向下取整
//cvCeil():返回不小于参数的最小整数值,即向上取整
int hist_w = 512,hist_h = 400;//定义宽高
int bin_w = cvRound((double)hist_w / 256);//每一条的宽度,用总宽度除以条数256
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
//归一化直方图数据
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
normalize(g_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
normalize(g_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
//绘制直方图曲线
//因为直方图是从下往上绘制的,而屏幕的纵坐标是最上面为0,因此纵坐标需要用纵坐标减去直方图坐标
for (int i = 1; i < bins[0]; i++) {
//b
line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
Point(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
rectangle(histImage, Rect(bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)),
bin_w, cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), -1, 8, 0);
//g
line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
Point(bin_w*(i), hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, 8, 0);
rectangle(histImage, Rect(bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)),
bin_w, cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), -1, 8, 0);
//r
line(histImage, Point(bin_w*(i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
Point(bin_w*(i), hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, 8, 0);
rectangle(histImage, Rect(bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)),
bin_w, cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), -1, 8, 0);
}
//显示直方图
namedWindow("Histogram", WINDOW_AUTOSIZE);
imshow("Histogram", histImage);
}
其实只要理解原理就不是很难
关键点:
- 一次只能绘制一个通道的直方图曲线,因此彩色图片需要分离通道,灰度图不需要
- 理解calcHist()中每一个参数的意义
- 只有计算直方图的函数而并没有直接显示直方图曲线的函数
- 在绘制时要注意屏幕的坐标原点在左上角,因此直方图的纵坐标需要转换
二维直方图
要绘制二维直方图,首先要将RGB图像转换成HSV图像
在这里我们选择h和s两个维度来制作
void Demo::histogram_2d_demo(Mat &image) {
Mat hsv, hs_hist;
//绘制二维直方图需要将图片转换成HSV格式
cvtColor(image, hsv, COLOR_BGR2HSV);
int hbins = 30, sbins = 32;
//定义h和s两个维度的bin
int hist_bins[] = { hbins,sbins };
float h_range[] = { 0,180 };//h的取值范围
float s_range[] = { 0,255 };//s的取值范围
const float* hs_range[] = { h_range,s_range };
int hs_channels[] = { 0,1 };
calcHist(&hsv, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_range, true, false);
double maxVal = 0;
minMaxLoc(hs_hist, 0, &maxVal, 0, 0);
int scale = 10;
Mat hist2d_image = Mat::zeros(sbins*scale, hbins*scale, CV_8UC3);
for (int h = 0; h < hbins; h++) {
for (int s = 0; s < sbins; s++) {
float binVal = hs_hist.at<float>(h, s);
int intensity = cvRound(binVal * 255 / maxVal);
rectangle(hist2d_image, Point(h*scale, s*scale),
Point((h + 1)*scale - 1, (s + 1)*scale - 1), Scalar::all(intensity), -1);
}
}
namedWindow("Histogram", WINDOW_AUTOSIZE);
imshow("Histogram", hist2d_image);
}