6 из 9 для Введение в обработку изображений

Цветность и различие изображений для сегментации изображений

Как мы сегментируем объекты на изображении на основе их цветности и временного движения?

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

В этом посте мы обсудим больше сегментации изображений по цветности и различиям изображений.

Сегментация цветности

Сегментация по цветности использует красно-зеленое (RG) цветовое пространство, которое является визуализацией цветового пространства RGB без интенсивности. Цветовые каналы красного и зеленого нормализуются относительно их суммы.

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

Будут обсуждаться два подхода к сегментации цветности: параметрический и непараметрический подход.

Параметрический подход

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

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

morn_calm_R = morn_calm[:, : ,0] * 1.0 / morn_calm.sum(axis=2)
morn_calm_G = morn_calm[:, : ,1] * 1.0 / morn_calm.sum(axis=2)

plt.figure(figsize=(5, 5))
plt.hist2d(morn_calm_R.flatten(), morn_calm_G.flatten(), bins=100, cmap='binary')
plt.xlim(0,1)
plt.ylim(0,1)
plt.xlabel('red', color='r')
plt.ylabel('green', color='g')
plt.show()

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

patch = morn_calm[750:800, 3300:3350, :]
imshow(patch)
plt.show()

Когда мы смотрим на проекцию этого пятна на пространство цветности RG ранее, оно находится внутри пространства внутри синей части пространства цветности, как показано на гистограмме ниже.

patch_R = patch[:, :, 0] * 1.0 / patch.sum(axis=2)
patch_G = patch[:, :, 1] * 1.0 / patch.sum(axis=2)

plt.figure(figsize=(5, 5))
plt.hist2d(patch_R.flatten(), patch_G.flatten(), bins=100, cmap='binary')
plt.xlim(0, 1)
plt.ylim(0, 1)
plt.xlabel('red', color='r')
plt.ylabel('green', color='g')
plt.show()

Используя эту гистограмму, мы собираемся подогнать распределение Гаусса к распределению на этой гистограмме заплат.

std_patch_R = np.std(patch_R.flatten())
mean_patch_R = np.mean(patch_R.flatten())

std_patch_G = np.std(patch_G.flatten())
mean_patch_G = np.mean(patch_G.flatten())

Мы можем определить функции Гаусса следующим образом:

def gaussian(p, mean, std):
    return np.exp(-(p-mean)**2/(2*std**2))*(1/(std*((2*np.pi)**0.5)))

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

x_R = np.linspace(0, 1)
y_R = gaussian(x, mean_patch_R, std_patch_R)
x_G = np.linspace(0, 1)
y_G = gaussian(x, mean_patch_G, std_patch_G)
fig, ax = plt.subplots()
plt.xlabel('red', color='r')
plt.ylabel('green', color='g')
ax.plot(x_R, y_R, 'r')
ax.plot(y_G, x_G, 'g')
plt.show()

Мы можем применить это к изображению Сада утреннего спокойствия, взяв вероятности появления цвета на нашем изображении, используя каналы R и G независимо.

prob_R = gaussian(morn_calm_R, mean_patch_R, std_patch_R)
plt.imshow(prob_R)
plt.axis('off')
plt.show()

prob_G = gaussian(morn_calm_G, mean_patch_G, std_patch_G)
plt.imshow(prob_G)
plt.axis('off')
plt.show()

Выполнение поэлементного умножения значений вероятностей как для красного, так и для зеленого каналов даст нам площадь, ограниченную двумя функциями Гаусса. Это будут окончательные матрицы вероятностей, которые фиксируют вероятности для всей цветовой полосы RG.

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

masked_morn_calm = morn_calm.copy()

prob = prob_R * prob_G

prob = prob.astype(np.uint8)

masked_morn_calm[:, :, 0] *= prob.clip(0, 1)
masked_morn_calm[:, :, 1] *= prob.clip(0, 1)
masked_morn_calm[:, :, 2] *= prob.clip(0, 1)

plt.axis('off')
imshow(masked_morn_calm);

Непараметрический подход

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

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

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

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

skin_patch = imread('skin_patch.png')
plt.axis('off')
imshow(skin_patch)
plt.show()

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

# Convert both images to the HSV color space
patch_hsv = colors.rgb_to_hsv(skin_patch[:, :, :3])
image_hsv = colors.rgb_to_hsv(skin_colors)

# Compute the 2D histogram of the reference patch in the H and S channels
patch_hist, x_edges, y_edges = np.histogram2d(
    patch_hsv[:,:,0].flatten(), patch_hsv[:,:,1].flatten(), bins=16, range=[[0, 1], [0, 1]])

# Normalize the histogram to have a maximum value of 1
patch_hist = patch_hist / np.max(patch_hist)

# Compute the 2D histogram of the image in the H and S channels
image_hist, x_edges, y_edges = np.histogram2d(
    image_hsv[:,:,0].flatten(), image_hsv[:,:,1].flatten(), bins=16, range=[[0, 1], [0, 1]])

# Normalize the image histogram to have a maximum value of 1
image_hist = image_hist / np.max(image_hist)

# Compute the backprojection of the image histogram using the reference patch histogram
backproj = np.zeros_like(image_hsv[:,:,0])
for i in range(image_hsv.shape[0]):
    for j in range(image_hsv.shape[1]):
        backproj[i,j] = patch_hist[np.searchsorted(x_edges, image_hsv[i,j,0])-1, np.searchsorted(y_edges, image_hsv[i,j,1])-1]

# Normalize the backprojection to have a maximum value of 1
backproj = backproj / np.max(backproj)

# Display the original image and the backprojection side by side
plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.imshow(skin_colors)
plt.title('Original Image')
plt.subplot(1,2,2)
plt.imshow(backproj)
plt.title('Backprojection')
plt.show()

Отличие изображений

Вместо цвета мы также можем сегментировать изображения на основе движения, что предполагает, что точка зрения камеры неподвижна, а движутся только объекты на изображениях.

Возьмем, к примеру, фото моего стола. Рассмотрите фото до и после и обратите внимание на то, какие предметы пропали без вести на втором фото.

fig, ax = plt.subplots(1, 2, figsize=(12, 10))
img_desk_before = imread('img_diff_before.jpg')
ax[0].imshow(img_desk_before)
ax[0].set_title('Before', fontsize=24)
ax[0].axis('off')
img_diff_after = imread('img_diff_after.jpg')
ax[1].imshow(img_diff_after)
ax[1].set_title('After', fontsize=24)
ax[1].axis('off')
fig.show()

Или вы можете просто запустить этот код и узнать его менее чем за секунду.

img_desk_before_gray = rgb2gray(img_desk_before)
img_desk_after_gray = rgb2gray(img_desk_after)
desk_diff = img_desk_before_gray - img_desk_after_gray

masked_img_desk_before = img_desk_before_gray * desk_diff

plt.imshow(masked_img_desk_before, cmap='gray')
plt.axis('off')
plt.show()

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

Заключение

На занятиях мы узнали о сегментации изображений на основе цветности и различении изображений для обнаружения объектов. Сегментация цветности включает представление цветов в пространстве цветности RG, которое нормализует каналы красного и зеленого цветов. Анализируя распределение цветов в этом пространстве и используя такие методы, как аппроксимация по Гауссу, мы можем определить конкретные интересующие цветовые области.

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

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

Рекомендации

Фонд Викимедиа. (2023, 14 июня). Цвет кожи человека. Википедия. https://en.wikipedia.org/wiki/Human_skin_color

째재와 육아 일기♡ : 네이버 블로그. (н.д.). https://blog.naver.com/hellomilja/220813661310