更新:请参阅下面的附加问题以及更多代码;
我正在尝试编写用于模糊图像的类别。我的出发点是Jeff LaMarche 的示例。虽然这(在其他人建议的修复之后)工作正常,但对于我的要求来说它太慢了一个数量级 - 在 3GS 上,可能需要 3 秒才能做一个像样的模糊,我想把它降到 0.5 以下秒全屏(越快越好)。
他提到 Accelerate 框架是一种性能增强,所以我花了最后一天研究这个,特别是根据 Apple 文档的 vDSP_f3x3
通过使用 3x3 内核执行二维卷积来过滤图像;单精度。
完美 - 我有一个合适的过滤器矩阵,我有一个图像......但这就是我难过的地方。
vDSP_f3x3 假设图像数据是 (float *) 但我的图像来自;
srcData = (unsigned char *)CGBitmapContextGetData (context);
上下文来自 CGBitmapContextCreate 和 kCGImageAlphaPremultipliedFirst,所以我的 srcData 实际上是 ARGB,每个组件有 8 位。
我怀疑我真正需要的是带有浮动组件的上下文,但根据这里的 Quartz 文档,kCGBitMapFloatComponents 仅在 Mac OS 上可用,在 iOS 上不可用:-(
有没有一种使用加速框架将我拥有的整数分量转换为 vDSP_f3x3 需要的浮点分量的快速方法?我的意思是我可以自己做,但是当我这样做时,然后是卷积,然后再转换回来,我怀疑我会让它变得比现在更慢,因为我可以边做边做卷积。
也许我有错误的方法?
有人对我使用 vDSP 在 iphone 上进行了一些图像处理有什么建议吗?我可以找到的文档非常面向参考,并且在涉及此类事情时对新手不太友好。
如果有人有关于真正快速模糊的参考(和高质量,而不是降低分辨率,然后重新缩放我见过的看起来很裤子的东西),那就太棒了!
编辑:
谢谢@Jason。我已经这样做了,它几乎可以工作了,但现在我的问题是,虽然图像确实模糊,但在每次调用时它都会向左移动 1 个像素。它似乎也使图像变成黑白的,但这可能是别的东西。
这段代码中有什么明显不正确的地方吗?我还没有优化它,它有点粗糙,但希望卷积代码足够清晰。
CGImageRef CreateCGImageByBlurringImage(CGImageRef inImage, NSUInteger pixelRadius, NSUInteger gaussFactor)
{
unsigned char *srcData, *finalData;
CGContextRef context = CreateARGBBitmapContext(inImage);
if (context == NULL)
return NULL;
size_t width = CGBitmapContextGetWidth(context);
size_t height = CGBitmapContextGetHeight(context);
size_t bpr = CGBitmapContextGetBytesPerRow(context);
int componentsPerPixel = 4; // ARGB
CGRect rect = {{0,0},{width,height}};
CGContextDrawImage(context, rect, inImage);
// Now we can get a pointer to the image data associated with the bitmap
// context.
srcData = (unsigned char *)CGBitmapContextGetData (context);
if (srcData != NULL)
{
size_t dataSize = bpr * height;
finalData = malloc(dataSize);
memcpy(finalData, srcData, dataSize);
//Generate Gaussian kernel
float *kernel;
// Limit the pixelRadius
pixelRadius = MIN(MAX(1,pixelRadius), 248);
int kernelSize = pixelRadius * 2 + 1;
kernel = malloc(kernelSize * sizeof *kernel);
int gauss_sum =0;
for (int i = 0; i < pixelRadius; i++)
{
kernel[i] = 1 + (gaussFactor*i);
kernel[kernelSize - (i + 1)] = 1 + (gaussFactor * i);
gauss_sum += (kernel[i] + kernel[kernelSize - (i + 1)]);
}
kernel[(kernelSize - 1)/2] = 1 + (gaussFactor*pixelRadius);
gauss_sum += kernel[(kernelSize-1)/2];
// Scale the kernel
for (int i=0; i<kernelSize; ++i) {
kernel[i] = kernel[i]/gauss_sum;
}
float * srcAsFloat,* resultAsFloat;
srcAsFloat = malloc(width*height*sizeof(float)*componentsPerPixel);
resultAsFloat = malloc(width*height*sizeof(float)*componentsPerPixel);
// Convert uint source ARGB to floats
vDSP_vfltu8(srcData,1,srcAsFloat,1,width*height*componentsPerPixel);
// Convolve (hence the -1) with the kernel
vDSP_conv(srcAsFloat, 1, &kernel[kernelSize-1],-1, resultAsFloat, 1, width*height*componentsPerPixel, kernelSize);
// Copy the floats back to ints
vDSP_vfixu8(resultAsFloat, 1, finalData, 1, width*height*componentsPerPixel);
free(resultAsFloat);
free(srcAsFloat);
}
size_t bitmapByteCount = bpr * height;
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, finalData, bitmapByteCount, &providerRelease);
CGImageRef cgImage = CGImageCreate(width, height, CGBitmapContextGetBitsPerComponent(context),
CGBitmapContextGetBitsPerPixel(context), CGBitmapContextGetBytesPerRow(context), CGBitmapContextGetColorSpace(context), CGBitmapContextGetBitmapInfo(context),
dataProvider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
CGContextRelease(context);
return cgImage;
}
我应该补充一点,如果我注释掉 vDSP_conv 行,并将下面的行更改为;
vDSP_vfixu8(srcAsFloat, 1, finalData, 1, width*height*componentsPerPixel);
然后正如预期的那样,我的结果是原始来源的克隆。在颜色上而不是向左移动。这对我来说意味着这是错误的卷积,但我看不出在哪里:-(
想法:实际上考虑到这一点,在我看来,卷积需要知道输入像素是 ARGB 格式,否则卷积会将这些值相乘而不知道它们的含义(即它将多个 R * B 等) . 这可以解释为什么我得到了我认为的黑白结果,而不是转变。同样,我认为可能需要比我这里的幼稚版本更多...
最后的想法:我认为向左移动是过滤器的自然结果,我需要查看图像尺寸并可能将其填充......所以我认为代码实际上工作正常,因为我已经提供了它。