专栏首页哈雷彗星撞地球iOS 中的CIFilter(基础用法)

iOS 中的CIFilter(基础用法)

本文大部分内容均来自:Core Image Tutorial: Getting Started Core Image 是一个很强大的库,PS图片时用到的各种滤镜就是在这个库中。而我们创建二维码、创建条形码用这里的滤镜,只需要短短几行代码就可以撸出来(后面会讲怎么用CIFilter绘制二维码、条形码)。 文中有提到在iOS 8 上,CIFilter 的API 里有126种滤镜可用,在 同时期 Mac OS 上有160多种滤镜可用;而在iOS 9.3 上,我测试可以使用的滤镜已经达到174种,Mac OS上肯定更多咯。

如何知道有哪些滤镜效果呢?

我一度想查找API里一共提供了多少种滤镜,每种滤镜分别有什么效果。可能是种类实在是太多,不同的滤镜又有很多不同的参数(参数名,参数各种都可能不同)设置,基本没有介绍每种滤镜的文章。 下面提供获取每种滤镜名称以及其属性的方法:

// swift 版
let properties = CIFilter.filterNamesInCategory(kCICategoryBuiltIn) 
println(properties)
for fileterName : String in properties {
    let filter = CIFilter(name: fileterName)
  // 滤镜的参数
    print(filter?.attributes)
}

// Objective-C版 (因转换成OC版的太简单,略?)

准备工作

在iOS 中使用滤镜效果,需要用到的重要类有三个:

  • CIContext. 图片的所有处理工作都是在 CIContext中做的. 它有点类似于 Core Graphics 和 OpenGL context.
  • CIImage. 这个类持有图片数据。可以用UIImage或者图片路径或者data来创建一个CIImage对象。
  • CIFilter.滤镜类,它有一个用来设置各种参数的字典,API已经提供了setValue: forKey:方法来设置参数。

基础用法

对一张图使用一个滤镜效果,总结起来需要四步:

  1. 创建一个CIImage对象 .CImage 有很多初始化方法。譬如:CIImage(contentsOfURL:), CIImage(data:), CIImage(CGImage:), CIImage(bitmapData:bytesPerRow:size:format:colorSpace:),用的最多的是CIImage(contentsOfURL:)。
  2. 创建一个CIContext. CIContext 可能是基于CPU,也可能是基于GPU的。所以创建CIContext会消耗资源,影响性能,我们应该尽可能多的复用它。将处理过后的图片数据,输出为CIImage的时候会用到CIContext。
  3. 创建一个滤镜. 创建好滤镜后,我们需要为其设置参数。有的滤镜要设置的参数比较多,有的滤镜却不需要设置参数。
  4. 获取filter output. 滤镜会输出一个CIImage对象,用CIContext 可以将CIImage转换为UIImage。 这里有一段示例代码:
// 1.获取本地图片路径
let fileURL = NSBundle.mainBundle().URLForResource("image", withExtension: "png") 
// 2.创建CIImage对象
let beginImage = CIImage(contentsOfURL: fileURL!) 
// 3. 创建滤镜
// 创建一个棕榈色滤镜
let filter = CIFilter(name: "CISepiaTone")!
filter.setValue(beginImage, forKey: kCIInputImageKey)
// 设置输入的强度系数
filter.setValue(0.5, forKey: kCIInputIntensityKey)
// 4.将CIImage转换为UIImage 
//  其实在这个API内部用到了CIContext,而它就是在每次使用的使用去创建一个新的CIContext,比较影响性能
let newImage = UIImage(CIImage: filter.outputImage!)
self.imageView.image = newImage

注意点 因为let newImage = UIImage(CIImage: filter.outputImage)内部会每次都创建一个CIContext,这里我们可以优化。用上面的方式创建的UIImage ,我们将其转换为NSData的时候,NSData为nil,原因是:May return nil if image has no CGImageRef or invalid bitmap format,这表明我们更应该优化。 将上面的第四步替换成如下代码:

        // 1 创建一个CIContext,只需要将这个context对象存起来,其他地方调用即可。
        let context = CIContext(options:nil)
        // 2 用CIContext将CIImage转换为CGImage
        let cgimg = context.createCGImage(filter.outputImage!, fromRect: filter.outputImage!.extent)
        // 3 将CGImage转换为UIImage
        let newImage = UIImage(CGImage: cgimg)
        self.imageView.image = newImage

将滤镜处理后的图片保存进相册

以前我们可能会用 UIImageWriteToSavedPhotosAlbum()。 ALAssetsLibrary 提供了将CGImage直接保存到相册的示例方法:writeImageToSavedPhotosAlbum,只可惜它到iOS 9.0 就弃用了☹️,当工程的最低兼容版本大于9.0时,编译器会给你一个警告,告诉你该用什么方法替换。

@IBAction func savePhoto(sender: UIButton) {
        // 1 获取滤镜输出的图片
        let imageToSave = filter.outputImage!
        // 2 创建一个使用CPU渲染器的CIContext
        let softwareContext = CIContext(options: [kCIContextUseSoftwareRenderer : true])
        // 3 将CIImage转换为CGImage
        let cgimage = softwareContext.createCGImage(imageToSave, fromRect: imageToSave.extent)
        // 4 使用 ALAssetsLibrary 保存到相册
        let library = ALAssetsLibrary()
        library.writeImageToSavedPhotosAlbum(cgimage, metadata: imageToSave.properties, completionBlock: nil) 
    }

组合滤镜

我们可以将多种滤镜效果组合起来,创建一个新的滤镜效果,这比将一个个的滤镜加到图片上,在输出要有效率的多。 这里有一个示例:

    func oldPhoto(img: CIImage, withAmount intensity: Float) -> CIImage {
        
        // 1 创建一个棕色滤镜
        let sepiaFilter = CIFilter(name: "CISepiaTone")
        sepiaFilter?.setValue(img, forKey: kCIInputImageKey)
        sepiaFilter?.setValue(intensity, forKey: kCIInputIntensityKey)
        
        // 2 创建一个随机点滤镜
        let randomFilter = CIFilter(name: "CIRandomGenerator")
        
        // 3
        let lighten = CIFilter(name: "CIColorControls")
        lighten?.setValue(randomFilter?.outputImage, forKey: kCIInputImageKey)
        lighten?.setValue(1 - intensity, forKey: "inputBrightness")
        lighten?.setValue(0, forKey: "inputSaturation")
        
        // 4 将滤镜输出裁剪成原始图片大小
        let croppedImage = lighten?.outputImage?.imageByCroppingToRect(beginImage.extent)
        
        // 5
        let composite = CIFilter(name: "CIHardLightBlendMode")
        composite?.setValue(sepiaFilter?.outputImage, forKey: kCIInputImageKey)
        composite?.setValue(croppedImage, forKey: kCIInputBackgroundImageKey)
        
        // 6
        let vignette = CIFilter(name: "CIVignette")
        vignette?.setValue(composite?.outputImage, forKey: kCIInputImageKey)
        vignette?.setValue(intensity * 2, forKey: "inputIntensity")
        vignette?.setValue(intensity * 30, forKey: "inputRadius")
        
        // 7
        return (vignette?.outputImage)!
    }

别人的图

代码效果图

为毛自己家的效果图是这个鬼样子,别人家的效果图那么好看!? ?

用滤镜效果创建二维码、条形码

** 创建条形码 **

+ (UIImage *)barCodeImageWithInfo:(NSString *)info
{
    // 创建条形码
    CIFilter *filter = [CIFilter filterWithName:@"CICode128BarcodeGenerator"];
    
    // 恢复滤镜的默认属性
    [filter setDefaults];
    // 将字符串转换成NSData
    NSData *data = [info dataUsingEncoding:NSUTF8StringEncoding];
    // 通过KVO设置滤镜inputMessage数据
    [filter setValue:data forKey:@"inputMessage"];
    // 获得滤镜输出的图像
    CIImage *outputImage = [filter outputImage];
    // 将CIImage 转换为UIImage
    UIImage *image = [UIImage imageWithCIImage:outputImage];
    
    // 如果需要将image转NSData保存,则得用下面的方式先转换为CGImage,否则NSData 会为nil
    //    CIContext *context = [CIContext contextWithOptions:nil];
    //    CGImageRef imageRef = [context createCGImage:outputImage fromRect:outputImage.extent];
    //
    //    UIImage *image = [UIImage imageWithCGImage:imageRef];
    
    return image;
}

条形码

创建二维码

+ (UIImage *)qrCodeImageWithInfo:(NSString *)info  width:(CGFloat)width
{
    if (!info) {
        return nil;
    }
    
    NSData *strData = [info dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
    //创建二维码滤镜
    CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    [qrFilter setValue:strData forKey:@"inputMessage"];
    [qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"];
    CIImage *qrImage = qrFilter.outputImage;
    //颜色滤镜
    CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"];
    [colorFilter setDefaults];
    [colorFilter setValue:qrImage forKey:kCIInputImageKey];
    [colorFilter setValue:[CIColor colorWithRed:0 green:0 blue:0] forKey:@"inputColor0"];
    
![Uploading 1A4978EE-427F-4804-B536-1D5C330A0578_306160.png . . .][colorFilter setValue:[CIColor colorWithRed:1 green:1 blue:1] forKey:@"inputColor1"];
    CIImage *colorImage = colorFilter.outputImage;
    //返回二维码
    CGFloat scale = width/31;
    UIImage *codeImage = [UIImage imageWithCIImage:[colorImage imageByApplyingTransform:CGAffineTransformMakeScale(scale, scale)]];
    return codeImage;
}

二维码

CIFilter初体验就先到这里了,Have Fun!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iOS下WebRTC音视频通话(一)WebRTC介绍WebRTC 过程

    在iOS下做IM功能时,难免都会涉及到音频通话和视频通话。QQ中的QQ电话和视频通话效果就非常好,但是如果你没有非常深厚的技术,也没有那么大的团队,很难做到QQ...

    Haley_Wong
  • Mac 升级至10.11后 CocoaPods不能用?现象解决方式

    一直不想升级到10.11,最近为了安装Xcode 7.3升级系统后,CocoaPods不能用了。 首先报的错误是:

    Haley_Wong
  • iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge

    WebViewJavascriptBridge是一个有点年代的JS与OC交互的库,使用该库的著名应用还挺多的,目前这个库有7000+star。我去翻看了它的第一...

    Haley_Wong
  • 日迹中视频编辑滤镜效果实现方法

    导语 本文简要分析,日迹视频解码流程以及视频滤镜的实现原理 需求背景:日迹需要的编辑滤镜效果预览图 图1:日迹滤镜效果 要实现产品想要的...

    MelonTeam
  • Qt编写自定义控件52-颜色下拉框

    这个控件写了很久了,元老级别的控件之一,开发之初主要是自己的好几个项目要用到,比如提供一个颜色下拉框设置对应的曲线或者时间颜色,视频监控项目中经常用到的OSD标...

    feiyangqingyun
  • Qt编写自定义控件53-自定义宽高下拉框

    默认的qcombobox控件,如果元素item中的内容过长超过控件本身的宽度的话,会自动切掉变成省略号显示,有些应用场景不希望是省略号显示,希望有多长就显示多长...

    feiyangqingyun
  • 彻底搞懂 etcd 系列文章(五):etcdctl 的使用

    etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管。etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key...

    aoho求索
  • 响铃:百度Q2财报再创新高,恰是踩对了内容赛道的拐点

    又涨了!近日百度2018Q2财报发布,营收额达260亿元人民币,在Q1总营收209亿元的基础上继续增长。而这亮眼的成绩离不开其核心业务“搜索+信息流“所做出的贡...

    曾响铃
  • 组合与自绘,我该选用何种方式自定义Widget?

    在实际开发中,我们经常会遇到一些复杂的UI需求,往往无法通过使用Flutter的基本Widget,设置其属性参数来满足。这个时候,我们就需要针对特定的场景自定义...

    拉维
  • 【更正】“给自定义控件(Web Control)添加事件的几种方法”有一个不太准确的地方。

        上一篇写了一下如何在自定义控件里面添加事件,由简单的开始,一步一步实现了几种添加事件的方式,由于当时只给自定义控件添加了一种外部事件,测试的时候没有什...

    用户1174620

扫码关注云+社区

领取腾讯云代金券