Edge Detection

2023. 11. 12. 20:14컴퓨터비전

728x90

Line Detector

Hozontal mask를 예를 들어보면 

이 mask를 통과시킨다면 수직방향으로 값의 차이가 있으면 0이 아닌 값을 반환,

수직방향으로 값의 차이가 없다면 0인 값을 반환.

-> y방향으로의 변화량을 보기 위함.

 

다른 mask들도 같은 메커니즘이다.

 

mask의 있는 값들을 모두 더하면 0이 되어야 한다.

 

 

Edge detection

: 밝기가 급격히 변하는 부분을 발견하는 것

 

Zero crossing

: intensity를 2차 미분했을 때 최고점과 최저점을 직선으로 이었을 때

0이 되는 부분을 에지라고 정의하면 바람직하다.

 

Gradient

gradient: 기울기
gradient의 크기

루트는 연산속도가 느려서 절댓값으로 근사해서 계산한다.

gradient의 방향성

 

Effect of noise

 

작은 noise가 섞였을 뿐이지만 여러 번 미분할수록 티가 많이 난다.

 

 

Three Fundamental Steps

1. noise를 줄이기 위해 이미지 smoothing

2. edge point들 detection 하기

3. Edge localization

 

 

1. Smoothing

Gaussian Filter

  • 가우시안 분포는 중심을 기준으로 대칭을 이룸
  • 총면적은 1
  • 중심에 더 많은 weight를 주고 거리가 멀어질수록 weight가 감소하는 kenel을 갖는다.
  • 시그마값이 커질수록 weight는 고르게 한다
    -> 노이즈가 많으면 시그마값을 크게 해야 한다, 강한 edge만 살린다.
    시그마값이 작아질수록 중심에 더 큰 weight를 준다.
    -> 노이즈가 적으면 시그마값을 작게 해야 한다, 약한 edge도 확인가

 

f는 noise가 섞인 이미지

g는 가우시안 필터

-> convolution 하면 noise가 제거

 

 

linear 하므로 g를 먼저 미분하고 f와 convolution해도 

결과는 같아진다.

 

 

2. Edge points detection

sobel mask

왼쪽이 y방향 edge 탐지, 오른쪽이 x방향 edge 탐지

->  x의 변화량으로는 y방향의 edge를 검출할 수 있으며,

y의 변화량으로는 x방향의 edge를 검출할 수 있다.

 

 

Thresholding

: 이미지에서 가장 높은 픽셀 값의 threshold=t%인 부분들만 선택

오른쪽이 t=33%, noise가 많이 제거된걸 볼수 있다.

 

#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;

Mat Filter(const Mat& img, const vector <vector <double> >& kernel,int ksize) {
	int center = ksize / 2; //커널의 중심 인덱스
	Mat result = Mat::zeros(img.rows, img.cols, img.type()); //zero padding 

	for (int y = center;y < img.rows - center;y++) {
		for (int x = center;x < img.cols - center;x++) {
			double sum = 0.0;
			//convolution
			for (int i = -center;i <= center;i++) {
				for (int j = -center;j <= center;j++) {
					sum += (img.at<uchar>(y + i, x + j) * kernel[i + center][j + center]);
				}
			}
			result.at<uchar>(y, x) = saturate_cast<uchar>(sum); //0~255범위를 넘지 않게 하기 위해
		}
	}
	return result;
}

Mat Threshold(const Mat& img, double threshold) {
	Mat result = img.clone();
	double pixel = 0;

	for (int y = 0;y < img.rows;y++) {
		for (int x = 0;x < img.cols;x++) {
			pixel = img.at<uchar>(y, x);

			if (pixel > threshold) {
				result.at<uchar>(y, x) = img.at<uchar>(y, x);
			}
			else
				result.at<uchar>(y, x) = 0;
		}
	}
	return result;
}

Mat Sobel(const Mat& img) {
	vector<vector <double> > sobel_x = {
		{-1,0,1},
		{-2,0,2},
		{-1,0,1}
	};
	vector<vector <double> > sobel_y = {
		{-1,-2,-1},
		{0,0,0},
		{1,2,1}
	};
	Mat gradient_x = Filter(img, sobel_x, 3); //x에 대하여 필터링
	Mat grdient_y = Filter(img, sobel_y, 3); //y에 대하여 필터링

	Mat result1, result2, result3;
	convertScaleAbs(gradient_x, result1); // |gx|
	convertScaleAbs(grdient_y, result2); //|gy|

	add(result1, result2, result3, noArray(), img.type());

	return result3;
}
Mat Sobel_x(const Mat& img) {
	vector<vector <double> > sobel_x = {
		{-1,0,1},
		{-2,0,2},
		{-1,0,1}
	};
	
	Mat gradient_x = Filter(img, sobel_x, 3); //x에 대하여 필터링


	Mat result1;
	convertScaleAbs(gradient_x, result1); // |gx|

	return result1;
}

Mat Sobel_y(const Mat& img) {
	
	vector<vector <double> > sobel_y = {
		{-1,-2,-1},
		{0,0,0},
		{1,2,1}
	};
	
	Mat grdient_y = Filter(img, sobel_y, 3); //y에 대하여 필터링

	Mat result1;

	convertScaleAbs(grdient_y, result1); //|gy|

	return result1;
}

int main() {
	Mat img = imread("Lena512.jpg",IMREAD_GRAYSCALE);

	Mat result1,result2;

	result1 = Sobel_x(img);
	imshow("sobel x", result1);

	result1 = Sobel_y(img);
	imshow("sobel y", result1);

	result1 = Sobel(img);
	imshow("sobel x+y", result1);

	double t = 0; //threshold
	t = 255 * 0.49;
	result2 = Threshold(result1, t);
	imshow("sobel,t=49%", result2);

	Mat result3,result4,result5;

	GaussianBlur(img, result3, Size(3,3), 0);
	result3 = Sobel(result3);
	result3 = Threshold(result3, t);
	imshow("Gaussian+Sobel", result3);
	

	GaussianBlur(img, result5, Size(7,7), 0);
	result5 = Sobel(result5);
	result5 = Threshold(result5, t);
	imshow("Gaussian+Sobel7*7", result5);
	//커널의 크기가 클수록 노이즈가 더욱 제거됨

	
	waitKey(0);
	return 0;

}

Canny Edge Detector

3가지 기본 목표

  1. 낮은 오차율
  2. edge point들이 잘 localized 돼야 함
    -> 어디가 edge인지 정확히 구별해야 함

Hysteresis thresholding

: weak edge를 살릴지 말지 주변을 보고 결정하는 방법

  • threshold 2개를 정의한다: Low and High
  • Low보다 작다면 edge가 아니다
  • High보다 크다면 strong edge이다
  • Low와 High 사이라면 weak edge이다

그림과 같이 진한선들 사이의 약한선도 edge로 보도록 한다

 

 

3. edge가 하나만 존재하면 하나로 표현
(여러 개로 표시하지 않고)

 

Non-maximum suppression

: 두꺼운 선은 어떻게 표현할 것인가에 해결법
(single edge point를 얻기 위한 기법)

-> 최댓값만 선택한다

오른쪽이 적용한 것.

 

 

Hough transform

: line detecting 기술

 

방법

: 가능한 모든 라인에 대해 투표를 진행해 기록하고 

가장 많은 표를 받은 라인을 선택한다.

 

그림과 같이 변형하면 직선이 점 하나로 표현이 가능해진다. 

점을 직선으로 표현하는 것 또한 가능.

Hough space에서 교점을 찾기
-> 교점이 생긴다는 건 원래 영상을 이으면 직선이 된다는 뜻

 

728x90