• Home
  • About
    • Yerim Oh photo

      Yerim Oh

      Happy and worthwhile day by day :)

    • Learn More
    • Email
    • LinkedIn
    • Instagram
    • Github
    • Youtube
  • Posts
    • All Posts
    • All Tags
  • Projects

[014] OpenCV 영상처리 에지 검출

15 Jan 2020

Reading time ~4 minutes

Table of Contents
  • 에지 검출
  • 에지
    • 소벨 sobel
      • 에지 구하는 법
      • 소벨 필터를 이용한 미분 함수 원형
      • 소벨 필터 정제
      • 소벨 필터 합치기
      • 에지 검출 과정
      • 코드
      • 샤를 필터를 이용한 미분 함수
    • 캐니 에지 디텍터 Canny Edge Detector
      • 함수 원형

에지 검출

  • 에지 검출
    • 소벨 sobel
      • 에지 구하는 법
      • 소벨 필터를 이용한 미분 함수 원형
      • 소벨 필터 정제
      • 소벨 필터 합치기
      • 에지 검출 과정
      • 코드
      • 샤를 필터를 이용한 미분 함수
    • 캐니 에지 디텍터 Canny Edge Detector

에지

소벨 sobel

에지는 픽셀값이 급격히 변하는 지점
1차원 그래프로 나타내면 픽셀값이 갑자기 커지는 부분이 에지

image

에지 구하는 법

미분을 사용
즉, 곡선에서 픽셀값이 급격하게 증가한 부분 = 1차 미분값이 가장 큼

[but] 픽셀은 연속공간에 있지 않음 -> 미분의 근사값을 구하자! 주변보다 1차 미분값이 큰 부분 이 에지로 검출

[미분의 근사값] 붙어 있는 픽셀 값을 빼면 된다.
x방향, y방향으로 각각 픽셀 값을 빼면 미분 근사값이 된다.
밑의 필터도 다 붙어 있는 값이죠?
image

  • x filter*: 수직방향의 에지 검출
  • y filter*: 수평방향의 에지 검출

image

소벨 필터를 이용한 미분 함수 원형

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)

(정제 전) image

(정제 후) image

소벨 필터 합치기

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()

image

image

샤를 필터를 이용한 미분 함수

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()

[소벨필터와 샤를 필터 결과값 차이] (소벨) image

(샤를) image


캐니 에지 디텍터 Canny Edge Detector

캐니 에지 디텍터는 1986년 존 F. 캐니에 의해서 개발됨.
캐니 에지 디텍터를 사용하면 2개의 임계값을 사용하여 한 픽셀로 구성된 에지를 검출함
가장 우월
윤곽을 가장 잘 찾아냄
원래 영상의 회색물질과 관련된 모든 에지들을 제거할 수 있는 유일한 방법

[목표]
에지 검출 시 다음 3가지를 만족하는 것을 목표로 함

  • 낮은 에러율: 실제 에지가 검출되어야 함. 노이즈로 인해 거짓 에지가 검출되면 안됨.
  • 정확한 에지 위치: 캐니 에지와 실제 이미지의 에지 사이의 거리가 최소가 되어야 함.
  • 응답 최소화: 실제 이미지상의 에지에서 하나의 에지만 검출되어야 함

[단점]
구현 복잡하고 실행시간이 길다.

image

함수 원형

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()


OpenCV Share Tweet +1