이전 포스팅에서 모멘트에 대해서 알아보았고, 그중 Hu의 7개 불변 모멘트가 영상의 크기, 회전, 이동, 대칭 변환에 불변하다는 것을 알 수 있었다. 이러한 부분을 참고하여 주어진 이미지에서 원하는 객체를 찾아보도록 하자.

OpenCV에서는 원하는 객체를 찾기위해 주어진 두 모양을 비교하는 matchShapes()라는 함수를 제공한다.

matchShape()

Imgproc.matchShapes(
    pts1, // 회색조 이미지 또는 첫번째 윤곽선
    pts2, // 회색조 이미지 또는 두번째 윤곽선
    method, // 모양을 매칭하는 방법을 제공
    parameter // method 특화적인 매개변수(지원하지 않으므로 여기서는 다루지 않음)
)

matchShape 함수는 주어진 두개의 윤곽선(Contour)을 비교하는 함수다. 내부적으로 Hu의 불변모멘트를 이용하여 영상의 모양을 비교한다. 호출시에 method에 몇가지 인자를 전달할 수 있다.

매칭 방식

아래의 표에서 A는 객체1, B는 객체2를 나타낸다.

표에서 mAimBi 는 다음과 같이 정의 된다.

이 표현식에서 hAihBi 는 각각 이미지 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
        )
    }
}

후원하기

카테고리: OpenCV

1개의 댓글

코린이 · 2022년 5월 1일 1:39 오전

찰스님 안녕하세요! 닉네임 변경 공지를 주말이라 못봐서 오리지날 방에서 강퇴당했는데..ㅠㅠ 다시 초대 가능할까요..? 그방이 아는분도있고 익숙해서요..

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.