Make opj_set_decode_area() and opj_decode() take into account opj_set_decoded_resolut...
authorEven Rouault <even.rouault@spatialys.com>
Mon, 28 Aug 2017 12:57:49 +0000 (14:57 +0200)
committerEven Rouault <even.rouault@spatialys.com>
Mon, 28 Aug 2017 12:57:49 +0000 (14:57 +0200)
* Better document usage of opj_set_decode_area(), ie expecting coordinates
  in full resolution/reference grid even if requesting at a lower resolution
  factor
* Make sure that image->comps[].factor is set by opj_set_decode_area() and
  opj_decode() from the value specified in opj_set_decoded_resolution_factor()
* opj_decompress: add 2 environmenet variables to test alternate ways of
  using the API, namely USE_OPJ_SET_DECODED_RESOLUTION_FACTOR=YES to use
  opj_set_decoded_resolution_factor() instead of parameters.cp_reduce, and
  SKIP_OPJ_SET_DECODE_AREA=YES to not call opj_set_decode_area() if -d is
  not specified.

src/bin/jp2/opj_decompress.c
src/lib/openjp2/j2k.c
src/lib/openjp2/openjpeg.h

index 848167babe15a1003d1e959cc93864bfe96c7c50..479d83822eee5f715d8396e01f5e999051e8eb78 100644 (file)
@@ -1297,6 +1297,7 @@ int main(int argc, char **argv)
     int failed = 0;
     OPJ_FLOAT64 t, tCumulative = 0;
     OPJ_UINT32 numDecompressedImages = 0;
+    OPJ_UINT32 cp_reduce;
 
     /* set decoding parameters to default values */
     set_default_parameters(&parameters);
@@ -1310,6 +1311,14 @@ int main(int argc, char **argv)
         goto fin;
     }
 
+    cp_reduce = parameters.core.cp_reduce;
+    if (getenv("USE_OPJ_SET_DECODED_RESOLUTION_FACTOR") != NULL) {
+        /* For debugging/testing purposes, do not set the cp_reduce member */
+        /* if USE_OPJ_SET_DECODED_RESOLUTION_FACTOR is defined, but used */
+        /* the opj_set_decoded_resolution_factor() API instead */
+        parameters.core.cp_reduce = 0;
+    }
+
 
     /* Initialize reading of directory */
     if (img_fol.set_imgdir == 1) {
@@ -1446,11 +1455,35 @@ int main(int argc, char **argv)
             goto fin;
         }
 
+        if (getenv("USE_OPJ_SET_DECODED_RESOLUTION_FACTOR") != NULL) {
+            /* For debugging/testing purposes, and also an illustration on how to */
+            /* use the alternative API opj_set_decoded_resolution_factor() instead */
+            /* of setting parameters.cp_reduce */
+            if (! opj_set_decoded_resolution_factor(l_codec, cp_reduce)) {
+                fprintf(stderr,
+                        "ERROR -> opj_decompress: failed to set the resolution factor tile!\n");
+                opj_destroy_codec(l_codec);
+                opj_stream_destroy(l_stream);
+                opj_image_destroy(image);
+                failed = 1;
+                goto fin;
+            }
+        }
+
         if (!parameters.nb_tile_to_decode) {
+            if (getenv("SKIP_OPJ_SET_DECODE_AREA") != NULL &&
+                    parameters.DA_x0 == 0 &&
+                    parameters.DA_y0 == 0 &&
+                    parameters.DA_x1 == 0 &&
+                    parameters.DA_y1 == 0) {
+                /* For debugging/testing purposes, */
+                /* do nothing if SKIP_OPJ_SET_DECODE_AREA env variable */
+                /* is defined and no decoded area has been set */
+            }
             /* Optional if you want decode the entire image */
-            if (!opj_set_decode_area(l_codec, image, (OPJ_INT32)parameters.DA_x0,
-                                     (OPJ_INT32)parameters.DA_y0, (OPJ_INT32)parameters.DA_x1,
-                                     (OPJ_INT32)parameters.DA_y1)) {
+            else if (!opj_set_decode_area(l_codec, image, (OPJ_INT32)parameters.DA_x0,
+                                          (OPJ_INT32)parameters.DA_y0, (OPJ_INT32)parameters.DA_x1,
+                                          (OPJ_INT32)parameters.DA_y1)) {
                 fprintf(stderr, "ERROR -> opj_decompress: failed to set the decoded area\n");
                 opj_stream_destroy(l_stream);
                 opj_destroy_codec(l_codec);
@@ -1471,15 +1504,6 @@ int main(int argc, char **argv)
             }
         } else {
 
-            /* It is just here to illustrate how to use the resolution after set parameters */
-            /*if (!opj_set_decoded_resolution_factor(l_codec, 5)) {
-                fprintf(stderr, "ERROR -> opj_decompress: failed to set the resolution factor tile!\n");
-                opj_destroy_codec(l_codec);
-                opj_stream_destroy(l_stream);
-                opj_image_destroy(image);
-                failed = 1; goto fin;
-            }*/
-
             if (!opj_get_decoded_tile(l_codec, l_stream, image, parameters.tile_index)) {
                 fprintf(stderr, "ERROR -> opj_decompress: failed to decode tile!\n");
                 opj_destroy_codec(l_codec);
index 543f62c8bd2d1b778d277d26bd71b2ba582ac920..4fd65872af4798d910329c258b0c2f4673097969 100644 (file)
@@ -9143,6 +9143,51 @@ static OPJ_BOOL opj_j2k_update_image_data(opj_tcd_t * p_tcd, OPJ_BYTE * p_data,
     return OPJ_TRUE;
 }
 
+static OPJ_BOOL opj_j2k_update_image_dimensions(opj_image_t* p_image,
+        opj_event_mgr_t * p_manager)
+{
+    OPJ_UINT32 it_comp;
+    OPJ_INT32 l_comp_x1, l_comp_y1;
+    opj_image_comp_t* l_img_comp = NULL;
+
+    l_img_comp = p_image->comps;
+    for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
+        OPJ_INT32 l_h, l_w;
+
+        l_img_comp->x0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->x0,
+                         (OPJ_INT32)l_img_comp->dx);
+        l_img_comp->y0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->y0,
+                         (OPJ_INT32)l_img_comp->dy);
+        l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
+        l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
+
+        l_w = opj_int_ceildivpow2(l_comp_x1, (OPJ_INT32)l_img_comp->factor)
+              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0, (OPJ_INT32)l_img_comp->factor);
+        if (l_w < 0) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Size x of the decoded component image is incorrect (comp[%d].w=%d).\n",
+                          it_comp, l_w);
+            return OPJ_FALSE;
+        }
+        l_img_comp->w = (OPJ_UINT32)l_w;
+
+        l_h = opj_int_ceildivpow2(l_comp_y1, (OPJ_INT32)l_img_comp->factor)
+              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0, (OPJ_INT32)l_img_comp->factor);
+        if (l_h < 0) {
+            opj_event_msg(p_manager, EVT_ERROR,
+                          "Size y of the decoded component image is incorrect (comp[%d].h=%d).\n",
+                          it_comp, l_h);
+            return OPJ_FALSE;
+        }
+        l_img_comp->h = (OPJ_UINT32)l_h;
+
+        l_img_comp++;
+    }
+
+    return OPJ_TRUE;
+}
+
+
 OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
                                  opj_image_t* p_image,
                                  OPJ_INT32 p_start_x, OPJ_INT32 p_start_y,
@@ -9151,10 +9196,8 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
 {
     opj_cp_t * l_cp = &(p_j2k->m_cp);
     opj_image_t * l_image = p_j2k->m_private_image;
-
+    OPJ_BOOL ret;
     OPJ_UINT32 it_comp;
-    OPJ_INT32 l_comp_x1, l_comp_y1;
-    opj_image_comp_t* l_img_comp = NULL;
 
     /* Check if we are read the main header */
     if (p_j2k->m_specific_param.m_decoder.m_state != J2K_STATE_TPHSOT) {
@@ -9163,6 +9206,12 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
         return OPJ_FALSE;
     }
 
+    /* Update the comps[].factor member of the output image with the one */
+    /* of m_reduce */
+    for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
+        p_image->comps[it_comp].factor = p_j2k->m_cp.m_specific_param.m_dec.m_reduce;
+    }
+
     if (!p_start_x && !p_start_y && !p_end_x && !p_end_y) {
         opj_event_msg(p_manager, EVT_INFO,
                       "No decoded area parameters, set the decoded area to the whole image\n");
@@ -9172,7 +9221,12 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
         p_j2k->m_specific_param.m_decoder.m_end_tile_x = l_cp->tw;
         p_j2k->m_specific_param.m_decoder.m_end_tile_y = l_cp->th;
 
-        return OPJ_TRUE;
+        p_image->x0 = l_image->x0;
+        p_image->y0 = l_image->y0;
+        p_image->x1 = l_image->x1;
+        p_image->y1 = l_image->y1;
+
+        return opj_j2k_update_image_dimensions(p_image, p_manager);
     }
 
     /* ----- */
@@ -9274,44 +9328,14 @@ OPJ_BOOL opj_j2k_set_decode_area(opj_j2k_t *p_j2k,
 
     p_j2k->m_specific_param.m_decoder.m_discard_tiles = 1;
 
-    l_img_comp = p_image->comps;
-    for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
-        OPJ_INT32 l_h, l_w;
-
-        l_img_comp->x0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->x0,
-                         (OPJ_INT32)l_img_comp->dx);
-        l_img_comp->y0 = (OPJ_UINT32)opj_int_ceildiv((OPJ_INT32)p_image->y0,
-                         (OPJ_INT32)l_img_comp->dy);
-        l_comp_x1 = opj_int_ceildiv((OPJ_INT32)p_image->x1, (OPJ_INT32)l_img_comp->dx);
-        l_comp_y1 = opj_int_ceildiv((OPJ_INT32)p_image->y1, (OPJ_INT32)l_img_comp->dy);
-
-        l_w = opj_int_ceildivpow2(l_comp_x1, (OPJ_INT32)l_img_comp->factor)
-              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->x0, (OPJ_INT32)l_img_comp->factor);
-        if (l_w < 0) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Size x of the decoded component image is incorrect (comp[%d].w=%d).\n",
-                          it_comp, l_w);
-            return OPJ_FALSE;
-        }
-        l_img_comp->w = (OPJ_UINT32)l_w;
-
-        l_h = opj_int_ceildivpow2(l_comp_y1, (OPJ_INT32)l_img_comp->factor)
-              - opj_int_ceildivpow2((OPJ_INT32)l_img_comp->y0, (OPJ_INT32)l_img_comp->factor);
-        if (l_h < 0) {
-            opj_event_msg(p_manager, EVT_ERROR,
-                          "Size y of the decoded component image is incorrect (comp[%d].h=%d).\n",
-                          it_comp, l_h);
-            return OPJ_FALSE;
-        }
-        l_img_comp->h = (OPJ_UINT32)l_h;
+    ret = opj_j2k_update_image_dimensions(p_image, p_manager);
 
-        l_img_comp++;
+    if (ret) {
+        opj_event_msg(p_manager, EVT_INFO, "Setting decoding area to %d,%d,%d,%d\n",
+                      p_image->x0, p_image->y0, p_image->x1, p_image->y1);
     }
 
-    opj_event_msg(p_manager, EVT_INFO, "Setting decoding area to %d,%d,%d,%d\n",
-                  p_image->x0, p_image->y0, p_image->x1, p_image->y1);
-
-    return OPJ_TRUE;
+    return ret;
 }
 
 opj_j2k_t* opj_j2k_create_decompress(void)
@@ -10796,6 +10820,31 @@ OPJ_BOOL opj_j2k_decode(opj_j2k_t * p_j2k,
         return OPJ_FALSE;
     }
 
+    /* Heuristics to detect sequence opj_read_header(), opj_set_decoded_resolution_factor() */
+    /* and finally opj_decode_image() without manual setting of comps[].factor */
+    /* We could potentially always execute it, if we don't allow people to do */
+    /* opj_read_header(), modify x0,y0,x1,y1 of returned image an call opj_decode_image() */
+    if (p_j2k->m_cp.m_specific_param.m_dec.m_reduce > 0 &&
+            p_j2k->m_private_image != NULL &&
+            p_j2k->m_private_image->numcomps > 0 &&
+            p_j2k->m_private_image->comps[0].factor ==
+            p_j2k->m_cp.m_specific_param.m_dec.m_reduce &&
+            p_image->numcomps > 0 &&
+            p_image->comps[0].factor == 0 &&
+            /* Don't mess with image dimension if the user has allocated it */
+            p_image->comps[0].data == NULL) {
+        OPJ_UINT32 it_comp;
+
+        /* Update the comps[].factor member of the output image with the one */
+        /* of m_reduce */
+        for (it_comp = 0; it_comp < p_image->numcomps; ++it_comp) {
+            p_image->comps[it_comp].factor = p_j2k->m_cp.m_specific_param.m_dec.m_reduce;
+        }
+        if (!opj_j2k_update_image_dimensions(p_image, p_manager)) {
+            return OPJ_FALSE;
+        }
+    }
+
     p_j2k->m_output_image = opj_image_create0();
     if (!(p_j2k->m_output_image)) {
         return OPJ_FALSE;
index 9ae29a4f3a538baaed34a4fa04aa1357b675f447..21755b48d5a091b80d84043941eca0da295f236f 100644 (file)
@@ -1336,6 +1336,10 @@ OPJ_API OPJ_BOOL OPJ_CALLCONV opj_read_header(opj_stream_t *p_stream,
 /**
  * Sets the given area to be decoded. This function should be called right after opj_read_header and before any tile header reading.
  *
+ * The coordinates passed to this function should be expressed in the reference grid,
+ * that is to say at the highest resolution level, even if requesting the image at lower
+ * resolution levels.
+ *
  * @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).