extract frames every two seconds:(fps:29.97)
ffmpeg[fps:30]: #30,#90,#150,#210 [ffmpeg: select one frame at the center] [ffmpeg start from 1 ]
ext.c [fps:29]: #1, #59, #117, #175 (since the c code starts from 0, so in fact, it should be #2, #60,#118,#176)
code
#/bin/bash
inputvideo=6040577337
inputvideo=HVC929804
outpath="out_ffm_all"
outdir="out_ffm"
ffmpeg -i $inputvideo $outpath/out-%d.jpg
#ffmpeg -i $inputvideo -vf fps=0.5 $outdir/out-%d.jpg
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <cv.h>
#include <highgui.h>
#define PNG_COMPRESSION 3
CvSize FixedSize(CvSize size);
int main (int argc, char **argv)
{
if(argc < 3){
fprintf(stderr,"usage:eframe VIDEO_FILE frame_interval(sec) OutputDir\n");
return -1;
}
CvCapture* capture;
IplImage *frame = NULL;
char *filename = argv[1];
float sec_interval_frame = atof(argv[2]);
char *dir = argv[3];
int fps = 0, width = 0, height = 0, nframe = 0;
int n = 0;
int param[2];
char video[100],s1[100],s2[100];
char *ptr;
param[0] = CV_IMWRITE_PNG_COMPRESSION;
param[1] = PNG_COMPRESSION;
if((capture = cvCaptureFromFile(filename)) == NULL){
fprintf(stderr,"Can't open file %s\n.",filename);
return -1;
}
fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
width = cvGetCaptureProperty (capture, CV_CAP_PROP_FRAME_WIDTH);
height = cvGetCaptureProperty (capture, CV_CAP_PROP_FRAME_HEIGHT);
nframe = cvGetCaptureProperty (capture, CV_CAP_PROP_FRAME_COUNT);
if (fps == 0) {
fprintf(stderr,"Cannot get frame rate of the video file: %s.\n",filename);
return -1;
}
strcpy(video,dir);
strcat(video,"/");
if ((ptr = strrchr(filename,'/')) != NULL){
ptr++;
}else{
ptr = filename;
}
ptr = strtok(ptr,".");
strcat(video,ptr);
strcat(video,"_");
fprintf(stdout,"# %s,%d,%d\n",filename,fps,nframe);
while(1) {
frame = cvQueryFrame(capture);
if(frame == NULL) break;
if (n % (int)(fps*sec_interval_frame) == 1){
strcpy(s1,video);
sprintf(s2, "%08d", n);
strcat(s1, s2);
strcat(s1,".png");
cvSaveImage(s1, frame, param);
fprintf(stdout,"%d\n",n);
}
n++;
}
cvReleaseCapture(&capture);
if (n != nframe){
fprintf(stderr,"# Error: %d != %d\n",n,nframe);
}
return 0;
}
compression
两种方法最后得到的图像的大小也不一样,因为都进行了压缩.
http://www.howtogeek.com/203979/is-the-png-format-lossless-since-it-has-a-compression-parameter/
PNG is Compressed, but Lossless
The compression level is a trade-off between file size and encoding/decoding speed. To overly generalize, even non-image formats such as FLAC have similar concepts.
Different Compression Levels, Same Decoded Output
Although the file sizes are different due the the different compression levels, the actual decoded output will be identical. You can compare the MD5 hashes of the decoded outputs with ffmpeg using the MD5 muxer. This is best shown with some examples.
Create PNG Files
ffmpeg -i input -vframes 1 -compression_level 0 0.png
ffmpeg -i input -vframes 1 -compression_level 100 100.png
By default, ffmpeg will use -compression_level 100 for PNG output.
A quick, sloppy test showed that 100 (the highest compression level) took roughly three times longer to encode and five times longer to decode than 0 (the lowest compression level) in this example.
compare
instaill imagemagick
for ((j=1;j<29;j++));do
i=$[30+60*(j-1)]
echo "-----------------$j---------------"
echo $i
compare out_ffm/out-${j}.png out_ffm_all/out-${i}.png -metric RMSE 3.png
echo $[i-1]
compare out_ffm/out-${j}.png out_ffm_all/out-$[i-1].png -metric RMSE 3.png
echo $[i+1]
compare out_ffm/out-${j}.png out_ffm_all/out-$[i+1].png -metric RMSE 3.png
echo ""
done
result
extracted frame index | frame number | frame number | frame number |
---|---|---|---|
c code written by dang | ffmpeg(frame rate = 29.97) | ffmpeg(frame rate=29.917) | |
1 | 2 | 30 | 30 |
2 | 60 | 90 | 90 |
3 | 118 | 150 | 150 |
4 | 176 | 210 | 210 |
5 | 234 | 270 | 270 |
6 | 292 | 330 | 330 |
7 | 350 | 390 | 389 |
8 | 408 | 450 | 449 |
9 | 466 | 510 | 509 |
10 | 524 | 570 | 569 |
11 | 582 | 630 | 629 |
12 | 640 | 690 | 689 |
13 | 698 | 750 | 748 |
14 | 756 | 810 | 808 |
15 | 814 | 870 | 868 |
16 | 872 | 930 | 928 |
17 | 930 | 990 | 988 |
18 | 988 | 1049 | 1048 |
19 | 1046 | 1109 | 1107 |
20 | 1104 | 1169 | 1167 |
conclusion
We extract frame every two seconds.
I test two videos, the frame rate of which are 29.97 and 29.917.
[fn
is the frame number in the original video( frame number starts from 1) for the n
th extracted frame]
When using the code written by dang,
fn=2+int(frame_rate*2)*n=2+58n
When using the ffmpeg, I thought it should be,
fn=math.ceil(frame_rate)+math.ceil(frame_rate)*2=30+60n
However, for the video whose frame rate equals to 29.97,
when n
is larger than 17, …
for the video whose frame rate equals to 29.917,
when n
is larger than 6, …
fn=math.ceil((frame_rateframe_rate)*2)=30+60n
use ffmpeg to reproduce the frames extracted by the C code
${ffdir}/ffmpeg -i $video -vf trim=start_frame=1,framestep=$((`${ffdir}/ffprobe $video 2>&1 | grep -o '[0-9.]\+ fps' | sed -e 's/[\. ].*$//g'` * 2)) -loglevel error -nostdin $outdir/${videoname%.*}/${videoname%.*}_%08d.png 2>&1 > /dev/null | sed -e 's/^/ ['${videoname%.*}'] /g' >&2
ffmpeg -i $video -vf trim=start_frame=1,framestep=$((`ffprobe $video 2>&1 | grep -o '[0-9.]\+ fps' | sed -e 's/[\. ].*$//g'` * 2)) $out
#when the option of "trim=start_frame", the video starts from 0. so when set it to 1, the video starts from 2.
fps_int=`ffprobe $video 2>&1 | grep -o '[0-9.]\+ fps' | sed -e 's/[\. ].*$//g'`
framestep=$(($fps_int * 2))
ffmpeg -i $video -vf trim=start_frame=1,framestep=$framestep $out
compare
for ((i=1;i<29;i++));do
j=$[1+(i-1)*58]
printf -v m "%08d" $j
compare ffmpeg_c/${i}.png c/6040577337_${m}.png -metric RMSE 3.png
done