Вогнутая маска к выпуклой

Я обнаруживаю объект на изображении и создаю маску из контуров. Затем маска расширяется и разглаживается. Например, из такого изображения:

стул

В итоге у меня получилась вот такая маска:

маска стула

Когда я обрезаю изображение (стул) с помощью маски, я теряю (фоновую) информацию между ножками стула. Чтобы обойти это, я хочу превратить эту вогнутую маску в выпуклую. Например, в это (создано в Photoshop):

маска выпуклого стула

Как мне это сделать?:

Код (show_mask.py):

import sys
from pathlib import Path
from helpers_cv2 import *
import cv2
import numpy

img_path = Path(sys.argv[1])

img = cmyk_to_bgr(str(img_path))

threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)

mask        = mask_from_contours(img, contours)
mask_smooth = smooth_mask(mask, 51)
mask_dilate = dilate_mask(mask_smooth, 51)
mask_smooth = smooth_mask(mask_dilate, 51)

cv2.imshow("img", img)
cv2.imshow("mask_smooth", mask_smooth)

cv2.waitKey(0)
cv2.destroyAllWindows()

helpers_cv2.py:

import os
from pathlib import Path
import math
import cv2
import numpy
from PIL import Image
from PIL import ImageCms
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

cwd = os.path.dirname(os.path.abspath(__file__))

def cmyk_to_bgr(cmyk_img):
    img = Image.open(cmyk_img)
    if img.mode == "CMYK":
        img = ImageCms.profileToProfile(img, "\\Color Profiles\\USWebCoatedSWOP.icc", cwd + "\\Color Profiles\\sRGB_Color_Space_Profile.icm", outputMode="RGB")
    return cv2.cvtColor(numpy.array(img), cv2.COLOR_RGB2BGR)

def threshold(img, thresh=128, maxval=255, type=cv2.THRESH_BINARY):
    if len(img.shape) == 3:
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    threshed = cv2.threshold(img, thresh, maxval, type)[1]
    return threshed

def find_contours(img):
    kernel   = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11,11))
    morphed  = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
    contours = cv2.findContours(morphed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    return contours[-2]

def max_contour(contours):
    return sorted(contours, key=cv2.contourArea)[-1]

def mask_from_contours(ref_img, contours):
    mask = numpy.zeros(ref_img.shape, numpy.uint8)
    mask = cv2.drawContours(mask, contours, -1, (255,255,255), -1)
    return cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)

def dilate_mask(mask, kernel_size=11):
    kernel  = numpy.ones((kernel_size, kernel_size), numpy.uint8)
    dilated = cv2.dilate(mask, kernel, iterations=1)
    return dilated

def smooth_mask(mask, kernel_size=11):
    blurred  = cv2.GaussianBlur(mask, (kernel_size, kernel_size), 0)
    threshed = threshold(blurred)
    return threshed

Полноразмерные изображения и коды можно найти в этом репозиторий.


person akinuri    schedule 07.05.2019    source источник
comment
Рассматривали ли вы использование выпуклой оболочки? Здесь есть простое руководство: learnopencv.com/convex -hull-using-opencv-in-python-and-c   -  person ma3oun    schedule 07.05.2019
comment
@ma3oun Ха, именно так. Протестировал это, и это работает. Хотите опубликовать ответ? Если нет, то могу.   -  person akinuri    schedule 07.05.2019
comment
Давай :-) Слишком занято здесь :-p   -  person ma3oun    schedule 07.05.2019
comment
@ma3oun Ха-ха. Это второй раз (последовательно) меня спасает комментарий, и мне пришлось опубликовать свой ответ :)   -  person akinuri    schedule 07.05.2019


Ответы (1)


После просмотра страницы ma3oun, мне удалось заставить его работать. Все, что мне нужно было сделать, это добавить несколько строк в мой код:

contours = find_contours(mask_smooth)
hull = []
for i in range(len(contours)):
    hull.append(cv2.convexHull(contours[i], False))
hull = mask_from_contours(img, hull)

cv2.imshow("hull", hull)

Выходное изображение:

выпуклая оболочка

Код (show_convex_hull.py):

import sys
from pathlib import Path
from helpers_cv2 import *
import cv2
import numpy

from pprint import pprint

img_path = Path(sys.argv[1])

img = cmyk_to_bgr(str(img_path))

threshed = threshold(img, 240, type=cv2.THRESH_BINARY_INV)
contours = find_contours(threshed)

mask        = mask_from_contours(img, contours)
mask_smooth = smooth_mask(mask, 51)
mask_dilate = dilate_mask(mask_smooth, 51)
mask_smooth = smooth_mask(mask_dilate, 51)

# convex hull ops
# find contours from the mask (contours needs to be calculated again because the mask is updated)
contours = find_contours(mask_smooth)
# find and store hull points
hull = []
for i in range(len(contours)):
    hull.append(cv2.convexHull(contours[i], False))
# create a mask from hull points
hull = mask_from_contours(img, hull)

cv2.imshow("hull", hull)

cv2.waitKey(0)
cv2.destroyAllWindows()
person akinuri    schedule 07.05.2019