관리 메뉴

ComputerVision Jack

[이미지 - 동전 검출] 본문

Campus Project/Homework

[이미지 - 동전 검출]

JackYoon 2020. 1. 16. 18:37
반응형

영상을 읽어와 동전별로 검출한 후, 값을 결과로 출력하는 프로그램

동전 검출 ex

우선 위 이미지가 입력으로 프로그램에 들어오게 된다

따라서 프로그램이 10원과 100원 500원을 각각 검출하여 동전의 총합을 구하는 프로그램을 구성했다.

 

Mat image = imread("../_res/coin.jpg", IMREAD_GRAYSCALE);
CV_Assert(!image.empty());

#우선 Mat객체에 이미지를 읽어와 작업환경을 생성합니다. 이미지가 들어 있는 폴더의 경로를 잘 설정한다.

흑백 (그레이 스케일)으로 읽어온다.

 

Mat EdgeImage(image.size(), CV_8U, Scalar(0));
CannyEdgeDetection(&image, &EdgeImage, 4.5, 30.0, 60.0);

#엣지 이미지를 따올 Mat객체를 생성하고

케니 엣지를 적용하여 엣지를 detect한 후 저장한다.

 

int c10, c100, c500;
CircleDetection(&EdgeImage, 35, 36, 1, 0.6, &findCircle);
c10 = findCircle.size();
CircleDetection(&EdgeImage, 42, 43, 1, 0.6, &findCircle);
c100 = findCircle.size() - c10;
CircleDetection(&EdgeImage, 51, 52, 1, 0.6, &findCircle);
c500 = findCircle.size() - c10 - c100;

#허프 변환 원검출을 통하여 10원과 100원 500원 사이즈에 맞게 원을 검출합니다.

그리고 findCircle()함수를 통하여 갯수를 변수에 누적

 

imshow("원본", image);
imshow("엣지 찾기", EdgeImage);

#결과를 출력하면

결과

정말 이쁘게 각각의 동전을 검출함.

동전 금액 결과

동전의 금액 결과 까지 콘솔창에 뿌려준다.

 

[함수 정의]

openCV라이브러리를 사용하지 않고 검출에 필요한 개념에 맞게 직접 함수를 구현하여 해결.

func.cpp에 정의된 내용.

 

케니 엣지 함수

void CannyEdgeDetection(Mat* image, Mat* edgeimg, double sigma, double th_low, double th_high)

 

Mat gauss(image->size(), CV_8U, Scalar(0));
GaussianFiltering(image, &gauss, sigma);

#우선 가우시안 필터링을 사용하여 영상의 잡음을 제거한다

 

Mat gradX(image->size(), CV_64F, Scalar(0));
Mat gradY(image->size(), CV_64F, Scalar(0));
Mat gradMag(image->size(), CV_64F, Scalar(0));

for (j = 1; j < h - 1; j++)
{
for (i = 1; i < w - 1; i++)
{
gradX.at(j, i) = -gauss.at(j - 1, i - 1) - 2.0*gauss.at(j, i - 1) - gauss.at(j + 1, i - 1)
+ gauss.at(j - 1, i + 1) + 2.0*gauss.at(j, i + 1) + gauss.at(j + 1, i + 1);

gradY.at(j, i) = -gauss.at(j - 1, i - 1) - 2.0*gauss.at(j - 1, i) - gauss.at(j - 1, i + 1)
+ gauss.at(j + 1, i - 1) + 2.0*gauss.at(j + 1, i) + gauss.at(j + 1, i + 1);

double gx = gradX.at(j, i);
double gy = gradY.at(j, i);
gradMag.at(j, i) = sqrt(gx*gx + gy * gy);
}
}

#그 다음 gradient를 계산하는 작업을 한다. (크기와 방향)

 

if (mag > th_low)
{
// 그래디언트 방향 계산 (4개 구역)
if (gx != .0)
{
ang = atan2(gy, gx) * 180 / PI;
if (((ang >= -22.5f) && (ang < 22.5f)) || (ang >= 157.5f) || (ang < -157.5f))
district = AREA0;
else if (((ang >= 22.5f) && (ang < 67.5f)) || ((ang >= -157.5f) && (ang < -112.5f)))
district = AREA45;
else if (((ang >= 67.5) && (ang < 112.5)) || ((ang >= -112.5) && (ang < -67.5)))
district = AREA90;
else
district = AREA135;
}
else
district = AREA90;

#위의 식처럼 비최대 억제를 구한다. 코드가 길어 다 올리는것은 효율이 떨어진다고 생각한다.

 

while (!strong_edges.empty())
{
Point p = strong_edges.back();
strong_edges.pop_back();
int x = p.x, y = p.y;

// 강한 엣지 주변의 약한 엣지는 최종 엣지(강한 엣지)로 설정
check_weak_edge(edgeimg, &strong_edges, x + 1, y);
check_weak_edge(edgeimg, &strong_edges, x + 1, y + 1);
check_weak_edge(edgeimg, &strong_edges, x, y + 1);
check_weak_edge(edgeimg, &strong_edges, x - 1, y + 1);
check_weak_edge(edgeimg, &strong_edges, x - 1, y);
check_weak_edge(edgeimg, &strong_edges, x - 1, y - 1);
check_weak_edge(edgeimg, &strong_edges, x, y - 1);
check_weak_edge(edgeimg, &strong_edges, x + 1, y - 1);
}

#히스테리시스 엣지 트래킹을 사용하여 약한 엣지와 강한 엣지를 파악한다.

 

허프변환 원검출

원 검출의 코드 또한 어마어마하다.

void CircleDetection(Mat* pSrc, int minradius, int maxradius, int radiusoffset, double ratio, vector* pvecCircle)

 

int ksize = 15;
int halfksize = ksize >> 1;
int minarea = (int)(PI*(minradius + maxradius)*ratio + .5);
for (int y = halfksize; y < h - halfksize; y++)
{
for (int x = halfksize; x < w - halfksize; x++)
{
int val = curmask.at(y, x);
bool bMax = true;
int nMax = 0;

if (val < minarea) continue;
for (int n = -halfksize; n <= halfksize; n++)
{
for (int m = -halfksize; m <= halfksize; m++)
{
if (n == 0 && m == 0) continue;
if (val < curmask.at(y + n, x + m))
{
bMax = false;
break;
}
}
if (bMax == false) break;
}
if (bMax)
{
nMax = curmask.at(y, x);
for (int n = -halfksize; n <= halfksize; n++)
{
for (int m = -halfksize; m <= halfksize; m++)
{
curmask.at(y + n, x + m) = 0;
}
}
curmask.at(y, x) = nMax;
}
}
}

#마스크를 통하여 원을 검출한다. 코드 부분을 삽입한 것.

 

for (int i = 0; i < circnt; i++)
{
if (34 <= pvecCircle->at(i)._radius && pvecCircle->at(i)._radius <= 37)
circle(resultcolor, pvecCircle->at(i)._center, pvecCircle->at(i)._radius, Scalar(0, 255, 255), 2);
else if (50.f <= pvecCircle->at(i)._radius && pvecCircle->at(i)._radius <= 53.f)
circle(resultcolor, pvecCircle->at(i)._center, pvecCircle->at(i)._radius, Scalar(0, 0, 255), 2);
else  if (39.f <= pvecCircle->at(i)._radius && pvecCircle->at(i)._radius <= 43.f)
circle(resultcolor, pvecCircle->at(i)._center, pvecCircle->at(i)._radius, Scalar(255, 0, 255), 2);
}

#반복 루프를 돌면서 각각의 원사이즈에 맞게 색을 입히는 과정

 

케니 엣지를 통하여 엣지를 판별하고 허프 변환을 사용해서 원을 검출한 후,

색을 다르게 주어 끝내면 된다.

func.cpp
0.02MB

다양한 영상처리 라이브러리를 함수와해 놓았다.

반응형

'Campus Project > Homework' 카테고리의 다른 글

[머신러닝 - 2차원 2클래스 분류]  (0) 2020.01.21
[게임 - snake game]  (2) 2020.01.21
[컴퓨터 비전 - Face Detect]  (0) 2020.01.20
[이미지 - Perspective Transform]  (0) 2020.01.19
[이미지 - 필터링 적용]  (1) 2020.01.17
Comments