ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android에서 Camera2 API를 활용한 RAW 이미지 캡처
    영상처리 2025. 2. 24. 16:20
    728x90

    일반적인 스마트폰 카메라는 JPG 형식의 이미지만 저장하지만, 보다 높은 화질과 후처리 가능성을 위해 RAW 이미지를 캡처하는 방법이 필요합니다. Camera2 API를 활용하면 스마트폰의 센서 데이터를 직접 RAW 포맷으로 저장할 수 있습니다. 이 글에서는 RAW 이미지 캡처 앱을 개발하는 방법을 설명합니다.

    1. RAW 이미지 캡처의 원리

     RAW 데이터란?

    RAW 데이터는 이미지 센서에서 수집된 원본 픽셀 정보를 그대로 저장하는 파일 형식입니다. 일반적으로 DNG(Digital Negative), ARW, CR2, NEF 등의 확장자로 저장됩니다. JPG와 달리, 압축 과정이 없어 세부 정보를 보존하며, 후처리를 통해 화이트 밸런스, 노출, 색감을 자유롭게 조정할 수 있습니다.

     Android에서 RAW 캡처 지원 여부 확인

    Android에서는 Camera2 API를 사용해야 RAW 캡처가 가능합니다. 하지만 모든 기기가 이를 지원하는 것은 아니므로, CameraCharacteristics를 이용해 RAW 지원 여부를 확인해야 합니다.

    val manager = getSystemService(CAMERA_SERVICE) as CameraManager
    val cameraId = manager.cameraIdList[0]
    val characteristics = manager.getCameraCharacteristics(cameraId)
    
    val rawCapability = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES)?.contains(
        CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW
    ) ?: false
    
    if (!rawCapability) {
        Log.e("Camera2", "이 기기는 RAW 촬영을 지원하지 않습니다.")
    }

    2. Camera2 API를 이용한 RAW 캡처 앱 개발

     1. 프로젝트 설정

    필요한 권한 추가

    AndroidManifest.xml에 카메라 및 저장소 접근 권한을 추가합니다.

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    기본 레이아웃 구성

    activity_main.xml 파일에서 TextureView버튼을 배치하여 촬영 기능을 구현합니다.

    <TextureView
        android:id="@+id/textureView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    
    <Button
        android:id="@+id/captureButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="RAW 촬영" />

     2. RAW 촬영 코드 구현

    Camera2 API를 이용해 RAW 이미지를 촬영하고 저장하는 방법을 구현합니다.

    카메라 초기화 및 세션 설정

    Camera2 API를 사용하기 위해 카메라를 열고 세션을 생성합니다.

    private fun openCamera() {
        val manager = getSystemService(CAMERA_SERVICE) as CameraManager
        val cameraId = manager.cameraIdList[0]
        val characteristics = manager.getCameraCharacteristics(cameraId)
    
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1)
            return
        }
        
        manager.openCamera(cameraId, object : CameraDevice.StateCallback() {
            override fun onOpened(camera: CameraDevice) {
                cameraDevice = camera
                setupImageReader()
                createCameraCaptureSession()
            }
    
            override fun onDisconnected(camera: CameraDevice) {
                camera.close()
            }
    
            override fun onError(camera: CameraDevice, error: Int) {
                camera.close()
            }
        }, backgroundHandler)
    }

    RAW 데이터를 처리하기 위한 ImageReader 설정

    private fun setupImageReader() {
        val pixelArraySize = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)
        val width = pixelArraySize?.width ?: 4000
        val height = pixelArraySize?.height ?: 3000
    
        imageReader = ImageReader.newInstance(width, height, ImageFormat.RAW_SENSOR, 2)
        imageReader.setOnImageAvailableListener({ reader ->
            val image = reader.acquireLatestImage()
            image?.let {
                saveRawImage(it)
                it.close()
            }
        }, backgroundHandler)
    }

    RAW 이미지를 저장하는 코드

    private fun saveRawImage(image: Image) {
        val fileName = "raw_image_${System.currentTimeMillis()}.dng"
        val outputStream: OutputStream? = applicationContext.contentResolver.openOutputStream(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI
        )
        outputStream?.use { output ->
            val dngCreator = DngCreator(cameraCharacteristics, captureResult!!)
            dngCreator.writeImage(output, image)
        }
    }

     3. RAW 촬영 실행

    버튼을 눌러 RAW 촬영을 실행합니다.

    captureButton.setOnClickListener {
        captureRawImage()
    }
    private fun captureRawImage() {
        val captureBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
            addTarget(imageReader.surface)
            set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
        }
    
        captureSession!!.capture(captureBuilder.build(), object : CameraCaptureSession.CaptureCallback() {
            override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
                Log.d("Camera2", "📸 RAW 촬영 완료")
            }
        }, backgroundHandler)
    }

    3. RAW 촬영 결과 예시

    아래 이미지는 Camera2 API를 사용하여 RAW 포맷으로 촬영한 후 변환한 샘플입니다.

    raw image capture app으로 촬영된 영상 변환 결과. color filter array의 pattern이 보인다.

    4. 프로젝트 전체 코드

    전체 프로젝트 코드는 GitHub에서 확인할 수 있습니다.

    🔗https://github.com/Namseop2/RawCaptureProject

     

    GitHub - Namseop2/RawCaptureProject: App for Raw image capture

    App for Raw image capture. Contribute to Namseop2/RawCaptureProject development by creating an account on GitHub.

    github.com

    촬영된 DNG 파일을 png로 변환하는 코드는 아래에서 확인할 수 있습니다.

    https://github.com/Namseop2/rawfile_converter

     

    GitHub - Namseop2/rawfile_converter: RawFile(DNG) to PNG converter

    RawFile(DNG) to PNG converter. Contribute to Namseop2/rawfile_converter development by creating an account on GitHub.

    github.com

     

    5. 결론

    Camera2 API를 이용해 Android에서 RAW 이미지를 촬영하는 방법을 설명했습니다. RAW 데이터는 후처리를 통해 더욱 풍부한 색상과 디테일을 살릴 수 있으며, 촬영 후 DNG 파일을 Python과 OpenCV를 이용해 처리하면 더 유연한 활용이 가능합니다.

    728x90
Designed by Tistory.