iOS開發中用imageIO漸進載入圖片及擷取exif的方法_IOS

來源:互聯網
上載者:User

imageIO完成漸進載入圖片

一、常見漸進載入圖片模式
 
  目前我們看到的漸進載入主要有以下三種實現方式:
 
  1)  依次從web上載入不同尺寸的圖片,從小到大。最開始先拉取一個小縮圖做展開顯示,然後拉取中等規格的圖,拉取完畢直接覆蓋顯示,最後拉取原圖,拉取完成後顯示原圖。
 
  2)直接從web上拉取最大的圖片,每接受一點兒資料就顯示一點兒圖片,這樣就會實現從上到下一點點重新整理出來的效果。
 
  3)結合第1種和第2種,先拉取一個縮圖做展開顯示,然後採用第二種方法直接拉取原圖,這樣即可以實現漸進載入,也可以節省幾次中間的網路請求。
 
 
 
二、通過imageIO實現圖片的漸進載入
 
  imageIO的guide中原話是這麼說的: "If you have a very large image, or are loading image data over the web, you may want to create an incremental image source so that you can draw the image data as you accumulate it. "
 
  翻譯過來就是: "如果你想載入一副特別大的圖片,或者從網路上載入一副圖片,你可以通過建立一個imageSource實現漸進載入的效果。"翻譯的不是很地道,大概就是這麼個意思,以前在做PowerCam的時候,當時為了在iOS上處理超大圖的時候也試過這種方法,當時測試使用的是一副中國地圖,解析度為10000*8000的,結果是當整幅圖片載入到記憶體時,記憶體吃不消,於是就放棄了。現在想想對於這種超大圖片的處理,我們可以採用分區的方式進行,每次只需要處理一小塊圖片即可,這個問題就留給大家思考吧。
 
  今天我們要討論的是CGImageSource實現從web端漸進載入圖片,要達到這個目的我們需要建立一個URLConnnection,然後實現代理,每次接收到資料時更新圖片即可。下面主要的實現源碼:
 

複製代碼 代碼如下:

//
//  SvIncrementallyImage.m
//  SvIncrementallyImage
//
//  Created by  maple on 6/27/13.
//  Copyright (c) 2013 maple. All rights reserved.
//

#import "SvIncrementallyImage.h"
#import <ImageIO/ImageIO.h>
#import <CoreFoundation/CoreFoundation.h>

@interface SvIncrementallyImage () {
    NSURLRequest    *_request;
    NSURLConnection *_conn;
   
    CGImageSourceRef _incrementallyImgSource;
   
    NSMutableData   *_recieveData;
    long long       _expectedLeght;
    bool            _isLoadFinished;
}

@property (nonatomic, retain) UIImage *image;
@property (nonatomic, retain) UIImage *thumbImage;

@end

@implementation SvIncrementallyImage

@synthesize imageURL = _imageURL;
@synthesize image    = _image;
@synthesize thumbImage = _thumbImage;

- (id)initWithURL:(NSURL *)imageURL
{
    self = [super init];
    if (self) {
        _imageURL = [imageURL retain];
       
        _request = [[NSURLRequest alloc] initWithURL:_imageURL];
        _conn    = [[NSURLConnection alloc] initWithRequest:_request delegate:self];
       
        _incrementallyImgSource = CGImageSourceCreateIncremental(NULL);
       
        _recieveData = [[NSMutableData alloc] init];
        _isLoadFinished = false;
    }
   
    return self;
}

#pragma mark -
#pragma mark NSURLConnectionDataDelegate

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    _expectedLeght = response.expectedContentLength;
    NSLog(@"expected Length: %lld", _expectedLeght);
   
    NSString *mimeType = response.MIMEType;
    NSLog(@"MIME TYPE %@", mimeType);
   
    NSArray *arr = [mimeType componentsSeparatedByString:@"/"];
    if (arr.count < 1 || ![[arr objectAtIndex:0] isEqual:@"image"]) {
        NSLog(@"not a image url");
        [connection cancel];
        [_conn release]; _conn = nil;
    }
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"Connection %@ error, error info: %@", connection, error);
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Connection Loading Finished!!!");
   
    // if download image data not complete, create final image
    if (!_isLoadFinished) {
        CGImageSourceUpdateData(_incrementallyImgSource, (CFDataRef)_recieveData, _isLoadFinished);
        CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_incrementallyImgSource, 0, NULL);
        self.image = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    [_recieveData appendData:data];
   
    _isLoadFinished = false;
    if (_expectedLeght == _recieveData.length) {
        _isLoadFinished = true;
    }
   
    CGImageSourceUpdateData(_incrementallyImgSource, (CFDataRef)_recieveData, _isLoadFinished);
    CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_incrementallyImgSource, 0, NULL);
    self.image = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
}

@end


  從上面代碼中我們可以看到,一開始我們根據傳入的URL建立一個URLConnection,同時建立一個空的CGImageSource,然後在每次收到資料的時候調用CGImageSourceUpdateData更新imageSource的資料,接著調用CGImageSourceCreateImageAtIndex擷取最新的圖片即可。
 
  怎麼樣,看到上面的實現是不是感覺實現從web上漸進載入圖片很簡單,雖然imageIO幫我們做了很多事情,但是我們也應該瞭解它的原理。我們知道檔案都是有格式的,一般檔案的頭部會記錄一些關於檔案格式的資料,後面就是實際的檔案資料。
 
  拿最簡單的BMP圖片檔案舉例:
 
  1)  最開始的BITMAPFILEHEADER,這部分主要記錄檔案的大小,以及實際的映像資料離檔案頭的距離。
 
  2)  接著是BITMAPINFOHEADER,這部分主要記錄圖片的寬,高,位深等資訊
 
  3)可選的調色盤資訊
 
  4)最後一部分就是實際的圖片資料。
 
  前三部分的資訊很小,一般加起來不會超過100個位元組,擷取到這寫資訊以後,我們就可以很輕鬆的根據後面的資料構建出圖片,當資料擷取的越來越完整的時候,我們構造出的圖片就會越完整,直至全部載入完成。
 
  BMP格式是簡單的圖片格式,其他的JPG,PNG雖然結果更加複雜,但是總體構成都差不多。imageIO正是協助我們完成了眾多圖片格式的編解碼,然後一步步構造出最終的圖片。


使用imageIO擷取圖片的exif資訊
一幅圖片除了包含我們能看見的像素資訊,背後還包含了拍攝時間,光圈大小,曝光等資訊。UIImage類將這些細節資訊都隱藏了起來,只提供我們關心的圖片尺寸,圖片方向等。我們可以通過imageIO架構擷取到圖片背後的所有資訊,下面就讓我們一起看看。
 
  imageIO架構是iOS中偏底層一點兒的架構,它內部提供的介面都是C風格的,關鍵資料也都是使用CoreFoundation進行儲存。慶幸的是CoreFoundation中有很多資料類型都可以上層的資料Foundation架構中的資料類型進行無縫橋接。這也就大大方便了我們對圖片資訊的操作。
 
  CGImageSourceRef是整個imageIO的入口,通過它我們可以完成從檔案的載入圖片。載入完成以後我們就得到一個CGImageSourceRef,通過CGImageSourceRef我們就可以擷取圖片檔案的大小,UTI(uniform type identifier),內部包含幾張圖片,訪問每一張圖片以及擷取每張圖片對應的exif資訊等。
 
  你可能會有一個疑問,為什麼會有幾張圖片呢?
 
  這塊兒我解釋一下,imageSourceRef和檔案是一一對應的,通常我們見到的圖片檔案(例如jpg,png)內部都只有一張圖片,這種情況我們通過CGImageSourceGetCount方法得到的就會是1。但是不能排除一個圖片檔案中會有多種圖片的情況,例如gif檔案,這個時候一個檔案中就可能包含幾張甚至幾十張圖片。前面我寫的一片部落格《IOS中如何解析並顯示Gif》就是通過imageSource實現載入和解析gif的功能。
 
  下面是系統相機拍的照片的exif資訊:

複製代碼 代碼如下:

image property: {
    ColorModel = RGB;
    DPIHeight = 72;
    DPIWidth = 72;
    Depth = 8;
    Orientation = 6;
    PixelHeight = 2448;
    PixelWidth = 3264;
    "{Exif}" =     {
        ApertureValue = "2.526069";
        BrightnessValue = "-0.5140446";
        ColorSpace = 1;
        ComponentsConfiguration =         (
            1,
            2,
            3,
            0
        );
        DateTimeDigitized = "2013:06:24 22:11:30";
        DateTimeOriginal = "2013:06:24 22:11:30";
        ExifVersion =         (
            2,
            2,
            1
        );
        ExposureMode = 0;
        ExposureProgram = 2;
        ExposureTime = "0.06666667";
        FNumber = "2.4";
        Flash = 16;
        FlashPixVersion =         (
            1,
            0
        );
        FocalLenIn35mmFilm = 33;
        FocalLength = "4.13";
        ISOSpeedRatings =         (
            400
        );
        MeteringMode = 3;
        PixelXDimension = 3264;
        PixelYDimension = 2448;
        SceneCaptureType = 0;
        SensingMethod = 2;
        ShutterSpeedValue = "3.906905";
        SubjectArea =         (
            2815,
            1187,
            610,
            612
        );
        WhiteBalance = 0;
    };
    "{GPS}" =     {
        Altitude = "27.77328";
        AltitudeRef = 0;
        Latitude = "22.5645";
        LatitudeRef = N;
        Longitude = "113.8886666666667";
        LongitudeRef = E;
        TimeStamp = "14:11:23.36";
    };
    "{TIFF}" =     {
        DateTime = "2013:06:24 22:11:30";
        Make = Apple;
        Model = "iPhone 5";
        Orientation = 6;
        ResolutionUnit = 2;
        Software = "6.1.4";
        XResolution = 72;
        YResolution = 72;
        "_YCbCrPositioning" = 1;
    };
}

  從中我們可以看出最開始的幾項分別顯示了當前圖片的顏色模式,色深,x,y方向的DPI,實際像素以及圖片的方向。我最開始看到這個方向時,心中一喜這不是UIImage中的imageOrientation,但是實驗發現這個方向和UIImage中的imageOrientation並不相等,此處的方向是exif標準定義的方向,從1到8分別對應這UIImage中的8個方向,只是順序不一樣,它們對應關係如下:
 

複製代碼 代碼如下:

enum {
    exifOrientationUp = 1,      // UIImageOrientationUp
    exifOrientationDown = 3,    // UIImageOrientationDown
    exifOrientationLeft = 6,    // UIImageOrientationLeft
    exifOrientationRight = 8,   // UIImageOrientationRight
   
    // these four exifOrientation does not support by all camera, but IOS support these orientation
    exifOrientationUpMirrored = 2,          // UIImageOrientationUpMirrored
    exifOrientationDownMirrored = 4,        // UIImageOrientationDownMirrored
    exifOrientationLeftMirrored = 5,        // UIImageOrientationLeftMirrored
    exifOrientationRightMirrored = 7,       // UIImageOrientationRightMirrored
};
typedef NSInteger ExifOrientation;

  目前市面上的大部分數位相機和手機都會內建一個方向感應器,拍出的照片中會寫如方向資訊,但是通常都只會有前四種方向。這幾種Mirrored方向通常都是手機自拍自拍的時候才會設定。
 
  exif為什麼要搞這麼一個方向呢?
 
  幾乎所有的網路攝影機在出場的時候成相晶片都是有方向的,拍出來的照片的像素都是預設方向的。如果每拍一張照片就對這些像素進行旋轉,如果數位相機每秒連拍20張來算,旋轉操作將會非常耗時。更聰明的做法是拍照時只記錄一個方向,然後顯示的時候按方向顯示出來即可。因此exif定義了一個標準的方向參數,只要讀圖的軟體都來遵守規則,載入時候讀取圖片方向,然後做相應的旋轉即可。這樣既可以達到快速成像的目的,又能達到正確的顯示,何樂而不為呢。
 
  常見的圖片瀏覽和編輯軟體都遵守這個規則,但是有一個我們最常用的看圖軟體(windows內建的看圖程式)不會去讀這個方向,因此我們將數位相機和手機拍出來的圖片匯入windows上時,會經常遇到方向錯誤的問題。不知道windows帝國是怎麼想的,或許和定義exif的組織有什麼過節吧。
 
  圖片資訊中除了上面看提到的那些,還有拍攝的GPS資訊,iOS內建的相簿軟體中的地點tab就是按照GPS資訊實現的。還有很多其他的資訊,感興趣的可以自己寫個程式研究研究,這裡就不展開了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.