在二进制图像中测量大颗粒时检测到太多微小区域。

huangapple go评论71阅读模式
英文:

Detection of too many tiny areas when measuring large particles in binary images

问题

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

我目前正在处理附加的图像,以保持事情简单。这是一堆直径从0.5到1毫米的颗粒物,放在一个台子上。

颗粒物 0.5-1 mm

现在,我用Python编写了这段代码,它使用了opencv和skimage函数的混合来创建和改进二值图像,应用分水岭算法(对于上面的示例不需要,但对于将来 - 在那里我将以高速拍摄下落的颗粒物 - 它将非常有用),并测量识别出的细胞。

编辑:附上了第二张图像,我遇到了相同的问题,但对于更小的颗粒物(0.250-0.5毫米),分水岭是重要的。

需要分水岭的较小颗粒物

编辑2:缩短的代码以显示导致问题的主要处理过程。

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

# 定义包含图像的文件夹路径
image = "C:/..."

# 设置像素/毫米的比例(可以基于ImageJ、参考图像或计算得出)
px_per_mm = 222
mm_per_px = 1 / px_per_mm
img = cv2.imread(image)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 阈值化并去噪声
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)

# 准备分水岭
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

# 现在我们准备进行分水岭填充。
markers = cv2.watershed(img, markers)

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

# 将属性从像素转换为毫米
props['Area'] = props['Area'] * mm_per_px ** 2

# 删除通常被识别为一个区域的图像框架
max_area_threshold = 700
props_df = pd.DataFrame(props)
filtered_props = props_df[props_df['Area'] <= max_area_threshold]
# 将筛选后的属性转换为DataFrame
particle_analysis = pd.DataFrame(filtered_props)

现在,当前代码给我提供了以下颗粒物面积的分布:

面积分布

它大部分是正确的,但显示了很多0.0-0.5范围内的区域,这些区域在去噪声和小颗粒物消除后实际上不存在。
使用ImageJ,我会获得类似的结果,但没有巨大的初始范围:
ImageJ结果

如果我检查二值图像、分水岭等,一切都似乎都正常,所以不知道为什么会检测到这么小的区域。这种情况在我用来测试代码的所有图像上都会发生。
帮助我找出问题的原因将不胜感激!非常感谢!

我根据opencv和skimage的教程编写了这段代码,并尝试了多种阈值化、去噪声、形态学操作和更改测量区域函数的参数的方法,希望只检测到分析中的真实颗粒物。然而,我总是会得到具有非常小区域的初始范围。

英文:

I am moving from ImageJ to Python for doing image processing of images showing particles - either on a bench or falling - and trying to write my first code for 1) opening images of particles, 2) applying thresholding and noise reduction to obtain binary images, 3) apply watershed to separate overlapping particles, and 4) measure the detected regions to get information on particle size distributions (e.g., area, perimeter, axes).

I am working on the attached image for now, to keep things simple. It's a bunch of particles from 0.5 to 1 mm in diameter on a bench.

Particles 0.5-1 mm

Now, I wrote this code in Python that uses a mix of opencv and skimage functions to create and polish binary images, apply watershed (Not needed for the example above, but for the future - where I will image falling particles at high-speed - it'll be very useful), and measure the identified cells.

Edit: attached a second image where I have the same issue, but for smaller particles (0.250-0.5 mm, but where watershed is important)'

Smaller particles where I need watershed

EDIT2: Shorter code to show the main processing leading to the issue.

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 = &quot;C:/...&quot;

# 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=[&#39;label&#39;, &#39;Area&#39;])

# Scale properties from pixels to mm
props[&#39;Area&#39;] = props[&#39;Area&#39;] * 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[&#39;Area&#39;] &lt;= max_area_threshold]
# Convert the filtered properties to a DataFrame
particle_analysis = pd.DataFrame(filtered_props)

Now, the current code gives me this distribution for the particles area:

Area distribution

It is mostly correct, but showing a lot of areas in the 0.0-0.5 bin, that are not present in the image once the noise and the small particulate is eliminated.
With ImageJ, I would get similar results but without the huge initial bin:
ImageJ results

If I check the binary image, watershed etc, it all seem fine, so no idea why is detecting such small regions. It happens with all the images I am using for testing the code.

Any help in identifying the issue is most welcome! Many thanks!

I wrote the code based on tutorials on opencv and skimage, and tested several ways of thresholding, reducing noise, morphology operations and changing parameters of the functions for measuring the regions, trying to only detect the real particles in the analysis. However, I always get that initial bin with apparently regions characterized by very small areas

答案1

得分: 0

该行代码移除小目标,平滑较大目标的边界。但被移除的目标小于5x5像素,即0.0005平方毫米。你正在复制的ImageJ代码在某种程度上过滤掉了更大的颗粒。当我查看小于0.1平方毫米的颗粒的大小分布时,我看到它可能是从对数正态分布中绘制出来的,颗粒的范围是0.006-0.015。因此,你可以移除所有小于约0.02平方毫米(约31x31像素)的颗粒。

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

  1. 在开操作中使用一个更大的结构元素。这是有问题的,因为OpenCV在处理较大的结构元素时速度很慢,而且较大的开操作会扭曲较大目标的区域。如果选择这种方式,应该使用一个圆形的结构元素,而不是方形的。
  2. 在之后过滤掉较小的颗粒,就像你对较大目标所做的那样。只需在这一行中添加一个最小面积阈值:
filtered_props = props_df[props_df['Area'] <= max_area_threshold]

还有第三种选择,即这些较小的颗粒实际上是样本的一部分,你应该对它们进行测量...


* 我建议你使用 DIPlib 而不是OpenCV,它的开操作在处理较大结构元素时速度更快。但我持有偏见,因为我对那个项目有深入的参与。

英文:

The line

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

removes small objects, and smooths out the boundary of larger objects. But the objects being removed are smaller than a 5x5 pixel square, which is 0.0005 mm². The ImageJ code you're replicating somehow filters out much larger particles. When I look at the size distribution for the particles smaller than 0.1 mm², I see something that likely is drawn from a log-normal distribution, with particles in the range 0.006-0.015. So you could remove all particles smaller than, say 0.02 mm² (~31x31 pixels).

You can accomplish that in two ways:

  1. Use a much larger structuring element in the opening. This is problematic because OpenCV is very slow with larger structuring elements, and because a larger opening will distort the area of larger objects. If you go this route, you should use a circular structuring element, not a square one.*
  2. Filter out smaller particles afterwards, like you do with larger objects. Just add a minimum area threshold to this line:
filtered_props = props_df[props_df[&#39;Area&#39;] &lt;= max_area_threshold]

There's a third option, which is that these smaller particles are actually part of your sample and you should be measuring them...


<sup>* I would also suggest you use DIPlib instead of OpenCV, it's opening is much faster with larger structuring elements. But I'm biased because I'm quite deeply involved in that project.</sup>

huangapple
  • 本文由 发表于 2023年6月15日 21:20:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/76482929.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定