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 — выходное значение. В этом примере мы уменьшаем наклон, тем самым уменьшая контраст. С тем же успехом вы могли бы сохранить наклон таким же, но отрезать крайности, уменьшив ваши максимумы и минимумы.
Вот некоторые измерения при обработке 100 изображений на iPad mini (в среднем четыре показания на настройку). Вы увидите, что графический процессор не является волшебной палочкой, и около 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
обновить
Для окна и уровня корректировки с нулевыми значениями для входных данных за пределами диапазона окна, вы хотите добиться чего-то вроде этого:
![введите здесь описание изображения](https://i.stack.imgur.com/zQK9h.png)
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, поэтому в любом случае вам придется немного увеличить, чтобы d.x = c.x*1,01)
Вы можете получить свой результат, если используете многошаговый фильтр. Первый фильтр использует CIToneCurve
соответствующим образом (это правильный алгоритм окна/уровня без попытки сбить ваши максимальные уровни до нуля).
![введите здесь описание изображения](https://i.stack.imgur.com/bkA5L.png)
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];
Теперь мы используем этот результат в качестве маски с окном/выровненным изображением, чтобы все уровни максимального белого были уменьшены до черного.
filter = [CIFilter filterWithName:@"CIDarkenBlendMode"
keysAndValues:kCIInputImageKey,
[ filter valueForKey:kCIOutputImageKey], nil];
[filter setValue:[filter2 valueForKey:kCIOutputImageKey]
forKey:@"inputBackgroundImage"];
оригинальный
![введите здесь описание изображения](https://i.stack.imgur.com/XxEV9.png)
фильтр1 CIToneCurve
![введите здесь описание изображения](https://i.stack.imgur.com/COney.png)
фильтр2 CIColorControls
![введите здесь описание изображения](https://i.stack.imgur.com/EfPKT.png)
фильтр3 CIColorPosterize
![введите здесь описание изображения](https://i.stack.imgur.com/30I7M.png)
фильтр4 CIColorInvert
![введите здесь описание изображения](https://i.stack.imgur.com/ksvM1.png)
фильтр5 CIDarkenBlendMode
![введите здесь описание изображения](https://i.stack.imgur.com/ZN9rU.png)
Если вы посмотрите на GPUImage
Брэда Ларсона, вы обнаружите, что GPUImageLuminanceThresholdFilter
лучше заменит фильтры 2->5.
person
foundry
schedule
25.04.2013