PR #25839 opened by nmschulte URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25839 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25839.patch
> - `-mct` option for j2kenc; `0`: off (default), `1`: on, `-1`: auto > - only supported for RGB-like pixel formats (by nature) > - FATE round-trip tests for various MCT options https://lists.ffmpeg.org/archives/list/[email protected]/thread/BNJKJAQ6RU6VQUGL434EGCELHDKCXWC2/ >From 15c0e84e761035fc5cebaead26a040cf61577c6a Mon Sep 17 00:00:00 2001 From: Nathan Schulte <[email protected]> Date: Sun, 18 Oct 2026 11:01:17 -0500 Subject: [PATCH] avcodec/j2kenc: add support for MCT (RCT/ICT) -mct option for j2kenc; 0: off (default), 1: on, -1: auto only supported for RGB-like pixel formats (by nature) FATE round-trip tests for various MCT options --- Changelog | 2 + doc/encoders.texi | 19 ++++++ libavcodec/j2kenc.c | 71 ++++++++++++++++++++++- tests/fate/jpeg2000.mak | 34 +++++++++++ tests/ref/fate/jpeg2000enc-mct-auto | 8 +++ tests/ref/fate/jpeg2000enc-mct-off | 8 +++ tests/ref/fate/jpeg2000enc-mct-on-rgb-ict | 8 +++ tests/ref/fate/jpeg2000enc-mct-on-rgb-rct | 8 +++ 8 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 tests/ref/fate/jpeg2000enc-mct-auto create mode 100644 tests/ref/fate/jpeg2000enc-mct-off create mode 100644 tests/ref/fate/jpeg2000enc-mct-on-rgb-ict create mode 100644 tests/ref/fate/jpeg2000enc-mct-on-rgb-rct diff --git a/Changelog b/Changelog index 32e7c8bfe74..a9db12c3216 100644 --- a/Changelog +++ b/Changelog @@ -19,6 +19,8 @@ version <next>: - Panasonic RW2 decoder - xHE-AAC encoding via libfdk-xhe-aac - jpegapp bitstream filter +- JPEG-2000 encoder MCT (RCT/ICT) support for RGB-like pixel formats with new + option -mct (default off) version 9.0: diff --git a/doc/encoders.texi b/doc/encoders.texi index 2c173ac916d..92626ab5070 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -1617,6 +1617,25 @@ Enable this to add SOP marker at the start of each packet. Disabled by default. @item eph @var{boolean} Enable this to add EPH marker at the end of each packet header. Disabled by default. +@item mct @var{integer} +Multiple Component Transformation. +@table @option +@item -1 +Automatically enable MCT for RGB-like pixel formats with at least three components. +@item 0 +Disable MCT (default). +@item 1 +Enable MCT. Applies the reversible colour transform (RCT) when using the +5/3 DWT (@option{pred=dwt53}) or the irreversible colour transform (ICT) +when using the 9/7 DWT. Only valid for RGB-like pixel formats with at least +three components (and without chroma subsampling). Alpha (or any extra components) +is left untouched. +@end table + +When enabled, the encoder both applies the transform and signals it in the +COD marker. Enabling MCT is recommended for RGB content as it usually +improves compression. + @item prog @var{integer} Sets the progression order to be used by the encoder. Possible values are: diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c index da777580a4a..e08799ec96c 100644 --- a/libavcodec/j2kenc.c +++ b/libavcodec/j2kenc.c @@ -145,6 +145,7 @@ typedef struct { int prog; int nlayers; char *lr_str; + int mct; //< multiple component transform (RGB-like): -1: auto, 0: off (default), 1: on } Jpeg2000EncoderContext; @@ -279,7 +280,7 @@ static int put_cod(Jpeg2000EncoderContext *s) // SGcod bytestream_put_byte(&s->buf, s->prog); // progression level bytestream_put_be16(&s->buf, s->nlayers); // num of layers - bytestream_put_byte(&s->buf, 0); // unspecified + bytestream_put_byte(&s->buf, codsty->mct); // multiple component transformation // SPcod bytestream_put_byte(&s->buf, codsty->nreslevels - 1); // num of decomp. levels bytestream_put_byte(&s->buf, codsty->log2_cblk_width-2); // cblk width @@ -435,6 +436,51 @@ static int init_tiles(Jpeg2000EncoderContext *s) return 0; } +static void mct_encode_rct(int *y, int *u, int *v, int n) +{ + int i; + for (i = 0; i < n; i++) { + int R = y[i], G = u[i], B = v[i]; + y[i] = (R + (2 * G) + B) >> 2; + u[i] = B - G; + v[i] = R - G; + } +} + +static void mct_encode_ict(int *y, int *u, int *v, int n) +{ + int i; + for (i = 0; i < n; i++) { + double R = y[i], G = u[i], B = v[i]; + y[i] = lrint( 0.29900 * R + 0.58700 * G + 0.11400 * B); + u[i] = lrint(-0.16875 * R - 0.33126 * G + 0.50000 * B); + v[i] = lrint( 0.50000 * R - 0.41869 * G - 0.08131 * B); + } +} + +static void apply_mct(Jpeg2000EncoderContext *s) +{ + int tileno; + + if (!s->codsty.mct) + return; + + for (tileno = 0; tileno < s->numXtiles * s->numYtiles; tileno++) { + Jpeg2000Tile *tile = s->tile + tileno; + Jpeg2000Component *c0 = tile->comp + 0; + Jpeg2000Component *c1 = tile->comp + 1; + Jpeg2000Component *c2 = tile->comp + 2; + int w = c0->coord[0][1] - c0->coord[0][0]; + int h = c0->coord[1][1] - c0->coord[1][0]; + int n = w * h; + + if (s->codsty.transform == FF_DWT53) + mct_encode_rct(c0->i_data, c1->i_data, c2->i_data, n); + else + mct_encode_ict(c0->i_data, c1->i_data, c2->i_data, n); + } +} + #define COPY_FRAME(D, PIXEL) \ static void copy_frame_ ##D(Jpeg2000EncoderContext *s) \ { \ @@ -1491,6 +1537,8 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, else copy_frame_8(s); + apply_mct(s); + reinit(s); if (s->format == CODEC_JP2) { @@ -1722,6 +1770,26 @@ static av_cold int j2kenc_init(AVCodecContext *avctx) } } + s->codsty.mct = 0; + if (s->mct < 0) { // mct == -1; auto-on for RGB-like + s->codsty.mct = (desc->flags & AV_PIX_FMT_FLAG_RGB) && s->ncomponents >= 3; + } else if (s->mct == 1) { + if (s->ncomponents >= 3) + s->codsty.mct = 1; + else + av_log(avctx, AV_LOG_WARNING, + "MCT requested but fewer than 3 components; disabling\n"); + } + if (s->codsty.mct && (s->chroma_shift[0] || s->chroma_shift[1])) { + av_log(avctx, AV_LOG_ERROR, "MCT is not supported with chroma subsampling; disabling\n"); + s->codsty.mct = 0; + } + if (s->codsty.mct && codsty->transform == FF_DWT53) { + // MCT expands chrominance range by 1 bit, and DWT can add more + // use extra guard bits to compensate + qntsty->nguardbits++; + } + ff_thread_once(&init_static_once, init_luts); init_quantization(s); @@ -1756,6 +1824,7 @@ static const AVOption options[] = { { "dwt53", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, { "sop", "SOP marker", OFFSET(sop), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, }, { "eph", "EPH marker", OFFSET(eph), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, }, + { "mct", "Multiple component transformation", OFFSET(mct), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, 1, VE, .unit = "mct" }, { "prog", "Progression Order", OFFSET(prog), AV_OPT_TYPE_INT, { .i64 = 0 }, JPEG2000_PGOD_LRCP, JPEG2000_PGOD_CPRL, VE, .unit = "prog" }, { "lrcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_LRCP }, 0, 0, VE, .unit = "prog" }, { "rlcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_RLCP }, 0, 0, VE, .unit = "prog" }, diff --git a/tests/fate/jpeg2000.mak b/tests/fate/jpeg2000.mak index a99b0c4e0cd..9fb7a356f27 100644 --- a/tests/fate/jpeg2000.mak +++ b/tests/fate/jpeg2000.mak @@ -67,3 +67,37 @@ FATE_JPEG2000DEC += $(FATE_JPEG2000DEC-yes) FATE_SAMPLES_FFMPEG-$(call FRAMECRC, IMAGE_J2K_PIPE, JPEG2000) += $(FATE_JPEG2000DEC) fate-jpeg2000dec: $(FATE_JPEG2000DEC) + +# JPEG 2000 encoder MCT tests. +# RGB + irreversible 9/7 transform: lossy round trip +FATE_JPEG2000ENC += fate-jpeg2000enc-mct-on-rgb-ict +fate-jpeg2000enc-mct-on-rgb-ict: CMD = transcode \ + "lavfi" "testsrc2=size=64x64:rate=1:duration=1,format=rgb24" \ + rawvideo "-c:v jpeg2000 -mct 1 -pred dwt97int -frames:v 1" \ + "-pix_fmt rgb24" + +# RGB + reversible 5/3 transform: lossless round trip +FATE_JPEG2000ENC += fate-jpeg2000enc-mct-on-rgb-rct +fate-jpeg2000enc-mct-on-rgb-rct: CMD = transcode \ + "lavfi" "testsrc2=size=64x64:rate=1:duration=1,format=rgb24" \ + rawvideo "-c:v jpeg2000 -mct 1 -pred dwt53 -frames:v 1" \ + "-pix_fmt rgb24" + +# Explicitly disabled MCT must retain lossless round-trip behavior +FATE_JPEG2000ENC += fate-jpeg2000enc-mct-off +fate-jpeg2000enc-mct-off: CMD = transcode \ + "lavfi" "testsrc2=size=64x64:rate=1:duration=1,format=rgb24" \ + rawvideo "-c:v jpeg2000 -mct 0 -pred dwt53 -frames:v 1" \ + "-pix_fmt rgb24" + +# Automatic MCT selection on RGB input; round trip +FATE_JPEG2000ENC += fate-jpeg2000enc-mct-auto +fate-jpeg2000enc-mct-auto: CMD = transcode \ + "lavfi" "testsrc2=size=64x64:rate=1:duration=1,format=rgb24" \ + rawvideo "-c:v jpeg2000 -mct -1 -pred dwt53 -frames:v 1" \ + "-pix_fmt rgb24" + +FATE_JPEG2000ENC-$(call ALLYES, JPEG2000_ENCODER JPEG2000_DECODER RAWVIDEO_ENCODER RAWVIDEO_DECODER LAVFI_INDEV) += $(FATE_JPEG2000ENC) + +FATE_SAMPLES_FFMPEG-$(call ALLYES, JPEG2000_ENCODER JPEG2000_DECODER RAWVIDEO_ENCODER RAWVIDEO_DECODER LAVFI_INDEV) += $(FATE_JPEG2000ENC-yes) +fate-jpeg2000enc: $(FATE_JPEG2000ENC-yes) diff --git a/tests/ref/fate/jpeg2000enc-mct-auto b/tests/ref/fate/jpeg2000enc-mct-auto new file mode 100644 index 00000000000..d7601398b89 --- /dev/null +++ b/tests/ref/fate/jpeg2000enc-mct-auto @@ -0,0 +1,8 @@ +f831eb2c9fb1673962435a257332ced4 *tests/data/fate/jpeg2000enc-mct-auto.rawvideo +6748 tests/data/fate/jpeg2000enc-mct-auto.rawvideo +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 64x64 +#sar 0: 0/1 +0, 0, 0, 1, 12288, 0xcd3a18af diff --git a/tests/ref/fate/jpeg2000enc-mct-off b/tests/ref/fate/jpeg2000enc-mct-off new file mode 100644 index 00000000000..392646eb9d6 --- /dev/null +++ b/tests/ref/fate/jpeg2000enc-mct-off @@ -0,0 +1,8 @@ +f7f378e60f4496b3f6854affe01af4fc *tests/data/fate/jpeg2000enc-mct-off.rawvideo +6043 tests/data/fate/jpeg2000enc-mct-off.rawvideo +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 64x64 +#sar 0: 0/1 +0, 0, 0, 1, 12288, 0xcd3a18af diff --git a/tests/ref/fate/jpeg2000enc-mct-on-rgb-ict b/tests/ref/fate/jpeg2000enc-mct-on-rgb-ict new file mode 100644 index 00000000000..f55a1e01a21 --- /dev/null +++ b/tests/ref/fate/jpeg2000enc-mct-on-rgb-ict @@ -0,0 +1,8 @@ +ccf2a1580d61f29a8c36d941a06a2944 *tests/data/fate/jpeg2000enc-mct-on-rgb-ict.rawvideo +7499 tests/data/fate/jpeg2000enc-mct-on-rgb-ict.rawvideo +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 64x64 +#sar 0: 0/1 +0, 0, 0, 1, 12288, 0xe11a1b4d diff --git a/tests/ref/fate/jpeg2000enc-mct-on-rgb-rct b/tests/ref/fate/jpeg2000enc-mct-on-rgb-rct new file mode 100644 index 00000000000..6576b592b7e --- /dev/null +++ b/tests/ref/fate/jpeg2000enc-mct-on-rgb-rct @@ -0,0 +1,8 @@ +f831eb2c9fb1673962435a257332ced4 *tests/data/fate/jpeg2000enc-mct-on-rgb-rct.rawvideo +6748 tests/data/fate/jpeg2000enc-mct-on-rgb-rct.rawvideo +#tb 0: 1/25 +#media_type 0: video +#codec_id 0: rawvideo +#dimensions 0: 64x64 +#sar 0: 0/1 +0, 0, 0, 1, 12288, 0xcd3a18af -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
