YOLOv8 segmentation 안드로이드 -6

알로에·2023년 10월 12일
1

YOLOv8-Segmentation

목록 보기
6/6

이전 글까지는 실시간 segmentation 추론 및 화면에 표출하는 것을 전부 메인쓰레드에서 실행했다.
추론을 하는 시간이 있다 보니 화면 전환이 매끄럽게 진행되지 못한다는 단점이 있었다. 이번 글에서는 코루틴을 이용해서 비동기로 추론하고, 화면 표출만 메인 쓰레드에서 실행하는 내용을 설명할 것이다.

✔ 1. MainActivity 코루틴 블록 추가

  1. 우선 아래와 같이 메인 액티비티의 전역 변수를 추가한다.
 	private lateinit var net: Net
    private lateinit var labels: Array<String>
    // 추가된 내용
    private var results = mutableListOf<Result>()
    private var isDetect = false

코루틴 블록에서 추론한 결과를 results 안에 담게 될 것이다.
화면의 갱신 속도보다 추론 하는 속도가 훨씬 느리다. 따라서 코루틴 블록에서 추론하고 있다면 추론을 하지 않게 isDetect 변수를 생성한다.

이전에는 메인쓰레드에서 모든 것을 수행했다. 그러다 보니 화면의 크기를 전환하는 시간을 줄이기 위해서 화면 자체의 크기를 제한했었다. 이제는 비동기로 대부분의 작업을 수행하므로 화면의 크기를 제한할 필요가 없다.

//onCreate 일부 

//제거 
setMaxFrameSize(640, 640)

따라서 화면의 크기를 강제로 줄였던 부분을 해제한다.

  1. onCameraFrame 메서드에 코루틴 블록을 추가한다.
override fun onCameraFrame(inputFrame: CameraBridgeViewBase.CvCameraViewFrame?): Mat {
        val frameMat = inputFrame!!.rgba()
        Imgproc.cvtColor(frameMat, frameMat, Imgproc.COLOR_RGBA2RGB)

        lifecycleScope.launch(Dispatchers.Default) {
            if (isDetect) return@launch

            isDetect = true
            val inputMat = frameMat.clone()
            val detections = detect(inputMat, net, labels)
            results = detections

            inputMat.release()
            isDetect = false
        }
        
        return drawSeg(frameMat, results, labels)
    }

이전 글과 변경점이 있다면 frame을 받아오자마자 rgba 형태의 mat 객체를 미리 rgb 형태로 변환한다.
코루틴 블록을 보면 알 수 있듯이 추론 중이라면 추론을 하지 않는다. 추론된 결과를 전역 변수인 results로 넣는 것을 확인할 수 있다.
마지막으로 화면을 그리는 메서드는 코루틴 블록이 아닌 메인 쓰레드에서 실행되는 것을 알 수 있다.

✔ 2. Inference 인터페이스 수정

detect 내부 메서드 중 일부 

이전 코드 
val inputMat = Mat()
Imgproc.resize(mat, inputMat, Size(INPUT_SIZE.toDouble(), INPUT_SIZE.toDouble()))
Imgproc.cvtColor(inputMat, inputMat, Imgproc.COLOR_RGBA2RGB)

이후 코드 
val inputMat = mat.clone()
Imgproc.resize(inputMat, inputMat, Size(INPUT_SIZE.toDouble(), INPUT_SIZE.toDouble()))

이전에는 Inference 인터페이스 내부 메서드인 detect 에서 input frame에 대해서 rgba -> rgb로 변경하였다.
또한 input frame(원본 화면)의 값을 수정하지 않게 input mat으로 값을 복사하고 사용한다.

✔ 3. Draw 인터페이스 수정

  1. text와 bounding box 색칠하는 부분 변경
drawSeg 메서드의 일부 

// 이전 코드 
Imgproc.rectangle(mat, box, color, 2)
Imgproc.putText(mat, text, textPoint, Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, color, 2)

// 이후 코드 
Imgproc.rectangle(maskImg, box, color, 5)
Imgproc.putText(maskImg, text, textPoint, Imgproc.FONT_HERSHEY_SIMPLEX, 1.0, color, 5)

입력 변수 중 mat 객체는 원본 이미지(input frame)이다. 문제가 되는 코드는 아니지만 이론상 원본 이미지와 새롭게 색칠한 이미지를 합치는 코드를 작성해야한다. 따라서 색칠할 이미지에 box와 text를 추가하는 것으로 변경했다.
두 번째는 화면 크기가 커짐에 따라 text와 box의 크기를 확대했다.

  1. 리소스 해제 취소
drawSeg 메서드 중 리소스를 해제하는 부분 

// 이전 코드 
cropMask.release()
cropMaskImg.release()
temp1.release()
temp2.release()
cropMaskRGB.release()
it.maskMat.release()
list.forEach { mat -> mat.release() }

// 수정 코드 
cropMaskImg.release()
temp1.release()
temp2.release()
cropMaskRGB.release()
list.forEach { mat -> mat.release() }

cropMask와 it.maskMat 두 객체의 리소스 해제를 취소한 것을 확인할 수 있다. 우리는 연속적으로 화면에 색을 칠해야 한다. 이전 코드는 화면에 한번 그리고 리소스를 해제하게 된다. 이러다 보니 색이 칠해지고 순식간에 사라지게 된다. 색이 칠해진 상태로 남아있게 하기 위해서 리소스 해제를 하지 않았다.

아래는 해당 어플을 실행했을 때이다.

영상을 보면 알 수 있듯이, 추론속도 보다 화면 전환 속도가 빠르다 보니 화면 프레임 자체는 부드럽지만, 추론 결과를 화면에 표출 하는 속도가 따라가지 못한다는 아쉬운 점이 있다.

아래 깃허브를 통해 비동기가 추가되기 전과 비교하면 좋을 듯하다.
https://github.com/Yurve/YOLOv8_Seg_android

2개의 댓글

comment-user-thumbnail
2023년 10월 12일

유튜브 영상도 왕 멋지네요 도움이 많이 됩니다

1개의 답글

관련 채용 정보