【瞎折腾/opencv】前后景分离-帧差法提取游戏素材

说在前面

效果展示

在这里插入图片描述

原图

在这里插入图片描述

前景图

原理

  • 准备一张背景图
    在这里插入图片描述
  • 使用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";

// threshold函数的阈值,可以针对不同的视频进行调整
const int thresh_value = 25;

// 录制的视频是1080的,取咱们感兴趣的地方
const int x = 790;
const int y = 500;
const int width = 320;
const int height = 450;

int main(int argc, char** argv)
{
	// 分别作为背景图、前景图、灰度图(用于findcontours)
	Mat back, front, gray;
	VideoCapture cap(input_video);//打开输入视频

	if (!cap.isOpened())
	{
		cout << "Could not open video! " << endl;
		return -1;
	}

	// 这里我在录制视频时就将背景图录制进去了,在视频最后面
	// 所以直接提取视频最后几帧中的一帧作为背景图即可
	// 建议不要用其他方法(比如qq截图啥的)截取背景图,可能会有噪声
	int frames = cap.get(CAP_PROP_FRAME_COUNT);// 获取帧数
	cap.set(CAP_PROP_POS_FRAMES, frames - 2); // 跳到最后面
	cap >> back; // 获取,这里是1080的
	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; // 存放contours
		cv::findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); // find
		Mat hole(gray.size(), CV_8U, Scalar(0)); 
		drawContours(hole, contours, -1, Scalar(255), FILLED); // 绘制轮廓,并用255填充
		
		Mat res(front_roi.rows, front_roi.cols, CV_8UC3, Scalar(0, 0, 0));// 结果图
		front_roi.copyTo(res, hole); // copyTo
		
		++count; // 原视频中有一部分不需要
		if(count<frames/3-20)
			outputVideo << res;

		char c = (char)waitKey(10);
		if (c == 27) break;
	}
	
	return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值