自动方法去除白色背景上的colord对象阴影?

时间:2013-12-12 11:32:37

标签: opencv image-processing

我正在使用OpenCV(Java)处理一些叶子图像。叶子被捕获在白纸上,有些像这样的阴影:

enter image description here

当然,它是某种极端情况(有更温和的阴影)。

现在,我想要对叶子进行阈值处理并删除阴影(同时保留叶子的细节)。


我目前的流程是:

1)转换为 HSV 并提取饱和度频道:

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

它看起来不错,但阴影导致沿左边界出现一些异常。此外,我有这种感觉,我没有使用白色背景对我有利。

现在,我有两个问题:
1)如何改善结果并消除阴影?

2)如果不使用饱和通道,我能获得良好的效果吗?。我问的原因是,在我的大多数图像上,使用 L 频道(来自 HLS )可以获得更好的效果(当然除了阴影)。


更新:使用 Hue 频道可以让threshdolding更好,但会让影子情况变得更糟:

enter image description here


Update2:在某些情况下,阴影比叶子更暗的假设并不总是成立。因此,处理强度无济于事。我正在寻找一种色彩通道方法。

3 个答案:

答案 0 :(得分:3)

我不使用opencv,而是尝试使用matlab图像处理工具箱来提取叶子。希望opencv能为您提供所有处理功能。请看下面的结果。我在原始图像通道3和通道1中完成了所有操作。

首先我使用了你的频道3,将它设为100(左上角)。然后我删除边框上的区域和像素大小小于100的区域,填充叶子中的孔,结果显示在右上角。

接下来我使用你的频道1,做了与我在频道3中做的相同的事情,结果显示在左下角。然后我找到了连接区域(左下图中只有两个),删除区域较小的区域(如右下图所示)。

enter image description here

假设右上图像是I1,右下图像是I,叶子由工具~I && I1提取。叶子是:

enter image description here

希望它有所帮助。感谢

答案 1 :(得分:1)

我尝试了两件不同的事情: 1.饱和通道上的其他阈值处理 2.尝试找到两个轮廓:阴影和叶子

我使用c ++,所以你的代码片段看起来会有所不同。

尝试otsu-thresholding而不是自适应阈值处理:

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

导致以下图像(仅在饱和通道上进行OTSU阈值处理):

enter image description here enter image description here

另一件事是计算梯度信息(我使用sobel,参见oppenCV文档),在打开操作符之后使用findContours给出类似这样的东西的阈值,不可用(渐变轮廓方法):< / p>

enter image description here enter image description here

答案 2 :(得分:0)

我正在尝试用蝴蝶的照片做同样的事情,但是有更多不均匀和不可预测的背景,例如this。一旦你确定了很好的背景部分(例如通过阈值处理,或者像我们一样,从随机点填充洪水),最好的方法是使用GrabCut算法来获取初始传递中可能遗漏的所有位数。在python中,假设您仍想通过在饱和度通道上设置阈值来识别背景的初始区域,请尝试类似

的内容
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等人提出的 chromacity distortion 测量来比较图像中的区域和现在已知背景的颜色。人。 (1999)在“用于实时鲁棒背景减法和阴影检测的统计方法”中。该措施考虑到对于去饱和颜色,色调不是相关措施的事实。

请注意,您在网上找到的预印本的pdf在等式6中有错误(无+符号)。您可以使用Rodriguez-Gomez et al (2012)中重新引用的版本,等式1&amp; 2.或者你可以使用我的python代码:

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

您可以提供已知的背景平均值&amp; stdev作为chromacity_distortion函数的最后两个参数,并且RGB像素图像作为第一个参数,它应该向您显示阴影与背景基本上是相同的色度,并且与叶子非常不同。在下面的代码中,我已经对chromacity进行了阈值处理,并完成了另一个抓取过程。即使第一个抓取通道没有(例如,如果你最初在色调上设定阈值),这也可以去除阴影

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认为它看起来与阴影颜色相似。如果你找到了保留它的方法,请告诉我。