Как маркировать и измерять размер капель? - PullRequest
3 голосов
/ 02 декабря 2019

Я изучаю анализ изображений с помощью Python, я только начинающий. Мне удалось написать код (я делюсь им ниже), чтобы обнаружить капли (наночастицы) на этом изображении наночастиц:

Nanoparticle image

Я могу обнаружитьчто есть 10 наночастиц, использующих cv2.connectedComponents, но теперь мне нужно:

  1. Маркировать каждую наночастицу числом для создания окончательного изображения.

  2. Рассчитайте количество пикселей, составляющих каждую наночастицу, чтобы я мог определить их размер.

Я пытался исследовать, но не смог найти ничего подходящего для меня. Кто-нибудь, кто готов помочь мне? Если бы вы могли предложить код, это было бы замечательно, если бы вы могли это объяснить, это было бы супер!

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()

1 Ответ

4 голосов
/ 02 декабря 2019

Если ваши частицы (почти) черные, не используйте порог Оцу, а фиксированный, чтобы замаскировать (почти) черные пиксели. На обратном бинаризованном изображении вы можете применить морфологическое закрытие (чтобы получить целые частицы) и открытие (чтобы избавиться от фонового шума), см. cv2.morphologyEx. После этого вы найдете все контуры для получения частиц и шкалы, см. cv2.findContours. Мы определяем ограничивающие прямоугольники всех контуров для размещения некоторых меток на частицах во входном изображении, а также для расчета горизонтального и вертикального диаметров частиц путем деления ширины / высоты ограничивающих прямоугольников частиц на ширинуограничительная шкала масштаба.

В моем коде я пропустил несколько вещей, включая выходные данные Matplotlib. (При написании я только заметил, что от вас гораздо больше кода; я не видел полосу прокрутки ... Я не видел этого и тоже не включал этот код.)

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()

Изображение thresh с фиксированным порогом:

Thresholded image

Изображение img_mop после применения морфологической операции (Примечание:масштаб все еще там, поэтому мы можем использовать его для приближения размера):

Morph image

Наконец, изображение ввода / вывода ìmg с соответствующими метками(здесь пришлось использовать JPG из-за ограничений на размер изображения):

Final image

Последний, но не менее важный вывод print:

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

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...