Core Image 的使用(iOS 的图像处理)

iOS Mar 18, 2015

Quartz 2D 以前写过文章介绍,是一个灰常强大的东西,是一个二维绘图引擎,同时支持iOS和Mac系统。比较重要的地方有图形上下文图形上下文栈,本文末再补充一点关于 Quartz 2D 的内容,这篇文章主要写 Core Image。

Photoshop 可以为照片添加各种滤镜大家都知道,不过如果在 iOS 上面实现,在 iOS5.0 之前,这一切都是很复杂的,要用算法来实现。在此之后,苹果官方已经提供了Core Image框架来帮助开发者进行特效制作。

滤镜中常用的基类对象

  • CIContext:图像上下文,用于管理整个图片处理过程,不同的图形上下文将利用不同的图像处理硬件进行图像处理(在 iOS 中可以通过不同的方式创建图像上下文,例如可以创建基于 CPU 的图像上下方、创建基于 GPU 的图像上下方以及创建 OpenGL 优化过的图像上下文)。
  • CIFilter:图像处理滤镜,每种滤镜有不同的参数设置。
  • CIImage:Core Image 框架中的图像类型,主要用于输入和输出图像。

查看所有滤镜

在使用滤镜之前我们先要弄清平台主要支持哪些滤镜,以及这些滤镜的方法和参数如何设置,此时不妨使用下面的方法进行打印查看:

#pragma mark 查看所有内置滤镜
-(void)showAllFilters{
    NSArray *filterNames=[CIFilter filterNamesInCategory:kCICategoryBuiltIn];
    for (NSString *filterName in filterNames) {
        CIFilter *filter=[CIFilter filterWithName:filterName];
        NSLog(@"\rfilter:%@\rattributes:%@",filterName,[filter attributes]);
    }
}

打印结果比较长,截取一部分:

2015-03-18 12:24:00.588 mosaic[703:31840] 
filter:CIAccordionFoldTransition
attributes:{
    CIAttributeFilterCategories =     (
        CICategoryTransition,
        CICategoryVideo,
        CICategoryStillImage,
        CICategoryBuiltIn
    );
    CIAttributeFilterDisplayName = "Accordion Fold Transition";
    CIAttributeFilterName = CIAccordionFoldTransition;
    inputBottomHeight =     {
        CIAttributeClass = NSNumber;
        CIAttributeDefault = 0;
        CIAttributeMin = 0;
        CIAttributeType = CIAttributeTypeDistance;
    };
    inputFoldShadowAmount =     {
        CIAttributeClass = NSNumber;
        CIAttributeDefault = "0.1";
        CIAttributeMax = 1;
        CIAttributeMin = 0;
        CIAttributeSliderMax = 1;
        CIAttributeSliderMin = 0;
        CIAttributeType = CIAttributeTypeScalar;
    };
    inputImage =     {
        CIAttributeClass = CIImage;
        CIAttributeType = CIAttributeTypeImage;
    };
    inputNumberOfFolds =     {
        CIAttributeClass = NSNumber;
        CIAttributeDefault = 3;
        CIAttributeMax = 50;
        CIAttributeMin = 1;
        CIAttributeSliderMax = 10;
        CIAttributeSliderMin = 1;
        CIAttributeType = CIAttributeTypeScalar;
    };
    inputTargetImage =     {
        CIAttributeClass = CIImage;
        CIAttributeType = CIAttributeTypeImage;
    };
    inputTime =     {
        CIAttributeClass = NSNumber;
        CIAttributeDefault = 0;
        CIAttributeIdentity = 0;
        CIAttributeMin = 0;
        CIAttributeSliderMax = 1;
        CIAttributeSliderMin = 0;
        CIAttributeType = CIAttributeTypeTime;
    };
}
2015-03-18 12:24:00.662 mosaic[703:31840] 
filter:CIAffineTransform
attributes:{
    CIAttributeFilterCategories =     (
        CICategoryGeometryAdjustment,
        CICategoryVideo,
        CICategoryStillImage,
        CICategoryBuiltIn
    );
    CIAttributeFilterDisplayName = "Affine Transform";
    CIAttributeFilterName = CIAffineTransform;
    inputImage =     {
        CIAttributeClass = CIImage;
        CIAttributeType = CIAttributeTypeImage;
    };
    inputTransform =     {
        CIAttributeClass = NSValue;
        CIAttributeDefault = "CGAffineTransform: {{1, 0, 0, 1}, {0, 0}}";
        CIAttributeIdentity = "CGAffineTransform: {{1, 0, 0, 1}, {0, 0}}";
        CIAttributeType = CIAttributeTypeTransform;
    };
}

在 iOS7 中打印会发现有 127 中滤镜,如果我们把每种滤镜都介绍一遍恐怕用几章内容也很难介绍详细,事实上也没有这个必要。这些滤镜使用方法是类似的,只是参数设置有所区别。在 iOS 文档中可以搜索“core image filter reference”一节的内容,里面有每种滤镜的详细介绍和图片使用效果。

创建滤镜效果

使用Core Image框架创建滤镜效果一般分为以下几步:

  1. 创建图像上下文 CIContext
  2. 创建滤镜 CIFilter
  3. 创建过滤原图片 CIImage
  4. 调用 CIFilter 的 setValue: forKey:方法为滤镜指定源图片
  5. 设置滤镜参数【可选】
  6. 取得输出图片显示或保存

代码

#import "KCMainViewController.h"
#define CONSTROLPANEL_FONTSIZE 12

@interface KCMainViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>{
    UIImagePickerController *_imagePickerController;//系统照片选择控制器
    UIImageView *_imageView;//图片显示控件
    CIContext *_context;//Core Image上下文
    CIImage *_image;//我们要编辑的图像
    CIImage *_outputImage;//处理后的图像
    CIFilter *_colorControlsFilter;//色彩滤镜
}

@end

@implementation KCMainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initLayout];
}


#pragma mark 初始化布局
-(void)initLayout{
    //初始化图片选择器
    _imagePickerController=[[UIImagePickerController alloc]init];
    _imagePickerController.delegate =self;

    //创建图片显示控件
    _imageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, 64, 320, 502)];
    _imageView.contentMode=UIViewContentModeScaleAspectFit;
    [self.view addSubview:_imageView];
    
    //上方导航按钮
    self.navigationItem.title=@"Enhance";
    self.navigationItem.leftBarButtonItem=[[UIBarButtonItem alloc]initWithTitle:@"Open" style:UIBarButtonItemStyleDone target:self action:@selector(openPhoto:)];
    self.navigationItem.rightBarButtonItem=[[UIBarButtonItem alloc]initWithTitle:@"Save" style:UIBarButtonItemStyleDone target:self action:@selector(savePhoto:)];

    
    //下方控制面板
    UIView *controlView=[[UIView alloc]initWithFrame:CGRectMake(0, 450, 320, 118)];
//    controlView.alpha=0.2;
//    controlView.backgroundColor=[UIColor colorWithRed:46.0/255.0 green:178.0/255.0 blue:235.0/255.0 alpha:1];
    [self.view addSubview:controlView];
    //饱和度(默认为1,大于饱和度增加小于1则降低)
    UILabel *lbSaturation=[[UILabel alloc]initWithFrame:CGRectMake(10, 10, 60, 25)];
    lbSaturation.text=@"Saturation";
    lbSaturation.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE];
    [controlView addSubview:lbSaturation];
    UISlider *sldStaturation=[[UISlider alloc]initWithFrame:CGRectMake(80, 10, 230, 30)];//注意UISlider高度虽然无法调整,很多朋友会说高度设置位0即可,事实上在iOS7中设置为0后是无法拖动的
    [controlView addSubview:sldStaturation];
    sldStaturation.minimumValue=0;
    sldStaturation.maximumValue=2;
    sldStaturation.value=1;
    [sldStaturation addTarget:self action:@selector(changeStaturation:) forControlEvents:UIControlEventValueChanged];
    //亮度(默认为0)
    UILabel *lbBrightness=[[UILabel alloc]initWithFrame:CGRectMake(10, 40, 60, 25)];
    lbBrightness.text=@"Brightness";
    lbBrightness.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE];
    [controlView addSubview:lbBrightness];
    UISlider *sldBrightness=[[UISlider alloc]initWithFrame:CGRectMake(80, 40, 230, 30)];
    [controlView addSubview:sldBrightness];
    sldBrightness.minimumValue=-1;
    sldBrightness.maximumValue=1;
    sldBrightness.value=0;
    [sldBrightness addTarget:self action:@selector(changeBrightness:) forControlEvents:UIControlEventValueChanged];
    //对比度(默认为1)
    UILabel *lbContrast=[[UILabel alloc]initWithFrame:CGRectMake(10, 70, 60, 25)];
    lbContrast.text=@"Contrast";
    lbContrast.font=[UIFont systemFontOfSize:CONSTROLPANEL_FONTSIZE];
    [controlView addSubview:lbContrast];
    UISlider *sldContrast=[[UISlider alloc]initWithFrame:CGRectMake(80, 70, 230, 30)];
    [controlView addSubview:sldContrast];
    sldContrast.minimumValue=0;
    sldContrast.maximumValue=2;
    sldContrast.value=1;
    [sldContrast addTarget:self action:@selector(changeContrast:) forControlEvents:UIControlEventValueChanged];
    
    
    //初始化CIContext
    //创建基于CPU的图像上下文
    //    NSNumber *number=[NSNumber numberWithBool:YES];
    //    NSDictionary *option=[NSDictionary dictionaryWithObject:number forKey:kCIContextUseSoftwareRenderer];
    //    _context=[CIContext contextWithOptions:option];
    _context=[CIContext contextWithOptions:nil];//使用GPU渲染,推荐,但注意GPU的CIContext无法跨应用访问,例如直接在UIImagePickerController的完成方法中调用上下文处理就会自动降级为CPU渲染,所以推荐现在完成方法中保存图像,然后在主程序中调用
    //    EAGLContext *eaglContext=[[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES1];
    //    _context=[CIContext contextWithEAGLContext:eaglContext];//OpenGL优化过的图像上下文
    
    //取得滤镜
    _colorControlsFilter=[CIFilter filterWithName:@"CIColorControls"];

}
#pragma mark 打开图片选择器
-(void)openPhoto:(UIBarButtonItem *)btn{
    //打开图片选择器
    [self presentViewController:_imagePickerController animated:YES completion:nil];
}
#pragma mark 保存图片
-(void)savePhoto:(UIBarButtonItem *)btn{
    //保存照片到相册
    UIImageWriteToSavedPhotosAlbum(_imageView.image, nil, nil, nil);
    UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"Sytem Info" message:@"Save Success!" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
    [alert show];
}

#pragma mark 图片选择器选择图片代理方法
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
    //关闭图片选择器
    [self dismissViewControllerAnimated:YES completion:nil];
    //取得选择图片
    UIImage *selectedImage=[info objectForKey:UIImagePickerControllerOriginalImage];
    _imageView.image=selectedImage;
    //初始化CIImage源图像
    _image=[CIImage imageWithCGImage:selectedImage.CGImage];
    [_colorControlsFilter setValue:_image forKey:@"inputImage"];//设置滤镜的输入图片
}

#pragma mark 将输出图片设置到UIImageView
-(void)setImage{
    CIImage *outputImage= [_colorControlsFilter outputImage];//取得输出图像
    CGImageRef temp=[_context createCGImage:outputImage fromRect:[outputImage extent]];
    _imageView.image=[UIImage imageWithCGImage:temp];//转化为CGImage显示在界面中
    
    CGImageRelease(temp);//释放CGImage对象
}

#pragma mark 调整饱和度
-(void)changeStaturation:(UISlider *)slider{
    [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputSaturation"];//设置滤镜参数
    [self setImage];
}

#pragma mark 调整亮度
-(void)changeBrightness:(UISlider *)slider{
    [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputBrightness"];
    [self setImage];
}

#pragma mark 调整对比度
-(void)changeContrast:(UISlider *)slider{
    [_colorControlsFilter setValue:[NSNumber numberWithFloat:slider.value] forKey:@"inputContrast"];
    [self setImage];
}
@end
  • 在上面的代码中除了使用了基于GPU的图像上下文(推荐方式),也创建了其他图像上下文,尽管已经被注释大家还是需要熟悉。
  • Core Image允许你一次给图像或视频帧叠加多种效果,同时Core Image还能保证强大的处理效率。
  • 和在使用Core Graphics绘图一样,UIKit中也封装了一些方法直接转换为Core Image中的对象,例如UIImage对象可以直接调用CIImage属性转换为CIImage类型。

Quartz 2D 补充:

  1. Core Graphics 是基于 C 语言的一套框架,开发时无法像使用 Obj-C 一样调用;
  2. 在 Quartz 2D 中凡是使用带有“Create”或者“Copy”关键字方法创建的对象,在使用后一定要使用对应的方法释放(由于这个框架基于C语言编写无法自动释放内存);
  3. Quartz 2D 是跨平台的,因此其中的方法中不能使用 UIKit 中的对象( UIKit 只有 iOS 可用),例如用到的颜色只能用 CGColorRef 而不能用 UIColor,但是 UIKit 中提供了对应的转换方法;
  4. 在 C 语言中枚举一般以“k”开头,由于 Quartz 2D 基于 C 语言开发,所以它也不例外(参数中很多枚举都是 k 开头的);
  5. 由于 Quartz 2D 是 Core Graphics 的一部分,所以 API 多数以 CG 开头;
  6. 在使用 Quartz 2D 绘图API中所有以“Ref”结尾对象,在声明时都不必声明为指针类型;
  7. 在使用 Quartz 2D 绘图 API 时,凡是“UI”开头的相关绘图函数,都是 UIKit 对 Core Graphics 的封装(主要为了简化绘图操作);

Reference

  1. iOS开发系列--打造自己的“美图秀秀”

Tags

Jie Li

🚘 On-road / 📉 US Stock / 💻 Full Stack Developer / 🎓 Grad Student / ®️ ENTJ

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.