봄날은 갔다. 이제 그 정신으로 공부하자

android Bitmap과 함께하는 즐거운 시간 3번째 본문

android Tip

android Bitmap과 함께하는 즐거운 시간 3번째

길재의 그 정신으로 공부하자 2021. 3. 24. 11:11

곰곰히 생각해보는 createScaledBitmap()과 compress() 함수 차이

 

이미지 관련 서비스 개발 시 개발자들이 자주 접하는 함수가 위 두 개 함수 입니다.

 

뒤에 자세히 설명하겠지만 대략 설명하자면 

createScaledBitmap()함수는 사이즈가 큰 비트맵을 내가 원하는 사이즈에 맞게 리사이징하는 함수이고

compress()함수는 이미지의 사이즈는 그대로 두고 퀄리티를 조절하는 함수 입니다.

 

이렇게 설명하면 고개를 끄덕거리는 분들도 있겠지만 고개를 끄덕이는 분들은 이글을 잘 읽지 않을테니... ㅠ_ㅠ

 

예를 들어 설명하자면,

읽어올 사진의 사이즈가 가로*세로 2048*2048 사이즈의 5MB 메모리 용량(저장된 파일 사이즈 아님.)을 가진 사진이라고 할 때 createScaledBitmap()함수를 사용해서 512*512 사이즈의  3KB 메모리 용량을 가진 사진으로 변환해 읽어올 수 있습니다.

 

compress()함수에서 퀄리티를 1/16 수준으로 떨어뜨려 읽어오는 경우, 이미지의 퀄리티는 앞서 언급한 512*512 수준으로 떨어지지만 메모리 용량은 그대로 5MB입니다.

 

퀄리티가 떨어지지만 메모리 용량은 그대로인 이유는 

compress()함수는 이미지의 퀄리티를 조절하는 함수이지 이미지의 사이즈를 조절하는 함수가 아니기 때문 입니다.

퀄리티를 떨어뜨려 읽어왔다고 해도 읽어온 이미지가 2048*2048 사이즈를 가지는 Bitmap이기  때문에 메모리 용량은 그대로 5MB인 것 입니다. 그에 비해 createScaledBitmap()함수는 512*512 사이즈로 사이즈를 줄여서 Bitmap을 만들었기 때문에 메모리 용량이 사이즈에 맞게 줄어든 것 입니다.

 

그럼 이 두개의 함수는 어디에 쓰이면 좋을까요?

createScaledBitmap()함수는 많은 이미지를 동시에 읽어와 처리하는 앱에서 메모리를 절약하기 위해 사용하고 

compress()함수는 파일을 저장 할 때 파일이 필요 이상의 용량을 가지는 경우, 저장 공간의 압박이 있으므로 이를 관리하기 위해 사용하는 것이 좋습니다.

 

createScaledBitmap()함수 사용방법

createScaledBitmap()함수는 4개의 매개변수를 필요로 합니다.

첫번째는 스케일을 조절한 Bitmap이고

두번째는 조절할 Bitmap의 가로 사이즈,

세번째는 조절할 Bitmap의 세로 사이즈,

마지막은 필터 적용 여부 입니다. 

마지막 매개 변수가 

   false일 경우: 지금  픽셀 형태 그대로 스케일을 조절하므로 이미지가 깨지거나 흐려보이게 되고

   true 일 경우: 픽셀 형태를 조정해 주어서 이미지가 선명하게 보이도록 도움을 줍니다.

리턴 값으로 스케일이 변환된 Bitmap이 반환됩니다.

 

아래 함수는 bitmap을 매개변수로 받아 bitmap 사이즈에 맞추어서 bitmap을 스케일 다운해주는 함수 입니다.

fun scaleDown(bitmap : Bitmap): Bitmap {
    val quality = if(bitmap.width > 2048 && bitmap.height > 2048) {
        30
    }else if(bitmap.width > 1024 && bitmap.height > 1024){
        50
    }else{
        80
    }
    val scaleDown = Bitmap.createScaledBitmap(bitmap, (bitmap.width*quality).toInt(), (bitmap.height*quality).toInt(), true)
    return scaleDown
}

 

조심해야 할 부분은 함수 호출 후 입니다.

비트맵 스케일 조절 함수를 사용한 다는 것은 메모리를 최대한 절약해서 사용해야 하는 상황이므로 scaleDown()함수 호출 시 매개변수로 사용한 bitmap은 recycle() 함수를 호출하여 메모리를 해제해주어야 합니다.

 

compress()함수 사용방법

이 함수는 사용방법이 조금 더 쉽습니다.

첫번째 매개변수는 압축할 파일 타입 입니다.

   JPEG와 PNG 타입 두개가 있는데, JPEG 타입이 PNG보다 압축 속도가 더 빠르고 압축 시 파일 용량이 더 작습니다.

   이유는 PNG는 투명값을 가지고 있는 32BIt btimap이고 JPEG는 투명값이 없는 24bit Bitmap이기 때문 입니다.

   기본적으로 JPEG 타입을 사용하면 되지만 투명 값을 가진 Bitmap을 저장 할때는 반드시 PNG 타입을 사용해야 합니다.

두번째 매개변수는 압축 퀄리티로 1~100까지의 Integer value 입니다.

   당연하겠지만 100이면 원본 그대로의 퀄리티이고 1이면 최저의 퀄리티(해상도)를 가집니다.

   적당한 퀄리티를 사용하는 것이 좋습니다.

세번째 매개변수는 압축된 Byte Array를 받을 매개 변수 입니다.

 

사용법은 아래와 같습니다.

var outStream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream)

// 이렇게 퀄리티 다운된 비트밷은 아래와 같은 방법으로 다시 비트맵으로 만들면 됩니다.
val sdBitmap = BitmapFactory.decodeByteArray(outStream.toByteArray(), 0, outStream.toByteArray().size)

// 사용 완료된 메모리 해제해주는 것 잊지 마시구요…
outStream.flush()
bitmap.recycle()

 

원본 비트맵을 읽어와서 비교를 해보면 bitmap.compress()함수 호출 시 퀄리티에 따라 outStream의 사이즈가 변하는 것을 확인할 수 있습니다.  하지만 이를 다시 Bitmap으로 변환해보면…

val sdBitmap = BitmapFactory.decodeByteArray(outStream.toByteArray(), 0, outStream.toByteArray().size)

 

비트맵의 사이즈가 퀄리티에 상관 없이 동일한 것을 알 수 있습니다.

이는 위에 언급한 것 처럼 압축하는 것은 파일 용량에 영향을 주지만 화면에 보여주기 위해 bitmap으로 만들어질 때는 OS의 비트맵 포맷에 영향을 받기 때문 입니다.

제가 알기로 android는 16bit Bitmap(565)을 사용하는 것으로 알고 있습니다.

 

정리

createScaledBitmap()함수와 compress()함수는 다른 사용성을 가진 함수입니다.

createScaledBitmap()함수는 비트맵의 사이즈를 조정해서 메모리 용량을 절약할 수 있고

compress()함수는 비트맵 파일의 용량을 조정하여 저장 공간과 읽어올 때 메모리를 절약할 수 있습니다.

 

createScaledBitmap()함수는 비트맵의 사이즈가 변하므로 사이즈를 계산해서 처리하는 서비스의 경우, 신중히 사용해야 합니다.

그에 비해 compress()함수 또한 비트맵 파일에 영구적으로 영향을 주므로 신중히 사용해야 합니다.

흔히 하는 실수로 저장 시마다 80% 퀄리티로 파일을 저장하게 한다면 디지털 풍화를 경험할 있는 좋은 기회가 됩니다. ^___^;;;

Comments