본문 바로가기
2023년 이전/Android

Camera 촬영 후 해상도가 낮아지는 현상 해결 방법

by JeongUPark 2021. 12. 3.
반응형

프로젝트를 하면서 Camera를 사용할 일 이있어서 사용을 하였다.

처음에는 다음과 같이 적용을 하였다.

startActivityForResult(Intent(MediaStore.ACTION_IMAGE_CAPTURE), FLAG_REQ_CAMERA)

//////

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
                Config.FLAG_REQ_CAMERA -> {
                    if (data?.extras?.get("data") != null) {
                        val bitmap = data.extras?.get("data") as Bitmap
                        val filename = viewModel.newFileName()
                        val uri =
                            viewModel.saveImageFile(contentResolver, filename, "image/jpg", bitmap)
                        uri?.let {
                            viewModel.requestCrop(it).start(this@AddContentActivity)
                        } ?: {
                            Toast.makeText(applicationContext, "사진 촬영에 실패하였습니다.", Toast.LENGTH_LONG)
                                .show()
                        }()
	                    }
                }
            }
        }
    }

////////

fun newFileName() : String{
        val sdf = SimpleDateFormat("yyyyMMdd_HHmmss")
        val filename = "${sdf.format(System.currentTimeMillis())}.png"
        return filename
    }

fun saveImageFile(contentResolver: ContentResolver, filename:String, mimeType:String, bitmap: Bitmap) : Uri? {
        var values = ContentValues()
        values.put(MediaStore.Images.Media.DISPLAY_NAME, filename)
        values.put(MediaStore.Images.Media.MIME_TYPE, mimeType)

        //안드로이드 버전이 Q보다 크거나 같으면
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
            values.put(MediaStore.Images.Media.IS_PENDING, 1)   //사용중임을 알려주는 코드
        }

        //내가 저장할 사진의 주소값 생성
        val uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)

        try{
            if(uri != null){
                //쓰기모드 열기
                var descriptor = contentResolver.openFileDescriptor(uri,"w")
                if(descriptor != null){
                    val fos = FileOutputStream(descriptor.fileDescriptor)   //OutputStream 예외처리
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
                    fos.close()
                    return uri
                }
            }
        }catch (e: Exception){
            Log.e("Camera","${e.localizedMessage}")
        }

        return null
    }

위와 같이 할경우 캡처된 사진 자체의 해상도가 떨어져서 bitmap.compress로 qurity를 100으로 주어도 원본 해상도를 가져올 수 업습니다.

원본 해상도를 가져오기 위해서는 다음과 같이 해결하면 됩니다.

참고 :https://developer.android.com/training/camera/photobasics?hl=ko

해결

인텐트와 파일을 만드는함수

val currentPhotoPath : MutableLiveData<String> by lazy { MutableLiveData<String>() }
@Throws(IOException::class)
    public fun createImageFile(): File {
        // Create an image file name
        val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
        val storageDir: File? = appContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        return File.createTempFile(
            "JPEG_${timeStamp}_", /* prefix */
            ".jpg", /* suffix */
            storageDir /* directory */
        ).apply {
            // Save a file: path for use with ACTION_VIEW intents
            currentPhotoPath.value = absolutePath
        }
    }
    fun dispatchTakePictureIntent(type : String) : Intent =
        Intent(type).also { takePictureIntent ->
            // Ensure that there's a camera activity to handle the intent
            takePictureIntent.resolveActivity(appContext.packageManager)?.also {
                // Create the File where the photo should go
                val photoFile: File? = try {
                    createImageFile()
                } catch (ex: IOException) {
                    // Error occurred while creating the File
                    null
                }
                // Continue only if the File was successfully created
                photoFile?.also {
                    val photoURI: Uri = FileProvider.getUriForFile(
                        appContext,
                        "{pakagename}.fileprovider",
                        it
                    )
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                }
            }
        }

activity와 fragment에서는

fun takePicture(){
	startActivityForResult(viewModel.dispatchTakePictureIntent(MediaStore.ACTION_IMAGE_CAPTURE), Config.FLAG_REQ_CAMERA)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
                Config.FLAG_REQ_CAMERA -> {
                    val f = File(currentPhotoPath)
                    val uri = Uri.fromFile(f)
                    uri?.let { itUri ->
                        viewModel.requestCrop(itUri).start(this@AddContentActivity)
                    } ?: {
                        Toast.makeText(this, "사진 촬영에 실패하였습니다.", Toast.LENGTH_LONG).show()
                    }()
                }
            }
        }
    }

위에서 startActivityForResult 하고나면 MediaStore.ACTION_IMAGE_CAPTURE만 해주었을 때와는 다르게 onActivityResult의 data가 null로 들어 옵니다. 그래서 파일 생성시 path를 저장하여 사용 합니다.

Mainfest

<application>
.....
	<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="{packgename}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"></meta-data>
        </provider>
.....
</application>

참고로 안드로이드디벨로퍼는 위의 name이 android.support.v4.content.FileProvider 이렇게 되어있는데 error가 뜨므로 위와같이 바꺼주시면 됩니다.

그리고 xml폴더에 file_paths파일을 생성해 줍니다.



    

이때 디벨로퍼는 다음과 같이 설명해 주고 있는데


    
        
    

디벨로퍼처럼하면 생성된 위치의 파일을 읽지 못하여 계속 에러가 나타납니다.

Failed to find configured root that contain 위치

그래서 path를 계속 바꿔주면서 처리한결과 위와같이 <root-path name="my_images" path="." /> 해주었을 경우 정상동작하는 것을 확인 할 수 있었습니다.

이렇게 적용을하면 사진 찍고 나서 정상적이 해상도의 결과를 볼 수 있습니다.

반응형