使用CIFilter和CISpotColor正确过滤NSImage / CIImage

时间:2014-05-25 03:28:19

标签: objective-c xcode macos image-processing cifilter

请注意:这适用于在Mac OSX上运行的Cocoa命令行应用,不是iOS应用

我在尝试理解Apple为 CISpotColor 过滤器提供的Limited Documentation时遇到了一些麻烦(使用 CIFilter )。


TL DR;

1)是否有更多关于CIFilter遗漏的文档,特别是CISpotColor?

2)鉴于我想要实现的目标(如下图所示,但简要说明:更换所有不会"看起来红色"白色,并强制每一件事情看起来都是红色的(ish) )"要么是纯红色,要么只是黑色,是CISpotColor我应该使用的正确过滤器吗?

3)如果没有,你建议使用哪种过滤器(或者我应该尝试编写自定义过滤器?)

4)如果CISSpotColor是正确的过滤器,我应该使用哪些参数来实现我想要实现的目标。如果我需要使用CISpotColor CIFilter的几个通道,那很好,我不希望你为我编码,只是指出我正确的方向。


以上问题的更多细节和背景:

link above给出了参数列表,一些默认值以及图片之前和之后的示例,但没有示例代码在图像之后生成样本,并且没有解释参数实际意味着什么,或者什么他们的有效范围是。

老实说,我不完全确定CISpotColor是否是我所追求的过滤器,除了它的名称,以及句子" 用专色替换一个或多个颜色范围",没有解释它是如何做的。

因为它似乎描述了我所追求的过滤器,所以我选择它作为起点,以这种方式使用过滤器。

输入图片(视频中的一帧) enter image description here

所需的输出(选项1 - 纯红色 - 使用GIMP创建) enter image description here

所需的输出(选项2 - 纯黑色 - 也使用GIMP创建) enter image description here

我的代码获得了什么(请参阅下面的列表) enter image description here

这接近我所需要的,但它似乎没有考虑到灰色或白色区域的事实。在原始图像中将具有相似数量的红色,绿色和蓝色,而不是主要是红色,这将使其看起来红色"。我可以使用它,如果它过滤掉你在右下角看到的区域,这显然只是被包括在内,因为那里有一些红色像素(以及一些绿色和蓝色,使其在原始中通常为灰色)

这是完整的" main.m"用于cocoa命令行应用程序(Mac OSX)

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <AppKit/AppKit.h>
#import <QuartzCore/QuartzCore.h>



@interface NSImage(saveAsJpegWithName)
- (void) saveAsPNGWithName:(NSString*) fileName;
- (NSImage*) filterEverythingButRed ;
@end

@implementation NSImage(saveAsJpegWithName)

- (void) saveAsPNGWithName:(NSString*) fileName
{
    NSData *imageData = [self TIFFRepresentation];
    NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
    NSDictionary *imageProps = nil;
    imageData = [imageRep representationUsingType:NSPNGFileType properties:imageProps];
    [imageData writeToFile:fileName atomically:NO];
}

-(NSImage*) filterEverythingButRed {

    CIImage *inputImage = [[CIImage alloc] initWithData:[self TIFFRepresentation]];

    CIFilter *hf = [CIFilter filterWithName:@"CISpotColor"];
    [hf setDefaults];
    [hf setValue:inputImage forKey:@"inputImage"];
    [hf setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0] forKey: @"inputCenterColor1"];
    [hf setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0] forKey: @"inputReplacementColor1"];
    [hf setValue:[NSNumber numberWithFloat:0.1] forKey: @"inputCloseness1"];
    [hf setValue:[NSNumber numberWithFloat:1.0] forKey: @"inputContrast1"];

    CIImage *outputImage = [hf valueForKey: @"outputImage"];

    NSImage *resultImage = [[NSImage alloc] initWithSize:[outputImage extent].size];
    NSCIImageRep *rep = [NSCIImageRep imageRepWithCIImage:outputImage];
    [resultImage addRepresentation:rep];

    return resultImage;
}

@end


int main(int argc, const char * argv[]) {


    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    if (argc == 1) {

        NSString * appname = [NSString stringWithFormat: @"%s", argv[0]];

        NSLog(@"Usage: %@ filename", appname);

    }  else {

        NSString * filename = [NSString stringWithFormat: @"%s", argv[1]];

        NSFileManager *fm = [NSFileManager defaultManager];

        if ([fm fileExistsAtPath:filename]) {

            NSLog(@"opening file:%@", filename);


            NSImage *img = [[NSImage alloc] initWithContentsOfFile:filename];

            [[img filterEverythingButRed]
             saveAsPNGWithName:[[filename stringByDeletingPathExtension] stringByAppendingString:@"-red.png"]];




        } else {

            NSLog(@"file not found:%@", filename);
        }

    }


    [pool release];
    return 0;
}

2 个答案:

答案 0 :(得分:1)

CISpotColor基本上可以进行四种颜色操作:

  1. inputCenterColor1附近的所有颜色替换为inputReplacementColor1
  2. inputCenterColor2附近的所有颜色替换为inputReplacementColor2
  3. inputCenterColor3附近的所有颜色替换为inputReplacementColor3
  4. 用白色替换其他所有东西。
  5. 默认情况下,输入颜色设置为各种偏红/粉红色调。您可以通过在构建代码后检查代码中的这些过滤器值并调用setDefaults来找到这些值 - 但为了便于说明,这里是Core Image Fun House示例中屏幕截图中的所有默认值代码应用程序:

    CISpotColor defaults

    使用默认选项应用过滤器可以获得:

    filtered with defaults

    请注意,红色环(您尝试成为图像中唯一剩余元素的部分)看起来像默认inputReplacementColor3,右下方的亮起区域看起来像默认inputReplacementColor2 ......就像在your output image中一样。这是因为您只配置了第一对中心/替换颜色,并且您将其他两个颜色保留为红色/粉红色默认值。

    如果要禁用第二和第三种颜色替换,请将 Closeness 参数降低到0.0和/或将 Contrast 参数调高至1.0。为安全起见,您还可以将中心颜色设置为不会出现在图像中的内容。在您的测试图片中,我发现只需关闭 Closeness 即可:

    new settings

    这会得到以下输出:

    new output

    顺便说一句,像这样的专色替换是一种简单形式的Color LookUp Table(CLUT)操作,由CIColorCube过滤器实现。如果您希望能够在CISpotColor提供的范围之外微调颜色替换,那么颜色立方体选项可能是一个不错的选择。有关于将其用于绿屏效果的教程in Apple's programming guide

    TLDR:

    1. 所有过滤器参数设置适当的值,而不仅仅是前几个,或者其他默认值可能会做您不期望的事情。将inputCloseness2inputCloseness3设置为零并将其他所有内容设置为默认值(但对于您已设置的input...1参数)似乎适用于您的测试图像。

      < / LI>
    2. 为过滤器提供实时测试环境确实可以帮助您微调参数(并确保默认值符合您的预期)。 Core Image Fun House非常适合。

答案 1 :(得分:-1)

你是对的,仍然没有文件。例如,使用“核心图像娱乐屋”作为您的信息来源不会给您一个指示,您输入点1中的颜色,以及输入点2中的哪种颜色非常重要...切换它们会给你一个结果差异很大,我在这个特殊的过滤器中发现了更多的问题。就像某些参数的“最大”值一样,代码更像是你所谓的“指南”而非实际规则。 :)使用“有趣的房子”作为一个来源,会让你失去一个有大约10个出口的兔子洞,没有迹象告诉你这些出口在哪里。

这里有一些注意事项,我通过在这个过滤器中投入疯狂的数字,看到什么“卡在墙上”。 (如果有人选择,则需要更多)

            //-------------------------  picked up a red ski helmet in full sun,  included all shadows on helmet, and some yellow from sun..  near perfect mask,  but also picked up most skin tones.
        //--------------------------  used a color from the helmet color: 0.662745 0.188235 0.223529 1.0  then stretched values to normalize to have one color 1.0 --------------
        CIFilter *clampFilter1 = [CIFilter filterWithName:@"CISpotColor"];


        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.09 blue:0.33] forKey: @"inputCenterColor1"];
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor1"];
        [clampFilter1 setValue:@(0.86) forKey: @"inputCloseness1"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast1"];

        [clampFilter1 setValue:@(0.00) forKey: @"inputCloseness2"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast2"];

        [clampFilter1 setValue:[CIColor colorWithRed:0.00 green:1.0 blue:0.56] forKey: @"inputCenterColor3"];
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0] forKey: @"inputReplacementColor3"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputCloseness3"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast3"];


        //---------------------------- picked up a blue jacket total, including all the shadows, a near perfect mask of a fairly blue jacket all ranges  ---------------------------
        [clampFilter1 setValue:[CIColor colorWithRed:0.01 green:0.165 blue:1.0] forKey: @"inputCenterColor1"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0] forKey: @"inputReplacementColor1"];
        [clampFilter1 setValue:@(0.86) forKey: @"inputCloseness1"];
        [clampFilter1 setValue:@(1.0) forKey: @"inputContrast1"];

        //---------------------------- did not need this input but experimenting and left it in to add other changes,  same with below experiments  ---------------------------
        [clampFilter1 setValue:[CIColor colorWithRed:0.01 green:0.165 blue:1.0] forKey: @"inputCenterColor2"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:0.0 blue:1.0 alpha:1.0] forKey: @"inputReplacementColor2"];
        [clampFilter1 setValue:@(0.86) forKey: @"inputCloseness2"];
        [clampFilter1 setValue:@(1.0) forKey: @"inputContrast2"];

        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.5 blue:0.5] forKey: @"inputCenterColor3"];
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0] forKey: @"inputReplacementColor3"];
        [clampFilter1 setValue:@(0.99) forKey: @"inputCloseness3"];
        [clampFilter1 setValue:@(0.99) forKey: @"inputContrast3"];



        //---------------------------- picked up all reds,  total,  including some purples,  also picked up all skin tones  -----------------------------------------------------------
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0] forKey: @"inputCenterColor1"];
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor1"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness1"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast1"];

        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0] forKey: @"inputCenterColor2"];
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor2"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness2"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast2"];

        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0] forKey: @"inputCenterColor3"];
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor3"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness3"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast3"];


        //---------------------------- removed all reds,  total,  turned all blues all ranges.. to green  -----------------------------------------------------------
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor1"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor1"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness1"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast1"];

        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor2"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor2"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness2"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast2"];

        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor3"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor3"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness3"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast3"];


        //---------------------------- removed most reds,   but skin still some tint,  turned all blues all ranges to green  -----------------------------------------------------------
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor1"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor1"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness1"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast1"];

        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor2"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor2"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness2"];
        [clampFilter1 setValue:@(0.80) forKey: @"inputContrast2"];

        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor3"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor3"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness3"];
        [clampFilter1 setValue:@(0.99) forKey: @"inputContrast3"];


        //---------------------------- picked up  shadow blue/purple replaced with green  -----------------------------------------------------------
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor1"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor1"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness1"];
        [clampFilter1 setValue:@(1.00) forKey: @"inputContrast1"];

        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0] forKey: @"inputCenterColor2"];
        [clampFilter1 setValue:[CIColor colorWithRed:0.0 green:1.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor2"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness2"];
        [clampFilter1 setValue:@(0.80) forKey: @"inputContrast2"];

        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0] forKey: @"inputCenterColor3"];
        [clampFilter1 setValue:[CIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:1.0] forKey: @"inputReplacementColor3"];
        [clampFilter1 setValue:@(0.90) forKey: @"inputCloseness3"];
        [clampFilter1 setValue:@(0.99) forKey: @"inputContrast3"];