De entrada el color rosa, míralo como el color chicle, es decir un valor como:
Verde = 0
Rojo = 255
Azul = 255
Tomando una foto, es muy probable que ese color se difumine en varios sonos similares, mejor si se escanea, aunque el resultado tampoco será perfecto.
Lo que se pide básicamente es obtener los colores de la hoja que no sean rosa, esto es el rosa se trata como si fuera un color transparente. Sin embargo, resulta ambiguo... no se aclara como debe ser el formato devuelto (aparte de que debe ser en hexadecimal)... ni si basta indicar una sola vez cada color o no.
Así que supondremos que se pide obtener todos los colores que no sean rosa, y que no se guarda posición del píxel para dicho color, y que se guarda para cada color píxel, haya aparecido previamente dicho color o no.
constantes byte RosaR=255, RosaA=255, RosaV=0 // valor canales del rosa-chicle
byte nivSimil // nivel de similitud
byte r, a, v // canal de cada colores
int f, p // num filas y valor píxel
string hexColor // color en hexadecimal.
int ancho, alto // medidas d ela imagen
array int Imagen(,) // array de dos dimensiones o unidimensional si se calcula fila a fila la posición absoluta del píxel...
fichero fiI // fichero de entrada (Input), el de la imagen
fichero fio // fichero de salida (output), el del resultado
fiI.Abrir(rutaImagen) // se abre el fichero de entrada
fio.Abrir(ruta) // se abre el fichero de salida.
alto = fiI.LeerEnCabecera(alto)
ancho = fiI.LeerEnCabecera(ancho)
// solo la que proceda... se leen los píxeles desde final de cabecera...
// se supone que la imagen está descomprimida, sino es así, se precisa
// el decodificador del formato de imagen para obtener el array de píxeles...
// se da por supuesto conocer tal circunstancia.
Imagen(alto * ancho) = fiI.LeerPixeles //lectura unidimensional
Imagen(alto, ancho) = fiI.LeerPixeles // lectura bidimensional
fiI.Cerrar
nivSimil = 16
bucle f por cada fila en la imagen //imagen sería un array de píxeles, por ejemplo.
bucle p por cada pixel en f // p es el enésimo píxel en la f (fila) actual.
// 1 obtener los canales del pixel.
r = (p and 255)
v = ((p \ 256) and 255)
b = (p \ 65536)
// 2 descartar si es rosa o similar... vamos a suponer algún grado de similitud
// si, el color está 16 valores a un lado u otro por canal consideramos que es similar... y en tal caso se descarta, si no, se acepta.
// como dicho rosa está en los extremos, basta mirar hacia el otro extremo.
Si (((v > = (RosaV + nivSimil )) y ((R <= (RosaV - nivSimil)) y ((a <= (RosaA - nivSimil)) )
// formato: AAVVRR (esto es primero azul, luego verde, luego rojo)
// el operador '+' actúa como un concatenador de caracteres...
hexcolor = (((a\16).ToChar) + ((a and 15).ToChar) + ((v\16).ToChar) + ((v and 15).ToChar) + ((r\16).ToChar) + ((r and 15).ToChar)) + " "
fiO.SaveToFile(appendtofinal, hexcolor)
Sino
fiO.SaveToFile(appendtofinal, "------ ") // color rosa se guarda así, necesario si
// ...se quiere mantener posición relativa de cada color que no sea rosa.
// cada clor va separado por un espacio, para facilitar su 'lectura', visualmente...
fin si
siguiente
siguiente
fiO.Cerrar
Se puede optimizar, si al canal de rosa, ya se le aplica antes del bucle, el valor de similitud, en vez de ecalcularlo por cada píxel... como los valores de rosa son constantes, se rquerirán sendas variables, para ello.
Sería aún más útlil, si en vez del rosa, fuera cualquier otro color recibido por parámetro, tal color se descomprondría e sus canales, aunque en tal caso, la verificación para descartar, es más larga pues hay que descartar también los valores al otro lado del límite de cada canal, actualmente se aprovecha que el rosa, tiene sus tres canales en valores que a un lado son límite...
Falta obtener la forma... Ahora nos ocupamos de ella...
La forma es un contorno. Un contorno, es el recorrido perimetral de la forma.
Sea una imagn de X*Y medidas, podemos saber que el controno, será como máximo (X+Y) * 2 de tamaño... que es el perímetro de un rectángulo, en este caso del rectángulo que forma la imagen. Nótese que solo consideramos la forma externa, si por ejemplo la hoja tuviera uno o más rotos internos, esas formas no quedan detectadas... Eso es una fase posteiror, para aplicar una vez se entienda al menos la primera, detectar la forma externa.
Cómo se obtiene?. Básicamente definimos dos arrays uno del vertical y otro del horizontal, de una estructura que para el vertical define izquierdo y derecho y para el horizontal superior e inferior... una simple abstración, nos permite resumir ambas en inicio y fin. Cada una define un punto, una posicion, el par X,Y.
Estructura Posicion
int X
int Y
Fin estructura
Estructura Extremo
posicion Inicial // izquierda o superior
posicion Final // derecha o inferior
fin estructura
Array de Extremo PerimVertical(alto)
array de Extremo PerimHorizontal(ancho)
Array Extremo NumPuntos(0 a 1) // 0 refiere vertical, 1 refiere horizontal, cantidad de puntos que cvontiene el array (el resto sobra)
int x, y, n // punteros de avance para cada array...
Nótese que el contorno, será la línea que una dichos puntos... como también resulta ambiguo la forma en que deba ser devuelto, baste estos dos arrays y luego haz con ellos lo que quiera, posiblemente escribirlos a fichero o sarlo para dibujar en algún lienzo... y luego guardar la imagen resultante a fichero.
Aunque los siguientes bucles parezcan mucha sobrecarga de trabajo, en realidad, solo se recorre hasta encontrar un pixle que no sea similar al rosa.
// obtener la parte vertical izquierda...
n=0
bucle para y desde 0 hasta Alto-1
bucle para x desde 0 hasta Ancho-1
Si (Similar(Rosa, imagen(y, x), 16) = FALSE)
PerimVertical(n).Inicial.X = x
PerimVertical(n).Inicial.y = y
n = (n +1)
Salir del bucle
fin si
siguiente
siguiente
NumPuntos(0).Inicial = n // solo los primeros 'n' puntos dle array PerimVertical son válidos
// obtener la parte vertical derecha...
n=0
bucle para y desde 0 hasta Alto-1
bucle para x desde Ancho-1 hasta 0 retrocediendo
Si (Similar(Rosa, imagen(y, x), 16) = FALSE)
PerimVertical(n).Final.X = x
PerimVertical(n).Final.y = y
n = (n +1)
Salir del bucle
fin si
siguiente
siguiente
NumPuntos(0).Final = n // solo los primeros 'n' puntos dle array PerimVertical son válidos
// obtener la parte horizontal superior...
n=0
bucle para x desde 0 hasta Ancho-1
bucle para y desde 0 hasta Alto-1
Si (Similar(Rosa, imagen(y, x), 16) = FALSE)
PerimHorizontal(n).Inicial.X = x
PerimHorizontal(n).Inicial.y = y
n = (n +1)
Salir del bucle
fin si
siguiente
siguiente
NumPuntos(1).Inicial = n // solo los primeros 'n' puntos dle array PerimHorizontal son válidos
// obtener la parte horizontal inferior...
n=0
bucle para x desde Ancho-1 hasta 0 retrocediendo
bucle para y desde 0 hasta Alto-1
Si (Similar(Rosa, imagen(y, x), 16) = FALSE)
PerimHorizontal(n).Final.X = x
PerimHorizontal(n).Final.y = y
n = (n +1)
Salir del bucle
fin si
siguiente
siguiente
NumPuntos(1).Final = n // solo los primeros 'n' puntos dle array PerimHorizontal son válidos
y listo, lo que hagas luego con dichos arrays ya es cosa tuya, pués no queda definido... Como dije antes, se puede guardar a fichero, o genera la imagen de la forma, recoriendo los 4 arrays para pintar el píxel que refieren sus cordenadas... y quizás luego guardarla la imagen resultante a fichero.
Similar ahora la definimos como una función, para dejar más claro la funcionalidad de obtener el contorno.
Esta función hace lo que ya hemos hecho antes en código, pero integrado en la misma funcion.
Rosa lo pasamos entero, es más rentabale pasar los canales ya descompuestos... (para hacelro una sola vez, no con cada color).
Buleano = ColorSimilar(int Color, int colCandidato, int nivel)
int r1, v1, a1, r2, g2, a2, r3, g3, a3
// Descomponer el color base a comparar aplicado sus niveles máximo y minimo a cada canal...
r1= fixToRangoByte((color and 255) + nivel)
r2 =fixToRangoByte((color and 255) - nivel)
g1= fixToRangoByte(((color \ 256) and 255) + nivel)
g2 =fixToRangoByte(((color \ 256) and 255) - nivel)
a1= fixToRangoByte((color \ 65536) + nivel)
a2 =fixToRangoByte((color \ 65536) - nivel)
// Descomponer el color candidato en sus canales...
r3 = (colCandidato and 255)
g3 = ((colCandidato\ 256) and 255)
a3 = (colcandidato \ 65536)
// Comparación:
// ejemplo para canal rojo: r= 45; r1=(45+16) = 61; r2= (45-16) = 29
// si r3 cae ente 29 y 61 el rojo es similar... igual para el resto de canales.
Si ((r3 <= r1) y (r3 >= r2))
Si ((g3 <= g1) y (g3 >= g2))
Si ((a3 <= a1) y (a3 >= a2))
devolver TRUE
fin si
fin si
fin si
fin funcion
Byte = fixToRangoByte(int Valor)
Si (valor < 0 )
valor = 0
osi (Valor > 255)
valor = 255
fin si
devolver Valor
Fin funcion
Si en vez de hablar de similitud, pudiéramos hablar de igualdad, es decir que el rosa, no tuviera variedad tonal, el cálculo se aceleraría, pués bastaría comparar el píxel 'p', con rosa , en vez de calcular si es similar... pero sacando fotos, o escaneos, no habrá un color puro... la luminosidad del ambiente y los reflejos marcarán variaciones tonales.