이전 포스팅에서 모멘트에 대해서 알아보았고, 그중 Hu의 7개 불변 모멘트가 영상의 크기, 회전, 이동, 대칭 변환에 불변하다는 것을 알 수 있었다. 이러한 부분을 참고하여 주어진 이미지에서 원하는 객체를 찾아보도록 하자.
OpenCV에서는 원하는 객체를 찾기위해 주어진 두 모양을 비교하는 matchShapes()라는 함수를 제공한다.
matchShape()
Imgproc.matchShapes(
pts1, // 회색조 이미지 또는 첫번째 윤곽선
pts2, // 회색조 이미지 또는 두번째 윤곽선
method, // 모양을 매칭하는 방법을 제공
parameter // method 특화적인 매개변수(지원하지 않으므로 여기서는 다루지 않음)
)
matchShape 함수는 주어진 두개의 윤곽선(Contour)을 비교하는 함수다. 내부적으로 Hu의 불변모멘트를 이용하여 영상의 모양을 비교한다. 호출시에 method에 몇가지 인자를 전달할 수 있다.
매칭 방식
아래의 표에서 A는 객체1, B는 객체2를 나타낸다.

표에서 mAi 와 mBi 는 다음과 같이 정의 된다.

이 표현식에서 hAi 와 hBi 는 각각 이미지 A와 B의 Hu 불변 모멘트다. 정의된 세가지 값은 각각 비교측정을 계산하는 방식에 따라 다른 의미를 갖는다. 이 측정 방식은 궁극적으로 matchShapes()에 의해 연산되는 결과값을 결정하고, 최종 파라미터는 현재 사용되지 않으므로 기본값을 0으로 지정한다.
matchShape 예제
다음 나오는 객체(스페이드)가 포함된 이미지에서 객체를 검출하는 예제코드를 작성해본다.
찾고자 하는 객체 찾고자 하는 객체가 포함된 이미지
val objBitmap = BitmapFactory.decodeResource(context.resources, objectResId)
val objSrc = Mat() // 검출하고자 하는 객체 이미지
Utils.bitmapToMat(objBitmap, objSrc)
Imgproc.cvtColor(objSrc, objSrc, Imgproc.COLOR_RGB2GRAY)
val binObjSrc = Mat() // 이진화된 객체 이미지
Imgproc.threshold(objSrc, binObjSrc, 128.0, 255.0, Imgproc.THRESH_BINARY_INV)
val objContours = ArrayList<MatOfPoint>() // 객체 이미지의 윤곽선 정보
val objHierarchy = Mat()
Imgproc.findContours(
binObjSrc,
objContours,
objHierarchy,
Imgproc.RETR_EXTERNAL,
Imgproc.CHAIN_APPROX_NONE
)
val objPts = objContours.firstOrNull() ?: return null //윤곽선을 못찾았으면 종료
val grayBgSrc = Mat() // 검출하고자 하는 객체가 포함된 이미지
Imgproc.cvtColor(bgSrc, grayBgSrc, Imgproc.COLOR_BGR2GRAY)
val binBgSrc = Mat() // 이진화된 이미지
Imgproc.threshold(grayBgSrc, binBgSrc, 128.0, 255.0, Imgproc.THRESH_BINARY_INV)
val bgContours = ArrayList<MatOfPoint>() // 이미지의 윤곽선 정보
val bgHierarchy = Mat()
Imgproc.findContours(
binBgSrc,
bgContours,
bgHierarchy,
Imgproc.RETR_EXTERNAL,
Imgproc.CHAIN_APPROX_NONE
)
bgContours.forEachIndexed { index, pts ->
if (Imgproc.contourArea(pts) > 1000) {
val rect = Imgproc.boundingRect(pts) // 검출한 객체의 감싸는 사각형
Imgproc.rectangle(bgSrc, rect, BLUE, 1) // 파랑색 사각형으로 객체를 감싼다.
// matchShape는 두 윤곽선의 사이의 거리(차이)를 반환
val dist = Imgproc.matchShapes(
objPts, // 찾고자 하는 객체의 윤곽선
pts, // 검출한 객체의 윤곽선
Imgproc.CONTOURS_MATCH_I3, // 매칭 방식
0.0 // (사용되지 않음)
)
// 0.1보다 낮은 차이를 보여줄 때 객체를 찾았다고 판단한다.
val found = dist < 0.1
if (found) {
// 찾은 객체는 빨간 선으로 두텁께 다시 그린다
Imgproc.rectangle(bgSrc, rect, RED, 2)
}
// dist값을 출력함
Imgproc.putText(
bgSrc,
"${dist}",
Point(rect.x.toDouble(), rect.y.toDouble() - 3),
Imgproc.FONT_HERSHEY_SIMPLEX,
1.0,
if (found) RED else BLUE
)
}
}


3개의 댓글
코린이 · 2022년 5월 1일 1:39 오전
찰스님 안녕하세요! 닉네임 변경 공지를 주말이라 못봐서 오리지날 방에서 강퇴당했는데..ㅠㅠ 다시 초대 가능할까요..? 그방이 아는분도있고 익숙해서요..
rect가 안되요 · 2022년 5월 27일 4:14 오후
Imgproc.rectangle(bgSrc, rect, BLUE, 1) // 파랑색 사각형으로 객체를 감싼다.
이부분 파라미터 자료형이 안맞는데 어떻게 해야하나요?
Charlezz · 2022년 5월 28일 2:59 오전
안드로이드로 배우는 OpenCV 예제 코드는 https://github.com/charlezz/opencvtutorial에서 확인하실 수 있습니다.