camera intent로 카메라를 구현하는 경우, 화질이 매우 낮다.

(촬영하고 난 썸네일 이미지를 그대로 사용해서 그렇다고 함)

그래서 촬영한 이미지를 저장하고, 그 저장한 이미지를 불러오는 식으로 카메라의 화질을 높일 수 있다.

 

또한, 촬영한 이미지가 회전돼서 출력되는 현상도 있는데, 

이미지를 돌아간 만큼 다시 회전시켜주는 기능도 같이 구현할 것이다.

 

이 사진을 서버에 업로드하는 경우, 찍힌 파일 그대로 업로드하면 용량이 너무 커서

multipart로 업로드할때 시간이 너무 오래걸리는데 (5초정도 걸린거같다.)

사진을 압축해서 보내는 것도 추가했다.

 


 

AndroidManifest.xml

1
2
3
4
5
6
7
8
9
<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.food_classification.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"></meta-data>
</provider>
cs

 

파일 공유 방식으로 이미지를 로드할 것이기 때문에 FileProvider를 사용한다.

file_paths는 xml폴더 안에 새로 만들어서 작성한다.

 

 

xml/file_paths.xml

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="my_images"
        path="Android/data/com.example.food_classification/files/Pictures" />
</paths>
cs

 

저장할 파일의 경로를 설정해준다.

 

 

MainActivity.kt

저번에 했던 코드를 수정해서 진행한다.

1
2
3
4
5
6
7
8
9
10
11
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // 카메라 실행 부분
                if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                    dispatchTakePictureIntent()
                    //val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
                    //startActivityForResult(cameraIntent, 1)
                } else {
                    Log.d("test""권한 설정 요청")
                    ActivityCompat.requestPermissions(this@MainActivity, arrayOf<String?>(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE), 1)
                }
}
cs

 

dispatchTakePictureIntent()가 새로 생겼는데, 

이 메서드에서 사진을 찍고 저장하는 기능을 구현한다.

 

 

dispatchTakePictureIntent()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private fun dispatchTakePictureIntent() {
        val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        // Ensure that there's a camera activity to handle the intent
        if (takePictureIntent.resolveActivity(packageManager) != null) {
            // Create the File where the photo should go
            var photoFile: File? = null
            try {
                photoFile = createImageFile()
            }
            catch (ex: IOException) { // 파일을 만드는데 오류가 발생한 경우
            }
            // Continue only if the File was successfully created
            if (photoFile != null) {
                val photoURI: Uri = FileProvider.getUriForFile(this,
                        BuildConfig.APPLICATION_ID + ".provider",
                        photoFile)
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO)
            }
        }
}
cs

 

코드는 Android developers에서 참고했다.

이제 onActivityResult에서 찍힌 파일을 bitmap으로 불러온 뒤에,

사진을 회전시키고 압축하는 기능을 구현한다.

 

 

onActivityResult

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        //findViewById(R.id.result_image).setImageURI(photoUri);
        try {
            when (requestCode) {
                REQUEST_TAKE_PHOTO -> {
                    if (resultCode === Activity.RESULT_OK) {
                        val file = File(mCurrentPhotoPath)
                        myBitmap = MediaStore.Images.Media
                                .getBitmap(contentResolver, Uri.fromFile(file))
                            if (myBitmap != null) {
                                val exif = mCurrentPhotoPath?.let { ExifInterface(it) }
                                val exifOrientation: Int = exif!!.getAttributeInt(
                                        ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
                                val exifDegree = exifOrientationToDegrees(exifOrientation)
                                myBitmap = rotate(myBitmap, exifDegree)
                            }
                            else {
                                Log.d("myBitmap null""null")
                            }
                    }
                }
            }
        } catch (error: Exception) {
            error.printStackTrace()
        }
}
cs

 

exifOrientationToDegrees는 ExifInterface라는 사진의 정보를 담고 있는 객체를 통해 사진의 정보를 받아와

사진에 대한 각도가 얼마인지 반환하는 함수이다.

rotate 함수가 사진을 회전하고, 압축하는 함수이다.

 

 

exifOrientationToDegrees

1
2
3
4
5
6
7
8
9
10
fun exifOrientationToDegrees(exifOrientation: Int): Int {
        if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
            return 90
        } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
            return 180
        } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
            return 270
        }
        return 0
}
cs

ExifInterface를 사용한다.

Android developers 참고

 

 

rotate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun rotate(bitmap: Bitmap?, degrees: Int): Bitmap? { // 이미지 회전 및 이미지 사이즈 압축
        var bitmap = bitmap
        if (degrees != 0 && bitmap != null) {
            val m = Matrix()
            m.setRotate(degrees.toFloat(), bitmap.width.toFloat() / 2,
                    bitmap.height.toFloat() / 2)
            try {
                val converted = Bitmap.createBitmap(bitmap, 0, 0,
                        bitmap.width, bitmap.height, m, true)
                if (bitmap != converted) {
                    bitmap.recycle()
                    bitmap = converted
                    val options = BitmapFactory.Options()
                    options.inSampleSize = 4
                    bitmap = Bitmap.createScaledBitmap(bitmap, 1280, 1280, true) // 이미지 사이즈 줄이기
                }
            } catch (ex: OutOfMemoryError) {
                // 메모리가 부족하여 회전을 시키지 못할 경우 그냥 원본을 반환합니다.
            }
        }
        return bitmap
}
cs

끝.

이제 해당 bitmap을 사용해 imageView에 출력해도 되고,

bitmap으로 이미지의 uri를 찾아 서버로 보낼 수도 있다.

+ Recent posts