使用 Python 图像处理分割生物样本的照片以提取感兴趣的圆形区域
Segment photos of bio-samples to extract circular area of interest with Python image processing
我在完成一些生物样本照片的分割时遇到问题,我正在尝试通过图像处理分析细菌的生长,理论上它应该有效。这是我的原始图像之一:
我正在尝试分割圆圈内的区域并查看像素值如何随着时间的推移而变化。我一直在尝试很多技术,因为我对分析这类 samples.Initially 比较陌生,我使用的是 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()
在最后一行,我用不同的颜色对样本区域进行了分割,并在一个标签中得到了我想要分析的部分,现在我不知道如何继续或至少不知道如何继续查看该标签,然后创建一个遮罩。
我还分享了带标签的图像,供任何人查看并可能在接下来的步骤中帮助我,我觉得或者我真的很接近分割我感兴趣的区域,或者真的很远而且很困惑。
这是样本的标记图像:
这是一种使用简单图像处理技术的方法
得到二值图像加载图像,转灰度,然后Otsu的阈值得到二值图像
执行形态学操作。我们创建一个椭圆形内核,然后执行变形接近填充轮廓
隔离感兴趣区域。我们找到轮廓并使用轮廓近似+轮廓区域进行过滤。一旦我们隔离了轮廓,找到一个最小的封闭圆以获得一个完美的圆,然后将其绘制到空白蒙版上。获得正圆的思路来自
隔离 ROI。 我们在蒙版上找到边界矩形 ROI,然后使用 Numpy 切片进行裁剪
按位与得到结果。最后我们按位与提取的两个ROI
这是每个步骤的可视化:
输入图片
二进制图像
变形关闭
孤立的感兴趣区域以绿色突出显示,填充轮廓绘制到空白蒙版上
孤立的 ROI
结果按位与(两种版本,一种是黑底,一种是白底,看你想要什么)
代码
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()
但是有很多参数所以它可能不是最实用的方法。
修正代码后,这是生物膜分割的正确答案:
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)
我在完成一些生物样本照片的分割时遇到问题,我正在尝试通过图像处理分析细菌的生长,理论上它应该有效。这是我的原始图像之一:
我正在尝试分割圆圈内的区域并查看像素值如何随着时间的推移而变化。我一直在尝试很多技术,因为我对分析这类 samples.Initially 比较陌生,我使用的是 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()
在最后一行,我用不同的颜色对样本区域进行了分割,并在一个标签中得到了我想要分析的部分,现在我不知道如何继续或至少不知道如何继续查看该标签,然后创建一个遮罩。
我还分享了带标签的图像,供任何人查看并可能在接下来的步骤中帮助我,我觉得或者我真的很接近分割我感兴趣的区域,或者真的很远而且很困惑。
这是样本的标记图像:
这是一种使用简单图像处理技术的方法
得到二值图像加载图像,转灰度,然后Otsu的阈值得到二值图像
执行形态学操作。我们创建一个椭圆形内核,然后执行变形接近填充轮廓
隔离感兴趣区域。我们找到轮廓并使用轮廓近似+轮廓区域进行过滤。一旦我们隔离了轮廓,找到一个最小的封闭圆以获得一个完美的圆,然后将其绘制到空白蒙版上。获得正圆的思路来自
隔离 ROI。 我们在蒙版上找到边界矩形 ROI,然后使用 Numpy 切片进行裁剪
按位与得到结果。最后我们按位与提取的两个ROI
这是每个步骤的可视化:
输入图片
二进制图像
变形关闭
孤立的感兴趣区域以绿色突出显示,填充轮廓绘制到空白蒙版上
孤立的 ROI
结果按位与(两种版本,一种是黑底,一种是白底,看你想要什么)
代码
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()
但是有很多参数所以它可能不是最实用的方法。
修正代码后,这是生物膜分割的正确答案:
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)