Core Image
一 處理圖像
CIFilter 是一個可變對象,代表一種效果。一個濾鏡對象至少要有一個輸入參數,併產生一個輸出圖片。
CIImage 是一個不變對象,代表一個圖片。你可以通過圖像數據,或者通過文件,或者另一個CIFilter對象的輸出得到一個CIImage。
CIContext 是一個對象,通過它Core Image可以繪製一個CIFilter產生的結果。一個Core Image Context可以基於CPU或GPU。
1 概覽
源碼:
CIContext *context = [CIContext contextWithOptions:nil]; // 1
CIImage *p_w_picpath = [CIImage p_w_picpathWithContentsOfURL:myURL]; // 2
CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"]; // 3
[filter setValue:p_w_picpath forKey:kCIInputImgeKey];
[filter setValue:[NSNumber numberWithFloat:0.8f] forKey:@"InputIntensity"];
CIImage *result = [filter valueForKey:kCIOutputImageKey]; // 4
CGImageRef cgImage = [context createCGImage:result fromRect:[result extent]; // 5
說明:
1. 創建一個CIContext對象。這個方法你可以在iOS上使用。
2. 創建一個CIImage對象. 你可以從多種來源創建一個CIImage對象,不日URL。
3. 創建濾鏡,並設置其輸入參數。
4. 獲得輸出圖像,輸出圖像是一個描述如何生成圖像的配方(這裏的配方是描述如何製作一道菜的配方,不是點菜的菜譜)。圖像還沒有被渲染出來。
5. 渲染CIImage,得到CGImageRef,CGImageRef是可以直接展示或者保存到文件中的。
2 內置濾鏡(說明:原文是filter,這裏採用濾鏡更直觀,因此不翻譯爲過濾器)
Core Image提供了大量內置的濾鏡,供你的應用做圖像處理。不同iOS版本提供的濾鏡是不同的,所以Core Image 提供了方法,讓你可以查詢系統提供了哪些濾鏡。
一個濾鏡類別定義了各種效果的類型,或者其可以作用的目標:
p_w_picpaths, video, nonsquare pixels, 等等.
一個濾鏡可以屬於多個類別。濾鏡有一個顯示名稱(給用戶看的)和一個濾鏡名稱(在代碼中要使用的)。
濾鏡屬性是以鍵-值對的形式保存的。鍵是一個常量,標識屬性及其對應的值。
Core Image使用鍵-值編碼,你可以通過KVC獲得屬性的值。
3 創建一個Core Image Context
要渲染圖像,你需要創建Core Image Context,並使用這個上下文繪製輸出圖像。
一個Core Image context標識一個繪製的目標。目標決定了使用CPU還是GPU去渲染。
創建的函數:
contextWithOptions: CPU或GPU都支持。
以下僅支持GPU:
contextWithCGLContext: pixelFormat:options: GPU OS X
contextWithCGLContext: pixelFormat:
colorSpace:options:
[1] 在iOS上創建一個Core Image Context,When You Don’t Need Real-Time
Performance
源碼:
CIContext *context = [CIContext contextWithOptions:nil];
這個方法可以使用CPU或者GPU進行渲染。要定義使用哪個,需要設置一個選項字典,增加鍵kCIContextUserSoftwareRenderer,並設置相應的布爾值。CPU渲染要比GPU要慢。但是對於GPU渲染,在渲染結果被拷貝回CPU內存並轉化爲UIImage之前,不會被顯示出來。
[2] 在iOS上創建一個Core Image Context,When You Need Real-Time Performance
如果你的應用要支持實時的圖像處理,你需要從一個EAGL context創建一個CIContext對象,不能使用contextWithOptions: 方法。優點是渲染的圖像保存在GPU上,並不需要拷貝回CPU內存。你首先需要創建一個EAGL context:
myEAGLContext = [EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
之後使用方法contextWithEAGLContext: 創建一個CIContext對象。
你應該將working color space設置爲null,來關閉顏色管理功能。顏色管理會降低性能。在需要顏色保真的情況下,你才需要顏色管理。但是對於一個real-time app,顏色保真通常並不是必要的。
代碼:
NSMutableDictionary *options = [NSMutableDictionary alloc] init];
[options setObject: [NSNull null] forKey: kCIContextWorkingColorSpace];
myContext = [CIContext contextWithEAGLContext:myEAGLContext options:options];
4 創建一個CIImage對象
Core Image filters 處理Core Image p_w_picpaths (CIImage objects).
記住,一個CIImage對象實際上是一個配方;在被調用,在目標上渲染結果之前,Core Image實際上不產生一個像素。創建的方法:
5 創建一個CIFilter並設置屬性值
filterWithName: 方法創建了一個過濾器,通過濾鏡名稱參數來設定濾鏡的類型。
名稱參數是一個字符串,其值必須與內置濾鏡嚴格匹配。
代碼:
hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"];
[hueAdjust setValue: myCIImage forKey: @"inputImage"];
[hueAdjust setValue: [NSNumber numberWithFloat: 2.094]
forKey: @"inputAngle"];
另一種方式:
hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"] keysAndValues:
@"inputImage",myCIImage,
@"inputAngle, [NSNumber numberWithFloat: 2.094],
nil]; // 你可以增加更多參數,但是必須以nil結尾。
6 獲取輸出圖像
你通過outputImage鍵,獲得輸出圖像。
CIImage *result = [hueAdjust valueForKey: @"outputImage"];
在你調用某個方法實際執行渲染之前,Core Image不會執行任何的圖像處理操作。
當你請求去輸出圖像時,Core Image組裝並保存要生成一個輸出圖像的運算步驟(保存到一個CIImage對象中)。只有在你顯示調用一個圖像繪製方法時,實際的圖像纔會被渲染。
將處理延遲到渲染的時候,使得Core Image 快速而高效。在渲染的時候,Core Image會判斷是否有超過一個濾鏡需要作用到一個圖像上。如果是這樣,它自動把多個配方連接成一個操作(好像就是做一系列的矩陣運算,運算結果仍是一個矩陣,所以可以合成一步對結果矩陣的操作,說明by王雲鵬),即每個像素只會被處理一次,而不是多次。
下圖列出了一個multiple-operations workflow ,Core Image 可以更高效。最終圖像是原始圖像的一個縮小版本。
對於一個大圖像,在大小調整之前,應用顏色調整,比先做大小調整,之後再應用顏色調整,需要更多的處理資源。
通過將濾鏡處理操作延遲到渲染的時候,Core Image可以按照最高效的處理順序來執行操作。
7 渲染輸出圖像
渲染輸出圖像會觸發CPU或者GPU(依賴於你設置的context)的密集的處理器操作。下面的函數可以用來渲染:
代碼:
[myContext drawImage:result inRect:destinationRect fromRect:contextRect];
效果對比:
8 保證線程安全
CIContext 和CIImage對象是不可變的, 在線程之間共享這些對象是安全的.
多個線程可以使用同一個GPU或者CPU CIContext對象來渲染CIImage對象。
但是對於可變的CIFilter確不行。一個CIFilter對象不能在多個線程間共享。如果你的應用是多線程的,每個線程都必須創建自己的CIFilter對象。否則,你的應用將產生不可預期的結果。
9 濾鏡鏈
你可以通過濾鏡鏈創造出驚人的效果,即將一個濾鏡的輸出作爲另一個濾鏡的輸入。
讓我們看看在一個圖像上如何應用兩個濾鏡—幽暗(?gloom)(CIGloom) 和爆沸畸變 (?bump distortion)(CIBumpDistortion).
gloom濾鏡:通過淡化亮度來使一張圖片變暗。
gloom = [CIFilter filterWithName:@"CIGloom"]; // 1
[gloom setValue: result forKey: @"inputImage"];
[gloom setValue: [NSNumber numberWithFloat: 25]
forKey: @"inputRadius"]; // 2
[gloom setValue: [NSNumber numberWithFloat: 0.75]
forKey: @"inputIntensity"]; // 3
result = [gloom valueForKey: @"outputImage"]; // 4
1. 創建一個CIGloom類型的濾鏡。
2. 設置輸入半徑爲25。半徑定義了效果的外延,可以設置0到100之間的值,缺省值是10。(你可以通過濾鏡的屬性字典獲得濾鏡的最小、最大、缺省值)。
3. 將輸入強度設置爲0.75。輸入強度是一個標量值,它定義了原圖與濾鏡輸出之間的一個線性的混合。最小值是0.0,最大值是1.0,缺省值是1.0。
4. 請求輸出圖像,但並不真正繪製。
處理結果:
bump distortion 濾鏡 (CIBumpDistortion)在一個圖像上的某個點創建一個突起。
源碼:
bumpDistortion = [CIFilter filterWithName:@"CIBumpDistortion"]; // 1
[bumpDistortion setValue: result forKey: @"inputImage"];
[bumpDistortion setValue: [CIVector vectorWithX:200 Y:150 ]
forKey: @"inputCenter"]; // 3
[bumpDistortion setValue: [NSNumber numberWithFloat: 100]
forKey: @"inputRadius"]; // 4
[bumpDistortion setValue: [NSNumber numberWithFloat: 3.0]
forKey: @"inputScale"]; // 5
result = [bumpDistortion valueForKey: @"outputImage"];
說明:
1. 通過濾鏡名稱創建濾鏡
2. OSX特有的處理,這裏刪掉了。
3. 將效果的中心點設置在圖像的中心位置。
4. 設置扭曲的半徑爲100。
5. 將輸入標量設置爲3。輸入標量定義了效果的方向和數量。
(原文:The input scale specifies the direction and the amount of the effect. )
缺省值是-0.5。範圍是-10.0到10.0。0是沒有效果。負值會創建一個向外扭曲的效果。正值向內扭曲。
效果:
10 使用過渡(transition)效果
過渡通常用於圖像間的滑動,或者在視頻中從一個場景切換到另個場景。
這些效果需要經過一段時間進行渲染,你需要爲此設置一個定時器。本斷的目的是展示如何設置一個定時器。copy machine transition(CICopyMachine)創建了一個發光條,類似於複印機或者圖片掃描儀。發光條從左向右掃過原始圖像,並揭示目標圖像。
過渡濾鏡需要完成一下任務:
1. 創建用於過渡的Core Image p_w_picpaths (CIImage objects) 。
2. 設置一個定時器。
3. 創建一個CIContext 對象。
4. 創建一個CIFilter對象來處理圖像。
6. 設置濾鏡參數。
7. 設置要處理的源與目標圖像。
8. 計算時間。
9. 應用濾鏡。
10. 繪製結果。
11. 重複8-10步,直到完成過渡。
效果:
代碼比較長,直接看原文吧。
11 在視頻上使用濾鏡
Core Image 和 Core Video一起工作,獲得豐富的效果。比如,在水下拍攝視頻時,
你可以使用一個顏色校正濾鏡來修正(水吸收紅光要比藍光更快)。有很多種方式讓你可以使用這些技術。
例子是OS X的,不貼出來了。
二 人臉探測
Core Image可以對圖像進行分析,並找到人臉。它進行的是人臉探測,但不是識別(識別的意思是能認出你是誰,探測的意思是是能找到人臉在圖像中的位置)。Core Image檢測出人臉後,可以提供面部特性,比如眼睛和嘴的位置。它還可以跟蹤視頻中人臉的位置。
知道臉在圖像中的位置讓你可以執行一些其他的操作,比如:對頭像做剪裁或者調整圖像質量的操作 (tone balance, red-eye correction and so on)。你還可以在臉上做其他有趣的操作:
● 僅在人臉上應用像素濾鏡。
● 在人臉邊上加小插圖。
1 探測人臉
CIContext *context = [CIContext contextWithOptions:nil]; // 1
NSDictionary *opts = [NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh
forKey:CIDetectorAccuracy]; // 2
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace
context:context
options:opts]; // 3
opts = [NSDictionary dictionaryWithObject:
[myImage properties] valueForKey:kCGImagePropertyOrientation;
forKey:CIDetectorImageOrientation];// 4
NSArray *features = [detector featuresInImage:myImage
options:opts]; // 5
1. 創建一個CIContext對象。
2. 創建一個選項字典來定義detector的精度。你可以定義低精度或高精度。
低精度 (CIDetectorAccuracyLow) 速度快; 高精度,更徹底,但是比較慢。
3. 創建一個用於人臉檢測的detector。你唯一能創建的就是用於人臉檢測的detector。
4. 設定一個用於檢測人臉的選項字典。有一點是非常重要的,必須讓Core Image 知道圖像的方向。這樣detector才能知道怎樣查找人臉。通常你可以從圖像本身獲得方向的信息,並傳入字典。
5. 使用detector去查找人臉的面部特徵。你提供的圖像必須是一個CIImage對象。Core Image返回一個CIFeature對象的數組,你可能想找到他們的特性,比如眼睛,嘴的位置。
2 獲得臉和臉部特徵界限
臉部特徵包括:
- 左右眼睛的位置
- 嘴巴的位置
- 跟蹤ID和跟蹤幀數(Core Image 以此來跟蹤視頻斷中的臉)(available in iOS v6.0 and later and in OS X v10.8 and later)
在你通過CIDetector對象獲得一個臉部特徵數據的array後,你可以循環這個array來檢測每張臉的邊界及其特性。
for (CIFaceFeature *f in features)
{
NSLog(NSStringFromRect(f.bounds));
if (f.hasLeftEyePosition)
printf("Left eye %g %g\n", f.leftEyePosition.x, f.leftEyePosition.y);
if (f.hasRightEyePosition)
printf("Right eye %g %g\n", f.rightEyePosition.x, f.rightEyePosition.y);
if (f.hasmouthPosition)
printf("Mouth %g %g\n", f.mouthPosition.x, f.mouthPosition.y);
}
三 自動增強圖像
Core Image的自動增強特性分析圖像的直方圖,臉部區域內容,和元數據屬性。之後返回一個CIFilter對象的array,這些對象的參數已經被設置好,用來增強被分析的圖像。
(Auto enhancement is available in iOS v5.0 and later and in OS X v10.8 and later)
1 自動增強濾鏡
下面這些濾鏡可以修正照片中的大部分問題:
2 使用自動增強濾鏡
自動增強API僅有2個方法: autoAdjustmentFilters和
autoAdjustmentFiltersWithOptions:. 在大多數情況下,你會使用帶有選項字典參數的方法。
你可以做以下設置:
- 對於CIRedEyeCorrection和CIFaceBalance 濾鏡,圖像的方向是很重要的,所以Core Image 可以精確的定位到臉。
- 是否僅應用紅眼校正。(把kCIImageAutoAdjustEnhance設置爲false)
- 是否使用紅眼以外的全部其他濾鏡。(把kCIImageAutoAdjustRedEye設置爲false)
autoAdjustmentFiltersWithOptions: 返回一個備選濾鏡的array,你可以把他們鏈接起來並應用到被分析的圖像。下面的代碼先創建一個選項字典。之後獲取圖像的方向並將其設置爲鍵CIDetectorImageOrientation的值。
NSDictionary *options = [NSDictionary dictionaryWithObject:
[p_w_picpath properties] valueForKey:kCGImagePropertyOrientation]
forKey:CIDetectorImageOrientation];
NSArray *adjustments = [myImage autoAdjustmentFiltersWithOptions:options];
for (CIFilter *filter in adjustments){
[filter setValue:myImage forKey:kCIInputImageKey];
myImage = filter.outputImage;
}
回想一下,這些輸入參數值都已經被Core Image 設定好,併產生最佳結果。
你不必立即應用自動調整濾鏡。你可以保存濾鏡名稱及參數,後續使用。保存這些使你的app可以在後續執行增強圖像操作時,不必再重新分析圖像。
四 向系統查詢濾鏡
Core Image提供了一些方法,你可以向系統查詢內置的濾鏡,以及每個濾鏡的相關信息-顯示名稱,輸入參數,參數類型,缺省值,等等。向系統查詢使你可以得到最新的濾鏡信息。
如果你的應用允許用戶選擇和設置濾鏡,那麼當你創建一個設置濾鏡的用戶界面時,你可以使用這些信息。
1 獲取濾鏡列表和屬性
使用filterNamesInCategory:和filterNamesInCategories:方法來得到系統支持哪些濾鏡。濾鏡被分了類別,這樣更易於管理。如果你知道一個濾鏡的類別,你可以通過filterNamesInCategory:查到這個類別支持哪些濾鏡。如果你想查一個類別列表支持的所有濾鏡,你可以調用filterNamesInCategories:方法。這個方法返回一個NSArray對象,裏面每個類別的濾鏡名稱。你可以把類別列表參數設置爲nil,來獲得所有類別的濾鏡的列表。
類別常量:
在你獲得一個濾鏡名稱列表後,你可以通過創建一個濾鏡對象,並調用屬性方法來獲得濾鏡的屬性。
CIFilter *myFilter;
NSDictionary *myFilterAttributes;
myFilter = [CIFilter filterWithName:@"<Filter Name Here>"];
myFilterAttributes = [myFilter attributes];
在真正編碼時,你要將"<Filter Name Here>"替換爲你感興趣的濾鏡名稱。
屬性包括這些內容:名稱,類別,類,最小、最大值。
2 構建一個濾鏡字典
如果你的應用提供了一個用戶界面,(你的應用)可以根據濾鏡字典創建和更新界面。
比如,濾鏡屬性是個布爾值,需要一個checkbox或者類似的界面元素,對於在一個範圍內持續變化的屬性,使用一個slider。你可以使用text label設置最大最小值。屬性的缺省值,就是用戶界面的初值。
濾鏡名稱和屬性提供充足的信息,使你可以構建一個用戶界面,並允許用戶選擇濾鏡,控制其輸入參數。
categories = [NSMutableDictionary alloc] init];
NSMutableArray *array;
array = [NSMutableArray arrayWithArray:
[CIFilter filterNamesInCategory:
kCICategoryGeometryAdjustment];
[array addObjectsFromArray:
[CIFilter filterNamesInCategory:
kCICategoryDistortionEffect];
[categories setObject: [self buildFilterDictionary: array]
forKey: @"Distortion"];
array = [NSMutableArray arrayWithArray:
[CIFilter filterNamesInCategory: kCICategorySharpen];
[array addObjectsFromArray:
[CIFilter filterNamesInCategory: kCICategoryBlur];
[categories setObject: [self buildFilterDictionary: array]
forKey:@"Focus"];
// 根據濾鏡名稱構造字典
- (NSMutableDictionary *)buildFilterDictionary: (NSArray *)names // 1
{
NSMutableDictionary *td, *catfilters;
NSDictionary *attr;
NSString *classname;
CIFilter *filter;
int i;
catfilters = [NSMutableDictionary dictionary];
for(i=0 ; i<[names count] ; i++) // 2
{
classname = [names objectAtIndex: i]; // 3
filter = [CIFilter filterWithName: classname]; // 4 構造濾鏡
if(filter)
{
attr = [filter attributes]; // 5 濾鏡屬性
td = [NSMutableDictionary dictionary];
[td setObject: classname forKey: @"class"]; // 6
[catfilters setObject: td
forKey:[attr objectForKey:@"name"]; // 7
}
else
NSLog(@" could not create '%@' filter", classname);
}
return catfilters;
}
五 繼承CIFilter:定製效果(Effect)的配方
你可以將一個濾鏡的輸入作爲另一個濾鏡的輸入,把多個濾鏡鏈接到一起,從而定製出特定的效果。
當你創建一種效果,並且後續想要複用,可以考慮繼承CIFilter來把這種效果封裝到一個濾鏡中。
本章介紹如何繼承CIFilter,創建一個CIColorInvert濾鏡。
之後描述了將多種濾鏡鏈接到一起,達到有趣效果的方式。
1 繼承CIFilter創建CIColorInvert濾鏡
當你繼承CIFilter時,你可以對現有的濾鏡進行設置或者將現有濾鏡鏈接起來。Core Image通過這種方式實現了一些內置的濾鏡。
要繼承一個濾鏡,你需要完成以下工作:
- 聲明濾鏡的輸入參數屬性。你必須預先設定好每個以input開頭的輸入參數,比如inputImage
- 如果有必要的話,覆寫setDefaults方法。
- 覆寫outputImage方法。
Core Image提供的CIColorInvert濾鏡,就是CIColorMatrix濾鏡的變異。見名知意,CIColorInvert向CIColorInvert提供向量,來反轉輸入圖像的顏色。
代碼:
@interface CIColorInvert: CIFilter{
CIImage *inputImage;
}
@property (retain, nonatomic) CIImage *inputImage;
@end
@implementation CIColorInvert
@synthesize inputmage;
- (CIImage *) outputImage {
return [CIFilter filterWithName:@"CIColorMatrix"
keysAndValues:
kCIInputImageKey, inputImage,
@"inputRVector ", [CIVector vectorWithX: -1 Y:0 Z:0 ],
@"inputGVector", [CIVector vectorWithX:0 Y:-1 Z:0 ],
@"inputBVector ", [CIVector vectorWithX: 0 Y:0 Z:-1],
@"inputBiasVector ", [CIVector vectorWithX:1 Y:1 Z:1],
nil, outputImage];
}
2 Chroma Key Filter Recipe(濃度鍵濾鏡配方)
從原圖中刪除一種顏色,或者某個範圍內的顏色,之後將原圖和背景組合起來。
創建一個Chroma Key濾鏡需要以下操作:
- Create a cube map of data that maps the color values you want to remove so they are transparent (alpha value is 0.0). 不會翻譯了-_-|||,大概意思是會把要過濾掉的顏色,設置爲透明色。
- Use the CIColorCube filter and the cube map to remove chroma-key color from the source p_w_picpath.使用CIColorCube濾鏡和cube map從原圖上刪除濃度-鍵顏色。
3 創建一個Cube Map
一個color cube是一個3D顏色查找表(lookup table)。Core Image 濾鏡 CIColorCube 使用色值作爲輸入,並應用一個查找表到這些色值。
CIColorCube的缺省查找表是一個特性矩陣-矩陣的含義是不會對輸入數據做任何操作。然而,這個配方需要你從圖像中刪除所有的綠色。你要把圖中的把綠色的alpha值設置爲0.0(透明),來刪除綠色。
“綠色”包括一定範圍內的顏色。最直接的處理方式是把圖像的色值從RGBA轉爲HSV。
HSV格式,顏色是使用圍繞柱面中軸的角度來表示的。在這種表示方式下,你可以把顏色想象成餅圖上的一片(pie slice),之後,簡單的刪掉chroma-key color。
要刪除綠色,你需要定義圍繞中心點的最小和最大的角度。之後,對於任何的綠色,將其alpha值設置爲0.0。純綠的相對角度是120o。最小值和最大值要以這個值爲中心。
Cube map數據必須預乘alpha,所以創建cube map的最後一步是把RGB值乘以你剛剛計算出的alpha值(如果是綠色,就是0,如果不是就是1.0)。
代碼:
// Allocate memory
const unsigned int size = 64;
float *cubeData = (float *)malloc (size * size * size * sizeof (float) * 4);
float rgb[3], hsv[3], *c = cubeData;
// Populate cube with a simple gradient going from 0 to 1
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
// Convert RGB to HSV
// You can find publicly available rgbToHSV functions on the Internet
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的指針不移動???
}
}
}
// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
length:cubeDataSize
freeWhenDone:YES];
CIColorCube *colorCube = [CIFilter filterWithName:@"CIColorCube"];
[colorCube setValue:[NSNumber numberWithInt:size] forKey:@"inputCubeDimension"];
// Set data for cube
[colorCube setValue:data forKey:@"inputCubeData"];
4 從原圖中刪除綠色
現在你有了color map data,給CIColorCube濾鏡提供背景圖(從這張圖上把綠色刪掉),並得到輸出圖像。
[colorCube setValue:myInputImage forKey:@"inputImage"];
CIImage *result = [colorCube valueForKey:kCIOutputImageKey];
5 混合處理過的圖像與背景圖
設置CISourceOverCompositing濾鏡的參數:
● 把CIColorCube濾鏡處理後的圖像作爲 inputImage參數。
● 把新的背景圖作爲inputBackgroundImage參數。
前面的圖現在看起來就像在海灘上。
6 White Vignette for Faces Filter Recipe (iOS only) 臉部濾鏡配方的白暈影
創建一個白暈影濾鏡:
● 找到圖像中的人臉
● 在CIRadialGradient上以人臉爲中心,創建一個base shade map
● 將base shade map與人臉混合
7 找到人臉
使用CIDetector獲得人臉在圖像中的位置。featuresInImage:options:函數返回的array的第一項就是濾鏡要操作的對象。在你有臉後(-_-),根據探測器提供的邊界計算臉部中心。你需要中心值來創建create the shade map。
CIDetector *detector = [CIDector detectorOfType:CIDetectorTypeFace
context:nil
options:nill];
NSArray *faceArray = [detector featuresInImage:p_w_picpath options:nil];
CIFeature *face = (CIFeature *) [faceArray objectAtIndex:0]; // 也不怕crash...
CGFloat xCenter = face.bounds.origin.x + face.bounds.size.width/2.0;
CGFloat yCenter = face.bounds.origin.y + face.bounds.size.height/2.0;
CIVector *center = [CIVector vectorWithX:xCenter Y:yCenter];
8 創建一個Shade Map
使用CIRadialGradient濾鏡創建一個以人臉爲中心的shade map。shade map的中心必須是透明的,這樣圖像中的人臉才能保持未變化。map的邊緣應該是不透明的白色。這兩個區域之間是不同程度的透明。
爲了達到這個效果,如下設置CIRadialGradient的參數:
● 設置 inputRadius0 成一個大於圖像最長維度的值
● 設置 inputRadius1 成一個比人臉大的值,比如face.bounds.size.height + 50.
● 設置 inputColor0 爲不透明的白.
● 設置 inputColor1 爲透明的白.
● 設置 inputCenter 爲你計算出的人臉的中心.
9 混合Gradient(梯度)與人臉
設置CISourceOverCompositing濾鏡的輸入參數:
● 設置 inputImage 爲原圖.
● 設置 inputBackgroundImage爲上步生成的shade map.
10 移軸濾鏡配方(Tilt-Shift Filter Recipe)
效果:
創建一個移軸濾鏡要完成的工作:
- 創建一個模糊的圖像
- 創建兩個線性gradients
- 通過混合線性gradients來創建一個遮掩
- 將模糊圖像,遮掩,原圖混合
下面的段落介紹如何執行每一步:
11 創建圖像的模糊版本
如下設置CIGaussianBlur的輸入參數:
- 把inputImage參數設置爲你要處理的圖像
- 把inputRadius設置爲10.0
12 創建線性梯度
從一個單色(如綠色或灰色)創建一個線性梯度,從上倒下變化。
如下設置CILinearGradient的輸入參數:
● Set inputPoint0 to (0, 0.75 * h)
● Set inputColor0 to (0,1,0,1)
● Set inputPoint1 to (0, 0.5*h)
● Set inputColor1 to (0,1,0,0)
創建一個綠色線性梯度,從上到下變化。
如下設置CILinearGradient的輸入參數:
● Set inputPoint0 to (0, 0.25 * h)
● Set inputColor0 to (0,1,0,1)
● Set inputPoint1 to (0, 0.5*h)
● Set inputColor1 to (0,1,0,0)
13 通過線性梯度,創建遮掩
如下設置CIAdditionCompositing濾鏡的參數:
- 給你創建的第一個線性梯度設置輸入圖像(inputImage)
- 給你創建的第二個線性梯度設置輸入背景圖像(inputBackgroundImage)
14 混合模糊圖像,原圖和梯度
最後一步使用CIBlendWithMask濾鏡,如下設置輸入參數:
- 設置inputImage參數爲圖像的模糊版本
- 設置inputBackgroundImage參數爲未處理的圖像
- 設置inputMaskImage爲混合的梯度
遮掩只會影響圖像中有東西的部分,透明的部分會被略過,顯示原始圖像。不透明的部分允許顯示模糊圖像。
其他濾鏡不翻譯了,需要時直接查文檔吧。
六 獲得最佳性能
Core Image提供了很多可選項來創建圖像,上下文和渲染內容。
你如何完成一項任務,取決於:
- 你的app需要執行一項任務的頻率
- 你的app是使用靜態還是視頻圖像
- 你是否需要支持實時的處理和分析
- 顏色的保真度對你的用戶是否重要
你應該通讀性能最佳實踐 (performance best practices),以確保您的app能夠高效地運行。
1 性能最佳實踐
如下來實踐最佳性能:
- 不要每次渲染都創建一個CIContext對象
上下文中保存了大量的狀態信息;重用他們會更加高效。
- 評估一下你的app是否需要顏色管理。如果你不需要就不要使用它。
- 使用GPU上下文渲染CIImage對象時,避免Core Animation的動畫。
- 保證圖像不超過CPU和GPU的限制。
Core Image 使用CPU或者GPU時CIContext對圖像大小的限制是不同的。
通過inputImageMaximumSize和outputImageMaximumSize來獲得限制的實際值。
- 儘可能使用小圖像。
性能會隨着輸出的像素數增加。你可以讓Core Image渲染到一個小的視圖,質地,或者幀緩衝區。
Allow Core Animation to upscale to display size.
使用Core Graphics或Image I/O函數來剪切或者採樣,比如
CGImageCreateWithImageInRect 或者 CGImageSourceCreateThumbnailAtIndex。
● UIImageView類最適合處理靜態圖像。
如果你的app要獲得最佳性能,請使用底層API。
● 避免CPU和GPU之間不必要的質地轉換。
●
Render to a rectangle that is the same size as the source p_w_picpath before applying a contents scale factor.
● 考慮使用能達到近似於算法濾鏡效果的簡單濾鏡。
比如,CIColorCube能產生與CISepiaTone近似的輸出,並且更高效。
● Take advantage of the support for YUV p_w_picpath in iOS v6.0 and later.
Camera pixel buffers are natively YUV but most p_w_picpath processing algorithms expect RBGA data. There is a cost to converting between the two. Core Image supports reading YUB from CVPixelBuffer objects and applying the appropriate color transform.
YUV, YUB是神碼?
options = @{ (id)kCVPixelBufferPixelFormatTypeKey: [NSNumber
numberWithInt:kCVPixelFormatType_420YpCvCr88iPlanarFullRange]};
2 你的APP需要顏色管理嗎?
默認情況下, Core Image 應用所有的濾鏡於輕-線性顏色空間。
這提供了最精準和一致的結果。
轉變到sRGB,和向sRGB轉變,增加了濾鏡的複雜程度,需要Core Image去應用這些方程:
rgb = mix(rgb.0.0774, pow(rgb*0.9479 + 0.05213, 2.4), step(0.04045, rgb))
rgb = mix(rgb12.92, pow(rgb*0.4167) * 1.055 - 0.055, step(0.00313, rgb))
以下場景,要考慮禁止顏色管理:
- 你的app需要絕對的高性能。
- 誇張的操作(exaggerated manipulations)後用戶不會注意到質量差別。
要禁用顏色管理,將kCIImageColorSpace鍵設置爲null。
如果你使用一個EAGL上下文,也要在創建EAGL上下文時,將上下文顏色空間設置爲null。
七 Using Feedback to Process Images
iOS 不支持,略過。
八 在實現一個自定義濾鏡前,你需要知道的幾件事
OS X provides support for writing custom filters.
iOS不支持,略過。
九 創建自定義濾鏡
On OS X, if the filters provided by Core Image don’t provide the functionality you need, you can write your own filter. (Custom filters are not available on iOS.)
iOS不支持,略過。
十 打包和加載圖像單元
An p_w_picpath unit represents the plug-in architecture for Core Image filters. Image units use the NSBundle class as the packaging mechanism to allow you to make the filters that you create available to other apps. An p_w_picpath unit can contain filters that are executable or nonexecutable. (See “Executable and Nonexecutable Filters” (page
69) for details.)
CIFilter 是一個可變對象,代表一種效果。一個濾鏡對象至少要有一個輸入參數,併產生一個輸出圖片。
CIImage 是一個不變對象,代表一個圖片。你可以通過圖像數據,或者通過文件,或者另一個CIFilter對象的輸出得到一個CIImage。
CIContext 是一個對象,通過它Core Image可以繪製一個CIFilter產生的結果。一個Core Image Context可以基於CPU或GPU。
1 概覽
源碼:
CIContext *context = [CIContext contextWithOptions:nil]; // 1
CIImage *p_w_picpath = [CIImage p_w_picpathWithContentsOfURL:myURL]; // 2
CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone"]; // 3
[filter setValue:p_w_picpath forKey:kCIInputImgeKey];
[filter setValue:[NSNumber numberWithFloat:0.8f] forKey:@"InputIntensity"];
CIImage *result = [filter valueForKey:kCIOutputImageKey]; // 4
CGImageRef cgImage = [context createCGImage:result fromRect:[result extent]; // 5
說明:
1. 創建一個CIContext對象。這個方法你可以在iOS上使用。
2. 創建一個CIImage對象. 你可以從多種來源創建一個CIImage對象,不日URL。
3. 創建濾鏡,並設置其輸入參數。
4. 獲得輸出圖像,輸出圖像是一個描述如何生成圖像的配方(這裏的配方是描述如何製作一道菜的配方,不是點菜的菜譜)。圖像還沒有被渲染出來。
5. 渲染CIImage,得到CGImageRef,CGImageRef是可以直接展示或者保存到文件中的。
2 內置濾鏡(說明:原文是filter,這裏採用濾鏡更直觀,因此不翻譯爲過濾器)
Core Image提供了大量內置的濾鏡,供你的應用做圖像處理。不同iOS版本提供的濾鏡是不同的,所以Core Image 提供了方法,讓你可以查詢系統提供了哪些濾鏡。
一個濾鏡類別定義了各種效果的類型,或者其可以作用的目標:
p_w_picpaths, video, nonsquare pixels, 等等.
一個濾鏡可以屬於多個類別。濾鏡有一個顯示名稱(給用戶看的)和一個濾鏡名稱(在代碼中要使用的)。
濾鏡屬性是以鍵-值對的形式保存的。鍵是一個常量,標識屬性及其對應的值。
Core Image使用鍵-值編碼,你可以通過KVC獲得屬性的值。
3 創建一個Core Image Context
要渲染圖像,你需要創建Core Image Context,並使用這個上下文繪製輸出圖像。
一個Core Image context標識一個繪製的目標。目標決定了使用CPU還是GPU去渲染。
創建的函數:
contextWithOptions: CPU或GPU都支持。
以下僅支持GPU:
contextWithCGLContext: pixelFormat:options: GPU OS X
contextWithCGLContext: pixelFormat:
colorSpace:options:
[1] 在iOS上創建一個Core Image Context,When You Don’t Need Real-Time
Performance
源碼:
CIContext *context = [CIContext contextWithOptions:nil];
這個方法可以使用CPU或者GPU進行渲染。要定義使用哪個,需要設置一個選項字典,增加鍵kCIContextUserSoftwareRenderer,並設置相應的布爾值。CPU渲染要比GPU要慢。但是對於GPU渲染,在渲染結果被拷貝回CPU內存並轉化爲UIImage之前,不會被顯示出來。
[2] 在iOS上創建一個Core Image Context,When You Need Real-Time Performance
如果你的應用要支持實時的圖像處理,你需要從一個EAGL context創建一個CIContext對象,不能使用contextWithOptions: 方法。優點是渲染的圖像保存在GPU上,並不需要拷貝回CPU內存。你首先需要創建一個EAGL context:
myEAGLContext = [EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
之後使用方法contextWithEAGLContext: 創建一個CIContext對象。
你應該將working color space設置爲null,來關閉顏色管理功能。顏色管理會降低性能。在需要顏色保真的情況下,你才需要顏色管理。但是對於一個real-time app,顏色保真通常並不是必要的。
代碼:
NSMutableDictionary *options = [NSMutableDictionary alloc] init];
[options setObject: [NSNull null] forKey: kCIContextWorkingColorSpace];
myContext = [CIContext contextWithEAGLContext:myEAGLContext options:options];
4 創建一個CIImage對象
Core Image filters 處理Core Image p_w_picpaths (CIImage objects).
記住,一個CIImage對象實際上是一個配方;在被調用,在目標上渲染結果之前,Core Image實際上不產生一個像素。創建的方法:
5 創建一個CIFilter並設置屬性值
filterWithName: 方法創建了一個過濾器,通過濾鏡名稱參數來設定濾鏡的類型。
名稱參數是一個字符串,其值必須與內置濾鏡嚴格匹配。
代碼:
hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"];
[hueAdjust setValue: myCIImage forKey: @"inputImage"];
[hueAdjust setValue: [NSNumber numberWithFloat: 2.094]
forKey: @"inputAngle"];
另一種方式:
hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"] keysAndValues:
@"inputImage",myCIImage,
@"inputAngle, [NSNumber numberWithFloat: 2.094],
nil]; // 你可以增加更多參數,但是必須以nil結尾。
6 獲取輸出圖像
你通過outputImage鍵,獲得輸出圖像。
CIImage *result = [hueAdjust valueForKey: @"outputImage"];
在你調用某個方法實際執行渲染之前,Core Image不會執行任何的圖像處理操作。
當你請求去輸出圖像時,Core Image組裝並保存要生成一個輸出圖像的運算步驟(保存到一個CIImage對象中)。只有在你顯示調用一個圖像繪製方法時,實際的圖像纔會被渲染。
將處理延遲到渲染的時候,使得Core Image 快速而高效。在渲染的時候,Core Image會判斷是否有超過一個濾鏡需要作用到一個圖像上。如果是這樣,它自動把多個配方連接成一個操作(好像就是做一系列的矩陣運算,運算結果仍是一個矩陣,所以可以合成一步對結果矩陣的操作,說明by王雲鵬),即每個像素只會被處理一次,而不是多次。
下圖列出了一個multiple-operations workflow ,Core Image 可以更高效。最終圖像是原始圖像的一個縮小版本。
對於一個大圖像,在大小調整之前,應用顏色調整,比先做大小調整,之後再應用顏色調整,需要更多的處理資源。
通過將濾鏡處理操作延遲到渲染的時候,Core Image可以按照最高效的處理順序來執行操作。
7 渲染輸出圖像
渲染輸出圖像會觸發CPU或者GPU(依賴於你設置的context)的密集的處理器操作。下面的函數可以用來渲染:
代碼:
[myContext drawImage:result inRect:destinationRect fromRect:contextRect];
效果對比:
8 保證線程安全
CIContext 和CIImage對象是不可變的, 在線程之間共享這些對象是安全的.
多個線程可以使用同一個GPU或者CPU CIContext對象來渲染CIImage對象。
但是對於可變的CIFilter確不行。一個CIFilter對象不能在多個線程間共享。如果你的應用是多線程的,每個線程都必須創建自己的CIFilter對象。否則,你的應用將產生不可預期的結果。
9 濾鏡鏈
你可以通過濾鏡鏈創造出驚人的效果,即將一個濾鏡的輸出作爲另一個濾鏡的輸入。
讓我們看看在一個圖像上如何應用兩個濾鏡—幽暗(?gloom)(CIGloom) 和爆沸畸變 (?bump distortion)(CIBumpDistortion).
gloom濾鏡:通過淡化亮度來使一張圖片變暗。
gloom = [CIFilter filterWithName:@"CIGloom"]; // 1
[gloom setValue: result forKey: @"inputImage"];
[gloom setValue: [NSNumber numberWithFloat: 25]
forKey: @"inputRadius"]; // 2
[gloom setValue: [NSNumber numberWithFloat: 0.75]
forKey: @"inputIntensity"]; // 3
result = [gloom valueForKey: @"outputImage"]; // 4
1. 創建一個CIGloom類型的濾鏡。
2. 設置輸入半徑爲25。半徑定義了效果的外延,可以設置0到100之間的值,缺省值是10。(你可以通過濾鏡的屬性字典獲得濾鏡的最小、最大、缺省值)。
3. 將輸入強度設置爲0.75。輸入強度是一個標量值,它定義了原圖與濾鏡輸出之間的一個線性的混合。最小值是0.0,最大值是1.0,缺省值是1.0。
4. 請求輸出圖像,但並不真正繪製。
處理結果:
bump distortion 濾鏡 (CIBumpDistortion)在一個圖像上的某個點創建一個突起。
源碼:
bumpDistortion = [CIFilter filterWithName:@"CIBumpDistortion"]; // 1
[bumpDistortion setValue: result forKey: @"inputImage"];
[bumpDistortion setValue: [CIVector vectorWithX:200 Y:150 ]
forKey: @"inputCenter"]; // 3
[bumpDistortion setValue: [NSNumber numberWithFloat: 100]
forKey: @"inputRadius"]; // 4
[bumpDistortion setValue: [NSNumber numberWithFloat: 3.0]
forKey: @"inputScale"]; // 5
result = [bumpDistortion valueForKey: @"outputImage"];
說明:
1. 通過濾鏡名稱創建濾鏡
2. OSX特有的處理,這裏刪掉了。
3. 將效果的中心點設置在圖像的中心位置。
4. 設置扭曲的半徑爲100。
5. 將輸入標量設置爲3。輸入標量定義了效果的方向和數量。
(原文:The input scale specifies the direction and the amount of the effect. )
缺省值是-0.5。範圍是-10.0到10.0。0是沒有效果。負值會創建一個向外扭曲的效果。正值向內扭曲。
效果:
10 使用過渡(transition)效果
過渡通常用於圖像間的滑動,或者在視頻中從一個場景切換到另個場景。
這些效果需要經過一段時間進行渲染,你需要爲此設置一個定時器。本斷的目的是展示如何設置一個定時器。copy machine transition(CICopyMachine)創建了一個發光條,類似於複印機或者圖片掃描儀。發光條從左向右掃過原始圖像,並揭示目標圖像。
過渡濾鏡需要完成一下任務:
1. 創建用於過渡的Core Image p_w_picpaths (CIImage objects) 。
2. 設置一個定時器。
3. 創建一個CIContext 對象。
4. 創建一個CIFilter對象來處理圖像。
6. 設置濾鏡參數。
7. 設置要處理的源與目標圖像。
8. 計算時間。
9. 應用濾鏡。
10. 繪製結果。
11. 重複8-10步,直到完成過渡。
效果:
代碼比較長,直接看原文吧。
11 在視頻上使用濾鏡
Core Image 和 Core Video一起工作,獲得豐富的效果。比如,在水下拍攝視頻時,
你可以使用一個顏色校正濾鏡來修正(水吸收紅光要比藍光更快)。有很多種方式讓你可以使用這些技術。
例子是OS X的,不貼出來了。
二 人臉探測
Core Image可以對圖像進行分析,並找到人臉。它進行的是人臉探測,但不是識別(識別的意思是能認出你是誰,探測的意思是是能找到人臉在圖像中的位置)。Core Image檢測出人臉後,可以提供面部特性,比如眼睛和嘴的位置。它還可以跟蹤視頻中人臉的位置。
知道臉在圖像中的位置讓你可以執行一些其他的操作,比如:對頭像做剪裁或者調整圖像質量的操作 (tone balance, red-eye correction and so on)。你還可以在臉上做其他有趣的操作:
● 僅在人臉上應用像素濾鏡。
● 在人臉邊上加小插圖。
1 探測人臉
CIContext *context = [CIContext contextWithOptions:nil]; // 1
NSDictionary *opts = [NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh
forKey:CIDetectorAccuracy]; // 2
CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeFace
context:context
options:opts]; // 3
opts = [NSDictionary dictionaryWithObject:
[myImage properties] valueForKey:kCGImagePropertyOrientation;
forKey:CIDetectorImageOrientation];// 4
NSArray *features = [detector featuresInImage:myImage
options:opts]; // 5
1. 創建一個CIContext對象。
2. 創建一個選項字典來定義detector的精度。你可以定義低精度或高精度。
低精度 (CIDetectorAccuracyLow) 速度快; 高精度,更徹底,但是比較慢。
3. 創建一個用於人臉檢測的detector。你唯一能創建的就是用於人臉檢測的detector。
4. 設定一個用於檢測人臉的選項字典。有一點是非常重要的,必須讓Core Image 知道圖像的方向。這樣detector才能知道怎樣查找人臉。通常你可以從圖像本身獲得方向的信息,並傳入字典。
5. 使用detector去查找人臉的面部特徵。你提供的圖像必須是一個CIImage對象。Core Image返回一個CIFeature對象的數組,你可能想找到他們的特性,比如眼睛,嘴的位置。
2 獲得臉和臉部特徵界限
臉部特徵包括:
- 左右眼睛的位置
- 嘴巴的位置
- 跟蹤ID和跟蹤幀數(Core Image 以此來跟蹤視頻斷中的臉)(available in iOS v6.0 and later and in OS X v10.8 and later)
在你通過CIDetector對象獲得一個臉部特徵數據的array後,你可以循環這個array來檢測每張臉的邊界及其特性。
for (CIFaceFeature *f in features)
{
NSLog(NSStringFromRect(f.bounds));
if (f.hasLeftEyePosition)
printf("Left eye %g %g\n", f.leftEyePosition.x, f.leftEyePosition.y);
if (f.hasRightEyePosition)
printf("Right eye %g %g\n", f.rightEyePosition.x, f.rightEyePosition.y);
if (f.hasmouthPosition)
printf("Mouth %g %g\n", f.mouthPosition.x, f.mouthPosition.y);
}
三 自動增強圖像
Core Image的自動增強特性分析圖像的直方圖,臉部區域內容,和元數據屬性。之後返回一個CIFilter對象的array,這些對象的參數已經被設置好,用來增強被分析的圖像。
(Auto enhancement is available in iOS v5.0 and later and in OS X v10.8 and later)
1 自動增強濾鏡
下面這些濾鏡可以修正照片中的大部分問題:
2 使用自動增強濾鏡
自動增強API僅有2個方法: autoAdjustmentFilters和
autoAdjustmentFiltersWithOptions:. 在大多數情況下,你會使用帶有選項字典參數的方法。
你可以做以下設置:
- 對於CIRedEyeCorrection和CIFaceBalance 濾鏡,圖像的方向是很重要的,所以Core Image 可以精確的定位到臉。
- 是否僅應用紅眼校正。(把kCIImageAutoAdjustEnhance設置爲false)
- 是否使用紅眼以外的全部其他濾鏡。(把kCIImageAutoAdjustRedEye設置爲false)
autoAdjustmentFiltersWithOptions: 返回一個備選濾鏡的array,你可以把他們鏈接起來並應用到被分析的圖像。下面的代碼先創建一個選項字典。之後獲取圖像的方向並將其設置爲鍵CIDetectorImageOrientation的值。
NSDictionary *options = [NSDictionary dictionaryWithObject:
[p_w_picpath properties] valueForKey:kCGImagePropertyOrientation]
forKey:CIDetectorImageOrientation];
NSArray *adjustments = [myImage autoAdjustmentFiltersWithOptions:options];
for (CIFilter *filter in adjustments){
[filter setValue:myImage forKey:kCIInputImageKey];
myImage = filter.outputImage;
}
回想一下,這些輸入參數值都已經被Core Image 設定好,併產生最佳結果。
你不必立即應用自動調整濾鏡。你可以保存濾鏡名稱及參數,後續使用。保存這些使你的app可以在後續執行增強圖像操作時,不必再重新分析圖像。
四 向系統查詢濾鏡
Core Image提供了一些方法,你可以向系統查詢內置的濾鏡,以及每個濾鏡的相關信息-顯示名稱,輸入參數,參數類型,缺省值,等等。向系統查詢使你可以得到最新的濾鏡信息。
如果你的應用允許用戶選擇和設置濾鏡,那麼當你創建一個設置濾鏡的用戶界面時,你可以使用這些信息。
1 獲取濾鏡列表和屬性
使用filterNamesInCategory:和filterNamesInCategories:方法來得到系統支持哪些濾鏡。濾鏡被分了類別,這樣更易於管理。如果你知道一個濾鏡的類別,你可以通過filterNamesInCategory:查到這個類別支持哪些濾鏡。如果你想查一個類別列表支持的所有濾鏡,你可以調用filterNamesInCategories:方法。這個方法返回一個NSArray對象,裏面每個類別的濾鏡名稱。你可以把類別列表參數設置爲nil,來獲得所有類別的濾鏡的列表。
類別常量:
在你獲得一個濾鏡名稱列表後,你可以通過創建一個濾鏡對象,並調用屬性方法來獲得濾鏡的屬性。
CIFilter *myFilter;
NSDictionary *myFilterAttributes;
myFilter = [CIFilter filterWithName:@"<Filter Name Here>"];
myFilterAttributes = [myFilter attributes];
在真正編碼時,你要將"<Filter Name Here>"替換爲你感興趣的濾鏡名稱。
屬性包括這些內容:名稱,類別,類,最小、最大值。
2 構建一個濾鏡字典
如果你的應用提供了一個用戶界面,(你的應用)可以根據濾鏡字典創建和更新界面。
比如,濾鏡屬性是個布爾值,需要一個checkbox或者類似的界面元素,對於在一個範圍內持續變化的屬性,使用一個slider。你可以使用text label設置最大最小值。屬性的缺省值,就是用戶界面的初值。
濾鏡名稱和屬性提供充足的信息,使你可以構建一個用戶界面,並允許用戶選擇濾鏡,控制其輸入參數。
categories = [NSMutableDictionary alloc] init];
NSMutableArray *array;
array = [NSMutableArray arrayWithArray:
[CIFilter filterNamesInCategory:
kCICategoryGeometryAdjustment];
[array addObjectsFromArray:
[CIFilter filterNamesInCategory:
kCICategoryDistortionEffect];
[categories setObject: [self buildFilterDictionary: array]
forKey: @"Distortion"];
array = [NSMutableArray arrayWithArray:
[CIFilter filterNamesInCategory: kCICategorySharpen];
[array addObjectsFromArray:
[CIFilter filterNamesInCategory: kCICategoryBlur];
[categories setObject: [self buildFilterDictionary: array]
forKey:@"Focus"];
// 根據濾鏡名稱構造字典
- (NSMutableDictionary *)buildFilterDictionary: (NSArray *)names // 1
{
NSMutableDictionary *td, *catfilters;
NSDictionary *attr;
NSString *classname;
CIFilter *filter;
int i;
catfilters = [NSMutableDictionary dictionary];
for(i=0 ; i<[names count] ; i++) // 2
{
classname = [names objectAtIndex: i]; // 3
filter = [CIFilter filterWithName: classname]; // 4 構造濾鏡
if(filter)
{
attr = [filter attributes]; // 5 濾鏡屬性
td = [NSMutableDictionary dictionary];
[td setObject: classname forKey: @"class"]; // 6
[catfilters setObject: td
forKey:[attr objectForKey:@"name"]; // 7
}
else
NSLog(@" could not create '%@' filter", classname);
}
return catfilters;
}
五 繼承CIFilter:定製效果(Effect)的配方
你可以將一個濾鏡的輸入作爲另一個濾鏡的輸入,把多個濾鏡鏈接到一起,從而定製出特定的效果。
當你創建一種效果,並且後續想要複用,可以考慮繼承CIFilter來把這種效果封裝到一個濾鏡中。
本章介紹如何繼承CIFilter,創建一個CIColorInvert濾鏡。
之後描述了將多種濾鏡鏈接到一起,達到有趣效果的方式。
1 繼承CIFilter創建CIColorInvert濾鏡
當你繼承CIFilter時,你可以對現有的濾鏡進行設置或者將現有濾鏡鏈接起來。Core Image通過這種方式實現了一些內置的濾鏡。
要繼承一個濾鏡,你需要完成以下工作:
- 聲明濾鏡的輸入參數屬性。你必須預先設定好每個以input開頭的輸入參數,比如inputImage
- 如果有必要的話,覆寫setDefaults方法。
- 覆寫outputImage方法。
Core Image提供的CIColorInvert濾鏡,就是CIColorMatrix濾鏡的變異。見名知意,CIColorInvert向CIColorInvert提供向量,來反轉輸入圖像的顏色。
代碼:
@interface CIColorInvert: CIFilter{
CIImage *inputImage;
}
@property (retain, nonatomic) CIImage *inputImage;
@end
@implementation CIColorInvert
@synthesize inputmage;
- (CIImage *) outputImage {
return [CIFilter filterWithName:@"CIColorMatrix"
keysAndValues:
kCIInputImageKey, inputImage,
@"inputRVector ", [CIVector vectorWithX: -1 Y:0 Z:0 ],
@"inputGVector", [CIVector vectorWithX:0 Y:-1 Z:0 ],
@"inputBVector ", [CIVector vectorWithX: 0 Y:0 Z:-1],
@"inputBiasVector ", [CIVector vectorWithX:1 Y:1 Z:1],
nil, outputImage];
}
2 Chroma Key Filter Recipe(濃度鍵濾鏡配方)
從原圖中刪除一種顏色,或者某個範圍內的顏色,之後將原圖和背景組合起來。
創建一個Chroma Key濾鏡需要以下操作:
- Create a cube map of data that maps the color values you want to remove so they are transparent (alpha value is 0.0). 不會翻譯了-_-|||,大概意思是會把要過濾掉的顏色,設置爲透明色。
- Use the CIColorCube filter and the cube map to remove chroma-key color from the source p_w_picpath.使用CIColorCube濾鏡和cube map從原圖上刪除濃度-鍵顏色。
3 創建一個Cube Map
一個color cube是一個3D顏色查找表(lookup table)。Core Image 濾鏡 CIColorCube 使用色值作爲輸入,並應用一個查找表到這些色值。
CIColorCube的缺省查找表是一個特性矩陣-矩陣的含義是不會對輸入數據做任何操作。然而,這個配方需要你從圖像中刪除所有的綠色。你要把圖中的把綠色的alpha值設置爲0.0(透明),來刪除綠色。
“綠色”包括一定範圍內的顏色。最直接的處理方式是把圖像的色值從RGBA轉爲HSV。
HSV格式,顏色是使用圍繞柱面中軸的角度來表示的。在這種表示方式下,你可以把顏色想象成餅圖上的一片(pie slice),之後,簡單的刪掉chroma-key color。
要刪除綠色,你需要定義圍繞中心點的最小和最大的角度。之後,對於任何的綠色,將其alpha值設置爲0.0。純綠的相對角度是120o。最小值和最大值要以這個值爲中心。
Cube map數據必須預乘alpha,所以創建cube map的最後一步是把RGB值乘以你剛剛計算出的alpha值(如果是綠色,就是0,如果不是就是1.0)。
代碼:
// Allocate memory
const unsigned int size = 64;
float *cubeData = (float *)malloc (size * size * size * sizeof (float) * 4);
float rgb[3], hsv[3], *c = cubeData;
// Populate cube with a simple gradient going from 0 to 1
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
// Convert RGB to HSV
// You can find publicly available rgbToHSV functions on the Internet
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的指針不移動???
}
}
}
// Create memory with the cube data
NSData *data = [NSData dataWithBytesNoCopy:cubeData
length:cubeDataSize
freeWhenDone:YES];
CIColorCube *colorCube = [CIFilter filterWithName:@"CIColorCube"];
[colorCube setValue:[NSNumber numberWithInt:size] forKey:@"inputCubeDimension"];
// Set data for cube
[colorCube setValue:data forKey:@"inputCubeData"];
4 從原圖中刪除綠色
現在你有了color map data,給CIColorCube濾鏡提供背景圖(從這張圖上把綠色刪掉),並得到輸出圖像。
[colorCube setValue:myInputImage forKey:@"inputImage"];
CIImage *result = [colorCube valueForKey:kCIOutputImageKey];
5 混合處理過的圖像與背景圖
設置CISourceOverCompositing濾鏡的參數:
● 把CIColorCube濾鏡處理後的圖像作爲 inputImage參數。
● 把新的背景圖作爲inputBackgroundImage參數。
前面的圖現在看起來就像在海灘上。
6 White Vignette for Faces Filter Recipe (iOS only) 臉部濾鏡配方的白暈影
創建一個白暈影濾鏡:
● 找到圖像中的人臉
● 在CIRadialGradient上以人臉爲中心,創建一個base shade map
● 將base shade map與人臉混合
7 找到人臉
使用CIDetector獲得人臉在圖像中的位置。featuresInImage:options:函數返回的array的第一項就是濾鏡要操作的對象。在你有臉後(-_-),根據探測器提供的邊界計算臉部中心。你需要中心值來創建create the shade map。
CIDetector *detector = [CIDector detectorOfType:CIDetectorTypeFace
context:nil
options:nill];
NSArray *faceArray = [detector featuresInImage:p_w_picpath options:nil];
CIFeature *face = (CIFeature *) [faceArray objectAtIndex:0]; // 也不怕crash...
CGFloat xCenter = face.bounds.origin.x + face.bounds.size.width/2.0;
CGFloat yCenter = face.bounds.origin.y + face.bounds.size.height/2.0;
CIVector *center = [CIVector vectorWithX:xCenter Y:yCenter];
8 創建一個Shade Map
使用CIRadialGradient濾鏡創建一個以人臉爲中心的shade map。shade map的中心必須是透明的,這樣圖像中的人臉才能保持未變化。map的邊緣應該是不透明的白色。這兩個區域之間是不同程度的透明。
爲了達到這個效果,如下設置CIRadialGradient的參數:
● 設置 inputRadius0 成一個大於圖像最長維度的值
● 設置 inputRadius1 成一個比人臉大的值,比如face.bounds.size.height + 50.
● 設置 inputColor0 爲不透明的白.
● 設置 inputColor1 爲透明的白.
● 設置 inputCenter 爲你計算出的人臉的中心.
9 混合Gradient(梯度)與人臉
設置CISourceOverCompositing濾鏡的輸入參數:
● 設置 inputImage 爲原圖.
● 設置 inputBackgroundImage爲上步生成的shade map.
10 移軸濾鏡配方(Tilt-Shift Filter Recipe)
效果:
創建一個移軸濾鏡要完成的工作:
- 創建一個模糊的圖像
- 創建兩個線性gradients
- 通過混合線性gradients來創建一個遮掩
- 將模糊圖像,遮掩,原圖混合
下面的段落介紹如何執行每一步:
11 創建圖像的模糊版本
如下設置CIGaussianBlur的輸入參數:
- 把inputImage參數設置爲你要處理的圖像
- 把inputRadius設置爲10.0
12 創建線性梯度
從一個單色(如綠色或灰色)創建一個線性梯度,從上倒下變化。
如下設置CILinearGradient的輸入參數:
● Set inputPoint0 to (0, 0.75 * h)
● Set inputColor0 to (0,1,0,1)
● Set inputPoint1 to (0, 0.5*h)
● Set inputColor1 to (0,1,0,0)
創建一個綠色線性梯度,從上到下變化。
如下設置CILinearGradient的輸入參數:
● Set inputPoint0 to (0, 0.25 * h)
● Set inputColor0 to (0,1,0,1)
● Set inputPoint1 to (0, 0.5*h)
● Set inputColor1 to (0,1,0,0)
13 通過線性梯度,創建遮掩
如下設置CIAdditionCompositing濾鏡的參數:
- 給你創建的第一個線性梯度設置輸入圖像(inputImage)
- 給你創建的第二個線性梯度設置輸入背景圖像(inputBackgroundImage)
14 混合模糊圖像,原圖和梯度
最後一步使用CIBlendWithMask濾鏡,如下設置輸入參數:
- 設置inputImage參數爲圖像的模糊版本
- 設置inputBackgroundImage參數爲未處理的圖像
- 設置inputMaskImage爲混合的梯度
遮掩只會影響圖像中有東西的部分,透明的部分會被略過,顯示原始圖像。不透明的部分允許顯示模糊圖像。
其他濾鏡不翻譯了,需要時直接查文檔吧。
六 獲得最佳性能
Core Image提供了很多可選項來創建圖像,上下文和渲染內容。
你如何完成一項任務,取決於:
- 你的app需要執行一項任務的頻率
- 你的app是使用靜態還是視頻圖像
- 你是否需要支持實時的處理和分析
- 顏色的保真度對你的用戶是否重要
你應該通讀性能最佳實踐 (performance best practices),以確保您的app能夠高效地運行。
1 性能最佳實踐
如下來實踐最佳性能:
- 不要每次渲染都創建一個CIContext對象
上下文中保存了大量的狀態信息;重用他們會更加高效。
- 評估一下你的app是否需要顏色管理。如果你不需要就不要使用它。
- 使用GPU上下文渲染CIImage對象時,避免Core Animation的動畫。
- 保證圖像不超過CPU和GPU的限制。
Core Image 使用CPU或者GPU時CIContext對圖像大小的限制是不同的。
通過inputImageMaximumSize和outputImageMaximumSize來獲得限制的實際值。
- 儘可能使用小圖像。
性能會隨着輸出的像素數增加。你可以讓Core Image渲染到一個小的視圖,質地,或者幀緩衝區。
Allow Core Animation to upscale to display size.
使用Core Graphics或Image I/O函數來剪切或者採樣,比如
CGImageCreateWithImageInRect 或者 CGImageSourceCreateThumbnailAtIndex。
● UIImageView類最適合處理靜態圖像。
如果你的app要獲得最佳性能,請使用底層API。
● 避免CPU和GPU之間不必要的質地轉換。
●
Render to a rectangle that is the same size as the source p_w_picpath before applying a contents scale factor.
● 考慮使用能達到近似於算法濾鏡效果的簡單濾鏡。
比如,CIColorCube能產生與CISepiaTone近似的輸出,並且更高效。
● Take advantage of the support for YUV p_w_picpath in iOS v6.0 and later.
Camera pixel buffers are natively YUV but most p_w_picpath processing algorithms expect RBGA data. There is a cost to converting between the two. Core Image supports reading YUB from CVPixelBuffer objects and applying the appropriate color transform.
YUV, YUB是神碼?
options = @{ (id)kCVPixelBufferPixelFormatTypeKey: [NSNumber
numberWithInt:kCVPixelFormatType_420YpCvCr88iPlanarFullRange]};
2 你的APP需要顏色管理嗎?
默認情況下, Core Image 應用所有的濾鏡於輕-線性顏色空間。
這提供了最精準和一致的結果。
轉變到sRGB,和向sRGB轉變,增加了濾鏡的複雜程度,需要Core Image去應用這些方程:
rgb = mix(rgb.0.0774, pow(rgb*0.9479 + 0.05213, 2.4), step(0.04045, rgb))
rgb = mix(rgb12.92, pow(rgb*0.4167) * 1.055 - 0.055, step(0.00313, rgb))
以下場景,要考慮禁止顏色管理:
- 你的app需要絕對的高性能。
- 誇張的操作(exaggerated manipulations)後用戶不會注意到質量差別。
要禁用顏色管理,將kCIImageColorSpace鍵設置爲null。
如果你使用一個EAGL上下文,也要在創建EAGL上下文時,將上下文顏色空間設置爲null。
七 Using Feedback to Process Images
iOS 不支持,略過。
八 在實現一個自定義濾鏡前,你需要知道的幾件事
OS X provides support for writing custom filters.
iOS不支持,略過。
九 創建自定義濾鏡
On OS X, if the filters provided by Core Image don’t provide the functionality you need, you can write your own filter. (Custom filters are not available on iOS.)
iOS不支持,略過。
十 打包和加載圖像單元
An p_w_picpath unit represents the plug-in architecture for Core Image filters. Image units use the NSBundle class as the packaging mechanism to allow you to make the filters that you create available to other apps. An p_w_picpath unit can contain filters that are executable or nonexecutable. (See “Executable and Nonexecutable Filters” (page
69) for details.)