我正在从ImageJ转到用于对显示粒子(无论是在长凳上还是在倒下)的图像进行图像处理的图像处理,并试图编写我的第一个代码,用于1)打开粒子图像,2)应用阈值和降噪来获得二值图像,3)应用分水岭来分离重叠的粒子,以及4)测量检测到的区域以获得关于粒子大小分布的信息(例如,面积、周长、轴).

我现在正在处理附加的图像,让事情变得简单.它是一串直径从0.5毫米到1毫米的颗粒放在长椅上.

Particles 0.5-1 mm

现在,我用Python编写了这段代码,它使用OpenCV和skImage函数的混合来创建和打磨二进制图像,应用分水岭(上面的例子不需要,但对于future -我将高速拍摄坠落的粒子-它将非常有用),并测量识别出的细胞.

编辑:附上了第二张图片,我有同样的问题,但针对较小的颗粒(0.250-0.50毫米,但分水岭很重要)‘

Smaller particles where I need watershed

EDIT2:显示导致该问题的主要处理过程的较短代码.

import cv2
import numpy as np
import pandas as pd
import os
from skimage.segmentation import watershed, clear_border
from skimage import measure, color, io, morphology

# Define the folder path containing the images
image = "C:/..."

# Set scale pixels/mm (either based on ImageJ or by reference image, or calculations)
px_per_mm = 222
mm_per_px = 1 / px_per_mm
img = cv2.imread(image)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#Threshold and remove noise
thresholded_img =  cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

kernel = np.ones((5, 5),np.uint8)
image = cv2.morphologyEx(thresholded_img, cv2.MORPH_OPEN, kernel)
image = clear_border(image)

# Prepare for Watershed
sure_bg = cv2.dilate(image,kernel, iterations=4)
dist_transform = cv2.distanceTransform(image, cv2.DIST_L2, 0)
ret2, sure_fg = cv2.threshold(dist_transform, 0.4 * dist_transform.max(), 255, 0)
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg, sure_fg)
ret3, markers = cv2.connectedComponents(sure_fg, connectivity=8)
markers = markers + 10
markers[unknown == 255] = 0

# Now we are ready for watershed filling.
markers = cv2.watershed(img, markers)

#Measure properties
label_image = measure.label(markers,background=255, connectivity=None)
props = measure.regionprops_table(label_image, image,
                          properties=['label', 'Area'])

# Scale properties from pixels to mm
props['Area'] = props['Area'] * mm_per_px ** 2

# Remove the image frame that is usually identified as a region
max_area_threshold = 700
props_df = pd.DataFrame(props)
filtered_props = props_df[props_df['Area'] <= max_area_threshold]
# Convert the filtered properties to a DataFrame
particle_analysis = pd.DataFrame(filtered_props)

现在,当前代码给出了粒子区域的分布:

Area distribution

这在很大程度上是正确的,但显示了0.0-0.5箱中的许多区域,一旦消除了噪声和小颗粒,这些区域就不会出现在图像中. 使用ImageJ,我会得到类似的结果,但没有巨大的初始bin: ImageJ results

如果我判断二值图像、分水岭等,一切似乎都很好,所以不知道为什么要检测到如此小的区域.它发生在我用来测试代码的所有图像上.

我们非常欢迎在识别问题方面的任何帮助!非常感谢!

我根据OpenCV和SkImage上的教程编写了代码,并测试了几种阈值处理、降噪、形态操作和改变区域测量函数的参数,试图在分析中仅检测真实的颗粒.然而,我总是得到初始绑定,显然区域的特征是非常小的区域

推荐答案

这条线

image = cv2.morphologyEx(thresholded_img, cv2.MORPH_OPEN, kernel)

删除小对象,并平滑较大对象的边界.但被移除的物体比5x5像素的正方形小,也就是0.0005 mm?你正在复制的ImageJ代码以某种方式过滤掉了更大的颗粒.当我查看小于0.1mm2的粒子的大小分布时,我看到的东西可能是从对数正态分布中得出的,粒子的范围在0.006-0.015之间.因此,您可以删除小于0.02 mm?(~31x31像素)的所有粒子.

您可以通过两种方式实现这一点:

  1. 在开场使用更大的 struct 元素.这是有问题的,因为OpenCV处理较大的 struct 元素时速度非常慢,而且较大的开口将扭曲较大对象的区域.如果你走这条路,你应该使用圆形的 struct 元素,而不是正方形.
  2. 然后过滤出较小的粒子,就像处理较大的对象一样.只需将最小面积阈值添加到此行:
filtered_props = props_df[props_df['Area'] <= max_area_threshold]

还有第三种 Select ,那就是这些更小的颗粒实际上是你的样本的一部分,你可以测量它们……


*我还建议您使用DIPlib而不是OpenCV,对于较大的 struct 元素,它的打开速度要快得多.但我有偏见,因为我深深地参与了那个项目.

Python相关问答推荐

将HLS纳入媒体包

在Python和matlab中显示不同 colored颜色 的图像

2维数组9x9,不使用numpy.数组(MutableSequence的子类)

对Numpy函数进行载体化

在Google Colab中设置Llama-2出现问题-加载判断点碎片时Cell-run失败

如何找到满足各组口罩条件的第一行?

Pandas - groupby字符串字段并按时间范围 Select

管道冻结和管道卸载

Stacked bar chart from billrame

在pandas中使用group_by,但有条件

Asyncio:如何从子进程中读取stdout?

不允许访问非IPM文件夹

python中的解释会在后台调用函数吗?

在Python 3中,如何让客户端打开一个套接字到服务器,发送一行JSON编码的数据,读回一行JSON编码的数据,然后继续?

如何杀死一个进程,我的Python可执行文件以sudo启动?

如何从pandas DataFrame中获取. groupby()和. agg()之后的子列?

BeautifulSoup:超过24个字符(从a到z)的迭代失败:降低了首次深入了解数据集的复杂性:

如何在Python Pandas中填充外部连接后的列中填充DDL值

jsonschema日期格式

为罕见情况下的回退None值键入