UICollectionView 이미지 처리: downsampling(feat. WWDC Image and Graphics Best Practices)

jane·2022년 8월 25일
0

iOS

목록 보기
29/32
post-thumbnail

문제 상황

검색 결과를 UICollectionView에 표시할때 크기가 큰 이미지가 들어오는 상황에서

  1. 이미지 로딩 속도가 느리고, 메모리 사용량이 급격하게 늘어남
  2. 스크롤시 이미지가 깜빡거리면서 바뀌기도 하는 현상이 발생함

<원본 이미지 크기들>

참고: URL로 이미지의 사이즈 얻는 방법

let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, imageSourceOptions) else {
    return UIImage()
}

if let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as Dictionary? {
    let pixelWidth = imageProperties[kCGImagePropertyPixelWidth] as! Int
    let pixelHeight = imageProperties[kCGImagePropertyPixelHeight] as! Int
    print(url.path, "Width: \(pixelWidth), Height: \(pixelHeight)")
}

<메모리 사용량>
5번 검색하면 메모리 사용량이 1기가가 넘었다...
이걸 어째 🤯

큰 이미지를 사용해서 이런 결과가 나왔다면... 원본 이미지를 셀의 크기에 맞게 width 60 height 80으로 줄이면 해결되지 않을까?

해결 방안

단순히 이미지 사이즈만 줄이면 처리 속도도 빨라지지 않을까? 라는 이유로 처음엔 resizing을 생각했다.

하지만, WWDC 영상 'Image and Graphics Best Practices' 를 보고 생각이 바뀌었다.

결론부터 말하자면 메모리 사용량을 줄이려면, resizing이 아니라 downsampling을 해야한다.

  • 이미지 사이즈를 줄이면 될까? NO
  • 답은 downsampling

지금부터 그 이유에 대해 알아보자 😆

Image and Graphics Best Practices

UIImageView는 view를 display하는 역할을 하고,
UIImage는 view를 load하는 역할을 한다.

크게 보면 UIImage가 view(=이미지)를 로딩하면
UIImageView가 그 결과물을 렌더링해서 Frame Buffer라는 곳에서 보여준다.

  • UIImage가 하는일
    Data Buffer(JPEG, PNG...)에서 Image Buffer로 Decoding

  • UIImageView가 하는일
    Image Buffer를 UIImageView의 contentMode에 맞게 rendering 하여 Frame Buffer에 표시

이때 UIImage가 하는 decoding 작업은 굉장히 CPU 집약적이라서 비용이 크다고 한다. 따라서 UIImage는 한번 디코딩된 이미지(Image Buffer)를 거의 영구적으로 가지고 있어버림...
이때 저 디코딩된 이미지는 원본 이미지라서 사이즈가 큰 이미지인 경우에 굉장한 비용이 들어가는 것,,
(렌더링된 작은 UIImageView의 사이즈를 가지고 있으면 참 좋을텐데...)

이렇게 디코딩된 이미지를 저장하려고 메모리를 많이 할당하게 되면 다른 컨텐츠들을 파편화시키게됨... cpu가 개입하고.. 심하면 앱종료로 이어진다.

하여튼 결론은 원본 이미지의 사이즈가 큰 경우에 UIImage가 디코딩된 이미지를 가지고 있으므로 메모리 사용량이 늘어난다는 소리다.

Downsampling

그렇다면, UIImage가 data buffer를 디코딩하기 전에 원본 사이즈를 줄이고 나서 디코딩하고, 이때 디코딩된 이미지(image buffer)를 저장하면 되겠지!
그렇게 하면 앱의 메모리 사용량을 줄일 수 있다.

<원래 방식>

<downsampling 하는 경우>

디코딩 하기전에 data buffer 자체의 사이즈를 줄여버려서 메모리 절약 가능~!

코드

url을 통해 이미지를 불러오고,
downsampling 하고싶은 사이즈를 입력하면 된다.

예를 들어 이미지 크기를 width 60, height 90으로 줄이고 싶다면 CGSize(width: 60, height: 90) 입력하면 된다.

func downSample(at url: URL, to pointSize: CGSize, scale: CGFloat) -> UIImage {
    let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
    guard let imageSource = CGImageSourceCreateWithURL(url as NSURL, imageSourceOptions) else {
        return
    }
    
    let maxDimensionInPixels = max(pointSize.width, pointSize.height) * scale
    let downsampleOptions = [
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceShouldCacheImmediately: true,
        kCGImageSourceCreateThumbnailWithTransform: true,
        kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels
    ] as CFDictionary
    
    guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, downsampleOptions) else {
        return UIImage()
    }
    return UIImage(cgImage: downsampledImage)
}

커스텀 셀 configure에서 url을 받아서 셀의 이미지뷰에 이미지를 할당하는 부분에서 적용해주었다.

let serialQueue = DispatchQueue(label: "Decode queue")
serialQueue.async { [weak self] in
    
    let downsampled = downSample(
        at: url,
        to: CGSize(width: 60, height: 90),
        scale: UIScreen.main.scale
    )
    DispatchQueue.main.async {
        self?.posterImageView.image = downsampled
    }
}

그런데 Downsampling을 했더니

이미지들이 블러처리한듯이 퀄리티가 낮아졌다.

그 이유는 앞에서 설명하지 않은 마지막 파라미터
scale 때문... scale을 화면에 맞게 설정하지 않으면 이미지 퀄리티가 낮아보인다.

따라서 scale에는 현재 화면의 scale 정보인UIScreen.main.scale을 넣어줬는데
DownSampling makes image blurry

위 링크에 따르면 디스플레이의 종류에 따라서 1point가 몇개의 pixel로 이루어지는지가 달라진다고 한다.

UIScreen.main.scale

  • iPhone 13 Pro의 경우 3이었고
  • iPhone SE (3th gen)의 경우 2였다.
    와 신기하다.

그러면 똑같은 CGSize(width: 60, height: 90)을 표현하기 위해서 iPhone 13 Pro는 180, 270 pixel을 사용하고 iPhone SE는 120, 180 pixel을 사용한다는 것이다.

이렇게 downsampling을 적용한 결과 같은 횟수로 검색한 결과 메모리 사용량이 현저하게 줄었다.. 거의 1/5 😆

Reference

WWDC19 Image and Graphics Best Practices

profile
제가 나중에 다시 보려고 기록합니다 ✏️

0개의 댓글