seam_finder.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. from collections import OrderedDict
  2. import cv2 as cv
  3. import numpy as np
  4. from .blender import Blender
  5. class SeamFinder:
  6. """https://docs.opencv.org/4.x/d7/d09/classcv_1_1detail_1_1SeamFinder.html""" # noqa
  7. SEAM_FINDER_CHOICES = OrderedDict()
  8. SEAM_FINDER_CHOICES['dp_color'] = cv.detail_DpSeamFinder('COLOR')
  9. SEAM_FINDER_CHOICES['dp_colorgrad'] = cv.detail_DpSeamFinder('COLOR_GRAD')
  10. SEAM_FINDER_CHOICES['voronoi'] = cv.detail.SeamFinder_createDefault(cv.detail.SeamFinder_VORONOI_SEAM) # noqa
  11. SEAM_FINDER_CHOICES['no'] = cv.detail.SeamFinder_createDefault(cv.detail.SeamFinder_NO) # noqa
  12. DEFAULT_SEAM_FINDER = list(SEAM_FINDER_CHOICES.keys())[0]
  13. def __init__(self, finder=DEFAULT_SEAM_FINDER):
  14. self.finder = SeamFinder.SEAM_FINDER_CHOICES[finder]
  15. def find(self, imgs, corners, masks):
  16. """https://docs.opencv.org/4.x/d0/dd5/classcv_1_1detail_1_1DpSeamFinder.html#a7914624907986f7a94dd424209a8a609""" # noqa
  17. imgs_float = [img.astype(np.float32) for img in imgs]
  18. return self.finder.find(imgs_float, corners, masks)
  19. @staticmethod
  20. def resize(seam_mask, mask):
  21. dilated_mask = cv.dilate(seam_mask, None)
  22. resized_seam_mask = cv.resize(dilated_mask, (mask.shape[1],
  23. mask.shape[0]),
  24. 0, 0, cv.INTER_LINEAR_EXACT)
  25. return cv.bitwise_and(resized_seam_mask, mask)
  26. @staticmethod
  27. def draw_seam_mask(img, seam_mask, color=(0, 0, 0)):
  28. seam_mask = cv.UMat.get(seam_mask)
  29. overlayed_img = np.copy(img)
  30. overlayed_img[seam_mask == 0] = color
  31. return overlayed_img
  32. @staticmethod
  33. def draw_seam_polygons(panorama, blended_seam_masks, alpha=0.5):
  34. return add_weighted_image(panorama, blended_seam_masks, alpha)
  35. @staticmethod
  36. def draw_seam_lines(panorama, blended_seam_masks,
  37. linesize=1, color=(0, 0, 255)):
  38. seam_lines = \
  39. SeamFinder.exctract_seam_lines(blended_seam_masks, linesize)
  40. panorama_with_seam_lines = panorama.copy()
  41. panorama_with_seam_lines[seam_lines == 255] = color
  42. return panorama_with_seam_lines
  43. @staticmethod
  44. def exctract_seam_lines(blended_seam_masks, linesize=1):
  45. seam_lines = cv.Canny(np.uint8(blended_seam_masks), 100, 200)
  46. seam_indices = (seam_lines == 255).nonzero()
  47. seam_lines = remove_invalid_line_pixels(
  48. seam_indices, seam_lines, blended_seam_masks
  49. )
  50. kernelsize = linesize + linesize - 1
  51. kernel = np.ones((kernelsize, kernelsize), np.uint8)
  52. return cv.dilate(seam_lines, kernel)
  53. @staticmethod
  54. def blend_seam_masks(seam_masks, corners, sizes):
  55. imgs = colored_img_generator(sizes)
  56. blended_seam_masks, _ = \
  57. Blender.create_panorama(imgs, seam_masks, corners, sizes)
  58. return blended_seam_masks
  59. def colored_img_generator(sizes, colors=(
  60. (255, 000, 000), # Blue
  61. (000, 000, 255), # Red
  62. (000, 255, 000), # Green
  63. (000, 255, 255), # Yellow
  64. (255, 000, 255), # Magenta
  65. (128, 128, 255), # Pink
  66. (128, 128, 128), # Gray
  67. (000, 000, 128), # Brown
  68. (000, 128, 255)) # Orange
  69. ):
  70. for idx, size in enumerate(sizes):
  71. if idx+1 > len(colors):
  72. raise ValueError("Not enough default colors! Pass additional "
  73. "colors to \"colors\" parameter")
  74. yield create_img_by_size(size, colors[idx])
  75. def create_img_by_size(size, color=(0, 0, 0)):
  76. width, height = size
  77. img = np.zeros((height, width, 3), np.uint8)
  78. img[:] = color
  79. return img
  80. def add_weighted_image(img1, img2, alpha):
  81. return cv.addWeighted(
  82. img1, alpha, img2, (1.0 - alpha), 0.0
  83. )
  84. def remove_invalid_line_pixels(indices, lines, mask):
  85. for x, y in zip(*indices):
  86. if check_if_pixel_or_neighbor_is_black(mask, x, y):
  87. lines[x, y] = 0
  88. return lines
  89. def check_if_pixel_or_neighbor_is_black(img, x, y):
  90. check = [is_pixel_black(img, x, y),
  91. is_pixel_black(img, x+1, y), is_pixel_black(img, x-1, y),
  92. is_pixel_black(img, x, y+1), is_pixel_black(img, x, y-1)]
  93. return any(check)
  94. def is_pixel_black(img, x, y):
  95. return np.all(get_pixel_value(img, x, y) == 0)
  96. def get_pixel_value(img, x, y):
  97. try:
  98. return img[x, y]
  99. except IndexError:
  100. pass