如何消除乳房 X 光检查标签伪影

3 python opencv image image-processing scikit-image

我有一个乳房X线摄影图像数据集(迷你DDSM)。这些图像显示了指示左妈妈或右妈妈的字母伪影以及其他对我的 ML 模型无用的信息,因此我想在训练模型之前整理此数据集。

\n

在这篇论文《基于 Otsu\xe2\x80\x99s 阈值的数字乳房 X 光图像预处理》中,他们使用 Otsu 的二值化和对乳房 X 光检查的开放来清洁图像(第 5 页,共 10 页):

\n

他们的结果

\n

到目前为止,我已经编码了:

\n
im = io.imread(\'/content/drive/MyDrive/TFM/DDSMPNG/ALL2/0.jpg\')\n\n# thresholding\nthresh = im > filters.threshold_otsu(im)\n\n# opening with a disk structure\ndisk = morphology.disk(5)\nopened = morphology.binary_opening(thresh,disk)\n\n# plotting\n\nplt.figure(figsize=(10, 10))\n\nplt.subplot(131)\nplt.imshow(im,cmap=\'gray\')\nplt.subplot(132)\nplt.imshow(opened,cmap=\'gray\')\n\nplt.imsave(\'/content/drive/MyDrive/TFM/DDSMPNG/Blackened/0.jpg\',opened)\n
Run Code Online (Sandbox Code Playgroud)\n

这些是情节:

\n

结果

\n

我也尝试过用更高的圆盘形状来做开口,它似乎去除了更多白色的小字母伪影,但也裁剪了一点乳房X光检查:

\n
disk = morphology.disk(45)\nopened = morphology.binary_opening(thresh,disk)\n
Run Code Online (Sandbox Code Playgroud)\n

结果:

\n

圆盘形状的结果 (45,45)

\n

我想我必须创建某种带有二值化的掩模并将其应用到原始图像,但我是图像处理库的新手,我不确定如何实现结果

\n

编辑1:我尝试了@fmw42建议,但我有一些问题(我在Google Colab工作,不知道它是否有关系......):

\n

首先,在您的代码中使用图像作为示例,它似乎无法正常工作,不知道为什么,我复制了您的代码,只是修改了图像的路径以及一些子图来查看结果:

\n
# read image\nimg = cv2.imread(\'/content/drive/MyDrive/TFM/DDSMPNG/ALL2/0.jpg\')\nhh, ww = img.shape[:2]\n\n# convert to grayscale\ngray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n\n# apply otsu thresholding\nthresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU )[1] \n\n# apply morphology close to remove small regions\nkernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))\nmorph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)\n\n# apply morphology open to separate breast from other regions\nkernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))\nmorph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)\n\n# get largest contour\ncontours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\ncontours = contours[0] if len(contours) == 2 else contours[1]\nbig_contour = max(contours, key=cv2.contourArea)\n\n# draw largest contour as white filled on black background as mask\nmask = np.zeros((hh,ww), dtype=np.uint8)\ncv2.drawContours(mask, [big_contour], 0, 255, cv2.FILLED)\n\n# dilate mask\nkernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (55,55))\nmask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel)\n\n# apply mask to image\nresult = cv2.bitwise_and(img, img, mask=mask)\n\n# save results\n\ncv2.imwrite(\'/content/drive/MyDrive/TFM/DDSMPNG/Blackened/0.jpg\', result)\n\n# show resultls\n\nplt.figure(figsize=(10, 10))\n\nplt.subplot(141)\nplt.imshow(thresh,cmap=\'gray\')\nplt.subplot(142)\nplt.imshow(morph,cmap=\'gray\')\nplt.subplot(143)\nplt.imshow(mask,cmap=\'gray\')\nplt.subplot(144)\nplt.imshow(result,cmap=\'gray\')\n
Run Code Online (Sandbox Code Playgroud)\n

结果:

\n

在此输入图像描述

\n

其次,对于其余图像,它似乎对大多数图像都效果良好,但它会裁剪一点乳房表面:

\n

在此输入图像描述

\n

在你的结果图像中,它似乎更加平滑,我怎样才能实现这一点?

\n

提前致谢!

\n

编辑2:@fmw42解决方案工作正常,如果有人有同样的问题,您只需要使用形态滤波器的内核大小,直到图像的行为像他在答案上的结果一样。

\n

太感谢了!

\n

fmw*_*w42 5

这是在 Python/OpenCV 中处理图像的一种方法。

 - Read the input
 - Convert to grayscale
 - Otsu threshold
 - Morphology processing
 - Get largest contour from external contours
 - Draw all contours as white filled on a black background except the largest as a mask and invert mask
 - Apply the mask to the input image
 - Save the results
Run Code Online (Sandbox Code Playgroud)

输入:

在此输入图像描述

import cv2
import numpy as np

# read image
img = cv2.imread("mammogram.png")
hh, ww = img.shape[:2]

# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# apply otsu thresholding
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU )[1] 

# apply morphology close to remove small regions
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# apply morphology open to separate breast from other regions
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)

# apply morphology dilate to compensate for otsu threshold not getting some areas
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (29,29))
morph = cv2.morphologyEx(morph, cv2.MORPH_DILATE, kernel)

# get largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
big_contour_area = cv2.contourArea(big_contour)

# draw all contours but the largest as white filled on black background as mask
mask = np.zeros((hh,ww), dtype=np.uint8)
for cntr in contours:
    area = cv2.contourArea(cntr)
    if area != big_contour_area:
        cv2.drawContours(mask, [cntr], 0, 255, cv2.FILLED)
    
# invert mask
mask = 255 - mask

# apply mask to image
result = cv2.bitwise_and(img, img, mask=mask)

# save results
cv2.imwrite('mammogram_thresh.jpg', thresh)
cv2.imwrite('mammogram_morph.jpg', morph)
cv2.imwrite('mammogram_mask.jpg', mask)
cv2.imwrite('mammogram_result.jpg', result)

# show resultls
cv2.imshow('thresh', thresh)
cv2.imshow('morph', morph)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

阈值图像:

在此输入图像描述

形态学处理图像:

在此输入图像描述

从轮廓蒙版图像:

在此输入图像描述

结果图像:

在此输入图像描述

备用

- Read the input
- Convert to grayscale
- Otsu threshold
- Morphology processing
- Get largest contour from external contours
- Draw largest as white filled on black background as a mask 
- Dilate mask
- Apply the mask to the input image
- Save the results
Run Code Online (Sandbox Code Playgroud)

输入:

import cv2
import numpy as np

# read image
img = cv2.imread("mammogram.png")
hh, ww = img.shape[:2]

# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# apply otsu thresholding
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU )[1] 

# apply morphology close to remove small regions
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# apply morphology open to separate breast from other regions
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)

# get largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# draw largest contour as white filled on black background as mask
mask = np.zeros((hh,ww), dtype=np.uint8)
cv2.drawContours(mask, [big_contour], 0, 255, cv2.FILLED)

# dilate mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (55,55))
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel)

# apply mask to image
result = cv2.bitwise_and(img, img, mask=mask)

# save results
cv2.imwrite('mammogram_thresh.jpg', thresh)
cv2.imwrite('mammogram_morph2.jpg', morph)
cv2.imwrite('mammogram_mask2.jpg', mask)
cv2.imwrite('mammogram_result2.jpg', result)

# show resultls
cv2.imshow('thresh', thresh)
cv2.imshow('morph', morph)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

阈值图像:

在此输入图像描述

形态学处理图像:

在此输入图像描述

蒙版图像:

在此输入图像描述

结果:

在此输入图像描述

添加

这是应用于较大 JPG 图像的第二种处理方法。我注意到它的宽度和高度大约是 6 倍。因此,我将形态内核从 5 个增加到 31 个,增加了约 6 倍。我还将图像边框周围修剪了 40 个像素,然后添加了相同数量的黑色边框。

输入:

在此输入图像描述

import cv2
import numpy as np

# read image
img = cv2.imread("mammogram.jpg")
hh, ww = img.shape[:2]

# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# shave 40 pixels all around
gray = gray[40:hh-40, 40:ww-40]

# add 40 pixel black border all around
gray = cv2.copyMakeBorder(gray, 40,40,40,40, cv2.BORDER_CONSTANT, value=0)

# apply otsu thresholding
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU )[1] 

# apply morphology close to remove small regions
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (31,31))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)

# apply morphology open to separate breast from other regions
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (31,31))
morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel)

# get largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# draw largest contour as white filled on black background as mask
mask = np.zeros((hh,ww), dtype=np.uint8)
cv2.drawContours(mask, [big_contour], 0, 255, cv2.FILLED)

# dilate mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (305,305))
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel)

# apply mask to image
result = cv2.bitwise_and(img, img, mask=mask)

# save results
cv2.imwrite('mammogram_thresh.jpg', thresh)
cv2.imwrite('mammogram_morph2.jpg', morph)
cv2.imwrite('mammogram_mask2.jpg', mask)
cv2.imwrite('mammogram_result2.jpg', result)

# show resultls
cv2.imshow('thresh', thresh)
cv2.imshow('morph', morph)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)

阈值图像:

在此输入图像描述

形态图像:

在此输入图像描述

蒙版图像:

在此输入图像描述

结果:

在此输入图像描述