Завершение контура для «разбитого» изображения - PullRequest
0 голосов
/ 18 декабря 2018

У меня шумное серое изображение, для которого я хочу сегментировать / замаскировать большую дугу, охватывающую изображение от остальных.Я намерен замаскировать дугу и все пиксели над дугой.

Для этого я установил пороговое изображение, чтобы создать двоичное изображение, и использовал cv2.findContours(), чтобы проследить контур дуги.

Исходное изображение:

original image

Изображение после порога Оцу:

image after threshold

Порог + Закрытие:

image after closing

Контуры замкнутыimage:

resulting contours

Как видите, закрытое изображение не создает сплошную дугу.При дальнейшем закрытии дуга теряет свою форму.Зеленая линия - это контур закрытого изображения.Синяя линия создана с approxpolyDP(), но я не могу заставить его работать.Возможно, есть ли лучшие способы маскировки дуги на изображении?

Вот мой код:

import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt

# read an image
img = cv2.imread('oct.png')

# get gray image and apply Gaussian blur
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)

# get binary image
ret, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# close image to "solidify" it
kernel = np.ones((3,3),np.uint8)
closing = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,kernel, iterations = 3)


# find contours
(_, contours, _) = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

cnt = contours[0]
max_area = cv2.contourArea(cnt)

for cont in contours:
    if cv2.contourArea(cont) > max_area:
        cnt = cont
        max_area = cv2.contourArea(cont)

# define main arc contour approx. and hull
perimeter = cv2.arcLength(cnt, True)
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)

# hull = cv2.convexHull(cnt)

# cv2.isContourConvex(cnt)
imgcopy = np.copy(img)
cv2.drawContours(imgcopy, [cnt], -1, (0, 255, 0), 3)
cv2.drawContours(imgcopy, [approx], -1, (0, 0, 255), 3)

# plot figures
plt.figure(1)
plt.imshow(imgcopy, cmap="gray")
plt.figure(2)
plt.imshow(thresh, cmap="gray")
plt.figure(3)
plt.imshow(closing, cmap="gray")

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

Я бы предложил использовать метод RANSAC, чтобы подогнать 2 эллипса, используя информацию о краях дуги.Edge можно получить, просто используя canny или любой другой метод, который вы считаете нужным.Конечно, этот метод может работать, только если дуга эллиптическая.Если это прямая линия, вы можете заменить деталь фитинга эллипса на деталь фитинга линии.

Вот результат:

enter image description here

Вот код:

import numpy as np
import cv2
import random as rp


def ransac_ellipse(iter, srcimg, x, y):

    x_size = np.size(x)
    best_count = x_size
    for i in range(iter):

        base = srcimg.copy()

        # get 5 random points
        r1 = int(rp.random() * x_size)
        r2 = int(rp.random() * x_size)  
        r3 = int(rp.random() * x_size)
        r4 = int(rp.random() * x_size)
        r5 = int(rp.random() * x_size)  

        p1 = (x[r1],y[r1])
        p2 = (x[r2],y[r2])
        p3 = (x[r3],y[r3])
        p4 = (x[r4],y[r4])
        p5 = (x[r5],y[r5])

        p_set = np.array((p1,p2,p3,p4,p5))

        # fit ellipse
        ellipse = cv2.fitEllipse(p_set)

        # remove intersected ellipse
        cv2.ellipse(base,ellipse,(0),1)

        # count remain
        local_count = cv2.countNonZero(base)

        # if count is smaller than best, update
        if local_count < best_count:
            best_count = local_count
            best_ellipse = ellipse


    return best_ellipse

img = cv2.imread('arc.jpg',0)

# Speed up and remove noise
small = cv2.resize(img,(0,0),fx = 0.25,fy = 0.25)

# remove remaining noise
median = cv2.medianBlur(small,21)

# get canny edge
edge = cv2.Canny(median,180,20)


cv2.imshow("Edge",edge)


# obtain the non zero locations
y, x = np.where(edge > 0)
# ransac ellipse to get the outter circle
ellipse1 = ransac_ellipse(10000,edge,x,y)


# remove the outter circle
cv2.ellipse(edge,ellipse1,(0),2)

# ransac ellipse to get the inner circle
y, x = np.where(edge > 0)
ellipse2 = ransac_ellipse(10000,edge,x,y)

disp = cv2.cvtColor(small,cv2.COLOR_GRAY2BGR)
cv2.ellipse(disp,ellipse1,(0,0,255),1)
cv2.ellipse(disp,ellipse2,(0,0,255),1)


cv2.imshow("result",disp)
cv2.waitKey(0)
0 голосов
/ 18 декабря 2018

Вы на правильном пути.Ваше закрытие, скорее всего, будет работать лучше, если сначала немного сгладить изображение.Мне нравится применять порог в конце, после морфологических операций.В этом случае на самом деле не имеет значения, каков порядок закрытия и определения порога, но сохранение порога в конце помогает позже при уточнении предварительной обработки.Как только вы достигнете порогового значения, вы потеряете много информации, вам необходимо убедиться, что вы сохранили всю информацию, которая вам понадобится, и, таким образом, правильно отфильтровать изображение перед установлением порога.

Вот небольшая попытка, яуверен, что это может быть уточнено:

import matplotlib.pyplot as pp
import PyDIP as dip

img = pp.imread('/Users/cris/Downloads/MipBB.jpg')
img = img[:,:,0]
smooth = dip.Gauss(img, [3])        # Gaussian smoothing with sigma=3
smooth = dip.Closing(smooth, 25)    # Note! This uses a disk SE with diameter 25 pixels
out = dip.Threshold(smooth, 'triangle')[0]
pp.imsave('/Users/cris/Downloads/MipBB_out.png', out)

output of the code above

Я использовал метод порога треугольника (также известный как метод аккордов или перекошенный порог бимодальности)см. PL Rosin, "Unimodal thresholding", Распознавание образов 34 (11): 2083-2096, 2001), поскольку в этом случае он работает лучше.

Код использует PyDIP , но яя уверен, что вы можете заново создать тот же процесс, используя OpenCV.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...