Алгоритм плавного цветового перехода

Я ищу общий алгоритм плавного перехода между двумя цветами.

например, это изображение взято из Википедия и показывает переход от оранжевого к синему.

enter image description here

когда я пытаюсь сделать то же самое с помощью моего кода (C++), первая идея, которая пришла на ум,-это использовать цветовое пространство HSV, но раздражает между цветами.

enter image description here

Что это хороший способ достичь этого ? Кажется быть связанным с уменьшением контраста или, возможно, использовать другое цветовое пространство ?

5 ответов


Я делал тонны этого в прошлом. Сглаживание может быть выполнено многими различными способами, но то, как они, вероятно, делают здесь, - это простой линейный подход. Это означает, что для каждого компонента R, G и B они просто вычисляют уравнение "y = m*x + b", которое соединяет две точки, и используют его для вычисления компонентов между ними.

m[RED]   = (ColorRight[RED]   - ColorLeft[RED])   / PixelsWidthAttemptingToFillIn
m[GREEN] = (ColorRight[GREEN] - ColorLeft[GREEN]) / PixelsWidthAttemptingToFillIn
m[BLUE]  = (ColorRight[BLUE]  - ColorLeft[BLUE])  / PixelsWidthAttemptingToFillIn

b[RED]   = ColorLeft[RED]
b[GREEN] = ColorLeft[GREEN]
b[BLUE]  = ColorLeft[BLUE]

любой новый цвет между ними выглядит так:

NewCol[pixelXFromLeft][RED]   = m[RED]   * pixelXFromLeft + ColorLeft[RED]    
NewCol[pixelXFromLeft][GREEN] = m[GREEN] * pixelXFromLeft + ColorLeft[GREEN]
NewCol[pixelXFromLeft][BLUE]  = m[BLUE]  * pixelXFromLeft + ColorLeft[BLUE]

существует много математических способов создания переход, то, что мы действительно хотим сделать, это понять, какой переход вы действительно хотите увидеть. Если вы хотите увидеть точный переход от вышеуказанного изображения, стоит посмотреть на цветовые значения этого изображения. Я написал программу назад во времени, чтобы посмотреть на такие изображения и вывести там значения графически. Вот вывод моей программы На приведенной выше шкале псевдоцветов.

enter image description here

основываясь на взгляде на график, он более сложный, чем линейный, как я сказал выше. Синий компонент выглядит в основном линейным, красный может быть эмулирован в линейный, зеленый, однако, имеет более округлую форму. Мы могли бы выполнить математический анализ зеленого, чтобы лучше понять его математическую функцию, и использовать его вместо этого. Вы можете обнаружить, что линейная интерполяция с увеличивающимся наклоном от 0 до ~70 пикселей с линейным уменьшающимся наклоном после пикселя 70 достаточно хороша.

Если вы посмотрите на нижнюю часть экрана, эта программа дает статистические показатели каждого цветового компонента, такие как min, max и average, а также ширина считываемого изображения.


простая линейная интерполяция значений R,G,B сделает это.

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

Top half linear interpolation, bottom half original

и вот код (Python), который его создал:

for y in range(height/2):
    for x in range(width):
        p = x / float(width - 1)
        r = int((1.0-p) * r1 + p * r2 + 0.5)
        g = int((1.0-p) * g1 + p * g2 + 0.5)
        b = int((1.0-p) * b1 + p * b2 + 0.5)
        pix[x,y] = (r,g,b)

цветовое пространство HSV не очень хорошее цветовое пространство для плавных переходов. Это потому что h значение, оттенок, просто используется для произвольного определения различных цветов вокруг "цветового колеса". Это означает, что если вы идете между двумя цветами далеко друг от друга на колесе, вам придется окунуться в кучу других цветов. Совсем не гладко.

было бы гораздо разумнее использовать RGB (или CMYK). Эти "компонентные" цветовые пространства лучше определены для плавных переходов потому что они представляют, сколько каждого "компонента" требуется цвету.

линейный переход (см. ответ @trumpetlicks) для каждого значения компонента R, G и B должен выглядеть "довольно хорошо". Что-то большее, чем "довольно хорошо", потребует от фактического человека настройки значений, потому что есть различия и асимметрии в том, как наши глаза воспринимают значения цвета в разных цветовых группах, которые не представлены ни в RBG, ни в CMYK (или в любом стандарте).

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


Я исследовал это, чтобы построить , которая принимает изображение в оттенках серого в качестве входных и colorises его искусственно в соответствии с цветовой палитрой:

■■■■ вход в градации серого ■ ■ ■ выход ■■■■■■■■■■■■■■■

enter image description here enter image description here

как и многие другие решения, алгоритм использует линейная интерполяция чтобы сделать переход между цветами. С пример,smooth_color_transition() следует вызывать со следующими аргументами:

QImage input("gradient.jpg");

QVector<QColor> colors;
colors.push_back(QColor(242, 177, 103)); // orange
colors.push_back(QColor(124, 162, 248)); // blue-ish

QImage output = smooth_color_transition(input, colors);    
output.save("output.jpg");

сравнение исходное изображение VS выход из алгоритма можно увидеть ниже:

enter image description here (выход)

enter image description here (оригинал)

визуальные артефакты, которые можно наблюдать на выходе, уже присутствуют на входе (оттенки серого). Этот входное изображение получило эти артефакты, когда оно было изменено до 189x51.

вот еще один пример, который был создан с более сложной цветовой палитры:

■■■■ вход в градации серого ■ ■ ■ выход ■■■■■■■■■■■■■■■


мне кажется, было бы проще создать градиент, используя значения RGB. Сначала вы должны рассчитать изменение цвета для каждого значения на основе ширины градиента. Для значений R, G и B необходимо выполнить следующий псевдокод.

if (redValue1 == redValue2) {
    redDifference = 0
} else {
    redDifference = absoluteValue(redValue1 - redValue2) / widthOfGradient
}

if (redValue1 > redValue2) {
    redDifference *= -1
}

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

for (int i = 0; i < widthOfGradient; i++) {
    int r = round(redValue1 + (i * redDifference))
    // ...repeat for green and blue

    drawLine(i, r, g, b)
}

Я знаю, что вы указали, что используете C++, но я создал JSFiddle, демонстрирующий эту работу с вашим первым градиентом как пример: http://jsfiddle.net/eumf7/