Сдвиг оттенка цвета RGB - PullRequest
33 голосов
/ 14 декабря 2011

Я пытаюсь написать функцию для смещения оттенка цвета RGB. В частности, я использую его в приложении для iOS, но математика универсальна.

График ниже показывает, как значения R, G и B изменяются относительно оттенка.

Graph of RGB values across hues

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

Вот что у меня есть, какие работы. Он отлично работает, если вы переходите от чистого желтого, голубого или пурпурного, но в некоторых местах он становится немного шатким.

Color4f ShiftHue(Color4f c, float d) {
    if (d==0) {
        return c;
    }
    while (d<0) {
        d+=1;
    }

    d *= 3;

    float original[] = {c.red, c.green, c.blue};
    float returned[] = {c.red, c.green, c.blue};

    // big shifts
    for (int i=0; i<3; i++) {
        returned[i] = original[(i+((int) d))%3];
    }
    d -= (float) ((int) d);
    original[0] = returned[0];
    original[1] = returned[1];
    original[2] = returned[2];

    float lower = MIN(MIN(c.red, c.green), c.blue);
    float upper = MAX(MAX(c.red, c.green), c.blue);

    float spread = upper - lower;
    float shift  = spread * d * 2;

    // little shift
    for (int i = 0; i < 3; ++i) {
        // if middle value
        if (original[(i+2)%3]==upper && original[(i+1)%3]==lower) {
            returned[i] -= shift;
            if (returned[i]<lower) {
                returned[(i+1)%3] += lower - returned[i];
                returned[i]=lower;
            } else
                if (returned[i]>upper) {
                    returned[(i+2)%3] -= returned[i] - upper;
                    returned[i]=upper;
                }
            break;
        }
    }

    return Color4fMake(returned[0], returned[1], returned[2], c.alpha);
}

Я знаю, что вы можете сделать это с помощью UIColors и изменить цвет на что-то вроде этого:

CGFloat hue;
CGFloat sat;
CGFloat bri;
[[UIColor colorWithRed:parent.color.red green:parent.color.green blue:parent.color.blue alpha:1] getHue:&hue saturation:&sat brightness:&bri alpha:nil];
hue -= .03;
if (hue<0) {
    hue+=1;
}
UIColor *tempColor = [UIColor colorWithHue:hue saturation:sat brightness:bri alpha:1];
const float* components= CGColorGetComponents(tempColor.CGColor);
color = Color4fMake(components[0], components[1], components[2], 1);

но я не в восторге от этого, так как он работает только в iOS 5, и между выделением ряда цветных объектов и преобразованием из RGB в HSB, а затем обратно это кажется довольно излишним.

Я мог бы в конечном итоге использовать справочную таблицу или предварительно рассчитать цвета в моем приложении, но мне действительно любопытно, есть ли способ заставить мой код работать. Спасибо!

Ответы [ 13 ]

0 голосов
/ 01 июня 2014

Кроме того, алгоритм Марка дает более достоверные результаты.

Например, если повернуть оттенок на 180º с использованием цветового пространства HSV, изображение может привести к красноватому цвету.

Но по алгоритму Марка изображение правильно вращается.Например, тоны скинов (Hue = 17, Sat = 170, L = 160 в PSP) превращаются в синий цвет с оттенком около 144 в PSP, а все остальные цвета изображения правильно вращаются.

Алгоритмимеет смысл, так как Hue - это не более чем ничего, кроме логарифмической функции арктана красного, зеленого, синего, как определено по этой формуле:

Hue = arctan((logR-logG)/(logR-logG+2*LogB))
0 голосов
/ 31 мая 2014

Скотт .... не совсем.Кажется, алгоритм работает так же, как в HSL / HSV, но быстрее.Кроме того, если вы просто умножите первые 3 элемента массива на коэффициент для серого, вы добавите / уменьшите яркость.

Пример ... В градациях серого из Rec709 есть эти значения [GrayRedFactor_Rec709: R $ 0,212671 GrayGreenFactor_Rec709: R0,715160 $ ​​GrayBlueFactor_Rec709: R $ 0,072169]

Когда вы умножаете self.matrix [x] [x] с корреспондентом GreyFactor, вы уменьшаете яркость, не касаясь насыщенности Пример:

def set_hue_rotation(self, degrees):
    cosA = cos(radians(degrees))
    sinA = sin(radians(degrees))
    self.matrix[0][0] = (cosA + (1.0 - cosA) / 3.0) * 0.212671
    self.matrix[0][1] = (1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA) * 0.715160
    self.matrix[0][2] = (1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA) * 0.072169
    self.matrix[1][0] = self.matrix[0][2] <---Not sure, if this is the right code, but i think you got the idea
    self.matrix[1][1] = self.matrix[0][0]
    self.matrix[1][2] = self.matrix[0][1]

И наоборотЭто также верно. Если вы делите вместо умножения, яркость резко увеличивается.

Из того, что я тестирую, эти алгоритмы могут быть прекрасной заменой HSL, если, конечно, вам не нужно насыщение.

Попробуйте сделать это ... поверните оттенок только на 1 градус (просто чтобы заставить алгоритм работать должным образом, сохраняя ту же чувствительность восприятия изображения), и умножьте на эти факторы.

0 голосов
/ 31 мая 2014

Отличный код, но мне интересно, что это может быть быстрее, если вы просто не используете self.matrix [2] [0], self.matrix [2] [1], self.matrix [2] [1]

Следовательно, set_hue_rotation можно записать просто как:

def set_hue_rotation(self, degrees):
    cosA = cos(radians(degrees))
    sinA = sin(radians(degrees))
    self.matrix[0][0] = cosA + (1.0 - cosA) / 3.0
    self.matrix[0][1] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA
    self.matrix[0][2] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA
    self.matrix[1][0] = self.matrix[0][2] <---Not sure, if this is the right code, but i think you got the idea
    self.matrix[1][1] = self.matrix[0][0]
    self.matrix[1][2] = self.matrix[0][1]
...