Обнаружение листа на непредсказуемом фоне - PullRequest
0 голосов
/ 26 июня 2018

Проект, над которым я работал в течение некоторого времени, - это неконтролируемая сегментация листьев.Листья фиксируются на белой или цветной бумаге, и на некоторых из них есть тени.

Я хочу иметь возможность пороговить лист и удалить тень (сохраняя детали листа);однако я не могу использовать фиксированные пороговые значения из-за болезней, меняющих цвет листа.

Затем я начинаю исследовать и нахожу предложение Horprasert et.и др.(1999) в «Статистическом подходе для надежного вычитания фона в реальном времени и обнаружения теней», в котором сравниваются области изображения с цветом уже известного фона с использованием показателя искажения цветности.Эта мера учитывает тот факт, что для ненасыщенных цветов оттенок не является релевантной мерой.

На основании этого мне удалось достичь следующих результатов:

Example 01

Тем не менее, листья, которые записаны на белой бумаге, должны изменить маску V cv2.bitwise_not (), чтобы получить следующий результат:

Example 02

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

Мой код:

import numpy as np
import cv2
import matplotlib.pyplot as plot
import scipy.ndimage as ndimage

def brightness_distortion(I, mu, sigma):
    return np.sum(I*mu/sigma**2, axis=-1) / np.sum((mu/sigma)**2, axis=-1)


def chromacity_distortion(I, mu, sigma):
    alpha = brightness_distortion(I, mu, sigma)[...,None]
    return np.sqrt(np.sum(((I - alpha * mu)/sigma)**2, axis=-1))

def bwareafilt ( image ):
    image = image.astype(np.uint8)
    nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(image, connectivity=4)
    sizes = stats[:, -1]

    max_label = 1
    max_size = sizes[1]
    for i in range(2, nb_components):
        if sizes[i] > max_size:
            max_label = i
            max_size = sizes[i]

    img2 = np.zeros(output.shape)
    img2[output == max_label] = 255

    return img2

img = cv2.imread("Amostra03.jpeg")
sat = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,1]
val = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,2]
sat = cv2.medianBlur(sat, 11)
val = cv2.medianBlur(val, 11)
thresh_S = cv2.adaptiveThreshold(sat , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10);
thresh_V = cv2.adaptiveThreshold(val , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10);

mean_S, stdev_S = cv2.meanStdDev(img, mask = 255 - thresh_S)
mean_S = mean_S.ravel().flatten()
stdev_S = stdev_S.ravel()
chrom_S = chromacity_distortion(img, mean_S, stdev_S)
chrom255_S = cv2.normalize(chrom_S, chrom_S, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX).astype(np.uint8)[:,:,None]

mean_V, stdev_V = cv2.meanStdDev(img, mask = 255 - thresh_V)
mean_V = mean_V.ravel().flatten()
stdev_V = stdev_V.ravel()
chrom_V = chromacity_distortion(img, mean_V, stdev_V)
chrom255_V = cv2.normalize(chrom_V, chrom_V, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX).astype(np.uint8)[:,:,None]

thresh2_S = cv2.adaptiveThreshold(chrom255_S , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10);
thresh2_V = cv2.adaptiveThreshold(chrom255_V , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10);

images = [img, thresh_S, thresh_V, cv2.bitwise_and(thresh2_S, cv2.bitwise_not(thresh2_V))]
titles = ['Original Image', 'Mask S', 'Mask V', 'S + V']
for i in range(4):
    plot.subplot(2,2,i+1),
    if i == 0 :
        plot.imshow(images[i])
    else :
        plot.imshow(images[i], cmap='gray')
    plot.title(titles[i])
    plot.xticks([]),plot.yticks([])
plot.show()

Есть идеи решить эту проблему?

1 Ответ

0 голосов
/ 26 июня 2018

Попробуйте это ... Я использую "grabCut" из openCV lib.Это не идеально, но это может быть хорошим началом.

import cv2
import numpy as np
from matplotlib import pyplot as plt
import matplotlib
#%matplotlib inline #uncomment if in notebook

def mask_leaf(im_name, external_mask=None):

    im = cv2.imread(im_name)
    im = cv2.blur(im, (5,5))

    height, width = im.shape[:2]

    mask = np.ones(im.shape[:2], dtype=np.uint8) * 2 #start all possible background
    '''
    #from docs:
    0 GC_BGD defines an obvious background pixels.
    1 GC_FGD defines an obvious foreground (object) pixel.
    2 GC_PR_BGD defines a possible background pixel.
    3 GC_PR_FGD defines a possible foreground pixel.
    '''

    #2 circles are "drawn" on mask. a smaller centered one I assume all pixels are definite foreground. a bigger circle, probably foreground.
    r = 100
    cv2.circle(mask, (int(width/2.), int(height/2.)), 2*r, 3, -3) #possible fg
    #next 2 are greens...dark and bright to increase the number of fg pixels.
    mask[(im[:,:,0] < 45) & (im[:,:,1] > 55) & (im[:,:,2] < 55)] = 1  #dark green
    mask[(im[:,:,0] < 190) & (im[:,:,1] > 190) & (im[:,:,2] < 200)] = 1  #bright green
    mask[(im[:,:,0] > 200) & (im[:,:,1] > 200) & (im[:,:,2] > 200) & (mask != 1)] = 0 #pretty white

    cv2.circle(mask, (int(width/2.), int(height/2.)), r, 1, -3) #fg

    #if you pass in an external mask derived from some other operation it is factored in here.
    if external_mask is not None:
        mask[external_mask == 1] = 1

    bgdmodel = np.zeros((1,65), np.float64)
    fgdmodel = np.zeros((1,65), np.float64)
    cv2.grabCut(im, mask, None, bgdmodel, fgdmodel, 1, cv2.GC_INIT_WITH_MASK)

    #show mask
    plt.figure(figsize=(10,10))
    plt.imshow(mask)
    plt.show()

    #mask image
    mask2 = np.where((mask==1) + (mask==3), 255, 0).astype('uint8')
    output = cv2.bitwise_and(im, im, mask=mask2)
    plt.figure(figsize=(10,10))
    plt.imshow(output)
    plt.show()

mask_leaf('leaf1.jpg', external_mask=None)
mask_leaf('leaf2.jpg', external_mask=None)

mask1 masked leaf 1 mask2 masked leaf2

Обращаясь к внешней маске.Вот пример кластеризации HDBSCAN ... Я не буду вдаваться в подробности ... вы можете посмотреть документы и изменить их или использовать как есть.

import hdbscan
from collections import Counter


def hdbscan_mask(im_name):

    im = cv2.imread(im_name)
    im = cv2.blur(im, (5,5))

    indices = np.dstack(np.indices(im.shape[:2]))
    data = np.concatenate((indices, im), axis=-1)
    data = data[:,2:]

    data = imb.reshape(im.shape[0]*im.shape[1], 3)
    clusterer = hdbscan.HDBSCAN(min_cluster_size=1000, min_samples=20)
    clusterer.fit(data)

    plt.figure(figsize=(10,10))
    plt.imshow(clusterer.labels_.reshape(im.shape[0:2]))
    plt.show()

    height, width = im.shape[:2]

    mask = np.ones(im.shape[:2], dtype=np.uint8) * 2 #start all possible background
    cv2.circle(mask, (int(width/2.), int(height/2.)), 100, 1, -3) #possible fg

    #grab cluster number for circle
    vals_im = clusterer.labels_.reshape(im.shape[0:2])

    vals = vals_im[mask == 1]
    commonvals = []
    cnts = Counter(vals)
    for v, count in cnts.most_common(20):
    #print '%i: %7d' % (v, count)
    if v == -1:
        continue
    commonvals.append(v)

    tst = np.in1d(vals_im, np.array(commonvals))
    tst = tst.reshape(vals_im.shape)

    hmask = tst.astype(np.uint8)

    plt.figure(figsize=(10,10))
    plt.imshow(hmask)
    plt.show()

    return hmask

hmask = hdbscan_mask('leaf1.jpg')

hdbscan cluster labels hdbscan leaf mask

, а затем использовать начальную функцию с новой маской (вывод подавлен):

mask_leaf('leaf1.jpg', external_mask=hmask)

Все это было сделано вНоутбук с нуля, так что, надеюсь, нет никаких случайных переменных, которые его душат, когда он запускается где-то еще.(примечание: я НЕ менял BGR на RGB для отображения plt, извините)

...