实际项目场景:去除图片的纯白色背景图,获得一张透明底图片用于拼图功能

下面介绍两种途径的三种处理方式(不知道为啥想起了孔乙己),具体性能鶸并未对比,如果有大佬能告知,不胜感激。

  • 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更加友好。

大致过程处理过程:

  1. 创建一个映射希望移除颜色值范围的立方体贴图cubeMap,将目标颜色的Alpha置为0.0f
  2. 使用CIColorCube滤镜和cubeMap对源图像进行颜色处理
  3. 获取到经过CIColorCube处理的Core Image对象CIImage,转换为Core Graphics中的CGImageRef对象,通过imageWithCGImage:获取结果图片

注意:第三步中,不可以直接使用imageWithCIImage:,因为得到的并不是一个标准的UIImage,如果直接拿来用,会出现不显示的情况。

- (UIImage *)removeColorWithMinHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle image:(UIImage *)originalImage{CIImage *image = [CIImage imageWithCGImage:originalImage.CGImage];CIContext *context = [CIContext contextWithOptions:nil];// kCIContextUseSoftwareRenderer : CPURender/** 注意*  UIImage 通过CIimage初始化,得到的并不是一个通过类似CGImage的标准UIImage*  所以如果不用context进行渲染处理,是没办法正常显示的*/CIImage *renderBgImage = [self outputImageWithOriginalCIImage:image minHueAngle:minHueAngle maxHueAngle:maxHueAngle];CGImageRef renderImg = [context createCGImage:renderBgImage fromRect:image.extent];UIImage *renderImage = [UIImage imageWithCGImage:renderImg];return renderImage;
}struct CubeMap {int length;float dimension;float *data;
};- (CIImage *)outputImageWithOriginalCIImage:(CIImage *)originalImage minHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle{struct CubeMap map = createCubeMap(minHueAngle, maxHueAngle);const unsigned int size = 64;// Create memory with the cube dataNSData *data = [NSData dataWithBytesNoCopy:map.datalength:map.lengthfreeWhenDone:YES];CIFilter *colorCube = [CIFilter filterWithName:@"CIColorCube"];[colorCube setValue:@(size) forKey:@"inputCubeDimension"];// Set data for cube[colorCube setValue:data forKey:@"inputCubeData"];[colorCube setValue:originalImage forKey:kCIInputImageKey];CIImage *result = [colorCube valueForKey:kCIOutputImageKey];return result;
}struct CubeMap createCubeMap(float minHueAngle, float maxHueAngle) {const unsigned int size = 64;struct CubeMap map;map.length = size * size * size * sizeof (float) * 4;map.dimension = size;float *cubeData = (float *)malloc (map.length);float rgb[3], hsv[3], *c = cubeData;for (int z = 0; z < size; z++){rgb[2] = ((double)z)/(size-1); // Blue valuefor (int y = 0; y < size; y++){rgb[1] = ((double)y)/(size-1); // Green valuefor (int x = 0; x < size; x ++){rgb[0] = ((double)x)/(size-1); // Red valuergbToHSV(rgb,hsv);// Use the hue value to determine which to make transparent// The minimum and maximum hue angle depends on// the color you want to removefloat alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f: 1.0f;// Calculate premultiplied alpha values for the cubec[0] = rgb[0] * alpha;c[1] = rgb[1] * alpha;c[2] = rgb[2] * alpha;c[3] = alpha;c += 4; // advance our pointer into memory for the next color value}}}map.data = cubeData;return map;
}
复制代码

rgbToHSV在官方文档中并没有提及,笔者在下文中提到的大佬的博客中找到了相关转换处理。感谢

void rgbToHSV(float *rgb, float *hsv) {float min, max, delta;float r = rgb[0], g = rgb[1], b = rgb[2];float *h = hsv, *s = hsv + 1, *v = hsv + 2;min = fmin(fmin(r, g), b );max = fmax(fmax(r, g), b );*v = max;delta = max - min;if( max != 0 )*s = delta / max;else {*s = 0;*h = -1;return;}if( r == max )*h = ( g - b ) / delta;else if( g == max )*h = 2 + ( b - r ) / delta;else*h = 4 + ( r - g ) / delta;*h *= 60;if( *h < 0 )*h += 360;
}
复制代码

接下来我们试一下,去除绿色背景的效果如何

我们可以通过使用HSV工具,确定绿色HUE值的大概范围为50-170

调用一下方法试一下

[[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

上文中提到的基于OpenGlCore Image显然功能十分强大,作为视图另一基石的Core Graphics同样强大。对他的探究,让鶸笔者更多的了解到图片的相关知识。所以在此处总结,供日后查阅。

如果对探究不感兴趣的同学,请直接跳到文章最后 Masking an Image with Color 部分

Bitmap

在 Quarz 2D官方文档中,对于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为我们提供了实现这种操作的能力,下面请看代码示例:

- (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{// 分配内存const int imageWidth = image.size.width;const int imageHeight = image.size.height;size_t bytesPerRow = imageWidth * 4;uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);// 创建contextCGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();// 色彩范围的容器CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);// 遍历像素int pixelNum = imageWidth * imageHeight;uint32_t* pCurPtr = rgbImageBuf;for (int i = 0; i < pixelNum; i++, pCurPtr++){uint8_t* ptr = (uint8_t*)pCurPtr;if (ptr[3] >= minR && ptr[3] <= maxR &&ptr[2] >= minG && ptr[2] <= maxG &&ptr[1] >= minB && ptr[1] <= maxB) {ptr[0] = 0;}else{printf("\n---->ptr0:%d ptr1:%d ptr2:%d ptr3:%d<----\n",ptr[0],ptr[1],ptr[2],ptr[3]);}}// 将内存转成imageCGDataProviderRef dataProvider =CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, nil);CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight,8, 32, bytesPerRow, colorSpace,kCGImageAlphaLast |kCGBitmapByteOrder32Little, dataProvider,NULL,true,kCGRenderingIntentDefault);CGDataProviderRelease(dataProvider);UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef];// 释放CGImageRelease(imageRef);CGContextRelease(context);CGColorSpaceRelease(colorSpace);return resultUIImage;
}
复制代码

还记得我们在Core Image中提到的HSV模式的弊端吗?那么Quarz 2D则是直接利用RGBA的信息进行处理,很好的规避了对黑白色不友好的问题,我们只需要设置一下RGB的范围即可(因为黑白色在RGB颜色模式中,很好确定),我们可以大致封装一下。如下

- (UIImage *)removeWhiteColorWithImage:(UIImage *)image{return [self removeColorWithMaxR:255 minR:250 maxG:255 minG:240 maxB:255 minB:240 image:image];
}
复制代码
- (UIImage *)removeBlackColorWithImage:(UIImage *)image{return [self removeColorWithMaxR:15 minR:0 maxG:15 minG:0 maxB:15 minB:0 image:image];
}
复制代码

看一下我们对于白色背景的处理效果对比

看起来似乎还不错,但是对于纱质的衣服,就显得很不友好。看一下笔者做的几组图片的测试

很显然,如果不是白色背景,“衣衫褴褛”的效果非常明显。这个问题,在笔者尝试的三种方法中,无一幸免,如果哪位大佬知道好的处理方法,而且能告诉鶸,将不胜感激。(先放俩膝盖在这儿)

除了上述问题外,这种对比每个像素的方法,读取出来的数值会同作图时出现误差。但是这种误差肉眼基本不可见。

如下图中,我们作图时,设置的 RGB值分别为100/240/220 但是通过CG上述处理时,读取出来的值则为92/241/220。对比图中的“新的”“当前”,基本看不出色差。这点小问题各位知道就好,对实际去色效果影响并不大

Masking an Image with Color

笔者尝试过理解并使用上一种方法后,在重读文档时发现了这个方法,简直就像是发现了Father Apple的恩赐。直接上代码

- (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{const CGFloat myMaskingColors[6] = {minR, maxR,  minG, maxG, minB, maxB};CGImageRef ref = CGImageCreateWithMaskingColors(image.CGImage, myMaskingColors);return [UIImage imageWithCGImage:ref];}
复制代码

官方文档点这儿

总结

HSV颜色模式相对于RGB模式而言,更利于我们抠除图片中的彩色,而RGB则正好相反。笔者因为项目中,只需要去除白色背景,所以最终采用了最后一种方式。

转载于:https://juejin.im/post/5a3a10baf265da43252971b5

iOS-抠图:去除图片中指定范围颜色的三种方式相关推荐

  1. html中常见表达颜色的三种方式

    html中常见表达颜色的三种方式 网页中经常应用到各种颜色,这关乎界面美观以及整体的设计感,那么,具体应用到哪几种表达方式,接下来让我们一起看看: 1 英文单词表示(局限):red(红),blue(蓝 ...

  2. LaTeX中设置字体颜色的三种方式

    以下的三种方式都需要导入color包,即\usepackage{color}. 实验环境:window10,TexLive2019. 1.使用系统自定义的颜色 使用语法: \textcolor{red ...

  3. python绘图颜色代码大全_matplotlib指定绘图颜色的八种方式——python篇

    在使用matplotlib的pyplot库进行绘图时,经常会发现各种开源代码指定"color"的方式并不一致.通过查阅官方资料[1],发现共有8种指定color的方式.8种方式如下 ...

  4. Compose 设置颜色的三种方式

    文章目录 前言 一.直接设置 二.使用colors.xml中的颜色 三.使用compose主题中的颜色 前言 下文三种的color都是替换下列代码中的color Text(text = "T ...

  5. iOS-改变UITextField的Placeholder颜色的三种方式

    转自:http://blog.csdn.net/mazy_ma/article/details/51775670 有时,UITextField自带的Placeholder的颜色太浅或者不满足需求,所以 ...

  6. jquery ajax调用服务器端指定的函数的三种方式

    1)通过webservice,注意去掉注释[System.Web.Script.Services.ScriptService]这行前的注释 2)通过aspx.cs文件中的静态方法 3)通过aspx文件 ...

  7. echarts饼图自定义设置颜色的三种方式

    第一种方式 option下 color:['#45C2E0', '#C1EBDD', '#FFC851','#5A5476','#1869A0','#FF9393'], 整体代码如下: option ...

  8. 【iOS开发】ipa安装到手机上的三种方式

    转载地址:ipa安装到手机上的三种方式 ipa包 安装三种方式,优先推荐第一种方法(通过iTunes安装). 1.通过iTunes安装 数据线连接手机之后,会自动连接iTunes,(第一次连接的时候会 ...

  9. python更改图片中物体的颜色_Python实现去除图片中指定颜色的像素功能示例

    Python实现去除图片中指定颜色的像素功能示例 本文实例讲述了Python实现去除图片中指定颜色的像素功能.分享给大家供大家参考,具体如下: 这里用python去除图片白色像素 需要python和p ...

最新文章

  1. 安全攻防之SQL注入
  2. 我心中的核心组件(可插拔的AOP)~分布式Session组件
  3. mvc5入门示例博客(有惊喜)
  4. IDL关系运算符Eq Ne Le Lt Gt Ge含义说明
  5. 服务器里面发邮件,通过SMTP中继服务器发送邮件的问题
  6. js判断对象是否为空对象_js对象
  7. node.js + express 初体验【hello world】
  8. c语言里凤霞答案,C语言中循环结构的教学方法研究
  9. SQlite Android数据库的应用 Android SQLite 简易的学生成绩管理系统
  10. thinkphp sql查询条件 一个字段多个限制条件
  11. 新版gsp五个附录计算机培训,新版GSP附录5 ——验证管理
  12. 多张图片怎么合成一个pdf?
  13. Docker配置文件位置
  14. python 谷歌地图_Python查询一个城市的谷歌地图的经度和纬度
  15. 2017年2月28日-----------乱码新手自学.net 之特性与验证
  16. 行为树 --- [4] 简单树
  17. 李宏毅机器学习 02回归
  18. mysql 查看碎片_MYSQL 碎片查询
  19. iPad 如何使用妙控键盘
  20. 纳米材料与技术类毕业论文文献有哪些?

热门文章

  1. CentOS 8报错 Failed to download metadata for repo ‘AppStream‘: Cannot download repomd.xml解决方法
  2. Torch 常用 Tricks 总结
  3. 数据湖(十四):Spark与Iceberg整合查询操作
  4. Hibernate入门经典实例
  5. 当txt文件或者sql文件数据量太大,无法打开时,可以通过Emeditor这个编辑器打开
  6. 雷击模型 Matlab/simulink 可用于模拟雷击引起的电能质量问题,可调脉冲电压幅值和发生时间,适配于本家的IEEE 33节点等模型
  7. python中*args和**kwargs的用法
  8. [msdn] WritePrivateProfileString 写入配置文件
  9. 沁春奶咖告诉您丝袜奶茶在家里可以这样做哦!
  10. 【Moving Least Squares】【移动最小二乘法】