iOS滤镜功能

一、iOS自带滤镜

1.CoreImage

使用苹果自带的CoreImage框架对图片进行处理,用CoreImage框架里的CIFilter对图片进行滤镜处理,

首先我们应该了解下CoreImage框架能够对图像进行那些处理和拥有哪些特效。

苹果给我们提供了将近200中滤镜效果

// 这里我们可以看到总共有多少种滤镜

 NSArray *filterNames = [CIFilter filterNamesInCategory:@"CICategoryBuiltIn"];       
NSLog(@"总共有%ld种滤镜效果:%@",filterNames.count,filterNames); //以一个具体分类中的滤镜信息 NSArray* filters = [CIFilter filterNamesInCategory:kCICategoryDistortionEffect]; for (NSString* filterName in filters) { NSLog(@"filter name:%@",filterName); // 我们可以通过filterName创建对应的滤镜对象 CIFilter* filter = [CIFilter filterWithName:filterName]; NSDictionary* attributes = [filter attributes]; // 获取属性键/值对(在这个字典中我们可以看到滤镜的属性以及对应的key) NSLog(@"filter attributes:%@",attributes); }

然后我们还可以进入苹果iOS官方文档中具体看看效果到底是什么样子的Core Image Filter Reference

可以看到CoreImage中的CIFilter效果确实很多,分很多种类别,每个分类中又有多个效果

 

2.滤镜怎么实现

CoreImage框架提供三个API来实现滤镜效果

CIContext:核心API,来管理所有的图片处理操作。

CIFilter:过滤器,通过在创建CIFilter时需要传入不同的参数即可创建不同类型的过滤器。

CIImage:它代表 Core Image 过滤器处理的图片,CIFilter过滤器的输入图片,输出图片都由该CIImage代表。

CIContext:创建分三种方式,因为采用基于GPU的CIContext将可以获得更好的性能,因此,

一般建议创建基于GPU的CIContext,但基于GPU的CIContext对象无法跨应用访问,这个问题需要注意

 //1.创建基于CPU的CIContext对象    
 self.context = [CIContext contextWithOptions:
    [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
 forKey:kCIContextUseSoftwareRenderer]];

    //2.创建基于GPU的CIContext对象 
 self.context = [CIContext contextWithOptions: nil];

    //3.创建基于OpenGL优化的CIContext对象,可获得实时性能
self.context = [CIContext contextWithEAGLContext:[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]];

// 将UIImage转换成CIImage
    CIImage *ciImage = [[CIImage alloc] initWithImage:[UIImage imageNamed:@"WechatIMG1.jpeg"]];
    // 创建滤镜
    CIFilter *filter = [CIFilter filterWithName:_dataSourse[indexPath.row]
                                  keysAndValues:kCIInputImageKey, ciImage, nil];
    [filter setDefaults];

    // 获取绘制上下文
    CIContext *context = [CIContext contextWithOptions:nil];
    // 渲染并输出CIImage
    CIImage *outputImage = [filter outputImage];
    // 创建CGImage句柄
    CGImageRef cgImage = [self.context createCGImage:outputImage
                                      fromRect:[outputImage extent]];
    imageview.image = [UIImage imageWithCGImage:cgImage];
    // 释放CGImage句柄
    CGImageRelease(cgImage);

二、GPUImage实现滤镜

1. GPUImage

GPUImage是现在做滤镜最主流的开源框架,没有之一。作者BradLarson基于openGL对图片处理单元进行封装,

提供出GPUImageFilter基类,配合shader,常用滤镜都拿下不是问题。

原理:

美颜的基本概念

OpenGL ES:开源嵌入式系统图形的处理框架,一套图形与硬件接口,创造了软件与图形加速间灵活强大的底层交互接口。用于把处理好的图片显示到屏幕上。

GPU:(图形处理单元)手机或者电脑用于图像处理和渲染的硬件。

GPU工作原理:CPU指定显示器工作,显示控制器根据CPU的控制到指定的地方去取数据和指令,目前的数据一般是从显存里取,如果显存里存不下,则从内存里取,内存也放不下,则从硬盘里取。

滤镜处理的原理:就是把静态图片或者视频的每一帧进行图形变化后在显示到屏幕上,其本质就是像素点的坐标和颜色的变化。

OpenGL ES程序处理图片的步骤:

1.初始化OpenGL ES 环境,编译、链接顶点着色器和片元着色器。

2.缓存顶点、纹理坐标数据,传送图像数据到GPU

3.绘制图元到特定的帧缓存;

4.在帧缓存去除绘制的图像。

GPUImage基本概念

GPUImage是采用链式方法来处理画面,通过addTarget方法添加对象到链中,处理完一个target,就会吧上一个环节处理好的图像数据传递到下一个target处理,成为GPUImage处理链。

GPUImage的四大输入基础类,都可以作为响应链的起点,这些基础类会把图像作为纹理传给OpenGL ES处理,然后把纹理传递给响应链的下一个target对象。

GPUImage的处理环节

source(视频,图片源)->filter(滤镜)->final target(处理后的视频、图片)

source:

GPUImageVideoCamera 摄像头-用于实时拍摄视频

GPUImageStillCamera 摄像头-用于实时拍摄照片

GPUImagePicture 用于处理已经拍摄好的图片

GPUImageMovie 用于处理已经拍摄好的视频

filter

GPUImageFilter:就是用来接收源图像,通过自定义的顶点,片元着色器来渲染新的图像,并在绘制完成后通知响应链的下一个对象。

GPUImageFramebuffer:就是用来管理纹理缓存的格式与读写帧缓存的buffer。

GPUImage的filter:GPUImageFilter类或者子类,这个类继承自GPUImageOutput,遵循GPUImageInput协议,既可以流进数据,又可以流出GPUImage的final target: GPUImageView,GPUImageMovieWriter最终输入目标,显示图片或者视频。

 

解析

GPUImageVideoCamera

GPUImageVideoCamera是GPUImageOutput的子类,提供来自摄像头的图像数据作为源数据,一般是响应链的源头。

1.视频图像采集:AVCaptureSession

GPUImage使用AVFoundation框架来获取视频。AVCaptureSession类从AV输入设备的采集数据到指定的输出。

2.颜色空间:YUV

YUV是被欧洲电视系统索采用的一种颜色编码方法。采用YUV色彩空间的重要性是它的亮度信号Y和色度信号U、V是分离的。如果只有Y信号分量而没有U、V分量,那么这样表示的图像就是黑白灰度图像,彩色电视采用YUV空间正式为了用亮度信号Y解决彩色电视机与黑白电视机的兼容问题,使黑白电视机也能接收彩色电视信号。

YUV主要用于优化彩色视频信号的传输,使其向后相容老式黑白电视。与RGB视频信号传输相比,它最大的优点在于只需占用极少的频宽。

GPUImage中的YUV

GLProgram *yuvConversionProgram:将YUV颜色空间转换成RGB颜色空间的GLSL。
CVPixelBufferGetPlaneCount:返回缓冲区的平面数。

CVOpenGLESTextureCacheCreateTextureFromImage():
创建两个纹理luminanceTextureRef(亮度纹理)和chrominanceTextureRef(色度纹理)。
convertYUVToRGBOutput():把YUV颜色空间的纹理转换成RGB颜色空间的纹理。

顶点着色器-通用kGPUImageVertexShaderString

片元着色器:

1.kGPUImageYUVFullRangeConversionForLAFragmentShaderString
 
2. kGPUImageYUVVideoRangeConversionForLAFragmentShaderString

  纹理绘制

 1. glActiveTexture(GL_TEXTURE1);
 2. glGenTextures(1, &_texture); 
 3. glBindTexture(GL_TEXTURE_2D, _texture);

  GPUImageView是响应链的终点,用于显示GPUImage图像

1.填充模式

GPUImageFillModeType fillMode图像的填充模式。

sizeInPixels 像素区域的大小

recalculateViewGeometry 重新计算图像顶点位置数据

AVMakeRectWithAspectRatioInsideRece 在保证宽高比不变的前提下,得到一个尽可能大的矩形。

 

1.1、安装(请参考这个 https://www.jianshu.com/p/4d419a88ecce

(1):首先下载GPUImagehttps://github.com/BradLarson/GPUImage

(2):解压后,在framework 目录下,打开 GPUImage.xcodeproj  工程

下载完成打开文件有件

 

 2、GPUImage的使用

使用GPUImage自带的滤镜,GPUImage自带的滤镜有很多种这里举例一种

 GPUImageBrightnessFilter *disFilter = [[GPUImageBrightnessFilter alloc] init];
        //设置美白参数
        disFilter.brightness = 0.2;
        //设置要渲染的区域
        [disFilter forceProcessingAtSize:image.size];
        
        [disFilter useNextFrameForImageCapture];
        
        //获取数据源
        GPUImagePicture *stillImageSource = [[GPUImagePicture alloc]initWithImage:image];
        
        //添加上滤镜
        [stillImageSource addTarget:disFilter];
        
        //开始渲染
        [stillImageSource processImage];
        //获取渲染后的图片
        UIImage *newImage = [disFilter imageFromCurrentFramebuffer];
        return newImage;

另外就是根据纹理自定义滤镜来处理图片,纹理图片可有设计提供,另外纹理的叠加还需要研究

#import "GPUImageTwoInputFilter.h"

#import "GPUImage.h"

NS_ASSUME_NONNULL_BEGIN

@interface DhGPUImageQingXinFilter : GPUImageTwoInputFilter

@end
@interface GPUImageQingXinFilter : GPUImageFilterGroup
{
    GPUImagePicture *imageSource ;
    GPUImagePicture *imageSource2 ;
}
@end
NS_ASSUME_NONNULL_END

 

#import "DhGPUImageQingXinFilter.h"

#import "GPUImageLookupFilter.h"
#import "GPUImageFilter.h"
//自定义shader

NSString *const GPUImageQingXinFilterString = SHADER_STRING
(
 precision lowp float;
 
 varying highp vec2 textureCoordinate;
 
 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2;
 void main()
 {
     
     vec3 texel = texture2D(inputImageTexture, textureCoordinate).rgb;
     
     texel = vec3(
                  texture2D(inputImageTexture2, vec2(texel.r, .16666)).r,
                  texture2D(inputImageTexture2, vec2(texel.g, .5)).g,
                  texture2D(inputImageTexture2, vec2(texel.b, .83333)).b);
     gl_FragColor = vec4(texel, 1.0);
 }
 );

@implementation DhGPUImageQingXinFilter
- (id)init;
{
    if (!(self = [super initWithFragmentShaderFromString:GPUImageQingXinFilterString]))
    {
        return nil;
    }
    
    return self;
}
@end

@implementation GPUImageQingXinFilter


- (id)init
{
    if (!(self = [super init]))
    {
        return nil;
    }
    //  清新
    
    UIImage *image2 = [UIImage imageNamed:@"camera_filter_overlay_map.png"];
    UIImage *image = [UIImage imageNamed:@"camera_filter_sierra_map"];
    
    
    imageSource = [[GPUImagePicture alloc] initWithImage:image];
    DhGPUImageQingXinFilter *filter = [[DhGPUImageQingXinFilter alloc] init];
    
    [self addFilter:filter];
    
    [imageSource addTarget:filter atTextureLocation:1];
    [imageSource processImage];
    
    imageSource2 = [[GPUImagePicture alloc] initWithImage:image2];
    DhGPUImageQingXinFilter *filter2 = [[DhGPUImageQingXinFilter alloc] init];
    [filter addTarget:filter2];
    [imageSource2 addTarget:filter2];
    [imageSource2 processImage];
    [self addFilter:filter2];
    
    self.initialFilters = [NSArray arrayWithObjects:filter, nil];
    self.terminalFilter = filter;
    
    return self;
}
- (void)dealloc
{
#if !OS_OBJECT_USE_OBJC
    if (imageCaptureSemaphore != NULL)
    {
        dispatch_release(imageCaptureSemaphore);
    }
#endif
    
}
@end

 

 

 

 
posted @ 2019-09-15 08:22  甘林梦  阅读(1429)  评论(0编辑  收藏  举报