Android/Camera

[Android] 카메라 캡처(촬영)하기 - CameraX ˙ ImageCapture

O_Gyong 2022. 11. 14.
 

[Android] 카메라 미리보기 - CameraX · PreviewView

앱에 카메라 기능을 추가하려는 경우 CameraX와 Camera2를 사용할 수 있다. CameraX는 Camera2 패키지를 기반으로 만들어진 Jetpack 라이브러리로, API가 Camera2보다 훨씬 단순하고 기기 호환성 문제가 없어

ogyong.tistory.com

카메라 미리보기에 이어서 카메라 캡처를 해보려 한다.

 

간단하게 '캡처하기' 버튼을 클릭하면 ImageView에 해당 이미지를 보여주고, '다시찍기' 버튼을 클릭하면 초기화면으로 돌아가는 앱을 만들었다.


카메라 캡처(촬영)하기 예제

카메라 캡처를 하기 위해서는 ImageCapture의 takePicture를 사용해야 한다. takePicture는 캡처된 이미지를 전달받은 파일 위치에 저장하는 기능을 갖고 있다.

파일은 캐시에 임시로 저장해 두고 Glide를 통해 캡처한 이미지를 그렸다. 캡처하기나 '다시찍기' 버튼을 클릭했을 때 해당 파일을 삭제하도록 하였다.

 

1. gradle에 Glide 의존성 추가
2. 이미지를 저장할 파일 경로 선언
3. ImageCapture 선언 및 CameraProvider에 정보 넘겨주기
4. takePicture 사용 및 Glide를 통해 이미지 표시
5. 버튼 이벤트 처리

Glide 의존성 추가

//Glide
implementation 'com.github.bumptech.glide:glide:4.13.2'

이미지 파일 경로 설정

class MainActivity : AppCompatActivity() {

    private lateinit var photoFile: File // 파일 변수 선언

    override fun onCreate(savedInstanceState: Bundle?) {
    
       /**
        * 생략
        */

        // 이미지 임시 파일 생성 -> 캐시 생성(data/data/패키지/cache)
        photoFile = File(
            applicationContext.cacheDir,
            "newImage.jpg"
        )
    }
}

photoFile 변수를 사진을 캡처할 때도 사용해야 하므로 필드 변수로 선언했다.

파일 경로는 applicationContext.cacheDir을 통해 캐시에 저장하도록 하였고 파일명은 "newImage"로 했다.


ImageCapture 선언 및 CameraProvider에 정보 넘겨주기

 

class MainActivity : AppCompatActivity() {

    private var imageCapture: ImageCapture? = null // 이미지 캡처를 위한 변수
 
    /**
     * 카메라 실행
     */
    private fun startCamera() {

        cameraProviderFuture.addListener({

            // 이미지캡처 Builder 객체 생성
            imageCapture = ImageCapture.Builder().build()

            try {
				
                // CameraProvider에 imageCapture 정보 넘기기
                camera = cameraProvider.bindToLifecycle(
                    this, cameraSelector, preview, imageCapture)
                    
                /**
                * 생략
                */

            } catch (exc: Exception) {
                println("에러 $exc")
            }
            preview.setSurfaceProvider(mBinding.viewFinder.surfaceProvider)
        }, ContextCompat.getMainExecutor(this))
    }
}

ImageCaptrue 객체는 카메라가 실행되고 있을 때 ImageCapture.Builder 객체로 초기화하여 CameraProvider에 해당 정보를 넘겨줘야 한다. 그리고 캡처를 할 때 다시 필요하기 때문에 필드 변수로 선언한다.

카메라가 실행되면 imageCapture 변수를 ImageCapture.Builder 객체로 초기화시켜주고 CameraProvider에 정보를 넘겨준다.


takePicture 사용 및 Glide를 통해 이미지 표시

    /**
     * 사진 캡처(촬영)
     */
    private fun takePhoto() {
        val mImageCapture = imageCapture ?: return

        // ImageCapture.OutputFileOptions는 새로 캡처한 이미지를 저장하기 위한 옵션
        // 저장 위치 및 메타데이터를 구성하는데 사용
        val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()

        // takePicture를 통해 사진을 촬영.
        mImageCapture.takePicture(
            outputOptions,
            ContextCompat.getMainExecutor(this),
            object : ImageCapture.OnImageSavedCallback {
                override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                    // Glide를 통해서 ImageView에 이미지 캡처한 이미지 설정
                    Glide.with(this@MainActivity)
                        .load(outputFileResults.savedUri)
                        .apply(
                            // 이전 이미지를 재활용하지 않도록 처리
                            RequestOptions()
                                .diskCacheStrategy(DiskCacheStrategy.NONE)
                                .skipMemoryCache(true)
                        )
                        .into(mBinding.ivCapture)

                    // 이미지 그린 후 UI 변경
                    mBinding.ivCapture.visibility = View.VISIBLE
                    mBinding.viewFinder.visibility = View.INVISIBLE
                    mBinding.btnCapture.text = getString(R.string.re_capture)
                }

                override fun onError(exception: ImageCaptureException) {
                    Toast.makeText(applicationContext, "사진 촬영에 실패하였습니다.", Toast.LENGTH_SHORT).show()
                }
            }
        )
    }

takePicture의 콜백 메서드를 통해 캡처 이후에 Glide를 사용하여 이미지를 표시하도록 했다.

Glide를 사용할 때 RequestOptions()로 작성한 부분을 빼면 캐시를 삭제해도 계속 이전 이미지로 나오는 현상이 있다.

경로가 바뀌지 않으면 Glide 자체 캐시에 저장된 이미지를 계속 재활용한다는 것 같다.

 

이미지를 표시한 이후에는 이미지를 보기 위해 UI 변경을 해준다.


버튼 이벤트 처리

override fun onCreate(savedInstanceState: Bundle?) {

  /**
   * 생략
   */
   
    mBinding.btnCapture.setOnClickListener {
        // 임시 파일 삭제
        photoFile.delete()
        if(mBinding.btnCapture.text == "캡처하기"){
            // 사진 캡처 하기
            takePhoto()
        }else {
            // 사진 캡처 세팅
            mBinding.ivCapture.visibility = View.INVISIBLE
            mBinding.viewFinder.visibility = View.VISIBLE
            mBinding.btnCapture.text = getString(R.string.capture)
        }
    }
}

버튼을 클릭했을 때 캐시에 저장한 파일을 삭제하고 '캡처하기' 버튼인지 '다시찍기' 버튼인지에 따라 다른 동작을 하도록 처리했다.

내용 참고


 

 

전체 코드

'Android > Camera' 카테고리의 다른 글

[Android] 카메라 미리보기 - CameraX · PreviewView  (0) 2022.11.09

댓글