CIToneCurve應該適合這種事情。
- (UIImage*) applyToneCurveToImage:(UIImage*)image
{
CIContext* context = self.context;
CIImage* ciImage = [[CIImage alloc] initWithImage:image];
CIFilter* filter =
[CIFilter filterWithName:@"CIToneCurve"
keysAndValues:
kCIInputImageKey, ciImage,
@"inputPoint0",[CIVector vectorWithX:0.00 Y:0.3]
,@"inputPoint1",[CIVector vectorWithX:0.25 Y:0.4]
,@"inputPoint2",[CIVector vectorWithX:0.50 Y:0.5]
,@"inputPoint3",[CIVector vectorWithX:0.75 Y:0.6]
,@"inputPoint4",[CIVector vectorWithX:1.00 Y:0.7]
,nil];
CIImage* result = [filter valueForKey:kCIOutputImageKey];
CGImageRef cgImage = [context createCGImage:result
fromRect:[result extent]];
UIImage* filteredImage = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
return filteredImage;
}
self.context
可以是CPU或GPU(EAGL)上下文。這些點描述了一條色調曲線。 X是輸入值,Y是輸出值。在這個例子中,我們正在減小斜率,從而降低對比度。您也可以保持斜率不變,但切斷極端值,以減少最大值和最小值。
以下是在iPad mini上處理100幅圖像的一些測量結果(平均每個設置有四個讀數)。你會發現GPU不是魔術,大約60-65%的時間只是運動圖像數據。這些示例以UIImage開始和結束。我用CGImage得到了非常相似的結果。
57x57px png 640x800px jpeg
UIImage->CIImage->UIImage (no filtering)
CPU 0.57s 2.83s
GPU 0.36s 2.83s
UIImage->CIImage->CIFilter->UIImage
CPU 0.75s 4.38s
GPU 0.58s 4.32s
更新
對於Window and Level調整與窗口範圍之外的輸入零個值,要實現這樣的事情:
CGFloat w; //window
CGFloat l; //level
@"inputPoint0",[CIVector vectorWithX:0.0 Y:0.0]
,@"inputPoint1",[CIVector vectorWithX:l-w/2 Y:0.0]
,@"inputPoint2",[CIVector vectorWithX:l+w/2 Y:1.0]
,@"inputPoint3",[CIVector vectorWithX:l+w/2 Y:0.0]
,@"inputPoint4",[CIVector vectorWithX:1.0 Y:0.0]
在實踐中這是行不通的。這些點描述了一條樣條曲線,它不能很好地適應方向的急劇變化,如b-c-d。 (同樣點c和d(2和3)不能共享相同的x值,因此在任何情況下,您都必須稍微增加以使dx = cx * 1.01)
如果您使用多點步篩選器。第一個過濾器使用CIToneCurve
apprpriately(這是正確的窗口/級別算法,而不會試圖將您的最大級別降低到零)。
CIFilter* filter =
[CIFilter filterWithName:@"CIToneCurve"
keysAndValues:
kCIInputImageKey, ciImage,
@"inputPoint0",[CIVector vectorWithX:0.0 Y:0.0]
,@"inputPoint1",[CIVector vectorWithX:l-w/2 Y:0.0]
,@"inputPoint2",[CIVector vectorWithX:l Y:0.5]
,@"inputPoint3",[CIVector vectorWithX:l+w/2 Y:1.0]
,@"inputPoint4",[CIVector vectorWithX:1.0 Y:1.0]
我們使過濾器的副本,我們將在最後一步再需要它:
filter2 = [filter copy];
應用CIColorControls過濾器,以最大限度地提高結果的對比度和亮度
filter = [CIFilter filterWithName:@"CIColorControls"
keysAndValues:kCIInputImageKey,
[filter valueForKey:kCIOutputImageKey], nil];
[filter setValue:[NSNumber numberWithFloat:-1]
forKey:@"inputBrightness"];
[filter setValue:[NSNumber numberWithFloat:4]
forKey:@"inputContrast"];
現在郵寄給1位調色板
filter = [CIFilter filterWithName:@"CIColorPosterize"
keysAndValues:kCIInputImageKey,
[filter valueForKey:kCIOutputImageKey], nil];
[filter setValue:@2 forKey:@"inputLevels"];
反轉結果
filter = [CIFilter filterWithName:@"CIColorInvert"
keysAndValues:kCIInputImageKey,
[filter valueForKey:kCIOutputImageKey], nil];
現在我們用這個結果作爲與窗口/拉平圖像的面具,讓所有的MAX-白色水平得到降低到黑色。
filter = [CIFilter filterWithName:@"CIDarkenBlendMode"
keysAndValues:kCIInputImageKey,
[ filter valueForKey:kCIOutputImageKey], nil];
[filter setValue:[filter2 valueForKey:kCIOutputImageKey]
forKey:@"inputBackgroundImage"];
原
濾波器1 CIToneCurve
過濾器2 CIColorControls
過濾器3 CIColorPosterize
過濾器4 CIColorInvert
filter5 CIDarkenBlendMode
如果你看看Brad Larson的GPUImage
,你會發現GPUImageLuminanceThresholdFilter
會更好地代替2-> 5的過濾器。
其實我想對灰色raw的每個值應用[線性公式](http://blogs.mathworks.com/steve/2006/02/17/all-about-pixel-colors-window-level)數據,當數值超出窗口時,我想將其設置爲零。所以輸入是窗口和水平值,輸出是計算值。你有沒有做過這樣的事情? – 2013-06-15 07:22:09
@MilanoFili,看我更新的回答 – foundry 2013-06-15 22:45:54
完美的例子和答案。謝謝。 – 2013-06-16 08:46:40