# 数字视频技术及应用实验二:视频增强与播放控制
# 实验任务书
编程实现一个简易的视频播放器,包含以下功能:
- 控制视频的播放与暂停。
- 控制视频播放进度。
- 可调节当前视频的对比度与亮度。
- 对视频进行直方图均衡化。
- 仍选一种或多种滤波对进行视频增强(方框滤波、均值滤波、高斯率波、理想低通滤波等)
# 实验内容指导
- 在 VS 中新建工程。
- 配置 OpenCV。
- 定义读取视频及控制播放所需要的变量。
- 创建滚动条并定义回调函数。
- 对比度、亮度修改函数。
图像对比度和亮度的公式为 ,其中, 表示源图像像素, 表示输出图像像素,参数 (增益)和 (偏置)常常被用来控制图像的对比度和亮度。可以使用 Mat 的 convertTo 函数对帧图像进行对比度和亮度的调节。- 直方图均衡化就是对图像进行非线性拉伸,使得变换后的图像直方图分布均匀。直方图均衡化操作可使用 opencv 中的库函数实现,最好按照其原理自己编写代码实现该功能。
- 滤波增强的方式有很多,opencv 也有封装好的方法,希望有余力的同学尝试自己实现。
- 播放视频。
# 实验解决代码
#include <opencv2/opencv.hpp> | |
#include<string> | |
using namespace cv; | |
using namespace std; | |
const char* windowName = "视频播放"; | |
int stop = 1;// 播放、暂停 | |
int rateOfProgress = 0;// 播放进度 0-100 | |
int contrastRatio = 50;// 对比度 | |
int light = 255;//0-255; | |
int histogramEqualization = 0; | |
int filter = 1; | |
int filter2 = 1; | |
int filter3 = 1; | |
bool IsDrag = false; | |
class videoProcess { | |
public: | |
Mat frame; | |
videoProcess(Mat f) { | |
this->frame = f; | |
} | |
// 调节亮度 | |
void adjustLight(int light) { | |
light = light - 255; | |
for (int r = 0; r < frame.rows; r++) { | |
for (int c = 0; c < frame.cols; c++) { | |
int B = frame.at<Vec3b>(r, c)[0]; | |
int G = frame.at<Vec3b>(r, c)[1]; | |
int R = frame.at<Vec3b>(r, c)[2]; | |
B += light; | |
G += light; | |
R += light; | |
if (B > 255)B = 255; | |
if (G > 255)G = 255; | |
if (R > 255)R = 255; | |
if (B < 0)B = 0; | |
if (G < 0)G = 0; | |
if (R < 0)R = 0; | |
frame.at<Vec3b>(r, c)[0] = B; | |
frame.at<Vec3b>(r, c)[1] = G; | |
frame.at<Vec3b>(r, c)[2] = R; | |
} | |
} | |
} | |
// 直方图均衡化 | |
void histgoramEqu() | |
{ | |
// 三个通道分别进行均衡化处理 | |
float totalPixelNum = frame.rows * frame.cols; | |
int hist[3][256] = { 0 };//BGR | |
// 构造 3 通道的直方图 | |
for (int r = 0; r < frame.rows; r++) { | |
for (int c = 0; c < frame.cols; c++) { | |
hist[0][frame.at<Vec3b>(r, c)[0]]++; | |
hist[1][frame.at<Vec3b>(r, c)[1]]++; | |
hist[2][frame.at<Vec3b>(r, c)[2]]++; | |
} | |
} | |
float CDF[3][256] = { 0 };//3 个通道的累计分布函数 //Cumulative distribution function | |
// 计算 3 通道的所有累计分布函数的值 | |
for (int i = 0; i < 256; i++) { | |
float sum[3] = { 0 }; | |
for (int k = 0; k <= i; k++) { | |
sum[0] += hist[0][k]; | |
sum[1] += hist[1][k]; | |
sum[2] += hist[2][k]; | |
} | |
CDF[0][i] = sum[0] / totalPixelNum; | |
CDF[1][i] = sum[1] / totalPixelNum; | |
CDF[2][i] = sum[2] / totalPixelNum; | |
} | |
for (int r = 0; r < frame.rows; r++) { | |
for (int c = 0; c < frame.cols; c++) { | |
for (int i = 0; i < 3; i++) | |
frame.at<Vec3b>(r, c)[i] = 255 * CDF[i][frame.at<Vec3b>(r, c)[i]]; | |
} | |
} | |
} | |
// 调节对比度,范围 50%-150% | |
void adjustContrast(int t) | |
{ | |
float k = 0; | |
if (t - 50 == 0)// 不处理 | |
return; | |
else if (t - 50 > 0) { | |
k = 1 + (t - 50) / 100.0; | |
} | |
else | |
k = 1 - (50 - t) / 100.0; | |
for (int r = 0; r < frame.rows; r++) { | |
for (int c = 0; c < frame.cols; c++) { | |
int B = frame.at<Vec3b>(r, c)[0]; | |
int G = frame.at<Vec3b>(r, c)[1]; | |
int R = frame.at<Vec3b>(r, c)[2]; | |
B *= k; | |
G *= k; | |
R *= k; | |
if (B > 255)B = 255; | |
if (G > 255)G = 255; | |
if (R > 255)R = 255; | |
frame.at<Vec3b>(r, c)[0] = B; | |
frame.at<Vec3b>(r, c)[1] = G; | |
frame.at<Vec3b>(r, c)[2] = R; | |
} | |
} | |
} | |
// 均值滤波 | |
void filter(int d) // 模板的边长 | |
{ | |
Mat temp = frame.clone(); | |
int num = d * d; | |
for (int r = d / 2; r < frame.rows - d / 2; r++) { | |
for (int c = d / 2; c < frame.cols - d / 2; c++) { | |
for (int i = 0; i < 3; i++) {// 分别处理 3 个通道 | |
int sum = 0; | |
for (int dr = -d / 2; dr <= d / 2; dr++) { | |
for (int dc = -d / 2; dc <= d / 2; dc++) { | |
sum += temp.at<Vec3b>(r + dr, c + dc)[i]; | |
} | |
} | |
frame.at<Vec3b>(r, c)[i] = (float)sum / num; | |
} | |
} | |
} | |
} | |
void boxfilter(int d) | |
{ | |
Mat temp = frame.clone(); | |
boxFilter(temp, frame, -1, Size(d, d)); | |
} | |
void gaussianblur(int d) | |
{ | |
Mat temp = frame.clone(); | |
GaussianBlur(temp, frame, Size(d, d), 0, 0); | |
} | |
// 返回最终的处理结果 | |
Mat getFrame() | |
{ | |
return frame; | |
} | |
}; | |
static void onTrackbar(int, void*) | |
{ | |
if (filter % 2 == 0) | |
filter++; | |
if (filter2 % 2 == 0) | |
filter2++; | |
if (filter3 % 2 == 0) | |
filter3++; | |
} | |
static void onTrackbar1(int, void*) | |
{ | |
IsDrag = true; | |
} | |
// 创建滑动条 | |
void createBar() { | |
createTrackbar("暂停", windowName, &stop, 1, onTrackbar); | |
createTrackbar("进度", windowName, &rateOfProgress, 100, onTrackbar1); | |
createTrackbar("对比度", windowName, &contrastRatio, 100, onTrackbar); | |
createTrackbar("亮度", windowName, &light, 510, onTrackbar); | |
createTrackbar("直方图均衡化", windowName, &histogramEqualization, 1, onTrackbar); | |
createTrackbar("均值滤波", windowName, &filter, 21, onTrackbar); | |
createTrackbar("方框滤波", windowName, &filter2, 21, onTrackbar); | |
createTrackbar("高斯滤波", windowName, &filter3, 21, onTrackbar); | |
} | |
void main() { | |
VideoCapture capture; | |
capture.open("exp2.avi"); | |
int totalNumOfFrame = capture.get(CAP_PROP_FRAME_COUNT);// 视频总帧数 | |
int currentNumOfFrame = 0; | |
while (1) | |
{ | |
Mat frame; | |
capture >> frame; | |
if (frame.empty())break;// 结束,退出 | |
// 创建滑动条 | |
createBar(); | |
// 调节视频进度 | |
if (!IsDrag) { // 进度条没动,根据当前是多少帧,更新进度条的位置 | |
currentNumOfFrame++;// 当前是第多少帧 | |
rateOfProgress = (float)currentNumOfFrame / totalNumOfFrame * 100;// 根据当前帧数,计算并更新滑动条位置 | |
} | |
else { | |
// 进度条动了,根据进度条位置,计算当前应该是多少帧,并更新画面 | |
currentNumOfFrame = rateOfProgress / 100.0 * totalNumOfFrame; | |
capture.set(CV_CAP_PROP_POS_FRAMES, currentNumOfFrame - 1); | |
IsDrag = false; | |
} | |
videoProcess vp = videoProcess(frame); | |
// 调节视频亮度 | |
vp.adjustLight(light); | |
// 直方图均衡化 | |
if (histogramEqualization == 1) | |
vp.histgoramEqu(); | |
// 调节对比度 | |
vp.adjustContrast(contrastRatio); | |
// 均值滤波 | |
vp.filter(filter); | |
vp.boxfilter(filter2); | |
vp.gaussianblur(filter3); | |
// 获得最终结果 | |
frame = vp.getFrame(); | |
// 控制视频的播放 \ 暂停 | |
while (stop == 0)// 重复显示这一帧,看起来,,暂停了 | |
{ | |
imshow(windowName, frame); | |
waitKey(30); | |
continue; | |
} | |
namedWindow(windowName,CV_WINDOW_FREERATIO); | |
imshow(windowName, frame); | |
waitKey(30); | |
} | |
capture.release(); | |
} |