mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-01-24 13:56:33 +02:00
avfilter/vf_idet: add a repeated field detection
This can be useful for determining telecine. Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
This commit is contained in:
parent
07c3a4f693
commit
fdf22f973d
@ -5574,8 +5574,9 @@ value.
|
|||||||
|
|
||||||
Detect video interlacing type.
|
Detect video interlacing type.
|
||||||
|
|
||||||
This filter tries to detect if the input is interlaced or progressive,
|
This filter tries to detect if the input frames as interlaced, progressive,
|
||||||
top or bottom field first.
|
top or bottom field first. It will also try and detect fields that are
|
||||||
|
repeated between adjacent frames (a sign of telecine).
|
||||||
|
|
||||||
Single frame detection considers only immediately adjacent frames when classifying each frame.
|
Single frame detection considers only immediately adjacent frames when classifying each frame.
|
||||||
Multiple frame detection incorporates the classification history of previous frames.
|
Multiple frame detection incorporates the classification history of previous frames.
|
||||||
@ -5616,6 +5617,18 @@ Cumulative number of frames that could not be classified using single-frame dete
|
|||||||
|
|
||||||
@item multiple.undetermined
|
@item multiple.undetermined
|
||||||
Cumulative number of frames that could not be classified using multiple-frame detection.
|
Cumulative number of frames that could not be classified using multiple-frame detection.
|
||||||
|
|
||||||
|
@item repeated.current_frame
|
||||||
|
Which field in the current frame is repeated from the last. One of ``neither'', ``top'', or ``bottom''.
|
||||||
|
|
||||||
|
@item repeated.neither
|
||||||
|
Cumulative number of frames with no repeated field.
|
||||||
|
|
||||||
|
@item repeated.top
|
||||||
|
Cumulative number of frames with the top field repeated from the previous frame's top field.
|
||||||
|
|
||||||
|
@item repeated.bottom
|
||||||
|
Cumulative number of frames with the bottom field repeated from the previous frame's bottom field.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
The filter accepts the following options:
|
The filter accepts the following options:
|
||||||
@ -5625,6 +5638,8 @@ The filter accepts the following options:
|
|||||||
Set interlacing threshold.
|
Set interlacing threshold.
|
||||||
@item prog_thres
|
@item prog_thres
|
||||||
Set progressive threshold.
|
Set progressive threshold.
|
||||||
|
@item repeat_thres
|
||||||
|
Threshold for repeated field detection.
|
||||||
@item half_life
|
@item half_life
|
||||||
Number of frames after which a given frame's contribution to the
|
Number of frames after which a given frame's contribution to the
|
||||||
statistics is halved (i.e., it contributes only 0.5 to it's
|
statistics is halved (i.e., it contributes only 0.5 to it's
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
#define LIBAVFILTER_VERSION_MAJOR 5
|
#define LIBAVFILTER_VERSION_MAJOR 5
|
||||||
#define LIBAVFILTER_VERSION_MINOR 2
|
#define LIBAVFILTER_VERSION_MINOR 2
|
||||||
#define LIBAVFILTER_VERSION_MICRO 102
|
#define LIBAVFILTER_VERSION_MICRO 103
|
||||||
|
|
||||||
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
|
||||||
LIBAVFILTER_VERSION_MINOR, \
|
LIBAVFILTER_VERSION_MINOR, \
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
static const AVOption idet_options[] = {
|
static const AVOption idet_options[] = {
|
||||||
{ "intl_thres", "set interlacing threshold", OFFSET(interlace_threshold), AV_OPT_TYPE_FLOAT, {.dbl = 1.04}, -1, FLT_MAX, FLAGS },
|
{ "intl_thres", "set interlacing threshold", OFFSET(interlace_threshold), AV_OPT_TYPE_FLOAT, {.dbl = 1.04}, -1, FLT_MAX, FLAGS },
|
||||||
{ "prog_thres", "set progressive threshold", OFFSET(progressive_threshold), AV_OPT_TYPE_FLOAT, {.dbl = 1.5}, -1, FLT_MAX, FLAGS },
|
{ "prog_thres", "set progressive threshold", OFFSET(progressive_threshold), AV_OPT_TYPE_FLOAT, {.dbl = 1.5}, -1, FLT_MAX, FLAGS },
|
||||||
|
{ "rep_thres", "set repeat threshold", OFFSET(repeat_threshold), AV_OPT_TYPE_FLOAT, {.dbl = 3.0}, -1, FLT_MAX, FLAGS },
|
||||||
{ "half_life", "half life of cumulative statistics", OFFSET(half_life), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, -1, INT_MAX, FLAGS },
|
{ "half_life", "half life of cumulative statistics", OFFSET(half_life), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, -1, INT_MAX, FLAGS },
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
@ -72,6 +73,16 @@ static int av_dict_set_fxp(AVDictionary **pm, const char *key, uint64_t value, u
|
|||||||
return av_dict_set(pm, key, valuestr, flags);
|
return av_dict_set(pm, key, valuestr, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *rep2str(RepeatedField repeated_field)
|
||||||
|
{
|
||||||
|
switch(repeated_field) {
|
||||||
|
case REPEAT_NONE : return "neither";
|
||||||
|
case REPEAT_TOP : return "top";
|
||||||
|
case REPEAT_BOTTOM : return "bottom";
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
int ff_idet_filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
|
int ff_idet_filter_line_c(const uint8_t *a, const uint8_t *b, const uint8_t *c, int w)
|
||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
@ -104,7 +115,9 @@ static void filter(AVFilterContext *ctx)
|
|||||||
int y, i;
|
int y, i;
|
||||||
int64_t alpha[2]={0};
|
int64_t alpha[2]={0};
|
||||||
int64_t delta=0;
|
int64_t delta=0;
|
||||||
|
int64_t gamma[2]={0};
|
||||||
Type type, best_type;
|
Type type, best_type;
|
||||||
|
RepeatedField repeat;
|
||||||
int match = 0;
|
int match = 0;
|
||||||
AVDictionary **metadata = avpriv_frame_get_metadatap(idet->cur);
|
AVDictionary **metadata = avpriv_frame_get_metadatap(idet->cur);
|
||||||
|
|
||||||
@ -125,6 +138,7 @@ static void filter(AVFilterContext *ctx)
|
|||||||
alpha[ y &1] += idet->filter_line(cur-refs, prev, cur+refs, w);
|
alpha[ y &1] += idet->filter_line(cur-refs, prev, cur+refs, w);
|
||||||
alpha[(y^1)&1] += idet->filter_line(cur-refs, next, cur+refs, w);
|
alpha[(y^1)&1] += idet->filter_line(cur-refs, next, cur+refs, w);
|
||||||
delta += idet->filter_line(cur-refs, cur, cur+refs, w);
|
delta += idet->filter_line(cur-refs, cur, cur+refs, w);
|
||||||
|
gamma[(y^1)&1] += idet->filter_line(cur , prev, cur , w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,6 +152,14 @@ static void filter(AVFilterContext *ctx)
|
|||||||
type = UNDETERMINED;
|
type = UNDETERMINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( gamma[0] > idet->repeat_threshold * gamma[1] ){
|
||||||
|
repeat = REPEAT_TOP;
|
||||||
|
} else if ( gamma[1] > idet->repeat_threshold * gamma[0] ){
|
||||||
|
repeat = REPEAT_BOTTOM;
|
||||||
|
} else {
|
||||||
|
repeat = REPEAT_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
memmove(idet->history+1, idet->history, HIST_SIZE-1);
|
memmove(idet->history+1, idet->history, HIST_SIZE-1);
|
||||||
idet->history[0] = type;
|
idet->history[0] = type;
|
||||||
best_type = UNDETERMINED;
|
best_type = UNDETERMINED;
|
||||||
@ -170,20 +192,30 @@ static void filter(AVFilterContext *ctx)
|
|||||||
idet->cur->interlaced_frame = 0;
|
idet->cur->interlaced_frame = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(i=0; i<3; i++)
|
||||||
|
idet->repeats[i] = av_rescale(idet->repeats [i], idet->decay_coefficient, PRECISION);
|
||||||
|
|
||||||
for(i=0; i<4; i++){
|
for(i=0; i<4; i++){
|
||||||
idet->prestat [i] = av_rescale(idet->prestat [i], idet->decay_coefficient, PRECISION);
|
idet->prestat [i] = av_rescale(idet->prestat [i], idet->decay_coefficient, PRECISION);
|
||||||
idet->poststat[i] = av_rescale(idet->poststat[i], idet->decay_coefficient, PRECISION);
|
idet->poststat[i] = av_rescale(idet->poststat[i], idet->decay_coefficient, PRECISION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idet->total_repeats [ repeat] ++;
|
||||||
|
idet->repeats [ repeat] += PRECISION;
|
||||||
|
|
||||||
idet->total_prestat [ type] ++;
|
idet->total_prestat [ type] ++;
|
||||||
idet->prestat [ type] += PRECISION;
|
idet->prestat [ type] += PRECISION;
|
||||||
|
|
||||||
idet->total_poststat[idet->last_type] ++;
|
idet->total_poststat[idet->last_type] ++;
|
||||||
idet->poststat [idet->last_type] += PRECISION;
|
idet->poststat [idet->last_type] += PRECISION;
|
||||||
|
|
||||||
av_log(ctx, AV_LOG_DEBUG, "Single frame:%12s, Multi frame:%12s\n", type2str(type), type2str(idet->last_type));
|
av_log(ctx, AV_LOG_DEBUG, "Repeated Field:%12s, Single frame:%12s, Multi frame:%12s\n",
|
||||||
|
rep2str(repeat), type2str(type), type2str(idet->last_type));
|
||||||
|
|
||||||
|
av_dict_set (metadata, "lavfi.idet.repeated.current_frame", rep2str(repeat), 0);
|
||||||
|
av_dict_set_fxp(metadata, "lavfi.idet.repeated.neither", idet->repeats[REPEAT_NONE], 2, 0);
|
||||||
|
av_dict_set_fxp(metadata, "lavfi.idet.repeated.top", idet->repeats[REPEAT_TOP], 2, 0);
|
||||||
|
av_dict_set_fxp(metadata, "lavfi.idet.repeated.bottom", idet->repeats[REPEAT_BOTTOM], 2, 0);
|
||||||
|
|
||||||
av_dict_set (metadata, "lavfi.idet.single.current_frame", type2str(type), 0);
|
av_dict_set (metadata, "lavfi.idet.single.current_frame", type2str(type), 0);
|
||||||
av_dict_set_fxp(metadata, "lavfi.idet.single.tff", idet->prestat[TFF], 2 , 0);
|
av_dict_set_fxp(metadata, "lavfi.idet.single.tff", idet->prestat[TFF], 2 , 0);
|
||||||
@ -261,6 +293,11 @@ static av_cold void uninit(AVFilterContext *ctx)
|
|||||||
{
|
{
|
||||||
IDETContext *idet = ctx->priv;
|
IDETContext *idet = ctx->priv;
|
||||||
|
|
||||||
|
av_log(ctx, AV_LOG_INFO, "Repeated Fields: Neither:%6"PRId64" Top:%6"PRId64" Bottom:%6"PRId64"\n",
|
||||||
|
idet->total_repeats[REPEAT_NONE],
|
||||||
|
idet->total_repeats[REPEAT_TOP],
|
||||||
|
idet->total_repeats[REPEAT_BOTTOM]
|
||||||
|
);
|
||||||
av_log(ctx, AV_LOG_INFO, "Single frame detection: TFF:%6"PRId64" BFF:%6"PRId64" Progressive:%6"PRId64" Undetermined:%6"PRId64"\n",
|
av_log(ctx, AV_LOG_INFO, "Single frame detection: TFF:%6"PRId64" BFF:%6"PRId64" Progressive:%6"PRId64" Undetermined:%6"PRId64"\n",
|
||||||
idet->total_prestat[TFF],
|
idet->total_prestat[TFF],
|
||||||
idet->total_prestat[BFF],
|
idet->total_prestat[BFF],
|
||||||
|
@ -33,16 +33,26 @@ typedef enum {
|
|||||||
UNDETERMINED,
|
UNDETERMINED,
|
||||||
} Type;
|
} Type;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
REPEAT_NONE,
|
||||||
|
REPEAT_TOP,
|
||||||
|
REPEAT_BOTTOM,
|
||||||
|
} RepeatedField;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const AVClass *class;
|
const AVClass *class;
|
||||||
float interlace_threshold;
|
float interlace_threshold;
|
||||||
float progressive_threshold;
|
float progressive_threshold;
|
||||||
|
float repeat_threshold;
|
||||||
float half_life;
|
float half_life;
|
||||||
uint64_t decay_coefficient;
|
uint64_t decay_coefficient;
|
||||||
|
|
||||||
Type last_type;
|
Type last_type;
|
||||||
|
|
||||||
|
uint64_t repeats[3];
|
||||||
uint64_t prestat[4];
|
uint64_t prestat[4];
|
||||||
uint64_t poststat[4];
|
uint64_t poststat[4];
|
||||||
|
uint64_t total_repeats[3];
|
||||||
uint64_t total_prestat[4];
|
uint64_t total_prestat[4];
|
||||||
uint64_t total_poststat[4];
|
uint64_t total_poststat[4];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user