目录
[1 视频/图像读写](#1 视频/图像读写)
[1.1 视频](#1.1 视频)
[1.1.1 视频读 VideoCapture](#1.1.1 视频读 VideoCapture)
[1.1.2 视频写 VideoWriter](#1.1.2 视频写 VideoWriter)
[1.2 图像](#1.2 图像)
[1.2.1 图像读 imread](#1.2.1 图像读 imread)
[1.2.2 图像写 imwriter](#1.2.2 图像写 imwriter)
[2 图像预处理 resize等](#2 图像预处理 resize等)
核心流程:
视频/图像读写 → 帧预处理 → 特征提取或模型推理 → 视频分析 → 结果输出
| 阶段 | 主要工作 | 常用模块 |
|---|---|---|
| 视频/图像读写 | 摄像头、文件、RTSP 拉流 | videoio |
| 帧预处理 | 缩放、裁剪、色彩转换、去噪 | imgproc、core |
| 特征提取/推理 | 边缘、光流、背景减除、DNN 推理 | video、dnn、features2d |
| 视频分析 | 检测、跟踪、运动分析、场景识别 | video、dnn |
| 结果输出 | 画框、显示、保存、推流 | imgcodecs、videoio |
1 视频/图像读写
1.1 视频
1.1.1 视频读 VideoCapture
camera: 从相机读取
file:从视频文件读取
rtsp:从视频流文件读取,需要使用多线程+队列保证视频流畅
cpp
void testCmaera() {
cv::VideoCapture capture;
bool result = capture.open(0);
if (!result) {
std::cout << ("capture open fail") << std::endl;
return;
}
cv::namedWindow("camera");
cv::Mat frame;
while (capture.read(frame)) {
if (frame.empty()) {
break;
}
cv::imshow("camera", frame);
cv::waitKey(30);
}
// 閲婃斁绐楀彛璧勬簮
cv::destroyAllWindows();
}
void testMP4() {
cv::VideoCapture capture;
bool result = capture.open("images/trailer.mp4");
if (!result) {
std::cout << "打开视频文件失败" << std::endl;
return;
}
double fps = capture.get(cv::CAP_PROP_FPS);
if (fps <= 0) {
fps = 30.0;
}
int pauseTime = 1000 / fps;
cv::Mat frame;
cv::namedWindow("mp4");
while (capture.read(frame)) {
if (frame.empty()) {
break;
}
cv::imshow("mp4", frame);
if (cv::waitKey(pauseTime) == 27) break;
}
capture.release();
cv::destroyAllWindows();
}
void testRtsp() {
string rtsp_url = "rtsp://rtsp.rtsplink.com/live/test?token=cfe62a0ba7e1e5f7d3cf21dc94b1b21d1a34d8f6e9f09d1263b90607aa0e6d2a";
queue<Mat> frame_q;
mutex mtx;
bool running = true;
const int MAX_QUEUE = 6;
// ========== 子线程:拉流 grab + retrieve ==========
thread producer([&]() {
VideoCapture cap(rtsp_url);
if (!cap.isOpened())
{
cout << "子线程打开RTSP失败!检查地址、网络、opencv是否带ffmpeg" << endl;
running = false;
return;
}
cout << "W:" << cap.get(CAP_PROP_FRAME_WIDTH)
<< " H:" << cap.get(CAP_PROP_FRAME_HEIGHT) << endl;
Mat frame;
while (running)
{
if (!cap.grab())
{
cout << "丢帧/断流" << endl;
this_thread::sleep_for(chrono::milliseconds(200));
continue;
}
if (!cap.retrieve(frame))
{
continue;
}
if (frame.empty())
continue;
lock_guard<mutex> lk(mtx);
if (frame_q.size() >= MAX_QUEUE)
{
frame_q.pop();
}
frame_q.push(frame.clone());
}
cap.release(); // cap在这里释放,子线程内部自己释放
});
// ========== 主线程:显示 + 写文件 ==========
VideoWriter writer("demo.mp4", cv::VideoWriter::fourcc('m', 'p', '4', 'v'), 30, Size(1280, 720));
if (!writer.isOpened())
{
running = false;
producer.join();
cout << "writer 创建失败" << endl;
return;
}
Mat curr;
while (true)
{
{
lock_guard<mutex> lk(mtx);
if (!frame_q.empty())
{
curr = frame_q.front();
frame_q.pop();
}
}
if (!curr.empty())
{
writer.write(curr);
imshow("rtsp", curr);
}
int key = waitKey(1);
if (key == 'q')
{
break;
}
}
// 优雅退出
running = false;
producer.join();// 阻塞,等待线程退出
writer.release();
destroyAllWindows();
}
1.1.2 视频写 VideoWriter
fourcc:编码格式,参考Video Codecs by FOURCC - fourcc.org
| fourcc 写法 | 编码 | 推荐后缀 | 说明 |
|---|---|---|---|
fourcc('M','J','P','G') |
MJPEG | .avi |
最稳,几乎所有 OpenCV 都支持,画质好、体积大 |
fourcc('m','p','4','v') |
MPEG-4 | .mp4 |
Windows MSMF 后端友好,兼容性首选 mp4 |
fourcc('a','v','c','1') |
H.264 | .mp4 |
压缩率高,必须开启 FFmpeg 才支持 |
fourcc('X','V','I','D') |
XVID | .avi |
压缩好,预编译包大多不带,容易打开失败 |
fourcc('I','Y','U','V') |
IYUV 无压缩 | .avi |
无压缩,文件巨大 |
cpp
void testWriter() {
cv::VideoCapture capture("oceans.mp4");
if (!capture.isOpened()) {
cout << "打开失败" << endl;
return;
}
int w = capture.get(cv::CAP_PROP_FRAME_WIDTH);
int h = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
//int fourcc = VideoWriter::fourcc('M', 'J', 'P', 'G');
int fourcc = VideoWriter::fourcc('m', 'p', '4', 'v');
VideoWriter writer("output.avi", fourcc, 30, Size(w, h));
if (!writer.isOpened()) {
cout << "writer 打开失败,编码器不可用" << endl;
capture.release();
return;
}
cv::Mat frameIn;
while (capture.read(frameIn)) {
if (frameIn.empty()) {
return;
}
imshow("oceans",frameIn );
writer.write(frameIn);
if (cv::waitKey(30) == 27) {
break;
}
}
capture.release();
writer.release();
cv::destroyAllWindows();
}
1.2 图像
1.2.1 图像读 imread
cpp
void testImg() {
cv::Mat image = cv::imread("images/1.jpg");
if (image.empty()) {
return;
}
cv::namedWindow("image");
cv::imshow("image", image);
cv::waitKey();
cv::destroyAllWindows();
}
1.2.2 图像写 imwriter
在退出时保存一帧图像
cpp
void testImgWrite() {
cv::VideoCapture capture("oceans.mp4");
if (!capture.isOpened()) {
cout << "打开失败" << endl;
return;
}
cv::Mat frameIn;
while (capture.read(frameIn)) {
if (frameIn.empty()) {
return;
}
imshow("oceans", frameIn);
//writer.write(frameIn);
if (cv::waitKey(30) == 27) {
cv::imwrite("snapshot.jpg", frameIn);
break;
}
}
capture.release();
cv::destroyAllWindows();
}
2 图像预处理 resize等
用于后续的分析等
cpp
void preProcess() {
cv::Mat image = cv::imread("images/1.jpg");
if (image.empty()) {
return;
}
cv::imshow("source", image);
//cv::namedWindow("aim"); // 可要可不要
// 1 缩放
cv::Mat aim;
cv::resize(image, aim, cv::Size(640, 480));
// 2 色彩转换
//cv::cvtColor(aim, aim,cv::COLOR_BGR2GRAY);
// 3 翻转
// 0 沿着X轴镜像
// 1 沿着Y轴镜像
// -1 沿着中心点镜像 (x+y)
cv::flip(aim, aim, 0);
// 4 旋转180
cv::rotate(aim, aim, cv::ROTATE_90_CLOCKWISE);
// 5 高斯模糊
cv::GaussianBlur(aim, aim, cv::Size(11, 11)/*卷积核大小*/, 0/*X轴方向上的sigma�*/);
// 6 边缘检测
//cv::Canny(aim, aim, 10/*下边界值*/, 30/*上边界值*/);
// 7 二值化
cv::threshold(aim, aim, 160/*边界*/, 255/*最大值*/, cv::THRESH_BINARY);
// 8 左右眼图像拼接,VR中常用
cv::Mat sidBySide;
cv::hconcat(aim, aim, sidBySide);
//cv::vconcat(aim, aim, sidBySide);
// 9 绘制文字
cv::putText(
aim, // 目标
"hello", // 文字
cv::Point(50, 50), // 起始点位置
cv::FONT_HERSHEY_SIMPLEX, // 字体
1.0, // 大小
cv::Scalar(0, 255, 0), // 色彩
2, // 线宽
cv::LINE_AA // 线的类型
);
// 10 roi
cv::Mat roi = aim(cv::Rect(10, 10, 200, 200));
cv::imshow("aim", aim);
//cv::imshow("sidBySide", sidBySide);
//cv::imshow("roi", roi);
cv::waitKey(0);
}