From 1de5fc6c51617f49515ef80708dd074432a27a6a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 23 Jan 2022 17:53:55 +0100 Subject: [PATCH] opj_encoder_set_extra_options(): add a GUARD_BITS=value option and add a -GuardBits option to opj_compress. The recently-released SMPTE DCP Bv2.1 Application Profile (link below) says that the number of guard bits in the QCD marker shall be 1 for 2K content and 2 for 4K content. This change allows the number of guard bits to be configured, so that users of openjpeg have the control they need to meet the specification. https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9161348 This is an alternative implementation of https://github.com/uclouvain/openjpeg/pull/1388 that keeps ABI unchanged. --- src/bin/jp2/opj_compress.c | 29 +++++++++++++++++++++++------ src/lib/openjp2/j2k.c | 21 +++++++++++++++++++++ src/lib/openjp2/j2k.h | 3 +++ src/lib/openjp2/openjpeg.h | 9 ++++++--- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/bin/jp2/opj_compress.c b/src/bin/jp2/opj_compress.c index 830ab96e..85e1e566 100644 --- a/src/bin/jp2/opj_compress.c +++ b/src/bin/jp2/opj_compress.c @@ -304,6 +304,9 @@ static void encode_help_display(void) fprintf(stdout, " Y >= 0 and Y <= 9.\n"); fprintf(stdout, " framerate > 0 may be specified to enhance checks and set maximum bit rate when Y > 0.\n"); + fprintf(stdout, "-GuardBits value\n"); + fprintf(stdout, + " Number of guard bits in [0,7] range. Usually 1 or 2 (default value).\n"); fprintf(stdout, "-jpip\n"); fprintf(stdout, " Write jpip codestream index box in JP2 output file.\n"); fprintf(stdout, " Currently supports only RPCL order.\n"); @@ -612,6 +615,7 @@ static int parse_cmdline_encoder(int argc, char **argv, int* pOutFramerate, OPJ_BOOL* pOutPLT, OPJ_BOOL* pOutTLM, + int* pOutGuardBits, int* pOutNumThreads, unsigned int* pTarget_bitdepth) { @@ -634,10 +638,11 @@ static int parse_cmdline_encoder(int argc, char **argv, {"threads", REQ_ARG, NULL, 'B'}, {"TLM", NO_ARG, NULL, 'D'}, {"TargetBitDepth", REQ_ARG, NULL, 'X'}, + {"GuardBits", REQ_ARG, NULL, 'G'} }; /* parse the command line */ - const char optlist[] = "i:o:r:q:n:b:c:t:p:s:SEM:x:R:d:T:If:P:C:F:u:JY:X:" + const char optlist[] = "i:o:r:q:n:b:c:t:p:s:SEM:x:R:d:T:If:P:C:F:u:JY:X:G:" #ifdef USE_JPWL "W:" #endif /* USE_JPWL */ @@ -933,6 +938,13 @@ static int parse_cmdline_encoder(int argc, char **argv, } break; + /* ----------------------------------------------------- */ + case 'G': { /* guard bits */ + char *s = opj_optarg; + sscanf(s, "%d", pOutGuardBits); + } + break; + /* ----------------------------------------------------- */ case 'n': { /* resolution */ @@ -1932,6 +1944,7 @@ int main(int argc, char **argv) OPJ_BOOL PLT = OPJ_FALSE; OPJ_BOOL TLM = OPJ_FALSE; int num_threads = 0; + int guard_bits = -1; /** desired bitdepth from input file */ unsigned int target_bitdepth = 0; @@ -1956,7 +1969,7 @@ int main(int argc, char **argv) 255; /* This will be set later according to the input image or the provided option */ if (parse_cmdline_encoder(argc, argv, ¶meters, &img_fol, &raw_cp, indexfilename, sizeof(indexfilename), &framerate, &PLT, &TLM, - &num_threads, &target_bitdepth) == 1) { + &guard_bits, &num_threads, &target_bitdepth) == 1) { ret = 1; goto fin; } @@ -2201,17 +2214,21 @@ int main(int argc, char **argv) goto fin; } - if (PLT || TLM) { - const char* options[3] = { NULL, NULL, NULL }; + { + const char* options[4] = { NULL, NULL, NULL, NULL }; int iOpt = 0; + char szGuardBits[32]; if (PLT) { options[iOpt++] = "PLT=YES"; } if (TLM) { options[iOpt++] = "TLM=YES"; } - (void)iOpt; - if (!opj_encoder_set_extra_options(l_codec, options)) { + if (guard_bits >= 0) { + sprintf(szGuardBits, "GUARD_BITS=%d", guard_bits); + options[iOpt++] = szGuardBits; + } + if (iOpt > 0 && !opj_encoder_set_extra_options(l_codec, options)) { fprintf(stderr, "failed to encode image: opj_encoder_set_extra_options\n"); opj_destroy_codec(l_codec); opj_image_destroy(image); diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c index 220f4b1e..6cb6b8ca 100644 --- a/src/lib/openjp2/j2k.c +++ b/src/lib/openjp2/j2k.c @@ -7654,6 +7654,8 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k, return OPJ_FALSE; } + p_j2k->m_specific_param.m_encoder.m_nb_comps = image->numcomps; + /* keep a link to cp so that we can destroy it later in j2k_destroy_compress */ cp = &(p_j2k->m_cp); @@ -12180,6 +12182,25 @@ OPJ_BOOL opj_j2k_encoder_set_extra_options( "Invalid value for option: %s.\n", *p_option_iter); return OPJ_FALSE; } + } else if (strncmp(*p_option_iter, "GUARD_BITS=", strlen("GUARD_BITS=")) == 0) { + OPJ_UINT32 tileno; + opj_cp_t *cp = cp = &(p_j2k->m_cp); + + int numgbits = atoi(*p_option_iter + strlen("GUARD_BITS=")); + if (numgbits < 0 || numgbits > 7) { + opj_event_msg(p_manager, EVT_ERROR, + "Invalid value for option: %s. Should be in [0,7]\n", *p_option_iter); + return OPJ_FALSE; + } + + for (tileno = 0; tileno < cp->tw * cp->th; tileno++) { + OPJ_UINT32 i; + opj_tcp_t *tcp = &cp->tcps[tileno]; + for (i = 0; i < p_j2k->m_specific_param.m_encoder.m_nb_comps; i++) { + opj_tccp_t *tccp = &tcp->tccps[i]; + tccp->numgbits = (OPJ_UINT32)numgbits; + } + } } else { opj_event_msg(p_manager, EVT_ERROR, "Invalid option: %s.\n", *p_option_iter); diff --git a/src/lib/openjp2/j2k.h b/src/lib/openjp2/j2k.h index 2b08e840..fc17166e 100644 --- a/src/lib/openjp2/j2k.h +++ b/src/lib/openjp2/j2k.h @@ -550,6 +550,9 @@ typedef struct opj_j2k_enc { /* reserved bytes in m_encoded_tile_size for PLT markers */ OPJ_UINT32 m_reserved_bytes_for_PLT; + /** Number of components */ + OPJ_UINT32 m_nb_comps; + } opj_j2k_enc_t; diff --git a/src/lib/openjp2/openjpeg.h b/src/lib/openjp2/openjpeg.h index b200a6ac..c0d6dbcb 100644 --- a/src/lib/openjp2/openjpeg.h +++ b/src/lib/openjp2/openjpeg.h @@ -1599,9 +1599,12 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec, *
  • PLT=YES/NO. Defaults to NO. If set to YES, PLT marker segments, * indicating the length of each packet in the tile-part header, will be * written. Since 2.4.0
  • - *
  • TLM=YES/NO. Defaults to NO (except for Cinema and IMF profiles). - * If set to YES, TLM marker segments, indicating the length of each - * tile-part part will be written. Since 2.4.0
  • + *
  • TLM=YES/NO. Defaults to NO (except for Cinema and IMF profiles). + * If set to YES, TLM marker segments, indicating the length of each + * tile-part part will be written. Since 2.4.0
  • + *
  • GUARD_BITS=value. Number of guard bits in [0,7] range. Default value is 2. + * 1 may be used sometimes (like in SMPTE DCP Bv2.1 Application Profile for 2K images). + * Since 2.5.0
  • * * * @param p_codec Compressor handle