title: Converting a Segemented Ground Truth to a Contour Image efficiently with Numpy
body: <p>Suppose I have a segmented image as a Numpy array, where each entry in the image is a number from 1, ... C, C+1 where C is the number of segmentation classes, and class C+1 is some background class. I want to find an efficient way to convert this to a contour image (a binary image where a contour pixel will have value 1, and the rest will have values 0), so that any pixel who has a neighbor in its 8-neighbourhood (or 4-neighbourhood) will be a contour pixel.</p>
  <p>The inefficient way would be something like:</p>
  <pre><code>def isValidLocation(i, j, image_height, image_width):
      if i&lt;0:
          return False
      if i&gt;image_height-1:
          return False
      if j&lt;0:
          return False
      if j&gt;image_width-1:
          return False
      return True
  
  def get8Neighbourhood(i, j, image_height, image_width):
      nbd = []
      for height_offset in [-1, 0, 1]:
          for width_offset in [-1, 0, 1]:
              if isValidLocation(i+height_offset, j+width_offset, image_height, image_width):
                  nbd.append((i+height_offset, j+width_offset))
      return nbd
  
  
  def getContourImage(seg_image):
      seg_image_height = seg_image.shape[0]
      seg_image_width = seg_image.shape[1]
      contour_image = np.zeros([seg_image_height, seg_image_width], dtype=np.uint8)
      for i in range(seg_image_height):
          for j in range(seg_image_width):
              nbd = get8Neighbourhood(i, j, seg_image_height, seg_image_width)
              for (m,n) in nbd:
                  if seg_image[m][n] != seg_image[i][j]:
                      contour_image[i][j] = 1
                      break
      return contour_image
  </code></pre>
  <p>I'm looking for a more efficient &quot;vectorized&quot; way of achieving this, as I need to be able to compute this at run time on batches of 8 images at a time in a deep learning context. Any insights appreciated. Visual Example Below. The first image is the original image overlaid over the ground truth segmentation mask (not the best segmentation admittedly...), the second is the output of my code, which looks good, but is way too slow. Takes me about 10 seconds per image with an intel 9900K cpu.</p>
  <p><a href="https://i.stack.imgur.com/WYDzR.jpg" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/WYDzR.jpg" alt="enter image description here" /></a></p>
  <p><a href="https://i.stack.imgur.com/tVLqi.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/tVLqi.png" alt="enter image description here" /></a></p>
  <p>Image Credit from SUN RGBD dataset.</p>