¿Cómo promediar 2 colores?

Páginas: (1/2) > >>

Tachikomaia:

Si digo CC0000, es decir 204, 0, 0
y
CCCCCC, es decir 204, 204, 204
el resultado debe ser
204, 102, 102

Entiendo que debo convertir el hexadecimal a decimal y luego hacer promedios.

GPT me dijo que con Action Script 1.0 se puede hacer esto para la conversión:
Color1 = "CC0000";
Color2 = "CCCCCC";
Color1Convertido = Number("0x"+Color1);
Color2Convertido = Number("0x"+Color2);

Para el promedio pensé en tomar los 2 1eros caracteres de cada uno, hacer el promedio, luego tomar otros 2, y así, pero para ahorrar tiempo le dije que lo haga él. Hizo:
Código
--
// Crea un cuadro de diálogo de entrada de texto para ingresar el primer color
--
var color1:String = prompt("Ingrese el primer color en formato hexadecimal:", "CC0000");
--
 
--
// Crea un cuadro de diálogo de entrada de texto para ingresar el segundo color
--
var color2:String = prompt("Ingrese el segundo color en formato hexadecimal:", "CCCCCC");
--
 
--
// Convierte los colores a decimal
--
var decimal1:Number = Number("0x" + color1);
--
var decimal2:Number = Number("0x" + color2);
--
 
--
// Calcula el promedio de cada componente RGB
--
var r1:Number = (decimal1 >> 16) & 0xFF;
--
var g1:Number = (decimal1 >> 8) & 0xFF;
--
var b1:Number = decimal1 & 0xFF;
--
 
--
var r2:Number = (decimal2 >> 16) & 0xFF;
--
var g2:Number = (decimal2 >> 8) & 0xFF;
--
var b2:Number = decimal2 & 0xFF;
--
 
--
var rPromedio:Number = Math.round((r1 + r2) / 2);
--
var gPromedio:Number = Math.round((g1 + g2) / 2);
--
var bPromedio:Number = Math.round((b1 + b2) / 2);
--
 
--
// Convierte el color promedio a hexadecimal
--
var colorPromedio:String = decimalToHex((rPromedio << 16) | (gPromedio << 8) | bPromedio);
--
 
--
// Muestra el color promedio
--
trace("El color promedio es: #" + colorPromedio);
--
 
--
// Función para convertir un número decimal a hexadecimal
--
function decimalToHex(decimal:Number):String {
--
   var hex:String = decimal.toString(16);
--
   while (hex.length < 6) {
--
       hex = "0" + hex;
--
   }
--
   return hex.toUpperCase();
--
}
--
Al intentar ejecutarlo dice:
Citar
--
Escena=Escena 1, Capa=Capa 1, Fotograma=1: Line 31: Se espera '{'
     function decimalToHex(decimal:Number):String {

Escena=Escena 1, Capa=Capa 1, Fotograma=1: Line 37: Se ha detectado un '}' inesperado
     }

--
Siempre me costó entender esas pistas, y este código claro que tampoco lo entiendo.

¿Por qué "0x"+ convierte a hexadecimal tan fácil? ¿es como una función (Number, toString, etc)?

¿Qué es esto
(decimal1 >> 16) & 0xFF;
(decimal1 >> 8) & 0xFF;
decimal1 & 0xFF;
?

¿Qué hace esto?
Código
--
function decimalToHex(decimal:Number):String {
--
   var hex:String = decimal.toString(16);
--
   while (hex.length < 6) {
--
       hex = "0" + hex;
--
   }
--
   return hex.toUpperCase();
--
}
--
¿Convierte decimal en string? Según recuerdo lo que se convierte es lo que va entre paréntesis. ¿Qué significaría 16? ¿convierte un número a hexadecimal así nomás?
Luego entiendo que agrega 0s si la longitud del hex generado es <6.

¿Y esto qué es?
var colorPromedio:String = decimalToHex((rPromedio << 16) | (gPromedio << 8) | bPromedio);
Se define a una variable llamando a una función... ¿pero qué es << 16, << 8 y los | ?

Por ahí dice:
Citar
--
Después, descomponemos cada valor decimal en sus componentes RGB (rojo, verde y azul) utilizando operaciones de desplazamiento y máscaras bit a bit. Calculamos el promedio de cada componente y lo redondeamos utilizando la función Math.round().

Finalmente, convertimos el color promedio de decimal a hexadecimal utilizando la función decimalToHex(), que toma el valor decimal y lo convierte a una cadena hexadecimal de 6 dígitos, agregando ceros a la izquierda si es necesario.

El resultado se muestra en la consola utilizando la función trace().
--
Creo que lo intentaré a mi manera pero agradezco explicación.

profinet:

Debes elevar al cuadrado cada valor RGB, después hacer la media, y luego calcular la raíz cuadrada de cada uno para revertir la primera operación.

Código:
--
NewColor = sqrt((R1^2+R2^2)/2), sqrt((G1^2+G2^2)/2), sqrt((B1^2+B2^2)/2)
--

Usando este enfoque, al elevar al cuadrado los componentes RGB antes de promediar, estamos aplicando un tipo de "ponderación" que favorece los valores más altos y minimiza el efecto de los valores más bajos. Esto es útil porque los valores RGB cuadrados preservan mejor las diferencias perceptuales entre los colores originales.

Serapis:

Cita de: Tachikomaia en  8 Mayo 2024, 02:52 am
--
¿Por qué "0x"+ convierte a hexadecimal tan fácil? ¿es como una función (Number, toString, etc)?
--
0x es el prefijo para indicar que el número debe interpretarse como un valor hexadecimal.

Citar
--
¿Qué es esto
(decimal1 >> 16) & 0xFF;
(decimal1 >> 8) & 0xFF;
decimal1 & 0xFF;
--
los signos: << y >> son operadores de desplazamiento de bits.
>> Equivale a dividir entre 2 por cada valor indicado.
<< Equivale a multiplicar por 2 por cada valor indicado.

Por ejemplo, supongamos que tengo la variable 'valor' que vale 1234
entonces: 'valor << 3' significa que se añaden 3 bits con valor 0 en la posición de menor peso, lo que equivaldría a haberlo multiplicado por 8, es decir equivale a esto:
valor = (valor * 2^³)
con lo que el valor finalmente sería:
En decimal:
antes: 1234, luego: 9872
En binario:
10011010010, luego 10011010010000

Ahora deberías entender que la operación: valor >> 3, lo que hace es eliminar los 3 bits más bajos que es equivalente a dividir entre 8.
Al ser operaciones de bit, son operaciones mucho más rápidas que multiplicar o dividir, pues requieren un solo ciclo de reloj (técnicamente).

El símbolo: '&' es el operador booleano AND y '0xFF' es el valor 255 expresado en hexadecimal, básicamene cuando haces un valor and 255, todos los bits por encima del 8º se ponen a cero.
toma la calculadora escribe (en decimal) 123456 (luego pulsa el botón de opción 'bin' para ver el valor en binario, luego devuélvelo a decimal pulsando el botón de opción 'dec') pulsa la tecla AND y luego escribe 255, el resultado será: 64, es decir 0100.0000 dicho de otro modo, hace que el valor esté en el rango 0-255. En definitiva tratándose del valor del canal de un píxel se asegura que el resultado no esté fuera de rango.


Citar
--
¿Qué hace esto?
Código
--
function decimalToHex(decimal:Number):String {
--
   var hex:String = decimal.toString(16);
--
   while (hex.length < 6) {
--
       hex = "0" + hex;
--
   }
--
   return hex.toUpperCase();
--
}
--
¿Convierte decimal en string?
--
Sí. Es una función que convierte un número decimal en una string formateado en hexadecimal y lo devuelve en mayúsculas .

Citar
--
Según recuerdo lo que se convierte es lo que va entre paréntesis.
--
Estás más verde de lo que pensaba en el lenguaje del que estás 'enamorado'...  :silbar: :silbar:
Convierte en hexadecimal el valor que contiene el parámetro de tipo numérico llamado 'decimal'.

Citar
--
¿Qué significaría 16? ¿convierte un número a hexadecimal así nomás?
Luego entiendo que agrega 0s si la longitud del hex generado es <6.
--
El 16, indica a la clase string, que lo cambie en la base numérica hexadecimal, si pones '8' lo convierte a octal, si pones '2' lo convierte a binario... creo que admite hasta la base 32 o así... (también 3, 5, 21,22... etc...)

Citar
--
¿Y esto qué es?
var colorPromedio:String = decimalToHex((rPromedio << 16) | (gPromedio << 8) | bPromedio);
Se define a una variable llamando a una función... ¿pero qué es << 16, << 8 y los | ?
--
Se define un variable sí, que recibe como valor el retorno de la función.
La función recibe un valor numérico, en este caso es una expresión numérica (una operación).
Sería equivalente a definir una variable numerica y asignarle el resultado de esa expresión... y que luego fuere esa variable la que pasaras a la función, pero así como está escrito, te ahorras código (igualmente el compilador debe crear una variable temporal para calcular el resultado de la expresión).

(rPromedio << 16) significa: multiplica 'rPromedio * 65536' (es una operación entera, como ya te indiqué más arriba no multiplica, solo añade bits, de la parte baja exactamente 16 bits '0', en realidad desplaza todos los bits (a la vez), 16 posiciones arriba, rellenando con 0 las posiciones más bajas que antes estaban ocupadas).
Después de calcular esos 2 valores 'r' y 'g' los unifica en un solo valor mediante el operador OR (el símbolo '|')
Un ejemplo de desplzamiento a la izquierda, sea el valor 5, en bnario 101, lo desplazaremos primero 1, luego 2, luego 3 y luego 8.
5 en binario: 101
5 << 1, que da: 1010, en decimal 10
5 << 2, que da: 10100, en decimal: 20
5 << 3, que da: 101000, en decimal: 40
5 << 8, que da: 10100000000, en decimal 1280


Esta expresión: (rPromedio << 16) | (gPromedio << 8) | bPromedio
lo que en realidad está haciendo es colocar cada valor (byte) que representa el canal de color del pixel, en la posición que deben ocupar... sería equivalente a:

Código:
--
color = (rojo * 65536) + (verde * 256) + azul
colorPromedio = decimalToHex(color)
--
Nota que usar la suma  en bytes perfectamente acotados en sus rangos, es
equivalente a usar el operador OR, que es más rápido que la suma.
RR000    \RR posición del byte rojo, tras multiplicarlo por 65536
00VV00  \ GG posición del byte verde, tras multplicarlo por 256
0000AA  \ posición del byte azul...
--------------
RRVVAA



Para calcular el promedio de 2 valores en efecto...
1 debes descomponer el color en sus canales:
Código:
--
azul1 = (color1 and 255)
verde1 = ((color1 \ 256) and 255)
rojo1 = (color1 \ 65536)
--
Lo mismo para color2... (rojo2, verde2 y azul2)
2 luego sumar los canales individuales y promediarlos:
Código:
--
tmpRojo = ((rojo1 + rojo2) \2)
tmpVerde = ((verde1 + verde2) \2)
tmpAzul = ((azul1 + azul2) \2)
--
3 Finalmente montar el resultado de los canales al color deseado...
Código:
--
color =  (tmpRojo * 65536) + (tmpVerde * 256) + tmpAzul
--

La forma seguida en el código es más óptima en cuanto al rendimiento, pues utiliza operaciones binarias... las multiplicaciones y divisiones son muy costosas comparativamente.

Todo esto en realidad son cosas elementales de programación, te suena a chino, no solo por ser autodidacta (que no es en sí el problema), es debido quizás a ser demasiado cabezón en no haber querido aprender las cosas elementales que además son comunes e indistintas a cualquier lenguaje. El escaso tiempo que ahorraste en no aprender (3-6 mses) lo pierdes ahora (con creces) en detalles insignificantes porque para tí son galimatías incomprensibles.

Tachikomaia:

profinet:
Eso lo pensaré más adelante.

Cita de: Serapis en  8 Mayo 2024, 23:48 pm
--
para tí son galimatías incomprensibles.
--
En general  :xD

Citar
--
0x es el prefijo para indicar que el número debe interpretarse como un valor hexadecimal.
--
Ya veo:
A = 0x20;
me da 32.

Así que con >> y << las operaciones funcionan más rápido... Nunca me habían dicho. Pero sólo sirven si son *2^algo o /2^algo ¿no?

Citar
--
El símbolo: '&' es el operador booleano AND y '0xFF' es el valor 255 expresado en hexadecimal, básicamene cuando haces un valor and 255, todos los bits por encima del 8º se ponen a cero.
toma la calculadora escribe (en decimal) 123456 (luego pulsa el botón de opción 'bin' para ver el valor en binario, luego devuélvelo a decimal pulsando el botón de opción 'dec') pulsa la tecla AND y luego escribe 255, el resultado será: 64, es decir 0100.0000 dicho de otro modo, hace que el valor esté en el rango 0-255. En definitiva tratándose del valor del canal de un píxel se asegura que el resultado no esté fuera de rango.
--
Esto me cuesta entenderlo ¿tiene sentido que le llaman AND? Bue, le llaman % al resto, no debería sorprender. El diccionario de Flash dice:
& (operador AND como bit)
Sintaxis

expresión1 & expresión2
Argumentos

expresión1, expresión2 Cualquier número.

Descripción

Operador (como bit); convierte expresión1 y expresión2 en números enteros de 32 bits sin signo y realiza una operación Booleana AND en cada bit de los argumentos del número entero. El resultado es un nuevo número entero de 32 bits sin signo.

Reproductor

Flash 5 o posterior. Si se ha utilizado el operador & en Flash 4 para concatenar cadenas. En Flash 5 el operador & es un AND como bit y los operadores add y + concatenan cadenas. Los archivos de Flash 4 que utilizan el operador & se actualizan automáticamente para utilizar add cuando se traen al entorno de autoría de Flash 5. Hice la prueba que me dijiste pero igual no lo entendí.

A = 0 & 1;
da 0.

A = 2 & 34;
da 2.

A = 5 & 678;
da 4.

A = 9 & 1234;
da 0.

GPT:
En Macromedia Flash 5, la línea de código "A = 0 & 1;" realiza una operación de "AND" a nivel de bits entre los números 0 y 1, y el resultado se asigna a la variable A.

En lenguajes de programación como ActionScript, el operador "&" se utiliza para realizar operaciones de bits a nivel de los valores binarios de los números. En este caso, el número 0 se representa como "00000000" y el número 1 como "00000001". Al realizar el operador "&", se compara cada bit en la misma posición en ambos números y se devuelve 1 solo si ambos bits son 1. De lo contrario, se devuelve 0.

En este caso específico, el resultado de la operación es 0, porque no hay ninguna posición en la que ambos números tengan un bit 1. Por lo tanto, la variable A se asignará con el valor 0.

Otro:
La expresión "123 & 456" también realiza una operación de "AND" a nivel de bits, pero esta vez entre los números decimales 123 y 456.

Para entender el resultado, primero debemos convertir esos números a su representación binaria:

123 en binario: 1111011
456 en binario: 111001000

Luego, aplicamos el operador "&" a nivel de bits comparando cada posición:

1111011
& 111001000
111001000

O sea:
1111011
&
111001000
=
111001000
Significa escribir un nuevo número de modo que, en el lugar donde cada uno tenga un 1 va un 1 y sino un 0, hasta que sea tan largo como el nro más largo. Lo que no entiendo es lo de "convierte expresión1 y expresión2 en números enteros de 32 bits", imagino que si son números pequeños les pone 0s delante ¿y sin son largos sólo toma lo que está delante?

Cuando se dice que el operador "&" convierte las expresiones a números enteros de 32 bits, significa que toma las expresiones y las representa internamente como números enteros de 32 bits antes de realizar la operación "AND" a nivel de bits.

Un número entero de 32 bits es una representación binaria de un número que utiliza 32 posiciones o bits. Cada bit puede tener un valor de 0 o 1. La conversión a 32 bits asegura que las expresiones se traten como números enteros de longitud fija antes de realizar cualquier operación a nivel de bits.

Si las expresiones originales son números más pequeños que 32 bits, se completa el número con ceros a la izquierda para alcanzar los 32 bits. Por ejemplo, si una expresión es el número decimal 5, se representa en binario como "00000000000000000000000000000101" para ocupar 32 bits.

Una vez que ambas expresiones se han convertido a números enteros de 32 bits, el operador "&" realiza la operación "AND" a nivel de bits entre cada par de bits correspondientes en las expresiones. El resultado es un nuevo número entero de 32 bits sin signo.

En resumen, la conversión a 32 bits asegura que las expresiones se traten como números enteros de longitud fija antes de realizar la operación "AND" a nivel de bits. Esto proporciona consistencia en la representación y permite realizar la operación de manera adecuada. Ah, ya veo, son muchos 0s, 32, me confundía con algo como esto:
Si hay 1 número son 2 posibles valores.
2 -> 4
3 -> 8
4 -> 16
5 -> 32
O no sé, tengo mucho lío, cuando busco cheats en juegos la opción 8 bits son 2 dígitos (o sea de 00 a FF), y de 32 son más pero no tantos, ah, puede ser porque eso es hexadecimal...

Entonces por ejemplo 123456 & xFF:
11110001001000000
&
00000000011111111
=
00000000001000000 = 64

¡Ok! Y esto sirve para... "tratándose del valor del canal de un píxel se asegura que el resultado no esté fuera de rango.", entiendo, pero no para qué aumentó los valores o cómo es eso.

Pero en el caso anterior no es así.
1111011
&
111001000
quedaría:
001111011
&
111001000
=
001001000

No entiendo entonces.

Citar
--
Es una función que convierte un número decimal en una string formateado en hexadecimal y lo devuelve en mayúsculas .
Estás más verde de lo que pensaba en el lenguaje del que estás 'enamorado'...  :silbar: :silbar:
Convierte en hexadecimal el valor que contiene el parámetro de tipo numérico llamado 'decimal'.
--
Ya, pero es que hace mucho que no convierto algo único a String, lo más parecido que he estado haciendo es concatenar números así Dato1+" "+Dato2, y como pensaba acabo de ver que esto:
J = 3;
J = J+"";
Hace que J sea "3", string, por lo que no hay necesidad de usar String(J) parece. Pero eso de J.toString(), no sé si alguna vez lo había visto, sobre cuestiones binarias y similares aún he visto muy poco.

Citar
--
sería equivalente a:

Código:
color = (rojo * 65536) + (verde * 256) + azul
colorPromedio = decimalToHex(color)
--
Ya ¿pero por qué funciona? A ver si lo puedo entender más simple:
- Supongamos que en vez de 0 a 255 fuese de 0 a 2 y que tenemos los colores 200 y 210.
- El resultado debería ser 100 o 110 dependiendo de cómo se redondeé.
¿Cómo se llega a ese resultado? ¿algo así?
R1 = Floor(200/100) = 2
Restante = 200-100*R1 = 0
G1 = Floor(Restante/10) = 0
B1 = Restante-10*G1 = 0
R2 = Floor(210/100) = 2
Restante = 210-100*R1 = 10
G1 = Floor(Restante/10) = 1
B1 = Restante-10*G2 = 0
NuevoR = Round((R1+R2)/2)
NuevoG = Round((G1+G2)/2)
NuevoB = Round((B1+B2)/2)
NuevoColor = NuevoR+""+NuevoG+NuevoB
Se supone que eso concatena bien, sino lo cambio.

Es mejor que obtener los caracteres como había planeado. Pero explicame el método de GPT o el tuyo con un ejemplo para que yo lo pueda entender, ojalá. Una de las diferencias es que los valores originales son hexadecimales y deben ser convertidos a decimales, supongo, para poder hacer el promedio. Ah, veo que lo hiciste:
Citar
--
azul1 = (color1 and 255)
verde1 = ((color1 \ 256) and 255)
rojo1 = (color1 \ 65536)
--
Lo que no entiendo es el and 255, y creo que te faltó ponerlo en rojo1 ¿no?
Ese and 255 ¿se puede convertir en una operación normal como hiciste con los >> y << o es más bien algo lógico?

Citar
--
son cosas elementales de programación, te suena a chino, no solo por ser autodidacta (que no es en sí el problema), es debido quizás a ser demasiado cabezón en no haber querido aprender las cosas elementales que además son comunes e indistintas a cualquier lenguaje. El escaso tiempo que ahorraste en no aprender (3-6 mses) lo pierdes ahora (con creces) en detalles insignificantes
--
No sé, con ese criterio tendría que haber estudiado electrónica y física atómica que es más elemental, yo aprendí lo que me fue necesario (y que me enteré qie era posible), hasta ahora nunca había precisado esto... De hecho quiero entender si se puede hacer "normal" porque yo programo en un lenguaje "normal", ya sabes, la típica discusión de si conviene aprender lenguaje máquina o no, en mi caso no creo porque me cuesta y tengo poca memoria para ciertas cosas...
¿Te parece que pierdo más tiempo por estar unas horas intentando aprender eso, en vez de pasarme horas haciendo ejercicios? Yo quise estudiar Programación hace años pero había cupos limitados y no obtuve suficiente buena calificación (hubo gente que con 1 punto más que yo entró, pero bue), pero luego vi que a un amigo le pedían hacer cálculos de convertir a binario o cosas que me parecían raras o poco útiles... así que perdí interés. Es como en Química y Física, yo quiero hacer cosas, no cálculos. Tampoco quiero hacer las cosas haciendo malabares si se pueden hacer más simple, me refiero a no usar moles y en este caso... bue, me interesó porque dices que funciona más rápido, pero sino lo del desplazamiento no lo usaría.

EdePC:

Citar
--
Si digo CC0000, es decir 204, 0, 0
y
CCCCCC, es decir 204, 204, 204
el resultado debe ser
204, 102, 102

Entiendo que debo convertir el hexadecimal a decimal y luego hacer promedios.
--

Citar
--
¿Por qué "0x"+ convierte a hexadecimal tan fácil? ¿es como una función (Number, toString, etc)?
--

Depende de como tomes el dato, por ejemplo CC0000 dará error porque para flash eso no es un número por lo que entenderá que es un nombre de variable que seguramente tampoco lo sea, para que Flash entienda que es un número debería ser: 0xCC0000, entonces Flash entenderá que le diste un número hexadecimal, si le das "CC0000" entenderá que es un String o Texto, en ambos no es que estás convirtiendo, solo le estás entregando los valores correctamente para que Flash lo entienda.

Citar
--
Siempre me costó entender esas pistas, y este código claro que tampoco lo entiendo.
--

Ese es código Action Script 2 o 3 por lo que se está usando declaración de variables tipadas especificando el tipo de variable (variable:String, variable:Number), se escribe la variable o función seguido de dos puntos y el tipo de dato que debe contener o devolver, quítalos para no tener problemas en Action Script 1 que es el que usas, no sé si ya hayas visto que hay programadores que usan prefijos en sus variables para indicar que tipo de variables son para ser más legibles, por ejemplo: sNombre = "pepe"; iEdad = 17; fEstatura = 1.67; (s de String, i de Integer, f de Float)

---
Todo lo demás son operaciones a nivel bit, básicamente se usan los desplazamientos binarios y la operación AND binaria para descomponer tu número hexadecimal 0xCC0000 en 0xCC, 0x00 y 0x00, puedes tomar ese camino o puedes trabajar con String que es más legible siempre y cuando ya hayas trabajado o conozcas el método SubStr y/o SubString para extraer partes de una String, supongo que parseInt y toString ya lo conoces bien, sino revisa la documentación o pregunta:

Código
--
sColor1 = "CC0000";
--
sColor2 = "CCCCCC";
--
 
--
iR1 = parseInt(sColor1.SubStr(0, 2), 16);
--
iG1 = parseInt(sColor1.SubStr(2, 2), 16);
--
iB1 = parseInt(sColor1.SubStr(4, 2), 16);
--
 
--
iR2 = parseInt(sColor2.SubStr(0, 2), 16);
--
iG2 = parseInt(sColor2.SubStr(2, 2), 16);
--
iB2 = parseInt(sColor2.SubStr(4, 2), 16);
--
 
--
iPromR = (iR1 + iR2) / 2;
--
iPromG = (iG1 + iG2) / 2;
--
iPromB = (iB1 + iB2) / 2;
--
 
--
trace(iPromR + ", " + iPromG + ", " + iPromB);
--
 
--
sColorPromHex = iPromR.toString(16) + iPromG.toString(16) + iPromB.toString(16);
--
trace(sColorPromHex);
--

Esto devuelve:

Código
--
204, 102, 102
--
cc6666
--


Páginas: (1/2) > >>