#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. Крис, я отредактировал свой ответ, чтобы показать правильные ядра и изображения. И дайте лучшее объяснение относительно свертки и корреляции.