docs: def triangulize(image, tile_size):
      """Processes the given image by breaking it down into tiles of the given
      size and applying a triangular effect to each tile. Returns the processed
      image as a PIL Image object.
  
      The image can be given as anything suitable for passing to `Image.open`
      (ie, the path to an image or as a file-like object containing image data).
  
      If tile_size is 0, the tile size will be guessed based on the image
      size. It will also be adjusted to be divisible by 2 if it is not already.
      """
      if isinstance(image, basestring) or hasattr(image, 'read'):
          image = Image.open(image)
      assert isinstance(tile_size, int)
  
      # Make sure we have a usable tile size, by guessing based on image size
      # and making sure it's a multiple of two.
      if tile_size == 0:
          tile_size = guess_tile_size(image)
      if tile_size % 2 != 0:
          tile_size = (tile_size / 2) * 2
  
      logging.info('Input image size: %r', image.size)
      logging.info('Tile size: %r', tile_size)
  
      # Preprocess image to make sure it's at a size we can handle
      image = prep_image(image, tile_size)
      logging.info('Prepped image size: %r', image.size)
  
      # Get pixmap (for direct pixel access) and draw objects for the image.
      pix = image.load()
      draw = ImageDraw.Draw(image)
  
      # Process the image, tile by tile
      for x, y in iter_tiles(image, tile_size):
          process_tile(x, y, tile_size, pix, draw, image)
      return image