说在前面
效果展示
原图
前景图
原理
准备一张背景图 使用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 ;
}