Galaxy 수채화 현상 완전 정복! Raw Image 를 활용한 Android Camera Module 개발 [2/3]

from https://r1.community.samsung.com/갤럭시-s/s22-울트라-카메라-수채화-현상-개선-요청드립니다-타-기기와-비교-결과-심층분석-꼭-답변-부탁드려요
from https://r1.community.samsung.com/t5/%EA%B0%A4%EB%9F%AD%EC%8B%9C-s

앞의 글에서 Camera2 API 를 사용하는 이유, RAW 가능 여부 확인, SurfaceView 커스터마이징, DNG 란 무엇인가, 에 대해 이야기 나누었다. 이번에는 DNG Creator 사용하는 것과 Galaxy A 시리즈의 한계에 대해서 나누어 보고자 한다.

Camera2 API 에서 이미지를 캡처하고, 그 결과물을 DngCreator 로 처리하는 실제 코드를 흐름 순으로 확인해보자.

import android.content.Context
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.DngCreator
import android.hardware.camera2.TotalCaptureResult
import android.media.Image
import android.os.Environment
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale

/**
 * 캡쳐된 RAW 데이터를 DNG 파일 형식으로 저장합니다.
 * 이 함수는 파일 I/O 작업을 포함하므로, 별도의 IO 스레드에서 호출하는 것이 좋습니다.
 *
 * @param image           캡쳐된 RAW 데이터가 담긴 Image 객체 (메인 재료)
 * @param characteristics 카메라 하드웨어의 고유한 특성 정보 (카메라 신분증)
 * @param captureResult   사진 촬영 시점의 모든 설정값이 담긴 메타데이터 (촬영 레시피)
 * @param context         파일 저장을 위한 애플리케이션 Context
 * @return 성공적으로 저장된 DNG 파일 객체. 실패 시 null을 반환합니다.
 */
suspend fun saveRawImageAsDng(
    image: Image,
    characteristics: CameraCharacteristics,
    totalCaptureResult: TotalCaptureResult,
    context: Context
): File? = withContext(Dispatchers.IO) { // 파일 I/O는 IO 스레드에서!

    try {
        // 1. DngCreator를 생성합니다. '카메라 신분증'과 '촬영 레시피'를 전달합니다.
        val dngCreator = DngCreator(characteristics, totalCaptureResult)

        // 2. 고유한 파일명을 생성합니다. 타임스탬프를 사용하는 것이 일반적입니다.
        val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
        val dngFile = File(
            context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), // 앱 외부 저장소의 사진 폴더
            "RAW_IMG_${timestamp}.dng"
        )

        // 3. FileOutputStream을 통해 '메인 재료(Image)'를 DNG 파일로 씁니다.
        //    use 블록을 사용하면 스트림이 자동으로 닫혀 안전합니다.
        FileOutputStream(dngFile).use { outputStream ->
            dngCreator.writeImage(outputStream, image)
        }

        // 4. 모든 과정이 성공하면, 완성된 파일 객체를 반환합니다.
        return@withContext dngFile

    } catch (e: IOException) {
        // 파일 쓰기 중 오류가 발생한 경우
        e.printStackTrace()
        return@withContext null
    } finally {
        image.close()
    }
}

실제 카메라 앱에서는 ImageReader.OnImageAvailableListner 안에서 이 함수를 호출하게 된다. 리스너에서 Image 객체를 얻고, 미리 저장해 둔 TotalCaptureResult (자동 초점값, 자동 노출, 수동 초점 거리, 줌 비율을 담고 있는 객체) CameraCharacteristics 를 함께 이 함수에 전달하는 방식이다. DngCreator 가 완벽한 DNG 파일을 만들기 위해서는 3가지 핵심 재료가 필요하다.

  1. Image 객체: 카메라 센서가 포착한 순수한 RAW 픽셀 데이터입니다. (메인 재료)
  2. TotalCaptureResult 객체: 사진이 촬영된 순간의 모든 설정값(ISO, 노출, 화이트밸런스 등)이 담긴 메타데이터입니다. (촬영 레시피)
  3. CameraCharacteristics 객체: 사용된 카메라 하드웨어의 고유한 물리적 특성 정보입니다. (카메라의 신분증)

DngCreator 를 사용하여 RAW 데이터를 DNG 파일로 저장을 한다. DNG 파일을 BitmapFactory 객체를 통해 Bitmap 으로 디코딩 한다. Bitmap 을 JPEG 형식으로 압축하여 파일로 저장을 한다. DNG 파일은 대략 25MB 를 넘었다. 그래서 최종적으로 JPEG 를 만들기까지 시간이 5초 이상은 걸렸다. 그래도 정확도를 위해 이렇게 해야만 했다. (더 좋은 방법이 있으면 알려주면 반영하겠다.)

// DNG 파일을 Bitmap으로 변환
val bitmap: Bitmap = BitmapFactory.decodeFile(dngFile.absolutePath)

// Bitmap을 JPEG로 저장
val jpegFile = File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "IMG.jpg")
FileOutputStream(jpegFile).use { outputStream ->
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
}

심지어 촬영된 이미지는 3840 * 2160 (4k) 의 이미지였다. Business 팀에서 이 화질의 이미지를 AI 분석에 활용하기로 최종적으로 합의 하였다.

개발 과정 가운데 Galaxy A 시리즈 폰들은 3840 * 2160(4k) 이미지를 가져오지 못한다. 따라서 1920 * 1080 의 이미지로 대체하였다. 나아가 앞의 글에서 말했듯이 RAW Image 를 가져오지 못한다. (App 이 Crash 됨). 따라서 앞단에서 RAW Image 를 가져오지 못하고 getOutputSizes 에서 3840 * 2160(4k) 이 목록에 존재하지 않을 때는 CameraX API 를 사용해 프로세스를 진행하였다.

한가지 조심해야할 것은, 실제 제조사 카메라 앱에서 4K 출력을 지원할 수도 있다. 하지만 어떠한 이유인지는 몰라도 CameraX API 에서 4k 촬영이 안될 것이다. 제조사의 정책과 하드웨어 설계에 따라 달라질 수 있는 부분이니 잘 염두해 둬야 한다.

Read more

Galaxy 수채화 현상 완전 정복! Raw Image 를 활용한 Android Camera Module 개발 [3/3]

Galaxy 수채화 현상 완전 정복! Raw Image 를 활용한 Android Camera Module 개발 [3/3]

앞선 글에서 Raw Image를 DNG Creator 를 활용함으로 최종적으로 JPEG 파일까지 만드는 과정에 대해 이야기를 나눴다. 추가적으로 우리 키트 배경지에는 ArUco 마커가 그려져 있다. AI 개발자와의 논의 끝에 도입한 내용이였으며, 회사에서도 배경지에 추가하기로 하였다. 굳이 사진 촬영을 하는 가운데 ArUco 마커를 도입한 이유와 과정에 대해 설명해보려고 한다. // 파일명: ArucoDetector.kt

By Jeongsu Choi
그래도 걸어야 한다 (영화 다가오는 것들)

그래도 걸어야 한다 (영화 다가오는 것들)

3개월에 한번은 프랑스 영화를 본다. 서른살이 넘으면서 나에게 했던 작은 다짐이다. 잘 한 것 같다. 영화, 다가오는 것들(L'Avenir)을 최근들어 다시 봤다. 이 영화는 반드시 글로 나름대로 남겨야 한다는 생각이 들었다. 줄거리를 어느정도는 이야기해야겠지. 철학 교수인 나탈리는 한꺼번에 말도 안되는 일을 경험한다. 남편은 바람이 나서 따로 살겠다고

By Jeongsu Choi
Galaxy 수채화 현상 완전 정복! Raw Image 를 활용한 Android Camera Module 개발 [1/3]

Galaxy 수채화 현상 완전 정복! Raw Image 를 활용한 Android Camera Module 개발 [1/3]

💡 TL;DR Galaxy S22/S23 수채화 현상으로 헬스케어 앱의 키트 판독 정확도가 15%까지 떨어졌다. CameraX에서 Camera2 API로 전환하고 Raw Image 처리 기반 커스텀 Camera Module을 개발해 판독 정확도를 95%까지 향상시켰다. 이 시리즈에서는 문제 분석부터 해결 과정, 성능 최적화까지 전 과정을 다룬다. Galaxy S22 이후부터 수채화 현상이 나타나는 경우가

By Jeongsu Choi