Core Image实际上还能对滤镜进行更加细粒度的控制,我们在新的工程中对其进行探索。为此,我重新建立了一个空的workspace,并把之前所使用的工程添加到这个workspace中,编译、运行,没问题的话我们就开始创建新的工程。
通过workspace左下角的Add Files to添加已有的工程文件(xx.xcodeproj):
当添加工程到workspace的时候,记得要把被添加的工程关掉,不然workspacce不能识别。 另外,在流程上这篇也会与上一篇不同,上一篇一开始我就给出了代码,然后先看效果再步步为营,这篇不会在一开始给出代码。 |
用Single View Application的工程模板建立一个新的工程,在View上放一个UIImageView,还是同样的frame,同样的ContentMode设置为Aspect Fit,同样的关闭Auto Layout以及Size Classes,最后把上个工程中使用的图片复制过来,在这个工程中同样使用这张图。
做完上面这些基础工作后,我们回到VC中,把showFiltersInConsole方法从上个工程中复制过来,然后在viewDidLoad里调用,在运行之前我们先看看Core Image有哪些类别,毕竟全部的滤镜有127种,不可能一一用到的。
类别有很多,而且我们从上一篇中知道了滤镜可以同时属于不同的类别,除此之外,类别还分为两大类:
这些专业词太难翻译了,有不准确的地方还望告知 |
class ViewController: UIViewController {
@IBOutlet var imageView: UIImageView!
@IBOutlet var slider: UISlider!
lazy var originalImage: UIImage = {
return UIImage(named: "Image")
}()
lazy var context: CIContext = {
return CIContext(options: nil)
}()
var filter: CIFilter!
......
把UIImageView及UISlider的连线与VC中的连接起来,然后我们在viewDidLoad方法里写上:
override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.shadowOpacity = 0.8
imageView.layer.shadowColor = UIColor.blackColor().CGColor
imageView.layer.shadowOffset = CGSize(width: 1, height: 1)
slider.maximumValue = Float(M_PI)
slider.minimumValue = Float(-M_PI)
slider.value = 0
slider.addTarget(self, action: "valueChanged", forControlEvents: UIControlEvents.ValueChanged)
let inputImage = CIImage(image: originalImage)
filter = CIFilter(name: "CIHueAdjust")
filter.setValue(inputImage, forKey: kCIInputImageKey)
slider.sendActionsForControlEvents(UIControlEvents.ValueChanged)
showFiltersInConsole()
}
接着对slider初始化,在之前我们了解到CIHueAdjust滤镜的inputAngle参数最大值是Ԉ,最小值是负Ԉ,默认值是0,就用这些值来初始化,然后添加一个当值发生改变时触发的事件。
初始化filter,由于只有一个滤镜,filter对象也可以重用,设置完inputImage后,触发slider的事件就可以了。
valueChanged方法实现:
@IBAction func valueChanged() {
filter.setValue(slider.value, forKey: kCIInputAngleKey)
let outputImage = filter.outputImage
let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())
imageView.image = UIImage(CGImage: cgImage)
}
虽然我并不是在Storyboard里把slider的valueChanged事件连接到VC的方法上,但是在这里使用@IBAction也是适当的,这样可以表明这个方法不是业务逻辑方法,而是一个UI控件触发的方法。
编译、运行,应该可以看到效果了。
这里是oldFilmEffect方法实现:
@IBAction func oldFilmEffect() {
let inputImage = CIImage(image: originalImage)
// 1.创建CISepiaTone滤镜
let sepiaToneFilter = CIFilter(name: "CISepiaTone")
sepiaToneFilter.setValue(inputImage, forKey: kCIInputImageKey)
sepiaToneFilter.setValue(1, forKey: kCIInputIntensityKey)
// 2.创建白斑图滤镜
let whiteSpecksFilter = CIFilter(name: "CIColorMatrix")
whiteSpecksFilter.setValue(CIFilter(name: "CIRandomGenerator").outputImage.imageByCroppingToRect(inputImage.extent()), forKey: kCIInputImageKey)
whiteSpecksFilter.setValue(CIVector(x: 0, y: 1, z: 0, w: 0), forKey: "inputRVector")
whiteSpecksFilter.setValue(CIVector(x: 0, y: 1, z: 0, w: 0), forKey: "inputGVector")
whiteSpecksFilter.setValue(CIVector(x: 0, y: 1, z: 0, w: 0), forKey: "inputBVector")
whiteSpecksFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputBiasVector")
// 3.把CISepiaTone滤镜和白斑图滤镜以源覆盖(source over)的方式先组合起来
let sourceOverCompositingFilter = CIFilter(name: "CISourceOverCompositing")
sourceOverCompositingFilter.setValue(whiteSpecksFilter.outputImage, forKey: kCIInputBackgroundImageKey)
sourceOverCompositingFilter.setValue(sepiaToneFilter.outputImage, forKey: kCIInputImageKey)
// ---------上面算是完成了一半
// 4.用CIAffineTransform滤镜先对随机噪点图进行处理
let affineTransformFilter = CIFilter(name: "CIAffineTransform")
affineTransformFilter.setValue(CIFilter(name: "CIRandomGenerator").outputImage.imageByCroppingToRect(inputImage.extent()), forKey: kCIInputImageKey
affineTransformFilter.setValue(NSValue(CGAffineTransform: CGAffineTransformMakeScale(1.5, 25)), forKey: kCIInputTransformKey)
// 5.创建蓝绿色磨砂图滤镜
let darkScratchesFilter = CIFilter(name: "CIColorMatrix")
darkScratchesFilter.setValue(affineTransformFilter.outputImage, forKey: kCIInputImageKey)
darkScratchesFilter.setValue(CIVector(x: 4, y: 0, z: 0, w: 0), forKey: "inputRVector")
darkScratchesFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputGVector")
darkScratchesFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputBVector")
darkScratchesFilter.setValue(CIVector(x: 0, y: 0, z: 0, w: 0), forKey: "inputAVector")
darkScratchesFilter.setValue(CIVector(x: 0, y: 1, z: 1, w: 1), forKey: "inputBiasVector")
// 6.用CIMinimumComponent滤镜把蓝绿色磨砂图滤镜处理成黑色磨砂图滤镜
let minimumComponentFilter = CIFilter(name: "CIMinimumComponent")
minimumComponentFilter.setValue(darkScratchesFilter.outputImage, forKey: kCIInputImageKey)
// ---------上面算是基本完成了
// 7.最终组合在一起
let multiplyCompositingFilter = CIFilter(name: "CIMultiplyCompositing")
multiplyCompositingFilter.setValue(minimumComponentFilter.outputImage, forKey: kCIInputBackgroundImageKey)
multiplyCompositingFilter.setValue(sourceOverCompositingFilter.outputImage, forKey: kCIInputImageKey)
// 8.最后输出
let outputImage = multiplyCompositingFilter.outputImage
let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())
imageView.image = UIImage(CGImage: cgImage)
}
class CIColorInvert: CIFilter {
var inputImage: CIImage!
override var outputImage: CIImage! {
get {
return CIFilter(name: "CIColorMatrix", withInputParameters: [
kCIInputImageKey : inputImage,
"inputRVector" : CIVector(x: -1, y: 0, z: 0),
"inputGVector" : CIVector(x: 0, y: -1, z: 0),
"inputBVector" : CIVector(x: 0, y: 0, z: -1),
"inputBiasVector" : CIVector(x: 1, y: 1, z: 1),
]).outputImage
}
}
}
@IBAction func colorInvert() {
let colorInvertFilter = CIColorInvert()
colorInvertFilter.inputImage = CIImage(image: imageView.image)
let outputImage = colorInvertFilter.outputImage
let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())
imageView.image = UIImage(CGImage: cgImage)
}
......
lazy var originalImage: UIImage = {
return UIImage(named: "Image2")
}()
......
@IBAction func showOriginalImage() {
self.imageView.image = originalImage
}
struct CubeMap {
int length;
float dimension;
float *data;
};
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;
}
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;
}
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// ComplexFilters-Bridging-Header.h
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "CubeMap.c"
@IBAction func replaceBackground() {
let cubeMap = createCubeMap(60,90)
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: imageView.image), forKey: kCIInputImageKey)
var outputImage = colorCubeFilter.outputImage
let sourceOverCompositingFilter = CIFilter(name: "CISourceOverCompositing")
sourceOverCompositingFilter.setValue(outputImage, forKey: kCIInputImageKey)
sourceOverCompositingFilter.setValue(CIImage(image: UIImage(named: "background")), forKey: kCIInputBackgroundImageKey)
outputImage = sourceOverCompositingFilter.outputImage
let cgImage = context.createCGImage(outputImage, fromRect: outputImage.extent())
imageView.image = UIImage(CGImage: cgImage)
}