From 30b4ff227457ee1922da64123d4c9bf09abcf91f Mon Sep 17 00:00:00 2001 From: Luis Pedro Coelho Date: Mon, 23 Aug 2010 12:15:01 -0400 Subject: [PATCH] Fix freeimage_plugin.imsave Previously, the plugin perfomed a copy of the bitmap's pixel memory instead of mapping into a ndarray. This fixes it. Also, it saved images rotated by 90 degrees as it didn't convert to freeimage's internal format. Copying less also makes imread visibly faster. --- scikits/image/io/_plugins/freeimage_plugin.py | 106 ++++++++++++------ 1 file changed, 69 insertions(+), 37 deletions(-) diff --git a/scikits/image/io/_plugins/freeimage_plugin.py b/scikits/image/io/_plugins/freeimage_plugin.py index 37824727..a8c99e50 100644 --- a/scikits/image/io/_plugins/freeimage_plugin.py +++ b/scikits/image/io/_plugins/freeimage_plugin.py @@ -286,7 +286,6 @@ def _wrap_bitmap_bits_in_array(bitmap, shape, dtype): """ pitch = _FI.FreeImage_GetPitch(bitmap) height = shape[-1] - byte_size = height * pitch itemsize = dtype.itemsize if len(shape) == 3: @@ -301,31 +300,33 @@ def _wrap_bitmap_bits_in_array(bitmap, shape, dtype): 'strides': strides, 'typestr': dtype.str, 'shape': tuple(shape), + 'version' : 3, } # Still segfaulting on 64-bit machine because of illegal memory access - return numpy.array(DummyArray()) + return numpy.array(DummyArray(), copy=False) def _array_from_bitmap(bitmap): - """Convert a FreeImage bitmap pointer to a numpy array + """Convert a FreeImage bitmap pointer to a numpy array - """ - dtype, shape = FI_TYPES.get_type_and_shape(bitmap) - array = _wrap_bitmap_bits_in_array(bitmap, shape, dtype) - # swizzle the color components and flip the scanlines to go from - # FreeImage's BGR[A] and upside-down internal memory format to something - # more normal - if len(shape) == 3 and _FI.FreeImage_IsLittleEndian() and \ - dtype.type == numpy.uint8: - b = array[0].copy() - array[0] = array[2] - array[2] = b - - array = array[..., ::-1] - array = array.T - - return array.copy() + """ + dtype, shape = FI_TYPES.get_type_and_shape(bitmap) + array = _wrap_bitmap_bits_in_array(bitmap, shape, dtype) + # swizzle the color components and flip the scanlines to go from + # FreeImage's BGR[A] and upside-down internal memory format to something + # more normal + def n(arr): + return arr[..., ::-1].T + if len(shape) == 3 and _FI.FreeImage_IsLittleEndian() and \ + dtype.type == np.uint8: + b = array[0] + g = array[1] + r = array[2] + return np.dstack( (n(r), n(g), n(b)) ) + # We need to copy because array does *not* own its memory + # after bitmap is freed. + return n(array).copy() def string_tag(bitmap, key, model=METADATA_MODELS.FIMD_EXIF_MAIN): """Retrieve the value of a metadata tag with the given string key as a @@ -338,7 +339,7 @@ def string_tag(bitmap, key, model=METADATA_MODELS.FIMD_EXIF_MAIN): return char_ptr.from_address(_FI.FreeImage_GetTagValue(tag)).raw() def write(array, filename, flags=0): - """Write a (width, height) or (nchannels, width, height) array to + """Write a (width, height) or (width, height, nchannels) array to a greyscale, RGB, or RGBA image, with file type deduced from the filename. @@ -394,34 +395,40 @@ def _array_to_bitmap(array): """ shape = array.shape dtype = array.dtype + r,c = shape[:2] if len(shape) == 2: n_channels = 1 + w_shape = (c,r) + elif len(shape) == 3: + n_channels = shape[2] + w_shape = (n_channels,c,r) else: n_channels = shape[0] try: fi_type = FI_TYPES.fi_types[(dtype.type, n_channels)] except KeyError: raise ValueError('Cannot write arrays of given type and shape.') - width, height = shape[-2:] itemsize = array.dtype.itemsize bpp = 8 * itemsize * n_channels - bitmap = _FI.FreeImage_AllocateT(fi_type, width, height, bpp, 0, 0, 0) + bitmap = _FI.FreeImage_AllocateT(fi_type, c, r, bpp, 0, 0, 0) if not bitmap: raise RuntimeError('Could not allocate image for storage') try: - wrapped_array = _wrap_bitmap_bits_in_array(bitmap, shape, dtype) + def n(arr): # normalise to freeimage's in-memory format + return arr.T[:,::-1] + wrapped_array = _wrap_bitmap_bits_in_array(bitmap, w_shape, dtype) # swizzle the color components and flip the scanlines to go to # FreeImage's BGR[A] and upside-down internal memory format if len(shape) == 3 and _FI.FreeImage_IsLittleEndian() and \ dtype.type == numpy.uint8: - wrapped_array[0] = array[2,:,::-1] - wrapped_array[1] = array[1,:,::-1] - wrapped_array[2] = array[0,:,::-1] - if shape[0] == 4: - wrapped_array[3] = array[3,:,::-1] + wrapped_array[0] = n(array[:,:,2]) + wrapped_array[1] = n(array[:,:,1]) + wrapped_array[2] = n(array[:,:,0]) + if shape[2] == 4: + wrapped_array[3] = n(array[:,:,3]) else: - wrapped_array[:] = array[...,::-1] + wrapped_array[:] = n(array) return bitmap, fi_type except: @@ -429,13 +436,38 @@ def _array_to_bitmap(array): raise -def imread(filename, as_grey=False, dtype=None): - """Warning: currenly as_grey and dtype is simply ignored. - +def imread(filename, as_grey=False): """ - return read(filename) + img = imread(filename, as_grey=False) -def imsave(filename, arr): - if arr.ndim == 3: - arr = numpy.rollaxis(arr, 2, 0) - write(arr, filename) + Reads an image from file `filename` + + Parameters + ---------- + filename : file name + as_grey : Whether to convert to grey scale image (default: no) + Returns + ------- + img : ndarray + """ + img = read(filename) + if as_grey and len(img) == 3: + # these are the values that wikipedia says are typical + transform = np.array([ 0.30, 0.59, 0.11]) + return np.dot(img, transform) + return img + +def imsave(filename, img): + ''' + imsave(filename, img) + + Save image to disk + + Image type is inferred from filename + + Parameters + ---------- + filename : file name + img : image to be saved as nd array + ''' + write(img, filename)