c++调用opencv库实现视频关键帧提取--灰度帧差法

为了提取关键帧,这里使用帧差法,语言为C语言,调用opencv库实现。

我们将视频第一帧设定为关键帧,后面的每一个帧与前一个关键帧进行像素帧差(灰度图),并设定阈值判断发生变化的像素点比例,通过此比例判断帧是否有发生突变,有发生突变的帧设定为关键帧并保存在特定文件夹中。以此循环将所有帧遍历完成即可。

在测试程序前,为了方便测试,我们将电影截取为两分钟的片段,不然太长了不方便测试,如果有备好的视频片段可以跳过这一步。在终端terminal打开窗口,输入以下命令,将(流浪地球)liulang_earth.mkv在一小时16分到一小时18分截取两分钟的视频片段并不保存为cutout1.mp4。之后对此两分钟视频提取关键帧。

 ffmpeg  -i ./liulang_earth.mkv -vcodec copy -acodec copy -ss 01:16:00 -to 01:18:00 ./cutout1.mp4 -y

程序我是放在文本编辑器中的,然后用g++编译。将pixels_cha.cpp编译生成pixels_cha可执行程序。g++编译命令如下

g++ `pkg-config opencv --cflags` pixels_cha.cpp -o pixels_cha `pkg-config opencv --libs

成功后再终端继续输入  ./pixels_cha   即可运行程序。如果使用其他编译器也是没有问题的。

程序如下

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/ml/ml.hpp>
#include <string.h>
 
using namespace std;
using namespace cv;
 
int main()
{
	system("color 2F");
	long currentFrame = 1;
	float p;
	VideoCapture cap;
	//这里放置需要提取关键字的视频
	cap.open("./cutout1.mp4");
	if(!cap.isOpened())//如果视频不能正常打开则返回
	{	cout<< "cannot open video!"<<endl;	
	        return 0;}
	Mat frame_key;
	cap>>frame_key;
	if(frame_key.empty())
		cout<< "frame_key is empty!" << endl;
	imshow("fram_1",frame_key);
	waitKey(20);
	stringstream str;  
	str<<"./keyframes_pixels_cha/" <<currentFrame << ".jpg";  
	cout << str.str() << endl;  
	imwrite(str.str() , frame_key);  
	Mat frame;
	Mat previousImage,currentImage,resultImage;	
	while(1)
	{
		//printf(">>>>>>>>>>>>>00");
		currentFrame++;
		Mat frame;
		cap>>frame;
		//printf(">>>>>>>>>>>>>1");
		if(frame.empty())
		{
			cout<< "frame is empty!" << endl;
			break;}
		imshow("fram_1",frame);
		waitKey(20);
	
		Mat srcImage_base, hsvImage_base;
		Mat srcImage_test1, hsvImage_test1;
		srcImage_base = frame_key;
		srcImage_test1 = frame;									
		//将图像从BGR色彩空间转换到 HSV色彩空间
		cvtColor(srcImage_base, previousImage, CV_BGR2GRAY);
		cvtColor(srcImage_test1, currentImage, CV_BGR2GRAY);		
		//printf(">>>>>>>>>>>>>2");
		absdiff(currentImage,previousImage,resultImage);  //帧差法,相减
		//printf(">>>>>>>>>>>>>3");
		threshold(resultImage, resultImage, 10, 255.0, CV_THRESH_BINARY); //二值化,像素值相差大于20则置为255,其余为0
		printf(">>>>>>>>>>>>>1\n");
		float counter = 0;
		float num = 0;
		// 统计两帧相减后图像素
		for (int i=0; i<resultImage.rows; i++)
		{	
			uchar *data = resultImage.ptr<uchar>(i); //获取每一行的指针
			for (int j=0;j<resultImage.cols; j++)
			{
				num = num+1;
				if (data[j] == 255) //访问到像素值
				{
					counter = counter+1;
				}					
			}
		}
		p = counter/num;
		// counter  num  p 分别为  有变化的像素点数  总像素点数目  比例
		printf(">>>>>>counter>>>>num>>>>p: %f  %f  %f  \n",counter,num,p);
		if (p > 0.55) //达到阈值的像素数达到一定的数量则保存该图像
		{
			//printf(">>>>>>>>>>>>>6");
			cout<< ">>>>>>>>>>>>>>>>>>>>.>>>>>>>.this frame is keyframe!"<<endl;
			frame_key = frame;
			cout << "正在写第" << currentFrame << "帧" << endl;  
			stringstream str;  	
			//写视频保存目录,我的是  ./keyframes_pixels_cha/xx.jpg  xx为当前帧的序号
			str<<"./keyframes_pixels_cha/" <<currentFrame << ".jpg";  
			cout << str.str() << endl;  
			imwrite(str.str() , frame_key);  
		}
		else
		{
			cout<< ">>>>>>>>>>>>.this frame is not keyframe!"<<endl;
		}

	}
}
		

程序运行结果如下。第一帧不知道怎么不显示图,不过双击后仍然可以打开的。关键帧名字代表该帧属于视频中的第几帧。结果很理想,没有太多冗余帧。程序中可调参数为threshold(resultImage, resultImage, 10, 255.0, CV_THRESH_BINARY)中的 10,10为二值化阈值,还有p,p为比例。通过调节这两个参数可以优化关键帧提取结果。

如果有需要视频片段+程序的压缩包的,我已经在csdn上传的资源https://download.csdn.net/download/weixin_39704651/11181825

也可以在评论留下邮箱我发送过去。欢迎讨论~

  • 12
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 79
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值