* [FFmpeg-devel] [PATCH] avfilter: add inverse tone mapping filter
@ 2025-07-01 6:24 Sarthak Indurkhya via ffmpeg-devel
0 siblings, 0 replies; 3+ messages in thread
From: Sarthak Indurkhya via ffmpeg-devel @ 2025-07-01 6:24 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Sarthak Indurkhya
[-- Attachment #1: Type: message/rfc822, Size: 28676 bytes --]
From: Sarthak Indurkhya <sindurkhya@adobe.com>
To: "ffmpeg-devel@ffmpeg.org" <ffmpeg-devel@ffmpeg.org>
Subject: [PATCH] avfilter: add inverse tone mapping filter
Date: Tue, 1 Jul 2025 06:24:01 +0000
Message-ID: <BN0PR02MB788796CDC12FC5E2B53FAD17D441A@BN0PR02MB7887.namprd02.prod.outlook.com>
Hello FFmpeg developers,
This patch introduces a new video filter called inversetonemap for FFmpeg.
The filter performs SDR to HDR conversion by mapping SDR BT.709 video to HDR BT.2020 PQ, using local adaptation and inverse tone mapping. The goal is to provide a simple, flexible tool for upconverting SDR content for HDR displays, with local adaptation, tone curve sensitivity, and chroma processing.
Please review.
Thanks,
Sarthak
---
From fdcd2f2c2b570bb59d1bf7a4de6df1865a0b0023 Mon Sep 17 00:00:00 2001
From: Sarthak Indurkhya sarthak@Sarthaks-MacBook-Pro.local<mailto:sarthak@Sarthaks-MacBook-Pro.local>
Date: Tue, 1 Jul 2025 11:12:20 +0530
Subject: [PATCH] avfilter: add inversetonemap filter
This filter performs inverse tone mapping from SDR to HDR using local adaptation and PQ mapping.
Usage:
ffmpeg -i input.mp4 -vf inversetonemap -c:v libx265 -x265-params range=full -crf 20 -tag:v hvc1 output.mp4
Signed-off-by: Sarthak Indurkhya sarthak@Sarthaks-MacBook-Pro.local<mailto:sarthak@Sarthaks-MacBook-Pro.local>
---
libavfilter/vf_inversetonemap.c | 503 ++++++++++++++++++++++++++++++++
1 file changed, 503 insertions(+)
create mode 100644 libavfilter/vf_inversetonemap.c
diff --git a/libavfilter/vf_inversetonemap.c b/libavfilter/vf_inversetonemap.c
new file mode 100644
index 0000000000..28ea1ef29e
--- /dev/null
+++ b/libavfilter/vf_inversetonemap.c
@@ -0,0 +1,503 @@
+/**
+ * @file
+ * @brief SDR to HDR inverse tone mapping filter for FFmpeg
+ * @author Sarthak Indurkhya
+ *
+ * This filter converts SDR BT.709 video to HDR BT.2020 PQ,
+ * using local adaptation and inverse tone mapping.
+ */
+
+
+#include <float.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libavutil/pixdesc.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/log.h"
+#include "avfilter.h"
+#include "libavutil/imgutils.h"
+#include "formats.h"
+#include "filters.h"
+#include "libswscale/swscale.h"
+#include "libavutil/frame.h"
+#include "libavfilter/video.h"
+#include <libavutil/common.h>
+
+#define OFFSET(x) offsetof(FilterContext, x)
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
+
+typedef struct FilterContext {
+ const AVClass *class;
+ float sigma_spatial;
+ float sigma_range;
+ float n;
+ float HDR_max;
+} FilterContext;
+
+//declaring lookup table
+static float bt709_gamma_lut[256];
+
+static void init_bt709_gamma_lut(void) {
+ for (int i = 0; i < 256; i++) {
+ float v = i / 255.0f;
+ if (v < 0.081f)
+ bt709_gamma_lut[i] = v / 4.5f;
+ else
+ bt709_gamma_lut[i] = powf((v + 0.099f) / 1.099f, 1.0f / 0.45f);
+ }
+}
+
+// Rec.709 to Rec.2020 matrix
+static const float bt709_to_bt2020[3][3] = {
+ {0.6274f, 0.3293f, 0.0433f},
+ {0.0691f, 0.9195f, 0.0114f},
+ {0.0164f, 0.0880f, 0.8956f}
+};
+
+// PQ transfer function
+#define PQ_M1 0.1593017578125f
+#define PQ_M2 78.84375f
+#define PQ_C1 0.8359375f
+#define PQ_C2 18.8515625f
+#define PQ_C3 18.6875f
+
+static float linear_to_pq(float L) {
+ float Lm;
+ L /= 10000.0f;
+ Lm = powf(L, PQ_M1);
+ return powf((PQ_C1 + PQ_C2 * Lm) / (1.0f + PQ_C3 * Lm), PQ_M2);
+}
+
+static void compute_local_adaptation(const float *R_full, float *sigma, int width, int height,
+ float sigma_spatial, float sigma_range, float HDR_max) {
+
+ int radius = (int)(3 * sigma_spatial);
+ int npix = width * height;
+ float *temp_blur = av_mallocz(npix * sizeof(float));
+ float *spatial_weights = av_malloc((2 * radius + 1) * sizeof(float));
+
+ // 1D Gaussian kernel for separable blur
+ for (int i = -radius; i <= radius; i++) {
+ spatial_weights[i + radius] = expf(-(i * i) / (2 * sigma_spatial * sigma_spatial));
+ }
+
+ // Scene max luminance
+ float scene_max = 1e-6f;
+ for (int i = 0; i < npix; i++) {
+ if (R_full[i] > scene_max)
+ scene_max = R_full[i];
+ }
+ float hdr_scale = HDR_max / scene_max;
+
+ // ---- Horizontal blur pass ----
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float sum = 0.0f, sum_weights = 0.0f;
+ for (int dx = -radius; dx <= radius; dx++) {
+ int nx = x + dx;
+ if (nx < 0 || nx >= width) continue;
+
+ float w = spatial_weights[dx + radius];
+ float intensity_diff = R_full[y * width + nx] - R_full[y * width + x];
+ float w_range = expf(-(intensity_diff * intensity_diff) / (2 * sigma_range * sigma_range));
+
+ float weight = w * w_range;
+ sum += weight * R_full[y * width + nx];
+ sum_weights += weight;
+ }
+ temp_blur[y * width + x] = (sum_weights > 0.0f) ? (sum / sum_weights) : R_full[y * width + x];
+ }
+ }
+
+ // ---- Vertical blur pass ----
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float sum = 0.0f, sum_weights = 0.0f;
+ for (int dy = -radius; dy <= radius; dy++) {
+ int ny = y + dy;
+ if (ny < 0 || ny >= height) continue;
+
+ float w = spatial_weights[dy + radius];
+ float intensity_diff = temp_blur[ny * width + x] - temp_blur[y * width + x];
+ float w_range = expf(-(intensity_diff * intensity_diff) / (2 * sigma_range * sigma_range));
+
+ float weight = w * w_range;
+ sum += weight * temp_blur[ny * width + x];
+ sum_weights += weight;
+ }
+ sigma[y * width + x] = (sum_weights > 0.0f) ? (sum / sum_weights) * hdr_scale : R_full[y * width + x];
+ }
+ }
+
+ av_free(temp_blur);
+ av_free(spatial_weights);
+}
+static void compute_local_adaptation_fast(const float *R_full, float *sigma, int width, int height,
+ float sigma_spatial, float sigma_range, float HDR_max)
+{
+ const int scale = 4;
+ int down_w = width / scale;
+ int down_h = height / scale;
+ int npix_small = down_w * down_h;
+
+ // Allocating downsampled and result buffers
+ float *R_small = av_malloc(npix_small * sizeof(float));
+ float *sigma_small = av_malloc(npix_small * sizeof(float));
+
+ // Simple box downsampling
+ for (int y = 0; y < down_h; y++) {
+ for (int x = 0; x < down_w; x++) {
+ float sum = 0.0f;
+ for (int dy = 0; dy < scale; dy++) {
+ for (int dx = 0; dx < scale; dx++) {
+ int sx = x * scale + dx;
+ int sy = y * scale + dy;
+ if (sx < width && sy < height) {
+ sum += R_full[sy * width + sx];
+ }
+ }
+ }
+ R_small[y * down_w + x] = sum / (scale * scale);
+ }
+ }
+
+ compute_local_adaptation(R_small, sigma_small, down_w, down_h, sigma_spatial, sigma_range, HDR_max);
+
+ // Upsampling sigma_small -> sigma
+ struct SwsContext *sws_ctx = sws_getContext(down_w, down_h, AV_PIX_FMT_GRAYF32,
+ width, height, AV_PIX_FMT_GRAYF32,
+ SWS_BILINEAR, NULL, NULL, NULL);
+
+ const uint8_t *src[1] = {(uint8_t *)sigma_small};
+ int src_linesize[1] = {down_w * sizeof(float)};
+ uint8_t *dst[1] = {(uint8_t *)sigma};
+ int dst_linesize[1] = {width * sizeof(float)};
+ sws_scale(sws_ctx, src, src_linesize, 0, down_h, dst, dst_linesize);
+
+ sws_freeContext(sws_ctx);
+ av_free(R_small);
+ av_free(sigma_small);
+}
+
+static void compute_hdr_intensity(const float *R_full, const float *sigma, float *I_hdr,
+ int width, int height, float n, float R_max) {
+ const float delta = 1e-6f; // 1e-6f
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ float R = R_full[y * width + x] / R_max;
+ R = fminf(R, 0.95f);
+ float denom = (1.0f - R) + delta;
+ I_hdr[y * width + x] = powf(R / denom, 1.0f / n) * sigma[y * width + x];
+ }
+ }
+}
+
+static void inverse_tone_map_linear_rgb(
+ const float *R, const float *G, const float *B,
+ float *R_hdr, float *G_hdr, float *B_hdr,
+ int width, int height, FilterContext *s)
+{
+ int npix = width * height;
+ float *Y = av_malloc(npix * sizeof(float));
+ float *Y_sigma = av_malloc(npix * sizeof(float));
+ float *Y_hdr = av_malloc(npix * sizeof(float));
+
+ // Computing luminance (BT.2020)
+ for (int i = 0; i < npix; i++)
+ Y[i] = 0.2627f * R[i] + 0.6780f * G[i] + 0.0593f * B[i];
+
+ compute_local_adaptation_fast(Y, Y_sigma, width, height,
+ s->sigma_spatial, s->sigma_range, s->HDR_max);
+
+ compute_hdr_intensity(Y, Y_sigma, Y_hdr, width, height, s->n, 1.0f);
+
+ // Scaling RGB channels
+ for (int i = 0; i < npix; i++) {
+ float scale = Y_hdr[i] / (Y[i] + 1e-6f);
+ R_hdr[i] = R[i] * scale;
+ G_hdr[i] = G[i] * scale;
+ B_hdr[i] = B[i] * scale;
+ }
+
+ av_free(Y);
+ av_free(Y_sigma);
+ av_free(Y_hdr);
+}
+
+static void dither_pq_to_10bit(const float *pq, uint16_t *y_temp, int width, int height) {
+ float *error = av_calloc(width * height, sizeof(float));
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int idx = y * width + x;
+ float val = fminf(fmaxf(pq[idx] + error[idx], 0.0f), 1.0f);
+ uint16_t q = (uint16_t)(val * 1023.0f + 0.5f);
+ y_temp[idx] = q;
+ float err = val - (q / 1023.0f);
+ if (x + 1 < width) error[idx + 1] += err * 7.0f / 16.0f;
+ if (y + 1 < height) {
+ if (x > 0) error[idx + width - 1] += err * 1.0f / 16.0f;
+ error[idx + width] += err * 5.0f / 16.0f;
+ if (x + 1 < width) error[idx + width + 1] += err * 3.0f / 16.0f;
+ }
+ }
+ }
+ av_free(error);
+}
+
+static int fil_func(AVFilterLink *inlink, AVFrame *in) {
+ AVFilterContext *ctx = inlink->dst;
+ FilterContext *s = ctx->priv;
+ int width = in->width, height = in->height, npix = width * height;
+
+ // Converting to RGB24
+ struct SwsContext *sws_ctx = sws_getContext(
+ width, height, in->format,
+ width, height, AV_PIX_FMT_RGB24,
+ SWS_BILINEAR, NULL, NULL, NULL
+ );
+ if (!sws_ctx) return AVERROR(ENOMEM);
+
+ AVFrame *rgb_frame = av_frame_alloc();
+ rgb_frame->format = AV_PIX_FMT_RGB24;
+ rgb_frame->width = width;
+ rgb_frame->height = height;
+ av_frame_get_buffer(rgb_frame, 32);
+ av_frame_copy_props(rgb_frame, in);
+
+ sws_scale(sws_ctx, (const uint8_t *const *)in->data, in->linesize, 0, height,
+ rgb_frame->data, rgb_frame->linesize);
+ sws_freeContext(sws_ctx);
+
+ //Calling gamma lookup table initialization
+ init_bt709_gamma_lut();
+ // Gamma linearization & gamut mapping
+ float *R = av_malloc(npix * sizeof(float));
+ float *G = av_malloc(npix * sizeof(float));
+ float *B = av_malloc(npix * sizeof(float));
+ for (int y = 0; y < height; y++) {
+ uint8_t *row = rgb_frame->data[0] + y * rgb_frame->linesize[0];
+ for (int x = 0; x < width; x++) {
+ float r = bt709_gamma_lut[row[x * 3 + 0]];
+ float g = bt709_gamma_lut[row[x * 3 + 1]];
+ float b = bt709_gamma_lut[row[x * 3 + 2]];
+ R[y * width + x] = bt709_to_bt2020[0][0]*r + bt709_to_bt2020[0][1]*g + bt709_to_bt2020[0][2]*b;
+ G[y * width + x] = bt709_to_bt2020[1][0]*r + bt709_to_bt2020[1][1]*g + bt709_to_bt2020[1][2]*b;
+ B[y * width + x] = bt709_to_bt2020[2][0]*r + bt709_to_bt2020[2][1]*g + bt709_to_bt2020[2][2]*b;
+ }
+ }
+
+ // Inverse Tone Mapping
+ float *R_hdr = av_malloc(npix * sizeof(float));
+ float *G_hdr = av_malloc(npix * sizeof(float));
+ float *B_hdr = av_malloc(npix * sizeof(float));
+
+ inverse_tone_map_linear_rgb(R, G, B, R_hdr, G_hdr, B_hdr, width, height, s);
+
+ float exposure = 3.5f; //4.0
+ float contrast = 1.02f; //1.10
+ float black = 0.03f, white = 8.0f; //0.04 //12.0
+ float s_curve_pow = 1.00f; //0.70
+
+ for (int i = 0; i < npix; i++) {
+ float r = R[i] * exposure, g = G[i] * exposure, b = B[i] * exposure;
+ float lum = 0.2627f * r + 0.6780f * g + 0.0593f * b;
+
+
+ float tm = (lum - black) / (white - black + 1e-6f);
+ tm = fmaxf(fminf(tm, 1.0f), 0.0f);
+ tm = powf(tm, s_curve_pow);
+
+ float scale = (lum > 1e-6f) ? tm / lum : 0.0f;
+ r *= scale;
+ g *= scale;
+ b *= scale;
+
+ // Contrast
+ r = (r - 0.5f) * contrast + 0.5f;
+ g = (g - 0.5f) * contrast + 0.5f;
+ b = (b - 0.5f) * contrast + 0.5f;
+
+ // White balance correction
+ float white_balance_r = 1.0f;
+ float white_balance_g = 1.0f;
+ float white_balance_b = 1.1f;
+
+ r *= white_balance_r;
+ g *= white_balance_g;
+ b *= white_balance_b;
+
+ // Final clamp
+ r = fminf(fmaxf(r, 0.0f), 1.0f);
+ g = fminf(fmaxf(g, 0.0f), 1.0f);
+ b = fminf(fmaxf(b, 0.0f), 1.0f);
+
+ R_hdr[i] = r;
+ G_hdr[i] = g;
+ B_hdr[i] = b;
+ }
+
+ //Computing linear luminance (BT.2020)
+ float *Y = av_malloc(npix * sizeof(float));
+ for (int i = 0; i < npix; i++)
+ Y[i] = 0.2627f * R_hdr[i] + 0.6780f * G_hdr[i] + 0.0593f * B_hdr[i];
+
+ // Applying PQ transfer
+ float *pq = av_malloc(npix * sizeof(float));
+ for (int i = 0; i < npix; i++) {
+ pq[i] = linear_to_pq(Y[i] * (10000.0f / s->HDR_max));
+ pq[i] = fminf(fmaxf(pq[i], 0.0f), 1.0f);
+ }
+ //Allocating output frame (YUV420P10LE)
+ AVFrame *out = av_frame_alloc();
+ out->format = AV_PIX_FMT_YUV420P10LE;
+ out->width = width;
+ out->height = height;
+ av_frame_get_buffer(out, 32);
+ av_frame_copy_props(out, in);
+
+ // Setting HDR metadata
+ out->color_primaries = AVCOL_PRI_BT2020;
+ out->color_trc = AVCOL_TRC_SMPTE2084; // PQ
+ out->colorspace = AVCOL_SPC_BT2020_NCL;
+ out->color_range = AVCOL_RANGE_MPEG; // Full range for HDR
+
+ // Setting mastering display metadata if available
+ if (out->metadata) {
+ // Mastering display primaries (BT.2020)
+ av_dict_set(&out->metadata, "mastering_display_primaries",
+ "0.708,0.292,0.170,0.797,0.131,0.046,0.3127,0.3290", 0);
+
+ // Mastering display luminance (in nits)
+ char luminance_str[64];
+ snprintf(luminance_str, sizeof(luminance_str), "%.1f,%.1f",
+ s->HDR_max, 0.001f); // Max luminance, min luminance
+ av_dict_set(&out->metadata, "mastering_display_luminance", luminance_str, 0);
+
+ // Content light level
+ char content_light_str[64];
+ float max_content_light = 0.0f;
+ for (int i = 0; i < npix; i++) {
+ max_content_light = fmaxf(max_content_light, Y[i]);
+ }
+ max_content_light = linear_to_pq(max_content_light * (10000.0f / s->HDR_max)) * 10000.0f;
+ snprintf(content_light_str, sizeof(content_light_str), "%.0f,%.0f",
+ max_content_light, max_content_light * 0.5f);
+ av_dict_set(&out->metadata, "content_light_level", content_light_str, 0);
+ }
+
+ // Clearing Y, U, V planes
+ av_frame_make_writable(out);
+ for (int y = 0; y < height; y++)
+ memset(out->data[0] + y * out->linesize[0], 0, out->linesize[0]);
+
+ // Dithering PQ to 10-bit Y plane
+ uint16_t *y_temp = av_malloc(npix * sizeof(uint16_t));
+ dither_pq_to_10bit(pq, y_temp, width, height);
+ for (int y = 0; y < height; y++) {
+ uint16_t *row = (uint16_t *)(out->data[0] + y * out->linesize[0]);
+ memcpy(row, y_temp + y * width, width * sizeof(uint16_t));
+ }
+
+ // Computing and encoding U, V chroma planes from original SDR RGB
+ for (int y = 0; y < height / 2; y++) {
+ uint16_t *u_row = (uint16_t *)(out->data[1] + y * out->linesize[1]);
+ uint16_t *v_row = (uint16_t *)(out->data[2] + y * out->linesize[2]);
+ for (int x = 0; x < width / 2; x++) {
+ float r = 0.0f, g = 0.0f, b = 0.0f;
+ for (int dy = 0; dy < 2; dy++) {
+ for (int dx = 0; dx < 2; dx++) {
+ int src_x = x * 2 + dx;
+ int src_y = y * 2 + dy;
+ if (src_x < width && src_y < height) {
+ int idx = src_y * width + src_x;
+ r += R[idx];
+ g += G[idx];
+ b += B[idx];
+ }
+ }
+ }
+ r /= 4.0f; g /= 4.0f; b /= 4.0f;
+ float Y_sdr = 0.2627f * r + 0.6780f * g + 0.0593f * b;
+ float U = (b - Y_sdr) / 1.8814f;
+ float V = (r - Y_sdr) / 1.4746f;
+ float chroma_blend = 1.0f - fminf(Y_sdr, 1.0f);
+ float chroma_boost = 0.85f * chroma_blend + 0.85f * (1.0f - chroma_blend); //0.8
+ // float chroma_boost = 1.08f;
+ U *= chroma_boost;
+ V *= chroma_boost;
+ U = fmaxf(fminf(U, 0.45f), -0.45f);
+ V = fmaxf(fminf(V, 0.45f), -0.45f);
+ u_row[x] = (uint16_t)roundf(U * 512.0f + 512.0f);
+ v_row[x] = (uint16_t)roundf(V * 512.0f + 512.0f);
+ }
+ }
+
+ av_free(R); av_free(G); av_free(B);
+ av_free(R_hdr); av_free(G_hdr); av_free(B_hdr);
+ av_free(Y); av_free(pq); av_free(y_temp);
+ av_frame_free(&rgb_frame);
+ av_frame_free(&in);
+
+ return ff_filter_frame(ctx->outputs[0], out);
+}
+
+static const AVOption fil_options[] = {
+ { "sigma_spatial", "Spatial sigma", OFFSET(sigma_spatial), AV_OPT_TYPE_FLOAT, {.dbl=2.0}, 1.0, 50.0, FLAGS },
+ { "sigma_range", "Range sigma", OFFSET(sigma_range), AV_OPT_TYPE_FLOAT, {.dbl=0.3}, 0.01, 1.0, FLAGS },
+ { "n", "Sensitivity exponent", OFFSET(n), AV_OPT_TYPE_FLOAT, {.dbl=0.9}, 0.5, 2.0, FLAGS },
+ { "hdr_max", "Peak HDR luminance", OFFSET(HDR_max), AV_OPT_TYPE_FLOAT, {.dbl=25.0}, 10.0, 10000.0, FLAGS },
+ { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(fil);
+
+static const AVFilterPad fil_inputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .filter_frame = fil_func,
+ }
+};
+
+static const AVFilterPad fil_outputs[] = {
+ {
+ .name = "default",
+ .type = AVMEDIA_TYPE_VIDEO,
+ }
+};
+
+static int ff_filter_init(AVFilterContext *avctx) {
+ av_log_set_level(AV_LOG_DEBUG);
+ av_log(avctx, AV_LOG_INFO, "Initializing filter with 1 input and 1 output\n");
+ return 0;
+}
+
+static void ff_filter_uninit(AVFilterContext *avctx)
+{
+
+}
+
+const FFFilter ff_vf_inversetonemap = {
+ .p.name = "inversetonemap",
+ .p.description = "SDR to HDR inverse tone mapping filter",
+ .p.priv_class = &fil_class,
+ .p.flags = AVFILTER_FLAG_SLICE_THREADS,
+ .priv_size = sizeof(FilterContext),
+ .init = &ff_filter_init,
+ .uninit = &ff_filter_uninit,
+ FILTER_INPUTS(fil_inputs),
+ FILTER_OUTPUTS(fil_outputs),
+ FILTER_PIXFMTS(AV_PIX_FMT_YUV420P10LE),
+};
+
+
+
+
+
+
+
+
--
2.49.0
Get Outlook for Mac <https://aka.ms/GetOutlookForMac>
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [FFmpeg-devel] [PATCH] avfilter: add inverse tone mapping filter
2025-06-30 9:44 Sarthak Indurkhya via ffmpeg-devel
@ 2025-06-30 14:05 ` Kacper Michajlow
0 siblings, 0 replies; 3+ messages in thread
From: Kacper Michajlow @ 2025-06-30 14:05 UTC (permalink / raw)
To: FFmpeg development discussions and patches; +Cc: Sarthak Indurkhya
On Mon, 30 Jun 2025 at 11:45, Sarthak Indurkhya via ffmpeg-devel
<ffmpeg-devel@ffmpeg.org> wrote:
>
>
>
>
> ---------- Forwarded message ----------
> From: Sarthak Indurkhya <sindurkhya@adobe.com>
> To: "ffmpeg-devel@ffmpeg.org" <ffmpeg-devel@ffmpeg.org>
> Cc:
> Bcc:
> Date: Mon, 30 Jun 2025 09:44:54 +0000
> Subject: [PATCH] avfilter: add inverse tone mapping filter
> Hello FFmpeg developers,
> This patch introduces a new video filter called inversetonemap for FFmpeg.
> The filter performs SDR to HDR conversion by mapping SDR BT.709 video to HDR BT.2020 PQ, using local adaptation and inverse tone mapping. The goal is to provide a simple, flexible tool for upconverting SDR content for HDR displays, with local adaptation, tone curve sensitivity, and chroma processing.
>
> Patch summary:
>
> * Implements local adaptation and inverse tone mapping for SDR-to-HDR conversion
> * Performs gamma correction, color space transformation, and PQ encoding
> * Supports configurable parameters: spatial sigma, range sigma, sensitivity exponent, HDR peak luminance
> * Outputs 10-bit YUV420P10LE BT.2020 PQ with HDR metadata
> * Includes local adaptation code, PQ transfer function, error diffusion dithering, and chroma recomputation
>
> Commit message:
> avfilter: add inversetonemap filter
> This filter converts SDR BT.709 video to HDR BT.2020 PQ using local adaptation and inverse tone mapping. It supports user-configurable parameters for spatial/range sigma, tone mapping exponent, and HDR peak luminance.
> The filter performs gamma correction, color conversion, PQ encoding, and outputs YUV420P10LE with HDR metadata.
>
> The patch file is attached to this email.
It is not. It's on the sharepoint and we don't have access to this
file. Please attach patches as an attachment or plain text.
- Kacper
> Best regards,
> Sarthak
> [?patch icon] 0002-avfilter-add-myfilter-filter.patch<https://adobe-my.sharepoint.com/:u:/p/sindurkhya/EbrQbZzcik9Eg7ywwTfQDN4BVRP4SavI-n_NXHBVs3ehog>
>
> Get Outlook for Mac <https://aka.ms/GetOutlookForMac>
>
>
>
> ---------- Forwarded message ----------
> From: Sarthak Indurkhya via ffmpeg-devel <ffmpeg-devel@ffmpeg.org>
> To: "ffmpeg-devel@ffmpeg.org" <ffmpeg-devel@ffmpeg.org>
> Cc: Sarthak Indurkhya <sindurkhya@adobe.com>
> Bcc:
> Date: Mon, 30 Jun 2025 09:44:54 +0000
> Subject: [FFmpeg-devel] [PATCH] avfilter: add inverse tone mapping filter
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* [FFmpeg-devel] [PATCH] avfilter: add inverse tone mapping filter
@ 2025-06-30 9:44 Sarthak Indurkhya via ffmpeg-devel
2025-06-30 14:05 ` Kacper Michajlow
0 siblings, 1 reply; 3+ messages in thread
From: Sarthak Indurkhya via ffmpeg-devel @ 2025-06-30 9:44 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Sarthak Indurkhya
[-- Attachment #1: Type: message/rfc822, Size: 11515 bytes --]
[-- Attachment #1.1.1: Type: text/plain, Size: 1556 bytes --]
Hello FFmpeg developers,
This patch introduces a new video filter called inversetonemap for FFmpeg.
The filter performs SDR to HDR conversion by mapping SDR BT.709 video to HDR BT.2020 PQ, using local adaptation and inverse tone mapping. The goal is to provide a simple, flexible tool for upconverting SDR content for HDR displays, with local adaptation, tone curve sensitivity, and chroma processing.
Patch summary:
* Implements local adaptation and inverse tone mapping for SDR-to-HDR conversion
* Performs gamma correction, color space transformation, and PQ encoding
* Supports configurable parameters: spatial sigma, range sigma, sensitivity exponent, HDR peak luminance
* Outputs 10-bit YUV420P10LE BT.2020 PQ with HDR metadata
* Includes local adaptation code, PQ transfer function, error diffusion dithering, and chroma recomputation
Commit message:
avfilter: add inversetonemap filter
This filter converts SDR BT.709 video to HDR BT.2020 PQ using local adaptation and inverse tone mapping. It supports user-configurable parameters for spatial/range sigma, tone mapping exponent, and HDR peak luminance.
The filter performs gamma correction, color conversion, PQ encoding, and outputs YUV420P10LE with HDR metadata.
The patch file is attached to this email.
Best regards,
Sarthak
[?patch icon] 0002-avfilter-add-myfilter-filter.patch<https://adobe-my.sharepoint.com/:u:/p/sindurkhya/EbrQbZzcik9Eg7ywwTfQDN4BVRP4SavI-n_NXHBVs3ehog>
Get Outlook for Mac <https://aka.ms/GetOutlookForMac>
[-- Attachment #1.1.2: image001.png --]
[-- Type: image/png, Size: 419 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-07-01 6:24 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-07-01 6:24 [FFmpeg-devel] [PATCH] avfilter: add inverse tone mapping filter Sarthak Indurkhya via ffmpeg-devel
-- strict thread matches above, loose matches on Subject: below --
2025-06-30 9:44 Sarthak Indurkhya via ffmpeg-devel
2025-06-30 14:05 ` Kacper Michajlow
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git