Tratamiento de imágenes II: Orientar imágenes según tono azul

Tratamiento de imágenes II: Orientar imágenes según tono azul
Facebook Twitter Flipboard E-mail

Continuamos con la serie sobre tratamiento de imágenes. En números pasados explicamos ciertos fundamentos sobre los colores y creamos un filtro sencillo que convertía a gris una foto. Para seguir este post es necesario que dispongas del código fuente realizado en anteriores números de esta serie.

En ocasiones se realizan fotografías que por cuadrar la figura principal se realizan con una orientación vertical. Cuando se realiza una foto así hay que realizar un proceso de revisión de rotación de las imágenes. Detectar que una imagen debe sea horizontal o vertical sin una revisión humana es complejo. Al abordar este problema, lo primero que debemos tener en cuenta es que nos podemos encontrar cualquier imagen y es necesario conocer que estamos observando para conocer si debería rotarse para pasarlo a vertical.

Para solventar este problema vamos a delimitar la solución a fotografías realizadas en el exterior donde se pueda apreciar el cielo. De esta manera tenemos un patrón que podemos analizar para conocer la orientación de la foto. El algoritmo que realicemos a continuación no servirá para fotografías de interior o que no se pueda apreciar un cielo lo suficientemente azul.

Aún así, realizando un algoritmo que busque el cielo nos podemos encontrar con problemas adicionales. Uno de estos problemas es la aparición nubes o la posibilidad de encontrar diferentes tonalidades de color azul. También nos podemos encontrar con el problema en el que el cielo se refleje en algún objeto como puede ser el mar. En estos casos nuestro algoritmo puede equivocarse.

Detectar el cielo según la cantidad de azul por cuadrante

Pueden existir diferentes técnicas para detectar donde está el cielo que funcionarán mejor o peor en función de como está realizada la foto y la tonalidad del cielo. En este ejemplo vamos a realizar una algoritmo sencillo que consistirá en sumar niveles de azul en los cuatro cuadrantes de la foto. Estos cuadrantes se obtendrán dividiendo la foto en 4 rectángulos iguales correspondientes a las cuatro esquinas.

A continuación, para detectar si un punto es de color azul debemos tener en cuenta que nos podemos encontrar con diferentes tonalidades de azul. Por todo ello la condición que utilizaremos para detectar el color azul será que el valor del canal azul sea mayor que el canal verde y el canal verde superior al canal rojo. De esta manera comprobaremos cualquier tipo de tonalidad azul y cyan.

Existirían diferentes formas de computar un punto en un cuadrante. Una opción es simplemente si el punto está en unos rangos de azul concreto incrementar el cuadrante en uno. Otra forma es aplicar una heurística o un valor variable según unos criterios como la cercanía con el borde o el total de puntos parecidos en el cuadrante.

Nosotros vamos a realizar algo sencillo que consistirá en incrementar la diferencia entre el tono azul y el rojo. De esta manera un color azul claro tendrá más peso que un color azul grisáceo. Sin embargo, al no comprobar posiciones contiguas objetos azules en la imagen se detectará cualquier objeto azul y podría dar falsos cómputos y como consecuencia orientaciones erróneas.

Finalmente, hemos incluido una variable umbral para que en el caso de que la imagen disponga de escasos colores azules, como pueda ser el caso de una fotografía de interior, deje la imagen como está. Será este umbral el que decida a partir de que valor se entiende que la imagen no debe ser rotada.

Colores

Rotar imagen

Una vez detectado cuanto azul hay en cada cuadrante obtendremos la orientación que dejan los dos cuadrantes con más azul en la parte superior. Para aplicar la rotación no utilizaremos formulas trigonométricas. Cuando la rotación es de 90, 180 o 270 tan solo hay que recorrer una imagen y al volcar ese punto en la otra imagen cambiar las variables de recorrido.

Colores

Por ejemplo, para rotar 180 grados tan solo habría que aplicar la fórmula xDestino=ancho-xOrigen y yDestino=alto-yOrigen. En nuestro algoritmo implementaremos la rotación de 90 y 180 grados. Para rotar 270 grados lo que haremos es ejecutar los dos algoritmos 90+180. Aquí tenéis el algoritmo del filtro.

package org.gbd.fotos;
import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
public class FiltroDetectarCielo implements Filtro{
    //A partir de este valor para valor obtenido se aplica la rotacion.
        private int umbralAzulParaRotar=10; 
    public FiltroDetectarCielo(){
    }
    public FiltroDetectarCielo(int umbralAzulParaRotar){
        this.umbralAzulParaRotar=umbralAzulParaRotar;
    }
    public BufferedImage filtrar(BufferedImage bi){
        double contarAzulPorCuadrantes[][]={
            {0,0},
            {0,0} 
        };
        for (int x=0;x < bi.getWidth();x++){
            for (int y=0;y < bi.getHeight();y++){
                Color c1=new Color(bi.getRGB(x, y));
                int r=c1.getRed();
                int v=c1.getGreen();
                int a=c1.getBlue();
                if (r max){
                max=rotaciones[n];
                posMax=n;
            }
        }
        //Si no hay suficientes colores azules deja la foto igual
        int umbral=bi.getWidth()*bi.getHeight()*umbralAzulParaRotar/2;
        if (max < umbral){ //Si no supera un umbral de azul o hay demasiado azul por toda la foto 50% 
            //no rota
            max=0;
            posMax=0; 
        }
        //Detecta si la nueva imagen es vertical o horizontal 
        boolean oritentarVertical=(posMax%2==1);
        int ancho=bi.getWidth();
        int alto=bi.getHeight();
        if (oritentarVertical){ //Horientación vertical
            ancho=bi.getHeight();
            alto=bi.getWidth();
        }
        //Crea el espacio para la nueva imagen
        BufferedImage biDestino = 
                        GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()
                                .createCompatibleImage(ancho, alto, Transparency.OPAQUE);
        for (int x=0;x < bi.getWidth();x++){
            for (int y=0;y < bi.getHeight();y++){
                int color=bi.getRGB(x, y);
                int x2=x, y2=y;
                //Rotacion 90 grados;
                if (oritentarVertical){
                    x2=y;
                    y2=bi.getWidth()-x-1;
                    }
                //Invertir la figura 180 grados
                boolean invertir=(posMax>1);
                    if (invertir){
                    x2=biDestino.getWidth()-x2-1;
                    y2=biDestino.getHeight()-y2-1;
                }
                biDestino.setRGB(x2,  y2, color);
            }
        }       
        return biDestino;
    }
}

Para ejecutar el algoritmo será necesario cambiar el método Main.main, creado en el anterior post de esta serie, añadiendo este nuevo filtro al ArrayList.

// Almacenar los filtros que deseamos aplicar
ArrayList < Filtro > alFiltros=new ArrayList< Filtro >();
alFiltros.add(new FiltroDetectarCielo()); 

Conclusión

En este post hemos implementado un algoritmo que detecta la orientación de las fotografías para casos concretos en se puede observar un cielo y el resto de la escena dispone elementos variados que no abusen del color azul hasta el punto de superar al nivel del tono azul del cielo. Este filtro no se recomienda en entornos donde el cielo pueda quedar reflejado sobre los objetos como en playas, aguas o edificios de cristal, ni cuando el objeto principal tiene un valor considerable de azul.

En Genbeta Dev | Tratamiento de imágenes I: Escala de grises

Comentarios cerrados
Inicio