图像处理框架Core Image

1.CoreImage的介绍

CoreImage是iOS5中新加入的一个Objective-C的框架,提供了强大高效的图像处理功能,用来对基于像素的图像进行操作与分析。iOS提供了很多强大的滤镜(Filter),其中iOS5中有48种,而到了最新的iOS6 Filter已经增加到了93种之多,并且这一数字会继续增加。目前我知道的有180种。这些Filter提供了各种各样的效果,并且还可以通过滤镜链将各种效果的Filter叠加起来,形成强大的自定义效果,如果你对该效果很满意,还可以子类化滤镜。

2.CoreImage框架中的对象

2.1 CIImage

CIImage是CoreImage框架中最基本代表图像的对象,他不仅包含元图像数据,还包含作用在原图像上的滤镜链。这里我想特别强调的是CIImage和其他图像是不同的,在CIImage被CIContext渲染出来之前,他是依赖于滤镜链的,滤镜是不会更改CIImage中的图像数据。这个需要正确理解,不然会给你的程序造成错误。说到了CIImage的不同,就必须得提一下如何创建CIImage了,CIImage是不能直接有UIImage转化而来的,有以下几种创建CIImage的类方法:

1.CIImage*image=[CIImage imageWithContentsOfURL:myURL];  
2.CIImage*image=[CIImage imageWithData:myData];  
3.CIImage*image=[CIImage imageWithCGImage:myCgimage];  
4.CIImage*image=[CIImage imageWithCVPixelBuffer:CVBuffer];  

2.2 CIFilter

CIFilter用来表示CoreImage提供的各种滤镜。滤镜使用键-值来设置输入值,一旦这些值设置好,CIFilter就可以用来生成新的CIImage输出图像了。记住,这里的输出的图像不会进行实际的图像渲染,他只包含一个对输入图像的引用以及需要应用与数据上的滤镜链。iOS永远在最佳的时间选择渲染图像。

CIFilter提供了一个简单的方法查询可用的滤镜种类:[CIFilter filterNamesInCategory:kCICategoryBuiltIn];//搜索属于类别为kCICategoryBuiltIn的所有滤镜名字,返回一个数组;
[CIFilter filterNamesInCategories];//搜索所有可用的滤镜名称;
调用[CIFilter attributes]会返回filter详细信息,下面我们以一个具体列子来看看他返回的信息。
下面是我程序返回的一个叫做CISepiaTone滤镜返回的详细信息:

CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"];
NSDictionary
  
  
   
    *attributes = [filter attributes];
  
  

{
    "CIAttributeFilterAvailable_Mac" = "10.4" //滤镜在Mac使用的最低版本
    "CIAttributeFilterAvailable_iOS" = 5; //滤镜在iOS使用的最低版本
    CIAttributeFilterCategories =     (//滤镜所示种类,通常一个滤镜可以属于几种
        CICategoryColorEffect,
        CICategoryVideo,
        CICategoryInterlaced,
        CICategoryNonSquarePixels,
        CICategoryStillImage,
        CICategoryBuiltIn,
        CICategoryXMPSerializable
    );
    CIAttributeFilterDisplayName = "Sepia Tone"
    CIAttributeFilterName = CISepiaTone; //滤镜的名称
    CIAttributeReferenceDocumentation = "http://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CoreImageFilterReference/index.html#//apple_ref/doc/filter/ci/CISepiaTone" 滤镜的官方文档
    inputImage =     { //使用滤镜需要输入的参数
        CIAttributeClass = CIImage; // 类型为CIImage
        CIAttributeDescription = "The image to use as an input image. For filters that also use a background image, this is the foreground image."
        CIAttributeDisplayName = Image;
        CIAttributeType = CIAttributeTypeImage;
    };
    inputIntensity =     { // 输入强度
        CIAttributeClass = NSNumber; // 类型
        CIAttributeDefault = 1;  // 默认值
        CIAttributeDescription = "The intensity of the sepia effect. A value of 1.0 creates a monochrome sepia image. A value of 0.0 has no effect on the image.";
        CIAttributeDisplayName = Intensity;
        CIAttributeIdentity = 0;
        CIAttributeMin = 0;
        CIAttributeSliderMax = 1;
        CIAttributeSliderMin = 0;
        CIAttributeType = CIAttributeTypeScalar;
    };
}

2.3 CIContext

CIContext用来渲染CIImage,将作用在CIImage上的滤镜链应用到原始的图片数据中。CIContext可以是基于CPU的,也可以是基于GPU的,这两种渲染的区别是:使用CPU渲染的IOS会采用GCD来对图像进行渲染,这保证了CPU渲染在大部分情况下更可靠,比CPU渲染更容易使用,他可以在后台实现渲染过程;而GPU渲染方式使用OpenGL ES2.0来渲染图像,这种方式CPU完全没有负担,应用程序的运行循环不会受到图像渲染的影响,而且他渲染比CPU渲染更快但是GPU渲染无法在后台运行。

对于如何选择更好的渲染方式,我认为应该视具体情况而定:对于复杂的图像滤镜使用GPU更好,但是如果在处理视频并保存文件,或保存照片到照片库中时为避免程序退出对图片保存造成影响,这时应该使用CPU进行渲染。默认情况是用CPU渲染的。

CIContext *context = [CIContext contextWithOptions:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:kCIContextUseSoftwareRenderer]];//CPU渲染  

渲染后的图片使用:

在imageView中使用:

// Create the CIContext to render into  
  CIContext *context = [CIContext context];  
// Get outputImage from the last filter in chain  
  CIImage *ciimage = [filter outputImage];  
// Render the CIImage into a CGImageRef  
  CGImageRef cgimg = [context createCGImage:ciimage fromRect:[ciimage extent]];  
// Create a UIImage from the CGImageRef  
UIImage *image = [UIImage imageWithCGImage:cgimg scale:1.0f  
orientation:ui_orientation([ciimage properties])];  
CGImageRelease(cgimg);  
// Use the UIImage in an UIImageView  
imageView.image = image;  

2.4 CIDetector和CIFeature

CIDetector用来分析CIImage,得到CIFeature。每个CIDetector都要用一个探测器来初始化,这个类型高数探测器要在图像中寻找什么特征。

当一个CIDetector分析一张图片时,返回一个探测到的CIFeature的数组,如果CIDetector 被初始化为寻找面孔,那么返回的数组会被填上CIFaceFeature对象,每个CIFaceFeature都包含一个面部的CGrect引用(按照图像的坐标系),以及检测到的面孔的左眼,右眼,嘴部位置的CGPoint;

CIDetector *faceDetector = [CIDetector   
                                    detectorOfType:CIDetectorTypeFace  
                                    context:self.imageContext   
                                    options:options];                
NSArray *faces = [faceDetector featuresInImage:coreImage  
                                               options:nil];  
for(CIFaceFeature *face in faces){  
            coreImage = [CIFilter filterWithName:@"CISourceOverCompositing"  
                                   keysAndValues:kCIInputImageKey, [self makeBoxForFace:face],  
                         kCIInputBackgroundImageKey, coreImage, nil].outputImage;  
        }

3 注意事项

1 CoreImage在IOS上有很高的效率,但是滤镜和渲染操作也会对主线程造成影响。应该将CoreImage滤镜渲染操作放在后台线程执行,当这些操作介绍后在返回主线程进行界面的更新。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void){  
           //CGImageRef cgImage = [self autoAdjustImage];  
           NSArray *filters;  
           // Create Core Image  
           CGImageRef cgImg = self.imageView.image.CGImage;  
           CIImage *coreImage = [CIImage imageWithCGImage:cgImg];  
           // Iterate through all of our filters and apply  
           // them to the CIImage  
           for(CIFilter *filter in filters){  
               [filter setValue:coreImage forKey:kCIInputImageKey];  
               coreImage = filter.outputImage;  
           }  
             
           // Create a new CGImageRef by rendering through CIContext  
           // This won't slow down main thread since we're in a background  
           // dispatch queue  
           CGImageRef newImg = [self.imageContext createCGImage:coreImage   
                                                       fromRect:[coreImage extent]];  
             
           dispatch_async(dispatch_get_main_queue(), ^(void){  
               // Update our image view on the main thread  
               // You can also perform any other UI updates needed  
               // here such as hidding activity spinners  
               self.imageView.image = [UIImage imageWithCGImage:newImg];    
           });  
       });

上面这段代码,就是为了防止阻塞主线程,用GCD异步执行滤镜与渲染操作,在获取渲染后的照片以后,返回主线程进行界面的更新。

2 不要重复应用滤镜,即使是同一个滤镜也不要应用两次,因为滤镜后输出照片包含滤镜链,在进行照片渲染是会将滤镜链效果叠加到原始数据上,这时会造成问题。比如,有一个CIImage,上面配置了强度为0.5的棕色滤镜,现在通过滑块将强度改为0.6,这个滤镜应该用在新的CIImage上,如果不是新的CIImage上,那么原来的CIImage中将包含强度为0.5和0.6的棕色滤镜,而我们只想0.6的棕色滤镜,这样就造成错误,这一点在编写程序的时候一定要切忌。

3 app中应用的滤镜太多,改变速率太快,如果是根据滑块来产生事件的话,一定要注意在使用滑条值前要首先判断更改的滤镜当前是否正在起作用,如果该滤镜正在生成新的渲染图片,则应该这次滑块的更新。这一点也是很重要的,弄的不好常常导致程序崩溃,出现内存泄露问题。

这些问题常常会导致程序的崩溃.

4 总结

CoreImage处理图像的流程:

1:创建一个新的CIImage;

2:创建一个行的CIFIlter,并通过键-值设置各种输入值,这些值有些是有默认值的,有些没有默认值,需要编程者的设置;

3:冲CIFilter中生成输出图像,如果存在滤镜链则将输出图像作为输入参数传入到下一个滤镜,跳回步骤2继续进行,如果到达滤镜末,则调用CIContext渲染CIImage对象。这个context可以是基于CPU或GPU的,基于CPU的产出CGImageRef对象,基于GPU的调用OpenGL ES在屏幕上画出结果,默认是基于CPU的。

在使用CoreImage时,一定要记住CIImage对象在开始时不会操作图像数据,知道使用CIContext渲染图片是才会这么做。还要记住最好在后台执行图像处理的操作,然后在主线程中修改界面。

参考资料:
官方文档
Xamarin文档

其他博客:
CoreImage

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值