이 페이지의 내용은 이전 릴리스에 관한 것입니다. 해당 영문 페이지는 최신 릴리스에서 제거되었습니다.

영상에서 원형 객체를 검출하고 측정하기

이 예제에서는 imfindcircles를 사용하여 영상에서 원이나 원형 객체를 자동으로 검출하는 방법을 보여줍니다. 또한 viscircles를 사용하여, 검출된 원을 시각화하는 방법도 보여줍니다.

1단계: 영상 불러오기

이 예제에서는 다양한 색의 둥근 플라스틱 칩의 영상을 사용합니다.

rgb = imread('coloredChips.png');
imshow(rgb)

이 영상에는 검출할 원이 많다는 점 외에도, 원 검출 측면에서 흥미로운 점도 몇 가지 있습니다.

  1. 다양한 색의 칩이 있으며, 배경과의 대비도 다양합니다. 파란색과 빨간색 칩은 배경과 강하게 대비됩니다. 반면, 노란색 칩 중 일부는 배경과 그다지 대비되지 않습니다.

  2. 서로 겹쳐진 칩도 있고, 서로 가까이 있는 칩과 거의 닿는 칩도 있습니다. 객체 경계선 겹침과 객체 가림은 객체 검출을 까다롭게 만듭니다.

2단계: 원을 찾기 위해 반지름 범위 구하기

원을 찾으려면 imfindcircles에 반지름 범위가 필요합니다. 적절한 반지름 범위를 빠르게 구하는 방법은 대화형 방식 툴 imdistline을 사용하여, 다양한 객체의 대략적인 반지름 추정값을 얻는 것입니다.

d = imdistline;

imdistline을 사용하면 끌기가 가능한 툴이 생기고 이 툴을 움직여 칩에 놓을 있습니다. 그 수치를 읽어서 해당 칩의 대략적인 반지름 추정값을 얻을 수 있습니다. 대부분 칩의 반지름 범위는 21~23 픽셀입니다. 확실히 하기 위해, 반지름 범위를 조금 더 늘려 20~25 픽셀을 사용합니다. 그 전에 imdistline 툴을 제거합니다.

delete(d)

3단계: 원을 찾기 위한 첫 번째 시도

이 영상에서 [20 25] 픽셀의 검색 반경과 함께 imfindcircles를 호출합니다. 그 전에, 객체를 배경보다 더 밝게 할지 아니면 더 어둡게 할지 정해 두는 것이 좋습니다. 그 답을 찾기 위해, 이 영상의 회색조 버전을 표시하겠습니다.

gray_image = rgb2gray(rgb);
imshow(gray_image)

배경이 상당히 밝고, 칩 대부분이 배경보다 더 어둡습니다. 그러나 기본적으로 imfindcircles는 배경보다 더 밝은 원형 객체를 찾습니다. 그러므로 어두운 원을 찾기 위해 imfindcircles에서 파라미터 'ObjectPolarity'를 'dark'로 설정합니다.

[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark')
centers =

     []


radii =

     []

출력 인수 centersradii가 비어 있습니다. 이는 발견된 원이 없다는 뜻입니다. 이 현상은 자주 발생합니다. imfindcircles는 원 검출기이고, 다른 검출기와 마찬가지로 imfindcircles는 감도를 확인하는 내부 검출 임계값을 갖기 때문입니다. 간단히 말하면, 특정 (원) 검출이 유효할 수 있으려면 그 검출에서 검출기의 신뢰가 특정 수준보다 커야 합니다. imfindcircles에는 이 내부 임계값을 조정(결과적으로, 알고리즘의 감도 조정)하는 데 사용할 수 있는 파라미터 'Sensitivity'가 있습니다. 'Sensitivity' 값을 높이면 검출 임계값이 더 낮게 설정되어 더 많은 원을 검출하게 됩니다. 이는 주택 보안 시스템에 사용되는 모션 감지기의 감도 조정과 비슷합니다.

4단계: 검출 감도 늘리기

다시 칩 영상으로 돌아와 보면, 디폴트 감도 수준에서 모든 원이 내부 임계값보다 낮을 수 있습니다. 원이 검출되지 않은 이유가 바로 이 때문입니다. 기본적으로 'Sensitivity'(0과 1 사이의 숫자)는 0.85로 설정되어 있습니다. 'Sensitivity'를 0.9로 늘립니다.

[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ...
    'Sensitivity',0.9)
centers = 8×2

  146.1895  198.5824
  328.8132  135.5883
  130.3134   43.8039
  175.2698  297.0583
  312.2831  192.3709
  327.1316  297.0077
  243.9893  166.4538
  271.5873  280.8920

radii = 8×1

   23.1604
   22.5710
   22.9576
   23.7356
   22.9551
   22.9995
   22.9055
   23.0298

이번에는 imfindcircles가 원을 몇 개(정확하게는 8개) 찾았습니다. centers에는 원 중심의 위치가 들어 있고, radii에는 그러한 원의 추정 반지름이 들어 있습니다.

5단계: 영상에 원 그리기

함수 viscircles를 사용하여 영상에 원을 그릴 수 있습니다. imfindcircles의 출력 변수 centersradiiviscircles에 바로 전달할 수 있습니다.

imshow(rgb)
h = viscircles(centers,radii);

원 중심 위치가 정확한 것 같고, 해당 반지름도 실제 칩에 잘 맞는 것 같습니다. 그러나 아직도 상당히 많은 칩이 누락되었습니다. 'Sensitivity'를 0.92로 더 늘려 봅니다.

[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ...
    'Sensitivity',0.92);

length(centers)
ans = 16

'Sensitivity'를 늘려 훨씬 더 많은 원을 찾을 수 있습니다. 이 원들을 다시 영상에 플로팅합니다.

delete(h)  % Delete previously drawn circles
h = viscircles(centers,radii);

6단계: 원을 찾기 위해 두 번째 방법(2단계) 사용하기

이 결과가 더 좋아 보입니다. imfindcircles에는 원을 찾을 수 있는 방법이 두 가지 있습니다. 지금까지는 원을 검출하는 데 위상 코딩(phase coding)이라는 디폴트 방법을 사용했습니다. imfindcircles에서는 2단계(two-stage) 방법이라고 하는 또 다른 방법도 사용할 수 있습니다. 2단계 방법을 사용하여 결과를 표시합니다.

[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ...
          'Sensitivity',0.92,'Method','twostage');

delete(h)
h = viscircles(centers,radii);

2단계 방법은 Sensitivity가 0.92일 때 더 많은 원을 검출했습니다. 대체로 이 두 방법은 장점이 서로 다르기 때문에 상호 보완적입니다. 위상 코딩 방법은 일반적으로 2단계 방법보다 속도가 빠르고 잡음에 조금 더 강합니다. 그러나 2단계 방법의 검출 개수와 동일하려면 'Sensitivity' 수준을 더 올려야 할 수도 있습니다. 예를 들어, 'Sensitivity' 수준을 더 올리면 즉, 0.95로 올리면, 위상 코딩 방법도 같은 개수의 칩을 찾게 됩니다.

[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ...
          'Sensitivity',0.95);

delete(h)
viscircles(centers,radii);

imfindcircles의 두 방법은 일부만 보이는(가린) 칩의 중심과 반지름을 정확하게 찾습니다.

7단계: 왜 아직도 일부 원이 누락되는 걸까요?

마지막 결과를 보면, 의아스럽게도 imfindcircles는 영상의 노란색 칩을 찾지 못합니다. 노란색 칩은 배경과 강한 대비를 이루지 않습니다. 실제로 노란색 칩은 배경과 명암이 상당히 유사한 것 같습니다. 가정한 대로, 노란색 칩이 배경보다 실제로 '더 어두울' 가능성은 없을까요? 확인하기 위해, 이 영상의 회색조 버전을 다시 표시합니다.

imshow(gray_image)

8단계: 영상에서 '밝은' 원 찾기

노란색 칩은 배경과 비교하여 명암이 거의 같거나, 조금 더 밝을 수 있습니다. 따라서 노란색 칩을 검출하기 위해 'ObjectPolarity'를 'bright'로 변경합니다.

[centersBright,radiiBright] = imfindcircles(rgb,[20 25], ...
    'ObjectPolarity','bright','Sensitivity',0.92);

9단계: 서로 다른 색으로 '밝은' 원 그리기

viscircles에서 'Color' 파라미터를 변경하여, 서로 다른 색으로 밝은 원을 그립니다.

imshow(rgb)

hBright = viscircles(centersBright, radiiBright,'Color','b');

누락된 노란색 칩 중 세 개는 찾았지만, 한 개는 여전히 찾지 못하고 있습니다. 그 칩은 이 배경에서 다른 칩과 함께 두드러지지 않기 때문에 찾기 힘듭니다.

10단계: 'EdgeThreshold' 값 낮추기

imfindcircles에는 'EdgeThreshold'라는 또 다른 파라미터가 있습니다. 여기서 유용할 수 있는 파라미터입니다. imfindcircles는 원을 찾을 때 영상의 경계 픽셀만 사용합니다. 이러한 경계 픽셀은 기본적으로 기울기 값이 큰 픽셀입니다. 픽셀을 경계 픽셀로 간주하여 계산에 넣기 전에, 'EdgeThreshold' 파라미터를 사용하여 해당 픽셀의 기울기 값을 얼마나 높일지 조정할 수 있습니다. 이 파라미터에 높은 값(1에 근접)을 사용하면 강한 경계(기울기 값이 더 높음)만 포함할 수 있는 반면, 낮은 값(0에 근접)은 좀 더 관대하여 약한 경계(기울기 값이 더 낮음)도 계산에 포함합니다. 누락된 노란색 칩의 경우, 대비가 낮아 (칩의 원주에서) 경계선 픽셀 중 일부의 기울기 값이 낮을 것입니다. 따라서 노란색 칩의 경계 픽셀 대부분을 계산에 넣으려면 'EdgeThreshold' 파라미터를 낮추어야 합니다.

[centersBright,radiiBright,metricBright] = imfindcircles(rgb,[20 25], ...
    'ObjectPolarity','bright','Sensitivity',0.92,'EdgeThreshold',0.1);

delete(hBright)
hBright = viscircles(centersBright, radiiBright,'Color','b');

11단계: '어두운' 원과 '밝은' 원을 함께 그리기

이제 imfindcircles는 모든 노란색 칩과 녹색 칩도 찾았습니다. 이러한 칩은 파란색으로 그리고, 이전에 찾은 다른 칩('ObjectPolarity'가 'dark'로 설정됨)은 빨간색으로 함께 그립니다.

h = viscircles(centers,radii);

모든 원을 검출했습니다. 마지막으로 한 가지 유의할 점을 덧붙이자면, 검출 시 파라미터를 좀 더 공격적으로 변경하면 원을 더 많이 찾을지 모르지만, 원을 잘못 찾을 가능성도 늘어납니다. 정확하게 찾을 수 있는 원(검출률)이 있으면, 그와 함께 잘못 찾는 원(오검출률)도 있는 법입니다.

즐겁게 원을 찾으시길 바랍니다!