Source code for image.algo.basic

# -*- python -*-
#
#       image: image manipulation
#
#       Copyright 2006 - 2011 INRIA - CIRAD - INRA
#
#       File author(s): Jerome Chopard <jerome.chopard@sophia.inria.fr>
#                       Eric Moscardi <eric.moscardi@sophia.inria.fr>
#
#       Distributed under the Cecill-C License.
#       See accompanying file LICENSE.txt or copy at
#           http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
#       OpenAlea WebSite : http://openalea.gforge.inria.fr
#
"""
This module import functions to manipulate images
"""

__license__= "Cecill-C"
__revision__ = " $Id: __init__.py 2245 2010-02-08 17:11:34Z cokelaer $ "

import sys
from math import sqrt
from numpy import ndarray, array,zeros,ones,uint8,apply_along_axis,rollaxis,bitwise_xor, uint16, zeros, zeros_like
from colorsys import hsv_to_rgb,rgb_to_hsv,rgb_to_hls
from openalea.image.spatial_image import SpatialImage
from scipy import ndimage


__all__ = ["bounding_box","apply_mask",
           "flatten","saturate",
           "high_level","color_select",
           "border","end_margin","stroke",
           "reverse_image","color2grey","grey2color","logicalnot",
           "scale_shift_intensities"]

[docs]def bounding_box (mask) : """Compute the bounding box of a mask :Parameters: - `mask` (array of bool) - a nd array of booleans :Returns: a slice (ind_min,ind_max) for each dimension of the mask or None if the mask do not contain any True value. Where ind_min correspond to the first slice that contains a True value and ind_max correspond to the first slice that contains only False after slices that contain at least one True value :Returns Type: list of (int,int) """ loc_mask = mask bb = [slice(0,m) for m in mask.shape] for ind in range(len(mask.shape) ) : #find bounding box along ind axis imax = mask.shape[ind] #find imin bb[ind] = 0 while (bb[ind] < imax) and (not loc_mask[bb].any() ) : bb[ind] += 1 if bb[ind] == imax : return None bbimin = bb[ind] #find imax bb[ind] += 1 while (bb[ind] < imax) and mask[bb].any() : bb[ind] += 1 bbimax = bb[ind] #restore slice bb[ind] = slice(bbimin,bbimax) return bb
[docs]def apply_mask (img, mask, background_color = None) : """Apply a mask on a given image If background_color is None, the mask is applied as the alpha channel in image. Pixels whose value is True will have an alpha value of 255 and others will have an alpha value of 0 Else, pixels whose value is True will conserve their original color and others will have a color equal to background color :Parameters: - `img` (NxM(xP)x3(4) array of uint8) - `mask` (NxM(xP) array of bool) - `background_color` (int,int,int,(int) ) :Returns Type: (NxM(xP)x3(4) array of uint8) """ if background_color is None : R = img[...,0] G = img[...,1] B = img[...,2] alpha = mask * 255 return rollaxis(array([R,G,B,alpha],img.dtype),0,len(img.shape) ) else : raise NotImplementedError
[docs]def flatten (img_list, alpha = False) : """Concatenate all images into a single image Use alpha to blend images one on top of each other .. warning:: all images must have the same nD shape and an alpha channel (except maybe for the first one) If alpha is True, the resulting image will use the max of all alpha channels as an alpha channel. .. warning:: if the first image is a |SpatialImage|, the resulting image will also be a |SpatialImage| but no test is made to ensure consistency in the resolution of the layers :Parameters: - `img_list` (list of NxM(xP)x4 array of uint8) - `alpha` (bool) - the resulting image will have an alpha channel or not :Returns Type: NxM(xP)x3(4) array of uint8 """ bg = img_list[0] R = bg[...,0] G = bg[...,1] B = bg[...,2] if bg.shape[-1] == 4 : alpha_list = [bg[...,3] ] else : alpha_list = [] for lay in img_list[1:] : A = lay[...,3] alpha_list.append(A) A = A / 255. iA = 1. - A R = R * iA + lay[...,0] * A G = G * iA + lay[...,1] * A B = B * iA + lay[...,2] * A if alpha : A = array(alpha_list).max(axis = 0) ret = rollaxis(array([R,G,B,A],bg.dtype),0,len(bg.shape) ) else : ret = rollaxis(array([R,G,B],bg.dtype),0,len(bg.shape) ) if isinstance(bg,SpatialImage) : return SpatialImage(ret,bg.resolution,4,bg.info) else : return ret
[docs]def saturate (img) : """Saturate colors in the image :Parameters: - `img` (NxM(xP)x3(4) array of uint8) :Returns Type: NxM(xP)x3(4) array of uint8 """ if img.shape[2] == 3 : def func (pix) : h,s,v = rgb_to_hsv(*tuple(v / 255. for v in pix[:3]) ) return tuple(int(v * 255) for v in hsv_to_rgb(h,1.,1.) ) else : def func (pix) : h,s,v = rgb_to_hsv(*tuple(v / 255. for v in pix[:3]) ) return tuple(int(v * 255) for v in hsv_to_rgb(h,1.,1.) ) + (pix[3],) return apply_along_axis(func,-1,img)
def intensity (color) : """Returns the intensity of a color """ if isinstance(color, (tuple, list, ndarray)): return sum(color)/len(color) return color
[docs]def high_level (img, threshold) : """Create a mask where all pixel whose intensity is smaller than threshold are transparent. :Parameters: - `img` (NxM(xP)x3(4) array of uint8) - `threshold` (int between 0 and 255) :Returns Type: NxM(xP) array of bool """ def func (pix) : return intensity(pix) > threshold return apply_along_axis(func,-1,img)
[docs]def color_select (img, color, tol) : """Create a mask to conserve only colors around the given color. :ref:`http://en.wikipedia.org/wiki/Color_difference` :Parameters: - `img` (NxM(xP)x3(4) array of uint8) - `color (int,int,int) - R,G,B color - `tol` (int between 0 and 100) - distance max between a pixel and color to be conserved :Returns Type: NxM(xP) array of bool """ href,lref,sref = rgb_to_hls(*tuple(v / 255. for v in color[:3]) ) tol /= 100. def func (pix) : h,l,s = rgb_to_hls(*tuple(v / 255. for v in pix[:3]) ) d = sqrt( ( (l - lref) / 1.)**2 + \ ( (s - sref) / (1 + 0.045 * sref) )**2 + \ ( (h - href) / (1 + 0.015 * sref) )**2) return d < tol return apply_along_axis(func,-1,img)
[docs]def border(img, (x_min,y_min,z_min)=(0,0,0), (x_max,y_max,z_max)=(0,0,0) ): """ A border is a outside black space that can be added to an array object. :Parameters: - `img` ( NxMxP array) - `x_min, y_min, z_min` (int, int,int) - The begining of the border - `x_max, y_max, z_max` (int, int, int) - The end of the border :Returns Type: (N+x_min+x_max) x (M+y_min+y_max) x (P+z_min+z_max) array """ xdim, ydim, zdim = img.shape mat = zeros([xdim+x_min+x_max, ydim+y_min+y_max, zdim+z_min+z_max], img.dtype) mat[x_min:xdim+x_min, y_min:ydim+y_min, z_min:zdim+z_min] = img return mat
[docs]def end_margin(img, width, axis=None): """ A end margin is a inside black space that can be added into the end of array object. :Parameters: - `img` ( NxMxP array) - `width` (int) - size of the margin - `axis` (int optional) - axis along which the margin is added. By default, add in all directions (see also stroke). :Returns Type: img .. code-block:: python :linenos: from openalea.image import end_margin img = random.random((3,4,5)) out = end_margin(img,1,0) assert out.shape == (3,4,5) assert (out[2,:,:] == zeros((4,5))).all() assert (out[1:2,:,:] == img[1:2,:,:] ).all() out = end_margin(img,1,1) assert out.shape == (3,4,5) assert (out[:,3,:] == zeros((3,5))).all() assert (out[:,1:3,:] == img[:,1:3,:] ).all() """ xdim, ydim, zdim = img.shape mat = zeros((xdim,ydim,zdim), img.dtype) if axis is None: mat[:-width,:-width,:-width] = img[:-width,:-width,:-width] elif axis == 0: mat[:-width,:,:] = img[:-width,:,:] elif axis == 1: mat[:,:-width,:] = img[:,:-width,:] elif axis == 2: mat[:,:,:-width] = img[:,:,:-width] else: raise AttributeError('axis') return mat
[docs]def stroke(img, width, outside=False): """ A stroke is an outline that can be added to an array object. :Parameters: - `img` ( NxMxP array) - `width` (int) - size of the stroke - `outside` (bool optional) - used to set the position of the stroke. By default, the position of the stroke is inside (outside = False) :Return Type : img """ xdim,ydim,zdim = img.shape if outside: mat = zeros([xdim+2*width, ydim+2*width, zdim+2*width], img.dtype) mat[width:xdim+width, width:ydim+width, width:zdim+width] = img else: mat = zeros((xdim,ydim,zdim), img.dtype) mat[width:-width,width:-width,width:-width] = img[width:-width,width:-width,width:-width] return mat
[docs]def reverse_image (image ) : """ """ return ( image.max() - image )
[docs]def color2grey (image): """ Convert a color image into a grey-level image. """ if image.ndim != 4: print "Error : Not a color image" return -1 if not isinstance(image,SpatialImage) : image = SpatialImage(image) return (SpatialImage(image[:,:,:,0],image.resolution), SpatialImage(image[:,:,:,1],image.resolution), SpatialImage(image[:,:,:,2],image.resolution))
[docs]def grey2color (r,g,b): """ convert a grey-level image into a color image. """ if (r.shape != g.shape) or (r.shape != b.shape): print "Error : r,g,b have not the same shape" return -1 xdim,ydim,zdim = r.shape if not isinstance(r,SpatialImage) : r = SpatialImage(r) if not isinstance(g,SpatialImage) : g = SpatialImage(g) if not isinstance(b,SpatialImage) : b = SpatialImage(b) color = SpatialImage(zeros(xdim,ydim,zdim,3),r.resolution) color[:,:,:,0] = r color[:,:,:,1] = g color[:,:,:,2] = b return color
[docs]def logicalnot (img) : """ """ d = img.dtype vmax = eval(d.name)(sys.maxint) im_target = vmax * ones(img.shape, img.dtype) image = bitwise_xor(img,im_target) if isinstance(img, SpatialImage): return SpatialImage(image,img.resolution) else: return SpatialImage(image)
def component_gaussian_filter(image, sigma, in_place = False): """ Implement a gaussian filter on each component of a 3D image. """ assert len(image.shape)==4 gfilter = ndimage.filters.gaussian_filter if in_place: img = image else: img = zeros_like(image) for i in (0,1,2): img[:,:,:,i] = gfilter(image[:,:,:,i], sigma=sigma) return img
[docs]def scale_shift_intensities(image, dtype=None, maxIn=None, maxOut=255): if dtype is None: dtype = uint8 if maxIn is None: maxIn = image.max() scale = maxOut/(float(maxIn)) return SpatialImage( dtype( image*scale ), image.resolution)