2013-12-12 5 views
5

Я работаю над некоторыми изображениями листьев, используя OpenCV (Java). Листы захватываются на белой бумаге, а некоторые есть тень, как это:Автоматический подход для удаления тени объекта цвета на белом фоне?

enter image description here

Конечно, это как-то крайний случай (есть более мягкая тень).

Теперь я хочу создать порог листа, а также удалить тень (при сохранении деталей листа).


Мой текущий поток заключается в следующем:

1) Преобразование в ВПГ и извлечение Saturation канал:

Imgproc.cvtColor(colorMat, colorMat, Imgproc.COLOR_RGB2HSV); 
ArrayList<Mat> channels = new ArrayList<Mat>(); 
Core.split(colorMat, channels); 
satImg = channels.get(1); 

2) Де-зашумления (медиана) и применяя adaptiveThreshold :

Imgproc.medianBlur(satImg , satImg , 11); 
Imgproc.adaptiveThreshold(satImg , satImg , 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 401, -10); 

И результат:

enter image description here

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

Теперь у меня есть 2 вопроса:
1) Как я могу улучшить результат и избавиться от тени?

2) Могу ли я получить хорошие результаты без работы по каналу насыщения?. Причина, по которой я спрашиваю, заключается в том, что на большинстве моих изображений работающий на канале L (от HLS) дает лучшие результаты (кроме тени, конечно).


Update: Использование канала Hue делает threshdolding лучше, но делает тень ситуация хуже:

enter image description here


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

+1

Поскольку лист зеленый, а фон белый/тень, вы можете работать на канале оттенка. – GilLevi

+0

@ GilLevi, см. Обновление. Это не помогает устранить тень. – Mahm00d

+0

Хммм, я действительно не уверен. Возможно, используя другое цветовое пространство, например LAB? Извините, я не могу помочь дальше. – GilLevi

ответ

2

Я не использую opencv, вместо этого я пытался использовать панель инструментов обработки изображений Matlab для извлечения листа. Надеюсь, у opencv есть все функции обработки для вас. См. Мой результат ниже. Я выполнил все операции в вашем исходном канале изображения 3 и в канале 1.

Сначала я использовал ваш канал 3, пороговый с 100 (левый верхний). Затем я удаляю области на границе и области с размером пикселя меньше 100, заполняя отверстие в листе, результат показан в правом верхнем углу.

Далее я использовал ваш канал 1, сделал то же самое, что и в канале 3, результат показан в левом нижнем углу. Затем я узнал связанные области (есть только два, как вы можете видеть в левом нижнем рисунке), удалите один с меньшей площадью (показано в правом нижнем углу).

enter image description here

Предположит, что правое верхнее изображение I1, а правая нижняя изображение I, лист извлекается осуществить ~I && I1. Лист:

enter image description here

Надеется, что это помогает. Благодаря

+0

Действительно хороший подход. Хотя есть две проблемы с автоматизацией: 1) я не знаю статический порог тени, 2) Нет никакой гарантии, что самой большой частью будет тень, а не другая часть (часть листа). – Mahm00d

+1

Спасибо, Mahm00d. Порог в обработке изображений всегда сложный, поэтому вам может потребоваться выбрать каналы, которые разделяют лист и тень с наибольшим запасом интенсивности. И вам может понадобиться несколько образцов для грубой оценки каналов и порога. Что касается компонентов соединения, интенсивность корня намного ближе к тени, чем к листу. Вот почему обычно обнаруживается две связанные части. Иногда корень может нести некоторые крошечные части в листе, но предположение, что большая часть будет тень, истинна в большинстве случаев. – lennon310

+0

Вы правы насчет сложной части. У меня есть образец с листом темно-зеленого. В этом случае почти невозможно найти статический порог, который также не включает части листа. Хуже того, иногда лист может быть темнее тени! Поэтому я думаю, что работа над каналами вместо интенсивностей будет лучшим подходом. – Mahm00d

1

Я пробовал две разные вещи: 1. других пороговые на канале насыщения 2. Попробуйте найти два контура: тень и лист

Я использую C++, так что ваши фрагменты кода будут выглядеть немного иначе.

пытается Оца-пороговые вместо адаптивных порогового:

cv::threshold(hsv_imgs,mask,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU); 

приводит к изменению следующих изображений (только OTSU пороговым на канале насыщения):

enter image description hereenter image description here

другой вещь вычисление градиент информации (я использовал sobel, см. документацию oppenCV), пороговое значение, которое и после оператора открытия я использовал findContours, давая что-то вроде этого, еще не пригодный для использования (градиент контурная подход):

enter image description hereenter image description here

0

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

import cv2 
import numpy as np 

img = cv2.imread("leaf.jpg") 
sat = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)[:,:,1] 
sat = cv2.medianBlur(sat, 11) 
thresh = cv2.adaptiveThreshold(sat , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10); 
cv2.imwrite("thresh.jpg", thresh) 

h, w = img.shape[:2] 
bgdModel = np.zeros((1,65),np.float64) 
fgdModel = np.zeros((1,65),np.float64) 
grabcut_mask = thresh/255*3 #background should be 0, probable foreground = 3 
cv2.grabCut(img, grabcut_mask,(0,0,w,h),bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) 
grabcut_mask = np.where((grabcut_mask ==2)|(grabcut_mask ==0),0,1).astype('uint8') 

cv2.imwrite("GrabCut1.jpg", img*grabcut_mask[...,None]) 

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

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

Обратите внимание, что в pdf препринте, который вы находите в Интернете, есть ошибка (нет + знаков) в уравнении 6. Вы можете использовать версию, указанную в Rodriguez-Gomez et al (2012), уравнения 1 & 2.Или вы можете использовать мой питон код ниже:

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)) 

Вы можете кормить известный фон означает, & STDEV как последние два параметра функции chromacity_distortion, а RGB пиксела изображения в качестве первого параметра, который должен показать, что тень в основном такая же, как и у фона, и очень отличается от листа. В приведенном ниже коде я установил пороговое значение в отношении непрозрачности и сделал еще один проход grabcut. Это работает, чтобы удалить тень, даже если первый grabcut проход не (например, если вы изначально порогами на оттенок)

mean, stdev = cv2.meanStdDev(img, mask = 255-thresh) 
mean = mean.ravel() #bizarrely, meanStdDev returns an array of size [3,1], not [3], so flatten it 
stdev = stdev.ravel() 
chrom = chromacity_distortion(img, mean, stdev) 
chrom255 = cv2.normalize(chrom, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX).astype(np.uint8)[:,:,None] 
cv2.imwrite("ChromacityDistortionFromBackground.jpg", chrom255) 

thresh2 = cv2.adaptiveThreshold(chrom255 , 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 401, 10); 
cv2.imwrite("thresh2.jpg", thresh2) 

grabcut_mask[...] = 3 
grabcut_mask[thresh==0] = 0 #where thresh == 0, definitely background, set to 0 
grabcut_mask[np.logical_and(thresh == 255, thresh2 == 0)] = 2 #could try setting this to 2 or 0 
cv2.grabCut(img, grabcut_mask,(0,0,w,h),bgdModel,fgdModel,5,cv2.GC_INIT_WITH_MASK) 
grabcut_mask = np.where((grabcut_mask ==2)|(grabcut_mask ==0),0,1).astype('uint8') 
cv2.imwrite("final_leaf.jpg", grabcut_mask[...,None]*img) 

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

Смежные вопросы