Замена цветов с помощью CIColorCube

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

Куб предназначен для замены множества цветов. R, G, B компоненты исходного цвета являются трехмерными координатами нового цвета, который задается в виде 4 float’ов (RGBA).

В приведенном примере происходит хитрая замена цвета со всеми оттенками. Для удобства используется работа с цветом в формате HSV, т.к. в этом случае гораздо легче выцеплять схожие цвета - они будут попадать в сектор.

Представление цвета в HSV:

Подробней в википедии.

Итак, на формочке есть 2 слайдера, с помощью которых меняется диапазон заменяемых оттенков. Для удобства считается, что основной цвет для замены находится посередине этого диапазона и именно он меняется на зеленый.

Для этого пробегаем по всем цветам, смотрим попадает ли HSV аналог этого цвета в нужный сектор и если да - запоминаем его смещение относительно центрального. После этого конструируем новый HSV цвет с таким же смещением относительно зеленого и конвертируем обратно в RGB. Подобный код можно было бы использовать, например, для вырезания фона определенного цвета (chroma key). В этом случае все цвета в нужном секторе меняют прозрачность (alpha) на 0.0

Пример заполнения куба:

const unsigned int size = 64;
size_t cubeDataSize = size * size * size * sizeof ( float ) * 4;
float *cubeData = (float *) malloc ( cubeDataSize );
float rgb[3], hsv[3], newRGB[3];

size_t offset = 0;
for (int z = 0; z < size; z++)
{
    rgb[2] = ((double) z) / size; // blue value
    for (int y = 0; y < size; y++)
    {
        rgb[1] = ((double) y) / size; // green value
        for (int x = 0; x < size; x++)
        {
            rgb[0] = ((double) x) / size; // red value
            rgbToHSV(rgb, hsv);
            
            if (hsv[0] < minHueAngle || hsv[0] > maxHueAngle)
                memcpy(newRGB, rgb, sizeof(newRGB));
            else
            {
                hsv[0] = destCenterHueAngle + (centerHueAngle - hsv[0]);
                hsvToRGB(hsv, newRGB);
            }

            cubeData[offset]   = newRGB[0];
            cubeData[offset+1] = newRGB[1];
            cubeData[offset+2] = newRGB[2];
            cubeData[offset+3] = 1.0;
            offset += 4;
        }
    }
}

NSData *data = [NSData dataWithBytesNoCopy:cubeData length:cubeDataSize freeWhenDone:YES];
CIFilter *colorCube = [CIFilter filterWithName:@"CIColorCube"];
[colorCube setValue:[NSNumber numberWithInt:size] forKey:@"inputCubeDimension"];
[colorCube setValue:data forKey:@"inputCubeData"];
[colorCube setValue:_ciImage forKey:kCIInputImageKey];

В действии

Исходное изображение:

Результат замены:

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

Полный код проекта доступен на гитхабе

Comments