Swift - 抠图,及图片合成功能的实现(适用于纯色背景)
(本文代码已升级至Swfit3)
大家肯定都用过PS进行抠图(扣图),而在Swift中,使用代码也可以实现抠图功能。
1,要把一个人物或物体从背景中抠出来,通常有两种办法:

而CIColorCube滤镜需要一张cube映射表,这张表其实就是张颜色表(3D颜色查找表),把你想消除的颜色的alpha值设置为0,其他的颜色不变,Core Image将会把图像数据上的颜色映射为表中的颜色,以此来达到消除某种颜色的目的。

通过这个可以很方便的查看RGB颜色对应的HSV值。比如小猫背景都是蓝色的(只不过深浅不一),我们只需要把HSV在210到240这段颜色去处即可。

(2)主页代码
7,源码下载:
hangge_904.zip
大家肯定都用过PS进行抠图(扣图),而在Swift中,使用代码也可以实现抠图功能。
1,要把一个人物或物体从背景中抠出来,通常有两种办法:
(1)使用CoreImage色域:适合纯色背景(或者背景色相对单一,色差不会太大),抠图精准
(2)使用openCv边缘检测:复杂背景情况也适用,默认抠图不够精确
2,下面使用第一种方案把下面的小猫抠出来,放置到雪地背景上。

3,如何使用Core Image抠图
对于纯色背景,可以直接把背景色给消除,这样剩下的便是主体了。要消除背景色,可以使用CIColorCube滤镜。而CIColorCube滤镜需要一张cube映射表,这张表其实就是张颜色表(3D颜色查找表),把你想消除的颜色的alpha值设置为0,其他的颜色不变,Core Image将会把图像数据上的颜色映射为表中的颜色,以此来达到消除某种颜色的目的。
4,代表颜色值区域的HSV(Hue值)图

5,下面是样例效果图
为便于比较,我这边分别做了“只抠图”,以及“抠图并更换背景”两个功能。(真是毫无PS痕迹)

6,代码如下:
(1)首先创建Cube Map表
新建一个“C File”文件CubeMap.c,同时Xcode会自动生成这个C文件对应的头文件CubeMap.h,还有连接头文件(Bridging Header文件)。各文件里代码如下:
--- CubeMap.c ---
#include "CubeMap.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
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;
}
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 value
for (int y = 0; y < size; y++){
rgb[1] = ((double)y)/(size-1); // Green value
for (int x = 0; x < size; x ++){
rgb[0] = ((double)x)/(size-1); // Red value
rgbToHSV(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 remove
float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) ? 0.0f: 1.0f;
// Calculate premultiplied alpha values for the cube
c[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;
}
--- CubeMap.h ---
#ifndef CubeMap_h
#define CubeMap_h
#include <stdio.h>
typedef struct CubeMap CubeMap;
struct CubeMap {
int length;
float dimension;
float *data;
};
struct CubeMap createCubeMap(float minHueAngle, float maxHueAngle);
#endif /* CubeMap_h */
--- Bridging-Header.h ---
#import "CubeMap.h"
import UIKit
class ViewController: UIViewController{
@IBOutlet weak var imageView: UIImageView!
//图片原图
lazy var originalImage: UIImage = {
return UIImage(named: "cat.jpg")
}()!
lazy var context: CIContext = {
return CIContext(options: nil)
}()
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = originalImage
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//抠图
@IBAction func cutOut(_ sender: AnyObject) {
let cubeMap = createCubeMap(210,240)
let data = NSData(bytesNoCopy: cubeMap.data, length: Int(cubeMap.length),
freeWhenDone: true)
//消除某种颜色
let colorCubeFilter = CIFilter(name: "CIColorCube")!
colorCubeFilter.setValue(cubeMap.dimension, forKey: "inputCubeDimension")
colorCubeFilter.setValue(data, forKey: "inputCubeData")
colorCubeFilter.setValue(CIImage(image: originalImage), forKey: kCIInputImageKey)
let outputImage = colorCubeFilter.outputImage
let cgImage = context.createCGImage(outputImage!, from: outputImage!.extent)
imageView.image = UIImage(cgImage: cgImage!)
}
//抠图并合成
@IBAction func cutOutAndCompose(_ sender: AnyObject) {
let cubeMap = createCubeMap(210,240)
let data = NSData(bytesNoCopy: cubeMap.data, length: Int(cubeMap.length),
freeWhenDone: true)
//消除某种颜色
let colorCubeFilter = CIFilter(name: "CIColorCube")!
colorCubeFilter.setValue(cubeMap.dimension, forKey: "inputCubeDimension")
colorCubeFilter.setValue(data, forKey: "inputCubeData")
colorCubeFilter.setValue(CIImage(image: originalImage), forKey: kCIInputImageKey)
var outputImage = colorCubeFilter.outputImage
//与背景图合成
let sourceOverCompositingFilter = CIFilter(name: "CISourceOverCompositing")!
sourceOverCompositingFilter.setValue(outputImage, forKey: kCIInputImageKey)
sourceOverCompositingFilter.setValue(CIImage(image: UIImage(named: "bg.jpg")!),
forKey: kCIInputBackgroundImageKey)
outputImage = sourceOverCompositingFilter.outputImage
let cgImage = context.createCGImage(outputImage!, from: outputImage!.extent)
imageView.image = UIImage(cgImage: cgImage!)
}
//还原图片
@IBAction func resetImage(_ sender: AnyObject) {
self.imageView.image = originalImage
}
}
7,源码下载:

感谢站长这么用心回复,另外还有个问题想请教一下:
这个方法怎么抠白色和黑色背景的图片?
或者swift有其他单独针对黑白的方法吗?
ps:因为白色是通过调整饱和度来的
```
duplicate symbol _createCubeMap in:
/Users/nathan/Library/Developer/Xcode/DerivedData/Koutu2-eplaxzssfxopryhhxjukijapxifp/Build/Intermediates/Koutu2.build/Debug-iphonesimulator/Koutu2.build/Objects-normal/x86_64/CubeMap.o
/Users/nathan/Library/Developer/Xcode/DerivedData/Koutu2-eplaxzssfxopryhhxjukijapxifp/Build/Intermediates/Koutu2.build/Debug-iphonesimulator/Koutu2.build/Objects-normal/x86_64/ViewController.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
```
你好,请问下在swift3中测试本案例的时候出现如上错误,google、百度、stackoverflow搜遍了也没找到解决办法,如果有时间的回复的话非常感谢。
这个压缩文件解压不出来