iOS에서의 이미지 처리 및 성능 최적화 상세 정보

사진은 컴퓨터 세계에서 어떻게 저장되고 표시됩니까?


그림은 다른 모든 자원과 마찬가지로 메모리에서 본질적으로 0과 1의 이진 데이터이다. 컴퓨터는 이런 원시 내용을 사람의 눈으로 관찰할 수 있는 그림으로 과장해야 하고, 반대로 그림을 적당한 형식으로 메모리에 저장하거나 네트워크에 전송해야 한다.
그림을 어떤 규칙으로 2진 인코딩하는 방식이 바로 그림의 형식이다.

일반적인 그림 형식


사진의 형식은 여러 가지가 있는데 우리가 잘 알고 있는 JPG, PNG, GIF를 제외하고 Webp, BMP, TIFF, CDR 등 몇 십 가지가 있는데 서로 다른 장면이나 플랫폼에 사용된다.
이 양식들은 두 종류로 나눌 수 있는데 그것이 바로 유손 압축과 무손실 압축이다.
손실 압축: 색채에 비해 사람의 눈은 빛의 밝기 정보에 더욱 민감하다. 이를 바탕으로 그림의 색 정보를 통합하고 밝기 정보를 보존함으로써 그림의 관감에 영향을 주지 않는 전제에서 저장 부피를 줄일 수 있다.말 그대로 이렇게 압축된 사진은 세부적인 부분을 영구적으로 손실할 것이다.가장 전형적인 손실 압축 형식은 jpg이다.
무손실 압축: 유손실 압축과 달리 무손실 압축은 그림의 세부 사항을 손실하지 않습니다.그림의 부피를 낮추는 방식은 색인을 통해 그림의 서로 다른 색 특징에 대해 색인표를 만들어 중복된 색 데이터를 줄여 압축 효과를 얻는 것이다.흔히 볼 수 있는 무손실 압축 형식은 png,gif이다.
상기에서 언급한 형식을 제외하고 웹과 비트맵 두 가지 형식을 간단하게 소개할 필요가 있다.
웹p:jpg는 주류인 인터넷 사진 표준으로 90년대 초반까지 거슬러 올라갈 수 있으며 이미 매우 오래되었다.그래서 구글은 웹 표준의도를 낡은 jpg를 대체하여 인터넷 이미지의 탑재 속도를 가속화하고 이미지 압축의 질을 향상시키려고 내놓았다.
웹은 유손과 무손 두 가지 압축 방식을 동시에 지원하는데 압축률도 매우 높다. 무손 압축 후의 웹은 png보다 45%의 부피가 적고 같은 품질의 웹과 jpg는 전자도 절반의 유량을 절약할 수 있다.이 동시에 웹p는 동도를 지원하여 그림 압축 형식의 집대성자라고 할 수 있다.
웹의 단점은 브라우저와 모바일 지원이 아직 완벽하지 않다는 것이다. 구글의libwebp 프레임워크를 도입해야 하기 때문에 코딩도 상대적으로 더 많은 자원을 소모할 수 있다.
비트맵:비트맵은 비트맵 파일이라고도 하는데 *비압축*의 그림 형식이기 때문에 부피가 매우 크다.이른바 비압축이란 그림의 각 픽셀의 원시 정보가 메모리에 순서대로 배열된 전형적인 1920*1080 픽셀의bitmap 그림이다. 각 픽셀은 RGBA 네 바이트로 색을 나타낸다. 그러면 그 부피는 1920*1080*4=1012.5kb이다.
비트맵은 그림의 픽셀 정보를 간단하게 순서대로 저장하기 때문에 디코딩 없이 UI에 렌더링할 수 있습니다.실제로 다른 형식의 그림은 일반적으로 비트맵으로 먼저 디코딩된 후에야 인터페이스에 렌더링할 수 있다.

어떻게 그림의 격식을 판단합니까?


일부 장면에서 우리는 그림 데이터의 형식을 수동으로 판단하여 서로 다른 처리를 해야 한다.일반적으로 원시적인 2진 데이터를 얻기만 하면 서로 다른 압축 형식의 인코딩 특징에 따라 간단한 분류를 할 수 있다.다음은 복사하여 사용할 수 있는 그림 프레임워크의 일반적인 구현입니다.

+ (XRImageFormat)imageFormatForImageData:(nullable NSData *)data {
    if (!data) {
        return XRImageFormatUndefined;
    }
    
    uint8_t c;
    [data getBytes:&c length:1];
    switch (c) {
        case 0xFF:
            return XRImageFormatJPEG;
        case 0x89:
            return XRImageFormatPNG;
        case 0x47:
            return XRImageFormatGIF;
        case 0x49:
        case 0x4D:
            return XRImageFormatTIFF;
        case 0x52:

            if (data.length < 12) {
                return XRImageFormatUndefined;
            }
            
            NSString *testString = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(0, 12)] encoding:NSASCIIStringEncoding];
            if ([testString hasPrefix:@"RIFF"] && [testString hasSuffix:@"WEBP"]) {
                return XRImageFormatWebP;
            }
    }
    return XRImageFormatUndefined;
}

UIImageView의 성능 병목


위에서 말한 바와 같이, 대부분의 형식의 그림은 비트맵으로 먼저 디코딩된 후에야 UI에 렌더링될 수 있다.
UIImageView는 그림을 표시하고 비슷한 과정을 거칩니다.실제로 파일 시스템에서 UIImageView에 표시될 때까지 다음 단계를 거칩니다.
  • 메모리 버퍼 및 기타 리소스 할당..
  • 디스크에서 코어 버퍼로 데이터를 복사합니다
  • 커널 버퍼에서 사용자 공간으로 데이터를 복사합니다
  • UIImageView를 생성하여 이미지 데이터에 값을 부여합니다
  • 압축된 이미지 데이터를 비트맵 데이터(bitmap)로 디코딩하고 데이터가 바이트 정렬이 없으면 Core Animation은 데이터를 한 부 더 복사하여 바이트 정렬을 진행합니다..
  • CATransaction은 UIImageView layer 트리의 변화를 포착하고 주 루틴 Runloop에서 CATransaction을 제출하여 이미지 렌더링을 시작합니다
  • GPU 처리 비트맵 데이터, 렌더링..
  • UIKIT의 포장성 때문에 개발자에게 직접 보여주지 않습니다.실제로 우리가 [UIImage imageNamed: @ "xx"]를 호출한 후, UIImage에 저장된 것은 디코딩되지 않은 그림이고, [UIImageViewsetImage:image]를 호출한 후, 메인 라인에서 그림의 디코딩 작업을 하고 그림을 UI에 표시합니다. 이때 UIImage에 저장된 것은 디코딩된bitmap 데이터입니다.
    그림의 압축 해제는 CPU 자원을 매우 소모하는 작업이다. 만약에 우리가 대량의 그림을 목록에 보여야 한다면 시스템의 응답 속도를 크게 늦추고 운행 프레임률을 낮출 것이다.이것이 바로 UIImageView의 개성적인 병목이다.

    성능 병목 해결: 강제 디코딩


    만약에 UIImage에 이미 디코딩된 데이터가 저장된다면 속도가 매우 빠르기 때문에 최적화된 사고방식은 바로 하위 라인에서 그림의 원시 데이터를 강제로 디코딩한 다음에 디코딩된 그림을 메인 라인에 던져 계속 사용함으로써 메인 라인의 응답 속도를 높이는 것이다.
    우리가 사용해야 할 도구는 Core Graphics 프레임워크의 CGBitmapContextCreate 방법과 관련된 그리기 함수입니다.전체 단계는 다음과 같습니다.
    A. 크기와 형식을 지정하는 bitmap context를 만듭니다.
    B. 디코딩되지 않은 그림을 이 context에 기록합니다. 이 과정은 *강제 디코딩*을 포함합니다.
    C. 이 context에서 새 UIImage 객체를 만들고 반환합니다.
    다음은 SDWebImage 구현의 핵심 코드입니다. 번호에 대한 해석은 다음과 같습니다.
    
    // 1.
    CGImageRef imageRef = image.CGImage;
    
    // 2.
    CGColorSpaceRef colorspaceRef = [UIImage colorSpaceForImageRef:imageRef];
            
    size_t width = CGImageGetWidth(imageRef);
    size_t height = CGImageGetHeight(imageRef);
    
    // 3.
    size_t bytesPerRow = 4 * width;
    
    // 4.
    CGContextRef context = CGBitmapContextCreate(NULL,
                                                 width,
                                                 height,
                                                 kBitsPerComponent,
                                                 bytesPerRow,
                                                 colorspaceRef,
                                                 kCGBitmapByteOrderDefault|kCGImageAlphaNoneSkipLast);
    if (context == NULL) {
        return image;
    }
            
    // 5.
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    
    // 6.
    CGImageRef newImageRef = CGBitmapContextCreateImage(context);
    
    // 7.
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef
                                            scale:image.scale
                                      orientation:image.imageOrientation];
    
    CGContextRelease(context);
    CGImageRelease(newImageRef);
    
    return newImage;
    
    위의 코드에 대한 확인:
    1. UIImage 객체에서 CGImageRef 참조를 가져옵니다.이 두 가지 구조는 애플이 서로 다른 등급에서 그림에 대한 표현 방식이다. UIImage는 UIKIT에 속하고 UI등급 그림의 추상적이며 그림의 전시에 사용된다.CGImageRef는 QuartzCore의 구조체 지침으로 C 언어로 작성되어 픽셀 비트맵을 만들 수 있으며 저장된 픽셀 비트를 조작하여 그림을 편집할 수 있습니다.이 두 가지 구조는 편리하게 서로 전환할 수 있다.
    
    // CGImageRef   UIImage
    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    UIImage *image = [UIImage imageWithCGImage:imageRef];
     
    // UIImage   CGImageRef
    UIImage *image=[UIImage imageNamed:@"xxx"];
    CGImageRef imageRef=loadImage.CGImage;
    
    2. UIImage의 +colorSpaceForImageRef: 방법을 호출하여 원본 그림의 색 공간 파라미터를 가져옵니다.
    색 공간이란 무엇입니까? 바로 같은 색 수치에 대한 해석 방식입니다. 예를 들어 픽셀의 데이터(FF0000FF)는 RGBA 색 공간에서 빨간색으로 해석되고 BGRA 색 공간에서 파란색으로 해석됩니다.그래서 우리는 이 파라미터를 추출하여 디코딩 전후의 그림 색 공간이 일치하도록 확보해야 한다.
    3. 이미지 디코딩 후 줄마다 필요한 비트 수를 계산하고 두 개의 매개 변수를 곱하여 얻을 수 있다. 줄마다 픽셀 수width와 픽셀 하나를 저장하는 데 필요한 비트 수 4.
    이곳의 4는 사실 모든 그림의 픽셀 형식과 픽셀 조합에 의해 결정된다. 다음 표는 애플 플랫폼이 지원하는 픽셀 조합 방식이다.
    우리가 디코딩한 그림은 기본적으로 kCGImageAlphaNoneSkipLast RGB의 픽셀 조합을 사용합니다. 알파 채널이 없습니다. 픽셀당 32비트 4바이트입니다. 앞의 세 바이트는 빨간색과 녹색, 파란색 세 개의 채널을 대표하고 마지막 바이트 폐기는 설명되지 않습니다.
    4. 가장 관건적인 함수: CGBitmapContextCreate () 방법을 호출하여 공백 그림 그리기 상하문을 생성합니다. 우리는 상술한 매개 변수를 전송하여 그림의 크기, 색 공간, 픽셀 배열 등 속성을 지정했습니다.
    5. CGContextDrawImage() 메서드를 호출하여 디코딩되지 않은 imageRef 포인터 내용을 우리가 만든 상하문에 기록합니다. 이 절차는 은밀한 디코딩 작업을 완성했습니다.
    6. context 상하문에서 새로운 imageRef를 만듭니다. 이것은 디코딩된 그림입니다.
    7. imageRef에서 UI층에 사용할 UIImage 대상을 생성하고 그림의 scale와orientation 두 파라미터를 지정합니다.
    scale는 그림이 렌더링될 때 압축되어야 하는 배수를 가리킨다. 왜 이 파라미터가 존재하는가. 애플은 패키지 부피를 절약하기 위해 개발자가 같은 사진에 다른 해상도 버전을 업로드할 수 있도록 하기 때문이다. 즉, 우리가 익숙한 @2x, @3x 접두사 그림이다.서로 다른 화면 소질의 장치는 대응하는 자원을 얻을 수 있다.그림을 그릴 때 통일되기 위해 이 그림들은 set 자체의 scale 속성에 의해 설정됩니다. 예를 들어 @2x 그림, scale 값은 2입니다. 1x 그림의 그림 그리기 폭과 같지만 실제 길이는width*scale입니다.
    orientation은 그림의 회전 속성을 잘 이해합니다. 장치에 그림의 기본 방향으로 렌더링하는지 알려 줍니다.
    이상의 절차를 통해 우리는 하위 라인에서 그림에 대해 강제 코드를 바꾸어 주 라인에 되돌려 사용함으로써 그림의 렌더링 효율을 크게 향상시켰다.이것은 현재 주류 앱과 대량의 삼자 라이브러리의 가장 좋은 실천이기도 하다.

    총결산


    이 문서의 내용을 요약합니다.
  • 사진은 컴퓨터 세계에서 서로 다른 봉인 형식에 따라 압축되어 저장과 전송을 편리하게 한다
  • .핸드폰은 메인 라인에서 압축된 이미지를 렌더링할 수 있는 비트맵 형식으로 압축합니다. 이 과정은 대량의 자원을 소모하고 앱 성능에 영향을 줍니다
  • 우리는 Core Graphics의 그리기 방법을 사용하여 하위 라인에서 UIImage에 대해 먼저 디코딩 작업을 하도록 강요하고 주 라인의 부담을 줄여 앱의 응답 속도를 향상시킨다
  • UIImageView와 유사하게 UIKIT는 많은 기술적 디테일을 숨기고 개발자의 학습 문턱을 낮추지만 다른 한편으로는 우리가 일부 밑바닥 기술에 대한 탐구를 제한한다.글에서 언급한 강제 디코딩 방법은 사실 CGBitmapContextCreate 방법의 하나인'부작용'으로 비교hack방식에 속한다. 이것은 iOS 플랫폼의 한계이기도 하다. 애플이 너무 폐쇄적이다.
    사용자는 소프트웨어 성능(스프레임율, 응답 속도, 플래시율 등)에 대해 사실 매우 민감하다. 개발자로서 성능 병목 뒤의 원리를 끊임없이 탐구하고 해결을 시도해야 한다. 이동단 개발의 성능 최적화는 끝이 없다.
    이상은 iOS에서의 이미지 처리와 성능 최적화에 대한 상세한 내용입니다. iOS 이미지 처리와 성능 최적화에 대한 더 많은 자료는 저희 다른 관련 글을 주목해 주십시오!

    좋은 웹페이지 즐겨찾기