Merge pull request #1359 from rouault/TLM

Add support for enabling generation of TLM markers in encoder
This commit is contained in:
Even Rouault 2021-06-08 11:11:04 +02:00 committed by GitHub
commit c28965f255
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 21 deletions

View File

@ -231,6 +231,8 @@ static void encode_help_display(void)
fprintf(stdout, " Write EPH marker after each header packet.\n"); fprintf(stdout, " Write EPH marker after each header packet.\n");
fprintf(stdout, "-PLT\n"); fprintf(stdout, "-PLT\n");
fprintf(stdout, " Write PLT marker in tile-part header.\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, "-M <key value>\n");
fprintf(stdout, " Mode switch.\n"); fprintf(stdout, " Mode switch.\n");
fprintf(stdout, " [1=BYPASS(LAZY) 2=RESET 4=RESTART(TERMALL)\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, size_t indexfilename_size,
int* pOutFramerate, int* pOutFramerate,
OPJ_BOOL* pOutPLT, OPJ_BOOL* pOutPLT,
OPJ_BOOL* pOutTLM,
int* pOutNumThreads) int* pOutNumThreads)
{ {
OPJ_UINT32 i, j; OPJ_UINT32 i, j;
@ -615,7 +618,8 @@ static int parse_cmdline_encoder(int argc, char **argv,
{"mct", REQ_ARG, NULL, 'Y'}, {"mct", REQ_ARG, NULL, 'Y'},
{"IMF", REQ_ARG, NULL, 'Z'}, {"IMF", REQ_ARG, NULL, 'Z'},
{"PLT", NO_ARG, NULL, 'A'}, {"PLT", NO_ARG, NULL, 'A'},
{"threads", REQ_ARG, NULL, 'B'} {"threads", REQ_ARG, NULL, 'B'},
{"TLM", NO_ARG, NULL, 'D'},
}; };
/* parse the command line */ /* parse the command line */
@ -1712,6 +1716,12 @@ static int parse_cmdline_encoder(int argc, char **argv,
} }
} }
break; 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_FLOAT64 t = opj_clock();
OPJ_BOOL PLT = OPJ_FALSE; OPJ_BOOL PLT = OPJ_FALSE;
OPJ_BOOL TLM = OPJ_FALSE;
int num_threads = 0; int num_threads = 0;
/* set encoding parameters to default values */ /* set encoding parameters to default values */
@ -1916,7 +1927,8 @@ int main(int argc, char **argv)
parameters.tcp_mct = (char) parameters.tcp_mct = (char)
255; /* This will be set later according to the input image or the provided option */ 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, 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; ret = 1;
goto fin; goto fin;
} }
@ -2160,8 +2172,16 @@ int main(int argc, char **argv)
goto fin; goto fin;
} }
if (PLT) { if (PLT || TLM) {
const char* const options[] = { "PLT=YES", NULL }; 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)) { if (!opj_encoder_set_extra_options(l_codec, options)) {
fprintf(stderr, "failed to encode image: opj_encoder_set_extra_options\n"); fprintf(stderr, "failed to encode image: opj_encoder_set_extra_options\n");
opj_destroy_codec(l_codec); opj_destroy_codec(l_codec);

View 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) 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, if (p_j2k->m_specific_param.m_encoder.m_Ttlmi_is_byte) {
p_j2k->m_current_tile_number, 1); /* PSOT */ opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
++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, opj_write_bytes(p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_current,
p_tile_part_size, 4); /* PSOT */ 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_BYTE * l_current_data = 00;
OPJ_UINT32 l_tlm_size; OPJ_UINT32 l_tlm_size;
OPJ_UINT32 size_per_tile_part;
/* preconditions */ /* preconditions */
assert(p_j2k != 00); assert(p_j2k != 00);
assert(p_manager != 00); assert(p_manager != 00);
assert(p_stream != 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) { 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( 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 = new_header_tile_data;
p_j2k->m_specific_param.m_encoder.m_header_tile_data_size = l_tlm_size; 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; 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*/ 1); /* Ztlm=0*/
++l_current_data; ++l_current_data;
opj_write_bytes(l_current_data, 0x50, /* Stlm 0x50= ST=1(8bits-255 tiles max),SP=1(Ptlm=32bits) */
1); /* Stlm 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; ++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, if (opj_stream_write_data(p_stream,
p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size, p_j2k->m_specific_param.m_encoder.m_header_tile_data, l_tlm_size,
p_manager) != 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; 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 = 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); p_j2k->m_specific_param.m_encoder.m_total_tile_parts);
if (! p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) { if (! p_j2k->m_specific_param.m_encoder.m_tlm_sot_offsets_buffer) {
return OPJ_FALSE; 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 */ /* Manage profiles and applications and set RSIZ */
/* set cinema parameters if required */ /* set cinema parameters if required */
if (OPJ_IS_CINEMA(parameters->rsiz)) { 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); "Invalid value for option: %s.\n", *p_option_iter);
return OPJ_FALSE; 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 { } else {
opj_event_msg(p_manager, EVT_ERROR, opj_event_msg(p_manager, EVT_ERROR,
"Invalid option: %s.\n", *p_option_iter); "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; 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, if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
(opj_procedure)opj_j2k_write_updated_tlm, p_manager)) { (opj_procedure)opj_j2k_write_updated_tlm, p_manager)) {
return OPJ_FALSE; return OPJ_FALSE;
@ -12528,7 +12572,7 @@ static OPJ_BOOL opj_j2k_setup_header_writing(opj_j2k_t *p_j2k,
return OPJ_FALSE; 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, if (! opj_procedure_list_add_procedure(p_j2k->m_procedure_list,
(opj_procedure)opj_j2k_write_tlm, p_manager)) { (opj_procedure)opj_j2k_write_tlm, p_manager)) {
return OPJ_FALSE; 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, opj_write_bytes(l_begin_data + 6, l_nb_bytes_written,
4); /* PSOT */ 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); 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, opj_write_bytes(l_begin_data + 6, l_part_tile_size,
4); /* PSOT */ 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); 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, opj_write_bytes(l_begin_data + 6, l_part_tile_size,
4); /* PSOT */ 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); 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_UINT32 l_tlm_size;
OPJ_OFF_T l_tlm_position, l_current_position; OPJ_OFF_T l_tlm_position, l_current_position;
OPJ_UINT32 size_per_tile_part;
/* preconditions */ /* preconditions */
assert(p_j2k != 00); assert(p_j2k != 00);
assert(p_manager != 00); assert(p_manager != 00);
assert(p_stream != 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_tlm_position = 6 + p_j2k->m_specific_param.m_encoder.m_tlm_start;
l_current_position = opj_stream_tell(p_stream); l_current_position = opj_stream_tell(p_stream);

View 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.*/ /** 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 */ 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 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. after encoding the tilepart, a jump (in j2k_write_sod) is done to the TLM marker to store the value of its length.

View File

@ -1592,7 +1592,10 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_setup_encoder(opj_codec_t *p_codec,
* <ul> * <ul>
* <li>PLT=YES/NO. Defaults to NO. If set to YES, PLT marker segments, * <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 * 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> * </ul>
* *
* @param p_codec Compressor handle * @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. * array of strings. Each string is of the form KEY=VALUE.
* *
* @return OPJ_TRUE in case of success. * @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_API OPJ_BOOL OPJ_CALLCONV opj_encoder_set_extra_options(
opj_codec_t *p_codec, opj_codec_t *p_codec,

View 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.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_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 # DECODER TEST SUITE
opj_decompress -i @INPUT_NR_PATH@/Bretagne2.j2k -o @TEMP_PATH@/Bretagne2.j2k.pgx 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 opj_decompress -i @INPUT_NR_PATH@/_00042.j2k -o @TEMP_PATH@/_00042.j2k.pgx