实际项目场景:去除图片的纯白色背景图,获得一张透明底图片用于拼图功能
介绍两种途径的三种处理方式(不知道为啥想起了孔乙己),具体性能鶸并未对比,如果有大佬能告知,不胜感激。
- Core Image
- Core Graphics/Quarz 2D
Core Image
Core Image是一个很强大的框架。它可以让你简单地应用各种滤镜来处理图像,比如修改鲜艳程度,色泽,或者曝光。 它利用GPU(或者CPU)来非常快速、甚至实时地处理图像数据和视频的帧。并且隐藏了底层图形处理的所有细节,通过提供的API就能简单的使用了,无须关心OpenGL或者OpenGL ES是如何充分利用GPU的能力的,也不需要你知道GCD在其中发挥了怎样的作用,Core Image处理了全部的细节。
在苹果官方文档Core Image Programming Guide中,提到了Chroma Key Filter Recipe对于处理背景的范例
其中使用了HSV颜色模型,因为HSV模型,对于颜色范围的表示,相比RGB更加友好。
大致过程处理过程:
- 创建一个映射希望移除颜色值范围的立方体贴图cubeMap,将目标颜色的
Alpha
置为0.0f
- 使用
CIColorCube
滤镜和cubeMap对源图像进行颜色处理 - 获取到经过
CIColorCube
处理的Core Image
对象CIImage
,转换为Core Graphics
中的CGImageRef
对象,通过imageWithCGImage:
获取结果图片
注意:第三步中,不可以直接使用imageWithCIImage:
,因为得到的并不是一个标准的UIImage
,如果直接拿来用,会出现不显示的情况。
1 | - (UIImage *)removeColorWithMinHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle image:(UIImage *)originalImage{ |
rgbToHSV
在官方文档中并没有提及,笔者在下文中提到的大佬的博客中找到了相关转换处理。感谢
1 | void rgbToHSV(float *rgb, float *hsv) { |
接下来我们试一下,去除绿色背景的效果如何
我们可以通过使用HSV工具,确定绿色HUE
值的大概范围为50-170
调用一下方法试一下
1 | [[SPImageChromaFilterManager sharedManager] removeColorWithMinHueAngle:50 maxHueAngle:170 image:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"nb" ofType:@"jpeg"]]] |
效果
效果还可以的样子。
如果认真观察HSV模型的同学也许会发现,我们通过指定色调角度(Hue)的方式,对于指定灰白黑显得无能为力。我们不得不去用饱和度(Saturation)和明度(Value)去共同判断,感兴趣的同学可以在代码中判断Alphafloat alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f: 1.0f;
那里试一下效果。(至于代码中为啥RGB和HSV这么转换,请百度他们的转换,因为鶸笔者也不懂。哎,鶸不聊生)
对于Core Image感兴趣的同学,请移步大佬的系列文章
iOS8 Core Image In Swift:自动改善图像以及内置滤镜的使用
iOS8 Core Image In Swift:更复杂的滤镜
iOS8 Core Image In Swift:人脸检测以及马赛克
iOS8 Core Image In Swift:视频实时滤镜
Core Graphics/Quarz 2D
上文中提到的基于OpenGl
的Core Image
显然功能十分强大,作为视图另一基石的Core Graphics同样强大。对他的探究,让鶸笔者更多的了解到图片的相关知识。所以在此处总结,供日后查阅。
如果对探究不感兴趣的同学,请直接跳到文章最后 Masking an Image with Color 部分
Bitmap
A bitmap image (or sampled image) is an array of pixels (or samples). Each pixel represents a single point in the image. JPEG, TIFF, and PNG graphics files are examples of bitmap images.
32-bit and 16-bit pixel formats for CMYK and RGB color spaces in Quartz 2D
回到我们的需求,对于去除图片中的指定颜色,如果我们能够读取到每个像素上的RGBA信息,分别判断他们的值,如果符合目标范围,我们将他的Alpha值改为0,然后输出成新的图片,那么我们就实现了类似上文中cubeMap的处理方式。
强大的Quarz 2D
为我们提供了实现这种操作的能力,下面请看代码示例:
1 | - (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{ |
还记得我们在Core Image中提到的HSV模式的弊端吗?那么Quarz 2D则是直接利用RGBA的信息进行处理,很好的规避了对黑白色不友好的问题,我们只需要设置一下RGB的范围即可(因为黑白色在RGB颜色模式中,很好确定),我们可以大致封装一下。如下
1 | - (UIImage *)removeWhiteColorWithImage:(UIImage *)image{ |
1 | - (UIImage *)removeBlackColorWithImage:(UIImage *)image{ |
看一下我们对于白色背景的处理效果对比
看起来似乎还不错,但是对于纱质的衣服,就显得很不友好。看一下笔者做的几组图片的测试
很显然,如果不是白色背景,“衣衫褴褛”的效果非常明显。这个问题,在笔者尝试的三种方法中,无一幸免,如果哪位大佬知道好的处理方法,而且能告诉鶸,将不胜感激。(先放俩膝盖在这儿)
除了上述问题外,这种对比每个像素的方法,读取出来的数值会同作图时出现误差。但是这种误差肉眼基本不可见。
如下图中,我们作图时,设置的RGB值分别为100/240/220 但是通过CG上述处理时,读取出来的值则为92/241/220。对比图中的“新的”“当前”,基本看不出色差。这点小问题各位知道就好,对实际去色效果影响并不大
Masking an Image with Color
笔者尝试过理解并使用上一种方法后,在重读文档时发现了这个方法,简直就像是发现了Father Apple的恩赐。直接上代码
1 | - (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{ |
总结
HSV颜色模式相对于RGB模式而言,更利于我们抠除图片中的彩色,而RGB则正好相反。笔者因为项目中,只需要去除白色背景,所以最终采用了最后一种方式。