说在前面
效果展示
原图
前景图
原理
- 准备一张背景图
- 使用absdiff函数
可以看到提取的挺不错了。
- 使用threshold进行阈值化处理
若不使用则会在后续处理中产生一些噪声
- 使用findcontours函数找出最大轮廓,并用白色(255)填充
- 使用copyto函数(见这里最后一点关于copyTo函数)将原图中在上面白色区域的地方提取出来,得到最终结果
缺陷
- 必须保证背景不动(这就很难受,咱这算是最基本的方法了,
真要搞游戏里的素材还是拆包吧,咱也不会啊 )
Code
#include <opencv2\imgcodecs.hpp>
#include <opencv2\highgui.hpp>
#include <opencv2\imgproc.hpp>
#include <opencv2\videoio.hpp>
#include <iostream>
#include <cmath>
using namespace cv;
using namespace std;
const char input_video[] = "test8.mp4";
const char output_video[] = "out8.mp4";
const int thresh_value = 25;
const int x = 790;
const int y = 500;
const int width = 320;
const int height = 450;
int main(int argc, char** argv)
{
Mat back, front, gray;
VideoCapture cap(input_video);
if (!cap.isOpened())
{
cout << "Could not open video! " << endl;
return -1;
}
int frames = cap.get(CAP_PROP_FRAME_COUNT);
cap.set(CAP_PROP_POS_FRAMES, frames - 2);
cap >> back;
cap.set(CAP_PROP_POS_FRAMES, 1);
Rect roi(x, y, width, height);
Mat back_roi = back(roi).clone();
VideoWriter outputVideo;
outputVideo.open(output_video, static_cast<int>(cap.get(CAP_PROP_FOURCC)), cap.get(CAP_PROP_FPS), back_roi.size(), true);
int count = 0;
for (;;)
{
cap >> front;
if (front.empty())
break;
Mat front_roi = front(roi).clone();
Mat diff;
absdiff(back_roi, front_roi, diff);
cvtColor(diff, gray, COLOR_RGB2GRAY);
threshold(gray, gray, thresh_value, 255, THRESH_BINARY);
imshow("My diff", diff);
vector<vector<Point> > contours;
cv::findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
Mat hole(gray.size(), CV_8U, Scalar(0));
drawContours(hole, contours, -1, Scalar(255), FILLED);
Mat res(front_roi.rows, front_roi.cols, CV_8UC3, Scalar(0, 0, 0));
front_roi.copyTo(res, hole);
++count;
if(count<frames/3-20)
outputVideo << res;
char c = (char)waitKey(10);
if (c == 27) break;
}
return 0;
}