ios - CIPhotoEffect CIFilters 在颜色管理方面是不变的。是什么赋予了 CIPhotoEffect 滤镜这个属性?

标签 ios objective-c image-processing core-image cifilter

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

为了创建一些自定义照片效果滤镜,我在 iOS 下将 CIFilter 子类化。根据 documentation ,这意味着创建一个“复合”过滤器,将一个或多个预先存在的 CIFilter 封装在我的自定义 CIFilter 子类的保护伞内。

一切都很好。那里没有问题。举个例子,假设我封装了一个 CIColorMatrix 滤镜,该滤镜已使用某些 rgba 输入向量进行了预设。

当应用我的自定义过滤器(或实际上单独使用 CIColorMatrix)时,我看到在使用颜色管理开启和关闭的 CIContext 时截然不同的结果。我正在按如下方式创建我的上下文:

颜色管理:

CIContext * context = [CIContext contextWithOptions:nil];

关闭颜色管理:

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

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

任何人都可以提供任何关于是什么赋予他们这种属性的见解吗?例如,它们本身是否封装了可能具有类似不变性的特定 CIFilter?

我的目标是创建一些具有相同属性的自定义滤镜,而不限于仅链接 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);

文档状态:

To provide a CGColorSpaceRef object as the input parameter, cast it to type id. With the default color space (null), which is equivalent to kCGColorSpaceGenericRGBLinear, this filter’s effect is identical to that of CIColorCube.

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

this 的帮助下答案,我也组装了代码来做到这一点,为方便起见转贴在这里。

基于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;
}

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

最佳答案

CIPhotoEffect 内部使用 CIColorCubeWithColorSpace 过滤器。

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

您可以在此处找到模拟器的 CoreImage.framework (/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/库/Frameworks/CoreImage.framework/).

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

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

关于ios - CIPhotoEffect CIFilters 在颜色管理方面是不变的。是什么赋予了 CIPhotoEffect 滤镜这个属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35049596/

相关文章:

python - 树莓派 - OCR 仪表中的数字

iphone - 在 UIViewController 中在哪里初始化一些东西

ios - 如何在 UITableView Cell 中以编程方式创建 n 个 UIButton?

ios - iOS 设备是否可以作为 iBeacon 进行广播并同时监视 iBeacon?

ios - AVAudioPlayer 没有从临时文件路径播放声音

ios - MicrosoftMaps,如何使用 Bing map 框架在 Appstore 上上传应用程序时解决 "Unexpected CFBundleExecutable Key"错误

objective-c - 提示在 cocoa 应用程序中进行根访问

java - java中用opencv绘制的图像直方图

iphone - 使用 OpenGL ES 应用亮度和对比度

ios - 简单的UIImageView幻灯片IOS计时器问题