如何标记和测量斑点的大小? [英] How to label and measure size of blobs?

查看:62
本文介绍了如何标记和测量斑点的大小?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习使用Python进行图像分析,我只是一个初学者.我能够编写代码(在下面与我分享)来检测此纳米粒子图像中的斑点(纳米粒子):

我可以使用 cv2.connectedComponents 检测到10个纳米粒子,但是现在我需要:

  1. 用数字标记每个纳米粒子以生成最终图像.

  2. 计算组成每个纳米粒子的像素数,以便我确定其尺寸.

我尝试研究一下,但是找不到任何对我有用的东西.有人愿意帮助我吗?如果您可以提出一个代码,那就太好了,如果您也可以解释它,那就太好了!

 将numpy导入为np导入cv2从matplotlib导入pyplot作为pltimg = cv2.imread('Izzie--0002.tif')#使用matplotlib显示图plt.figure(1)plt.subplot(2,2,1)#图1有子图2个raws,2列,这是图1plt.gca().set_title('Original')plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))#,cmap ='gray'灰色= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)plt.figure(1)plt.subplot(2,2,2)#图1具有子图2 raw,2列,这是图2plt.gca().set_title('灰色')plt.imshow(cv2.cvtColor(灰色,cv2.COLOR_BGR2RGB))#,cmap ='灰色'#在全局阈值化(常规方法)中,我们使用任意选择的值作为阈值#相反,大津的方法#避免选择一个值并自动确定它.#该方法返回两个输出.第一个是使用的阈值,第二个是#输出为阈值图像.ret,thresh = cv2.threshold(灰色,0、255,cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)print('Ret =',ret)#应用任意阈值128plt.figure(1)plt.subplot(2,2,3)plt.gca().set_title('Threshold')plt.imshow(cv2.cvtColor(阈值,cv2.COLOR_BGR2RGB))#-------------------------------------------------------------------------------------------#形态转变#使用形态学变形去除噪声#有关更多信息,请参见:https://opencv-pythontutroals.readthedocs.io/zh-CN/latest/py_tutorials/py_imgproc/py_morphologic_ops/py_morphologic_ops.html#设置内核-结构元素内核= np.ones((3,3),np.uint8)#数据类型为8字节的1s的3x3数组#使用开孔消除噪音(侵蚀然后膨胀)开头= cv2.morphologyEx(thresh,cv2.MORPH_OPEN,内核,迭代次数= 4)plt.figure(2)plt.subplot(2,2,1)plt.gca().set_title('Noise rem')plt.imshow(cv2.cvtColor(正在打开,cv2.COLOR_BGR2RGB))#确定背景区域#膨胀运算sure_bg = cv2.dilate(opening,kernel,iterations = 3)plt.figure(2)plt.subplot(2,2,2)plt.gca().set_title('Diimg img')plt.imshow(cv2.cvtColor(sure_bg,cv2.COLOR_BGR2RGB))#应用距离转换将图像转换为黑白像素的渐变并检测可能的连接对象dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)plt.figure(2)plt.subplot(2,2,3)plt.gca().set_title('Dist_transform')plt.imshow(cv2.cvtColor(dist_transform,cv2.COLOR_BGR2RGB))#应用阈值以返回到黑白图像ret,sure_fg = cv2.threshold(dist_transform,0.7 * dist_transform.max(),255,0)print('Ret treshold:',ret)plt.figure(2)plt.subplot(2,2,4)plt.gca().set_title('Threshold')plt.imshow(cv2.cvtColor(sure_fg,cv2.COLOR_BGR2RGB))#寻找未知地区sure_fg = np.uint8(sure_fg)#创建一个8位无符号矩阵plt.figure(3)plt.subplot(1、2、1)plt.gca().set_title('Sure_fg')plt.imshow(cv2.cvtColor(sure_fg,cv2.COLOR_BGR2RGB))未知= cv2.subtract(sure_bg,sure_fg)plt.figure(3)plt.subplot(1、2、2)plt.gca().set_title('未知')plt.imshow(cv2.cvtColor(未知,cv2.COLOR_BGR2RGB))#----------------------------------------------------------------------------------------------------------------------##标记标签#连接的组件会计算图像中的所有黑色物体.有关说明,请参见:https://www.youtube.com/watch?v=hMIrQdX4BkE#它会返回2个对象,对象数量和带有标签对象的图片.n_objects,标记= cv2.connectedComponents(sure_fg)plt.figure(4)plt.subplot(2,1,1)plt.gca().set_title('markers')plt.imshow(标记)#在所有标签上添加一个,以确保背景不是0,而是1标记=标记+1#现在,将未知区域标记为零markers [unknown == 255] = 0标记= cv2.watershed(img,标记)img [markers == 8] = [255,0,0]#将红色圆圈(-1 val)覆盖到img.2、3、4是图像中检测到的所有不同物体plt.figure(4)plt.subplot(2,1,2)plt.gca().set_title('markers')plt.imshow(img)print('检测到的粒子数:',n_objects-2)plt.show() 

解决方案

如果您的粒子(几乎)是黑色,请不要使用Otsu的阈值,而应使用固定值来掩盖(几乎)黑色像素.然后,在逆二值化图像上,可以应用形态学闭合(以获取整个粒子)和打开(以消除背景噪声),请参见

img_mop 图片,应用了形态学操作后(注意:比例尺仍然存在,因此我们可以将其用于尺寸近似):

最后,输入/输出图像ìmg及其相应的标签(由于图像尺寸的限制,此处必须使用JPG):

最后但并非最不重要的 print 输出:

  Part 0 |水平直径:20.83 nm,垂直直径:23.03 nm粒子1 |水平直径:20.83 nm,垂直直径:20.83 nm粒子2 |水平直径:19.74 nm,垂直直径:17.54 nm粒子3 |水平直径:23.03 nm,垂直直径:23.03 nm粒子4 |水平直径:24.12 nm,垂直直径:24.12 nm粒子5 |水平直径:21.93 nm,垂直直径:20.83 nm粒子6 |水平直径:24.12 nm,垂直直径:23.03 nm粒子7 |水平直径:21.93 nm,垂直直径:23.03 nm粒子8 |水平直径:19.74 nm,垂直直径:21.93 nm粒子9 |水平直径:19.74 nm,垂直直径:19.74 nm 

希望有帮助!

I am learning image analysis with Python, I am just a beginner. I was able to write a code (I am sharing it below) to detect the blobs (nanoparticles) in this nanoparticle image:

I can detect that there are 10 nanoparticles using cv2.connectedComponents, but now I need to:

  1. Label each nanoparticle with a number to generate a final image.

  2. Calculate the number of pixels that composes each nanoparticle so that I can determine their size.

I tried to research around, but couldn't find anything that works for me. Anyone that is willing help me? If you can propose a code it would be great, if you could also explain it it, it would be super!

import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    img = cv2.imread('Izzie -  - 0002.tif')

    #show figure using matplotlib
    plt.figure(1)
    plt.subplot(2, 2, 1) # Figure 1 has subplots 2 raws, 2 columns, and this is plot 1
    plt.gca().set_title('Original')
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # , cmap='gray'

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    plt.figure(1)
    plt.subplot(2, 2, 2) # Figure 1 has subplots 2 raw, 2 columns, and this is plot 2
    plt.gca().set_title('Gray')
    plt.imshow(cv2.cvtColor(gray, cv2.COLOR_BGR2RGB)) # , cmap='gray'


# In global thresholding (normal methods), we used an arbitrary chosen value as a threshold
    # In contrast, Otsu's method
    # avoids having to choose a value and determines it automatically.
    #The method returns two outputs. The first is the threshold that was used and the secon
    # output is the thresholded image.

ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

print('Ret = ', ret) # Applies an arbitrary threshold of 128

plt.figure(1)
    plt.subplot(2, 2, 3)
    plt.gca().set_title('Threshold')
    plt.imshow(cv2.cvtColor(thresh, cv2.COLOR_BGR2RGB))


#-------------------------------------------------------------------------------------------
    # MORPHOLOGICAL TRANSFORMATION
    # noise removal using morphological trasnformations
    # For more info see: https://opencv-python
tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html

    # Set up the kernel - structuring element
    kernel = np.ones((3,3), np.uint8) # 3x3 array of 1s of datatype 8-bytes

    # Remove noise using Opening (erosion followed by dilation)
    opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 4)
    plt.figure(2)
    plt.subplot(2, 2, 1)
    plt.gca().set_title('Noise rem')
    plt.imshow(cv2.cvtColor(opening, cv2.COLOR_BGR2RGB))


    # sure background area
    # dilation operation
    sure_bg = cv2.dilate(opening,kernel,iterations=3)

    plt.figure(2)
    plt.subplot(2, 2, 2)
    plt.gca().set_title('Dilated img')
    plt.imshow(cv2.cvtColor(sure_bg, cv2.COLOR_BGR2RGB))



    # Apply a distance transformation to transform the image into a gradient of B&W pixels and detect possible connected objects
    dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)

    plt.figure(2)
    plt.subplot(2, 2, 3) 
    plt.gca().set_title('Dist_transform')
    plt.imshow(cv2.cvtColor(dist_transform, cv2.COLOR_BGR2RGB))



    # Apply a threshold to go back to binary B&W image
    ret, sure_fg = cv2.threshold(dist_transform, 0.7 * dist_transform.max(),255,0)
    print('Ret treshold: ', ret)

    plt.figure(2)
    plt.subplot(2, 2, 4) 
    plt.gca().set_title('Threshold')
    plt.imshow(cv2.cvtColor(sure_fg, cv2.COLOR_BGR2RGB))


    # Finding unknown region
    sure_fg = np.uint8(sure_fg) # creates an 8-bit unsigned matrix

    plt.figure(3)
    plt.subplot(1, 2, 1) 
    plt.gca().set_title('Sure_fg')
    plt.imshow(cv2.cvtColor(sure_fg, cv2.COLOR_BGR2RGB))


    unknown = cv2.subtract(sure_bg,sure_fg)

    plt.figure(3)
    plt.subplot(1, 2, 2) 
    plt.gca().set_title('Unknown')
    plt.imshow(cv2.cvtColor(unknown, cv2.COLOR_BGR2RGB))


    #----------------------------------------------------------------------------------------------------------------------#

    # Marker labelling
    # Connected components counts all black objects in the image. For explaination see: https://www.youtube.com/watch?v=hMIrQdX4BkE
    # It gives 2 objects in return, the number of objects and a picture with labelled objects.

n_objects, markers = cv2.connectedComponents(sure_fg)

    plt.figure(4)
    plt.subplot(2, 1, 1) 
    plt.gca().set_title('markers')
    plt.imshow(markers) 


    # Add one to all labels so that sure background is not 0, but 1
    markers = markers+1

    # Now, mark the region of unknown with zero
    markers[unknown==255] = 0


    markers = cv2.watershed(img, markers)
    img[markers == 8] = [255, 0, 0] # Overlay red circles (-1 val) to img. 2, 3, 4 are all the different objects detected in the image

    plt.figure(4)
    plt.subplot(2, 1, 2)
    plt.gca().set_title('markers')
    plt.imshow(img)



    print('Number of particles detected: ', n_objects-2)


    plt.show()

解决方案

If your particles are (almost) black, don't use Otsu's threshold, but a fixed to mask the (almost) black pixels. On the inverse binarized image, you can then apply morphological closing (to get the whole particles) and opening (to get rid of the background noise), see cv2.morphologyEx. Afterwards, you find all contours to get the particles and the scale, see cv2.findContours. We determine the bounding rectangles of all contours for placing some labels on the particles in the input image, as well as to calculate the horizontal and vertical diameters of the particles by dividing the width/height of the particles' bounding boxes by the width of the scale's bounding box.

In my code, I omitted several things, including the Matplotlib outputs. (While writing, I just noticed, there's a lot more code from you; I didn't see the scroll bar... I haven't seen that, and didn't incorporated that code, too.)

import cv2
from matplotlib import pyplot as plt
from skimage import io                  # Only needed for web grabbing images, use cv2.imread for local images

# Read image from web; Attention: it's already RGB
img = io.imread('https://i.stack.imgur.com/J46nA.jpg')

# Convert to grayscale; Attention: Source is RGB from web grabbing
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)

# Use fixed threshold to mask black areas
_, thresh = cv2.threshold(gray, 30, 255, cv2.THRESH_BINARY_INV)

# Morphological closing to get whole particles; opening to get rid of noise
img_mop = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)))
img_mop = cv2.morphologyEx(img_mop, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15)))

# Find contours
cnts, _ = cv2.findContours(img_mop, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# Get bounding rectangles for the scale and the particles
thr_size = 2000
scale = [cv2.boundingRect(cnt) for cnt in cnts if cv2.contourArea(cnt) > thr_size]
particles = [cv2.boundingRect(cnt) for cnt in cnts if cv2.contourArea(cnt) < thr_size]

# Iterate all particles, add label and diameters to input image
for i, p in enumerate(particles):
    x = p[0]
    y = max(0, p[1]-10)
    d_h = p[2] / scale[0][2] * 500
    d_v = p[3] / scale[0][2] * 500
    cv2.putText(img, str(i), (x, y), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
    print('Particle ' + str(i) + ' | Horizontal diameter: ' + '{:.2f}'.format(d_h) +
          ' nm, vertical diameter: ' +  '{:.2f}'.format(d_v) + ' nm')

cv2.imshow('img', cv2.resize(img, dsize=(0, 0), fx=0.5, fy=0.5))
cv2.imshow('thresh', cv2.resize(thresh, dsize=(0, 0), fx=0.5, fy=0.5))
cv2.imshow('img_mop',  cv2.resize(img_mop, dsize=(0, 0), fx=0.5, fy=0.5))
cv2.waitKey(0)
cv2.destroyAllWindows()

The thresh image with the fixed threshold:

The img_mop image, after applying the morphological operation (Note: the scale is still there, so we can use it for size approximation):

Finally, the input/output image ìmg with the corresponding labels (had to use JPG here due to image size restrictions):

Last, but not least, the print output:

Particle 0 | Horizontal diameter: 20.83 nm, vertical diameter: 23.03 nm
Particle 1 | Horizontal diameter: 20.83 nm, vertical diameter: 20.83 nm
Particle 2 | Horizontal diameter: 19.74 nm, vertical diameter: 17.54 nm
Particle 3 | Horizontal diameter: 23.03 nm, vertical diameter: 23.03 nm
Particle 4 | Horizontal diameter: 24.12 nm, vertical diameter: 24.12 nm
Particle 5 | Horizontal diameter: 21.93 nm, vertical diameter: 20.83 nm
Particle 6 | Horizontal diameter: 24.12 nm, vertical diameter: 23.03 nm
Particle 7 | Horizontal diameter: 21.93 nm, vertical diameter: 23.03 nm
Particle 8 | Horizontal diameter: 19.74 nm, vertical diameter: 21.93 nm
Particle 9 | Horizontal diameter: 19.74 nm, vertical diameter: 19.74 nm

Hope that helps!

这篇关于如何标记和测量斑点的大小?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆