【问题标题】:How to remove hair from skin images using opencv?如何使用opencv从皮肤图像中去除头发?
【发布时间】:2018-10-17 15:09:04
【问题描述】:

我正在处理皮肤斑点的识别。为此,我处理了许多具有不同噪声的图像。这些噪音之一是毛发,因为我的图像在污渍(ROI)区域上有毛发。如何减少或去除这些类型的图像噪声?

下面的代码会减少毛发所在的区域,但不会去除感兴趣区域(ROI)上方的毛发。

import numpy as np
import cv2

IMD = 'IMD436'
# Read the image and perfrom an OTSU threshold
img = cv2.imread(IMD+'.bmp')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh =     cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

# Remove hair with opening
kernel = np.ones((2,2),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)

# Combine surrounding noise with ROI
kernel = np.ones((6,6),np.uint8)
dilate = cv2.dilate(opening,kernel,iterations=3)

# Blur the image for smoother ROI
blur = cv2.blur(dilate,(15,15))

# Perform another OTSU threshold and search for biggest contour
ret, thresh =     cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
contours, hierarchy =     cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)

# Create a new mask for the result image
h, w = img.shape[:2]
mask = np.zeros((h, w), np.uint8)

# Draw the contour on the new mask and perform the bitwise operation
cv2.drawContours(mask, [cnt],-1, 255, -1)
res = cv2.bitwise_and(img, img, mask=mask)

# Display the result
cv2.imwrite(IMD+'.png', res)
cv2.imshow('img', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

退出:

如何去除我感兴趣区域顶部的毛发?

使用的图片:

【问题讨论】:

  • 你希望通过去除头发来达到什么目的?它不会透露有关皮肤斑点的新信息。仅仅是因为光学还是有其他原因?
  • 您使用的图片与this other user 相同——他今天删除了一个问同样问题的问题。也许你们是不同的人一起工作,那很好。但如果你是同一个人,你应该知道创建多个帐户是违反 TOS 的。
  • 这是我对那个已删除问题的建议:“有很多关于此的论文(“数字剃须”,我记得几年前看过一篇)以及您之前的问题。我建议您搜索科学文献而不是在这里问。你可能会从科学文献中得到更好的答案,因为你不太可能在这里找到像写这些论文的人那样彻底研究这个话题的人。我想重申这一点。这是一个有几个好的解决方案的话题,不要试图重新发明轮子!!!
  • @Cris 我们是不同的人一起工作。我正在研究文献,但由于我是处理的新手,我在实施解决方案时遇到了困难。所以我是来寻求帮助的。
  • 我建议您尝试实施其中一种解决方案,并带着您遇到的具体问题来到这里。这些类型的问题更容易回答。当然,我可以阅读其中一些论文,并为您实施其中一种方法,但我没有几天的空闲时间来免费这样做。我相信这是有道理的,不是吗?您将在这里得到的任何答案都将是次优的即兴解决方案。或者,您也许可以找到其中一篇论文的源代码,并且您可以随时尝试询问作者是否愿意分享。

标签: python image opencv image-processing


【解决方案1】:

我正在回复您在相关帖子上的标签。据我了解,您和另一所大学正在合作开展一个项目来定位皮肤上的痣?因为我想我已经在类似的问题上为你们中的一个或两个提供过帮助,并且已经提到去除头发是非常棘手和困难的任务。如果您删除图像上的头发,您会丢失信息并且您无法替换图像的那部分(没有程序或算法可以猜测头发下的内容 - 但它可以做出估计)。正如我在其他帖子中提到的那样,你可以做什么,我认为最好的方法是学习深度神经网络并自己制作脱毛。你可以谷歌“去水印深度神经网络”,看看我的意思。话虽如此,您的代码似乎并未提取您在示例图像中给出的所有 ROI(痣)。我举了另一个例子,说明如何更好地提取痣。基本上你应该在转换为二进制之前执行关闭,你会得到更好的结果。

对于第二部分 - 脱毛,如果您不想制作神经网络,我认为替代解决方案可能是,您计算包含痣的区域的平均像素强度。然后遍历每个像素,并就像素与平均值的差异制定某种标准。头发似乎呈现出比痣区域更暗的像素。因此,当您找到像素时,将其替换为不属于此标准的相邻像素。在这个例子中,我做了一个简单的逻辑,它不适用于每个图像,但它可以作为一个例子。要制定一个完全可操作的解决方案,您应该制定一个更好、更复杂的算法,我想这需要相当长的时间。希望它有点帮助!干杯!

import numpy as np
import cv2
from PIL import Image

# Read the image and perfrom an OTSU threshold
img = cv2.imread('skin2.png')
kernel = np.ones((15,15),np.uint8)

# Perform closing to remove hair and blur the image
closing = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel, iterations = 2)
blur = cv2.blur(closing,(15,15))

# Binarize the image
gray = cv2.cvtColor(blur,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)


# Search for contours and select the biggest one
_, contours, hierarchy =     cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)

# Create a new mask for the result image
h, w = img.shape[:2]
mask = np.zeros((h, w), np.uint8)

# Draw the contour on the new mask and perform the bitwise operation
cv2.drawContours(mask, [cnt],-1, 255, -1)
res = cv2.bitwise_and(img, img, mask=mask)

# Calculate the mean color of the contour
mean = cv2.mean(res, mask = mask)
print(mean)

# Make some sort of criterion as the ratio hair vs. skin color varies
# thus makes it hard to unify the threshold.
# NOTE that this is only for example and it will not work with all images!!!

if mean[2] >182:
    bp = mean[0]/100*35
    gp = mean[1]/100*35
    rp = mean[2]/100*35   

elif 182 > mean[2] >160:
    bp = mean[0]/100*30
    gp = mean[1]/100*30
    rp = mean[2]/100*30

elif 160>mean[2]>150:
    bp = mean[0]/100*50
    gp = mean[1]/100*50
    rp = mean[2]/100*50

elif 150>mean[2]>120:
    bp = mean[0]/100*60
    gp = mean[1]/100*60
    rp = mean[2]/100*60

else:
    bp = mean[0]/100*53
    gp = mean[1]/100*53
    rp = mean[2]/100*53

# Write temporary image
cv2.imwrite('temp.png', res)

# Open the image with PIL and load it to RGB pixelpoints
mask2 = Image.open('temp.png')
pix = mask2.load()
x,y = mask2.size

# Itearate through the image and make some sort of logic to replace the pixels that
# differs from the mean of the image
# NOTE that this alghorithm is for example and it will not work with other images

for i in range(0,x):
    for j in range(0,y):
        if -1<pix[i,j][0]<bp or -1<pix[i,j][1]<gp or -1<pix[i,j][2]<rp:
            try:
                pix[i,j] = b,g,r
            except:
                pix[i,j] = (int(mean[0]),int(mean[1]),int(mean[2]))
        else:
            b,g,r = pix[i,j]

# Transform the image back to cv2 format and mask the result         
res = np.array(mask2)
res = res[:,:,::-1].copy()
final = cv2.bitwise_and(res, res, mask=mask)

# Display the result
cv2.imshow('img', final)
cv2.waitKey(0)
cv2.destroyAllWindows()

【讨论】:

【解决方案2】:

您可以尝试以下步骤,至少可以获得正确实施解决方案的路线图:

  1. 使用自适应局部阈值查找头发区域 - Otsu 的 方法或任何其他方法。我认为“局部阈值”甚至 “局部直方图均衡,然后全局阈值”将 找到头发区域。
  2. 要填充头发区域,请使用“纹理合成”来合成皮肤 比如头发区域的纹理。

“A.A. Efros 和 T.K. Leung,通过非参数采样进行纹理合成”,在国际计算机视觉会议 (ICCV) 会议记录中,Kerkyra,希腊,1999 中描述了一种良好且简单的纹理合成方法。 纹理合成将提供比平均或中值滤波更好的结果来估计头发区域中的像素。

另外,看看这篇论文,它应该对你有很大帮助:

http://link.springer.com/article/10.1007%2Fs00521-012-1149-1?LI=true

【讨论】:

    猜你喜欢
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    • 2012-10-09
    • 2015-11-14
    • 1970-01-01
    相关资源
    最近更新 更多