관심쟁이 영호

[#16] OpenCV (With. C++) ㅣ 영역처리ㅣ 샤프닝, 에지 본문

학교공부/OpenCV

[#16] OpenCV (With. C++) ㅣ 영역처리ㅣ 샤프닝, 에지

관심쟁이 영호 2020. 11. 23. 01:30
반응형

안녕하세요!

관심쟁이 영호입니다.

 

오늘 공부해볼 과목은

OpenCV입니다.

 


이전에 했던 회선 연산, 블러링을 응욯해서!

샤프닝과 에지에 대해서 공부를 할 계획이에요.

 

샤프닝이란?

블러링은 인접한 화소가 크게 차이가 날 때, 차이를 덜 나게 하여

영상 데이터를 부드럽게 하는 것이었다면

샤프닝은 더더욱 차이가 나게 만들어서 날카로운 느낌이 나도록 도와주는 것입니다.

 

주로 강조할 때, 경계 부분 명암대비의 증가를 위해 사용됩니다.

 

이 또한 블러링처럼 마스크 값이 결정되어 있는데요!

마스크 값을 살펴볼게요.

 

-1 -1 -1
-1 9 -1
-1 -1 -1

이렇게 나타낼 수 있어요!

모든 값의 합은 1이 나와야 하고, 중앙값은 1보다 현저히 크게!

다른 값들은 작게 해서 중앙값이 더욱 크게 만들어 주는 것입니다.

 

그럼 회선을 이용한

코드를 바로 살펴볼게요!

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void filter(Mat img, Mat& dst, Mat mask) {
	dst = Mat(img.size(), CV_32F, Scalar(0));
	Point h_m = mask.size() / 2;

	for (int i = h_m.y; i < img.rows - h_m.y; i++) // 입력 행렬 반복 순회
	{
		for (int j = h_m.x; j < img.cols; j++)
		{
			float sum = 0;

			for (int u = 0; u < mask.rows; u++) //마스크 원소를 순회한다.
			{
				for (int v = 0; v < mask.cols; v++)
				{
					int y = i + u - h_m.y;
					int x = j + v - h_m.x;
					sum += mask.at<float>(u, v) * img.at<uchar>(y, x); //회선 수식!
				}

			}
			dst.at<float>(i, j) = sum; //회선 누적값 출력화소 저장!
		}

	}
}

int main()
{
	Mat image = imread("../image/sample.jpg", IMREAD_COLOR);
	
	CV_Assert(image.data);

	float data[] = {						//샤프닝 마스크 지정
		-1, -1, -1,
		-1, 9, -1,
		-1,-1,-1
	};


	Mat mask(3, 3, CV_32F, data);
	Mat sharpe;
	filter(image, sharpe, mask); //회선 수행
	sharpe.convertTo(sharpe, CV_8U);


	imshow("image", image), imshow("sharpe", sharpe);
	waitKey(0);

		return 0;
}

코드 결과를 이렇게 살펴볼 수 있어요!

이전에 했던 블러링과 같죠!

마스크 배열을 정해두고 해당 배열을 영상 데이터에 순회하면서 곱해주면서 값을 얻어내는 방식입니다!

참 쉽죠!?

 

에지란?

영상 처리에서 에지란, 급격하게 화소 값이 변화하는 부분으로 말합니다!

그럼 급격하게 화소값이 변화하는 것을 어떻게 아느냐?

이때의 방법은 많아요!

 

1. 차분 연산 - 유사 연산자 에지 검출 ,  차 연산자 에지 검출

 

차분 연산은 차 분연산을 한 후, 가장 큰 값을 출력 화소로 결정하는 방법입니다!

여기서 유사 연산자는 중앙값을 기준으로 (중앙값 - 주변 화소 한 개) 가장 큰 값을 찾고

차 연산자는 자신의 대각선에 있는 값을 계산하여 가장 큰 값을 정하는 것입니다!

 

M0 M1 M2 -유사 연산자 =
MAX(ㅣC-M0ㅣ, ㅣC-M1ㅣ, ㅣC-M2ㅣ,ㅣ C-M3ㅣ, ㅣC-M5ㅣ, ㅣC-M6ㅣ,ㅣ C-M7ㅣ, ㅣC-M8)

-차 연산자 = 
MAX(ㅣM0-M8ㅣ, ㅣM1-M7ㅣ, ㅣM2-M6ㅣ, ㅣM3-M5ㅣ)
M3 C M5
M6 M7 M8

코드로 바로 살펴볼게요!

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void differOp(Mat img, Mat& dst, int mask_size) {

	dst = Mat(img.size(), CV_8U, Scalar(0));
	Point h_m(mask_size / 2, mask_size / 2); //마스크의 절반 크기!
	int mask_length = mask_size * mask_size; //마스크 전체 개수

	for (int i = h_m.y; i < img.rows - h_m.y; i++) // 입력 영상 반복 순회
	{
		for (int j = h_m.x; j < img.cols - h_m.x; j++)
		{
			vector<uchar> mask(mask_length, 0);


			for (int u = 0, k = 0; u < mask_size; u++) //마스크 범위를 순회한다.
			{
				for (int v = 0; v < mask_size; v++, k++)
				{
					int y = i + u - h_m.y;
					int x = j + v - h_m.x;
					mask[k] = img.at<uchar>(y,x); //입력 화소 마스크에 저장
				}

			}
			uchar max = 0;

			for (int k = 0; k < mask_length / 2; k++) // 전체 원소 절반만 순회
			{
				int start = mask[k]; //시작 방향 원소
				int end = mask[mask_length - k - 1]; //종료 방향 원소
				uchar difference = abs(start - end); //차분 계산
				if (difference > max) max = difference; //차분 최댓값
			}
			dst.at<uchar>(i, j) = max; //출력 화소에 차분 차분 최댓값 저장
		}
	}

	}



int main()
{
	Mat image = imread("../image/sample.jpg", 0);
	
	CV_Assert(image.data);

	Mat edge;
	differOp(image, edge, 3);


	

	imshow("image", image), imshow("edge", edge);
	waitKey(0);

		return 0;
}

위의 과정을 순서대로 보자면!

 

영상을 흑백으로 받는다. -> differOp함수로 보낸다. -> differ함수에서 영상 전체를 순회하면서 최댓값을 찾는다. -> 최댓값으로 간주되는 부분만 화면에 표시한다.

 

정도로 볼 수 있어요!

 

2. 1차 미분 연산 - 로버츠 마스크, 프리윗 마스크, 소벨 마스크

 

함수의 순간 변화율을 구하는 과정을 통해서 화소의 밝기가 급격히 변하는 부분을 찾아서 에지를 검출하는 과정입니다!

 

3. 2차 미분 연산 - 라플라시안 에지 검출, LOG/DOG 에지 검출

 

4. 캐니 에지 검출

 


오늘은 여기까지!

다음 시간에는

1차 미분 연산을 이용한 에지 검출에 대해서 공부를 해볼게요!

 

 

300x250
Comments