Python Обработка изображений - Как удалить определенный контур и смешать значение с окружающими пикселями? - PullRequest
11 голосов
/ 02 мая 2020

Я делаю проект с глубиной изображения. Но у меня проблемы с шумом и не удалось прочитать пиксели с помощью глубины камеры. Есть некоторые точки и контуры (особенно края), которые имеют нулевое значение. Как просто игнорировать это нулевое значение и смешать его с окружающими значениями? Я пробовал dilation и erosion (морфинг обработки изображений), но все еще не могу получить правильную комбинацию. Это действительно убрало часть шума, но мне просто нужно избавиться от нулей во всех точках

Пример изображения:

Depth Image

Нулевое значение - самый темный синий (я использую карту цветов)

Чтобы проиллюстрировать, что я хочу сделать, пожалуйста, обратитесь к этому плохому рисунку краски:

Illustration

Я хочу избавиться от черного пятна (например, значение черного равно 0 или определенное значение), и смешать его с его окружением. Да, я могу локализовать место, используя np.where или подобную функцию, но я не знаю, как его смешать. Может быть, фильтр для применения? Мне нужно сделать это в потоке, поэтому мне нужен довольно быстрый процесс, может быть, 10-20 кадров в секунду. Заранее спасибо!

Обновление:

Есть ли другой способ кроме inpaint? Я искал различные inpaints, но я не нуждаюсь в такой сложной, как impainting. Мне просто нужно смешать его с простой линией, кривой или формой и 1D. Я думаю, что Inpaint - это излишество. Кроме того, мне нужно, чтобы они были достаточно быстрыми, чтобы их можно было использовать для видеопотока 10-20 кадров в секунду или даже лучше.

Ответы [ 3 ]

7 голосов
/ 06 мая 2020

Вот один из способов сделать это в Python / OpenCV.

Используйте медианную фильтрацию для заполнения отверстий.

  • Считайте ввод
  • Преобразовать в серый
  • Порог, чтобы сделать маска (пятна черные)
  • Инвертирование маски (пятна белые)
  • Найдите наибольший периметр контура пятна из инвертированной маски и используйте половину этого значения в качестве среднего размера фильтра
  • Примените медианную фильтрацию к изображению
  • Примените маску к входному сигналу
  • Примените обратную маску к медианно-фильтрованному изображению
  • Добавьте их вместе, чтобы сформировать результат
  • Сохранить результаты

Ввод:

enter image description here

import cv2
import numpy as np
import math

# read image
img = cv2.imread('spots.png')

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold 
mask = cv2.threshold(gray,0,255,cv2.THRESH_BINARY)[1]

# erode mask to make black regions slightly larger
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)


# make mask 3 channel
mask = cv2.merge([mask,mask,mask])

# invert mask
mask_inv = 255 - mask

# get area of largest contour
contours = cv2.findContours(mask_inv[:,:,0], cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours = contours[0] if len(contours) == 2 else contours[1]
perimeter_max = 0
for c in contours:
    perimeter = cv2.arcLength(c, True)
    if perimeter > perimeter_max:
        perimeter_max = perimeter

# approx radius from largest area
radius = int(perimeter_max/2) + 1
if radius % 2 == 0:
    radius = radius + 1
print(radius)

# median filter input image
median = cv2.medianBlur(img, radius)

# apply mask to image
img_masked = cv2.bitwise_and(img, mask)

# apply inverse mask to median
median_masked = cv2.bitwise_and(median, mask_inv)

# add together
result = cv2.add(img_masked,median_masked)

# save results
cv2.imwrite('spots_mask.png', mask)
cv2.imwrite('spots_mask_inv.png', mask_inv)
cv2.imwrite('spots_median.png', median)
cv2.imwrite('spots_masked.png', img_masked)
cv2.imwrite('spots_median_masked.png', median_masked)
cv2.imwrite('spots_removed.png', result)

cv2.imshow('mask', mask)
cv2.imshow('mask_inv', mask_inv )
cv2.imshow('median', median)
cv2.imshow('img_masked', img_masked)
cv2.imshow('median_masked', median_masked)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()


Пороговое изображение в качестве маски:

enter image description here

Инвертированная маска:

enter image description here

Отфильтровано по медиане изображение:

enter image description here

Маскированное изображение:

enter image description here

Маскированная фильтрованная медиана изображение:

enter image description here

Результат:

enter image description here

2 голосов
/ 06 мая 2020

Возможно, использование гауссовского фильтра с поправкой на NaN хорошо и достаточно быстро? Когда вы рассматриваете свои нули / черные пятна как NaN, этот подход также работает для больших черных областей.

enter image description here

# import modules
import matplotlib.pyplot as plt
import numpy as np
import skimage
import skimage.filters

# set seed
np.random.seed(42)

# create dummy image
# (smooth for more realisitc appearance)
size = 50
img = np.random.rand(size, size)
img = skimage.filters.gaussian(img, sigma=5)

# create dummy missing/NaN spots
mask = np.random.rand(size, size) < 0.02
img[mask] = np.nan

# define and apply NaN-adjusted Gaussian filter
# (https://stackoverflow.com/a/36307291/5350621)
def nangaussian(U, sigma=1, truncate=4.0):
    V = U.copy()
    V[np.isnan(U)] = 0
    VV = skimage.filters.gaussian(V, sigma=sigma, truncate=truncate)
    W = 0*U.copy()+1
    W[np.isnan(U)] = 0
    WW = skimage.filters.gaussian(W, sigma=sigma, truncate=truncate)
    return VV/WW
smooth = nangaussian(img, sigma=1, truncate=4.0)

# do not smooth full image but only copy smoothed NaN spots
fill = img.copy()
fill[mask] = smooth[mask]

# plot results
vmin, vmax = np.nanmin(img), np.nanmax(img)
aspect = 'auto'
plt.subplot(121)
plt.title('original image (white = NaN)')
plt.imshow(img, aspect=aspect, vmin=vmin, vmax=vmax)
plt.axis('off')
plt.subplot(122)
plt.title('filled image')
plt.imshow(fill, aspect=aspect, vmin=vmin, vmax=vmax)
plt.axis('off')
2 голосов
/ 03 мая 2020

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

Если вы действительно заинтересованы в «традиционных» (то есть без глубокого изучения) алгоритмах перерисовки и готовы их реализовать, я бы настоятельно рекомендовал Предлагаем взглянуть на soupault / scikit-inpaint # 4 . Этот алгоритм выполняет визуально равный или превосходящий метод biharmoni c и, будучи должным образом превращенным в код, может быть действительно быстрым даже для больших изображений.

Действительно, реализация inhainting biharmoni c довольно далека от оптимального в смысле производительности. Текущая версия довольно проста из-за того, что она была написана с акцентом на поддержку nD-ввода в качестве основной цели.

Возможные улучшения в реализации включают, но не ограничиваются:

  1. Предварительная генерация билапласианов (атм, она рассчитывается для каждого замаскированного пикселя отдельно)

  2. Маска для разделения на независимые связанные области (построена единая огромная матрица атм)

  3. Cythonization (не уверен, что смогу написать nD-код в Cython atm)

  4. Быстрее Linsolve

  5. Параллельная реализация.

В качестве промежуточного решения можно попытаться реализовать более быструю Cythonized версию для 2D (+ цвет) (принимая во внимание и другие пункты выше), как это ожидается наиболее распространенный вариант использования.

Если вы ищете просто «быстрый и достаточно хороший» метод рисования, взгляните на многочисленные основанные на глубоком обучении решения для inpaintin g на GitHub.

...