tabla perder pasteles pasar paleta illustrator hexadecimales combinaciones colores color codigo objective-c c colors rgb

objective c - perder - Cambiar el tono de un color RGB



tabla de colores html pdf (11)

Además, el algo de Mark produce resultados más precisos.

Por ejemplo, si gira el tono a 180º utilizando el espacio de color de HSV, la imagen puede resultar en un color de tono rojizo.

Pero en algo de Mark, la imagen está correctamente girada. Los tonos de máscaras, por ejemplo (Hue = 17, Sat = 170, L = 160 en PSP) se convierten correctamente en azul, que tienen Hue alrededor de 144 en PSP, y todos los otros colores de la imagen giran correctamente.

El algo tiene sentido ya que Hue no es nada más, nada más que una función de Logaritmo de un arctan de Rojo, Verde, Azul como se define en esta fórmula:

Hue = arctan((logR-logG)/(logR-logG+2*LogB))

Estoy tratando de escribir una función para cambiar el tono de un color RGB. Específicamente lo estoy usando en una aplicación de iOS, pero la matemática es universal.

El siguiente gráfico muestra cómo cambian los valores R, G y B con respecto al tono.

Al ver eso, parece que debería ser relativamente simple escribir una función para cambiar el tono sin hacer ninguna conversión desagradable a un formato de color diferente que introduciría más errores (lo que podría ser un problema si continúa aplicando pequeños cambios a un color) , y sospecho que sería más costoso computacionalmente.

Aquí está lo que tengo hasta ahora que tipo de trabajo. Funciona perfectamente si está cambiando de color amarillo puro o cian o magenta, pero por lo demás se pone un poco blandito en algunos lugares

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); }

Sé que puedes hacer esto con UIColors y cambiar el tono con algo como esto:

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);

pero no estoy loco por eso, ya que solo funciona en iOS 5, y entre la asignación de varios objetos de color y la conversión de RGB a HSB y viceversa, parece bastante exagerado.

Podría terminar usando una tabla de búsqueda o pre-calculando los colores en mi aplicación, pero tengo mucha curiosidad si hay una manera de hacer que mi código funcione. ¡Gracias!


Básicamente hay dos opciones:

  1. Convierta RGB -> HSV, cambie el tono, convierta HSV -> RGB
  2. Cambia el tono directamente con una transformación lineal.

No estoy muy seguro de cómo implementar 2, pero básicamente tendrá que crear una matriz de transformación y filtrar la imagen a través de esta matriz. Sin embargo, esto volverá a colorear la imagen en lugar de cambiar solo el tono. Si esto está bien para usted, entonces esta podría ser una opción, pero si no se puede evitar una conversión.

Editar

Una pequeña investigación muestra this , lo que confirma mis pensamientos. Para resumir: se debe preferir la conversión de RGB a HSV, si se desea un resultado exacto. La modificación de la imagen RGB original mediante una transformación lineal también conduce a un resultado, pero esto más bien tiñe la imagen. La diferencia se explica de la siguiente manera: La conversión de RGB a HSV no es lineal, mientras que la transformación es lineal.


Código excelente, pero me sorprende que pueda ser más rápido si simplemente no usas self.matrix [2] [0], self.matrix [2] [1], self.matrix [2] [1]

Por lo tanto, set_hue_rotation puede escribirse simplemente como:

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]


El espacio de color RGB describe un cubo. Es posible rotar este cubo alrededor del eje diagonal de (0,0,0) a (255,255,255) para efectuar un cambio de matiz. Tenga en cuenta que algunos de los resultados quedarán fuera del rango de 0 a 255 y deberán recortarse.

Finalmente tuve la oportunidad de codificar este algoritmo. Está en Python, pero debería ser fácil de traducir al idioma de su elección. La fórmula para la rotación 3D provino de http://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle

Editar: Si vio el código que publiqué anteriormente, ignórelo. Estaba tan ansioso por encontrar una fórmula para la rotación que convertí una solución basada en matriz en una fórmula, sin darme cuenta de que la matriz era la mejor forma desde el principio. Todavía simplifiqué el cálculo de la matriz utilizando la constante sqrt (1/3) para los valores de vector de unidad de eje, pero esto es mucho más cercano en espíritu a la referencia y también más simple en el cálculo por píxel.

from math import sqrt,cos,sin,radians def clamp(v): if v < 0: return 0 if v > 255: return 255 return int(v + 0.5) class RGBRotate(object): def __init__(self): self.matrix = [[1,0,0],[0,1,0],[0,0,1]] 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] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA self.matrix[1][1] = cosA + 1./3.*(1.0 - cosA) self.matrix[1][2] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA self.matrix[2][0] = 1./3. * (1.0 - cosA) - sqrt(1./3.) * sinA self.matrix[2][1] = 1./3. * (1.0 - cosA) + sqrt(1./3.) * sinA self.matrix[2][2] = cosA + 1./3. * (1.0 - cosA) def apply(self, r, g, b): rx = r * self.matrix[0][0] + g * self.matrix[0][1] + b * self.matrix[0][2] gx = r * self.matrix[1][0] + g * self.matrix[1][1] + b * self.matrix[1][2] bx = r * self.matrix[2][0] + g * self.matrix[2][1] + b * self.matrix[2][2] return clamp(rx), clamp(gx), clamp(bx)

Aquí hay algunos resultados de lo anterior:

Puede encontrar una implementación diferente de la misma idea en http://www.graficaobscura.com/matrix/index.html


Implementación de PHP:

class Hue { public function convert(int $r, int $g, int $b, int $hue) { $cosA = cos($hue * pi() / 180); $sinA = sin($hue * pi() / 180); $neo = [ $cosA + (1 - $cosA) / 3, (1 - $cosA) / 3 - sqrt(1 / 3) * $sinA, (1 - $cosA) / 3 + sqrt(1 / 3) * $sinA, ]; $result = [ $r * $neo[0] + $g * $neo[1] + $b * $neo[2], $r * $neo[2] + $g * $neo[0] + $b * $neo[1], $r * $neo[1] + $g * $neo[2] + $b * $neo[0], ]; return array_map([$this, ''crop''], $result); } private function crop(float $value) { return 0 > $value ? 0 : (255 < $value ? 255 : (int)round($value)); } }


La publicación es antigua y el póster original buscaba el código ios. Sin embargo, me enviaron aquí a través de una búsqueda de código visual básico, así que para todos aquellos como yo, convertí el código de Mark en un módulo vb .net:

Public Module HueAndTry Public Function ClampIt(ByVal v As Double) As Integer Return CInt(Math.Max(0F, Math.Min(v + 0.5, 255.0F))) End Function Public Function DegreesToRadians(ByVal degrees As Double) As Double Return degrees * Math.PI / 180 End Function Public Function RadiansToDegrees(ByVal radians As Double) As Double Return radians * 180 / Math.PI End Function Public Sub HueConvert(ByRef rgb() As Integer, ByVal degrees As Double) Dim selfMatrix(,) As Double = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}} Dim cosA As Double = Math.Cos(DegreesToRadians(degrees)) Dim sinA As Double = Math.Sin(DegreesToRadians(degrees)) Dim sqrtOneThirdTimesSin As Double = Math.Sqrt(1.0 / 3.0) * sinA Dim oneThirdTimesOneSubCos As Double = 1.0 / 3.0 * (1.0 - cosA) selfMatrix(0, 0) = cosA + (1.0 - cosA) / 3.0 selfMatrix(0, 1) = oneThirdTimesOneSubCos - sqrtOneThirdTimesSin selfMatrix(0, 2) = oneThirdTimesOneSubCos + sqrtOneThirdTimesSin selfMatrix(1, 0) = selfMatrix(0, 2) selfMatrix(1, 1) = cosA + oneThirdTimesOneSubCos selfMatrix(1, 2) = selfMatrix(0, 1) selfMatrix(2, 0) = selfMatrix(0, 1) selfMatrix(2, 1) = selfMatrix(0, 2) selfMatrix(2, 2) = cosA + oneThirdTimesOneSubCos Dim rx As Double = rgb(0) * selfMatrix(0, 0) + rgb(1) * selfMatrix(0, 1) + rgb(2) * selfMatrix(0, 2) Dim gx As Double = rgb(0) * selfMatrix(1, 0) + rgb(1) * selfMatrix(1, 1) + rgb(2) * selfMatrix(1, 2) Dim bx As Double = rgb(0) * selfMatrix(2, 0) + rgb(1) * selfMatrix(2, 1) + rgb(2) * selfMatrix(2, 2) rgb(0) = ClampIt(rx) rgb(1) = ClampIt(gx) rgb(2) = ClampIt(bx) End Sub End Module

Puse términos comunes en variables (largas), pero de lo contrario es una conversión directa, funcionó bien para mis necesidades.

Por cierto, traté de dejar a Mark un voto favorable por su excelente código, pero no tuve suficientes votos para permitir que sea visible (Sugerencia, Sugerencia).


Me decepcionó la mayoría de las respuestas que encontré aquí, algunas estaban defectuosas y, básicamente, estaban totalmente equivocadas. Terminé gastando más de 3 horas tratando de resolver esto. La respuesta de Mark Ransom es correcta, pero quiero ofrecer una solución C completa que también se verifica con MATLAB. He probado esto a fondo, y aquí está el código C:

#include <math.h> typedef unsigned char BYTE; //define an "integer" that only stores 0-255 value typedef struct _CRGB //Define a struct to store the 3 color values { BYTE r; BYTE g; BYTE b; }CRGB; BYTE clamp(float v) //define a function to bound and round the input float value to 0-255 { if (v < 0) return 0; if (v > 255) return 255; return (BYTE)v; } CRGB TransformH(const CRGB &in, const float fHue) { CRGB out; const float cosA = cos(fHue*3.14159265f/180); //convert degrees to radians const float sinA = sin(fHue*3.14159265f/180); //convert degrees to radians //calculate the rotation matrix, only depends on Hue float matrix[3][3] = {{cosA + (1.0f - cosA) / 3.0f, 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA}, {1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f*(1.0f - cosA), 1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA}, {1.0f/3.0f * (1.0f - cosA) - sqrtf(1.0f/3.0f) * sinA, 1.0f/3.0f * (1.0f - cosA) + sqrtf(1.0f/3.0f) * sinA, cosA + 1.0f/3.0f * (1.0f - cosA)}}; //Use the rotation matrix to convert the RGB directly out.r = clamp(in.r*matrix[0][0] + in.g*matrix[0][1] + in.b*matrix[0][2]); out.g = clamp(in.r*matrix[1][0] + in.g*matrix[1][1] + in.b*matrix[1][2]); out.b = clamp(in.r*matrix[2][0] + in.g*matrix[2][1] + in.b*matrix[2][2]); return out; }

NOTA: La matriz de rotación solo depende del Tono ( fHue ), así que una vez que haya calculado la matrix[3][3] , puede reutilizarla para cada píxel de la imagen que esté experimentando la misma transformación de tono. Esto mejorará la eficiencia drásticamente. Aquí hay un código de MATLAB que verifica los resultados:

function out = TransformH(r,g,b,H) cosA = cos(H * pi/180); sinA = sin(H * pi/180); matrix = [cosA + (1-cosA)/3, 1/3 * (1 - cosA) - sqrt(1/3) * sinA, 1/3 * (1 - cosA) + sqrt(1/3) * sinA; 1/3 * (1 - cosA) + sqrt(1/3) * sinA, cosA + 1/3*(1 - cosA), 1/3 * (1 - cosA) - sqrt(1/3) * sinA; 1/3 * (1 - cosA) - sqrt(1/3) * sinA, 1/3 * (1 - cosA) + sqrt(1/3) * sinA, cosA + 1/3 * (1 - cosA)]; in = [r, g, b]''; out = round(matrix*in); end

Aquí hay una muestra de entrada / salida que fue reproducible por ambos códigos:

TransformH(86,52,30,210) ans = 36 43 88

Así que la entrada RGB de [86,52,30] se convirtió a [36,43,88] usando un tono de 210 .


Para cualquier persona que necesite el cambio de tono descrito anteriormente (no corregido por rayos gamma) como un sombreador de píxeles HLSL parametrizado (lo hice a través de una aplicación WPF y pensé que podría simplemente compartirlo):

sampler2D implicitInput : register(s0); float factor : register(c0); float4 main(float2 uv : TEXCOORD) : COLOR { float4 color = tex2D(implicitInput, uv); float h = 360 * factor; //Hue float s = 1; //Saturation float v = 1; //Value float M_PI = 3.14159265359; float vsu = v * s*cos(h*M_PI / 180); float vsw = v * s*sin(h*M_PI / 180); float4 result; result.r = (.299*v + .701*vsu + .168*vsw)*color.r + (.587*v - .587*vsu + .330*vsw)*color.g + (.114*v - .114*vsu - .497*vsw)*color.b; result.g = (.299*v - .299*vsu - .328*vsw)*color.r + (.587*v + .413*vsu + .035*vsw)*color.g + (.114*v - .114*vsu + .292*vsw)*color.b; result.b = (.299*v - .300*vsu + 1.25*vsw)*color.r + (.587*v - .588*vsu - 1.05*vsw)*color.g + (.114*v + .886*vsu - .203*vsw)*color.b;; result.a = color.a; return result; }



Scott ... no exactamente. El algo parece funcionar igual que en HSL / HSV, pero más rápido. Además, si simplemente multiplicas los primeros 3 elementos de la matriz con el factor de gris, agregas / disminuyes luma.

Ejemplo ... La escala de grises de Rec709 tiene esos valores [GrayRedFactor_Rec709: R $ 0.212671 GrayGreenFactor_Rec709: R $ 0.715160 GrayBlueFactor_Rec709: R $ 0.072169]

Cuando multiplicas self.matrix [x] [x] con el correspondiente de GreyFactor, disminuyes la luz sin tocar la saturación Ex:

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]

Y lo contrario también es cierto. Si se divide en lugar de multiplicarse, la luminosidad aumenta dramáticamente.

Por lo que estoy probando, estos algoritmos pueden ser un maravilloso reemplazo para HSL, siempre y cuando no necesites saturación, por supuesto.

Intente hacer esto ... rote el tono a solo 1 grado (solo para forzar el funcionamiento del algoritmo mientras mantiene la misma sensibilidad de percepción de la imagen) y multiplique por esos factores.


Editar por comentario cambiado "son todos" a "se puede aproximar linealmente por".
Editar 2 añadiendo compensaciones.

Esencialmente, los pasos que quieres son

RBG->HSV->Update hue->RGB

Dado que estos se pueden aproximar mediante transformaciones de matriz lineal (es decir, son asociativos), puede realizarlo en un solo paso sin ninguna conversión desagradable o pérdida de precisión. Solo tienes que multiplicar las matrices de transformación entre sí y usarlas para transformar tus colores.

Hay un paso a paso rápido aquí http://beesbuzz.biz/code/hsv_color_transforms.php

Aquí está el código C ++ (con las transformaciones de saturación y valor eliminadas):

Color TransformH( const Color &in, // color to transform float H ) { float U = cos(H*M_PI/180); float W = sin(H*M_PI/180); Color ret; ret.r = (.299+.701*U+.168*W)*in.r + (.587-.587*U+.330*W)*in.g + (.114-.114*U-.497*W)*in.b; ret.g = (.299-.299*U-.328*W)*in.r + (.587+.413*U+.035*W)*in.g + (.114-.114*U+.292*W)*in.b; ret.b = (.299-.3*U+1.25*W)*in.r + (.587-.588*U-1.05*W)*in.g + (.114+.886*U-.203*W)*in.b; return ret; }