Michael,
Thanks for the comments, much appreciated. I've attached an updated patch
including your suggestions and some more whitespace for readability. There
was no reason other than simplicity for not returning a 8-bit numpy array.
I actually meant to ask about the ternary blocks - I think I picked up the
style from the original code and had continued in the same vein for
compactness.
While I was testing this I came across another issue - which variety of
FLOAT should the code return? My understanding was that Python floats are
C doubles. However the code was previously returning a PyArray_FLOAT,
which seems to be a FLOAT32 rather than a FLOAT64. Hence I removed any
trace of doubles from my code and have left it all at float precision.
Thanks again,
Toby
On Mon, 22 Jun 2009 15:58:58 +0100, Michael Droettboom <md...@stsci.edu>
wrote:
I don't ever work with data-in-PNGs, so I won't comment on the use cases
or API here -- I'll leave that to others.
However, for the patch, I think the reinterpret_cast<unsigned short *>
would be safer as reinterpret_cast<png_uint_16>, since unsigned ints are
not guaranteed to be 16-bits, and png.h provides a nice convenient
typedef for us. Also, why does the code not create an 8-bit numpy array
for "raw" images that are only 8-bits?
Also a style note: I find assignments inside of ternary operators (... ?
... : ...) confusing. I'd rather see that as a proper "if" block.
Cheers,
Mike
Tobias Wood wrote:
Dear list,
Back in April I submitted a patch that allowed imread() to correctly
read PNGs that have odd bit-depths, ie not 8 or 16 (I actually
submitted that to the Users list as I was unsure of protocol). There
were a couple of things I left unfinished that I've finally got round
to looking at again.
The main remaining issue for me is that PNG specifies that all bit
depths should be scaled to have the same maximum brightness, so that a
value of 8191 in an 13-bit image is displayed the same as 65535 in a
16-bit image. Unfortunately, the LabView drivers for the 12-bit CCD in
our lab do not follow this convention. A higher bit-depth from this
setup means the image was brighter in an absolute sense and no scaling
takes place. So this is not an error with Matplotlib as such, but more
about having a decent way to handle iffy PNGs. It is worth noting that
Matlab does not handle these PNGs well either (We have to query the
image file using iminfo and then correct it) and PIL ignores anything
above 8-bits as far as I can tell.
A simple method, in my mind, and originally suggested by Andrew Straw
is to add a keyword argument to imread() that indicates whether a user
wants floats scaled between 0 and 1, or the raw byte values which they
can then scale as required. This then gets passed to read_png(), which
does the scaling if necessary and if not returns an array of UINT16s. I
wrote a patch that does this, changing both image.py and _png.cpp. I'm
very much open to other suggestions, as I didn't particularly want to
fiddle with a core function like imread() and I'm fairly new to Python.
In particular I have not changed anything to do with PIL - although it
would not be much work to update pil_to_array() to follow the same
behaviour as read_png(). I have tested this with the pngsuite.py*, and
if desired I can submit an extended version of this that tests the
extended bit-depth images from the PNG suite.
Thanks in advance,
Toby Wood
* My patch also includes a minor change to pngsuite.py which was
throwing a deprecation warning about using get_frame() istead of patch
------------------------------------------------------------------------
------------------------------------------------------------------------------
Are you an open source citizen? Join us for the Open Source Bridge
conference!
Portland, OR, June 17-19. Two days of sessions, one day of
unconference: $250.
Need another reason to go? 24-hour hacker lounge. Register today!
http://ad.doubleclick.net/clk;215844324;13503038;v?http://opensourcebridge.org
------------------------------------------------------------------------
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel
Index: src/_png.cpp
===================================================================
--- src/_png.cpp (revision 7230)
+++ src/_png.cpp (working copy)
@@ -178,8 +178,11 @@
Py::Object
_png_module::read_png(const Py::Tuple& args) {
- args.verify_length(1);
+ args.verify_length(1,2);
std::string fname = Py::String(args[0]);
+ bool raw = false;
+ if (args.length() == 2)
+ raw = Py::Boolean(args[1]);
png_byte header[8]; // 8 is the maximum size that can be checked
@@ -213,16 +216,21 @@
png_uint_32 width = info_ptr->width;
png_uint_32 height = info_ptr->height;
+ // Bit depth can be 1, 2, 4, 8, or 16
int bit_depth = info_ptr->bit_depth;
-
+ float max_value = (1 << bit_depth) - 1; // For scaling later
// Unpack 1, 2, and 4-bit images
if (bit_depth < 8)
png_set_packing(png_ptr);
- // If sig bits are set, shift data
+ // If a pngs max value does not use the full bit depth, then values are
shifted up during writing.
+ // This shifts them back and then recalculates max_value
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);
+ max_value = (1 << sig_bit->red) - 1; // RGB values appear to always be
equal
+ }
// Convert big endian to little
if (bit_depth == 16)
@@ -260,26 +268,51 @@
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;
+
+ // If read mode is raw give back ints, otherwise float between 0 and 1
+ int ArrayType = PyArray_FLOAT;
+
+ if (raw) {
+ if (bit_depth == 16)
+ ArrayType = PyArray_UINT16;
+ else
+ ArrayType = PyArray_UINT8;
+ }
+ PyArrayObject *A = (PyArrayObject *)PyArray_SimpleNew(num_dims, dimensions,
ArrayType);
+
+ // Actually copy the data across
+ for (png_uint_32 y = 0; y < height; y++) {
- double max_value = (1 << ((bit_depth < 8) ? 8 : bit_depth)) - 1;
- PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNew(num_dims, dimensions,
PyArray_FLOAT);
-
- 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 < (png_uint_32)dimensions[2]; p++)
- *(float*)(A->data + offset + p*A->strides[2]) = (float)(ptr[p]) /
max_value;
+ if (bit_depth == 16) {
+ // Need to reinterpret the row arrays to read 2 bytes at a time
+ png_uint_16* ptr = &reinterpret_cast<png_uint_16*> (row)[x *
dimensions[2]];
+ for (png_uint_32 p = 0; p < (png_uint_32)dimensions[2]; p++) {
+
+ if (raw)
+ *reinterpret_cast<png_uint_16*>(A->data + offset +
p*A->strides[2]) = ptr[p];
+ else
+ *reinterpret_cast<float*>(A->data + offset +
p*A->strides[2]) = static_cast<float>(ptr[p]) / max_value;
+
+ }
+
} else {
+
png_byte* ptr = &(row[x * dimensions[2]]);
- for (png_uint_32 p = 0; p < (png_uint_32)dimensions[2]; p++)
- {
- *(float*)(A->data + offset + p*A->strides[2]) = (float)(ptr[p]) /
max_value;
- }
+ for (png_uint_32 p = 0; p < (png_uint_32)dimensions[2]; p++) {
+
+ if (raw)
+ *reinterpret_cast<png_byte*>(A->data + offset +
p*A->strides[2]) = ptr[p];
+ else
+ *reinterpret_cast<float*>(A->data + offset +
p*A->strides[2]) = static_cast<float>(ptr[p]) / max_value;
+
+ }
}
}
}
Index: lib/matplotlib/image.py
===================================================================
--- lib/matplotlib/image.py (revision 7230)
+++ lib/matplotlib/image.py (working copy)
@@ -739,7 +739,7 @@
rows, cols, buffer = im.as_rgba_str()
_png.write_png(buffer, cols, rows, fname)
-def imread(fname):
+def imread(fname, raw=False):
"""
Return image file in *fname* as :class:`numpy.array`.
@@ -773,7 +773,7 @@
return im
handler = handlers[ext]
- return handler(fname)
+ return handler(fname, raw)
def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
origin=None):
Index: examples/tests/pngsuite/pngsuite.py
===================================================================
--- examples/tests/pngsuite/pngsuite.py (revision 7230)
+++ examples/tests/pngsuite/pngsuite.py (working copy)
@@ -24,6 +24,6 @@
cmap = cm.gray
plt.imshow(data, extent=[i,i+1,0,1], cmap=cmap)
-plt.gca().get_frame().set_facecolor("#ddffff")
+plt.gca().patch.set_facecolor("#ddffff")
plt.gca().set_xlim(0, len(files))
plt.show()
------------------------------------------------------------------------------
Are you an open source citizen? Join us for the Open Source Bridge conference!
Portland, OR, June 17-19. Two days of sessions, one day of unconference: $250.
Need another reason to go? 24-hour hacker lounge. Register today!
http://ad.doubleclick.net/clk;215844324;13503038;v?http://opensourcebridge.org
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel