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:
parent
5d07d463fd
commit
0ae3cba340
|
@ -9147,10 +9147,15 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
|
|||
OPJ_BOOL ret;
|
||||
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 */
|
||||
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,
|
||||
"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;
|
||||
}
|
||||
|
||||
|
@ -10508,20 +10513,27 @@ static OPJ_BOOL opj_j2k_decode_tiles(opj_j2k_t *p_j2k,
|
|||
}
|
||||
|
||||
for (;;) {
|
||||
if (! opj_j2k_read_tile_header(p_j2k,
|
||||
&l_current_tile_no,
|
||||
NULL,
|
||||
&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 (p_j2k->m_cp.tw == 1 && p_j2k->m_cp.th == 1 &&
|
||||
p_j2k->m_cp.tcps[0].m_data != NULL) {
|
||||
l_current_tile_no = 0;
|
||||
p_j2k->m_current_tile_number = 0;
|
||||
p_j2k->m_specific_param.m_decoder.m_state |= J2K_STATE_DATA;
|
||||
} else {
|
||||
if (! opj_j2k_read_tile_header(p_j2k,
|
||||
&l_current_tile_no,
|
||||
NULL,
|
||||
&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) {
|
||||
break;
|
||||
if (! l_go_on) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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,
|
||||
"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)) {
|
||||
return OPJ_FALSE;
|
||||
if (p_j2k->m_output_image == NULL) {
|
||||
p_j2k->m_output_image = opj_image_create0();
|
||||
if (!(p_j2k->m_output_image)) {
|
||||
return OPJ_FALSE;
|
||||
}
|
||||
}
|
||||
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++) {
|
||||
p_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;
|
||||
#if 0
|
||||
char fn[256];
|
||||
|
|
|
@ -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
|
||||
* 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_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).
|
||||
|
|
|
@ -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 (tccp->qmfbid == 1) {
|
||||
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->x1,
|
||||
(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;
|
||||
}
|
||||
|
||||
|
@ -1770,8 +1786,6 @@ void opj_t1_decode_cblks(opj_tcd_t* tcd,
|
|||
opj_tcd_cblk_dec_t* cblk = &precinct->cblks.dec[cblkno];
|
||||
opj_t1_cblk_decode_processing_job_t* job;
|
||||
|
||||
assert(cblk->decoded_data == NULL);
|
||||
|
||||
if (!opj_tcd_is_subband_area_of_interest(tcd,
|
||||
tilec->compno,
|
||||
resno,
|
||||
|
@ -1780,15 +1794,34 @@ void opj_t1_decode_cblks(opj_tcd_t* tcd,
|
|||
(OPJ_UINT32)cblk->y0,
|
||||
(OPJ_UINT32)cblk->x1,
|
||||
(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;
|
||||
}
|
||||
|
||||
if (!tcd->whole_tile_decoding) {
|
||||
OPJ_UINT32 cblk_w = (OPJ_UINT32)(cblk->x1 - cblk->x0);
|
||||
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) {
|
||||
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 */
|
||||
cblk->decoded_data = opj_calloc(1, cblk_w * cblk_h * sizeof(OPJ_INT32));
|
||||
if (cblk->decoded_data == NULL) {
|
||||
|
@ -1803,6 +1836,11 @@ void opj_t1_decode_cblks(opj_tcd_t* tcd,
|
|||
*pret = OPJ_FALSE;
|
||||
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,
|
||||
|
|
|
@ -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)
|
||||
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)
|
||||
|
||||
# No image send to the dashboard if lib PNG is not available.
|
||||
|
|
|
@ -99,31 +99,13 @@ static void info_callback(const char *msg, void *client_data)
|
|||
/*fprintf(stdout, "[INFO] %s", msg);*/
|
||||
}
|
||||
|
||||
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)
|
||||
static opj_codec_t* create_codec_and_stream(const char* input_file,
|
||||
opj_stream_t** pOutStream)
|
||||
{
|
||||
opj_dparameters_t l_param;
|
||||
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_stream = opj_stream_create_default_file_stream(input_file, OPJ_TRUE);
|
||||
if (!l_stream) {
|
||||
fprintf(stderr, "ERROR -> failed to create the stream from the file\n");
|
||||
|
@ -168,6 +150,40 @@ opj_image_t* decode(
|
|||
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*/
|
||||
if (! opj_read_header(l_stream, l_codec, &l_image)) {
|
||||
fprintf(stderr, "ERROR -> failed to read the header\n");
|
||||
|
@ -226,6 +242,122 @@ opj_image_t* decode(
|
|||
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_UINT32 compno;
|
||||
|
@ -273,10 +405,13 @@ int main(int argc, char** argv)
|
|||
OPJ_UINT32 step_x, step_y;
|
||||
OPJ_BOOL quiet = OPJ_FALSE;
|
||||
OPJ_UINT32 nsteps = 100;
|
||||
OPJ_UINT32 strip_height = 0;
|
||||
OPJ_BOOL strip_check = OPJ_FALSE;
|
||||
|
||||
if (argc < 2) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -288,6 +423,11 @@ int main(int argc, char** argv)
|
|||
} else if (strcmp(argv[iarg], "-steps") == 0 && iarg + 1 < argc) {
|
||||
nsteps = (OPJ_UINT32)atoi(argv[iarg + 1]);
|
||||
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) {
|
||||
input_file = argv[iarg];
|
||||
} 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,
|
||||
&tilew, &tileh, &cblkw, &cblkh);
|
||||
if (!l_image) {
|
||||
return 1;
|
||||
if (!strip_height || strip_check) {
|
||||
l_image = decode(quiet, input_file, 0, 0, 0, 0,
|
||||
&tilew, &tileh, &cblkw, &cblkh);
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue