【问题标题】:How do you effectively color balance an image using Core Image filters?您如何使用 Core Image 滤镜有效地平衡图像?
【发布时间】:2015-08-02 11:33:48
【问题描述】:

我正在尝试使用 Core Image 滤镜为我的 Mac 应用中的图像添加一些颜色校正。首先,我正在考虑允许自定义白平衡,以从图像中移除色偏。看起来CIWhitePointAdjust 正是我要找的东西,但是在尝试之后,我不确定是我用错了,还是它没有按照我的想法做。

从这张图片开始,随着年龄的增长变黄,右边缘有一条细白条:

我应用过滤器,如下所示:

NSColor *whiteSample = // Chosen from the speech bubble's background
CIColor *whiteInputColor = [[CIColor alloc] initWithColor:whiteSample];

layer.filters = @[
    [CIFilter filterWithName:@"CIWhitePointAdjust"
         withInputParameters:@{kCIInputColorKey: whiteInputColor}]
];

并取回这张图片:

请注意,它看起来比原版更暗更黄(与我想要的效果相反)。我一直期待效果更像是在 Photoshop 中执行 Auto Color,如下所示:

我是在错误地使用CIWhitePointAdjust,还是使用错误的工具来完成这项工作?如果另一个过滤器或过滤器组合会更好,我很想知道。

由于我正在处理已经存在于 CALayer 对象中的图像,Core Image 过滤器似乎是正确的选择,但如果这只能通过其他方式可行,我愿意接受。

更新

信号处理网站上的helpful answer 告诉我我正在尝试实现的名称。从广义上讲,它被称为直方图均衡。我试图弄清楚是否有一种方法可以使用 Core Image 过滤器来执行该过程,到目前为止,它看起来并没有太大希望(没有我自己写的)。

【问题讨论】:

  • @matt 谢谢,但我已经尝试过了,它似乎太繁琐了。如果不调整色调,色温调整似乎没有任何作用,即使使用色调我也无法让图像看起来正确,而且我不想给用户一个色调滑块。它旨在对照片进行实际的色温调整,这不是我的目标。
  • 您是否尝试过使用 CIColorControls 对图像进行去饱和处理?
  • @joelg 谢谢,但去饱和无济于事。我想让用户更正颜色,而不是删除颜色(在任何程度上)
  • 你能单独看一下 R G B 吗?重新归一化 R G B 。您可能会发现其中一个的价差可能很小(0 到 100),因此将(0 到 100)标准化为(0 到 255)或某个值而不是 255。

标签: cocoa core-image


【解决方案1】:

斯威夫特 5

import Accelerate
import Metal

extension UIImage {

func whiteBalance() -> UIImage? {
    
    // Create a vImage_Buffer from the CGImage
    
    guard let sourceRef = cgImage else { return nil }
    
    var srcBuffer = vImage_Buffer()
    
    var format = vImage_CGImageFormat(bitsPerComponent: 8,
                                      bitsPerPixel: 32,
                                      colorSpace: nil,
                                      bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.first.rawValue),
                                      version: 0,
                                      decode: nil,
                                      renderingIntent: .defaultIntent)
    
    // init cgImage from sourceBuffer
    
    var err = vImageBuffer_InitWithCGImage(&srcBuffer, &format, nil, sourceRef, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        return nil
    }
    
    // Create dest buffers
            
    let dstWidth = sourceRef.width
    let dstHeight = sourceRef.height
            
    let pixelBits : UInt32 = UInt32(8*MemoryLayout<__uint8_t>.size)

    var dstA : vImage_Buffer = vImage_Buffer.init()
    var dstR : vImage_Buffer = vImage_Buffer.init()
    var dstG : vImage_Buffer = vImage_Buffer.init()
    var dstB : vImage_Buffer = vImage_Buffer.init()
                    
    // dstA - Alpha
                    
    err = vImageBuffer_Init(&dstA, UInt(dstHeight), UInt(dstWidth), pixelBits, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageBuffer_Init (alpha) error: \(String(describing: err))")
        return nil
    }
    
    // dstR - Red
    
    err = vImageBuffer_Init(&dstR, UInt(dstHeight), UInt(dstWidth), pixelBits, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageBuffer_Init (red) error: \(String(describing: err))")
        return nil
    }
    
    // dstG - Green
    
    err = vImageBuffer_Init(&dstG, UInt(dstHeight), UInt(dstWidth), pixelBits, vImage_Flags(kvImageNoFlags))

    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageBuffer_Init (green) error: \(String(describing: err))")
        return nil
    }
    
    // _dstB - Blue
    
    err = vImageBuffer_Init(&dstB, UInt(dstHeight), UInt(dstWidth), pixelBits, vImage_Flags(kvImageNoFlags))

    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageBuffer_Init (blue) error: \(String(describing: err))")
        return nil
    }

    // convert to planar8
    
    err = vImageConvert_ARGB8888toPlanar8(&srcBuffer, &dstA, &dstR, &dstG, &dstB, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageConvert_ARGB8888toPlanar8 error: \(String(describing: err))")
        return nil
    }
    
    // equalize red

    err = vImageEqualization_Planar8(&dstR, &dstR, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageEqualization_Planar8 (red) error: \(String(describing: err))")
        return nil
    }

    // equalize green

    err = vImageEqualization_Planar8(&dstG, &dstG, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageEqualization_Planar8 (green) error: \(String(describing: err))")
        return nil
    }

    // equalize blue

    err = vImageEqualization_Planar8(&dstB, &dstB, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageEqualization_Planar8 (blue) error: \(String(describing: err))")
        return nil
    }

    // planar8 to ARGB8888

    err = vImageConvert_Planar8toARGB8888(&dstA, &dstR, &dstG, &dstB, &srcBuffer, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageConvert_Planar8toARGB8888 error: \(String(describing: err))")
        return nil
    }

    // contrast stretch

    err = vImageContrastStretch_ARGB8888( &srcBuffer, &srcBuffer, vImage_Flags(kvImageNoFlags))
    
    guard err == kvImageNoError else {
        free(srcBuffer.data)
        print("vImageContrastStretch_ARGB8888 error: \(String(describing: err))")
        return nil
    }

    // free buffers

    free(dstA.data)
    free(dstR.data)
    free(dstG.data)
    free(dstB.data)
    
    // Create CGImage from vImage_Buffer
    
    guard let result = vImageCreateCGImageFromBuffer(&srcBuffer, &format, nil, nil, vImage_Flags(kvImageNoAllocate), &err)?.takeRetainedValue() else {
        
        print("vImageCreateCGImageFromBuffer error: \(err))")

        return nil
    }
    
    //free(srcBuffer.data)

    // Create UIImage
    
    let destImage = UIImage(cgImage: result, scale: 0.0, orientation: imageOrientation)
    
    return destImage
    
}
}

【讨论】:

    【解决方案2】:

    好的,所以我想我已经成功了!以下是我从https://stackoverflow.com/a/30447041/734860 和一些粘合位中拼凑出来的内容:

    @import Accelerator;
    
    // I haven't yet found a way to do it within Core Image
    
    // use this when you need to CIImage* -> CGImageRef
    CIImage *ciImage = [CIImage imageWithCGImage:cgImage];
    
    ...
    
    // use this when you need to CGImageRef -> CIImage*
    CIContext* context = [[CIContext alloc] init];
    CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent];
    
    ...
    
    // the algorithm itself, which uses vImage and has to convert to/from it via CGImage
    CGImageRef CreateEqualisedCGImageFromCGImage(CGImageRef original)
    {
        vImage_Error err;
        vImage_Buffer _img;
        vImage_CGImageFormat format = {
            .bitsPerComponent = 8,
            .bitsPerPixel = 32,
            .colorSpace = NULL,
            .bitmapInfo = (CGBitmapInfo)kCGImageAlphaFirst,
            .version = 0,
            .decode = NULL,
            .renderingIntent = kCGRenderingIntentDefault,
        };
    
        CGFloat width = CGImageGetWidth(original);
        CGFloat height = CGImageGetHeight(original);
    
        vImage_Buffer _dstA, _dstR, _dstG, _dstB;
    
        err = vImageBuffer_InitWithCGImage(&_img, &format, NULL, original, kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageBuffer_InitWithCGImage error: %ld", err);
    
        err = vImageBuffer_Init( &_dstA, height, width, 8 * sizeof( uint8_t ), kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageBuffer_Init (alpha) error: %ld", err);
    
        err = vImageBuffer_Init( &_dstR, height, width, 8 * sizeof( uint8_t ), kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageBuffer_Init (red) error: %ld", err);
    
        err = vImageBuffer_Init( &_dstG, height, width, 8 * sizeof( uint8_t ), kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageBuffer_Init (green) error: %ld", err);
    
        err = vImageBuffer_Init( &_dstB, height, width, 8 * sizeof( uint8_t ), kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageBuffer_Init (blue) error: %ld", err);
    
        err = vImageConvert_ARGB8888toPlanar8(&_img, &_dstA, &_dstR, &_dstG, &_dstB, kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageConvert_ARGB8888toPlanar8 error: %ld", err);
    
        err = vImageEqualization_Planar8(&_dstR, &_dstR, kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageEqualization_Planar8 (red) error: %ld", err);
    
        err = vImageEqualization_Planar8(&_dstG, &_dstG, kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageEqualization_Planar8 (green) error: %ld", err);
    
        err = vImageEqualization_Planar8(&_dstB, &_dstB, kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageEqualization_Planar8 (blue) error: %ld", err);
    
        err = vImageConvert_Planar8toARGB8888(&_dstA, &_dstR, &_dstG, &_dstB, &_img, kvImageNoFlags);
        if (err != kvImageNoError)
            NSLog(@"vImageConvert_Planar8toARGB8888 error: %ld", err);
    
        err = vImageContrastStretch_ARGB8888( &_img, &_img, kvImageNoError );
        if (err != kvImageNoError)
            NSLog(@"vImageContrastStretch_ARGB8888 error: %ld", err);
    
        free(_dstA.data);
        free(_dstR.data);
        free(_dstG.data);
        free(_dstB.data);
    
        CGImageRef result = vImageCreateCGImageFromBuffer(&_img, &format, NULL, NULL, kvImageNoFlags, &err);
        if (err != kvImageNoError)
            NSLog(@"vImageCreateCGImageFromBuffer error: %ld", err);
    
        free(_img.data);
    
        return result;
    }
    

    该解决方案的优点都归于https://stackoverflow.com/users/4735340/james-bush,但我一直在寻找面向图像的解决方案(与该问题中讨论的视频处理相反)都没有成功,我认为这是一个现成的答复比如这个是相关的。在 OS X 或 iOS 上寻找“AutoLevels”让我无处可去,我希望这对其他人有用。

    【讨论】:

    • 请分享 swift 版本
    【解决方案3】:

    尝试使用autoAdjustmentFilters or autoAdjustmentFiltersWithOptions 获取可能的过滤器列表...可能是后者,以便您可以排除需要面部检测的过滤器,如果您打算自动调整卡通扫描。

    【讨论】:

    • 这是个好主意(我真的希望它会起作用),但我在我正在使用的测试图像上尝试了它,它看起来像是调整了整体水平,但没有返回任何滤镜以更改色彩平衡。具体来说,它返回以下过滤器:CIVibranceCIToneCurveCIHighlightShadowAdjust)。这令人失望:-(
    猜你喜欢
    • 2012-11-12
    • 1970-01-01
    • 2015-11-11
    • 1970-01-01
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 2019-01-28
    相关资源
    最近更新 更多