Надежная оценка объема прозрачной жидкости с использованием обработки изображений

#image-processing #image-segmentation #opencv-python #image-thresholding

#обработка изображений #сегментация изображения #opencv-python #изображение-пороговое значение

Вопрос:

Я работаю над проектом, который включает в себя определение объема прозрачной жидкости (или воздуха, если это окажется проще) в ограниченном пространстве. Изображения, с которыми я работаю, представляют собой фоновое изображение контейнера без жидкости и изображение переднего плана, которое в редких случаях также может быть пустым, но в большинстве случаев частично заполнено некоторым количеством жидкости. Слева: фоновое изображение; Справа: изображение переднего плана (более темная область слева - жидкость

Хотя это может показаться довольно простым плавным и пороговым подходом, он оказывается несколько более сложным. Я работаю с набором с тоннами этих пар изображений фоновых и передних изображений, и, похоже, я не могу найти подход, который был бы достаточно надежным для применения ко всем изображениям в наборе. Моя работа до сих пор включает сглаживание и пороговое значение изображения и применение закрытия для его завершения.

 bg_image = cv.imread("bg_image", 0)
fg_image = cv.imread("fg_image", 0)

blur_fg = cv.GaussianBlur(fg_image, (5, 5), sigmaX=0, sigmaY=0)
thresholded_image = cv.threshold(blur_fg, 186, 255, cv.THRESH_BINARY_INV)[1]

kernel = np.ones((4,2),np.uint8)
closing = cv.morphologyEx(thresholded_image, cv.MORPH_CLOSE, kernel)
 

Результаты различаются, вот пример, когда все идет хорошо:
Достойная оценка объема

В других примерах это не так хорошо: Плохая оценка объема

Помимо этого, я также пытался:

  1. Вычитание фоновых и передних изображений
  2. Контрастное растяжение
  3. Выравнивание гистограммы
  4. Другие методы определения порога, такие как Otsu

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

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

Редактировать:

Вот несколько примеров изображений, с которыми можно поиграть.

bg_1

fg_1

bg_2

fg_2

Комментарии:

1. Я склоняюсь к использованию границы, которая возникает между жидкостью и воздухом, но я не совсем уверен, как это сделать. Из трех приведенных изображений кажется, что упомянутый «край» довольно темный по сравнению с обеими окружающими областями. Вы пробовали создавать какое-либо градиентное изображение (только горизонтальный градиент), которое также используется для определения границ (Canny, Sobel)? Также, пожалуйста, разместите некоторые фактические входные изображения (не эти графики), чтобы люди здесь могли поиграть.

2. Я использовал обнаружение краев, но не создал градиентное изображение. Но скажем, что можно выделить четкий край, как это может помочь в оценке объема? Я думал, что его можно использовать для определения длины области, в которой находится жидкость. Другим подходом может быть применение некоторого адаптированного порогового значения к различным областям, разделенным краем. Я добавил две пары входных изображений.

Ответ №1:

Вот подход, при котором вы вычисляете среднее значение каждого столбца пикселей в вашем изображении, а затем вычисляете градиент средних значений:

 #!/usr/bin/env python3

import cv2
import numpy as np
import matplotlib.pyplot as plt

filename = 'fg1.png'

# Load image as greyscale and calculate means of each column of pixels
im = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
means = np.mean(im, axis=0)

# Calculate the gradient of the means
y = np.gradient(means)

# Plot the gradient of the means
xdata = np.arange(0, y.shape[0])
plt.plot(xdata, y, 'bo')           # blue circles
plt.title(f'Gradient of Column Means for "{filename}"')
plt.xlabel('x')
plt.ylabel('Gradient of Column Means')
plt.grid(True)
plt.show()
 

введите описание изображения здесь

введите описание изображения здесь


Если вы просто построите средние значения всех столбцов, не используя градиент, вы получите следующее:

введите описание изображения здесь

введите описание изображения здесь

Комментарии:

1. Это отличное начало, однако я все еще не уверен, как это можно использовать для оценки объема (количества пикселей в темной области).

2. Не уверен, что понимаю. Если переход осуществляется в пикселе 58 на первом изображении и пикселе 22 на втором изображении, а ширина изображений составляет 118 пикселей, то, несомненно, это составит 58/118 пикселей в первом и 22/118 пикселей во втором?

3. О, да, это правда, извините за плотность.. Есть ли способ программно определить, на какой стороне градиента находится жидкость? (в двух примерах жидкость находится слева от градиента в одном случае и справа от градиента в другом.

4. Если вы посмотрите на второй красный график, вы можете увидеть, что средняя яркость пикселей с левой стороны заметно темнее (ниже), чем справа. На первом красном графике… что ж, остается только гадать, какая сторона темнее!

5. Если вы контролируете лабораторное оборудование и фотографируете, возможно, вы могли бы помочь себе, выбрав более контрастный фон, или покрасив жидкость, или улучшив освещение, или пометив нижний конец пробирки…