Skip to content

Commit

Permalink
Add support for enabling generation of TLM markers in encoder
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
rouault committed Jun 7, 2021
1 parent 2624908 commit a36ae03
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 21 deletions.
28 changes: 24 additions & 4 deletions src/bin/jp2/opj_compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -1712,6 +1716,12 @@ static int parse_cmdline_encoder(int argc, char **argv,
}
}
break;
/* ------------------------------------------------------ */

case 'D': { /* TLM markers */
*pOutTLM = OPJ_TRUE;
}
break;

/* ------------------------------------------------------ */

Expand Down Expand Up @@ -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 */
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
77 changes: 62 additions & 15 deletions src/lib/openjp2/j2k.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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(
Expand All @@ -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;

Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}

Expand All @@ -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);

Expand Down
6 changes: 6 additions & 0 deletions src/lib/openjp2/j2k.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions src/lib/openjp2/openjpeg.h
Original file line number Diff line number Diff line change
Expand Up @@ -1592,15 +1592,18 @@ 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
* @param p_options Compression options. This should be a NULL terminated
* 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,
Expand Down
4 changes: 4 additions & 0 deletions tests/nonregression/test_suite.ctest.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit a36ae03

Please sign in to comment.