Вместо того, чтобы использовать пороговое значение, как предложено в комментариях, я использовал обнаружение края Канни для обнаружения бабочек, поскольку у некоторых из них есть пятна на крыльях близко к краю и того же цвета, что и фон книги, что может вызвать проблемы получить все крыло. Кроме того, поскольку изображения достаточно велики, обнаружение краев здесь выглядит достаточно надежным.
Сам подход довольно прост (для реализации я использовал Python и OpenCV):
- Обнаружение канни края на изображении (параметры устанавливаются вручную).
- Поиск контуров; исключить небольшие контуры; нарисуйте оставшиеся контуры, чтобы создать двоичную маску.
- Повторяйте оставшиеся контуры; получить ограничивающий прямоугольник; получить обрезанную часть изображения и маску; «чистая» маска путем удаления потенциальных частей соседних бабочек; создать новое (обрезанное) изображение с прозрачным фоном.
Вот весь код:
import cv2
import numpy as np
import platform # Only needed for system information
from skimage import io # Only needed for image web grabbing
# Read image from web; enforce BGR color ordering
image = cv2.cvtColor(io.imread('https://i.stack.imgur.com/bwS3g.jpg'),
cv2.COLOR_RGB2BGR)
# Canny edge detection
canny = cv2.Canny(image, 50, 150)
canny = cv2.morphologyEx(canny, cv2.MORPH_CLOSE,
cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
# Find contours; use proper return value with respect to OpenCV version
cnts = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
# Filter contours with sufficient areas; create binary mask from them
cnts = [c for c in cnts if cv2.contourArea(c) > 10000]
mask = np.zeros_like(canny)
mask = cv2.drawContours(mask, np.array(cnts), -1, 255, cv2.FILLED)
# Iterate all contours...
for i, c in enumerate(cnts):
# Get bounding rectangle of contour and min/max coordinates
rect = cv2.boundingRect(c)
(x1, y1) = rect[:2]
x2 = x1 + rect[2]
y2 = y1 + rect[3]
# Get image section
crop_image = image[y1:y2, x1:x2]
# Get mask section and cut possible neighbouring contours
crop_mask = mask[y1:y2, x1:x2].copy()
cnts = cv2.findContours(crop_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
c = max(cnts, key=cv2.contourArea)
crop_mask[:, :] = 0
cv2.drawContours(crop_mask, [c], -1, 255, cv2.FILLED)
# Create butterfly image with transparent background
butterfly = np.zeros((rect[3], rect[2], 4), np.uint8)
butterfly[:, :, :3] = cv2.bitwise_and(crop_image, crop_image,
mask=crop_mask)
butterfly[:, :, 3] = crop_mask
cv2.imwrite(str(i) + '.png', butterfly)
print('------------------')
print('System information')
print('------------------')
print('Python: ', platform.python_version())
print('NumPy: ', np.__version__)
print('OpenCV: ', cv2.__version__)
print('------------------')
Вот два из сохраненных изображений бабочки.
![Example 1](https://i.stack.imgur.com/rMzvX.png)
![Example 2](https://i.stack.imgur.com/dor4Z.png)
Вы можете слегка размыть альфа-канал для более плавного перехода от бабочки к фону.
Надеюсь, это поможет!
------------------
System information
------------------
Python: 3.7.1
NumPy: 1.18.1
OpenCV: 4.1.2
------------------