1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-03-23 04:24:35 +02:00

lavfi: add vf_iccdetect for parsing ICC profiles

This filter is designed to parse embedded ICC profiles and attempt
extracting colorspace tags from them, updating the AVFrame metadata
accordingly.

This is intentionally made a separate filter, rather than being part of
libavcodec itself, so that it's an opt-in behavior for the time being.
This also gives the user more flexibility to e.g. first attach an ICC
profile and then also set the colorspace tags from it.

This makes  possible, though not automatic.

Signed-off-by: Niklas Haas <git@haasn.dev>
This commit is contained in:
Niklas Haas 2022-04-11 13:26:07 +02:00
parent 5cfeaeef0c
commit 2cb0cebd11
5 changed files with 159 additions and 0 deletions

1
configure vendored

@ -3665,6 +3665,7 @@ gblur_vulkan_filter_deps="vulkan spirv_compiler"
hflip_vulkan_filter_deps="vulkan spirv_compiler"
histeq_filter_deps="gpl"
hqdn3d_filter_deps="gpl"
iccdetect_filter_deps="lcms2"
iccgen_filter_deps="lcms2"
interlace_filter_deps="gpl"
kerndeint_filter_deps="gpl"

@ -14384,6 +14384,20 @@ By default value is 0.
The @code{hysteresis} filter also supports the @ref{framesync} options.
@section iccdetect
Detect the colorspace from an embedded ICC profile (if present), and update
the frame's tags accordingly.
This filter accepts the following options:
@table @option
@item force
If true, the frame's existing colorspace tags will always be overridden by
values detected from an ICC profile. Otherwise, they will only be assigned if
they contain @code{unknown}. Enabled by default.
@end table
@section iccgen
Generate ICC profiles and attach them to frames.

@ -322,6 +322,7 @@ OBJS-$(CONFIG_HWMAP_FILTER) += vf_hwmap.o
OBJS-$(CONFIG_HWUPLOAD_CUDA_FILTER) += vf_hwupload_cuda.o
OBJS-$(CONFIG_HWUPLOAD_FILTER) += vf_hwupload.o
OBJS-$(CONFIG_HYSTERESIS_FILTER) += vf_hysteresis.o framesync.o
OBJS-$(CONFIG_ICCDETECT_FILTER) += vf_iccdetect.o fflcms2.o colorspace.o
OBJS-$(CONFIG_ICCGEN_FILTER) += vf_iccgen.o fflcms2.o colorspace.o
OBJS-$(CONFIG_IDENTITY_FILTER) += vf_identity.o
OBJS-$(CONFIG_IDET_FILTER) += vf_idet.o

@ -305,6 +305,7 @@ extern const AVFilter ff_vf_hwmap;
extern const AVFilter ff_vf_hwupload;
extern const AVFilter ff_vf_hwupload_cuda;
extern const AVFilter ff_vf_hysteresis;
extern const AVFilter ff_vf_iccdetect;
extern const AVFilter ff_vf_iccgen;
extern const AVFilter ff_vf_identity;
extern const AVFilter ff_vf_idet;

142
libavfilter/vf_iccdetect.c Normal file

@ -0,0 +1,142 @@
/*
* Copyright (c) 2022 Niklas Haas
* 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
* filter for generating ICC profiles
*/
#include <lcms2.h>
#include "libavutil/opt.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "fflcms2.h"
#include "internal.h"
typedef struct IccDetectContext {
const AVClass *class;
FFIccContext icc;
int force;
/* (cached) detected ICC profile values */
AVBufferRef *profile;
enum AVColorPrimaries profile_prim;
enum AVColorTransferCharacteristic profile_trc;
} IccDetectContext;
#define OFFSET(x) offsetof(IccDetectContext, x)
#define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
static const AVOption iccdetect_options[] = {
{ "force", "overwrite existing tags", OFFSET(force), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, VF },
{ NULL }
};
AVFILTER_DEFINE_CLASS(iccdetect);
static av_cold void iccdetect_uninit(AVFilterContext *avctx)
{
IccDetectContext *s = avctx->priv;
av_buffer_unref(&s->profile);
ff_icc_context_uninit(&s->icc);
}
static av_cold int iccdetect_init(AVFilterContext *avctx)
{
IccDetectContext *s = avctx->priv;
return ff_icc_context_init(&s->icc, avctx);
}
static int iccdetect_filter_frame(AVFilterLink *inlink, AVFrame *frame)
{
AVFilterContext *avctx = inlink->dst;
IccDetectContext *s = avctx->priv;
const AVFrameSideData *sd;
struct ColorPrimaries coeffs;
cmsHPROFILE profile;
int ret;
sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE);
if (!sd)
return ff_filter_frame(inlink->dst->outputs[0], frame);
if (s->profile && s->profile->data == sd->buf->data) {
/* No change from previous ICC profile */
goto done;
}
if ((ret = av_buffer_replace(&s->profile, sd->buf)) < 0)
return ret;
s->profile_prim = AVCOL_PRI_UNSPECIFIED;
s->profile_trc = AVCOL_TRC_UNSPECIFIED;
profile = cmsOpenProfileFromMemTHR(s->icc.ctx, sd->data, sd->size);
if (!profile)
return AVERROR_INVALIDDATA;
ret = ff_icc_profile_read_primaries(&s->icc, profile, &coeffs);
if (!ret)
ret = ff_icc_profile_detect_transfer(&s->icc, profile, &s->profile_trc);
cmsCloseProfile(profile);
if (ret < 0)
return ret;
s->profile_prim = ff_detect_color_primaries(&coeffs);
done:
if (s->profile_prim != AVCOL_PRI_UNSPECIFIED) {
if (s->force || frame->color_primaries == AVCOL_PRI_UNSPECIFIED)
frame->color_primaries = s->profile_prim;
}
if (s->profile_trc != AVCOL_TRC_UNSPECIFIED) {
if (s->force || frame->color_trc == AVCOL_TRC_UNSPECIFIED)
frame->color_trc = s->profile_trc;
}
return ff_filter_frame(inlink->dst->outputs[0], frame);
}
static const AVFilterPad iccdetect_inputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
.filter_frame = iccdetect_filter_frame,
},
};
static const AVFilterPad iccdetect_outputs[] = {
{
.name = "default",
.type = AVMEDIA_TYPE_VIDEO,
},
};
const AVFilter ff_vf_iccdetect = {
.name = "iccdetect",
.description = NULL_IF_CONFIG_SMALL("Detect and parse ICC profiles."),
.priv_size = sizeof(IccDetectContext),
.priv_class = &iccdetect_class,
.flags = AVFILTER_FLAG_METADATA_ONLY,
.init = &iccdetect_init,
.uninit = &iccdetect_uninit,
FILTER_INPUTS(iccdetect_inputs),
FILTER_OUTPUTS(iccdetect_outputs),
};