GPUImage源码阅读(七)

作者: 秦明Qinmin | 来源:发表于2017-05-11 20:36 被阅读518次

    概述

    GPUImage是一个著名的图像处理开源库,它让你能够在图片、视频、相机上使用GPU加速的滤镜和其它特效。与CoreImage框架相比,可以根据GPUImage提供的接口,使用自定义的滤镜。项目地址:https://github.com/BradLarson/GPUImage
    这篇文章主要是阅读GPUImage框架中的 GPUImageFilterPipeline、GPUImageFilterGroup 这几个类的源码。这两个类都和组合滤镜相关,但是它们又有不同的地方。GPUImageFilterPipeline 继承自NSObject,也没有实现其它协议,而GPUImageFilterGroup继承自GPUImageOutput 实现了GPUImageInput协议。因此,最大的区别就是GPUImageFilterGroup自身可以作为响应链的一员,而GPUImageFilterPipeline则不能。以下是源码内容:
    GPUImageFilterPipeline
    GPUImageFilterGroup

    实现效果

    • 从文件中读取滤镜配置。


      filterConfig.png
    • 直接配置滤镜。


      filter.png
    • 自定义组合滤镜。


      GPUImageFilterGroup.png

    GPUImageFilterPipeline

    GPUImageFilterPipeline 继承自NSObject,它的主要作用是管理滤镜链,自身不能参与响应链中。可以用来构建简单的滤镜组合。如果滤镜比较复杂或是涉及到多个纹理的处理,GPUImageFilterGroup则是更好的选择。

    • 属性。
    // filter数组
    @property (strong) NSMutableArray *filters;
    // 输入对象
    @property (strong) GPUImageOutput *input;
    // 输出对象
    @property (strong) id <GPUImageInput> output;
    
    
    • 构造方法。可以通过指定filter数组以及配置字典、文件来构造GPUImageFilterPipeline。
    - (id) initWithOrderedFilters:(NSArray*) filters input:(GPUImageOutput*)input output:(id <GPUImageInput>)output;
    - (id) initWithConfiguration:(NSDictionary*) configuration input:(GPUImageOutput*)input output:(id <GPUImageInput>)output;
    - (id) initWithConfigurationFile:(NSURL*) configuration input:(GPUImageOutput*)input output:(id <GPUImageInput>)output;
    
    // 根据输入的filter数组、GPUImageOutput、GPUImageInput构建GPUImageFilterPipeline
    - (id)initWithOrderedFilters:(NSArray *)filters input:(GPUImageOutput *)input output:(id <GPUImageInput>)output {
        self = [super init];
        if (self) {
            self.input = input;
            self.output = output;
            self.filters = [NSMutableArray arrayWithArray:filters];
            [self _refreshFilters];
        }
        return self;
    }
    
    // 根据filter配置字典、GPUImageOutput、GPUImageInput构建GPUImageFilterPipeline
    - (id)initWithConfiguration:(NSDictionary *)configuration input:(GPUImageOutput *)input output:(id <GPUImageInput>)output {
        self = [super init];
        if (self) {
            self.input = input;
            self.output = output;
            if (![self _parseConfiguration:configuration]) {
                NSLog(@"Sorry, a parsing error occurred.");
                abort();
            }
            [self _refreshFilters];
        }
        return self;
    }
    
    // 根据filter配置文件的URL、GPUImageOutput、GPUImageInput构建GPUImageFilterPipeline
    - (id)initWithConfigurationFile:(NSURL *)configuration input:(GPUImageOutput *)input output:(id <GPUImageInput>)output {
        return [self initWithConfiguration:[NSDictionary dictionaryWithContentsOfURL:configuration] input:input output:output];
    }
    
    // 解析配置文件
    - (BOOL)_parseConfiguration:(NSDictionary *)configuration {
        NSArray *filters = [configuration objectForKey:@"Filters"];
        if (!filters) {
            return NO;
        }
        
        NSError *regexError = nil;
        // 匹配配置文件参数如:float(1.0), CGPoint(1.0, 2.0) 等类型
        NSRegularExpression *parsingRegex = [NSRegularExpression regularExpressionWithPattern:@"(float|CGPoint|NSString)\\((.*?)(?:,\\s*(.*?))*\\)"
                                                                                      options:0
                                                                                        error:&regexError];
        
        // It's faster to put them into an array and then pass it to the filters property than it is to call [self addFilter:] every time
        NSMutableArray *orderedFilters = [NSMutableArray arrayWithCapacity:[filters count]];
        for (NSDictionary *filter in filters) {
            // 由FilterName生成相应的实例对象
            NSString *filterName = [filter objectForKey:@"FilterName"];
            Class theClass = NSClassFromString(filterName);
            GPUImageOutput<GPUImageInput> *genericFilter = [[theClass alloc] init];
            // Set up the properties
            NSDictionary *filterAttributes;
            // 解析Attributes,并传递参数
            if ((filterAttributes = [filter objectForKey:@"Attributes"])) {
                for (NSString *propertyKey in filterAttributes) {
                    // Set up the selector
                    SEL theSelector = NSSelectorFromString(propertyKey);
                    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[theClass instanceMethodSignatureForSelector:theSelector]];
                    [inv setSelector:theSelector];
                    [inv setTarget:genericFilter];
                    
                    // check selector given with parameter
                    if ([propertyKey hasSuffix:@":"]) {
                        
                        stringValue = nil;
                        
                        // Then parse the arguments
                        NSMutableArray *parsedArray;
                        if ([[filterAttributes objectForKey:propertyKey] isKindOfClass:[NSArray class]]) {
                            NSArray *array = [filterAttributes objectForKey:propertyKey];
                            parsedArray = [NSMutableArray arrayWithCapacity:[array count]];
                            for (NSString *string in array) {
                                NSTextCheckingResult *parse = [parsingRegex firstMatchInString:string
                                                                                       options:0
                                                                                         range:NSMakeRange(0, [string length])];
    
                                NSString *modifier = [string substringWithRange:[parse rangeAtIndex:1]];
                                if ([modifier isEqualToString:@"float"]) {
                                    // Float modifier, one argument
                                    CGFloat value = [[string substringWithRange:[parse rangeAtIndex:2]] floatValue];
                                    [parsedArray addObject:[NSNumber numberWithFloat:value]];
                                    [inv setArgument:&value atIndex:2];
                                } else if ([modifier isEqualToString:@"CGPoint"]) {
                                    // CGPoint modifier, two float arguments
                                    CGFloat x = [[string substringWithRange:[parse rangeAtIndex:2]] floatValue];
                                    CGFloat y = [[string substringWithRange:[parse rangeAtIndex:3]] floatValue];
                                    CGPoint value = CGPointMake(x, y);
                                    [parsedArray addObject:[NSValue valueWithCGPoint:value]];
                                } else if ([modifier isEqualToString:@"NSString"]) {
                                    // NSString modifier, one string argument
                                    stringValue = [[string substringWithRange:[parse rangeAtIndex:2]] copy];
                                    [inv setArgument:&stringValue atIndex:2];
                                    
                                } else {
                                    return NO;
                                }
                            }
                            [inv setArgument:&parsedArray atIndex:2];
                        } else {
                            NSString *string = [filterAttributes objectForKey:propertyKey];
                            NSTextCheckingResult *parse = [parsingRegex firstMatchInString:string
                                                                                   options:0
                                                                                     range:NSMakeRange(0, [string length])];
                            
                            NSString *modifier = [string substringWithRange:[parse rangeAtIndex:1]];
                            if ([modifier isEqualToString:@"float"]) {
                                // Float modifier, one argument
                                CGFloat value = [[string substringWithRange:[parse rangeAtIndex:2]] floatValue];
                                [inv setArgument:&value atIndex:2];
                            } else if ([modifier isEqualToString:@"CGPoint"]) {
                                // CGPoint modifier, two float arguments
                                CGFloat x = [[string substringWithRange:[parse rangeAtIndex:2]] floatValue];
                                CGFloat y = [[string substringWithRange:[parse rangeAtIndex:3]] floatValue];
                                CGPoint value = CGPointMake(x, y);
                                [inv setArgument:&value atIndex:2];
                            } else if ([modifier isEqualToString:@"NSString"]) {
                                // NSString modifier, one string argument
                                stringValue = [[string substringWithRange:[parse rangeAtIndex:2]] copy];
                                [inv setArgument:&stringValue atIndex:2];
                                
                            } else {
                                return NO;
                            }
                        }
                    }
                    
    
                    [inv invoke];
                }
            }
            [orderedFilters addObject:genericFilter];
        }
        self.filters = orderedFilters;
        
        return YES;
    }
    
    • 配置文件示例
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Filters</key>
        <array>
            <dict>
                <key>FilterName</key>
                <string>GPUImageGammaFilter</string>
                <key>Attributes</key>
                <dict>
                    <key>setGamma:</key>
                    <string>float(1.0)</string>
                </dict>
            </dict>
            <dict>
                <key>FilterName</key>
                <string>GPUImageBrightnessFilter</string>
                <key>Attributes</key>
                <dict>
                    <key>setBrightness:</key>
                    <string>float(0.2)</string>
                </dict>
            </dict>
        </array>
    </dict>
    </plist>
    
    • 其它方法
    // filter的增加、删除、替换
    - (void) addFilter:(GPUImageOutput<GPUImageInput> *)filter;
    - (void) addFilter:(GPUImageOutput<GPUImageInput> *)filter atIndex:(NSUInteger)insertIndex;
    - (void) replaceFilterAtIndex:(NSUInteger)index withFilter:(GPUImageOutput<GPUImageInput> *)filter;
    - (void) replaceAllFilters:(NSArray *) newFilters;
    - (void) removeFilter:(GPUImageOutput<GPUImageInput> *)filter;
    - (void) removeFilterAtIndex:(NSUInteger)index;
    - (void) removeAllFilters;
    
    // 由final filter生成图片
    - (UIImage *) currentFilteredFrame;
    - (UIImage *) currentFilteredFrameWithOrientation:(UIImageOrientation)imageOrientation;
    - (CGImageRef) newCGImageFromCurrentFilteredFrame;
    
    // 在特定位置增加filter
    - (void)addFilter:(GPUImageOutput<GPUImageInput> *)filter atIndex:(NSUInteger)insertIndex {
        [self.filters insertObject:filter atIndex:insertIndex];
        [self _refreshFilters];
    }
    
    // 在末尾增加filter
    - (void)addFilter:(GPUImageOutput<GPUImageInput> *)filter {
        [self.filters addObject:filter];
        [self _refreshFilters];
    }
    
    // 替换filter
    - (void)replaceFilterAtIndex:(NSUInteger)index withFilter:(GPUImageOutput<GPUImageInput> *)filter {
        [self.filters replaceObjectAtIndex:index withObject:filter];
        [self _refreshFilters];
    }
    
    // 删除filter
    - (void) removeFilter:(GPUImageOutput<GPUImageInput> *)filter;
    {
        [self.filters removeObject:filter];
        [self _refreshFilters];
    }
    
    // 删除特定索引的filter
    - (void)removeFilterAtIndex:(NSUInteger)index {
        [self.filters removeObjectAtIndex:index];
        [self _refreshFilters];
    }
    
    // 删除所有filter
    - (void)removeAllFilters {
        [self.filters removeAllObjects];
        [self _refreshFilters];
    }
    
    // 替换所有filter
    - (void)replaceAllFilters:(NSArray *)newFilters {
        self.filters = [NSMutableArray arrayWithArray:newFilters];
        [self _refreshFilters];
    }
    
    // 更新响应链
    - (void)_refreshFilters {
        // 将input作为响应链源
        id prevFilter = self.input;
        GPUImageOutput<GPUImageInput> *theFilter = nil;
        
        // 循环添加
        for (int i = 0; i < [self.filters count]; i++) {
            theFilter = [self.filters objectAtIndex:i];
            [prevFilter removeAllTargets];
            [prevFilter addTarget:theFilter];
            prevFilter = theFilter;
        }
        
        [prevFilter removeAllTargets];
        
        // 最后将output加入响应链  
        if (self.output != nil) {
            [prevFilter addTarget:self.output];
        }
    }
    
    // 由final filter生成图像
    - (UIImage *)currentFilteredFrame {
        return [(GPUImageOutput<GPUImageInput> *)[_filters lastObject] imageFromCurrentFramebuffer];
    }
    
    // 根据imageOrientation生成图像
    - (UIImage *)currentFilteredFrameWithOrientation:(UIImageOrientation)imageOrientation {
      return [(GPUImageOutput<GPUImageInput> *)[_filters lastObject] imageFromCurrentFramebufferWithOrientation:imageOrientation];
    }
    
    // 由final filter生成图像
    - (CGImageRef)newCGImageFromCurrentFilteredFrame {
        return [(GPUImageOutput<GPUImageInput> *)[_filters lastObject] newCGImageFromCurrentlyProcessedOutput];
    }
    

    GPUImageFilterGroup

    GPUImageFilterGroup继承自GPUImageOutput ,实现了GPUImageInput协议。因此,可以自身可以作为独立的滤镜参与响应链中。相比GPUImageFilterPipeline,GPUImageFilterGroup功能更强大。

    • 属性
    @property(readwrite, nonatomic, strong) GPUImageOutput<GPUImageInput> *terminalFilter;
    @property(readwrite, nonatomic, strong) NSArray *initialFilters;
    @property(readwrite, nonatomic, strong) GPUImageOutput<GPUImageInput> *inputFilterToIgnoreForUpdates; 
    
    • 方法
    // 增加滤镜
    - (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter;
    - (GPUImageOutput<GPUImageInput> *)filterAtIndex:(NSUInteger)filterIndex;
    - (NSUInteger)filterCount;
    
    // 增加滤镜
    - (void)addFilter:(GPUImageOutput<GPUImageInput> *)newFilter;
    {
        [filters addObject:newFilter];
    }
    
    // 获取滤镜
    - (GPUImageOutput<GPUImageInput> *)filterAtIndex:(NSUInteger)filterIndex;
    {
        return [filters objectAtIndex:filterIndex];
    }
    
    // 滤镜数量
    - (NSUInteger)filterCount;
    {
        return [filters count];
    }
    
    // 
    - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
    {
        for (GPUImageOutput<GPUImageInput> *currentFilter in _initialFilters)
        {
            if (currentFilter != self.inputFilterToIgnoreForUpdates)
            {
                [currentFilter newFrameReadyAtTime:frameTime atIndex:textureIndex];
            }
        }
    }
    
    // 设置帧缓存对象
    - (void)setInputFramebuffer:(GPUImageFramebuffer *)newInputFramebuffer atIndex:(NSInteger)textureIndex;
    {
        for (GPUImageOutput<GPUImageInput> *currentFilter in _initialFilters)
        {
            [currentFilter setInputFramebuffer:newInputFramebuffer atIndex:textureIndex];
        }
    }
    

    实现过程

    • 从文件中读取滤镜配置。
    // 加载图片
    GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:[UIImage imageNamed:@"1.jpg"]];
    
    // 配置文件
    NSURL *file = [[NSBundle mainBundle] URLForResource:@"filterConfig" withExtension:@"plist"];
    
    // 滤镜组合
    _pipleLine = [[GPUImageFilterPipeline alloc] initWithConfigurationFile:file input:picture output:_imageView];
    
    [picture processImage];
    
    • 直接配置滤镜。
    // 加载图片
    GPUImagePicture *picture = [[GPUImagePicture alloc] initWithImage:[UIImage imageNamed:@"2.jpg"]];
    
    // filters
    GPUImageRGBFilter *rgbFilter = [[GPUImageRGBFilter alloc] init];
    GPUImagePerlinNoiseFilter *noiseFilter = [[GPUImagePerlinNoiseFilter alloc] init];
    
    // 配置文件
    NSArray *filters = @[rgbFilter, noiseFilter];
    
    // 滤镜组合
    _pipleLine = [[GPUImageFilterPipeline alloc] initWithOrderedFilters:filters input:picture output:_imageView];
    
    [picture processImage];
    
    • 自定义组合滤镜。
    #import "QMGrayBilateralBlendFilter.h"
    
    NSString *const kGrayBilateralBlendFragmentShaderString = SHADER_STRING
    (
     varying highp vec2 textureCoordinate;
     varying highp vec2 textureCoordinate2;
     
     uniform sampler2D inputImageTexture;
     uniform sampler2D inputImageTexture2;
     
     void main()
     {
         highp vec4 gray = texture2D(inputImageTexture, textureCoordinate);
         highp vec4 bilateral = texture2D(inputImageTexture2, textureCoordinate2);
         
         highp float range = distance(textureCoordinate, vec2(0.5, 0.5));
         
         highp vec4 dstClor = bilateral;
         if (range < 0.4) {
             dstClor = gray;
         }
         
         gl_FragColor = dstClor;
         
         //gl_FragColor = vec4(vec3(mix(gray, bilateral, 0.6)), 1.0);
     }
     );
    
    @implementation QMGrayBilateralBlendFilter
    
    - (instancetype)init
    {
        if (self = [super init])
        {
            // GrayscaleFilter
            GPUImageGrayscaleFilter *grayFilter = [[GPUImageGrayscaleFilter alloc] init];
            [self addFilter:grayFilter];
            
            // BilateralFilter
            GPUImageBilateralFilter *bilateralFilter = [[GPUImageBilateralFilter alloc] init];
            bilateralFilter.distanceNormalizationFactor = 16.0;
            [self addFilter:bilateralFilter];
            
            // GPUImageTwoPassFilter
            GPUImageTwoInputFilter *twoPassFilter = [[GPUImageTwoInputFilter alloc] initWithFragmentShaderFromString:kGrayBilateralBlendFragmentShaderString];
            
            // GPUImageHSBFilter
            GPUImageHSBFilter *hsbFilter = [[GPUImageHSBFilter alloc] init];
            
            [grayFilter addTarget:twoPassFilter];
            [bilateralFilter addTarget:twoPassFilter];
            [twoPassFilter addTarget:hsbFilter];
            
            self.initialFilters = @[grayFilter, bilateralFilter];
            self.terminalFilter = hsbFilter;
            
        }
        return self;
    }
    @end
    

    总结

    GPUImageFilterPipeline、GPUImageFilterGroup 都可用于组合滤镜,GPUImageFilterPipeline相对比较简单,可定制程度较低,而GPUImageFilterGroup则功能比较强大,可定制程度高。在选用的时候可以根据自己的需求,选用不同的滤镜组合。

    源码地址:GPUImage源码阅读系列 https://github.com/QinminiOS/GPUImage
    系列文章地址:GPUImage源码阅读 http://www.jianshu.com/nb/11749791

    相关文章

      网友评论

      • Zszen:pipeline是网友写的

      本文标题:GPUImage源码阅读(七)

      本文链接:https://www.haomeiwen.com/subject/vflotxtx.html