Revision: 7909
          http://matplotlib.svn.sourceforge.net/matplotlib/?rev=7909&view=rev
Author:   mdboom
Date:     2009-10-26 14:19:04 +0000 (Mon, 26 Oct 2009)

Log Message:
-----------
Support Python file-like objects in readpng

Modified Paths:
--------------
    trunk/matplotlib/lib/matplotlib/image.py
    trunk/matplotlib/lib/matplotlib/tests/test_image.py
    trunk/matplotlib/src/_png.cpp

Modified: trunk/matplotlib/lib/matplotlib/image.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/image.py    2009-10-25 23:29:31 UTC (rev 
7908)
+++ trunk/matplotlib/lib/matplotlib/image.py    2009-10-26 14:19:04 UTC (rev 
7909)
@@ -938,10 +938,15 @@
 
 
 
-def imread(fname):
+def imread(fname, format=None):
     """
-    Return image file in *fname* as :class:`numpy.array`.
+    Return image file in *fname* as :class:`numpy.array`.  *fname* may
+    be a string path or a Python file-like object.
 
+    If *format* is provided, will try to read file of that type,
+    otherwise the format is deduced from the filename.  If nothing can
+    be deduced, PNG is tried.
+
     Return value is a :class:`numpy.array`.  For grayscale images, the
     return array is MxN.  For RGB images, the return value is MxNx3.
     For RGBA images the return value is MxNx4.
@@ -959,12 +964,16 @@
         image = Image.open( fname )
         return pil_to_array(image)
 
+    handlers = {'png' :_png.read_png, }
+    if format is None:
+        if cbook.is_string_like(fname):
+            basename, ext = os.path.splitext(fname)
+            ext = ext.lower()[1:]
+        else:
+            ext = 'png'
+    else:
+        ext = format
 
-    handlers = {'png' :_png.read_png,
-                }
-    basename, ext = os.path.splitext(fname)
-    ext = ext.lower()[1:]
-
     if ext not in handlers.keys():
         im = pilread()
         if im is None:
@@ -972,6 +981,13 @@
         return im
 
     handler = handlers[ext]
+
+    # To handle Unicode filenames, we pass a file object to the PNG
+    # reader extension, since Python handles them quite well, but it's
+    # tricky in C.
+    if cbook.is_string_like(fname):
+        fname = open(fname, 'rb')
+
     return handler(fname)
 
 

Modified: trunk/matplotlib/lib/matplotlib/tests/test_image.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/tests/test_image.py 2009-10-25 23:29:31 UTC 
(rev 7908)
+++ trunk/matplotlib/lib/matplotlib/tests/test_image.py 2009-10-26 14:19:04 UTC 
(rev 7909)
@@ -4,6 +4,9 @@
 import matplotlib.pyplot as plt
 from nose.tools import assert_raises
 
+import cStringIO
+import os
+
 @image_comparison(baseline_images=['image_interps'])
 def test_image_interps():
     'make the basic nearest, bilinear and bicubic interps'
@@ -26,6 +29,24 @@
 
     fig.savefig('image_interps')
 
+def test_image_python_io():
+    fig = plt.figure()
+    ax = fig.add_subplot(111)
+    ax.plot([1,2,3])
+    buffer = cStringIO.StringIO()
+    fig.savefig(buffer)
+    buffer.seek(0)
+    plt.imread(buffer)
+
+def test_image_unicode_io():
+    fig = plt.figure()
+    ax = fig.add_subplot(111)
+    ax.plot([1,2,3])
+    fname = u"\u0a3a\u0a3a.png"
+    fig.savefig(fname)
+    plt.imread(fname)
+    os.remove(fname)
+
 if __name__=='__main__':
     import nose
     nose.runmodule(argv=['-s','--with-doctest'], exit=False)

Modified: trunk/matplotlib/src/_png.cpp
===================================================================
--- trunk/matplotlib/src/_png.cpp       2009-10-25 23:29:31 UTC (rev 7908)
+++ trunk/matplotlib/src/_png.cpp       2009-10-26 14:19:04 UTC (rev 7909)
@@ -130,12 +130,12 @@
       png_init_io(png_ptr, fp);
     } else {
       png_set_write_fn(png_ptr, (void*)py_fileobj.ptr(),
-                      &write_png_data, &flush_png_data);
+                       &write_png_data, &flush_png_data);
     }
     png_set_IHDR(png_ptr, info_ptr,
-                width, height, 8,
-                PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
-                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+                 width, height, 8,
+                 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+                 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
 
     // Save the dpi of the image in the file
     if (args.size() == 5) {
@@ -157,14 +157,14 @@
     png_write_image(png_ptr, row_pointers);
     png_write_end(png_ptr, info_ptr);
   } catch (...) {
-      if (fp && close_file) fclose(fp);
-      delete [] row_pointers;
-      /* Changed calls to png_destroy_write_struct to follow
-         http://www.libpng.org/pub/png/libpng-manual.txt.
-         This ensures the info_ptr memory is released.
-      */
-      if (png_ptr && info_ptr) png_destroy_write_struct(&png_ptr, &info_ptr);
-      throw;
+    if (fp && close_file) fclose(fp);
+    delete [] row_pointers;
+    /* Changed calls to png_destroy_write_struct to follow
+       http://www.libpng.org/pub/png/libpng-manual.txt.
+       This ensures the info_ptr memory is released.
+    */
+    if (png_ptr && info_ptr) png_destroy_write_struct(&png_ptr, &info_ptr);
+    throw;
   }
 
   png_destroy_write_struct(&png_ptr, &info_ptr);
@@ -174,39 +174,85 @@
   return Py::Object();
 }
 
+static void _read_png_data(PyObject* py_file_obj, png_bytep data, png_size_t 
length) {
+  PyObject* read_method = PyObject_GetAttrString(py_file_obj, "read");
+  PyObject* result = NULL;
+  char *buffer;
+  Py_ssize_t bufflen;
+  if (read_method)
+    result = PyObject_CallFunction(read_method, (char *)"i", length);
+  if (PyString_AsStringAndSize(result, &buffer, &bufflen) == 0) {
+    if (bufflen == (Py_ssize_t)length) {
+      memcpy(data, buffer, length);
+    }
+  }
+  Py_XDECREF(read_method);
+  Py_XDECREF(result);
+}
 
+static void read_png_data(png_structp png_ptr, png_bytep data, png_size_t 
length) {
+  PyObject* py_file_obj = (PyObject*)png_get_io_ptr(png_ptr);
+  _read_png_data(py_file_obj, data, length);
+}
+
 Py::Object
 _png_module::read_png(const Py::Tuple& args) {
 
   args.verify_length(1);
-  std::string fname = Py::String(args[0]);
+  png_byte header[8];   // 8 is the maximum size that can be checked
+  FILE* fp = NULL;
+  bool close_file = false;
 
-  png_byte header[8];  // 8 is the maximum size that can be checked
+  Py::Object py_fileobj = Py::Object(args[0]);
+  if (py_fileobj.isString()) {
+    std::string fileName = Py::String(py_fileobj);
+    const char *file_name = fileName.c_str();
+    if ((fp = fopen(file_name, "rb")) == NULL)
+      throw Py::RuntimeError( Printf("Could not open file %s for reading", 
file_name).str() );
+    close_file = true;
+  } else if (PyFile_CheckExact(py_fileobj.ptr())) {
+    fp = PyFile_AsFile(py_fileobj.ptr());
+  } else {
+    PyObject* read_method = PyObject_GetAttrString(py_fileobj.ptr(), "read");
+    if (!(read_method && PyCallable_Check(read_method))) {
+      Py_XDECREF(read_method);
+      throw Py::TypeError("Object does not appear to be a 8-bit string path or 
a Python file-like object");
+    }
+    Py_XDECREF(read_method);
+  }
 
-  FILE *fp = fopen(fname.c_str(), "rb");
-  if (!fp)
-    throw Py::RuntimeError(Printf("_image_module::readpng could not open PNG 
file %s for reading", fname.c_str()).str());
-
-  if (fread(header, 1, 8, fp) != 8)
-    throw Py::RuntimeError("_image_module::readpng: error reading PNG header");
-  if (png_sig_cmp(header, 0, 8))
+  if (fp) {
+    if (fread(header, 1, 8, fp) != 8) {
+      throw Py::RuntimeError("_image_module::readpng: error reading PNG 
header");
+    }
+  } else {
+    _read_png_data(py_fileobj.ptr(), header, 8);
+  }
+  if (png_sig_cmp(header, 0, 8)) {
     throw Py::RuntimeError("_image_module::readpng: file not recognized as a 
PNG file");
+  }
 
-
   /* initialize stuff */
   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 
NULL, NULL);
 
-  if (!png_ptr)
+  if (!png_ptr) {
     throw Py::RuntimeError("_image_module::readpng:  png_create_read_struct 
failed");
+  }
 
   png_infop info_ptr = png_create_info_struct(png_ptr);
-  if (!info_ptr)
+  if (!info_ptr) {
     throw Py::RuntimeError("_image_module::readpng:  png_create_info_struct 
failed");
+  }
 
-  if (setjmp(png_jmpbuf(png_ptr)))
+  if (setjmp(png_jmpbuf(png_ptr))) {
     throw Py::RuntimeError("_image_module::readpng:  error during init_io");
+  }
 
-  png_init_io(png_ptr, fp);
+  if (fp) {
+    png_init_io(png_ptr, fp);
+  } else {
+    png_set_read_fn(png_ptr, (void*)py_fileobj.ptr(), &read_png_data);
+  }
   png_set_sig_bytes(png_ptr, 8);
   png_read_info(png_ptr, info_ptr);
 
@@ -272,26 +318,28 @@
 
   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 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;
-         } 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;
-           }
-         }
+              *(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 < (png_uint_32)dimensions[2]; p++)
+                {
+              *(float*)(A->data + offset + p*A->strides[2]) = (float)(ptr[p]) 
/ max_value;
+            }
+          }
     }
   }
 
   //free the png memory
   png_read_end(png_ptr, info_ptr);
   png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
-  fclose(fp);
+  if (close_file) {
+    fclose(fp);
+  }
   for (row = 0; row < height; row++)
     delete [] row_pointers[row];
   delete [] row_pointers;


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay 
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins

Reply via email to