Обнаружение частиц с помощью Python OpenCV

Я ищу правильное решение, как считать частицы и измерять их размеры на этом изображении:

Обнаружение частиц с помощью Python OpenCV

В итоге мне нужно получить списки координат частиц и площадей. После некоторого поиска в Интернете я понял, что есть 3 подхода к обнаружению частиц:

  1. капли
  2. Контуры
  3. связанные компоненты с статистикой

Глядя на разные проекты, я собрал некоторый код, смешав его.

import pylab
import cv2
import numpy as np

Размытие по Гауссу и пороговое значение

original_image = cv2.imread(img_path)
img = original_image
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.GaussianBlur(img, (5, 5), 0)
img = cv2.blur(img, (5, 5))
img = cv2.medianBlur(img, 5)
img = cv2.bilateralFilter(img, 6, 50, 50)

max_value = 255
adaptive_method = cv2.ADAPTIVE_THRESH_GAUSSIAN_C
threshold_type = cv2.THRESH_BINARY
block_size = 11
img_thresholded = cv2.adaptiveThreshold(img, max_value, adaptive_method, threshold_type, block_size, -3)

фильтровать мелкие объекты

min_size = 4
nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(img, connectivity=8)
sizes = stats[1:, -1]
nb_components = nb_components - 1

# for every component in the image, you keep it only if it's above min_size
for i in range(0, nb_components):
    if sizes[i] < min_size:
       img[output == i + 1] = 0

генерация контуров для заполнения отверстий и измерений. pos_list и size_list это то, что мы искали

contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
pos_list = []
size_list = []
for i in range(len(contours)):
    area = cv2.contourArea(contours[i])
    size_list.append(area)
    (x, y), radius = cv2.minEnclosingCircle(contours[i])
    pos_list.append((int(x), int(y)))

для самопроверки, если мы нанесем эти координаты на исходное изображение

pts = np.array(pos_list)
pylab.figure(0)
pylab.imshow(original_image)
pylab.scatter(pts[:, 0], pts[:, 1], marker = "x", color = "green", s=5, linewidths=1)
pylab.show()

Мы могли бы получить что-то вроде следующего:

Обнаружение частиц с помощью Python OpenCV

И... я не очень доволен результатами. Некоторые хорошо видимые частицы не включены, с другой стороны, были подсчитаны некоторые сомнительные флуктуации интенсивности. Играюсь сейчас с разными настройками фильтров, но ощущение, что это неправильно.

Если кто-то знает, как улучшить мое решение, поделитесь, пожалуйста.

Вы не можете избежать частоты ошибок Байеса.

Yves Daoust 04.05.2022 22:49

Что, если мы применим порог перед использованием k-средних?

William Davis 12.05.2022 16:50

@WilliamDavis, вы можете применить порог до k-средних, но вы можете потерять точность контура. Причина, по которой я бы порекомендовал сначала k-средние, заключается в том, что они сегментируют изображение на основные цвета, поскольку частицы могут иметь разные оттенки серого/черного. Пороговое значение сначала потеряет детали, но это зависит от вас

nathancy 13.05.2022 00:21
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
3
149
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку частицы белые, а фон черный, мы можем использовать квантование цвета Kmeans, чтобы разделить изображение на две группы с помощью cluster=2. Это позволит нам легко различать частицы и фон. Поскольку частицы могут быть очень маленькими, мы должны стараться избегать размытия, расширения или любых морфологических операций, которые могут изменить контуры частиц. Вот подход:

  1. K означает квантование цвета. Мы выполняем Kmeans с двумя кластерами, оттенками серого, затем порогом Оцу, чтобы получить бинарное изображение.

  2. Отфильтруйте супер крошечный шум. Затем мы находим контуры, удаляем крошечные частицы шума с помощью фильтрации области контура и собираем координаты каждой частицы (x, y) и ее площадь. Мы удаляем крошечные частицы на бинарной маске, «заполняя» эти контуры, чтобы эффективно стереть их.

  3. Примените маску к исходному изображению. Теперь мы применяем побитовую и отфильтрованную маску к исходному изображению, чтобы выделить кластеры частиц.


Kзначает с clusters=2

Результат

Number of particles: 204
Average particle size: 30.537

Код

import cv2
import numpy as np
import pylab

# Kmeans 
def kmeans_color_quantization(image, clusters=8, rounds=1):
    h, w = image.shape[:2]
    samples = np.zeros([h*w,3], dtype=np.float32)
    count = 0

    for x in range(h):
        for y in range(w):
            samples[count] = image[x][y]
            count += 1

    compactness, labels, centers = cv2.kmeans(samples,
            clusters, 
            None,
            (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10000, 0.0001), 
            rounds, 
            cv2.KMEANS_RANDOM_CENTERS)

    centers = np.uint8(centers)
    res = centers[labels.flatten()]
    return res.reshape((image.shape))

# Load image
image = cv2.imread('1.png')
original = image.copy()

# Perform kmeans color segmentation, grayscale, Otsu's threshold
kmeans = kmeans_color_quantization(image, clusters=2)
gray = cv2.cvtColor(kmeans, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find contours, remove tiny specs using contour area filtering, gather points
points_list = []
size_list = []
cnts, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:]
AREA_THRESHOLD = 2
for c in cnts:
    area = cv2.contourArea(c)
    if area < AREA_THRESHOLD:
        cv2.drawContours(thresh, [c], -1, 0, -1)
    else:
        (x, y), radius = cv2.minEnclosingCircle(c)
        points_list.append((int(x), int(y)))
        size_list.append(area)

# Apply mask onto original image
result = cv2.bitwise_and(original, original, mask=thresh)
result[thresh==255] = (36,255,12)

# Overlay on original
original[thresh==255] = (36,255,12)

print("Number of particles: {}".format(len(points_list)))
print("Average particle size: {:.3f}".format(sum(size_list)/len(size_list)))

# Display
cv2.imshow('kmeans', kmeans)
cv2.imshow('original', original)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey()

как идея использования K-средних

Jeru Luke 05.05.2022 21:44

Другие вопросы по теме