UIImage 使用 vImage 进行高斯模糊

问题描述 投票:0回答:3

我正在尝试找到一种更快的方法来生成

Gaussian Blur
图像,这个博客适用于大多数图像。

但是当图像具有透明背景色时,模糊的图像看起来很糟糕

下面的代码是从博客复制的:

-(UIImage *)vImageBlurWithNumber:(CGFloat)blur
{
    if (blur < 0.f || blur > 1.f) {
        blur = 0.5f;
    }
    int boxSize = (int)(blur * 100);
    boxSize = boxSize - (boxSize % 2) + 1;

    CGImageRef img = self.CGImage;

    vImage_Buffer inBuffer, outBuffer;
    vImage_Error error;

    void *pixelBuffer;

    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);

    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

    pixelBuffer = malloc(CGImageGetBytesPerRow(img) *
                     CGImageGetHeight(img));

    if(pixelBuffer == NULL)
        NSLog(@"No pixelbuffer");

    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);

    // may be i should modify last 2 parameter below ,how ?
    error = vImageBoxConvolve_ARGB8888(&inBuffer,
                                   &outBuffer,
                                   NULL,
                                   0,
                                   0,
                                   boxSize,
                                   boxSize,
                                   NULL,
                                   kvImageEdgeExtend); //        kvImageBackgroundColorFill ?



    if (error) {
        NSLog(@"error from convolution %ld", error);
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef ctx = CGBitmapContextCreate(
                                         outBuffer.data,
                                         outBuffer.width,
                                         outBuffer.height,
                                         8,
                                         outBuffer.rowBytes,
                                         colorSpace,
                                         kCGImageAlphaNoneSkipLast);
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];

    //clean up
//    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);

    free(pixelBuffer);
    CFRelease(inBitmapData);

    CGColorSpaceRelease(colorSpace);
    CGImageRelease(imageRef);

    return returnImage;
}

我尝试了另一种方法来制作

Gaussian Blur
效果,
通过使用
Apple WWDC 2013 UIImage-ImageEffects
类别,
但这个类别的效果更像是
Frosted glass
而不是
Gaussian Blur

Blur in Core Image
工作正常,但比 vImage 方式慢。

GPUImage
也比vImage方式慢。

请帮我修改上面的vImage代码, 我已经尝试了很多,并用演示发布了代码here;

:)

ios blur gaussian core-image vimage
3个回答
0
投票

看看它有什么不好的地方会很有用。

如果只是透明区域附近的区域,您可以尝试先对图像进行预乘。这将防止完全透明区域中无意义的颜色渗透到图像的其余部分。


0
投票

你也可以这样做:

var noAlpha = CIVector(x: 0, y: 0, z: 0, w: 1)

image.imageByApplyingFilter("CIColorMatrix", 
    withInputParameters: ["inputAVector": noAlpha])

摆脱阿尔法。如果您需要与其他

CIFilter
链接,这会更有效。


0
投票

如果输入数据未预乘,模糊可能看起来很糟糕。因此,CG 更喜欢预乘数据,并且其上下文不能设为非预乘,但某些图像提供者未预乘,因此数据可以采用多种方式。从源图像中获得的内容取决于图像格式,这就是为什么您需要查看图像的 Alpha 信息。

问题发生在非不透明非预乘像素上,一些颜色将被平均到其周围的像素中。但是,如果像素完全透明,则其颜色信息毫无意义,并且可以在其中编码任何内容。 (有些艺术家不遗余力地用洋红色填充这些区域!)如果它部分不透明,那么颜色只是被过度呈现。解决办法是先对数据进行预乘。这可以确保透明像素在其邻居的结果颜色中不会出现过多的情况,并且完全透明的像素根本不会产生任何影响。

预乘可能会在卷积之前插入,如下所示:

CGImageAlphaInfo alpha = CGImageGetAlphaInfo(img);
vImage_Buffer convolutionSrcBufffer = inBuffer;
switch(alpha)
{
    case kCGImageAlphaLast:
         vImageBuffer_Init( &convolutionSrcBuffer, inBuffer.height, inBuffer.width, 32, kvImageNoFlags );
         vImagePremultiplyData_RGBA8888( &inBuffer, & convolutionSrcBuffer, kvImageNoFlags);
         alpha = kCGImageAlphaPremultipliedLast;
         break;
    case kCGImageAlphaFirst:
         vImageBuffer_Init( &convolutionSrcBuffer, inBuffer.height, inBuffer.width, 32, kvImageNoFlags );
         vImagePremultiplyData_ARGB8888( &inBuffer, & convolutionSrcBuffer, kvImageNoFlags);
         alpha = kCGImageAlphaPremultipliedFirst;
         break;
    default:
         // do nothing
         break;
}

...

error = vImageBoxConvolve_ARGB8888(&convolutionSrcBufffer,
                               &outBuffer,
                               NULL,
                               0,
                               0,
                               boxSize,
                               boxSize,
                               NULL,
                               kvImageEdgeExtend); //        kvImageBackgroundColorFill ?
if( convolutionSrcBuffer.data != inBuffer.data)
    free(convolutionSrcBuffer.data);

代码中的另一个错误来源是您可能采用了非预乘或预乘图像并将其放入不透明的 kCGImageAlphaNoneSkipLast 上下文中,导致上下文将透明图像视为不透明,即使它不是不透明的。您获得的图像可能无法正确绘制。因此,请使用图像内容的实际 alpha 值来代替:

...

CGContextRef ctx = CGBitmapContextCreate(
                                     outBuffer.data,
                                     outBuffer.width,
                                     outBuffer.height,
                                     8,
                                     outBuffer.rowBytes,
                                     colorSpace,  // <<== wrong colorspace!
                                     alpha);  // <<=== changed this line

请注意,CGImageContextRef 对于接受哪个 alpha 非常挑剔,因此上述方法在某些情况下会失败。通过使用 vImageCreateCGImageFromBuffer() 可以避免这些令人头疼的问题。另外,当我们这样做时,如果您使用 vImageBuffer_InitWithCGImage() 获取了图像,我们可以首先要求数据采用预乘格式,并且如果我们愿意,还可以进行其他细微调整,例如首先/最后的 alpha。

一些图像被旋转。你的代码无法处理这个问题。

你还扔掉了图像色彩空间!使用源图像中的图像,而不是设备 RGB。即使其他一切都是正确的,这仍然会让事情看起来很糟糕。

为了完整起见,我应该指出框卷积并不是高斯模糊。如果你运行过滤器三四次,它就会开始看起来像一次。 https://nghiaho.com/?p=1159。如果您打算这样做,您可以尝试使用帐篷模糊,这就像运行两次盒子模糊一样,但效率更高一些。

© www.soinside.com 2019 - 2024. All rights reserved.