Фильтр Собеля, дающий плохие результаты

#python #opencv #computer-vision #sobel

#python #opencv #компьютерное зрение #sobel

Вопрос:

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

Ввод, который я дал, был (после размытия):

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

Код для фильтра Собеля приведен ниже:

 def sobel_filters(img):
  Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
  Ky = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])

  Ix = ndimage.filters.convolve(img, Kx)
  Iy = ndimage.filters.convolve(img, Ky)

  G = np.sqrt(np.square(Ix)   np.square(Iy))
  G *= 255.0 / G.max()
  

  return G
  

Я размыл результаты, используя sigma = 1.3. Размер входного изображения составляет 512 на 512.
Я ожидал, что результат будет похож на то, что показано в here:https://www.adeveloperdiary.com/data-science/computer-vision/how-to-implement-sobel-edge-detection-using-python-from-scratch/

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

1. Размещенная вами ссылка не использует правильное ядро Sobel. Перейдите на страницу Википедии, это правильно: en.wikipedia.org/wiki/Sobel_operator

Ответ №1:

Вот один из способов сделать это в Python / OpenCV. Ваша проблема в том, что ваши производные не нормализованы должным образом и должны обрабатываться как значения с плавающей запятой. Ваша нормализация также не учитывает отрицательные значения. В этом ответе я использую встроенный в OpenCV Sobel и другие методы. Поэтому нет необходимости вводить scipy.ndimage

Ввод:

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

 import cv2
import numpy as np
import skimage.exposure as exposure

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

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

# blur
blur = cv2.GaussianBlur(gray, (0,0), 1.3, 1.3)

# apply sobel derivatives
sobelx = cv2.Sobel(blur,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(blur,cv2.CV_64F,0,1,ksize=3)

# optionally normalize to range 0 to 255 for proper display
sobelx_norm= exposure.rescale_intensity(sobelx, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)
sobely_norm= exposure.rescale_intensity(sobelx, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)

# square 
sobelx2 = cv2.multiply(sobelx,sobelx)
sobely2 = cv2.multiply(sobely,sobely)

# add together and take square root
sobel_magnitude = cv2.sqrt(sobelx2   sobely2)

# normalize to range 0 to 255 and clip negatives
sobel_magnitude = exposure.rescale_intensity(sobel_magnitude, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)

# save results
cv2.imwrite('gray_lena_sobelx_norm.jpg', sobelx_norm)
cv2.imwrite('gray_lena_sobely_norm.jpg', sobely_norm)
cv2.imwrite('gray_lena_sobel_magnitude.jpg', sobel_magnitude)

# show results
cv2.imshow('sobelx_norm', sobelx_norm)  
cv2.imshow('sobely_norm', sobely_norm)  
cv2.imshow('sobel_magnitude', sobel_magnitude)  
cv2.waitKey(0)
cv2.destroyAllWindows()
  

Собель X (нормализованный):

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

Собель Y (нормализованный):

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

Величина Собеля:

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

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

1. Большое вам спасибо! это работает! на самом деле я использовал свою собственную реализацию gaussian blur и сохранял результат в int8. После преобразования он работает! Также, пожалуйста, обратите внимание, что в вашей реализации есть некоторые ошибки: в переменной sobely_norm вы использовали тот же параметр Ix вместо Iy

Ответ №2:

Вот ваш способ сделать это с помощью изображений с плавающей запятой и правильной нормализации в Python / OpenCV. Без данных с плавающей запятой вы получаете только односторонние производные (не как положительные, так и отрицательные результаты).

Как указал Крис Луенго, производные в вашем ответе ошибочны по сравнению со стандартными в OpenCV. Производные в ссылке предназначены для правильной свертки. Но Scipy имеет как свертку, так и корреляцию. Большинство «сверток» на самом деле являются корреляциями. (OpenCV cv2.filter2D таков. Функция фактически вычисляет корреляцию, а не свертку). Итак, я исправил ядра, чтобы корреляция соответствовала тому, что есть в OpenCV Sobel или используется cv2.filter2D(). Свертки и корреляции связаны транспонированием. См. https://medium.com/@aybukeyalcinerr/correlation-vs-convolution-filtering-2711d8bb3666

Ввод:

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

 import cv2
import numpy as np
import scipy.ndimage as ndimage
import skimage.exposure as exposure

# read the image and convert to float
img = cv2.imread('gray_lena.png').astype(np.float32)

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

# blur
blur = cv2.GaussianBlur(gray, (0,0), 1.3, 1.3)

# define Sobel X and Y (correlation) kernels
Kx = np.array([[-1, 0, 1], 
               [-2, 0, 2], 
               [-1, 0, 1]])

Ky = np.array([[-1, -2, -1], 
               [ 0,  0,  0], 
               [ 1,  2,  1]])

# apply correlations and normalize by sum of absolute values of elements
sobelx = ndimage.filters.correlate(blur, Kx)
sobely = ndimage.filters.correlate(blur, Ky)

#OpenCV alternate:
#sobelx = cv2.filter2D(blur, cv2.CV_32F, Kx)
#sobely = cv2.filter2D(blur, cv2.CV_32F, Ky)

# optionally normalize to range 0 to 255 for proper display and saving as 8-bit data.
sobelx_norm= exposure.rescale_intensity(sobelx, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)
sobely_norm= exposure.rescale_intensity(sobelx, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)

# add and take square root
sobel_magnitude = np.sqrt(np.square(sobelx)   np.square(sobely))

# normalize to range 0 to 255 and clip negatives
sobel_magnitude = exposure.rescale_intensity(sobel_magnitude, in_range='image', out_range=(0,255)).clip(0,255).astype(np.uint8)

# save results
cv2.imwrite('gray_lena_sobelx_norm2.jpg', sobelx_norm)
cv2.imwrite('gray_lena_sobely_norm2.jpg', sobely_norm)
cv2.imwrite('gray_lena_sobel_magnitude2.jpg', sobel_magnitude)

# show results
cv2.imshow('sobelx_norm', sobelx_norm)  
cv2.imshow('sobely_norm', sobely_norm)  
cv2.imshow('sobel_magnitude', sobel_magnitude)  
cv2.waitKey(0)
cv2.destroyAllWindows()
  

Собель X (нормализованный):

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

Собель Y (нормализованный):

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

Величина Собеля:

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

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

1. Спасибо, Крис. Я не заметил проблемы со знаками. Он получил эти значения из ссылки, которую пытался воспроизвести. В одном я ответил, чтобы показать ему, почему его код не работает. В другом я показал ему, как бы я это сделал, используя OpenCV

2. Извините, Крис, я переписал неправильный ответ, когда попытался изменить нормализацию, а затем решил вернуть его обратно. Я не хотел исправлять его ядра собеля, поскольку он получил их из ссылки, которую он перечислил. Но ваша точка зрения хорошо воспринята, и я рад, что вы указали на это. Надеюсь, он поймет. Однако, поскольку величина учитывает сумму абсолютных значений, я думаю, что это не должно иметь значения в величине. Конечно, производные изображения X и Y будут иметь измененную полярность по краям.

3. Крис, я отредактировал свой ответ, чтобы показать правильные ядра и изображения. И дайте лучшее объяснение относительно свертки и корреляции.