이전 포스팅에서 모멘트에 대해서 알아보았고, 그중 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에서 확인하실 수 있습니다.