From a36ae0386066c9ddd089788930be5a42a176446e Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 7 Jun 2021 15:16:53 +0200 Subject: [PATCH] Add support for enabling generation of TLM markers in encoder 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 | 28 +++++++-- src/lib/openjp2/j2k.c | 77 ++++++++++++++++++++----- src/lib/openjp2/j2k.h | 6 ++ src/lib/openjp2/openjpeg.h | 7 ++- tests/nonregression/test_suite.ctest.in | 4 ++ 5 files changed, 101 insertions(+), 21 deletions(-) diff --git a/src/bin/jp2/opj_compress.c b/src/bin/jp2/opj_compress.c index 76ba8ec4..e488abcd 100644 --- a/src/bin/jp2/opj_compress.c +++ b/src/bin/jp2/opj_compress.c @@ -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 \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, ¶meters, &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); diff --git a/src/lib/openjp2/j2k.c b/src/lib/openjp2/j2k.c index 8e343ab2..1f064c31 100644 --- a/src/lib/openjp2/j2k.c +++ b/src/lib/openjp2/j2k.c @@ -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); diff --git a/src/lib/openjp2/j2k.h b/src/lib/openjp2/j2k.h index 9eb50b50..740ed9b6 100644 --- a/src/lib/openjp2/j2k.h +++ b/src/lib/openjp2/j2k.h @@ -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. diff --git a/src/lib/openjp2/openjpeg.h b/src/lib/openjp2/openjpeg.h index 269ac329..8829963f 100644 --- a/src/lib/openjp2/openjpeg.h +++ b/src/lib/openjp2/openjpeg.h @@ -1592,7 +1592,10 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec, * * * @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, diff --git a/tests/nonregression/test_suite.ctest.in b/tests/nonregression/test_suite.ctest.in index 72155329..80dd3306 100644 --- a/tests/nonregression/test_suite.ctest.in +++ b/tests/nonregression/test_suite.ctest.in @@ -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