怎么让客户做网站网站优化推广排名
引言
提到OpenCV,大家首先想到的可能是图像处理、目标检测,但你是否想过——用OpenCV实现一个带进度条、倍速播放、暂停功能的视频播放器?本文将通过一个实战项目,带你深入掌握OpenCV的视频处理能力,并解锁以下功能:
- 基础播放/暂停
- 动态倍速调节(0.5x~4x)
- 交互式进度条
- 实时时间戳显示
文末提供完整代码,可直接运行!
一、环境准备
安装OpenCV
请参考其他博客,C++版本的OpenCV安装,每个操作系统都有自己的玩法。比如我在Ubuntu中下载了OpenCV 4.11版本的源码自行编译。可以参考我的上一篇文章
准备测试视频
准备一个MP4或AVI格式的视频文件(示例代码路径为/home/user/video.mp4,读者自行替换)。
二、核心功能实现
1. 基础播放器
#include <opencv2/opencv.hpp>int main() {// 1. 创建视频捕获对象(核心对象)cv::VideoCapture cap("video.mp4");// 2. 主循环(核心逻辑)while(true) {cv::Mat frame;// 3. 读取帧(核心方法)if (!cap.read(frame)) break;// 4. 显示帧(核心输出)cv::imshow("Video", frame);// 5. 控制逻辑(核心交互)int key = cv::waitKey(25); // 控制播放速度if(key == 27) break; // ESC退出}// 6. 资源释放(核心清理)cap.release();cv::destroyAllWindows();return 0;
}
代码解析
- VideoCapture:支持文件、摄像头、网络流多种输入源。
- waitKey(25):控制播放速度(25ms对应约40 FPS)。
三、功能扩展:让播放器更强大
1. 倍速播放
通过调整waitKey的延迟时间实现变速:
#include <opencv2/opencv.hpp>int main() {cv::VideoCapture cap("video.mp4");double fps = cap.get(cv::CAP_PROP_FPS);int base_delay = 1000 / fps; // 基准延迟double speed = 1.0; // 初始速度while(true) {cv::Mat frame;if(!cap.read(frame)) break;cv::imshow("Video", frame);// 核心控制逻辑int delay = static_cast<int>(base_delay / speed);int key = cv::waitKey(std::max(1, delay)); if(key == 27) break; // ESC退出else if(key == '+') speed = std::min(4.0, speed + 0.5); // 加速else if(key == '-') speed = std::max(0.5, speed - 0.5); // 减速}cap.release();cv::destroyAllWindows();return 0;
}
按+加速,按-减速,速度范围限制在0.5x~4x。
2. 进度条与跳转
利用OpenCV的滑动条控件实现交互:
#include <opencv2/opencv.hpp>using namespace cv;// 全局变量
VideoCapture cap;
int currentFrame = 0;
int totalFrames = 0;// 进度条回调函数
void onTrackbar(int pos, void* userdata) {cap.set(CAP_PROP_POS_FRAMES, pos);currentFrame = pos;
}int main() {cap.open("video.mp4");totalFrames = cap.get(CAP_PROP_FRAME_COUNT);namedWindow("Video Player");createTrackbar("Progress", "Video Player", ¤tFrame, totalFrames, onTrackbar);Mat frame;while(true) {cap.read(frame);if(frame.empty()) break;// 更新当前帧位置currentFrame = cap.get(CAP_PROP_POS_FRAMES);setTrackbarPos("Progress", "Video Player", currentFrame);imshow("Video Player", frame);if(waitKey(30) == 27) break; // ESC退出}cap.release();destroyAllWindows();return 0;
}
3. 实时信息叠加
在视频帧上绘制进度条和时间戳:
void drawOverlay(Mat& frame) {int videoWidth = frame.cols;float progressRatio = static_cast<float>(currentFrame) / totalFrames;int progressWidth = static_cast<int>(videoWidth * progressRatio);rectangle(frame, Point(0, 10), Point(progressWidth, 30), Scalar(0, 255, 0), FILLED);double currentTime = currentFrame / fps;ostringstream timeText;timeText << "Time: " << fixed << setprecision(2) << currentTime << "s";putText(frame, timeText.str(), Point(10, 60), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 255, 0), 2);ostringstream speedText;speedText << "Speed: " << fixed << setprecision(1) << speed << "x";putText(frame, speedText.str(), Point(10, 100), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 255, 0), 2);}
四、完整代码
注意,完整代码中头文件是分开引入的。只引入了opencv两个模块:highgui和imgproc。并优化了快进时CPU占用100%的问题,当然,也将程序引入了复杂的困境。
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <string>
#include <sstream>using namespace cv;
using namespace std;
using namespace chrono;class VideoPlayer {
private:VideoCapture cap;int totalFrames;double fps;int baseDelay;bool pause;int currentFrame;double speed;string windowName;high_resolution_clock::time_point lastFrameTime;static void onTrackbar(int pos, void* userdata) {VideoPlayer* self = static_cast<VideoPlayer*>(userdata);if (self->currentFrame != pos) {self->cap.set(CAP_PROP_POS_FRAMES, pos);self->currentFrame = pos;}}void drawOverlay(Mat& frame) {int videoWidth = frame.cols;float progressRatio = static_cast<float>(currentFrame) / totalFrames;int progressWidth = static_cast<int>(videoWidth * progressRatio);rectangle(frame, Point(0, 10), Point(progressWidth, 30), Scalar(0, 255, 0), FILLED);double currentTime = currentFrame / fps;ostringstream timeText;timeText << "Time: " << fixed << setprecision(2) << currentTime << "s";putText(frame, timeText.str(), Point(10, 60), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 255, 0), 2);ostringstream speedText;speedText << "Speed: " << fixed << setprecision(1) << speed << "x";putText(frame, speedText.str(), Point(10, 100), FONT_HERSHEY_SIMPLEX, 0.7, Scalar(0, 255, 0), 2);}public:VideoPlayer(const string& videoPath) :windowName("OpenCV Video Player"),pause(false),currentFrame(0),speed(1.0) {cap.open(videoPath);if (!cap.isOpened()) {throw runtime_error("无法打开视频文件");}totalFrames = static_cast<int>(cap.get(CAP_PROP_FRAME_COUNT));if (totalFrames <= 0) {throw runtime_error("无效的视频文件");}fps = cap.get(CAP_PROP_FPS);baseDelay = static_cast<int>(1000.0 / fps);namedWindow(windowName, WINDOW_AUTOSIZE);createTrackbar("Progress", windowName, nullptr, totalFrames, onTrackbar, this);setTrackbarPos("Progress", windowName, 0);lastFrameTime = high_resolution_clock::now();}void run() {Mat frame;int64 frameCounter = 0;double accumulatedDelay = 0;while (true) {auto startTime = high_resolution_clock::now();if (!pause) {// 动态帧处理控制double speedFactor = max(0.5, min(4.0, speed));int framesToProcess = static_cast<int>(speedFactor);int actuallyProcessed = 0;for (int i = 0; i < framesToProcess; ++i) {bool ret = cap.read(frame);if (!ret) {cap.set(CAP_PROP_POS_FRAMES, 0);currentFrame = 0;setTrackbarPos("Progress", windowName, currentFrame);break;}currentFrame = static_cast<int>(cap.get(CAP_PROP_POS_FRAMES));actuallyProcessed++;}if (actuallyProcessed > 0) {// 更新进度条(每5帧更新一次以降低开销)if (frameCounter++ % 5 == 0) {setTrackbarPos("Progress", windowName, currentFrame);}drawOverlay(frame);imshow(windowName, frame);}}// 智能延迟控制auto processTime = duration_cast<milliseconds>(high_resolution_clock::now() - startTime).count();int targetDelay = static_cast<int>((baseDelay / speed) - processTime);int actualDelay = max(1, targetDelay);int key = waitKeyEx(actualDelay);handleKeyInput(key);// 自动跳帧机制(当处理落后时)if (targetDelay < 0 && speed > 1.0) {int skipFrames = static_cast<int>(-targetDelay / baseDelay * speed);currentFrame = min(currentFrame + skipFrames, totalFrames - 1);cap.set(CAP_PROP_POS_FRAMES, currentFrame);}}}private:void handleKeyInput(int key) {switch (key) {case 27: // ESCcap.release();destroyAllWindows();exit(0);case 32: // Spacepause = !pause;break;case '+':speed = min(4.0, speed + 0.5);cout << "Speed: " << speed << "x" << endl;break;case '-':speed = max(0.5, speed - 0.5);cout << "Speed: " << speed << "x" << endl;break;}}
};int main() {try {VideoPlayer player("C:\\Users\\lebro\\Videos\\video_test.mp4");player.run();}catch (const exception& e) {cerr << "Error: " << e.what() << endl;return -1;}return 0;
}
五、后记
请参考《学习OpenCV3》,第二章 OpenCV初探中的第二个程序:视频。