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.
This commit is contained in:
Luis Pedro Coelho
2010-08-23 12:15:01 -04:00
parent 5179113ded
commit 30b4ff2274
+69 -37
View File
@@ -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)