opj_encoder_set_extra_options(): add a GUARD_BITS=value option 1403/head
authorEven Rouault <even.rouault@spatialys.com>
Sun, 23 Jan 2022 16:53:55 +0000 (17:53 +0100)
committerEven Rouault <even.rouault@spatialys.com>
Sun, 23 Jan 2022 16:54:44 +0000 (17:54 +0100)
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
src/lib/openjp2/j2k.c
src/lib/openjp2/j2k.h
src/lib/openjp2/openjpeg.h

index 830ab96e00af6ed915b2bfef1b1c82279f65314e..85e1e56669705d0490816348ea84a65f029bb839 100644 (file)
@@ -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, &parameters, &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);
index 220f4b1e8dbea77ec22ad8bf11b0e63f8214ee2e..6cb6b8caacf81cc6541be546f279b4190a2272ea 100644 (file)
@@ -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);
index 2b08e840723f7dea8db95c0afb4be6236a9b9394..fc17166e020261f8623ece62ffcf09d46af7fa1f 100644 (file)
@@ -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;
 
 
index b200a6ac87714ff39e1d762adc28e9ed5f04ff2f..c0d6dbcb2e64458c544d240373dbf5dce218aeae 100644 (file)
@@ -1599,9 +1599,12 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
  * <li>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</li>
-  * <li>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</li>
+ * <li>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</li>
+ * <li>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</li>
  * </ul>
  *
  * @param p_codec       Compressor handle