Tobias,
I would like to apply your patch, but the test in
examples/tests/pngsuite fails. If you can submit a new patch where this
test passes, and, even better, if a small example 12-bit PNG of yours is
added to the test, I will apply it.
Apart from that, I would echo Eric's thanks for the patch and
explanation.
-Andrew
Hi Andrew and Eric,
Thanks for the responses. I was unaware of the png test suite. I have
attached a new diff that passes this test correctly. It originally failed
because I was not handling greyscale images with an alpha channel, but it
also brought to light several other issues with my code that I have fixed.
I have changed the structure of the code significantly - the if/else
struct has gone and a single loop returns the different image matrices.
Although this is more concise, it no longer informs the user if it hits an
unsupported image type.
Unfortunately I do not have a small test image available, all of ours are
a minimum of 512x620. However the pngsuite page,
http://libpng.org/pub/png/pngsuite.html, does have a set of suitable
images labelled cs*n*.png. These have thrown up an interesting issue - to
what maximum value should n-bit images be scaled when n is between 8 and
16? The png spec and test images suggest it should be (2^n - 1). This
means that higher bit depths give higher precision over the same intensity
range and the same maximum value. However for my particular camera and
software this would be wrong, as the CCD has a fixed 12-bit dynamic range
and the lower png bit depths are only used to save file space. Hence at
the moment I have set my software to scale to (2^16 - 1) for 8 < n < 16,
but it follows the png spec for n < 8, so there are two contradictory
behaviours and I am unsure which is the best approach. Personally I would
prefer matplotlib to return raw integer values, not floats scaled between
0 and 1 and then I can apply the scaling myself, but I am aware that this
is not particularly user friendly for anyone else. imshow() seems to
handle integer values fine and correctly scales for display, provided that
no alpha channel is present.
Should I post another message to the developer list about this to see what
people think? I'd very much like to discuss this with someone who has a
lot more experience of pngs than me.
Thanks,
Tobias
Index: _png.cpp
===================================================================
--- _png.cpp (revision 7026)
+++ _png.cpp (working copy)
@@ -208,38 +208,37 @@
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
-
png_read_info(png_ptr, info_ptr);
png_uint_32 width = info_ptr->width;
png_uint_32 height = info_ptr->height;
- bool do_gray_conversion = (info_ptr->bit_depth < 8 &&
- info_ptr->color_type == PNG_COLOR_TYPE_GRAY);
int bit_depth = info_ptr->bit_depth;
- if (bit_depth == 16) {
- png_set_strip_16(png_ptr);
- } else if (bit_depth < 8) {
+
+ // Unpack 1, 2, and 4-bit images
+ if (bit_depth < 8)
png_set_packing(png_ptr);
- }
- // convert misc color types to rgb for simplicity
- if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
- info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
- png_set_gray_to_rgb(png_ptr);
- } else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+ // If sig bits are set, shift data
+ png_color_8p sig_bit;
+ if ((info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) &&
png_get_sBIT(png_ptr, info_ptr, &sig_bit))
+ png_set_shift(png_ptr, sig_bit);
+
+ // Convert big endian to little
+ if (bit_depth == 16)
+ png_set_swap(png_ptr);
+
+ // Convert palletes to full RGB
+ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png_ptr);
- }
+
+ // If there's an alpha channel convert gray to RGB
+ if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
- bool rgba = info_ptr->color_type == PNG_COLOR_TYPE_RGBA;
- if ( (info_ptr->color_type != PNG_COLOR_TYPE_RGB) && !rgba) {
- std::cerr << "Found color type " << (int)info_ptr->color_type <<
std::endl;
- throw Py::RuntimeError("_image_module::readpng: cannot handle color_type");
- }
-
/* read file */
if (setjmp(png_jmpbuf(png_ptr)))
throw Py::RuntimeError("_image_module::readpng: error during read_image");
@@ -255,37 +254,36 @@
npy_intp dimensions[3];
dimensions[0] = height; //numrows
dimensions[1] = width; //numcols
- dimensions[2] = 4;
+ if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+ dimensions[2] = 4; //RGBA images
+ else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+ dimensions[2] = 3; //RGB images
+ else
+ dimensions[2] = 1; //Greyscale images
+ //For gray, return an x by y array, not an x by y by 1
+ int num_dims = (info_ptr->color_type & PNG_COLOR_MASK_COLOR) ? 3 : 2;
+
+ double max_value = (1 << ((bit_depth < 8) ? 8 : bit_depth)) - 1;
+ PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNew(num_dims, dimensions,
PyArray_FLOAT);
- PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNew(3, dimensions,
PyArray_FLOAT);
-
- if (do_gray_conversion) {
- float max_value = (float)((1L << bit_depth) - 1);
- for (png_uint_32 y = 0; y < height; y++) {
- png_byte* row = row_pointers[y];
- for (png_uint_32 x = 0; x < width; x++) {
- float value = row[x] / max_value;
- size_t offset = y*A->strides[0] + x*A->strides[1];
- *(float*)(A->data + offset + 0*A->strides[2]) = value;
- *(float*)(A->data + offset + 1*A->strides[2]) = value;
- *(float*)(A->data + offset + 2*A->strides[2]) = value;
- *(float*)(A->data + offset + 3*A->strides[2]) = 1.0f;
- }
+ for (png_uint_32 y = 0; y < height; y++) {
+ png_byte* row = row_pointers[y];
+ for (png_uint_32 x = 0; x < width; x++) {
+ size_t offset = y*A->strides[0] + x*A->strides[1];
+ if (bit_depth == 16) {
+ png_uint_16* ptr = &reinterpret_cast<png_uint_16*> (row)[x *
dimensions[2]];
+ for (png_uint_32 p = 0; p < dimensions[2]; p++)
+ *(float*)(A->data + offset + p*A->strides[2]) = (float)(ptr[p]) /
max_value;
+ } else {
+ png_byte* ptr = &(row[x * dimensions[2]]);
+ for (png_uint_32 p = 0; p < dimensions[2]; p++)
+ {
+ *(float*)(A->data + offset + p*A->strides[2]) = (float)(ptr[p]) /
max_value;
+ }
+ }
}
- } else {
- for (png_uint_32 y = 0; y < height; y++) {
- png_byte* row = row_pointers[y];
- for (png_uint_32 x = 0; x < width; x++) {
- png_byte* ptr = (rgba) ? &(row[x*4]) : &(row[x*3]);
- size_t offset = y*A->strides[0] + x*A->strides[1];
- *(float*)(A->data + offset + 0*A->strides[2]) = (float)(ptr[0]/255.0);
- *(float*)(A->data + offset + 1*A->strides[2]) = (float)(ptr[1]/255.0);
- *(float*)(A->data + offset + 2*A->strides[2]) = (float)(ptr[2]/255.0);
- *(float*)(A->data + offset + 3*A->strides[2]) = rgba ?
(float)(ptr[3]/255.0) : 1.0f;
- }
- }
}
-
+
//free the png memory
png_read_end(png_ptr, info_ptr);
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
------------------------------------------------------------------------------
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users