使用Python图像处理分割生物样本照片,提取圆形感兴趣区域。

3

我在一些生物样本的照片中遇到了分割完成的问题,我正在尝试使用图像处理分析细菌的生长情况,理论上应该有效。这是我拥有的原始图像之一:

biofilm of pseudomona

我正在尝试分割圆形内部区域,并查看像素值随时间变化的情况。由于我相对较新于分析这种样本,因此尝试了许多技术。最初我使用opencv,但没有得到想要的结果,所以现在我使用scikit-image进行所有图像处理和分割技术。 以下是我目前拥有的代码:
from skimage import morphology, exposure, io, filters
from scipy import ndimage as ndi
from skimage.color import rgb2gray, label2rgb
from skimage.filters import sobel, rank
import matplotlib.pyplot as plt
y1=400
y2=1600
x1=700
x2=1900
test_img = io.imread(folders_path+hour_tested[0]+'5.jpg')
roi_test = test_img[y1:y2, x1:x2,:]
gray_img = rgb2gray(roi_test)
denoised_img = rank.median(gray_img, morphology.disk(5))
val = filters.threshold_otsu(denoised_img)
mask = denoised_img > val
elevation_map=sobel(denoised_img)
segmentation = morphology.watershed(elevation_map, mask=mask)
labeled_bio, num_seg = ndi.label(segmentation)
image_label_overlay = label2rgb(labeled_bio, image=gray_img)
plt.imshow(image_label_overlay)
plt.show()

在最后一行,我可以通过不同颜色来分割样本的区域,并将我想要分析的部分放入一个标签中。现在我不知道如何继续下去,或者至少如何查看该标签,然后创建一个掩模。
我还分享了标记图像,供任何人查看并可能在下一步中帮助我,我感觉我已经接近或者离我的感兴趣的区域很远并且感到困惑。
好了,这是样本的标记图像:

segmented area with labels

2个回答

0

这里介绍一种使用简单图像处理技术的方法

  • 获取二值图像。 加载图像,转换为灰度图像,然后使用Otsu阈值获取二值图像

  • 执行形态学操作。 我们创建一个椭圆形状的核,然后执行形态学闭运算以填充轮廓

  • 隔离感兴趣区域。 我们找到轮廓并使用轮廓逼近+轮廓面积进行过滤。一旦我们隔离出轮廓,就找到一个最小外接圆来获得一个完美的圆,然后将其绘制在一个空白掩模上。获得完美圆的想法来自于如何修改掩模以制作完美圆

  • 隔离ROI。 我们在掩模上找到边界矩形ROI,然后使用Numpy切片进行裁剪

  • 位与运算以获得结果。 最后,我们对提取的两个ROI进行位与运算


以下是每个步骤的可视化图像:
输入图像

二进制图像

形态学闭运算

在空白掩模上突出显示绿色的感兴趣区域,并绘制填充轮廓

孤立的 ROIs

按位与运算得到结果(两个版本,一个黑色背景,一个白色背景,取决于您想要什么)

代码

import cv2
import numpy as np

# Load image, create blank mask, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
original = image.copy()
mask = np.zeros(image.shape, dtype=np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Morph close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=5)

# Find contours and filter using contour area + contour approximation
# Determine perfect circle contour then draw onto blank mask
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.04 * peri, True)
    area = cv2.contourArea(c)
    if len(approx) > 4 and area > 10000 and area < 500000:
        ((x, y), r) = cv2.minEnclosingCircle(c)
        cv2.circle(mask, (int(x), int(y)), int(r), (255, 255, 255), -1)
        cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 3)

# Extract ROI
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
x,y,w,h = cv2.boundingRect(mask)
mask_ROI = mask[y:y+h, x:x+w]
image_ROI = original[y:y+h, x:x+w]

# Bitwise-and for result
result = cv2.bitwise_and(image_ROI, image_ROI, mask=mask_ROI)
result[mask_ROI==0] = (255,255,255) # Color background white

cv2.imwrite('close.png', close)
cv2.imwrite('thresh.png', thresh)
cv2.imwrite('image.png', image)
cv2.imwrite('mask.png', mask)
cv2.imwrite('result.png', result)
cv2.waitKey()

注意:确定兴趣区的另一种方法是使用已经实现的霍夫圆变换,即cv2.HoughCircles(),但由于有很多参数,因此可能不是最实用的方法。


我有一个问题,当我使用相同的图像尝试代码时,我最终得到一个完全黑色的掩模和没有分割。我看到问题出现在for语句中,我是否需要更改peri、approx和area中的参数?此外,如果我有更多样本图像,这将对大多数图像起作用吗?还是我必须为每个图像调整代码。谢谢,不想打扰,但我想了解代码如何工作,然后它没有工作。 - Jose Zapana
你使用的OpenCV版本是哪个?我使用的是3.4.6。 - Jose Zapana
我正在使用 python 3.7.4numpy==1.14.5opencv-python==4.1.0.25。您可能需要更改下限/上限区域的值,但它应该适用于大多数图像。 - nathancy

0

修复代码后,生物膜分割的正确答案如下:

import cv2
import numpy as np
import os
def resize_image(image, percentage):
   scale_percent=percentage
   width = int(image.shape[1] * scale_percent/100)
   height= int(image.shape[0] * scale_percent/100)
   dimensions = (width, height)
   resized = cv2.resize(image, dimensions, interpolation = cv2.INTER_AREA)
   return resized
#this path is changed for each image in the DB
path=folders_path+hour_tested[0]+'1.jpg'
image = cv2.imread(path)
s_image = resize_image(image,50)
original = s_image.copy()
mask = np.zeros(s_image.shape, dtype=np.uint8)
gray = cv2.cvtColor(s_image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
# Morph close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
close = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=3)
# Find contours and filter using contour area + contour approximation
# Determine perfect circle contour then draw onto blank mask
im,cnts,hierarchy = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in cnts:
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.04*peri, True)
    area = cv2.contourArea(c)
    if len(approx) > 4 and (area > 8000 and area < 250000) and (peri<2000 and peri>1000):
        ((x, y), r) = cv2.minEnclosingCircle(c)
        x,y,r = int(x),int(y),int(r)
        blank_circle=cv2.circle(mask, (x, y), r, (255, 255, 255), -1)
        filled_circle=cv2.circle(s_image, (x, y), r, (36, 255, 12), 3)
        # Extract ROI
        mask_ROI = blank_circle[y-r:y+r, x-r:x+r]
        mask_ROI = cv2.cvtColor(mask_ROI, cv2.COLOR_BGR2GRAY)
        image_ROI = filled_circle[y-r:y+r, x-r:x+r]
        result = cv2.bitwise_and(image_ROI, image_ROI, mask=mask_ROI)
        cv2.imwrite('result.png',result)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接