Ejemplo n.º 1
0
    def __init__(self,**params):
        """
        Initialize this object as an EventProcessor, then also as
        a SheetCoordinateSystem with equal xdensity and ydensity.

        sheet_views is a dictionary that stores SheetViews,
        i.e. representations of the sheet for use by analysis or plotting
        code.    
        """
        EventProcessor.__init__(self,**params)

        # Initialize this object as a SheetCoordinateSystem, with
        # the same density along y as along x.
        SheetCoordinateSystem.__init__(self,self.nominal_bounds,self.nominal_density)

        n_units = round((self.lbrt[2]-self.lbrt[0])*self.xdensity,0)
        if n_units<1: raise ValueError(
           "Sheet bounds and density must be specified such that the "+ \
           "sheet has at least one unit in each direction; " \
           +self.name+ " does not.")

        # setup the activity matrix
        self.activity = zeros(self.shape,activity_type)

        # For non-plastic inputs
        self.__saved_activity = []
        self._plasticity_setting_stack = []  
        self.sheet_views = {}        
Ejemplo n.º 2
0
    def __init__(self, **params):
        """
        Initialize this object as an EventProcessor, then also as
        a SheetCoordinateSystem with equal xdensity and ydensity.

        sheet_views is a dictionary that stores SheetViews,
        i.e. representations of the sheet for use by analysis or plotting
        code.    
        """
        EventProcessor.__init__(self, **params)

        # Initialize this object as a SheetCoordinateSystem, with
        # the same density along y as along x.
        SheetCoordinateSystem.__init__(self, self.nominal_bounds,
                                       self.nominal_density)

        n_units = round((self.lbrt[2] - self.lbrt[0]) * self.xdensity, 0)
        if n_units < 1:            raise ValueError(
                "Sheet bounds and density must be specified such that the "+ \
 "sheet has at least one unit in each direction; " \
 +self.name+ " does not.")

        # setup the activity matrix
        self.activity = zeros(self.shape, activity_type)

        # For non-plastic inputs
        self.__saved_activity = []
        self._plasticity_setting_stack = []
        self.sheet_views = {}
Ejemplo n.º 3
0
    def __init__(self, bounds, shape, initial_items=None, **kwargs):
        (l, b, r, t) = bounds.lbrt()
        (dim1, dim2) = shape
        xdensity = dim1 / (r - l)
        ydensity = dim2 / (t - b)

        SheetCoordinateSystem.__init__(self, bounds, xdensity, ydensity)
        super(ProjectionGrid, self).__init__(initial_items, **kwargs)
Ejemplo n.º 4
0
    def __init__(self, bounds, shape, initial_items=None, **kwargs):
        (l, b, r, t) = bounds.lbrt()
        (dim1, dim2) = shape
        xdensity = dim1 / (r-l) if (r-l) else 1
        ydensity = dim2 / (t-b) if (t-b) else 1
        self._style = None

        SheetCoordinateSystem.__init__(self, bounds, xdensity, ydensity)
        super(CoordinateGrid, self).__init__(initial_items, **kwargs)
Ejemplo n.º 5
0
    def __init__(self, data, bounds=None, **kwargs):
        bounds = bounds if bounds else BoundingBox()
        data = np.array([[0]]) if data is None else data
        (l, b, r, t) = bounds.lbrt()
        (dim1, dim2) = data.shape[0], data.shape[1]
        xdensity = dim1/(r-l)
        ydensity = dim2/(t-b)

        SheetLayer.__init__(self, data, bounds, **kwargs)
        SheetCoordinateSystem.__init__(self, bounds, xdensity, ydensity)
Ejemplo n.º 6
0
    def _set_image(self,image):
        # Stores a SheetCoordinateSystem with an activity matrix
        # representing the image
        if not isinstance(image,numpy.ndarray):
            image = array(image,Float)

        rows,cols = image.shape
        self.scs = SheetCoordinateSystem(xdensity=1.0,ydensity=1.0,
                                         bounds=BoundingBox(points=((-cols/2.0,-rows/2.0),
                                                                    ( cols/2.0, rows/2.0))))
        self.scs.activity=image
Ejemplo n.º 7
0
    def __call__(self, **params_to_override):
        p = ParamOverrides(self, params_to_override)

        xsize, ysize = SheetCoordinateSystem(p.bounds, p.xdensity,
                                             p.ydensity).shape
        xsize, ysize = int(round(xsize)), int(round(ysize))

        xdisparity = int(round(xsize * p.xdisparity))
        ydisparity = int(round(xsize * p.ydisparity))
        dotsize = int(round(xsize * p.dotsize))

        bigxsize = 2 * xsize
        bigysize = 2 * ysize
        ndots = int(
            round(p.dotdensity * (bigxsize + 2 * dotsize) *
                  (bigysize + 2 * dotsize) / min(dotsize, xsize) /
                  min(dotsize, ysize)))
        halfdot = floor(dotsize / 2)

        # Choose random colors and locations of square dots
        random_seed = p.random_seed

        np.random.seed(random_seed * 12 + random_seed * 99)
        col = np.where(np.random.random((ndots)) >= 0.5, 1.0, -1.0)

        np.random.seed(random_seed * 122 + random_seed * 799)
        xpos = np.floor(np.random.random(
            (ndots)) * (bigxsize + 2 * dotsize)) - halfdot

        np.random.seed(random_seed * 1243 + random_seed * 9349)
        ypos = np.floor(np.random.random(
            (ndots)) * (bigysize + 2 * dotsize)) - halfdot

        # Construct arrays of points specifying the boundaries of each
        # dot, cropping them by the big image size (0,0) to (bigxsize,bigysize)
        x1 = xpos.astype(numpy.int)
        x1 = choose(less(x1, 0), (x1, 0))
        y1 = ypos.astype(numpy.int)
        y1 = choose(less(y1, 0), (y1, 0))
        x2 = (xpos + (dotsize - 1)).astype(numpy.int)
        x2 = choose(greater(x2, bigxsize), (x2, bigxsize))
        y2 = (ypos + (dotsize - 1)).astype(numpy.int)
        y2 = choose(greater(y2, bigysize), (y2, bigysize))

        # Draw each dot in the big image, on a blank background
        bigimage = zeros((bigysize, bigxsize))
        for i in range(ndots):
            bigimage[y1[i]:y2[i] + 1, x1[i]:x2[i] + 1] = col[i]

        result = p.offset + p.scale * bigimage[
            (ysize / 2) + ydisparity:(3 * ysize / 2) + ydisparity,
            (xsize / 2) + xdisparity:(3 * xsize / 2) + xdisparity]

        for of in p.output_fns:
            of(result)

        return result
Ejemplo n.º 8
0
    def _set_image(self,image):
        # Stores a SheetCoordinateSystem with an activity matrix
        # representing the image
        if not isinstance(image,numpy.ndarray):
            image = array(image,Float)

        rows,cols = image.shape
        self.scs = SheetCoordinateSystem(xdensity=1.0,ydensity=1.0,
                                         bounds=BoundingBox(points=((-cols/2.0,-rows/2.0),
                                                                    ( cols/2.0, rows/2.0))))
        self.scs.activity=image
Ejemplo n.º 9
0
    def __call__(self,**params_to_override):
        p = ParamOverrides(self,params_to_override)

        shape = SheetCoordinateSystem(p.bounds,p.xdensity,p.ydensity).shape

        result = p.scale*ones(shape, Float)+p.offset
        self._apply_mask(p,result)

        for of in p.output_fns:
            of(result)

        return result
Ejemplo n.º 10
0
    def __call__(self, **params_to_override):
        p = ParamOverrides(self, params_to_override)

        shape = SheetCoordinateSystem(p.bounds, p.xdensity, p.ydensity).shape

        result = self._distrib(shape, p)
        self._apply_mask(p, result)

        for of in p.output_fns:
            of(result)

        return result
Ejemplo n.º 11
0
    def _setup_xy(self,bounds,xdensity,ydensity,x,y,orientation):
        """
        Produce pattern coordinate matrices from the bounds and
        density (or rows and cols), and transforms them according to
        x, y, and orientation.
        """
        self.debug(lambda:"bounds=%s, xdensity=%s, ydensity=%s, x=%s, y=%s, orientation=%s"%(bounds,xdensity,ydensity,x,y,orientation))
        # Generate vectors representing coordinates at which the pattern
        # will be sampled.

        # CB: note to myself - use slice_._scs if supplied?
        x_points,y_points = SheetCoordinateSystem(bounds,xdensity,ydensity).sheetcoordinates_of_matrixidx()

        # Generate matrices of x and y sheet coordinates at which to
        # sample pattern, at the correct orientation
        self.pattern_x, self.pattern_y = self._create_and_rotate_coordinate_arrays(x_points-x,y_points-y,orientation)
Ejemplo n.º 12
0
class PatternSampler(ImageSampler):
    """
    When called, resamples - according to the size_normalization
    parameter - an image at the supplied (x,y) sheet coordinates.

    (x,y) coordinates outside the image are returned as the background
    value.
    """
    whole_pattern_output_fns = param.HookList(class_=TransferFn,default=[],doc="""
        Functions to apply to the whole image before any sampling is done.""")

    background_value_fn = param.Callable(default=None,doc="""
        Function to compute an appropriate background value. Must accept
        an array and return a scalar.""")

    size_normalization = param.ObjectSelector(default='original',
        objects=['original','stretch_to_fit','fit_shortest','fit_longest'],
        doc="""
        Determines how the pattern is scaled initially, relative to the
        default retinal dimension of 1.0 in sheet coordinates:

        'stretch_to_fit': scale both dimensions of the pattern so they
        would fill a Sheet with bounds=BoundingBox(radius=0.5) (disregards
        the original's aspect ratio).

        'fit_shortest': scale the pattern so that its shortest dimension
        is made to fill the corresponding dimension on a Sheet with
        bounds=BoundingBox(radius=0.5) (maintains the original's aspect
        ratio, filling the entire bounding box).

        'fit_longest': scale the pattern so that its longest dimension is
        made to fill the corresponding dimension on a Sheet with
        bounds=BoundingBox(radius=0.5) (maintains the original's
        aspect ratio, fitting the image into the bounding box but not
        necessarily filling it).

        'original': no scaling is applied; each pixel of the pattern
        corresponds to one matrix unit of the Sheet on which the
        pattern being displayed.""")

    def _get_image(self):
        return self.scs.activity

    def _set_image(self,image):
        # Stores a SheetCoordinateSystem with an activity matrix
        # representing the image
        if not isinstance(image,numpy.ndarray):
            image = array(image,Float)

        rows,cols = image.shape
        self.scs = SheetCoordinateSystem(xdensity=1.0,ydensity=1.0,
                                         bounds=BoundingBox(points=((-cols/2.0,-rows/2.0),
                                                                    ( cols/2.0, rows/2.0))))
        self.scs.activity=image

    def _del_image(self):
        self.scs = None


    def __call__(self, image, x, y, sheet_xdensity, sheet_ydensity, width=1.0, height=1.0):
        """
        Return pixels from the supplied image at the given Sheet (x,y)
        coordinates.

        The image is assumed to be a NumPy array or other object that
        exports the NumPy buffer interface (i.e. can be converted to a
        NumPy array by passing it to numpy.array(), e.g. Image.Image).
        The whole_pattern_output_fns are applied to the image before
        any sampling is done.

        To calculate the sample, the image is scaled according to the
        size_normalization parameter, and any supplied width and
        height. sheet_xdensity and sheet_ydensity are the xdensity and
        ydensity of the sheet on which the pattern is to be drawn.
        """
        # CEB: could allow image=None in args and have 'if image is
        # not None: self.image=image' here to avoid re-initializing the
        # image.
        self.image=image

        for wpof in self.whole_pattern_output_fns:
            wpof(self.image)
        if not self.background_value_fn:
            self.background_value = 0.0
        else:
            self.background_value = self.background_value_fn(self.image)

        pattern_rows,pattern_cols = self.image.shape

        if width==0 or height==0 or pattern_cols==0 or pattern_rows==0:
            return ones(x.shape, Float)*self.background_value

        # scale the supplied coordinates to match the pattern being at density=1
        x=x*sheet_xdensity # deliberately don't operate in place (so as not to change supplied x & y)
        y=y*sheet_ydensity

        # scale according to initial pattern size_normalization selected (size_normalization)
        self.__apply_size_normalization(x,y,sheet_xdensity,sheet_ydensity,self.size_normalization)

        # scale according to user-specified width and height
        x/=width
        y/=height

        # now sample pattern at the (r,c) corresponding to the supplied (x,y)
        r,c = self.scs.sheet2matrixidx(x,y)
        # (where(cond,x,y) evaluates x whether cond is True or False)
        r.clip(0,pattern_rows-1,out=r)
        c.clip(0,pattern_cols-1,out=c)
        left,bottom,right,top = self.scs.bounds.lbrt()
        return numpy.where((x>=left) & (x<right) & (y>bottom) & (y<=top),
                           self.image[r,c],
                           self.background_value)


    def __apply_size_normalization(self,x,y,sheet_xdensity,sheet_ydensity,size_normalization):
        pattern_rows,pattern_cols = self.image.shape

        # Instead of an if-test, could have a class of this type of
        # function (c.f. OutputFunctions, etc)...
        if size_normalization=='original':
            return

        elif size_normalization=='stretch_to_fit':
            x_sf,y_sf = pattern_cols/sheet_xdensity, pattern_rows/sheet_ydensity
            x*=x_sf; y*=y_sf

        elif size_normalization=='fit_shortest':
            if pattern_rows<pattern_cols:
                sf = pattern_rows/sheet_ydensity
            else:
                sf = pattern_cols/sheet_xdensity
            x*=sf;y*=sf

        elif size_normalization=='fit_longest':
            if pattern_rows<pattern_cols:
                sf = pattern_cols/sheet_xdensity
            else:
                sf = pattern_rows/sheet_ydensity
            x*=sf;y*=sf
Ejemplo n.º 13
0
    def _distrib(self, shape, p):
        assert ( p.grid_density <= p.xdensity ), 'grid density bigger than pixel density x'
        assert ( p.grid_density <= p.ydensity),  'grid density bigger than pixel density y'
        assert ( shape[1] > 0 ), 'Pixel matrix can not be zero'
        assert ( shape[0] > 0 ),  'Pixel matrix can not be zero'
        
        Nx = shape[1]  
        Ny = shape[0] # Size of the pixel matrix 
        
      
        SC = SheetCoordinateSystem(p.bounds, p.xdensity, p.ydensity)
        unitary_distance_x = SC._SheetCoordinateSystem__xstep
        unitary_distance_y = SC._SheetCoordinateSystem__ystep

        sheet_x_size = unitary_distance_x * Nx 
        sheet_y_size = unitary_distance_y * Ny

        # Sizes of the structure matrix 
        nx = int(round(sheet_x_size * p.grid_density))  # Number of points in the x's
        ny = int(round(sheet_y_size * p.grid_density))  # Number of points in the y's
        
        assert ( nx > 0 ), 'Grid density or bound box in the x dimension too smal'
        assert ( ny > 0 ), 'Grid density or bound bonx in the y dimension too smal'
        
        ps_x = int(round(Nx / nx)) #Closest integer 
        ps_y = int(round(Ny / ny))
              
        # This is the actual matrix of the pixels 
        A = np.ones(shape) * 0.5   
           
        if p.grid == False:  #The centers of the spots are randomly distributed in space 
                        
            x = p.random_generator.randint(0, Nx - ps_x + 1)
            y = p.random_generator.randint(0, Ny - ps_y + 1)
            z = p.random_generator.randint(0,2) 
                        
            # Noise matrix is mapped to the pixel matrix   
            A[x: (x + ps_y), y: (y + ps_x)] =  z   
           
            return A * p.scale + p.offset
        
        else: #In case you want the grid
            
            if  ( Nx % nx == 0) and (Ny % ny == 0): #When the noise grid falls neatly into the the pixel grid 
                x = p.random_generator.randint(0, nx)
                y = p.random_generator.randint(0, ny)
                z = p.random_generator.randint(0,2) 
                
               # Noise matrix is mapped to the pixel matrix (faster method)       
                A[x*ps_y: (x*ps_y + ps_y), y*ps_x: (y*ps_x + ps_x)] = z  
                
                return A * p.scale + p.offset
                    
            else: # If noise grid does not fit neatly in the pixel grid (slow method)
                
                x_points,y_points = SC.sheetcoordinates_of_matrixidx()
                
                # Obtain length of the side and length of the
                # division line between the grid 
                
                division_x = 1.0 / p.grid_density
                division_y = 1.0 / p.grid_density

                size_of_block_x = Nx * 1.0 / nx
                size_of_block_y = Ny * 1.0 / ny
            
                # Construct the noise matrix 
                Z = np.ones((nx,ny)) * 0.5
                x = p.random_generator.randint(0, nx)
                y = p.random_generator.randint(0, ny)
                z = p.random_generator.randint(0,2) 
                Z[x,y] = z
                
                            # Noise matrix is mapped to the pixel matrix   
                for i in range(Nx):
                    for j in range(Ny):
                        # Map along the x coordinates 
                        x_entry = int( i / size_of_block_x)
                        y_entry = int( j / size_of_block_y)
                        A[j][i] = Z[x_entry][y_entry]

                return A * p.scale + p.offset
Ejemplo n.º 14
0
    def _distrib(self, shape, p):               
        assert ( p.grid_density <= p.xdensity ), 'grid density bigger than pixel density x'
        assert ( p.grid_density <= p.ydensity),  'grid density bigger than pixel density y'
        assert ( shape[1] > 0 ), 'Pixel matrix can not be zero'
        assert ( shape[0] > 0 ),  'Pixel matrix can not be zero'
        
        Nx = shape[1]  
        Ny = shape[0] # Size of the pixel matrix 
      
        SC = SheetCoordinateSystem(p.bounds, p.xdensity, p.ydensity)
        unitary_distance_x = SC._SheetCoordinateSystem__xstep
        unitary_distance_y = SC._SheetCoordinateSystem__ystep

        
        sheet_x_size = unitary_distance_x * Nx 
        sheet_y_size = unitary_distance_y * Ny

        # Sizes of the structure matrix 
        nx = int(round(sheet_x_size * p.grid_density))  # Number of points in the x's
        ny = int(round(sheet_y_size * p.grid_density))  # Number of points in the y's
        
        assert ( nx > 0 ), 'Grid density or bound box in the x dimension too smal'
        assert ( ny > 0 ), 'Grid density or bound bonx in the y dimension too smal'
        
        # If the noise grid is proportional to the pixel grid 
        # and fits neatly into it then this method is faster (~100 times faster)
        if ( Nx % nx == 0) and (Ny % ny == 0):
              
            if (Nx == nx) and (Ny == ny):  #This is faster to call the whole procedure 
                result = 0.5 * (p.random_generator.randint(-1, 2, shape) + 1)
                return  result * p.scale + p.offset
            
            else: 
                # This is the actual matrix of the pixels 
                A = np.zeros(shape)    
                # Noise matrix that contains the structure of 0, 0.5 and 1's  
                Z = 0.5 * (p.random_generator.randint(-1, 2, (nx, ny)) + 1 )               

                ps_x = int(round(Nx * 1.0/ nx))  #Closest integer 
                ps_y = int(round(Ny * 1.0/ ny))
        
                # Noise matrix is mapped to the pixel matrix   
                for i in range(nx):
                    for j in range(ny): 
                        A[i * ps_y: (i + 1) * ps_y, j * ps_x: (j + 1) * ps_x] = Z[i,j]

                return A * p.scale + p.offset
            
        # General method in case the noise grid does not 
        # fall neatly in the pixels grid      
        else:
            
            # Obtain length of the side and length of the
            # division line between the grid 
            x_points,y_points = SC.sheetcoordinates_of_matrixidx()

            division_x = 1.0 / p.grid_density
            division_y = 1.0 / p.grid_density
            
            # This is the actual matrix of the pixels 
            A = np.zeros(shape)
            # Noise matrix that contains the structure of 0, 0.5 and 1's  
            Z = 0.5 * (p.random_generator.randint(-1, 2, (nx, ny)) + 1 )

            size_of_block_x = Nx * 1.0 / nx
            size_of_block_y = Ny * 1.0 / ny
            
            # Noise matrix is mapped to the pixel matrix   
            for i in range(Nx):
                for j in range(Ny):
                    # Map along the x coordinates 
                    x_entry = int( i / size_of_block_x)
                    y_entry = int( j / size_of_block_y)
                    A[j][i] = Z[x_entry][y_entry]
                
            return A * p.scale + p.offset
Ejemplo n.º 15
0
class PatternSampler(ImageSampler):
    """
    When called, resamples - according to the size_normalization
    parameter - an image at the supplied (x,y) sheet coordinates.
    
    (x,y) coordinates outside the image are returned as the background
    value.
    """
    whole_pattern_output_fns = param.HookList(class_=TransferFn,default=[],doc="""
        Functions to apply to the whole image before any sampling is done.""")

    background_value_fn = param.Callable(default=None,doc="""
        Function to compute an appropriate background value. Must accept
        an array and return a scalar.""")

    size_normalization = param.ObjectSelector(default='original',
        objects=['original','stretch_to_fit','fit_shortest','fit_longest'],
        doc="""
        Determines how the pattern is scaled initially, relative to the
        default retinal dimension of 1.0 in sheet coordinates:
            
        'stretch_to_fit': scale both dimensions of the pattern so they
        would fill a Sheet with bounds=BoundingBox(radius=0.5) (disregards
        the original's aspect ratio).
    
        'fit_shortest': scale the pattern so that its shortest dimension
        is made to fill the corresponding dimension on a Sheet with
        bounds=BoundingBox(radius=0.5) (maintains the original's aspect
        ratio, filling the entire bounding box).
    
        'fit_longest': scale the pattern so that its longest dimension is
        made to fill the corresponding dimension on a Sheet with
        bounds=BoundingBox(radius=0.5) (maintains the original's
        aspect ratio, fitting the image into the bounding box but not
        necessarily filling it).
    
        'original': no scaling is applied; each pixel of the pattern 
        corresponds to one matrix unit of the Sheet on which the
        pattern being displayed.""")

    def _get_image(self):
        return self.scs.activity

    def _set_image(self,image):
        # Stores a SheetCoordinateSystem with an activity matrix
        # representing the image
        if not isinstance(image,numpy.ndarray):
            image = array(image,Float)

        rows,cols = image.shape
        self.scs = SheetCoordinateSystem(xdensity=1.0,ydensity=1.0,
                                         bounds=BoundingBox(points=((-cols/2.0,-rows/2.0),
                                                                    ( cols/2.0, rows/2.0))))
        self.scs.activity=image
        
    def _del_image(self):
        self.scs = None
        

    def __call__(self, image, x, y, sheet_xdensity, sheet_ydensity, width=1.0, height=1.0):
        """
        Return pixels from the supplied image at the given Sheet (x,y)
        coordinates.

        The image is assumed to be a NumPy array or other object that
        exports the NumPy buffer interface (i.e. can be converted to a
        NumPy array by passing it to numpy.array(), e.g. Image.Image).
        The whole_pattern_output_fns are applied to the image before
        any sampling is done.

        To calculate the sample, the image is scaled according to the
        size_normalization parameter, and any supplied width and
        height. sheet_xdensity and sheet_ydensity are the xdensity and
        ydensity of the sheet on which the pattern is to be drawn.
        """
        # CEB: could allow image=None in args and have 'if image is
        # not None: self.image=image' here to avoid re-initializing the
        # image.
        self.image=image

        for wpof in self.whole_pattern_output_fns:
            wpof(self.image)
        if not self.background_value_fn:
            self.background_value = 0.0
        else:
            self.background_value = self.background_value_fn(self.image)

        pattern_rows,pattern_cols = self.image.shape

        if width==0 or height==0 or pattern_cols==0 or pattern_rows==0:
            return ones(x.shape, Float)*self.background_value

        # scale the supplied coordinates to match the pattern being at density=1
        x=x*sheet_xdensity # deliberately don't operate in place (so as not to change supplied x & y)
        y=y*sheet_ydensity
      
        # scale according to initial pattern size_normalization selected (size_normalization)
        self.__apply_size_normalization(x,y,sheet_xdensity,sheet_ydensity,self.size_normalization)

        # scale according to user-specified width and height
        x/=width
        y/=height

        # now sample pattern at the (r,c) corresponding to the supplied (x,y)
        r,c = self.scs.sheet2matrixidx(x,y)
        # (where(cond,x,y) evaluates x whether cond is True or False)
        r.clip(0,pattern_rows-1,out=r)
        c.clip(0,pattern_cols-1,out=c)
        left,bottom,right,top = self.scs.bounds.lbrt()
        return numpy.where((x>=left) & (x<right) & (y>bottom) & (y<=top),  
                           self.image[r,c],
                           self.background_value)
    

    def __apply_size_normalization(self,x,y,sheet_xdensity,sheet_ydensity,size_normalization):
        pattern_rows,pattern_cols = self.image.shape

        # Instead of an if-test, could have a class of this type of
        # function (c.f. OutputFunctions, etc)...
        if size_normalization=='original':
            return
        
        elif size_normalization=='stretch_to_fit':
            x_sf,y_sf = pattern_cols/sheet_xdensity, pattern_rows/sheet_ydensity
            x*=x_sf; y*=y_sf

        elif size_normalization=='fit_shortest':
            if pattern_rows<pattern_cols:
                sf = pattern_rows/sheet_ydensity
            else:
                sf = pattern_cols/sheet_xdensity
            x*=sf;y*=sf
            
        elif size_normalization=='fit_longest':
            if pattern_rows<pattern_cols:
                sf = pattern_cols/sheet_xdensity
            else:
                sf = pattern_rows/sheet_ydensity
            x*=sf;y*=sf