5.25. 블롭 찾기

임계값 처리는 캡처한 프레임을 이진 마스크로 바꿉니다. 모든 픽셀은 임계값 테스트를 통과하거나 통과하지 못합니다. 이는 애플리케이션이 관심을 두는 색상이 장면에 나타나는가에는 답하지만, 어디에 있는지는 알려주지 않습니다. 마스크는 그저 1과 0의 바다일 뿐입니다. 다음 단계는 블롭 검출입니다. 마스크를 따라가며 통과한 픽셀들이 연속으로 이어진 영역을 찾고, 각 영역을 위치, 크기, 방향, 그리고 애플리케이션이 활용할 수 있는 그 밖의 속성을 가진 객체로 반환합니다.

find_blobs() 는 이 단계의 주력 메서드이며, image 모듈의 결과 객체 세계로 들어가는 가장 흔한 진입점입니다. 색깔 공 추적하기, 바닥에 그려진 선 따라가기, 열 센서가 보는 밝은 점의 개수 세기, 파란 LED가 켜졌는지 꺼졌는지 판단하기 – 이 모든 것이 같은 호출로 처리됩니다. 입력값(임계값, 검색 영역, 결과에 적용하는 필터)은 달라지지만 호출 패턴은 동일합니다.

5.25.1. 기본 호출

find_blobs 는 임계값 리스트를 받아 블롭 결과 객체의 리스트를 반환합니다:

thresholds = [(30, 100, 15, 127, 15, 127)]  # LAB threshold for red
blobs = img.find_blobs(thresholds)

for b in blobs:
    img.draw_rectangle(b.rect, color=(255, 0, 0))
    img.draw_cross(b.cx, b.cy, color=(255, 0, 0))

각 임계값 튜플은 binary() 에 전달하는 임계값과 같은 형식을 가집니다 – RGB565 이미지의 경우 여섯 개의 항목 (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi) (경계값은 LAB 기준), 그레이스케일 이미지의 경우 두 개의 항목 (lo, hi). 한 번의 호출에 최대 32개의 임계값을 제공할 수 있는데, 바로 이 점이 find_blobs() 를 매우 유연하게 만듭니다. 빨강, 초록, 파랑 비콘을 동시에 추적할 수 있으며, 각 임계값이 자신만의 블롭을 반환 리스트에 기여하고, 각 블롭의 code 속성이 어떤 임계값과 일치했는지 식별합니다.

위의 draw_rectangle()draw_cross() 호출은 IDE 미리보기를 위해 캡처한 프레임에 주석을 답니다. 블롭 결과는 이미 b.rect (4-튜플 형태의 경계 상자)와 b.cx / b.cy (정수 중심점)를 가지고 있으므로, 검출 결과를 프레임에 다시 그리는 작업은 두 번의 메서드 호출이면 됩니다.

5.25.2. 결과에 담긴 내용

Blob 는 검출기가 영역에 대해 측정한 모든 것을 함께 담은 속성 튜플입니다. 속성은 네 그룹으로 나뉩니다.

경계 상자와 중심점 그룹 – x, y, w, h, rect, cx, cy, cxf, cyf – 은 블롭의 위치를 기술합니다. rect 은 그리기 메서드가 기대하는 (x, y, w, h) 4-튜플입니다. cxcy 는 정수 픽셀 좌표의 중심점입니다. cxfcyf 는 서브픽셀 부동소수점 좌표의 중심점으로, 상위 단계의 보정 과정이 소수점 단위 위치를 신경 쓸 때 유용합니다.

형태 디스크립터pixels, area, density, perimeter, roundness, elongation, compactness, rotation – 는 블롭이 어떻게 생겼는지를 기술합니다. pixels 는 통과한 픽셀의 개수입니다. area 는 축 정렬 경계 상자의 면적(w * h)입니다. density 는 둘의 비율로, 꽉 찬 사각형에서는 1.0 에 가까워지고 가느다란 대각선 획에서는 0.0 쪽으로 떨어집니다. roundnesscompactness 는 모두 블롭이 얼마나 둥근지를 서로 다른 기하학적 관점에서 점수화합니다(roundness 는 2차 모멘트로부터, compactness 는 둘레-면적 비율로부터). elongation 은 편의를 위한 1.0 - roundness 값입니다. rotation 은 장축의 방향을 라디안으로 나타낸 값으로, 길쭉한 블롭에서 가장 정확하고 거의 둥근 블롭에서는 노이즈가 심해집니다(모호한 축은 명확한 방향이 없기 때문입니다).

임계값 및 병합 메타데이터code, count – 는 어떤 임계값이 일치했는지, 그리고 몇 개의 원본 블롭이 반환된 블롭으로 병합되었는지를 식별합니다. code 는 일치한 임계값마다 한 비트씩 설정되는 32비트 비트맵입니다(단일 임계값이면 code == 1, 병합된 다색 블롭은 여러 비트가 설정될 수 있습니다). countmerge=True 로 여러 검출이 하나로 합쳐진 경우가 아니면 1 입니다.

코너 그룹 – corners, min_corners – 는 블롭의 회전된 기하 정보를 제공합니다. corners 는 블롭의 윤곽선에서 추출한 (x, y) 극점들의 4-튜플로, 좌상단에서 시계 방향으로 정렬됩니다. min_corners 는 블롭을 둘러싸는 최소 면적 회전 사각형의 코너 4-튜플입니다. 최소 면적 사각형은 꼭 맞는 사각형이고, 축 정렬 rect 은 픽셀 격자에 맞춰 정렬된 느슨한 사각형입니다. 하위 단계가 방향이 있는 상자를 필요로 하는지 단순한 상자를 필요로 하는지에 따라 둘 다 유용합니다.

이진 임계값 마스크에 대해 블롭 검출을 그림으로 나타낸 모습. 왼쪽 패널은 통과한 픽셀들로 이루어진 기울어진 타원 마스크를 보여줍니다. 오른쪽 패널은 같은 마스크에 주석을 단 모습으로, 마스크 주위에 그려진 축 정렬 경계 상자, 가운데에 십자로 표시된 중심점, 타원을 실제 각도로 감싸는 점선의 최소 면적 회전 사각형, 그리고 중심점을 지나 타원의 긴 방향을 따라 가리키는 장축 선을 보여줍니다.

블롭은 축 정렬 경계 상자(rect, x, y, w, h), 중심점(cx, cy 또는 서브픽셀 cxf, cyf), 최소 면적 회전 사각형(min_cornersrotation), 그리고 아래의 모듈 수준 헬퍼로 계산하는 선택적 장축 / 단축 선을 가집니다.

5.25.4. 겹치는 블롭 병합하기

merge=True 는 결과 리스트를 후처리하여 경계 사각형이 겹치는 블롭들을 합칩니다. 자연스러운 용도는 정반사 하이라이트, 그림자 선, 또는 객체 전체에 걸쳐 고르지 않은 조명 때문에 카메라가 그 색상을 여러 개의 임계값 처리된 영역으로 보는 대상을 검출하는 것입니다. 하나의 빨간 공이 세 개나 네 개의 작은 빨간 블롭으로 돌아올 수 있는데, 이들을 합치면 공의 윤곽을 그리게 됩니다. merge=True 를 사용하면 세 개의 블롭이 하나의 큰 블롭이 되고, rect 은 그 합집합을 덮으며, code 는 병합된 블롭들의 코드를 비트 OR한 값이 되고(따라서 다색 병합은 어떤 색상들이 기여했는지 식별합니다), count 는 몇 개의 원본 블롭이 합쳐졌는지 알려줍니다.

margin 은 겹침 테스트 전에 경계 사각형을 키우거나 줄입니다. margin=2 를 사용하면 경계 사각형이 서로 2픽셀 이내로 가까워지는 블롭들도 병합됩니다. margin=-2 를 사용하면 경계 사각형이 최소 2픽셀 이상 겹치는 블롭들만 병합됩니다. 자연스러운 조정 방법은 다음과 같습니다. 양수 마진은 임계값이 인접한 조각으로 쪼개 놓은 블롭을 처리하는 데, 음수 마진은 빽빽하게 모여 있는 별개의 객체들을 분리된 상태로 유지하는 데 사용합니다.

merge_cb 는 병합이 일어나기 전에 각 후보 쌍에 대해 실행됩니다. 콜백은 두 블롭을 받아서 병합을 허용하려면 True 를, 막으려면 False 를 반환합니다. 이는 기하학적 규칙이 놓치는 병합을 교차 검증하기에 적합한 도구입니다 – 예를 들어 rotation 각도가 임계값 이상 차이 나는 두 블롭의 병합을 거부하거나, 작은 블롭이 단지 얼룩일 뿐이라면 그것을 훨씬 큰 블롭에 병합하기를 거부하는 식입니다.

5.25.5. 투영 히스토그램

x_hist_bins_maxy_hist_bins_max 는 각 블롭에 선택적인 투영 히스토그램 을 첨부합니다. 투영 히스토그램은 한 축을 따라 통과한 픽셀의 개수입니다. X축 히스토그램은 블롭 경계 상자 내부의 열마다 통과한 픽셀을 합산하고, Y축 히스토그램은 행마다 합산합니다. 둘 다 기본값은 0입니다 – 0이 아닌 max 가 제공되지 않으면 히스토그램은 계산되지 않는데, 그렇지 않으면 모든 검출에 작업이 추가되기 때문입니다.

히스토그램이 계산되면, 애플리케이션이 추가 분석을 수행할 수 있는 저렴한 1차원 신호를 제공합니다. 블롭 내부 수직 줄무늬의 위치 검출, 두 색상으로 된 대상의 경계점 찾기, 장축을 따라 나타나는 간격의 개수 세기 등입니다. 이들은 각 Blobx_hist_binsy_hist_bins 속성으로 채워집니다.

5.25.6. 추가 기하 헬퍼

그 밖의 몇 가지 기하학적 측정값은 블롭을 받아 요청된 측정값을 반환하는 모듈 수준 함수로 제공됩니다:

  • image.get_solidity() 는 블롭의 solidity(밀집도), 즉 픽셀 수를 볼록 껍질(convex hull)의 면적으로 나눈 값을 반환합니다. 꽉 채워진 영역은 1.0 에 가깝고, 오목한 부분이 있는 블롭(말굽이나 손가락을 편 손 모양)은 그보다 훨씬 낮은 값을 갖습니다.

  • image.get_convexity()convexity(볼록도), 즉 볼록 껍질의 둘레를 블롭의 둘레로 나눈 값을 반환합니다. 완벽하게 볼록한 블롭은 1.0 이고, 들쭉날쭉하거나 홈이 파인 블롭은 더 낮은 값을 갖습니다.

  • image.get_major_axis_line()image.get_minor_axis_line() 는 회전된 최소 면적 사각형에서 유도된, 블롭의 장축과 단축을 따라가는 Line 객체를 반환합니다.

  • image.get_enclosing_circle() 는 블롭을 둘러싸는 Circle 를 반환합니다 – 하위 단계가 그리거나 검사할 원을 원할 때 유용합니다.

  • image.get_enclosed_ellipse() 는 블롭의 최소 면적 사각형에 내접하는 타원에 대한 5-튜플 (cx, cy, rx, ry, rotation) 을 반환합니다. 이 값들은 draw_ellipse() 에 바로 넣을 수 있습니다.

5.25.7. 임계값 자동 학습

블롭 검출기는 함께 사용하는 임계값만큼만 좋으며, 대상 색상에 맞는 적절한 임계값을 찾는 작업은 그 자체로 하나의 문제입니다. 두 가지 흔한 패턴이 그 작업을 줄여 줍니다.

첫 번째는 IDE에서의 대화형 선택 입니다. 프레임을 캡처하고, 대상 색상의 예시 주위에 사각형을 드래그하면, IDE의 임계값 편집기 가 자신이 본 LAB 경계값을 알려줍니다. 그 경계값을 스크립트의 find_blobs() 임계값으로 넣으면 검출기가 준비됩니다.

두 번째는 프로그래밍적 자동 학습입니다. 카메라에서 실행되는 보정 루틴이 프레임을 캡처하고, 대상이 있는 알려진 패치의 히스토그램을 취한 다음(roi= 와 함께 get_histogram()), get_percentile() 로 히스토그램에서 패치의 값 범위를 읽습니다. 5번째 백분위수가 각 채널의 하한을, 95번째가 상한을 설정하여 양쪽 끝의 이상치 픽셀을 무시합니다. RGB565 이미지에서는 한 번의 백분위수 호출이 세 LAB 채널을 한꺼번에 알려주므로, 두 번의 호출로 find_blobs() 가 기대하는 여섯 개의 숫자가 만들어집니다:

h = img.get_histogram(roi=patch)
lo = h.get_percentile(0.05)
hi = h.get_percentile(0.95)
threshold = (lo.l_value, hi.l_value,
             lo.a_value, hi.a_value,
             lo.b_value, hi.b_value)