Advertisement
homer512

python convolve

Dec 5th, 2015
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 4.14 KB | None | 0 0
  1. #!/usr/bin/python2
  2.  
  3.  
  4. """Demonstration of image convolution in Python
  5.  
  6. Yes, scipy.ndimage does a better job at it.
  7. This is just a demonstration of proper optimization of numpy routines
  8. """
  9.  
  10.  
  11. import numpy
  12.  
  13.  
  14. def apply_filter_mask(image, mask):
  15.     """Applies some kind of convolution filter
  16.  
  17.    Arguments:
  18.    image -- Array-like object (preferably numpy.array). First and second
  19.             dimension: Height and width. Third dimension: Color channels
  20.    mask -- Array-like object of the filter coefficients. Two-dimensional.
  21.            For example ((1, 2, 1), (2, 4, 2), (1, 2, 1)) for a Gauss filter
  22.  
  23.    Return value:
  24.    A new image object of the same shape and type as image. Borders are
  25.    initialized to 0
  26.    """
  27.     # First, ensure we don't get any surprises with the argument types
  28.     image, mask = (numpy.asarray(arr) for arr in (image, mask))
  29.     # Alright. Let's assume we have the mask mentioned in the description.
  30.     # Over the course of this function we will need every pixel in image
  31.     # multiplied by 1, 2, and 4. So let's do this only once in the beginning.
  32.     #
  33.     # Also, because we are multiplying a huge array with a scalar, it is worth
  34.     # checking whether we actually need to do a multiplication. For 0 we skip
  35.     # processing altogether.
  36.     # This can be extended to deal with cases where a bit shift is sufficient
  37.     # but you get the idea.
  38.     parts = {factor: image if factor == 1 else image * factor for factor
  39.              in set(mask.flat) if factor}
  40.     # We don't need to care about the number of color channels.
  41.     # Numpy's broadcast and vector operation rules will do it automatically
  42.     height, width = image.shape[0:2]
  43.     # For the iteration through the mask, we need the factor and the relative
  44.     # offsets compared to the center pixel in the mask
  45.     maskheight, maskwidth = mask.shape
  46.     verticalborder, horizontalborder = (length / 2 for length
  47.                                         in (maskheight, maskwidth))
  48.     # Now build the output array. As an optimization, we could copy the first
  49.     # parts entry, saving the zero initialization and an addition but let's
  50.     # keep it simple for now
  51.     result = numpy.zeros_like(image)
  52.     # Here we just leave the borders at 0 but in general, when we need special
  53.     # treatment for border pixels, it is faster to do it separately outside
  54.     # the loop for the center frame
  55.     center_frame = result[verticalborder:-verticalborder,
  56.                           horizontalborder:-horizontalborder]
  57.     # Let's skip being fancy and use a simple nested for loop for the rest.
  58.     # It's only a few iterations, anyway
  59.     for verticaloff, maskline in enumerate(mask):
  60.         vertical_end = height - maskheight + verticaloff + 1
  61.         for horizontaloff, factor in enumerate(maskline):
  62.             if not factor:
  63.                 continue
  64.             part = parts[factor]
  65.             horizontal_end = width - maskwidth + horizontaloff + 1
  66.             center_frame += part[verticaloff:vertical_end,
  67.                                  horizontaloff:horizontal_end]
  68.     # Alright. Nearly done. Now we just need to normalize the result. This is
  69.     # an integer division and therefore extremely slow, especially on ARM CPUs.
  70.     # So, let's go the extra mile and replace it with a bit shift if we can
  71.     normfactor = mask.sum()
  72.     if normfactor == 1: # maybe we are dealing with a normalized mask of floats?
  73.         return result
  74.     # In Python3 we could use math.log2. Oh well, let's do it the ugly way
  75.     try:
  76.         binnorm = bin(normfactor)
  77.     except TypeError: # again, probably a mask of floats
  78.         center_frame /= normfactor
  79.     else:
  80.         if binnorm.count('1') > 1: # no power of two
  81.             center_frame /= normfactor
  82.         else:
  83.             normshift = binnorm.count('0') - 1
  84.             center_frame >>= normshift
  85.     return result
  86.  
  87.  
  88. def main():
  89.     image = numpy.random.random_integers(0, 10, 25).reshape(5, 5)
  90.     mask = numpy.array(((1, 2, 1), (2, 4, 2), (1, 2, 1)))
  91.     filtered = apply_filter_mask(image, mask)
  92.     print "%s\n*\n%s\n=\n%s\n" % (image, mask, filtered)
  93.  
  94.  
  95. if __name__ == '__main__':
  96.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement