Allow several repeated calls to opj_set_decode_area() and opj_decode() for single-tiled images

* Only works for single-tiled images --> will error out cleanly, as currently
  in other cases
* Save re-reading the codestream for the tile, and re-use code-blocks of the
  previous decoding pass.
* Future improvements might involve improving opj_decompress, and the image writing logic,
  to use this strategy.
This commit is contained in:
Even Rouault 2017-09-01 16:30:48 +02:00
parent 5d07d463fd
commit 0ae3cba340
5 changed files with 268 additions and 46 deletions

View File

@ -9147,10 +9147,15 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
OPJ_BOOL ret; OPJ_BOOL ret;
OPJ_UINT32 it_comp; OPJ_UINT32 it_comp;
if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
&p_j2k->m_cp.tcps[0].m_data != NULL) {
/* In the case of a single-tiled image whose codestream we have already */
/* ingested, go on */
}
/* Check if we are read the main header */ /* Check if we are read the main header */
if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) { else if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) {
opj_event_msg(p_manager, EVT_ERROR, opj_event_msg(p_manager, EVT_ERROR,
"Need to decode the main header before begin to decode the remaining codestream"); "Need to decode the main header before begin to decode the remaining codestream.\n");
return OPJ_FALSE; return OPJ_FALSE;
} }
@ -10508,20 +10513,27 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
} }
for (;;) { for (;;) {
if (! opj_j2k_read_tile_header(p_j2k, if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
&l_current_tile_no, p_j2k->m_cp.tcps[0].m_data != NULL) {
NULL, l_current_tile_no = 0;
&l_tile_x0, &l_tile_y0, p_j2k->m_current_tile_number = 0;
&l_tile_x1, &l_tile_y1, p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_DATA;
&l_nb_comps, } else {
&l_go_on, if (! opj_j2k_read_tile_header(p_j2k,
p_stream, &l_current_tile_no,
p_manager)) { NULL,
return OPJ_FALSE; &l_tile_x0, &l_tile_y0,
} &l_tile_x1, &l_tile_y1,
&l_nb_comps,
&l_go_on,
p_stream,
p_manager)) {
return OPJ_FALSE;
}
if (! l_go_on) { if (! l_go_on) {
break; break;
}
} }
if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0, if (! opj_j2k_decode_tile(p_j2k, l_current_tile_no, NULL, 0,
@ -10538,7 +10550,16 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
p_j2k->m_output_image)) { p_j2k->m_output_image)) {
return OPJ_FALSE; return OPJ_FALSE;
} }
opj_j2k_tcp_data_destroy(&p_j2k->m_cp.tcps[l_current_tile_no]);
if (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
!(p_j2k->m_output_image->x0 == p_j2k->m_private_image->x0 &&
p_j2k->m_output_image->y0 == p_j2k->m_private_image->y0 &&
p_j2k->m_output_image->x1 == p_j2k->m_private_image->x1 &&
p_j2k->m_output_image->y1 == p_j2k->m_private_image->y1)) {
/* Keep current tcp data */
} else {
opj_j2k_tcp_data_destroy(&p_j2k->m_cp.tcps[l_current_tile_no]);
}
opj_event_msg(p_manager, EVT_INFO, opj_event_msg(p_manager, EVT_INFO,
"Image data has been updated with tile %d.\n\n", l_current_tile_no + 1); "Image data has been updated with tile %d.\n\n", l_current_tile_no + 1);
@ -10738,9 +10759,11 @@ OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
} }
} }
p_j2k->m_output_image = opj_image_create0(); if (p_j2k->m_output_image == NULL) {
if (!(p_j2k->m_output_image)) { p_j2k->m_output_image = opj_image_create0();
return OPJ_FALSE; if (!(p_j2k->m_output_image)) {
return OPJ_FALSE;
}
} }
opj_copy_image_header(p_image, p_j2k->m_output_image); opj_copy_image_header(p_image, p_j2k->m_output_image);
@ -10760,6 +10783,7 @@ OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
for (compno = 0; compno < p_image->numcomps; compno++) { for (compno = 0; compno < p_image->numcomps; compno++) {
p_image->comps[compno].resno_decoded = p_image->comps[compno].resno_decoded =
p_j2k->m_output_image->comps[compno].resno_decoded; p_j2k->m_output_image->comps[compno].resno_decoded;
opj_image_data_free(p_image->comps[compno].data);
p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data; p_image->comps[compno].data = p_j2k->m_output_image->comps[compno].data;
#if 0 #if 0
char fn[256]; char fn[256];

View File

@ -1340,6 +1340,12 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
* that is to say at the highest resolution level, even if requesting the image at lower * that is to say at the highest resolution level, even if requesting the image at lower
* resolution levels. * resolution levels.
* *
* Generally opj_set_decode_area() should be followed by opj_decode(), and the
* codec cannot be re-used.
* In the particular case of an image made of a single tile, several sequences of
* calls to opoj_set_decode_area() and opj_decode() are allowed, and will bring
* performance improvements when reading an image by chunks.
*
* @param p_codec the jpeg2000 codec. * @param p_codec the jpeg2000 codec.
* @param p_image the decoded image previously setted by opj_read_header * @param p_image the decoded image previously setted by opj_read_header
* @param p_start_x the left position of the rectangle to decode (in image coordinates). * @param p_start_x the left position of the rectangle to decode (in image coordinates).

View File

@ -1668,6 +1668,11 @@ static void opj_t1_clbl_decode_processor(void* user_data, opj_tls_t* tls)
} }
} }
/* Both can be non NULL if for example decoding a full tile and then */
/* partially a tile. In which case partial decoding should be the */
/* priority */
assert((cblk->decoded_data != NULL) || (tilec->data != NULL));
if (cblk->decoded_data) { if (cblk->decoded_data) {
if (tccp->qmfbid == 1) { if (tccp->qmfbid == 1) {
for (j = 0; j < cblk_h; ++j) { for (j = 0; j < cblk_h; ++j) {
@ -1763,6 +1768,17 @@ void opj_t1_decode_cblks(opj_tcd_t* tcd,
(OPJ_UINT32)precinct->y0, (OPJ_UINT32)precinct->y0,
(OPJ_UINT32)precinct->x1, (OPJ_UINT32)precinct->x1,
(OPJ_UINT32)precinct->y1)) { (OPJ_UINT32)precinct->y1)) {
for (cblkno = 0; cblkno < precinct->cw * precinct->ch; ++cblkno) {
opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
if (cblk->decoded_data) {
#ifdef DEBUG_VERBOSE
printf("Discarding codeblock %d,%d at resno=%d, bandno=%d\n",
cblk->x0, cblk->y0, resno, bandno);
#endif
opj_free(cblk->decoded_data);
cblk->decoded_data = NULL;
}
}
continue; continue;
} }
@ -1770,8 +1786,6 @@ void opj_t1_decode_cblks(opj_tcd_t* tcd,
opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno]; opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
opj_t1_cblk_decode_processing_job_t* job; opj_t1_cblk_decode_processing_job_t* job;
assert(cblk->decoded_data == NULL);
if (!opj_tcd_is_subband_area_of_interest(tcd, if (!opj_tcd_is_subband_area_of_interest(tcd,
tilec->compno, tilec->compno,
resno, resno,
@ -1780,15 +1794,34 @@ void opj_t1_decode_cblks(opj_tcd_t* tcd,
(OPJ_UINT32)cblk->y0, (OPJ_UINT32)cblk->y0,
(OPJ_UINT32)cblk->x1, (OPJ_UINT32)cblk->x1,
(OPJ_UINT32)cblk->y1)) { (OPJ_UINT32)cblk->y1)) {
if (cblk->decoded_data) {
#ifdef DEBUG_VERBOSE
printf("Discarding codeblock %d,%d at resno=%d, bandno=%d\n",
cblk->x0, cblk->y0, resno, bandno);
#endif
opj_free(cblk->decoded_data);
cblk->decoded_data = NULL;
}
continue; continue;
} }
if (!tcd->whole_tile_decoding) { if (!tcd->whole_tile_decoding) {
OPJ_UINT32 cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0); OPJ_UINT32 cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
OPJ_UINT32 cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0); OPJ_UINT32 cblk_h = (OPJ_UINT32)(cblk->y1 - cblk->y0);
if (cblk->decoded_data != NULL) {
#ifdef DEBUG_VERBOSE
printf("Reusing codeblock %d,%d at resno=%d, bandno=%d\n",
cblk->x0, cblk->y0, resno, bandno);
#endif
continue;
}
if (cblk_w == 0 || cblk_h == 0) { if (cblk_w == 0 || cblk_h == 0) {
continue; continue;
} }
#ifdef DEBUG_VERBOSE
printf("Decoding codeblock %d,%d at resno=%d, bandno=%d\n",
cblk->x0, cblk->y0, resno, bandno);
#endif
/* Zero-init required */ /* Zero-init required */
cblk->decoded_data = opj_calloc(1, cblk_w * cblk_h * sizeof(OPJ_INT32)); cblk->decoded_data = opj_calloc(1, cblk_w * cblk_h * sizeof(OPJ_INT32));
if (cblk->decoded_data == NULL) { if (cblk->decoded_data == NULL) {
@ -1803,6 +1836,11 @@ void opj_t1_decode_cblks(opj_tcd_t* tcd,
*pret = OPJ_FALSE; *pret = OPJ_FALSE;
return; return;
} }
} else if (cblk->decoded_data) {
/* Not sure if that code path can happen, but better be */
/* safe than sorry */
opj_free(cblk->decoded_data);
cblk->decoded_data = NULL;
} }
job = (opj_t1_cblk_decode_processing_job_t*) opj_calloc(1, job = (opj_t1_cblk_decode_processing_job_t*) opj_calloc(1,

View File

@ -103,6 +103,10 @@ add_test(NAME tda_prep_irreversible_203_201_17_19_no_precinct COMMAND test_tile_
add_test(NAME tda_irreversible_203_201_17_19_no_precinct COMMAND test_decode_area -q irreversible_203_201_17_19_no_precinct.j2k) add_test(NAME tda_irreversible_203_201_17_19_no_precinct COMMAND test_decode_area -q irreversible_203_201_17_19_no_precinct.j2k)
set_property(TEST tda_irreversible_203_201_17_19_no_precinct APPEND PROPERTY DEPENDS tda_prep_irreversible_203_201_17_19_no_precinct) set_property(TEST tda_irreversible_203_201_17_19_no_precinct APPEND PROPERTY DEPENDS tda_prep_irreversible_203_201_17_19_no_precinct)
add_test(NAME tda_prep_strip COMMAND test_tile_encoder 1 256 256 256 256 8 0 tda_single_tile.j2k)
add_test(NAME tda_strip COMMAND test_decode_area -q -strip_height 3 -strip_check tda_single_tile.j2k)
set_property(TEST tda_strip APPEND PROPERTY DEPENDS tda_prep_strip)
add_executable(include_openjpeg include_openjpeg.c) add_executable(include_openjpeg include_openjpeg.c)
# No image send to the dashboard if lib PNG is not available. # No image send to the dashboard if lib PNG is not available.

View File

@ -99,31 +99,13 @@ static void info_callback(const char *msg, void *client_data)
/*fprintf(stdout, "[INFO] %s", msg);*/ /*fprintf(stdout, "[INFO] %s", msg);*/
} }
opj_image_t* decode( static opj_codec_t* create_codec_and_stream(const char* input_file,
OPJ_BOOL quiet, opj_stream_t** pOutStream)
const char* input_file,
OPJ_INT32 x0,
OPJ_INT32 y0,
OPJ_INT32 x1,
OPJ_INT32 y1,
OPJ_UINT32* ptilew,
OPJ_UINT32* ptileh,
OPJ_UINT32* pcblkw,
OPJ_UINT32* pcblkh)
{ {
opj_dparameters_t l_param; opj_dparameters_t l_param;
opj_codec_t * l_codec = NULL; opj_codec_t * l_codec = NULL;
opj_image_t * l_image = NULL;
opj_stream_t * l_stream = NULL; opj_stream_t * l_stream = NULL;
if (!quiet) {
if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
printf("Decoding %d,%d,%d,%d\n", x0, y0, x1, y1);
} else {
printf("Decoding full image\n");
}
}
l_stream = opj_stream_create_default_file_stream(input_file, OPJ_TRUE); l_stream = opj_stream_create_default_file_stream(input_file, OPJ_TRUE);
if (!l_stream) { if (!l_stream) {
fprintf(stderr, "ERROR -> failed to create the stream from the file\n"); fprintf(stderr, "ERROR -> failed to create the stream from the file\n");
@ -168,6 +150,40 @@ opj_image_t* decode(
return NULL; return NULL;
} }
*pOutStream = l_stream;
return l_codec;
}
opj_image_t* decode(
OPJ_BOOL quiet,
const char* input_file,
OPJ_INT32 x0,
OPJ_INT32 y0,
OPJ_INT32 x1,
OPJ_INT32 y1,
OPJ_UINT32* ptilew,
OPJ_UINT32* ptileh,
OPJ_UINT32* pcblkw,
OPJ_UINT32* pcblkh)
{
opj_codec_t * l_codec = NULL;
opj_image_t * l_image = NULL;
opj_stream_t * l_stream = NULL;
if (!quiet) {
if (x0 != 0 || x1 != 0 || y0 != 0 || y1 != 0) {
printf("Decoding %d,%d,%d,%d\n", x0, y0, x1, y1);
} else {
printf("Decoding full image\n");
}
}
l_codec = create_codec_and_stream(input_file, &l_stream);
if (l_codec == NULL) {
return NULL;
}
/* Read the main header of the codestream and if necessary the JP2 boxes*/ /* Read the main header of the codestream and if necessary the JP2 boxes*/
if (! opj_read_header(l_stream, l_codec, &l_image)) { if (! opj_read_header(l_stream, l_codec, &l_image)) {
fprintf(stderr, "ERROR -> failed to read the header\n"); fprintf(stderr, "ERROR -> failed to read the header\n");
@ -226,6 +242,122 @@ opj_image_t* decode(
return l_image; return l_image;
} }
int decode_by_strip(OPJ_BOOL quiet,
const char* input_file,
OPJ_UINT32 strip_height,
opj_image_t* full_image)
{
/* OPJ_UINT32 tilew, tileh; */
opj_codec_t * l_codec = NULL;
opj_image_t * l_image = NULL;
opj_stream_t * l_stream = NULL;
OPJ_UINT32 x0, y0, x1, y1, y;
l_codec = create_codec_and_stream(input_file, &l_stream);
if (l_codec == NULL) {
return 1;
}
/* Read the main header of the codestream and if necessary the JP2 boxes*/
if (! opj_read_header(l_stream, l_codec, &l_image)) {
fprintf(stderr, "ERROR -> failed to read the header\n");
opj_stream_destroy(l_stream);
opj_destroy_codec(l_codec);
return 1;
}
x0 = l_image->x0;
y0 = l_image->y0;
x1 = l_image->x1;
y1 = l_image->y1;
for (y = y0; y < y1; y += strip_height) {
OPJ_UINT32 h_req = strip_height;
if (y + h_req > y1) {
h_req = y1 - y;
}
if (!quiet) {
printf("Decoding %u...%u\n", y, y + h_req);
}
if (!opj_set_decode_area(l_codec, l_image, (OPJ_INT32)x0, (OPJ_INT32)y,
(OPJ_INT32)x1, (OPJ_INT32)(y + h_req))) {
fprintf(stderr, "ERROR -> failed to set the decoded area\n");
opj_stream_destroy(l_stream);
opj_destroy_codec(l_codec);
opj_image_destroy(l_image);
return 1;
}
/* Get the decoded image */
if (!(opj_decode(l_codec, l_stream, l_image))) {
fprintf(stderr, "ERROR -> failed to decode image!\n");
opj_stream_destroy(l_stream);
opj_destroy_codec(l_codec);
opj_image_destroy(l_image);
return 1;
}
if (full_image) {
OPJ_UINT32 y_check, x;
OPJ_UINT32 compno;
for (compno = 0; compno < l_image->numcomps; compno ++) {
for (y_check = 0; y_check < h_req; y_check++) {
for (x = x0; x < x1; x++) {
OPJ_INT32 sub_image_val =
l_image->comps[compno].data[y_check * (x1 - x0) + x];
OPJ_INT32 image_val =
full_image->comps[compno].data[(y + y_check) * (x1 - x0) + x];
if (sub_image_val != image_val) {
fprintf(stderr,
"Difference found at subimage pixel (%u,%u) "
"of compno=%u: got %d, expected %d\n",
x, y_check + y, compno, sub_image_val, image_val);
return 1;
}
}
}
}
}
}
/* If image is small enough, try a final whole image read */
if (x1 - x0 < 10000 && y1 - y0 < 10000) {
if (!quiet) {
printf("Decoding full image\n");
}
if (!opj_set_decode_area(l_codec, l_image, (OPJ_INT32)x0, (OPJ_INT32)y0,
(OPJ_INT32)x1, (OPJ_INT32)y1)) {
fprintf(stderr, "ERROR -> failed to set the decoded area\n");
opj_stream_destroy(l_stream);
opj_destroy_codec(l_codec);
opj_image_destroy(l_image);
return 1;
}
/* Get the decoded image */
if (!(opj_decode(l_codec, l_stream, l_image))) {
fprintf(stderr, "ERROR -> failed to decode image!\n");
opj_stream_destroy(l_stream);
opj_destroy_codec(l_codec);
opj_image_destroy(l_image);
return 1;
}
}
if (! opj_end_decompress(l_codec, l_stream)) {
opj_stream_destroy(l_stream);
opj_destroy_codec(l_codec);
opj_image_destroy(l_image);
return 1;
}
opj_stream_destroy(l_stream);
opj_destroy_codec(l_codec);
opj_image_destroy(l_image);
return 0;
}
OPJ_BOOL check_consistency(opj_image_t* p_image, opj_image_t* p_sub_image) OPJ_BOOL check_consistency(opj_image_t* p_image, opj_image_t* p_sub_image)
{ {
OPJ_UINT32 compno; OPJ_UINT32 compno;
@ -273,10 +405,13 @@ int main(int argc, char** argv)
OPJ_UINT32 step_x, step_y; OPJ_UINT32 step_x, step_y;
OPJ_BOOL quiet = OPJ_FALSE; OPJ_BOOL quiet = OPJ_FALSE;
OPJ_UINT32 nsteps = 100; OPJ_UINT32 nsteps = 100;
OPJ_UINT32 strip_height = 0;
OPJ_BOOL strip_check = OPJ_FALSE;
if (argc < 2) { if (argc < 2) {
fprintf(stderr, fprintf(stderr,
"Usage: test_decode_area [-q] [-steps n] input_file_jp2_or_jk2 [x0 y0 x1 y1]\n"); "Usage: test_decode_area [-q] [-steps n] input_file_jp2_or_jk2 [x0 y0 x1 y1]\n"
"or : test_decode_area [-q] [-strip_height h] [-strip_check] input_file_jp2_or_jk2\n");
return 1; return 1;
} }
@ -288,6 +423,11 @@ int main(int argc, char** argv)
} else if (strcmp(argv[iarg], "-steps") == 0 && iarg + 1 < argc) { } else if (strcmp(argv[iarg], "-steps") == 0 && iarg + 1 < argc) {
nsteps = (OPJ_UINT32)atoi(argv[iarg + 1]); nsteps = (OPJ_UINT32)atoi(argv[iarg + 1]);
iarg ++; iarg ++;
} else if (strcmp(argv[iarg], "-strip_height") == 0 && iarg + 1 < argc) {
strip_height = (OPJ_UINT32)atoi(argv[iarg + 1]);
iarg ++;
} else if (strcmp(argv[iarg], "-strip_check") == 0) {
strip_check = OPJ_TRUE;
} else if (input_file == NULL) { } else if (input_file == NULL) {
input_file = argv[iarg]; input_file = argv[iarg];
} else if (iarg + 3 < argc) { } else if (iarg + 3 < argc) {
@ -300,10 +440,20 @@ int main(int argc, char** argv)
} }
} }
l_image = decode(quiet, input_file, 0, 0, 0, 0, if (!strip_height || strip_check) {
&tilew, &tileh, &cblkw, &cblkh); l_image = decode(quiet, input_file, 0, 0, 0, 0,
if (!l_image) { &tilew, &tileh, &cblkw, &cblkh);
return 1; if (!l_image) {
return 1;
}
}
if (strip_height) {
int ret = decode_by_strip(quiet, input_file, strip_height, l_image);
if (l_image) {
opj_image_destroy(l_image);
}
return ret;
} }
if (da_x0 != 0 || da_x1 != 0 || da_y0 != 0 || da_y1 != 0) { if (da_x0 != 0 || da_x1 != 0 || da_y0 != 0 || da_y1 != 0) {