1. 소벨 연산자
엣지는 픽셀 값이 전환되는 위치로 영상의 두드러진 특징 중 하나로 영상 특징 추출, 객체 검출, 패턴 인식 등에 중요한 역할을 합니다.
인간의 눈은 이미지 가장자리를 어떻게 식별합니까?
예를 들어 그림이 있고 그림에 선이 있고 왼쪽은 매우 밝고 오른쪽은 매우 어두우면 사람의 눈은 이 선을 가장자리로 쉽게 식별할 수 있습니다. 픽셀 값이 빠르게 변합니다 .
소벨 연산자는 이미지의 1차 도함수를 취합니다 . 1차 도함수가 클수록 해당 방향의 픽셀 변화가 더 커지고 에지 신호가 더 강해집니다.
이미지의 회색조 값은 모두 이산 숫자이므로 소벨 연산자는 이산 차이 연산자를 사용하여 이미지 픽셀 점의 밝기 값에 대한 대략적인 기울기를 계산합니다.
이미지는 너비/높이 방향을 따르는 2차원입니다.
원본 이미지를 처리하기 위해 두 개의 컨볼루션 커널을 사용합니다.
- 수평 방향
- 수직 방향
이 경우, 우리는 수평 방향의 각 픽셀의 밝기 변화와 수직 방향의 밝기 변환을 각각 반영하는 두 개의 새로운 행렬을 얻습니다.
**이 두 방향의 변화를 고려**하여 특정 픽셀의 기울기 변화를 반영하기 위해 다음 공식을 사용합니다.
때때로 단순화를 위해 절대값 덧셈이 대신 사용됩니다.
# 索贝尔算子.
import cv2
import numpy as np
#导入图片
img = cv2.imread('./chess.png')#
# x轴方向, 获取的是垂直边缘
dx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
dy = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=5)
# 可利用numpy的加法, 直接整合两张图片
# dst = dx + dy
# 也可利用opencv的加法
dst = cv2.add(dx, dy)
cv2.imshow('dx', np.hstack((dx, dy, dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
2. 샤르(Scharr) 연산자
- Scharr(src, dlength, dx, dy[, dst[, scale[, delta[, borderType]]]]) - 커널 크기가
3일 때 위의 Sobel 커널은 명백한 오류를 생성할 수 있습니다( Sobel 연산자는 단지 미분의 대략적인 값을 취함 ). 이 문제를 해결하기 위해 OpenCV는 Scharr 함수를 제공하지만 이 함수는 크기가 3인 커널에서만 작동합니다 . 이 함수는 Sobel 함수만큼 빠르게 작동하지만 결과는 더 정확합니다.
- Scharr 연산자는 Sobel과 매우 유사 하지만 픽셀 변환을 증폭시키기 위해 다른 커널 값을 사용합니다 .
- Scharr 연산자는 3*3 커널만 지원하므로 커널 매개변수가 없습니다.
- Scharr 연산자는 x 방향이나 y 방향의 모서리만 찾을 수 있습니다.
- Sobel 연산자의 ksize를 -1로 설정하는 것이 Scharr 연산자입니다.
- Scharr는 작은 모서리를 찾는 데 능숙하며 일반적으로 덜 자주 사용됩니다.
# 索贝尔算子.
import cv2
import numpy as np
#导入图片
img = cv2.imread('./lena.png')#
# x轴方向, 获取的是垂直边缘
dx = cv2.Scharr(img, cv2.CV_64F, 1, 0)
# y轴方向, 获取的是水平边缘
dy = cv2.Scharr(img, cv2.CV_64F, 0, 1)
# 可利用numpy的加法, 直接整合两张图片
# dst = dx + dy
# 也可利用opencv的加法
dst = cv2.add(dx, dy)
cv2.imshow('dx', np.hstack((dx, dy, dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
3. 라플라시안 연산자
Sobel 연산자는 1차 파생을 시뮬레이션합니다. 파생이 클수록 변환이 더 심각해지고 가장자리가 될 가능성이 높아집니다.
f'(t)의 도함수를 계속해서 도출하면 어떻게 될까요?
2차 미분 = 0 "가장자리에서" 임을 알 수 있으며 , 이 기능을 사용하여 이미지의 가장자리를 찾을 수 있습니다. ** 문제가 있다는 점에 유의하세요. 2차 미분은 0인 위치입니다. 의미없는 입장일 수도 있습니다 **.
- **라플라시안 연산자 도출 과정**
- 예를 들어 x 방향의 해를 취합니다.
1차 차이: f'(x) = f(x) - f(x - 1)
2차 차이: f''(x) = f'(x +1) - f' (x) = (f(x + 1) - f(x)) - (f(x) - f(x - 1)) 단순화 후: f''(x) = f(
x - 1) - 2 f(x)) + f(x + 1)
같은 방법으로 다음을 얻을 수 있습니다: f''(y) = f(y - 1) - 2 f(y)) + f(y + 1)
x 및 y 방향의 그래디언트를 함께 쌓습니다.
$f''(x,y) = f''_x(x,y) + f''_y(x,y)$
$f''(x,y) = f(x - 1, y) - 2 f(x,y)) + f(x + 1, y) + f(x, y - 1) - 2 f(x ,y)) + f(x,y + 1)$
$f''(x,y) = f(x - 1, y) + f(x + 1, y) + f(x, y - 1) + f(x,y + 1) - 4 f(x ,y)) $
이 방정식은 행렬로 작성할 수 있습니다.
이러한 방법으로 라플라시안 연산자의 컨볼루션 커널, 즉 컨볼루션 템플릿을 얻는다.
- 라플라시안(src, dlength[, dst[, ksize[, scale[, delta[, borderType]]]]])
-동시에 두 방향의 가장자리를 찾을 수 있습니다
. -잡음에 민감하며 일반적으로 먼저 잡음을 제거한 다음 Laplacian을 호출 해야 합니다.
# 拉普拉斯
import cv2
import numpy as np
#导入图片
img = cv2.imread('./chess.png')#
dst = cv2.Laplacian(img, -1, ksize=3)
cv2.imshow('dx', np.hstack((img, dst)))
cv2.waitKey(0)
cv2.destroyAllWindows()
일반적으로 회색조 이미지로 변환한 후 가장자리를 찾습니다.
4. 가장자리 감지 Canny
*캐니 에지 검출 알고리즘* 은 John F. Canny가 1986년에 개발한 다단계 에지 검출 알고리즘 입니다. 또한 많은 사람들이 에지 검출을 위한 *최적의 알고리즘*으로 간주합니다. 최적의 에지 검출을 위한 세 가지 주요 평가 기준은 다음과 같습니다. :
> - **낮은 오류율:** 노이즈로 인한 거짓 긍정을 최소화하면서 실제 가장자리를 최대한 많이 식별합니다.
> - **높은 현지화:** 식별된 가장자리는 이미지의 실제 가장자리에 최대한 가까워야 합니다.
> - **최소 응답:** 이미지의 가장자리는 한 번만 식별할 수 있습니다.
- 캐니 에지 감지를 위한 일반적인 단계
- Denoising. Edge 감지는 노이즈의 영향을 받기 쉽습니다. 일반적으로 Edge 감지 전에 Denoising이 필요합니다. Gaussian 필터링은 일반적으로 노이즈를 제거하는 데 사용됩니다 .
- 기울기 계산: 소벨 연산자를 사용하여 평활화된 이미지의 기울기와 방향을 계산합니다.
- 그라데이션 방향은 세로, 가로, 대각선 2개의 네 가지 범주로 분류됩니다.
- 계산된 기울기와 방향은 대략 다음과 같습니다.
- 비최대 억제
- 기울기와 방향을 얻은 후 이미지를 횡단하여 경계가 아닌 모든 점을 제거합니다.
- 구현 방법: 픽셀을 하나씩 탐색하여 현재 픽셀이 동일한 방향 그라데이션을 갖는 주변 픽셀의 최대값인지 확인합니다.
- 아래 그림에서 점 A, B, C는 방향이 같고, 그라데이션 방향은 모서리에 수직입니다.
- A 지점이 A, B, C 중 로컬 최대값인지 확인하고, 그렇다면 해당 지점을 유지하고, 그렇지 않으면 억제합니다(0으로 재설정).
- 좀 더 생생한 예:
- 히스테리시스 임계값
캐니(img, minVal, maxVal, ...)
# Canny
import cv2
import numpy as np
#导入图片
img = cv2.imread('./lena.png')#
# 阈值越小, 细节越丰富
lena1 = cv2.Canny(img, 100, 200)
lena2 = cv2.Canny(img, 64, 128)
cv2.imshow('lena', np.hstack((lena1, lena2)))
cv2.waitKey(0)
cv2.destroyAllWindows()