관심쟁이 영호
[#16] OpenCV (With. C++) ㅣ 영역처리ㅣ 샤프닝, 에지 본문
안녕하세요!
관심쟁이 영호입니다.
오늘 공부해볼 과목은
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차 미분 연산을 이용한 에지 검출에 대해서 공부를 해볼게요!
'학교공부 > OpenCV' 카테고리의 다른 글
[#19] OpenCV (With. C++) ㅣ 모폴로지 -침식, 팽창 연산 (0) | 2020.11.26 |
---|---|
[#18] OpenCV (With. cpp) ㅣ 캐니 에지, 최대/최소/평균/미디언/가우시안 필터링 (0) | 2020.11.25 |
[#15] OpenCV (With. CPP) ㅣ 영역처리 ver.1 ㅣ 회선, 블러링 (0) | 2020.11.22 |
[#14] OpenCV (With. C++) ㅣ 화소처리 ver. 2 ㅣat함수 상세히! (0) | 2020.11.18 |
[#13] OpenCV(With. c++)ㅣ컬러공간 변환! ㅣRGB, CMY (0) | 2020.11.16 |