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,源码下载:hangge_904.zip
感谢站长这么用心回复,另外还有个问题想请教一下:
这个方法怎么抠白色和黑色背景的图片?
或者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搜遍了也没找到解决办法,如果有时间的回复的话非常感谢。
这个压缩文件解压不出来