ios CoreImage CIHueAdjust滤镜给出错误颜色

wsewodh2  于 2023-05-08  发布在  iOS
关注(0)|答案(3)|浏览(198)

我正在使用Dondragmer的“Rotated Hue”示例中的代码来解决这个问题:How to programmatically change the hue of UIImage?
然而,我得到的结果与我预期的非常不同。我希望色调的变化给予类似的结果,改变色调在Photoshop中,但变化是不是在所有相似。
为了说明,使用此源图像:

Photoshop中的pi/4旋转给出:

使用Dondragmer的代码,我得到:

同样,在Photoshop中旋转180度可以得到:

但是CIHueAdjust过滤器产生:

我的代码:

- (CGImageRef)changeHueOfImage(CGImageRef)source By:(NSInteger)angle
{
    CIImage *image = [CIImage imageWithCGImage:source];

    // Convert degrees to radians
    CGFloat angleRadians = GLKMathDegreesToRadians(angle);

    // Use the Core Image CIHueAdjust filter to change the hue
    CIFilter *hueFilter = [CIFilter filterWithName:@"CIHueAdjust"];
    [hueFilter setDefaults];
    [hueFilter setValue:image forKey:@"inputImage"];
    [hueFilter setValue:[NSNumber numberWithFloat:angleRadians] forKey:@"inputAngle"];
    image = [hueFilter outputImage];

    // Save the modified image
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef result = [context createCGImage:image fromRect:[image extent]];

    return result;
}

我的问题:
1.我是否误解了CIHueAdjust过滤器的作用?
1.我是否需要考虑如何将亮度和饱和度因素纳入过滤器?
1.如何复制Photoshop行为
1.总的来说,为什么结果如此不同?

0mkxixxg

0mkxixxg1#

我在试图更新CIImage的亮度时遇到了类似的问题。问题是CoreImageRGB空间中工作,修改色调,饱和度或亮度应该在HSV空间中完成。
最后我用here这个代码片段来操作每个像素。可能有更好的方法,但我用每个像素的RGB值创建了一个UIColor,从该颜色中获取HSV值,更新了我想要更新的组件,创建了一个新的UIColor并使用该颜色的RGB值来修改图像。

CGImageRef imageRef = [img CGImage];
uint width = CGImageGetWidth(imageRef);
uint height = CGImageGetHeight(imageRef);
unsigned char *pixels = malloc(height*width*4); //1d array with size for every pixel. Each pixel has the components: Red,Green,Blue,Alpha

CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, 4*width, colorSpaceRef, kCGImageAlphaPremultipliedLast); //our quartz2d drawing env
CGColorSpaceRelease(colorSpaceRef);

CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

for (int y=0;y<height;++y){
    for (int x=0;x<width;++x){
        int idx = (width*y+x)*4; //the index of pixel(x,y) in the 1d array pixels

        //Pixel manipulation here

        //red = pixels[idx]
        //green = piexls[idx+1]
        //blue = pixels[idx+2]
        //alpha = pixels[idx+3]

        //pixels[idx] = NEW_RED_VALUE

        //Please note that this assumes an image format with alpha stored in the least significant bit.
        //See kCGImageAlphaPremultipliedLast for more info. 
        //Change if needed and also update bitmapInfo provided to CGBitmapContextCreate
    }
}

imageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
free(pixels);

//load our new image
UIImage* newImg = [UIImage imageWithCGImage:imageRef];

**注意:**我没有写这段代码,I found it here,并粘贴在这里,以防要点被删除。All credit to bjorndagerman@github

如果你自己实现RGB -> HSV -> RGB转换,你可能会得到更好的性能,如果你试图每秒多次执行这个过滤器,你会看到性能下降,但这是我最好的建议,我找不到一种方法来修改色调,饱和度或亮度在HSV空间与CoreImage

更新:正如@dfd在评论中提到的,你也可以为一个过滤器编写一个自定义内核来进行这些计算,这会快得多,但你需要谷歌如何将RGB转换为HSV并返回。

fkaflof6

fkaflof62#

我从来没有找到我的问题的答案:为什么CIHueAdjust没有给予预期的结果。但我确实找到了替代代码,它给了我想要的结果:

#define Mask8(x) ( (x) & 0xFF )
#define R(x) ( Mask8(x) )
#define G(x) ( Mask8(x >> 8 ) )
#define B(x) ( Mask8(x >> 16) )
#define A(x) ( Mask8(x >> 24) )
#define RGBAMake(r, g, b, a) ( Mask8(r) | Mask8(g) << 8 | Mask8(b) << 16 | Mask8(a) << 24 )

- (CGImageRef)changeHueForImage:(CGImageRef)image by:(NSInteger)angle
{
    // Get size and allocate array of UIColors
    NSInteger width = CGImageGetWidth(image);
    NSInteger height = CGImageGetHeight(image);
    NSInteger count = width * height;

    // Draw image into pixel buffer
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    NSInteger bytesPerPixel = 4;
    NSInteger bitsPerComponent = 8;

    NSInteger bytesPerRow = bytesPerPixel * width;

    UInt32 *bitmapPixels = (UInt32 *)calloc(count, sizeof(UInt32));

    CGContextRef context = CGBitmapContextCreate(bitmapPixels, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

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

    // Shift hue of every pixel
    for (int i = 0; i < count; i++) {
        // Get pixel
        UInt32 pixel = bitmapPixels[i];

        // Convert to HSBA
        UIColor *color = [UIColor colorWithRed:R(pixel)/255.0 green:G(pixel)/255.0 blue:B(pixel)/255.0 alpha:A(pixel)/255.0];
        CGFloat h, s, br, a;
        [color getHue:&h saturation:&s brightness:&br alpha:&a];

        // Shift by angle
        CGFloat angleRadians = GLKMathDegreesToRadians(angle);
        angleRadians /= 2.0f*M_PI;
        h += angleRadians;
        if (h > 1.0f || h < 0.0f) {
            h -= floor(h);
        }

        // Make new color
        UIColor *newColor = [UIColor colorWithHue:h saturation:s brightness:br alpha:a];

        // Convert back to RGBA
        CGFloat r, g, b;
        [newColor getRed:&r green:&g blue:&b alpha:&a];

        // Set pixel
        bitmapPixels[i] = RGBAMake((UInt32)(r * 255.0), (UInt32)(g * 255.0), (UInt32)(b * 255.0), (UInt32)(a * 255.0));
    }

    // Create the new image and clean up
    CGImageRef newImg = CGBitmapContextCreateImage(context);
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);
    free(bitmapPixels);

    return newImg;
 }

我对这个解决方案并不满意;特别地,几乎可以肯定的是,存在比依赖于为每个像素生成新的UIColor更有效的东西。但它会像预期的那样转移图像的色调。

vc9ivgsu

vc9ivgsu3#

色调的输入参数以弧度表示。

下面的代码允许您输入一个从0. 0到1. 0的浮点数,表示0到360度。

extension UIImage {

    // CIHueAdjust
    
    func withHueAdjustment(hue: CGFloat) -> UIImage {
        
        // angle
        
        let angle = deg2rad(hue * 360)
                
        // cgImage
        
        guard let cgImage = self.cgImage else { return self }
    
        // filter
        
        guard let filter = CIFilter(name: "CIHueAdjust") else { return self }
    
        filter.setValue(CIImage(cgImage: cgImage), forKey: kCIInputImageKey)
        
        // Hue
        
        filter.setValue(angle, forKey: kCIInputAngleKey)
    
        // result
        
        guard let result = filter.value(forKey: kCIOutputImageKey) as? CIImage else { return self }
    
        guard let newCgImage = CIContext(options: nil).createCGImage(result, from: result.extent) else { return self }
    
        return UIImage(cgImage: newCgImage, scale: self.scale, orientation: imageOrientation)
    
    }
    
    func deg2rad(_ number: CGFloat) -> CGFloat {
        
        return (number * .pi) / 180
    
    }

}

用法

let myImage = UIImage.init(named: "someImage")
    
    let hueAdjustedImage = myImage?.withHueAdjustment(hue: 0.4)

相关问题