Tensorflow Lite를 이용해 객체 분석 및 프리뷰 띄우기 및 객체 촬영 흐름도

TensorFlow Lite를 이용해 객체 분석
다음 코드들은 Camera2와 연동하기 전에 TensorFlow Lite를 Android에서 사용하기 위해 필요한 설정들과 이에 대한 코드이다.
build.gradle (:app) 설정
dependencies {
// TFLite
implementation 'org.tensorflow:tensorflow-lite-task-vision:0.3.1'
}
Tensorflow Lite 사용을 위한 종속성 추가
모델 ( .tflite ) 을 저장하는 assets 폴더 생성
/[안드로이드 프로젝트]/app/src/main 위치에 assets 폴더 생성
해당 assets 폴더에 사용하고자 하는 모델 ( .tflite ) 파일 추가
본인이 사용한 .tflite 파일
TensorFlow Hub
tfhub.dev
setDetecter( ) - 객체 분석 디렉터 설정
객체 분석을 도와줄 디렉터 설정하는 함수
아래 코드는 정확도 높은 랜드마크 감지 및 얼굴 분류되게 설정하고, 사용할 모델을 설정한다.
private fun setDetecter() {
// High-accuracy landmark detection and face classification
val highAccuracyOpts = FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
.enableTracking()
.build()
faceDetector = FaceDetection.getClient(highAccuracyOpts)
// Initialize the detector object
val options = ObjectDetector.ObjectDetectorOptions.builder()
.setMaxResults(5) // 최대 결과 (모델에서 감지해야 하는 최대 객체 수)
.setScoreThreshold(0.5f) // 점수 임계값 (감지된 객체를 반환하는 객체 감지기의 신뢰도)
.build()
customObjectDetector = ObjectDetector.createFromFileAndOptions(
context,
"lite-model_efficientdet_lite0_detection_metadata_1.tflite",
options
)
}
runObjectDetection(bitmap: Bitmap) - bitmap 사진을 객체 분석하기
이미지를 받아 분석하여 분석 결과가 표시된 이미지 반환하는 함수
/**
* runObjectDetection(bitmap: Bitmap)
* TFLite Object Detection function
* 사진 속 객체를 감지하고, 감지된 객체에 boundingBox를 표시해 반환한다.
*/
fun runObjectDetection(bitmap: Bitmap): Bitmap {
// 카메라와 연결했을 때 사용되는 변수 : 카메라가 지금 현재 result를 가져갔는지 확인
if(!isGetDetectionResult) {
// Object Detection
detectionResult = getObjectDetection(bitmap)
}
// ObjectDetection 결과(bindingBox) 그리기
val objectDetectionResult =
drawDetectionResult(bitmap, detectionResult)
return objectDetectionResult!!
}
getObjectDetection(bitmap: Bitmap) - bitmap 사진 분석해 분석 결과 반환
이미지를 분석하여 분석 결과를 반환하는 함수
- Tensorflow Image 객체 생성
- Image 객체로 변환한 이미지를 디텍터로 공급
- 분석 결과를 List<DetectionResult> 형태로 제작하여 반환
/**
* getObjectDetection(bitmap: Bitmap):
* ObjectDetection 결과(bindingBox)를 반환한다.
*/
private fun getObjectDetection(bitmap: Bitmap): List<DetectionResult> {
// Step 1: Create TFLite's TensorImage object
val image = TensorImage.fromBitmap(bitmap)
// Step 2: Feed given image to the detector
val results = customObjectDetector.detect(image)
// Step 3: Parse the detection result and show it
val resultToDisplay = results.map {
// Get the top-1 category and craft the display text
val category = it.categories.first()
val text = "${category.label}"
// Create a data object to display the detection result
DetectionResult(it.boundingBox, text)
}
return resultToDisplay
}
data class DetectionResult(val boundingBox: RectF, val text: String)
drawDetectionResult(bitmap: Bitmap, detectionResults: List<DetectionResult>
- 분석 결과를 바탕으로 이미지에 표시
객체 분석 결과(detectionResults)를 이용해 이미지 위에 분석 결과를 표시한 후 표시된 이미지를 반환하는 함수
/**
* drawDetectionResult(bitmap: Bitmap, detectionResults: List<DetectionResult>
* 객체 분석 된 boundingBox에 맞춰 이미지 위에 표시한 후 표시된 이미지를 반환한다.
*/
private fun drawDetectionResult(
bitmap: Bitmap,
detectionResults: List<DetectionResult>
): Bitmap? {
val outputBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true)
val canvas = Canvas(outputBitmap)
val pen = Paint()
pen.textAlign = Paint.Align.LEFT
detectionResults.forEach {
// draw bounding box
pen.color = Color.parseColor("#B8C5BB")
pen.strokeWidth = 10F
pen.style = Paint.Style.STROKE
val box = it.boundingBox
canvas.drawRoundRect(box, 10F, 10F, pen)
}
return outputBitmap
}
getDetectionResult( ) - 현재 객체 분석을 정지시키고, 분석 결과 중 하나(한 객체 정보)를 반환하는 함수
1. isGetDetectionResult = true로 설정함으로써, 현재 객체 분석을 정지시킨다.
2. index 변수를 가지고 해당 index에 분석 결과를 선택한다.
만약 index가 분석결과 사이즈보다 작을 경우, resetDetectionResult( ) 함수를 호출해 다시 객체 분석을 시작한다.
fun getDetectionResult() : DetectionResult? {
if(detectionResult.size > detectionResultIndex) {
isGetDetectionResult = true
return detectionResult[detectionResultIndex++]
}
else {
resetDetectionResult()
return null
}
}
fun resetDetectionResult() {
isGetDetectionResult = false
detectionResultIndex = 0
}
추가적인 함수
fun getIsDetectionStop() : Boolean {
return isGetDetectionResult
}
fun getIsDetectionSize() : Int {
return detectionResult.size
}
Camera2에서 실시간으로 객체 분석
Object Detection 함수를 지금 할 것인지 안할 것인지 Switch로 판단
Switch가 true일 때는 Object Detection 실행, false일 때는 Object Detection 을 실행하지 않는다.
isDetectionChecked 변수 - 현재 Object Detection을 할지 안할지에 대한 변수
binding.detectionBtn.setOnCheckedChangeListener { _, isChecked ->
isDetectionChecked = isChecked
if(!isChecked) {
binding.imageView.setImageBitmap(null)
}
}
TextureView.SurfaceTextureListener의 onSurfaceTextureUpdated(texture: SurfaceTexture) 함수
TextureView.SurfaceTextureListener의 onSurfaceTextureUpdated(texture: SurfaceTexture) 함수를 이용하여 Surface의 변화가 생길 때마다 Object Detection을 다시 실행하여 Surface위에 ImageView에 띄운다.
TextureView.SurfaceTextureListener: TextureView에 등록된 Listener
onSurfaceTextureUpdated(texture: SurfaceTexture) : surfaceTexture가 업데이트 될 때 호출되는 함수
// surfaceTexture가 업데이트 됨
override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {
if (isDetectionChecked) {
// 객체 인식 코드 호출
val newBitmap = binding.texture.bitmap?.let {
objectDetectionModule.runObjectDetection(it)
}
binding.imageView.setImageBitmap(newBitmap)
}
}
focusDetectionPictures( ) : 객체 분석 결과를 얻어와 해당 객체에 초점 맞추기
객체 분석 결과를 얻어와 해당 객체에 초점을 맞추는 함수
1. objectDetectionModule.getDetectionResult()
: 객체 분석 결과를 하나 얻어온다
2. 분석 결과로 얻어진 boundingBox를 가지고, 객체 중앙 값과 절반너비, 절반높이를 알아낸다
3. setTouchPointDistanceChange( ... )
: 위에 알아낸 값들을 토대로 해당 객체에 초점을 맞춘다. ( 해당 함수는 다음 포스터에서 확인 )
[Camera2] Camera2 Preview 터치된 곳으로 초점 변경
kyumq.tistory.com
setTouchPointDistanceChange에서 CameraCaptureSession.CaptureCallback() 수정
CameraCaptureSession.CaptureCallback() 수정을 통해 초점이 완료되면 사진 촬영하게 설정
val captureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult,
) {
// 현재 객체 감지 촬영일 경우
if(objectDetectionModule.getIsDetectionStop()) {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, null)
captureSession?.setRepeatingRequest(previewRequestBuilder.build(), null, null)
// 사진 촬영을 위한 포커스 잠금
lockFocus()
}
else {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, null)
captureSession?.setRepeatingRequest(previewRequestBuilder.build(), null, null)
}
}
}
captureStillPicture에서 CameraCaptureSession.CaptureCallback() 수정
CameraCaptureSession.CaptureCallback() 수정을 통해 촬영이 완료된 후, 현재 객체 초점 촬영일 경우 다음 객체 초점 촬영 시작
val captureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult,
) {
val distanceResult = result.get(CaptureResult.LENS_FOCUS_DISTANCE)
Log.d("렌즈 초점 결과", distanceResult.toString())
// 수동 초점
if(value != null && distanceResult == 10f) {
setAutoFocus()
}
// 자동 초점
else {
unlockFocus()
// 객체 촬영일 경우, 다음 객체 촬영 시작
if(isDetectionChecked) {
focusDetectionPictures()
}
}
}
}
\
'OnePIC (Android App) > 카메라' 카테고리의 다른 글
[Camera2] 객체 초점 촬영 무한 로딩 현상 발생 (0) | 2023.08.19 |
---|---|
[Camera2] 전면 후면 카메라 전환 (0) | 2023.08.09 |
[Camera2] Camera2 Preview 터치된 곳으로 초점 변경 (0) | 2023.08.07 |
[Camera2] Camera2를 이용한 수동 초점 촬영 (1) | 2023.08.01 |
[Camera2] 카메라 장치에 관한 설정 변수 정리 (0) | 2023.08.01 |