如何在图像上执行快速像素化滤镜?
我的像素化image processingalgorithm有点问题。
我从一开始就把图像加载到unsigned char*
types的数组中,然后在需要的时候修改这些数据并更新图像。 这个更新需要很长时间。 这是我如何做到的:
CGDataProviderRef dataProvider = CGProviderCrateWithData(.....); CGImageRef cgImage = CGImageCreate(....); [imageView setImage:[UIImage imageWithCGImage:cgImage]]];
一切正常,但处理一个大的图像是非常缓慢的。 我试着在后台线程上运行这个,但是没有帮助。
所以基本上这要花太长的时间。 有没有人有任何想法如何改善?
正如其他人所build议的那样,为了在这些移动设备上拥有任何体面的处理性能,您需要将这项工作从CPU转移到GPU。
为此,我为iOS创build了一个名为GPUImage的开源框架 ,使得这种加速的image processing变得相对简单。 它确实需要OpenGL ES 2.0支持,但是过去几年销售的每一个iOS设备都有这个数据(统计数据显示,该领域的所有iOS设备都达到了97%)。
作为该框架的一部分,我捆绑的初始filter之一是像素化filter。 SimpleVideoFilter示例应用程序演示了如何使用这个控制器,通过一个控制处理图像中像素宽度的滑块:
此滤镜是具有以下GLSL代码的片段着色器的结果:
varying highp vec2 textureCoordinate; uniform sampler2D inputImageTexture; uniform highp fractionalWidthOfPixel; void main() { highp vec2 sampleDivisor = vec2(fractionalWidthOfPixel); highp vec2 samplePos = textureCoordinate - mod(textureCoordinate, sampleDivisor); gl_FragColor = texture2D(inputImageTexture, samplePos ); }
在我的基准testing中,像这样的基于GPU的filter比iOS上的图像和video的CPU等效处理例程快6-24倍。 上面链接的框架应该可以合理地容易地join到应用程序中,并且源代码可以自由地供您自定义,但是您认为合适。
如何使用名为CIPixellate
的Core Image
filter? 这是我如何实现它的代码片段。 你可以玩kCIInputScaleKey
来获得你想要的强度:
// initialize context and image CIContext *context = [CIContext contextWithOptions:nil]; CIImage *logo = [CIImage imageWithData:UIImagePNGRepresentation([UIImage imageNamed:@"test"])]; // set filter and properties CIFilter *filter = [CIFilter filterWithName:@"CIPixellate"]; [filter setValue:logo forKey:kCIInputImageKey]; [filter setValue:[[CIVector alloc] initWithX:150 Y:150] forKey:kCIInputCenterKey]; // default: 150, 150 [filter setValue:[NSNumber numberWithDouble:100.0] forKey:kCIInputScaleKey]; // default: 8.0 // render image CIImage *result = (CIImage *) [filter valueForKey:kCIOutputImageKey]; CGRect extent = result.extent; CGImageRef cgImage = [context createCGImage:result fromRect:extent]; // result UIImage *image = [[UIImage alloc] initWithCGImage:cgImage];
这是官方的Apple Filter Tutorial和一个可用的filter列表 。
更新#1
我刚刚写了一个方法来在后台执行渲染工作:
- (void) pixelateImage:(UIImage *) image withIntensity:(NSNumber *) intensity completionHander:(void (^)(UIImage *pixelatedImage)) handler { // async task dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // initialize context and image CIContext *context = [CIContext contextWithOptions:nil]; CIImage *logo = [CIImage imageWithData:UIImagePNGRepresentation(image)]; // set filter and properties CIFilter *filter = [CIFilter filterWithName:@"CIPixellate"]; [filter setValue:logo forKey:kCIInputImageKey]; [filter setValue:[[CIVector alloc] initWithX:150 Y:150] forKey:kCIInputCenterKey]; // default: 150, 150 [filter setValue:intensity forKey:kCIInputScaleKey]; // default: 8.0 // render image CIImage *result = (CIImage *) [filter valueForKey:kCIOutputImageKey]; CGRect extent = result.extent; CGImageRef cgImage = [context createCGImage:result fromRect:extent]; // result UIImage *image = [[UIImage alloc] initWithCGImage:cgImage]; // dispatch to main thread dispatch_async(dispatch_get_main_queue(), ^{ handler(image); }); }); }
像这样调用它:
[self pixelateImage:[UIImage imageNamed:@"test"] withIntensity:[NSNumber numberWithDouble:100.0] completionHander:^(UIImage *pixelatedImage) { self.logoImageView.image = pixelatedImage; }];
对于像image processing这样的计算密集型任务来说,iPhone并不是一个好的设备。 如果您希望提高显示非常高分辨率图像的性能(可能同时执行一些image processing任务),请查看使用CATiledLayer 。 它是用平铺块显示内容,所以你可以只显示/处理内容数据在个别瓷砖上的需要。
我同意@Xorlev。 我唯一希望的是(假设你正在使用大量的浮点操作),你正在为arm6构build和使用拇指isa。 在这种情况下编译不带-mthumb选项,性能可能会提高。
转换@Kai Burghardt对Swift 3的回答
func pixelateImage(_ image: UIImage, withIntensity intensity: Int) -> UIImage { // initialize context and image let context = CIContext(options: nil) let logo = CIImage(data: UIImagePNGRepresentation(image)!)! // set filter and properties let filter = CIFilter(name: "CIPixellate") filter?.setValue(logo, forKey: kCIInputImageKey) filter?.setValue(CIVector(x:150,y:150), forKey: kCIInputCenterKey) filter?.setValue(intensity, forKey: kCIInputScaleKey) let result = filter?.value(forKey: kCIOutputImageKey) as! CIImage let extent = result.extent let cgImage = context.createCGImage(result, from: extent) // result let processedImage = UIImage(cgImage: cgImage!) return processedImage }
调用这个代码
self.myImageView.image = pixelateImage(UIImage(named:"test"),100)