Add support for enabling generation of TLM markers in encoder 1359/head
authorEven Rouault <even.rouault@spatialys.com>
Mon, 7 Jun 2021 13:16:53 +0000 (15:16 +0200)
committerEven Rouault <even.rouault@spatialys.com>
Mon, 7 Jun 2021 13:49:08 +0000 (15:49 +0200)
Support was already there, but restricted to Cinema and IMF profiles,
and 255 tiles

* Add -TLM switch added to opj_compress
* Make opj_encoder_set_extra_options() function accept a TLM=YES option.

src/bin/jp2/opj_compress.c
src/lib/openjp2/j2k.c
src/lib/openjp2/j2k.h
src/lib/openjp2/openjpeg.h
tests/nonregression/test_suite.ctest.in

index 76ba8ec4aef27bf31110e7b7835d5c38a49fa3d5..e488abcd46142d14e21b582a99bf37e642d4bdbc 100644 (file)
@@ -231,6 +231,8 @@ static void encode_help_display(void)
     fprintf(stdout, "    Write EPH marker after each header packet.\n");
     fprintf(stdout, "-PLT\n");
     fprintf(stdout, "    Write PLT marker in tile-part header.\n");
+    fprintf(stdout, "-TLM\n");
+    fprintf(stdout, "    Write TLM marker in main header.\n");
     fprintf(stdout, "-M <key value>\n");
     fprintf(stdout, "    Mode switch.\n");
     fprintf(stdout, "    [1=BYPASS(LAZY) 2=RESET 4=RESTART(TERMALL)\n");
@@ -597,6 +599,7 @@ static int parse_cmdline_encoder(int argc, char **argv,
                                  size_t indexfilename_size,
                                  int* pOutFramerate,
                                  OPJ_BOOL* pOutPLT,
+                                 OPJ_BOOL* pOutTLM,
                                  int* pOutNumThreads)
 {
     OPJ_UINT32 i, j;
@@ -615,7 +618,8 @@ static int parse_cmdline_encoder(int argc, char **argv,
         {"mct", REQ_ARG, NULL, 'Y'},
         {"IMF", REQ_ARG, NULL, 'Z'},
         {"PLT", NO_ARG, NULL, 'A'},
-        {"threads",   REQ_ARG, NULL, 'B'}
+        {"threads",   REQ_ARG, NULL, 'B'},
+        {"TLM", NO_ARG, NULL, 'D'},
     };
 
     /* parse the command line */
@@ -1712,6 +1716,12 @@ static int parse_cmdline_encoder(int argc, char **argv,
             }
         }
         break;
+        /* ------------------------------------------------------ */
+
+        case 'D': {         /* TLM markers */
+            *pOutTLM = OPJ_TRUE;
+        }
+        break;
 
         /* ------------------------------------------------------ */
 
@@ -1895,6 +1905,7 @@ int main(int argc, char **argv)
     OPJ_FLOAT64 t = opj_clock();
 
     OPJ_BOOL PLT = OPJ_FALSE;
+    OPJ_BOOL TLM = OPJ_FALSE;
     int num_threads = 0;
 
     /* set encoding parameters to default values */
@@ -1916,7 +1927,8 @@ int main(int argc, char **argv)
     parameters.tcp_mct = (char)
                          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, &num_threads) == 1) {
+                              indexfilename, sizeof(indexfilename), &framerate, &PLT, &TLM,
+                              &num_threads) == 1) {
         ret = 1;
         goto fin;
     }
@@ -2160,8 +2172,16 @@ int main(int argc, char **argv)
             goto fin;
         }
 
-        if (PLT) {
-            const char* const options[] = { "PLT=YES", NULL };
+        if (PLT || TLM) {
+            const char* options[3] = { NULL, NULL, NULL };
+            int iOpt = 0;
+            if (PLT) {
+                options[iOpt++] = "PLT=YES";
+            }
+            if (TLM) {
+                options[iOpt++] = "TLM=YES";
+            }
+            (void)iOpt;
             if (!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);
index 8e343ab2e3780d984b5056156743d0f4229163e9..1f064c3115f30b22b351006164cb75cf33eced94 100644 (file)
@@ -910,9 +910,15 @@ static OPJ_BOOL opj_j2k_read_sod(opj_j2k_t *p_j2k,
 
 static void opj_j2k_update_tlm(opj_j2k_t * p_j2k, OPJ_UINT32 p_tile_part_size)
 {
-    opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
-                    p_j2k->m_current_tile_number, 1);           /* PSOT */
-    ++p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current;
+    if (p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte) {
+        opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
+                        p_j2k->m_current_tile_number, 1);
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current += 1;
+    } else {
+        opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
+                        p_j2k->m_current_tile_number, 2);
+        p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current += 2;
+    }
 
     opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
                     p_tile_part_size, 4);                                       /* PSOT */
@@ -4170,13 +4176,33 @@ static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
 {
     OPJ_BYTE * l_current_data = 00;
     OPJ_UINT32 l_tlm_size;
+    OPJ_UINT32 size_per_tile_part;
 
     /* preconditions */
     assert(p_j2k != 00);
     assert(p_manager != 00);
     assert(p_stream != 00);
 
-    l_tlm_size = 6 + (5 * p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
+    /* 10921 = (65535 - header_size) / size_per_tile_part where */
+    /* header_size = 4 and size_per_tile_part = 6 */
+    if (p_j2k->m_specific_param.m_encoder.m_total_tile_parts > 10921) {
+        /* We could do more but it would require writing several TLM markers */
+        opj_event_msg(p_manager, EVT_ERROR,
+                      "A maximum of 10921 tile-parts are supported currently "
+                      "when writing TLM marker\n");
+        return OPJ_FALSE;
+    }
+
+    if (p_j2k->m_specific_param.m_encoder.m_total_tile_parts <= 255) {
+        size_per_tile_part = 5;
+        p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte = OPJ_TRUE;
+    } else {
+        size_per_tile_part = 6;
+        p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte = OPJ_FALSE;
+    }
+
+    l_tlm_size = 2 + 4 + (size_per_tile_part *
+                          p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
 
     if (l_tlm_size > p_j2k->m_specific_param.m_encoder.m_header_tile_data_size) {
         OPJ_BYTE *new_header_tile_data = (OPJ_BYTE *) opj_realloc(
@@ -4191,6 +4217,7 @@ static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
         p_j2k->m_specific_param.m_encoder.m_header_tile_data = new_header_tile_data;
         p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_tlm_size;
     }
+    memset(p_j2k->m_specific_param.m_encoder.m_header_tile_data, 0, l_tlm_size);
 
     l_current_data = p_j2k->m_specific_param.m_encoder.m_header_tile_data;
 
@@ -4210,11 +4237,14 @@ static OPJ_BOOL opj_j2k_write_tlm(opj_j2k_t *p_j2k,
                     1);                                                    /* Ztlm=0*/
     ++l_current_data;
 
-    opj_write_bytes(l_current_data, 0x50,
-                    1);                                                 /* Stlm ST=1(8bits-255 tiles max),SP=1(Ptlm=32bits) */
+    /* Stlm 0x50= ST=1(8bits-255 tiles max),SP=1(Ptlm=32bits) */
+    /* Stlm 0x60= ST=2(16bits-65535 tiles max),SP=1(Ptlm=32bits) */
+    opj_write_bytes(l_current_data,
+                    size_per_tile_part == 5 ? 0x50 : 0x60,
+                    1);
     ++l_current_data;
 
-    /* do nothing on the 5 * l_j2k->m_specific_param.m_encoder.m_total_tile_parts remaining data */
+    /* do nothing on the size_per_tile_part * l_j2k->m_specific_param.m_encoder.m_total_tile_parts remaining data */
     if (opj_stream_write_data(p_stream,
                               p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size,
                               p_manager) != l_tlm_size) {
@@ -5354,9 +5384,9 @@ static OPJ_BOOL opj_j2k_update_rates(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
-    if (OPJ_IS_CINEMA(l_cp->rsiz) || OPJ_IS_IMF(l_cp->rsiz)) {
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
         p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer =
-            (OPJ_BYTE *) opj_malloc(5 *
+            (OPJ_BYTE *) opj_malloc(6 *
                                     p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
         if (! p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
             return OPJ_FALSE;
@@ -7709,6 +7739,10 @@ OPJ_BOOL opj_j2k_setup_encoder(opj_j2k_t *p_j2k,
         }
     }
 
+    if (OPJ_IS_CINEMA(parameters->rsiz) || OPJ_IS_IMF(parameters->rsiz)) {
+        p_j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
+    }
+
     /* Manage profiles and applications and set RSIZ */
     /* set cinema parameters if required */
     if (OPJ_IS_CINEMA(parameters->rsiz)) {
@@ -12048,6 +12082,16 @@ 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, "TLM=", 4) == 0) {
+            if (strcmp(*p_option_iter, "TLM=YES") == 0) {
+                p_j2k->m_specific_param.m_encoder.m_TLM = OPJ_TRUE;
+            } else if (strcmp(*p_option_iter, "TLM=NO") == 0) {
+                p_j2k->m_specific_param.m_encoder.m_TLM = OPJ_FALSE;
+            } else {
+                opj_event_msg(p_manager, EVT_ERROR,
+                              "Invalid value for option: %s.\n", *p_option_iter);
+                return OPJ_FALSE;
+            }
         } else {
             opj_event_msg(p_manager, EVT_ERROR,
                           "Invalid option: %s.\n", *p_option_iter);
@@ -12445,7 +12489,7 @@ static OPJ_BOOL opj_j2k_setup_end_compress(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
-    if (OPJ_IS_CINEMA(p_j2k->m_cp.rsiz) || OPJ_IS_IMF(p_j2k->m_cp.rsiz)) {
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
         if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
                                                (opj_procedure)opj_j2k_write_updated_tlm, p_manager)) {
             return OPJ_FALSE;
@@ -12528,7 +12572,7 @@ static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
-    if (OPJ_IS_CINEMA(p_j2k->m_cp.rsiz) || OPJ_IS_IMF(p_j2k->m_cp.rsiz)) {
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
         if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
                                                (opj_procedure)opj_j2k_write_tlm, p_manager)) {
             return OPJ_FALSE;
@@ -12661,7 +12705,7 @@ static OPJ_BOOL opj_j2k_write_first_tile_part(opj_j2k_t *p_j2k,
     opj_write_bytes(l_begin_data + 6, l_nb_bytes_written,
                     4);                                 /* PSOT */
 
-    if (OPJ_IS_CINEMA(l_cp->rsiz) || OPJ_IS_IMF(l_cp->rsiz)) {
+    if (p_j2k->m_specific_param.m_encoder.m_TLM) {
         opj_j2k_update_tlm(p_j2k, l_nb_bytes_written);
     }
 
@@ -12731,7 +12775,7 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
         opj_write_bytes(l_begin_data + 6, l_part_tile_size,
                         4);                                   /* PSOT */
 
-        if (OPJ_IS_CINEMA(l_cp->rsiz) || OPJ_IS_IMF(l_cp->rsiz)) {
+        if (p_j2k->m_specific_param.m_encoder.m_TLM) {
             opj_j2k_update_tlm(p_j2k, l_part_tile_size);
         }
 
@@ -12777,7 +12821,7 @@ static OPJ_BOOL opj_j2k_write_all_tile_parts(opj_j2k_t *p_j2k,
             opj_write_bytes(l_begin_data + 6, l_part_tile_size,
                             4);                                   /* PSOT */
 
-            if (OPJ_IS_CINEMA(l_cp->rsiz) || OPJ_IS_IMF(l_cp->rsiz)) {
+            if (p_j2k->m_specific_param.m_encoder.m_TLM) {
                 opj_j2k_update_tlm(p_j2k, l_part_tile_size);
             }
 
@@ -12796,13 +12840,16 @@ static OPJ_BOOL opj_j2k_write_updated_tlm(opj_j2k_t *p_j2k,
 {
     OPJ_UINT32 l_tlm_size;
     OPJ_OFF_T l_tlm_position, l_current_position;
+    OPJ_UINT32 size_per_tile_part;
 
     /* preconditions */
     assert(p_j2k != 00);
     assert(p_manager != 00);
     assert(p_stream != 00);
 
-    l_tlm_size = 5 * p_j2k->m_specific_param.m_encoder.m_total_tile_parts;
+    size_per_tile_part = p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte ? 5 : 6;
+    l_tlm_size = size_per_tile_part *
+                 p_j2k->m_specific_param.m_encoder.m_total_tile_parts;
     l_tlm_position = 6 + p_j2k->m_specific_param.m_encoder.m_tlm_start;
     l_current_position = opj_stream_tell(p_stream);
 
index 9eb50b50da6977500a95d7a64d20b675c1754b50..740ed9b6d418bbf70ac5c1656b441d21934b1525 100644 (file)
@@ -503,6 +503,12 @@ typedef struct opj_j2k_enc {
     /** Tile part number currently coding, taking into account POC. m_current_tile_part_number holds the total number of tile parts while encoding the last tile part.*/
     OPJ_UINT32 m_current_tile_part_number; /*cur_tp_num */
 
+    /* whether to generate TLM markers */
+    OPJ_BOOL   m_TLM;
+
+    /* whether the Ttlmi field in a TLM marker is a byte (otherwise a uint16) */
+    OPJ_BOOL   m_Ttlmi_is_byte;
+
     /**
     locate the start position of the TLM marker
     after encoding the tilepart, a jump (in j2k_write_sod) is done to the TLM marker to store the value of its length.
index 269ac329ae097fc938b54f8469cf3bd7eeb93a57..8829963f5aafd8a4d219c084d3c653e83ee3a63b 100644 (file)
@@ -1592,7 +1592,10 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
  * <ul>
  * <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.3.2</li>
+ *     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>
  * </ul>
  *
  * @param p_codec       Compressor handle
@@ -1600,7 +1603,7 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
  *                      array of strings. Each string is of the form KEY=VALUE.
  *
  * @return OPJ_TRUE in case of success.
- * @since 2.3.2
+ * @since 2.4.0
  */
 OPJ_API OPJ_BOOL OPJ_CALLCONV opj_encoder_set_extra_options(
     opj_codec_t *p_codec,
index 72155329a5f331c20495d2f1fe3179f7b5560d85..80dd330665600fa51fbaf559a435095da0f2576c 100644 (file)
@@ -186,6 +186,10 @@ opj_compress_no_raw_lossless -i @INPUT_NR_PATH@/ElephantDream_4K.tif -o @TEMP_PA
 opj_compress_no_raw_lossless -i @INPUT_NR_PATH@/byte.tif -o @TEMP_PATH@/byte_PLT.j2k -n 1 -PLT
 opj_compress_no_raw_lossless -i @INPUT_NR_PATH@/byte.tif -o @TEMP_PATH@/byte_PLT.jp2 -n 1 -PLT
 
+opj_compress_no_raw_lossless -i @INPUT_NR_PATH@/byte.tif -o @TEMP_PATH@/byte_TLM.j2k -n 1 -TLM
+# Use tile size 1x1 to generate more than 255 tiles
+opj_compress_no_raw_lossless -i @INPUT_NR_PATH@/byte.tif -o @TEMP_PATH@/byte_TLM_400tiles.j2k -n 1 -t 1,1 -TLM
+
 # DECODER TEST SUITE
 opj_decompress -i  @INPUT_NR_PATH@/Bretagne2.j2k -o @TEMP_PATH@/Bretagne2.j2k.pgx
 opj_decompress -i  @INPUT_NR_PATH@/_00042.j2k -o @TEMP_PATH@/_00042.j2k.pgx