ios - CIPhotoEffect CIFilter在颜色管理方面是不变的。是什么赋予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本质上是不变的。

任何人都可以对什么赋予他们这种 property 提供任何见解?例如,它们本身是否封装了可能以相似不变性应用的特定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);

文档状态:

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

我想走得更远,并且能够从文件中读取cubeData。所谓的Hald Color查找表或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;
}

可以通过获取身份图像(Google!),然后向其应用与用于在任何图像编辑程序中可视化“外观”的图像相同的图像处理链,来创建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/Library/Frameworks/CoreImage.framework/)。

颜色立方体数据以scube路径扩展名命名。例如CIPhotoEffectChrome.scubeCIColorCubeWithColorSpace通过使用 private 方法在内部隐瞒颜色立方体的颜色值以匹配当前核心图像上下文的工作颜色空间: -[CIImage _imageByMatchingWorkingSpaceToColorSpace:]; -[CIImage _imageByMatchingColorSpaceToWorkingSpace:];

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

相关文章:

ios - 如何使用TableView删除核心数据中的数据

ios - 如何根据UITableview的行数来调整UIScrollView/ContainerView的高度?

ios - CALayer-setAnimationDuration不起作用

ios - 有没有一种方法可以模拟一段时间内的多个位置,例如设备上的“City Run”?

ios - 尝试在 iOS 中使用单例模式重构网络方法

ios - 如何从TextField中获取十进制值并在Objective C中进行计算

python - 我如何使用Python openCV在表格的这张图片中找到左上角框的位置(x,y,宽度,高度)?

image-processing - 在视频输入库或 OpenCV 中禁用自动对焦

ios - 防止 UITableViewCells 出队和重新加载

iOS:将 RGB 滤镜应用于灰度 PNG