1. 에지 검출과 영상의 미분
1) 에지 : 영상에서 픽셀 밝기 값이 급격히 변하는 부분으로 객체와 객체 또는 객체와 배경의 의 경계를 표현한다.
에지검출(edge detection)은 객체분할 및 인식을 위한 기본 과정
2) 1차 미분의 근사화
a) 전진차분
b) 후진차분
c) 중앙차분
중앙차분을 선택 시 가장 미분을 정확히 계산하게 됨
** h 의 최솟값 1px
** 미분
f(x) = x^n
f'(x) = n*x^(n-1)
f(x) = sinX
f'(x) = -cosX
f(x) = e^x
f'(x) = e^x
3) 다양한 에지 검출마스크
- Prewitt (plat한 가중치) , Sobel(가우시안 형태의 가중치 적용) , Scharr
가로방향
-1 0 1 -1 0 1 -3 0 3
-1 0 1 -2 0 2 -10 0 10
-1 0 1 -1 0 1 -3 0 3
세로방향
-1 -1 -1 -1 -2 -1 -3 -10 -3
0 0 0 0 0 0 0 0 0
1 1 1 1 2 1 3 10 3
Prewitt Sobel Scharr
Sobel이 Prewitt보다 좋은 결과 얻을 수 있음.
Sharr 가 Sobel 보다 좀 더 정확하나 Sobel이 간단하고 연산도 빠르기에 Sobel 사용
소벨 마스크 구현
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image laod failed!" << endl;
return -1;
}
Mat dx = Mat::zeros(src.rows, src.cols, CV_8UC1); // Gray scale 형식
/*
//dst 픽셀값 for문 이용해 채우기
for (int y = 0; y < src.rows; y++) {
for (int x = 0; x < src.cols; x++) {
//입력영상의 픽셀값 접근 , 소벨마스크
int v1 = src.at<uchar>(y - 1, x + 1) // y -1 이나 x-1 이 처음에 y, x 0일 경우 -1 값으로 나오게되어 assert에러 뜸.
+ src.at<uchar>(y, x + 1) * 2
+ src.at<uchar>(y + 1, x + 1)
- src.at<uchar>(y - 1, x - 1)
- src.at<uchar>(y, x - 1) * 2
- src.at<uchar>(y + 1, x - 1);
//출력영상의 픽셀값 접근
dx.at<uchar>(y, x) = saturate_cast<uchar>(v1); // 255보다 커지거나 0보다 작아질경우 staturate cast하여 uchar 변환 후 v1값을 dx에 넣기.
}
}
*/
/*
assert error
((unsigned)i0 < (unsigned)size.p[0])
에서 참이 아닌 거짓이 나온 경우 에러뜸.
*/
//dst 픽셀값 for문 이용해 채우기
for (int y = 1; y < src.rows-1; y++) { //최외곽은 엣지가 적용되지 않지만 이걸 감안해서 코드작성. ( 개개별로 조건문 달면 되나 코드가 좀 더러워지기에 이 와 같이 작성함)
for (int x = 1; x < src.cols-1; x++) {
//입력영상의 픽셀값 접근 , 소벨마스크
int v1 = src.at<uchar>(y - 1, x + 1)
+ src.at<uchar>(y, x + 1) * 2
+ src.at<uchar>(y + 1, x + 1)
- src.at<uchar>(y - 1, x - 1)
- src.at<uchar>(y, x - 1) * 2
- src.at<uchar>(y + 1, x - 1);
//출력영상의 픽셀값 접근
//dx.at<uchar>(y, x) = saturate_cast<uchar>(v1); // 255보다 커지거나 0보다 작아질경우 staturate cast하여 uchar 변환 후 v1값을 dx에 넣기.
//다만 이렇게 하여 출력시 미분값 낮아지는 부분들은 그레이스케일 영상에 의해 엣지가 나타나지 않음
dx.at<uchar>(y, x) = saturate_cast<uchar>(v1 + 128); // 미분값이 커지는 부분과 작아지는 부분 모두 엣지가 잡혀짐
//dx이미지에서 픽셀값이 흰색에 가까워질 정도로 밝거나 검정색에 가까워질 정도로 어두운 부분은 픽셀값의 변화가 큰 부분이다.
}
}
imshow("src", src);
imshow("dst", dx);
waitKey();
}
dy도 구현하자
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image laod failed!" << endl;
return -1;
}
Mat dx = Mat::zeros(src.rows, src.cols, CV_8UC1); // Gray scale 형식
Mat dy = Mat::zeros(src.rows, src.cols, CV_8UC1);
//dst 픽셀값 for문 이용해 채우기
for (int y = 1; y < src.rows-1; y++) { //최외곽은 엣지가 적용되지 않지만 이걸 감안해서 코드작성. ( 개개별로 조건문 달면 되나 코드가 좀 더러워지기에 이 와 같이 작성함)
for (int x = 1; x < src.cols-1; x++) {
//입력영상의 픽셀값 접근 , 소벨마스크
int v1 = src.at<uchar>(y - 1, x + 1)
+ src.at<uchar>(y, x + 1) * 2
+ src.at<uchar>(y + 1, x + 1)
- src.at<uchar>(y - 1, x - 1)
- src.at<uchar>(y, x - 1) * 2
- src.at<uchar>(y + 1, x - 1);
int v2 = src.at<uchar>(y + 1, x + 1)
+ src.at<uchar>(y + 1, x) * 2
+ src.at<uchar>(y + 1, x - 1)
- src.at<uchar>(y - 1, x + 1)
- src.at<uchar>(y - 1, x) * 2
- src.at<uchar>(y - 1, x - 1);
dx.at<uchar>(y, x) = saturate_cast<uchar>(v1 + 128); // 미분값이 커지는 부분과 작아지는 부분 모두 엣지가 잡혀짐
dy.at<uchar>(y, x) = saturate_cast<uchar>(v2 + 128);
}
}
imshow("src", src);
imshow("dx", dx);
imshow("dy", dy);
waitKey();
}
기둥을 보면 x축에선 미분변화값이 큼에 따라 엣지가 추출됬지만, y축에선 미분변화값이 일정하여(거의 0의 값) 엣지가 없는 걸 확인 할수 있다. (좌측)
입력영상에서 어느 한 방향으로만 큰 변화가 있을 경우, x방향으로의 미분과 y방향으로의 미분 모두 조합해 그 결과를 반영하여, 입력영상에서 임의의 방향에서 변화가 큰걸 모두 검출 할 수 있다.
dx, dy값을 어떻게 조합해서 변화량이 큰 부분을 모두 찾을 수 있을 지 고민을 해야한다.
2. 다양한 미분 마스크
'프로그래머스 > OPENCV' 카테고리의 다른 글
캐니 에지 검출기 (0) | 2022.12.07 |
---|---|
에지검출과 소벨필터(2) (0) | 2022.12.07 |
색상범위 관련 (0) | 2022.12.07 |
특정 색상 영역 추출 하기 [다시] (0) | 2022.12.07 |
컬러영상처리 (0) | 2022.12.07 |