학습 내용
1. Bilinear Interpolation
2. Edge 방향 구하는법 / Non-Maxima Suppression
3. Canny Edge Operatior
1. Bilinear Interpolation
정확히 엣지 픽셀을 찾아내기란 어렵다. 엣지값을 정확하게 구하려면, Bilinear Interpolation이 필요할때가 있다.
2. Edge 방향 구하는법 / Non-Maxima Suppression
360도를 8등분해서 4방향만 있다고 생각하자.
그리고, Non-Maxima Suppression은 극댓값을 찾는 과정을 말한다.
아래의 방식은 FDG를 적용한 이후부터를 말하는거다.
_igDir._ppA[i][j]는 바로 이전 사진에서 0~3으로 분류된 값이다.
즉, 예를들어 _igDir._ppA[i][j]이 0이면 노란색 영역을 살펴볼텐데, 오른쪽 노란색과 왼쪽 노란색을 둘 다 보는게 위의 소스다.
3. Canny Edge Operatior
<<캐니 엣지 검출 방법>>
해당 픽셀이 캐니 엣지인지 보려면, 그 픽셀 주변으로 (위에서 말한) 0~3 방향을 봤을때
그래디언트가 일정 threshold(T1)를 넘으면 확실히 검출하고
T1보다는 작지만, T2보다는 크면, 일단 후보군으로 넣어두고,
T2보다 작으면 절대로 엣지가 아니라고 판단한다.
아래 소스의 nHalf는 gradient 판별 kernel의 크기의 절반을 의미한다.
void Jeong::CannyEdgeDetection(const KImageGray& igIn, KImageGray &igOut, int lowT, int highT)
{
int nHalf = 1;
int nRow = igIn.Row(), nCol = igIn.Col();
int nSize = nRow * nCol;
double dGradX, dGradY;
double dSigma = 0.3;
double d2Sigma = 2. * dSigma * dSigma;
double dConstant = 1/(d2Sigma * _PI * dSigma * dSigma);
double dScale = 0;
int dx[4] = {1, 1, 0, -1};
int dy[4] = {0, 1, 1, 1};
int dx8[8] = {1, 1, 0, -1, -1, -1, 0, 1};
int dy8[8] = {0, 1, 1, 1, 0, -1, -1, -1};
using namespace std;
KImageGray igMag(nRow,nCol), igAng(nRow,nCol), igDir(nRow,nCol), igEdge(nRow,nCol), igBuf(nRow,nCol);
KMatrix mKernelX(nHalf*2+1, nHalf*2+1), mKernelY(nHalf*2+1, nHalf*2+1);
// FDG를 위한 masking kernel
for(int i=-nHalf, ii=0; i<=nHalf; i++, ii++){ // i가 v
for(int j=-nHalf, jj=0; j<=nHalf; j++, jj++){ // j 가 u
mKernelY[ii][jj] = 1/(dConstant)*(-i)*exp(-i*i/(d2Sigma)) * exp(-j*j / (d2Sigma));
mKernelX[jj][ii] = mKernelY[ii][jj];
dScale += (i<0?mKernelY[ii][jj] : 0.0);
}
}
// 이러면 이중포문 돌아서 모든 원소 나눗셈 해준다. operator 확인해봐라.
mKernelX /= -dScale;
mKernelY /= -dScale;
// 1. Gradient Image 만들기 with FDG
for(int i=nHalf; i<nRow-nHalf;i++)
for(int j=nHalf; j<nCol-nHalf;j++){
dGradX = dGradY = 0.0;
for(int r = -nHalf, rr=0; r<=nHalf; r++, rr++)
for(int c = -nHalf, cc=0; c<=nHalf; c++, cc++){
dGradX += igIn[i+r][j+c] * mKernelX[rr][cc]; // 가로방향
dGradY += igIn[i+r][j+c] * mKernelY[rr][cc]; // 세로방향
}
// magnitude
igMag[i][j] = abs(dGradX)+abs(dGradY);
// lowT 넘나?
if(igMag[i][j]>lowT){
igAng[i][j] = atan2(dGradY, dGradX) * (180.0/_PI)+ 0.5; //(unsigned short)(oMath.Atan(dGradY, dGradX)+0.5); 이 씨발때문에... 존나 고생함.. // angle(DEG)
igDir[i][j] = (unsigned char)((((int)(atan2(dGradY, dGradX) * (180.0/_PI)/22.5)+1)>>1) & 0x00000003); // direction (RAD을 DEG로 바꿔서 계산)
// igDir[i][j] = (unsigned char)((((int)(igAng[i][j]/22.5)+1)>>1) & 0x00000003); // 이거대로 하면 잘 안된다!!!!!
}else{
igMag[i][j]=0;
}
}
//3.에서 pop용으로 쓰일 vector선언
vector<EdgeInfo*> vtmp;
//2. magnitude가 다 등록되었으로, Edge를 찾아서 등록하자
for(int i=nHalf; i<nRow-nHalf; i++)
for(int j=nHalf; j<nCol-nHalf; j++){
if(igMag[i][j]==0)
continue;
else{ // 극대인지 check
if(igMag[i][j]>igMag[i + dy[igDir[i][j]]][j + dx[igDir[i][j]]]
&&
igMag[i][j] > igMag[i - dy[igDir[i][j]]][j - dx[igDir[i][j]]]){
if(igMag[i][j] > highT){// 극대이면서 highT보다 높으면 Edge지.
igOut[i][j] = 255;
EdgeInfo* tmp = new EdgeInfo;
tmp->_nx = j;
tmp->_ny = i;
tmp->_ang = igAng[i][j]; // RAD
tmp->_dir = igDir[i][j];
tmp->_mag = igMag[i][j];
Edge.push_back(tmp);
vtmp.push_back(tmp); // 3.에서 pop용으로 만들었음.
}else{ // 극대이긴 하지만, highT보다 작으므로, 애매한 녀석으로 등록.
igBuf[i][j] = 255;
}
}
}
}
// 3. 애매한 녀석들 처리 -> Edge 주변에 애매한 녀석들이 있으면, 걔도 Edge로 등록
while(!(vtmp.size()==0)){
int i=vtmp.back()->_ny;
int j=vtmp.back()->_nx;
vtmp.pop_back();
for(int k=0; k<8; k++){
if(igBuf[i+dx8[k]][j+dy8[k]]==255){
igBuf[i+dx8[k]][j+dy8[k]]=0;
igOut[i+dx8[k]][j+dy8[k]]=255;
EdgeInfo* tmp = new EdgeInfo;
tmp->_nx = j+dy8[k];
tmp->_ny = i+dx8[k];
tmp->_ang = igAng[i+dx8[k]][j+dy8[k]]; // DEG
tmp->_dir = igDir[i+dx8[k]][j+dy8[k]];
tmp->_mag = igMag[i+dx8[k]][j+dy8[k]];
Edge.push_back(tmp);
vtmp.push_back(tmp);
}
}
}
}
출처
'Computer Vision' 카테고리의 다른 글
15. Hough Transform Circle (0) | 2021.02.07 |
---|---|
14. Corner Detection(Harris Corner) (0) | 2021.01.22 |
12. Edge Detection (Discrete 환경, Noise 환경) (0) | 2021.01.22 |
11. Fourier Transform (1) | 2021.01.13 |
10. Sampling Theorem (Image Scaling, Image Subsampling) (0) | 2021.01.10 |
댓글