Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
F
ffmpeg
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
ffmpeg
Commits
15a2a29b
Commit
15a2a29b
authored
Oct 24, 2011
by
Clément Bœsch
Committed by
Clément Bœsch
Dec 27, 2011
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
lavfi: add thumbnail video filter.
parent
b7254437
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
266 additions
and
0 deletions
+266
-0
Changelog
Changelog
+1
-0
filters.texi
doc/filters.texi
+20
-0
Makefile
libavfilter/Makefile
+1
-0
allfilters.c
libavfilter/allfilters.c
+1
-0
vf_thumbnail.c
libavfilter/vf_thumbnail.c
+243
-0
No files found.
Changelog
View file @
15a2a29b
...
@@ -144,6 +144,7 @@ easier to use. The changes are:
...
@@ -144,6 +144,7 @@ easier to use. The changes are:
- Dxtory capture format decoder
- Dxtory capture format decoder
- cellauto source
- cellauto source
- Simple segmenting muxer
- Simple segmenting muxer
- thumbnail video filter
version 0.8:
version 0.8:
...
...
doc/filters.texi
View file @
15a2a29b
...
@@ -2400,6 +2400,26 @@ For example:
...
@@ -2400,6 +2400,26 @@ For example:
will create two separate outputs from the same input, one cropped and
will create two separate outputs from the same input, one cropped and
one padded.
one padded.
@section thumbnail
Select the most representative frame in a given sequence of consecutive frames.
It accepts as argument the frames batch size to analyze (default @var{N}=100);
in a set of @var{N} frames, the filter will pick one of them, and then handle
the next batch of @var{N} frames until the end.
Since the filter keeps track of the whole frames sequence, a bigger @var{N}
value will result in a higher memory usage, so a high value is not recommended.
The following example extract one picture each 50 frames:
@example
thumbnail=50
@end example
Complete example of a thumbnail creation with @command{ffmpeg}:
@example
ffmpeg -i in.avi -vf thumbnail,scale=300:200 -frames:v 1 out.png
@end example
@section transpose
@section transpose
Transpose rows with columns in the input video and optionally flip it.
Transpose rows with columns in the input video and optionally flip it.
...
...
libavfilter/Makefile
View file @
15a2a29b
...
@@ -79,6 +79,7 @@ OBJS-$(CONFIG_SETTB_FILTER) += vf_settb.o
...
@@ -79,6 +79,7 @@ OBJS-$(CONFIG_SETTB_FILTER) += vf_settb.o
OBJS-$(CONFIG_SHOWINFO_FILTER)
+=
vf_showinfo.o
OBJS-$(CONFIG_SHOWINFO_FILTER)
+=
vf_showinfo.o
OBJS-$(CONFIG_SLICIFY_FILTER)
+=
vf_slicify.o
OBJS-$(CONFIG_SLICIFY_FILTER)
+=
vf_slicify.o
OBJS-$(CONFIG_SPLIT_FILTER)
+=
vf_split.o
OBJS-$(CONFIG_SPLIT_FILTER)
+=
vf_split.o
OBJS-$(CONFIG_THUMBNAIL_FILTER)
+=
vf_thumbnail.o
OBJS-$(CONFIG_TRANSPOSE_FILTER)
+=
vf_transpose.o
OBJS-$(CONFIG_TRANSPOSE_FILTER)
+=
vf_transpose.o
OBJS-$(CONFIG_UNSHARP_FILTER)
+=
vf_unsharp.o
OBJS-$(CONFIG_UNSHARP_FILTER)
+=
vf_unsharp.o
OBJS-$(CONFIG_VFLIP_FILTER)
+=
vf_vflip.o
OBJS-$(CONFIG_VFLIP_FILTER)
+=
vf_vflip.o
...
...
libavfilter/allfilters.c
View file @
15a2a29b
...
@@ -89,6 +89,7 @@ void avfilter_register_all(void)
...
@@ -89,6 +89,7 @@ void avfilter_register_all(void)
REGISTER_FILTER
(
SHOWINFO
,
showinfo
,
vf
);
REGISTER_FILTER
(
SHOWINFO
,
showinfo
,
vf
);
REGISTER_FILTER
(
SLICIFY
,
slicify
,
vf
);
REGISTER_FILTER
(
SLICIFY
,
slicify
,
vf
);
REGISTER_FILTER
(
SPLIT
,
split
,
vf
);
REGISTER_FILTER
(
SPLIT
,
split
,
vf
);
REGISTER_FILTER
(
THUMBNAIL
,
thumbnail
,
vf
);
REGISTER_FILTER
(
TRANSPOSE
,
transpose
,
vf
);
REGISTER_FILTER
(
TRANSPOSE
,
transpose
,
vf
);
REGISTER_FILTER
(
UNSHARP
,
unsharp
,
vf
);
REGISTER_FILTER
(
UNSHARP
,
unsharp
,
vf
);
REGISTER_FILTER
(
VFLIP
,
vflip
,
vf
);
REGISTER_FILTER
(
VFLIP
,
vflip
,
vf
);
...
...
libavfilter/vf_thumbnail.c
0 → 100644
View file @
15a2a29b
/*
* Copyright (c) 2011 Smartjog S.A.S, Clément Bœsch <clement.boesch@smartjog.com>
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* Potential thumbnail lookup filter to reduce the risk of an inappropriate
* selection (such as a black frame) we could get with an absolute seek.
*
* Simplified version of algorithm by Vadim Zaliva <lord@crocodile.org>.
* @url http://notbrainsurgery.livejournal.com/29773.html
*/
#include "avfilter.h"
#define HIST_SIZE (3*256)
struct
thumb_frame
{
AVFilterBufferRef
*
buf
;
///< cached frame
int
histogram
[
HIST_SIZE
];
///< RGB color distribution histogram of the frame
};
typedef
struct
{
int
n
;
///< current frame
int
n_frames
;
///< number of frames for analysis
struct
thumb_frame
*
frames
;
///< the n_frames frames
}
ThumbContext
;
static
av_cold
int
init
(
AVFilterContext
*
ctx
,
const
char
*
args
,
void
*
opaque
)
{
ThumbContext
*
thumb
=
ctx
->
priv
;
if
(
!
args
)
{
thumb
->
n_frames
=
100
;
}
else
{
int
n
=
sscanf
(
args
,
"%d"
,
&
thumb
->
n_frames
);
if
(
n
!=
1
||
thumb
->
n_frames
<
2
)
{
thumb
->
n_frames
=
0
;
av_log
(
ctx
,
AV_LOG_ERROR
,
"Invalid number of frames specified (minimum is 2).
\n
"
);
return
AVERROR
(
ENOMEM
);
}
}
thumb
->
frames
=
av_calloc
(
thumb
->
n_frames
,
sizeof
(
*
thumb
->
frames
));
if
(
!
thumb
->
frames
)
{
av_log
(
ctx
,
AV_LOG_ERROR
,
"Allocation failure, try to lower the number of frames
\n
"
);
return
AVERROR
(
ENOMEM
);
}
av_log
(
ctx
,
AV_LOG_INFO
,
"batch size: %d frames
\n
"
,
thumb
->
n_frames
);
return
0
;
}
static
void
draw_slice
(
AVFilterLink
*
inlink
,
int
y
,
int
h
,
int
slice_dir
)
{
int
i
,
j
;
AVFilterContext
*
ctx
=
inlink
->
dst
;
ThumbContext
*
thumb
=
ctx
->
priv
;
int
*
hist
=
thumb
->
frames
[
thumb
->
n
].
histogram
;
AVFilterBufferRef
*
picref
=
inlink
->
cur_buf
;
const
uint8_t
*
p
=
picref
->
data
[
0
]
+
y
*
picref
->
linesize
[
0
];
// update current frame RGB histogram
for
(
j
=
0
;
j
<
h
;
j
++
)
{
for
(
i
=
0
;
i
<
inlink
->
w
;
i
++
)
{
hist
[
0
*
256
+
p
[
i
*
3
]]
++
;
hist
[
1
*
256
+
p
[
i
*
3
+
1
]]
++
;
hist
[
2
*
256
+
p
[
i
*
3
+
2
]]
++
;
}
p
+=
picref
->
linesize
[
0
];
}
}
/**
* @brief Compute Sum-square deviation to estimate "closeness".
* @param hist color distribution histogram
* @param median average color distribution histogram
* @return sum of squared errors
*/
static
double
frame_sum_square_err
(
const
int
*
hist
,
const
double
*
median
)
{
int
i
;
double
err
,
sum_sq_err
=
0
;
for
(
i
=
0
;
i
<
HIST_SIZE
;
i
++
)
{
err
=
median
[
i
]
-
(
double
)
hist
[
i
];
sum_sq_err
+=
err
*
err
;
}
return
sum_sq_err
;
}
static
void
end_frame
(
AVFilterLink
*
inlink
)
{
int
i
,
j
,
best_frame_idx
=
0
;
double
avg_hist
[
HIST_SIZE
]
=
{
0
},
sq_err
,
min_sq_err
=
-
1
;
AVFilterLink
*
outlink
=
inlink
->
dst
->
outputs
[
0
];
ThumbContext
*
thumb
=
inlink
->
dst
->
priv
;
AVFilterContext
*
ctx
=
inlink
->
dst
;
AVFilterBufferRef
*
picref
;
// keep a reference of each frame
thumb
->
frames
[
thumb
->
n
].
buf
=
inlink
->
cur_buf
;
// no selection until the buffer of N frames is filled up
if
(
thumb
->
n
<
thumb
->
n_frames
-
1
)
{
thumb
->
n
++
;
return
;
}
// average histogram of the N frames
for
(
j
=
0
;
j
<
FF_ARRAY_ELEMS
(
avg_hist
);
j
++
)
{
for
(
i
=
0
;
i
<
thumb
->
n_frames
;
i
++
)
avg_hist
[
j
]
+=
(
double
)
thumb
->
frames
[
i
].
histogram
[
j
];
avg_hist
[
j
]
/=
thumb
->
n_frames
;
}
// find the frame closer to the average using the sum of squared errors
for
(
i
=
0
;
i
<
thumb
->
n_frames
;
i
++
)
{
sq_err
=
frame_sum_square_err
(
thumb
->
frames
[
i
].
histogram
,
avg_hist
);
if
(
i
==
0
||
sq_err
<
min_sq_err
)
best_frame_idx
=
i
,
min_sq_err
=
sq_err
;
}
// free and reset everything (except the best frame buffer)
for
(
i
=
0
;
i
<
thumb
->
n_frames
;
i
++
)
{
memset
(
thumb
->
frames
[
i
].
histogram
,
0
,
sizeof
(
thumb
->
frames
[
i
].
histogram
));
if
(
i
==
best_frame_idx
)
continue
;
avfilter_unref_buffer
(
thumb
->
frames
[
i
].
buf
);
thumb
->
frames
[
i
].
buf
=
NULL
;
}
thumb
->
n
=
0
;
// raise the chosen one
picref
=
thumb
->
frames
[
best_frame_idx
].
buf
;
av_log
(
ctx
,
AV_LOG_INFO
,
"frame id #%d (pts_time=%f) selected
\n
"
,
best_frame_idx
,
picref
->
pts
*
av_q2d
(
inlink
->
time_base
));
avfilter_start_frame
(
outlink
,
picref
);
thumb
->
frames
[
best_frame_idx
].
buf
=
NULL
;
avfilter_draw_slice
(
outlink
,
0
,
inlink
->
h
,
1
);
avfilter_end_frame
(
outlink
);
}
static
av_cold
void
uninit
(
AVFilterContext
*
ctx
)
{
int
i
;
ThumbContext
*
thumb
=
ctx
->
priv
;
for
(
i
=
0
;
i
<
thumb
->
n_frames
&&
thumb
->
frames
[
i
].
buf
;
i
++
)
{
avfilter_unref_buffer
(
thumb
->
frames
[
i
].
buf
);
thumb
->
frames
[
i
].
buf
=
NULL
;
}
av_freep
(
&
thumb
->
frames
);
}
static
void
null_start_frame
(
AVFilterLink
*
link
,
AVFilterBufferRef
*
picref
)
{
}
static
int
request_frame
(
AVFilterLink
*
link
)
{
ThumbContext
*
thumb
=
link
->
src
->
priv
;
/* loop until a frame thumbnail is available (when a frame is queued,
* thumb->n is reset to zero) */
while
(
thumb
->
n
)
{
int
ret
=
avfilter_request_frame
(
link
->
src
->
inputs
[
0
]);
if
(
ret
<
0
)
return
ret
;
}
return
0
;
}
static
int
poll_frame
(
AVFilterLink
*
link
)
{
ThumbContext
*
thumb
=
link
->
src
->
priv
;
AVFilterLink
*
inlink
=
link
->
src
->
inputs
[
0
];
int
ret
,
available_frames
=
avfilter_poll_frame
(
inlink
);
/* If the input link is not able to provide any frame, we can't do anything
* at the moment and thus have zero thumbnail available. */
if
(
!
available_frames
)
return
0
;
/* Since at least one frame is available and the next frame will allow us
* to compute a thumbnail, we can return 1 frame. */
if
(
thumb
->
n
==
thumb
->
n_frames
-
1
)
return
1
;
/* we have some frame(s) available in the input link, but not yet enough to
* output a thumbnail, so we request more */
ret
=
avfilter_request_frame
(
inlink
);
return
ret
<
0
?
ret
:
0
;
}
static
int
query_formats
(
AVFilterContext
*
ctx
)
{
static
const
enum
PixelFormat
pix_fmts
[]
=
{
PIX_FMT_RGB24
,
PIX_FMT_BGR24
,
PIX_FMT_NONE
};
avfilter_set_common_pixel_formats
(
ctx
,
avfilter_make_format_list
(
pix_fmts
));
return
0
;
}
AVFilter
avfilter_vf_thumbnail
=
{
.
name
=
"thumbnail"
,
.
description
=
NULL_IF_CONFIG_SMALL
(
"Select the most representative frame in a given sequence of consecutive frames."
),
.
priv_size
=
sizeof
(
ThumbContext
),
.
init
=
init
,
.
uninit
=
uninit
,
.
query_formats
=
query_formats
,
.
inputs
=
(
const
AVFilterPad
[])
{
{
.
name
=
"default"
,
.
type
=
AVMEDIA_TYPE_VIDEO
,
.
get_video_buffer
=
avfilter_null_get_video_buffer
,
.
start_frame
=
null_start_frame
,
.
draw_slice
=
draw_slice
,
.
end_frame
=
end_frame
,
},{
.
name
=
NULL
}
},
.
outputs
=
(
const
AVFilterPad
[])
{
{
.
name
=
"default"
,
.
type
=
AVMEDIA_TYPE_VIDEO
,
.
request_frame
=
request_frame
,
.
poll_frame
=
poll_frame
,
.
rej_perms
=
AV_PERM_REUSE2
,
},{
.
name
=
NULL
}
},
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment