CIPhotoEffect Фильтры CIFilter не зависят от управления цветом. Что дает фильтрам CIPhotoEffect это свойство?

Чтобы дать этому вопросу некоторый контекст (хо-хо):

Я создаю подкласс CIFilter под iOS с целью создания некоторых пользовательских фильтров с фотоэффектами. Согласно документации, это означает создание «составного» фильтра, который инкапсулирует один или несколько ранее существовавших фильтров CIFilter в рамках моего собственного подкласса CIFilter.

Все хорошо. Никаких проблем нет. В качестве примера предположим, что я инкапсулирую один фильтр CIColorMatrix, который был предварительно установлен с определенными входными векторами rgba.

Применяя мой собственный фильтр (или действительно только CIColorMatrix), я вижу радикально разные результаты при использовании CIContext с включенным и выключенным управлением цветом. Я создаю свои контексты следующим образом:

Управление цветом включено:

CIContext * context = [CIContext contextWithOptions:nil];

Управление цветом отключено.

NSDictionary *options = @{kCIContextWorkingColorSpace:[NSNull null], kCIContextOutputColorSpace:[NSNull null]};
CIContext * context = [CIContext contextWithOptions:options];

В этом нет ничего удивительного. Однако я заметил, что все предварительно созданные фильтры CIPhotoEffect CIFilters, например CIPhotoEffectInstant, по сути, инвариантны при тех же двух условиях управления цветом.

Может ли кто-нибудь рассказать, что дает им эту собственность? Например, инкапсулируют ли они сами определенные фильтры CIFilter, которые могут применяться с аналогичной инвариантностью?

Моя цель - создать несколько настраиваемых фильтров с одним и тем же свойством, не ограничиваясь цепочкой только фильтров CIPhotoEffect.

--

Изменить: благодаря YuAo я собрал несколько примеров рабочего кода, которые я публикую здесь, чтобы помочь другим:

Программно сгенерированный CIColorCubeWithColorSpace CIFilter, инвариантный для разных схем управления цветом / рабочего цветового пространства:

    self.filter = [CIFilter filterWithName:@"CIColorCubeWithColorSpace"];
   [self.filter setDefaults];

    int cubeDimension = 2; // Must be power of 2, max 128
    int cubeDataSize = 4 * cubeDimension * cubeDimension * cubeDimension; // bytes
    float cubeDataBytes[8*4] = {
        0.0, 0.0, 0.0, 1.0,
        0.1, 0.0, 1.0, 1.0,
        0.0, 0.5, 0.5, 1.0,
        1.0, 1.0, 0.0, 1.0,
        0.5, 0.0, 0.5, 1.0,
        1.0, 0.0, 1.0, 1.0,
        0.0, 1.0, 1.0, 1.0,
        1.0, 1.0, 1.0, 1.0
    };

    NSData *cubeData = [NSData dataWithBytes:cubeDataBytes length:cubeDataSize * sizeof(float)];

    [self.filter setValue:@(cubeDimension) forKey:@"inputCubeDimension"];
    [self.filter setValue:cubeData forKey:@"inputCubeData"];
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    [self.filter setValue:(__bridge id)colorSpace forKey:@"inputColorSpace"];
    [self.filter setValue:sourceImageCore forKey:@"inputImage"];

    CIImage *filteredImageCore = [self.filter outputImage];
    CGColorSpaceRelease(colorSpace);

В документах говорится:

Чтобы предоставить объект CGColorSpaceRef в качестве входного параметра, приведите его к типу id. С цветовым пространством по умолчанию (null), которое эквивалентно kCGColorSpaceGenericRGBLinear, эффект этого фильтра идентичен эффекту CIColorCube.

Я хотел пойти дальше и иметь возможность читать cubeData из файла. Для определение сопоставления входного цвета с выходным цветом.

С помощью этого ответа я собрал код для выполнения это также размещено здесь для удобства.

Hald CLUT на основе CIColorCubeWithColorSpace CIFilter изображения CLUT, инвариантный при различных схемах управления цветом / рабочем цветовом пространстве:

Использование:

    NSData *cubeData = [self colorCubeDataFromLUT:@"LUTImage.png"];
    int cubeDimension = 64;

    [self.filter setValue:@(cubeDimension) forKey:@"inputCubeDimension"];
    [self.filter setValue:cubeData forKey:@"inputCubeData"];
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); // or whatever your image's colour space
    [self.filter setValue:(__bridge id)colorSpace forKey:@"inputColorSpace"];
    [self.filter setValue:sourceImageCore forKey:@"inputImage"];

Вспомогательные методы (использующие Accelerate Framework):

- (nullable NSData *) colorCubeDataFromLUT:(nonnull NSString *)name
{
    UIImage *image = [UIImage imageNamed:name inBundle:[NSBundle bundleForClass:self.class] compatibleWithTraitCollection:nil];
    static const int kDimension = 64;

    if (!image) return nil;

    NSInteger width = CGImageGetWidth(image.CGImage);
    NSInteger height = CGImageGetHeight(image.CGImage);
    NSInteger rowNum = height / kDimension;
    NSInteger columnNum = width / kDimension;

    if ((width % kDimension != 0) || (height % kDimension != 0) || (rowNum * columnNum != kDimension)) {
        NSLog(@"Invalid colorLUT %@",name);
        return nil;
    }

    float *bitmap = [self createRGBABitmapFromImage:image.CGImage];
    if (bitmap == NULL) return nil;

    // Convert bitmap data written in row,column order to cube data written in x:r, y:g, z:b representation where z varies > y varies > x.
    NSInteger size = kDimension * kDimension * kDimension * sizeof(float) * 4;
    float *data = malloc(size);
    int bitmapOffset = 0;
    int z = 0;
    for (int row = 0; row <  rowNum; row++)
    {
        for (int y = 0; y < kDimension; y++)
        {
            int tmp = z;
            for (int col = 0; col < columnNum; col++) {
                NSInteger dataOffset = (z * kDimension * kDimension + y * kDimension) * 4;

                const float divider = 255.0;
                vDSP_vsdiv(&bitmap[bitmapOffset], 1, &divider, &data[dataOffset], 1, kDimension * 4); // Vector scalar divide; single precision. Divides bitmap values by 255.0 and puts them in data, processes each column (kDimension * 4 values) at once.

                bitmapOffset += kDimension * 4; // shift bitmap offset to the next set of values, each values vector has (kDimension * 4) values.
                z++;
            }
            z = tmp;
        }
        z += columnNum;
    }

    free(bitmap);

    return [NSData dataWithBytesNoCopy:data length:size freeWhenDone:YES];
}

- (float *)createRGBABitmapFromImage:(CGImageRef)image {
    CGContextRef context = NULL;
    CGColorSpaceRef colorSpace;
    unsigned char *bitmap;
    NSInteger bitmapSize;
    NSInteger bytesPerRow;

    size_t width = CGImageGetWidth(image);
    size_t height = CGImageGetHeight(image);

    bytesPerRow   = (width * 4);
    bitmapSize     = (bytesPerRow * height);

    bitmap = malloc( bitmapSize );
    if (bitmap == NULL) return NULL;

    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL) {
        free(bitmap);
        return NULL;
    }

    context = CGBitmapContextCreate (bitmap,
                                     width,
                                     height,
                                     8,
                                     bytesPerRow,
                                     colorSpace,
                                     (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease( colorSpace );

    if (context == NULL) {
        free (bitmap);
        return NULL;
    }

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
    CGContextRelease(context);

    float *convertedBitmap = malloc(bitmapSize * sizeof(float));
    vDSP_vfltu8(bitmap, 1, convertedBitmap, 1, bitmapSize); // Converts an array of unsigned 8-bit integers to single-precision floating-point values.
    free(bitmap);

    return convertedBitmap;
}

Можно создать изображение Hald CLUT, получив изображение идентичности (Google!) И затем применив к нему ту же цепочку обработки изображений, которая применяется к изображению, используемому для визуализации «взгляда» в любой программе редактирования изображений. Просто убедитесь, что вы установили cubeDimension в примере кода на правильное измерение для изображения LUT. Если размер d - это количество элементов вдоль одной стороны куба 3D LUT, ширина и высота изображения Hald CLUT будут d * sqrt (d) пикселей, а общее количество пикселей изображения будет d ^ 3.


person Alexander Gingell    schedule 27.01.2016    source источник


Ответы (2)


CIPhotoEffect внутренне использует CIColorCubeWithColorSpace фильтр.

Все данные цветового куба хранятся в CoreImage.framework.

Вы можете найти CoreImage.framework симулятора здесь (/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreImage.framework/).

Данные цветового куба именуются с расширением пути scube. например CIPhotoEffectChrome.scube

CIColorCubeWithColorSpace внутренне скрывает значения цвета цветового куба, чтобы они соответствовали рабочему цветовому пространству текущего основного контекста изображения, используя частные методы: -[CIImage _imageByMatchingWorkingSpaceToColorSpace:]; -[CIImage _imageByMatchingColorSpaceToWorkingSpace:];

person YuAo    schedule 29.01.2016
comment
Спасибо YuAo, это было очень полезно. Я собираюсь исследовать лучший способ создания данных цветного куба. Я хотел бы написать инструмент, который позволит мне манипулировать цветами на живом изображении, а затем сохранять внешний вид в формате .scube. Затем я прочитаю это в своем настраиваемом фильтре и применяю его через CIColorCubeWithColorSpace. Предположительно, я могу просто передать этому методу рабочее цветовое пространство CIContext через свойство context.workingColorSpace. - person Alexander Gingell; 30.01.2016
comment
Я отредактировал свой вопрос, добавив продолжение - CIColorCubeWithColorSpace, похоже, не дает мне результатов, которые я ожидал на основе вашего ответа. Есть ли у вас какие-либо дополнительные сведения, основанные на том, что я пытаюсь? Еще раз, спасибо. - person Alexander Gingell; 30.01.2016
comment
Привет, Александр. Данные цветового куба - это многомерный массив цветов. inputColorSpace из CIColorCubeWithColorSpace - это цветовое пространство, в котором находятся данные цветового куба. Вам необходимо установить inputColorSpace на основе данных цветового куба. Установка context.workingColorSpace неверна. - person YuAo; 30.01.2016
comment
Привет, YuAo, спасибо за разъяснение - это имеет смысл, я не знаю, почему я предположил иное! Теперь он инвариантен. Кроме того, я нашел и реализовал код для чтения в cubeData из старого LUT-изображения. Я отредактирую свой ответ, указав правильный код и т. Д. - person Alexander Gingell; 30.01.2016
comment
Я тоже пытаюсь делать то, что делаете вы. Однако использование CIColorCubeWithColorSpace не очень эффективно с точки зрения памяти. Каждый компонент цвета в данных цветового куба хранится в значении float, которое использует память в 4 раза больше по сравнению с хранением компонента цвета в 8-битном значении char. Например, если у вас есть таблица поиска цвета 512x512 пикселей, сама таблица поиска будет использовать 1 МБ памяти, данные цветового куба будут использовать память 4 МБ. - person YuAo; 30.01.2016
comment
Верно, есть компромиссы. Фактически, прежде чем я рассмотрел этот подход, я написал отдельный инструмент, который позволил мне поиграть с обычными цепочками CIFilter и включить / выключить управление цветом. Затем я нашел понравившийся мне вид с включенным CM и сделал снимок настроек фильтра, затем выключил CM и перенастроил настройки, пока у меня не было примерно одинакового вида между двумя парами настроек. Затем в моем настраиваемом фильтре я просто применял предустановки настроек в зависимости от того, был ли контекст управляемым цветом или нет. В результате получился псевдоинвариантный вид с минимальными затратами памяти (просто дополнительная работа по их определению!). - person Alexander Gingell; 30.01.2016
comment
Почему нужно выключать CM? Даже некоторые встроенные фильтры не работают при выключенном CM. - person YuAo; 30.01.2016
comment
Из соображений производительности, связанных с приложением, которое я создаю, я хотел бы, чтобы при необходимости можно было отключить управление цветом. По умолчанию он включен по указанным вами причинам. - person Alexander Gingell; 30.01.2016
comment
Интересно, что они сделали для CIPhotoEffect. Их файлы .scube имеют размер около 131 КБ. У некоторых есть два файла, но не все. Это допустимые размеры, а также размер изображения и размер в памяти с плавающими цветовыми компонентами. 4: 8 x 8 px (1.02 KB) 9: 27 x 27 px (11.66 KB) 16: 64 x 64 px (65.54 KB) 25: 125 x 125 px (0.25 MB) 36: 216 x 216 px (0.75 MB) 49: 343 x 343 px (1.88 MB) 64: 512 x 512 px (4.19 MB) 81: 729 x 729 px (8.50 MB) 100: 1000 x 1000 px (16.00 MB) 121: 1331 x 1331 px (28.34 MB). Думаю, я посмотрю, насколько маленьким можно пойти без ущерба для результатов. - person Alexander Gingell; 31.01.2016
comment
quelsolaar.com/technology/clut.html говорит: Использование 4096 на 4096 identity CLUT, он будет занимать примерно 50 МБ в необработанном формате файла, таком как Targa. При сжатии с использованием ZIP он уменьшится до 36 МБ, но при сжатии RAR размер файла будет всего 124 КБ. С форматом файла PNG вы также можете получить резкое сжатие до 131 КБ. Мы настоятельно рекомендуем формат файла PNG, если размер имеет значение. Итак, теперь эти 131 КБ файлы .scube очень похожи на файлы размером 4096x4096 .png, хотя я пытался открыть их как таковые, но безрезультатно. - person Alexander Gingell; 31.01.2016
comment
Файл scube представляет собой цветной куб размером 32 * 32 * 32. Таким образом, он занимает 32 * 32 * 32 * 4 = 131072 байта. - person YuAo; 31.01.2016
comment
Не стоит так поздно программировать :). Это имеет смысл, поскольку я сравнивал использование памяти CIPhotoEffect с этими другими методами, и, конечно же, они более эффективны, чем распаковка изображения 4k в память! Я, вероятно, собираюсь использовать старые изображения для определения внешнего вида, а затем предварительно обработать их в данные цветового куба, как они это делают. - person Alexander Gingell; 31.01.2016
comment
К вашему сведению, я сравнил визуальные результаты (на глаз на дисплее сетчатки 5k), работая с данными цветного куба разных размеров. В моем тестировании типичных фотографических фильтров результаты в значительной степени сходятся на уровне 25–36, что согласуется с тем, что Apple использует 32x32x32 - хороший компромисс между памятью и визуальной точностью. - person Alexander Gingell; 31.01.2016

Вот как CIPhotoEffect / CIColorCubeWithColorSpace должен работать с включенным и выключенным управлением цветом.

При включенном управлении цветом CI должен делать следующее:

  1. соответствие цвета от пространства ввода к пространству куба. Если эти двое равны, это ни к чему.

  2. применить цветной куб.

  3. соответствие цвета от пространства куба к пространству вывода. Если эти двое равны, это ни к чему.

Если управление цветом выключено, CI должен делать следующее:

  1. применить цветной куб.
person David Hayward    schedule 17.05.2016