c++ - imagenes - Algoritmos de reducción de la imagen
convertir rgb a escala de grises c++ (6)
¿Cuál es el mejor algoritmo de remuestreo que puedo usar para dividir una imagen en la mitad de su tamaño original? La velocidad es de primordial importancia, pero no debe degradar la calidad demasiado. Básicamente estoy tratando de generar una pirámide de imagen.
Originalmente estaba planeando saltar píxeles. ¿Es esta la mejor manera de ir? Por lo que he leído, la imagen producida por la omisión de píxeles es demasiado nítida. Podría alguien que haya intentado este comentario. Mis imágenes contienen datos de mapas de this.
El paquete NetPBM incluye una utilidad llamada pamscale , que proporciona algunas opciones para reducir el muestreo. Es de código abierto, por lo que puedes probar varias opciones y luego copiar el algoritmo que más te guste (o simplemente usar libnetpbm).
Intenté generalizar la solución de Thilo Köhler (pero en Python):
STRIDE = 2
MASK = 0x3F3F3F3F
color = 0
for Δx, Δy in itertools.product(range(STRIDE), repeat=2):
color += (get_pixel(x + Δx, y + Δy) // STRIDE) & MASK
Esto funciona bien para escalar en 2 (resultado del tamaño del trimestre), pero no funciona para escalar en 3 o 4 u otros valores int. ¿Es posible generalizar esto?
Por cierto, para los no pitonistas, el bucle for anterior es equivalente a esto (excepto que la primera versión es escalable cambiando el STRIDE):
for Δx, Δy in [(0, 0), (0, 1), (1, 0), (1, 1)]:
color += (get_pixel(x + Δx, y + Δy) // STRIDE) & MASK
Estoy usando valores ARGB de 32 bits.
La omisión de píxeles dará lugar a un alias, donde los cambios de alta frecuencia (como alternar las bandas claras / oscuras) se convertirán a bajas frecuencias (como la luz constante u oscura).
La forma más rápida de reducir a la mitad sin aliasing es promediar 2x2 píxeles en un solo píxel. Se pueden obtener mejores resultados con núcleos de reducción más sofisticados, pero se producirán a expensas de la velocidad.
Edición: Aquí hay algunos ejemplos de las técnicas discutidas hasta ahora.
Al omitir cualquier otro píxel, puedes ver que los resultados no son muy buenos al mirar la leyenda en el lado izquierdo. Es casi ilegible:
Promediando cada cuadrícula de 2x2: el texto ahora es nítido y legible:
Desenfoque gaussiano, como lo sugiere R. - un poco borroso, pero más legible hasta cierto punto. La cantidad de desenfoque se puede ajustar para dar diferentes resultados:
R. también tiene razón sobre la curva Gamma que afecta a los resultados, pero esto solo debería ser visible en las aplicaciones más exigentes. Mis ejemplos fueron hechos sin corrección gamma.
Para reducir la escala, el promedio de área (ver la respuesta de Mark) es lo mejor que obtendrás.
El otro contendiente principal es gaussiano, con un radio ligeramente mayor. Esto aumentará un poco el desenfoque, lo que podría verse como una desventaja, pero haría que el desenfoque fuera más uniforme en lugar de depender de la alineación de los píxeles mod 2.
En caso de que no quede inmediatamente claro a qué me refiero, considere los patrones de píxeles 0,0,2,2,0,0 y 0,0,0,2,2,0. Con promedios de área, reducirían su escala a 0,2,0 y 0,1,1, respectivamente, es decir, uno será nítido y brillante, mientras que el otro quedará borroso y tenue. Usando un filtro más largo, ambos estarán borrosos, pero aparecerán más similares, lo que presumiblemente es importante para los observadores humanos.
Otro tema a considerar es el gamma. A menos que el gamma sea lineal, dos píxeles de intensidad k
tendrán una intensidad total mucho menor que un solo píxel de intensidad 2*k
. Si su filtro realiza un desenfoque suficiente, es posible que no importe mucho, pero con el filtro de área simple puede ser un problema importante. La única solución alternativa que conozco es aplicar y revertir la curva gamma antes y después de escalar ...
Si la velocidad es un problema, como se mencionó, recomiendo tomar un Bloque 2x2 y calcular el promedio como el píxel resultante. La calidad no es la mejor que se puede lograr, pero está cerca. Puedes provocar que este algoritmo muestre sus debilidades, pero en la mayoría de las imágenes no verás una diferencia que justifique el tiempo de cómputo mucho mayor. También no tienes ningún gasto de memoria. Si la resolución de color se puede reducir a 6 bits por canal, esta es una forma bastante rápida que evita que se descompongan los canales ARGB (aquí, suponiendo ARGB de 32 bits):
destPixel[x,y] = ((sourcePixel[2*x ,2*y ]>>2)&0x3f3f3f3f) +
((sourcePixel[2*x+1,2*y ]>>2)&0x3f3f3f3f) +
((sourcePixel[2*x ,2*y+1]>>2)&0x3f3f3f3f) +
((sourcePixel[2*x+1,2*y+1]>>2)&0x3f3f3f3f);
El efecto secundario de este algoritmo es que si se guarda como PNG, el tamaño del archivo se reduce. Así es como esto luce: