에지 검출
에지
소벨 sobel
에지는 픽셀값이 급격히 변하는 지점
1차원 그래프로 나타내면 픽셀값이 갑자기 커지는 부분이 에지
에지 구하는 법
미분을 사용
즉, 곡선에서 픽셀값이 급격하게 증가한 부분 = 1차 미분값이 가장 큼
[but] 픽셀은 연속공간에 있지 않음 -> 미분의 근사값을 구하자! 주변보다 1차 미분값이 큰 부분
이 에지로 검출
[미분의 근사값] 붙어 있는 픽셀 값을 빼면 된다.
x방향, y방향으로 각각 픽셀 값을 빼면 미분 근사값이 된다.
밑의 필터도 다 붙어 있는 값이죠?
- x filter*: 수직방향의 에지 검출
- y filter*: 수평방향의 에지 검출
소벨 필터를 이용한 미분 함수 원형
cv2.Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None) -> dst
- src: 입력 영상
- ddepth: 출력 영상 데이터 타입. -1이면 입력 영상과 같은 데이터 타입을 사용.
- dx: x 방향 미분 차수. 1차미분할지 2차미분 할지 결정
- dy: y 방향 미분 차수.
- dst: 출력 영상(행렬)
- ksize: 커널 크기. 기본값은 3.
- scale 연산 결과에 추가적으로 곱할 값. 기본값은 1.
- delta: 연산 결과에 추가적으로 더할 값. 기본값은 0.
- borderType: 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_DEFAULT.
소벨 필터 정제
img_sobel_x = cv2.convertScaleAbs(img_sobel_x)
(정제 전)
(정제 후)
소벨 필터 합치기
addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
- src1 첫 번째 입력 행렬
- alpha src1 행렬의 가중치
- src2 두 번째 입력 행렬. src1과 크기와 채널 수가 같아야 함
- betasrc2 행렬의 가중치
- gamma 가중합 결과에 추가적으로 더할 값
- dst 출력 행렬. 입력 행렬과 같은 크기, 같은 채널 수의 행렬이 생성됨
- dtype 출력 행렬의 깊이.
- src1과 src2의 깊이가 같은 경우에는 dtype에 -1을 지정할 수 있고, 이 경우 dst의 깊이는 src1, src2와 같은 깊이로 설정
- src1과 src2의 깊이가 서로 다른 경우에는 dtype을 반드시 지정 필요
에지 검출 과정
[소벨 필터 x,y 각각 사용]
(x)
img_sobel_x = cv2.Sobel (img_gray, cv2.CV_64F, 1, 0, ksize=3)
(y)
img_sobel_y = cv2.Sobel (img_gray, cv2.CV_64F, 0, 1, ksize=3)
[소벨 필터 합치기] img_sobel = cv2.addWeighted (img_sobel_x, 1, img_sobel_y, 1, 0)
코드
[C++]
# include < opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat img_color;
img_color = imread("box.png", IMREAD_COLOR);
Mat img_gray;
cvtColor(img_color, img_gray, COLOR_BGR2GRAY);
Mat img_sobel_x;
Sobel(img_gray, img_sobel_x, CV_64F, 1, 0, 3);
convertScaleAbs(img_sobel_x, img_sobel_x);
Mat img_sobel_y;
Sobel(img_gray, img_sobel_y, CV_64F, 0, 1, 3);
convertScaleAbs(img_sobel_y, img_sobel_y);
Mat img_sobel;
addWeighted(img_sobel_x, 1, img_sobel_y, 1, 0, img_sobel);
imshow("Sobel X", img_sobel_x);
imshow("Sobel Y", img_sobel_y);
imshow("Sobel", img_sobel);
waitKey(0);
return 0;
}
[Python]
import cv2
img_color = cv2.imread('box.png', cv2.IMREAD_COLOR)
img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)
img_sobel_x = cv2.Sobel(img_gray, cv2.CV_64F, 1, 0, ksize=3)
img_sobel_x = cv2.convertScaleAbs(img_sobel_x)
img_sobel_y = cv2.Sobel(img_gray, cv2.CV_64F, 0, 1, ksize=3)
img_sobel_y = cv2.convertScaleAbs(img_sobel_y)
img_sobel = cv2.addWeighted(img_sobel_x, 1, img_sobel_y, 1, 0);
cv2.imshow("Sobel X", img_sobel_x)
cv2.imshow("Sobel Y", img_sobel_y)
cv2.imshow("Sobel", img_sobel)
cv2.waitKey(0)
cv2.destroyAllWindows()
샤를 필터를 이용한 미분 함수
cv2.Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None) -> dst
- src: 입력 영상
- ddepth: 출력 영상 데이터 타입. -1이면 입력 영상과 같은 데이터 타입을 사용.
- dx: x 방향 미분 차수
- dy: y 방향 미분 차수
- dst: 출력 영상(행렬)
- scale 연산 결과에 추가적으로 곱할 값. 기본값은 1
- delta: 연산 결과에 추가적으로 더할 값. 기본값은 0.
- borderType: 가장자리 픽셀 확장 방식. 기본값은 cv2.BORDER_DEFAULT
[Python]
import cv2
img_color = cv2.imread('cat.png', cv2.IMREAD_COLOR)
img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)
img_sobel_x = cv2.Scharr(img_gray, cv2.CV_64F, 1, 0)
img_sobel_x = cv2.convertScaleAbs(img_sobel_x)
img_sobel_y = cv2.Scharr(img_gray, cv2.CV_64F, 0, 1)
img_sobel_y = cv2.convertScaleAbs(img_sobel_y)
img_sobel = cv2.addWeighted(img_sobel_x, 1, img_sobel_y, 1, 0)
cv2.imshow("Sobel X", img_sobel_x)
cv2.imshow("Sobel Y", img_sobel_y)
cv2.imshow("Sobel", img_sobel)
cv2.waitKey(0)
cv2.destroyAllWindows()
[소벨필터와 샤를 필터 결과값 차이] (소벨)
(샤를)
캐니 에지 디텍터 Canny Edge Detector
캐니 에지 디텍터는 1986년 존 F. 캐니에 의해서 개발됨.
캐니 에지 디텍터를 사용하면 2개의 임계값을 사용하여 한 픽셀로 구성된 에지를 검출함
가장 우월
윤곽을 가장 잘 찾아냄
원래 영상의 회색물질과 관련된 모든 에지들을 제거할 수 있는 유일한 방법
[목표]
에지 검출 시 다음 3가지를 만족하는 것을 목표로 함
- 낮은 에러율: 실제 에지가 검출되어야 함.
노이즈로 인해 거짓 에지가 검출되면 안됨. - 정확한 에지 위치: 캐니 에지와 실제 이미지의 에지 사이의 거리가 최소가 되어야 함.
- 응답 최소화: 실제 이미지상의 에지에서 하나의 에지만 검출되어야 함
[단점]
구현 복잡하고 실행시간이 길다.
함수 원형
void cv::Canny ( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize = 3, bool L2gradient = false)
Python: edges = cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] )
- Image: 그레이 스케일 입력 이미지
- Edges: 에지 검출 결과
- threshold1: 첫 번째 임계값
- threshold2: 두 번째 임계값
- apertureSize: 소벨 연산에서 사용할 마스크의 크기
[C++]
# include < opencv2/opencv.hpp> # include < iostream>
using namespace cv;
using namespace std;
int main()
{
Mat img_color = imread("house.png", IMREAD_COLOR);
Mat img_gray;
cvtColor(img_color, img_gray, COLOR_BGR2GRAY);
imshow("Original", img_gray);
Mat img_edge;
// 블러링을 적용한 후, 캐니 에지 디텍터를 적용합니다.
blur(img_gray, img_gray, Size(3, 3));
// 보통 첫 번째 임계값의 2~3배로 두 번째 임계값을 정합니다.
Canny(img_gray, img_edge, 50, 150, 3);
imshow("Canny Edge", img_edge);
waitKey(0);
return 0;
}
[Python]
import cv2
img_gray = cv2.imread('house.png', cv2.IMREAD_GRAYSCALE)
cv2.imshow("Original", img_gray)
// 블러링을 적용한 후, 캐니 에지 디텍터를 적용합니다.
img_gray = cv2.blur(img_gray,(3, 3));
// 보통 첫 번째 임계값의 2~3배로 두 번째 임계값을 정합니다.
img_canny = cv2.Canny(img_gray, 50, 150)
cv2.imshow("Canny Edge", img_canny)
cv2.waitKey(0)
cv2.destroyAllWindows()