首页 文章

CIPhotoEffect CIFilters在颜色管理方面是不变的 . 是什么让CIPhotoEffect过滤了这个属性?

提问于
浏览
1

给这个问题一些背景(ho ho):

我在iOS下继承了CIFilter,目的是创建一些自定义的照片效果滤镜 . 根据documentation,这意味着创建一个"compound"过滤器,它将一个或多个预先存在的CIF过滤器封装在我的自定义CIFilter子类的保护伞中 .

一切都很好 . 没有问题 . 举例来说,假设我封装了一个CIColorMatrix过滤器,该过滤器已经预设了某些rgba输入向量 .

在应用我的自定义过滤器(或单独使用CIColorMatrix)时,当使用带有颜色管理的CIContext时,我会看到截然不同的结果 . 我正在创建我的上下文如下:

Colour management on:

CIContext * context = [CIContext contextWithOptions:nil];

Colour management off:

NSDictionary *options = @{kCIContextWorkingColorSpace:[NSNull null], kCIContextOutputColorSpace:[NSNull null]};
CIContext * context = [CIContext contextWithOptions:options];

现在,这并不奇怪 . 但是,我注意到所有预先构建的CIPhotoEffect CIFilters,例如CIPhotoEffectInstant在相同的两种颜色管理条件下基本上是不变的 .

任何人都可以提供有关什么给他们这个属性的任何见解?例如,它们是否自己封装了可能具有类似不变性的特定CIF过滤器?

我的目标是创建一些具有相同属性的自定义过滤器,而不仅限于仅链接CIPhotoEffect过滤器 .

编辑:感谢YuAo,我已经汇集了一些工作代码示例,我在这里发布以帮助其他人:

以编程方式生成的CIColorCubeWithColorSpace CIFilter,在不同的颜色管理方案/工作色彩空间下不变:

self.filter = [CIFilter filterWithName:@"CIColorCubeWithColorSpace"];
   [self.filter setDefaults];

    int cubeDimension = 2; // Must be power of 2, max 128
    int cubeDataSize = 4 * cubeDimension * cubeDimension * cubeDimension; // bytes
    float cubeDataBytes[8*4] = {
        0.0, 0.0, 0.0, 1.0,
        0.1, 0.0, 1.0, 1.0,
        0.0, 0.5, 0.5, 1.0,
        1.0, 1.0, 0.0, 1.0,
        0.5, 0.0, 0.5, 1.0,
        1.0, 0.0, 1.0, 1.0,
        0.0, 1.0, 1.0, 1.0,
        1.0, 1.0, 1.0, 1.0
    };

    NSData *cubeData = [NSData dataWithBytes:cubeDataBytes length:cubeDataSize * sizeof(float)];

    [self.filter setValue:@(cubeDimension) forKey:@"inputCubeDimension"];
    [self.filter setValue:cubeData forKey:@"inputCubeData"];
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    [self.filter setValue:(__bridge id)colorSpace forKey:@"inputColorSpace"];
    [self.filter setValue:sourceImageCore forKey:@"inputImage"];

    CIImage *filteredImageCore = [self.filter outputImage];
    CGColorSpaceRelease(colorSpace);

文档说明:

要提供CGColorSpaceRef对象作为输入参数,请将其强制转换为id类型 . 使用默认颜色空间(null)(相当于kCGColorSpaceGenericRGBLinear),此滤镜的效果与CIColorCube的效果相同 .

我想更进一步,能够从文件中读取cubeData . 所谓的Hald颜色查找表或Hald CLUT images可用于定义从输入颜色到输出颜色的映射 .

this回答的帮助下,我也组装了代码来执行此操作,为方便起见,这里重新发布 .

基于Hald CLUT图像的CIColorCubeWithColorSpace CIFilter,在不同的颜色管理方案/工作色彩空间下不变:

用法:

NSData *cubeData = [self colorCubeDataFromLUT:@"LUTImage.png"];
    int cubeDimension = 64;

    [self.filter setValue:@(cubeDimension) forKey:@"inputCubeDimension"];
    [self.filter setValue:cubeData forKey:@"inputCubeData"];
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); // or whatever your image's colour space
    [self.filter setValue:(__bridge id)colorSpace forKey:@"inputColorSpace"];
    [self.filter setValue:sourceImageCore forKey:@"inputImage"];

帮助方法(使用Accelerate Framework):

- (nullable NSData *) colorCubeDataFromLUT:(nonnull NSString *)name
{
    UIImage *image = [UIImage imageNamed:name inBundle:[NSBundle bundleForClass:self.class] compatibleWithTraitCollection:nil];
    static const int kDimension = 64;

    if (!image) return nil;

    NSInteger width = CGImageGetWidth(image.CGImage);
    NSInteger height = CGImageGetHeight(image.CGImage);
    NSInteger rowNum = height / kDimension;
    NSInteger columnNum = width / kDimension;

    if ((width % kDimension != 0) || (height % kDimension != 0) || (rowNum * columnNum != kDimension)) {
        NSLog(@"Invalid colorLUT %@",name);
        return nil;
    }

    float *bitmap = [self createRGBABitmapFromImage:image.CGImage];
    if (bitmap == NULL) return nil;

    // Convert bitmap data written in row,column order to cube data written in x:r, y:g, z:b representation where z varies > y varies > x.
    NSInteger size = kDimension * kDimension * kDimension * sizeof(float) * 4;
    float *data = malloc(size);
    int bitmapOffset = 0;
    int z = 0;
    for (int row = 0; row <  rowNum; row++)
    {
        for (int y = 0; y < kDimension; y++)
        {
            int tmp = z;
            for (int col = 0; col < columnNum; col++) {
                NSInteger dataOffset = (z * kDimension * kDimension + y * kDimension) * 4;

                const float divider = 255.0;
                vDSP_vsdiv(&bitmap[bitmapOffset], 1, &divider, &data[dataOffset], 1, kDimension * 4); // Vector scalar divide; single precision. Divides bitmap values by 255.0 and puts them in data, processes each column (kDimension * 4 values) at once.

                bitmapOffset += kDimension * 4; // shift bitmap offset to the next set of values, each values vector has (kDimension * 4) values.
                z++;
            }
            z = tmp;
        }
        z += columnNum;
    }

    free(bitmap);

    return [NSData dataWithBytesNoCopy:data length:size freeWhenDone:YES];
}

- (float *)createRGBABitmapFromImage:(CGImageRef)image {
    CGContextRef context = NULL;
    CGColorSpaceRef colorSpace;
    unsigned char *bitmap;
    NSInteger bitmapSize;
    NSInteger bytesPerRow;

    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);

    bytesPerRow   = (width * 4);
    bitmapSize     = (bytesPerRow * height);

    bitmap = malloc( bitmapSize );
    if (bitmap == NULL) return NULL;

    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL) {
        free(bitmap);
        return NULL;
    }

    context = CGBitmapContextCreate (bitmap,
                                     width,
                                     height,
                                     8,
                                     bytesPerRow,
                                     colorSpace,
                                     (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease( colorSpace );

    if (context == NULL) {
        free (bitmap);
        return NULL;
    }

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
    CGContextRelease(context);

    float *convertedBitmap = malloc(bitmapSize * sizeof(float));
    vDSP_vfltu8(bitmap, 1, convertedBitmap, 1, bitmapSize); // Converts an array of unsigned 8-bit integers to single-precision floating-point values.
    free(bitmap);

    return convertedBitmap;
}

可以通过获得身份图像(Google!)然后向其应用相同的图像处理链来创建Hald CLUT图像,该图像处理链应用于用于在任何图像编辑程序中可视化“外观”的图像 . 只需确保将示例代码中的cubeDimension设置为LUT图像的正确尺寸 . 如果尺寸d是沿3D LUT立方体一侧的元素数量,则Hald CLUT图像宽度和高度将是d * sqrt(d)像素,并且图像将具有d ^ 3个总像素 .

2 回答

  • 1

    以下是CIPhotoEffect / CIColorCubeWithColorSpace应该如何使用颜色管理而不是关闭 .

    CI管理应该执行以下颜色管理:

    • 从输入空间到立方体空间的颜色匹配 . 如果这两者相等,这就是一个noop .

    • 应用颜色立方体 .

    • 从立方体空间到输出空间的颜色匹配 . 如果这两者相等,这就是一个noop .

    关于颜色管理关闭这是CI应该做的事情:

    • 应用颜色立方体 .
  • 0

    CIPhotoEffect 内部使用 CIColorCubeWithColorSpace 过滤器 .

    所有颜色立方体数据都存储在 CoreImage.framework 中 .

    你可以在这里找到模拟器的 CoreImage.framework/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreImage.framework/ ) .

    颜色立方体数据以 scube 路径扩展名命名 . 例如 CIPhotoEffectChrome.scube

    CIColorCubeWithColorSpace 内部隐藏颜色立方体颜色值,以使用私有方法匹配当前核心图像上下文的工作颜色空间: -[CIImage _imageByMatchingWorkingSpaceToColorSpace:]; -[CIImage _imageByMatchingColorSpaceToWorkingSpace:];

相关问题