Если ваши частицы (почти) черные, не используйте порог Оцу, а фиксированный, чтобы замаскировать (почти) черные пиксели. На обратном бинаризованном изображении вы можете применить морфологическое закрытие (чтобы получить целые частицы) и открытие (чтобы избавиться от фонового шума), см. 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
с фиксированным порогом:
Изображение img_mop
после применения морфологической операции (Примечание:масштаб все еще там, поэтому мы можем использовать его для приближения размера):
Наконец, изображение ввода / вывода ìmg
с соответствующими метками(здесь пришлось использовать JPG из-за ограничений на размер изображения):
Последний, но не менее важный вывод 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
Надеюсь, это поможет!