我的编程空间,编程开发者的网络收藏夹
学习永远不晚

iOS开发中用imageIO渐进加载图片及获取exif的方法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

iOS开发中用imageIO渐进加载图片及获取exif的方法

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信息实现的。还有很多其他的信息,感兴趣的可以自己写个程序研究研究,这里就不展开了。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

iOS开发中用imageIO渐进加载图片及获取exif的方法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

iOS开发中用imageIO渐进加载图片及获取exif的方法

imageIO完成渐进加载图片 一、常见渐进加载图片模式 目前我们看到的渐进加载主要有以下三种实现方式: 1) 依次从web上加载不同尺寸的图片,从小到大。最开始先拉取一个小缩略图做拉伸显示,然后拉取中等规格的图,拉取完毕直接覆盖显示
2022-05-16

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录