-
Notifications
You must be signed in to change notification settings - Fork 1
이미지 압축 기능 구현
summercat edited this page Oct 18, 2023
·
3 revisions
- 사용자가 선택한 사진을 API에 업로드하고, 분석 결과를 받는 시간이 오래 걸려 사용자 경험 저하
- 사용자의 사진/서버에서 받은 사진의 크기가 커 캐시 용량이 빠르게 소진되는 현상 발생
- 사용자의 사진을 사진 분석 API에 업로드하기 전에 이미지 압축 후 업로드함으로써 데이터 업로드, 사진 분석 시간을 단축시켰습니다.
- API로부터 다운받은 이미지 데이터를 사용/캐싱하기 전 압축함으로써 캐시 공간 사용 효율성을 높였습니다.
용량이 큰 이미지일 수록 압축 비율을 높게 해서 설정한 maxByte 크기 미만이 될 때까지 이미지를 반복해서 압축하는 방식입니다. 압축 과정은 아래의 과정을 순서대로 반복합니다.
- 압축할 비율(줄일 비율. 예: 0.1만큼 줄인다)을 구한다.
- 해당 비율에 맞추어
canvasSize
를 생성한다. - 해당
canvasSize
에 맞추어 이미지를 리사이징 한다. - 리사이징한 이미지의 크기를 구하고, 위의 과정을 반복한다.
// ImageCompressor.swift
struct ImageCompressor {
static func compress(
imageData: Data,
maxByte: Int = 1_024_000,
completion: @escaping (Data?) -> Void)
{
DispatchQueue.global().async {
guard let image = UIImage(data: imageData),
let currentImageSize = image.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}
var iterationImage: UIImage? = image
var iterationImageSize = currentImageSize
var iterationCompression: CGFloat = 1.0
while iterationImageSize > maxByte,
iterationCompression > 0.01 {
let percentageDecrease = getPercentageToDecreaseTo(forDataCount: iterationImageSize)
let canvasSize = CGSize(
width: image.size.width * iterationCompression,
height: image.size.height * iterationCompression)
iterationImage = image.resize(width: canvasSize.width, height: canvasSize.height)
guard let newImageSize = iterationImage?.jpegData(compressionQuality: 1.0)?.count else {
return completion(nil)
}
iterationImageSize = newImageSize
iterationCompression -= percentageDecrease
}
completion(iterationImage?.jpegData(compressionQuality: 1.0))
}
}
private static func getPercentageToDecreaseTo(forDataCount dataCount: Int) -> CGFloat {
switch dataCount {
case 0..<5_000_000:
return 0.03
case 5_000_000..<10_000_000:
return 0.1
default:
return 0.2
}
}
}
// UIImage+resize
extension UIImage {
func resize(width: CGFloat, height: CGFloat) -> UIImage {
return UIGraphicsImageRenderer(size: CGSize(width: width, height: height)).image { _ in
self.draw(in: CGRect(x: 0, y: 0, width: Int(width), height: Int(height)))
}
}
}
- 참고링크
- 이미지 데이터 매개변수/반환 타입을
UIImage
에서Data
로 변경- 뷰모델과 같이 로직을 처리하는 객체에서 호출할 것이라고 생각했기 때문에, 해당 객체에서 UI에 의존하지 않을 수 있도록(
import UIKit
하지 않아도 되도록) 이미지 데이터와 관련한 매개변수/반환 타입을Data
타입으로 변경하였습니다.
- 뷰모델과 같이 로직을 처리하는 객체에서 호출할 것이라고 생각했기 때문에, 해당 객체에서 UI에 의존하지 않을 수 있도록(
-
maxByte
에 1MB를 기본값으로 설정- 1MB로 설정한 이유는 카카오톡에서 사진을 'Standard Quality'로 압축해서 보낼 때 파일 크기가 100Kb ~ 700Kb 사이였기 때문에 1MB 정도면 충분하다고 생각했습니다.
-
UIGraphicsBeginImageContext
를 사용하지 않도록 변경- 공식문서를 확인해 보니 iOS17.0 이후 deprecated 될 예정이어서,
UIGraphicsImageRenderer
를 사용하는 메서드를 이용하도록 내부 구현을 변경해 주었습니다 (UIGraphicsImageRenderer
는 iOS10.0 이상에서 사용 가능)
- 공식문서를 확인해 보니 iOS17.0 이후 deprecated 될 예정이어서,