Capítulo 6 Herramientas

Llegamos a lo que, en mi opinión, es la parte más interesante de esta librería: las herramientas.

FreeImage dispone de varias herramientas para manipular imágenes. Veamos algunas de ellas.

Rotaciones

Gato
Gato
Gato rotado 45º
Gato rotado 45º

Es posible rotar imágenes mediante la función FreeImage_Rotate. Esta función requiere tres parámetros. El primero el manipulador del mapa de bits a rotar, el segundo el ángulo de rotación, en grados y en el sentido contrario a las agujas del reloj. Se trata de un valor double, por lo que se pueden rotar imágenes en fracciones de ángulo. Como la imagen rotada seguirá siendo un rectángulo, parte de la imagen final no estará ocupada por la imagen rotada. El tercer parámetro es opcional, y especifica el color de fondo para la parte que no ocupa la imagen inicial. Si no se especifica, será de color negro.

    RGBQUAD bkcolor = {64, 96, 64, 0};
    dib2 = FreeImage_Rotate(dib, 19, &bkcolor);
    FreeImage_Save(FIF_JPEG, dib2, "rotado1.jpg", JPEG_DEFAULT);
    FreeImage_Unload(dib2);

Las imágenes monocromas de un bit sólo admiten rotaciones con ángulos múltiplos de 90º.

También podemos usar la función FreeImage_RotateEx, que tiene algunas características añadidas:

Gato rotado 10º
Gato rotado 10º
  • La imagen rotada tendrá el mismo tamaño que la original, por lo tanto, parte de la imagen será eliminada.
  • Podemos elegir el centro de rotación en cualquier punto, dentro o fuera de la imagen.
  • Podemos seleccionar una traslación de la imagen rotada, para elegir qué parte de la imagen queremos conservar.
  • Y también se puede elegir entre llenar el fondo de negro o rellenarlo con una imagen especular de la imagen generada.

Por supuesto, estas opciones requieren más parámetros:

  • Los dos primeros parámetros son los mismos que para la otra función de rotación: la imagen a rotar y el ángulo.
  • Los dos siguientes parámetros definen una traslación en el eje x e y, respectivamente.
  • A continuación se definen las coordenadas del centro de rotación x e y, respectivamente.
  • El último parámetro es un booleano. Un valor TRUE indica que el fondo se quede negro, y un valor FALSE rellenará el fondo con una imagen especular.
    double x_orig = 93;
    double y_orig = 112;
    dib2 = FreeImage_RotateEx(dib, 10, 0, 0, x_orig, y_orig, FALSE);
    FreeImage_Save(FIF_JPEG, dib2, "rotado2.jpg", JPEG_DEFAULT);
    FreeImage_Unload(dib2);

Voltear imágenes

Una imagen se puede voltear verticalmente u horizontalmente. FreeImage nos proporciona funciones para los dos casos.

La función FreeImage_FlipHorizontal realiza un volteo horizontal, es decir, intercambia los pixels de izquierda y derecha.

La función FreeImage_FlipVertical realiza un volteo vertical, es decir, intercambia los pixels de arriba y abajo.

A diferencia de las funciones que hemos visto hasta ahora, estas dos últimas modifican la imagen pasada como parámetro, en lugar de crear una nueva imagen.

Sin embargo, como no hay pérdida de información, ambas operaciones son reversibles.

Cambio de escala

El cambio de escala puede realizarse en dos direcciones. Podemos aumentar la escala, equivale a un zoom digital o aumentar el tamaño. También podemos reducirla, o disminuir el tamaño.

Cuando se aumenta la escala será necesario añadir pixels, y cuando se disminuye habrá que combinar varios pixels en uno solo.

FILTER_BOX
FILTER_BOX
FILTER_BILINEAR
FILTER_BILINEAR
FILTER_BSPLINE
FILTER_BSPLINE
FILTER_BICUBIC
FILTER_BICUBIC
FILTER_CATMULLROM
FILTER_CATMULLROM
FILTER_LANCZOS3
FILTER_LANCZOS3

También es posible aumentar o disminuir la escala en distintos valores en el eje horizontal y vertical. Podemos, por ejemplo, aumentar una cantidad diferente en cada eje, o aumentarla en uno y disminuirla en otro, etc.

Hay varias formas de recalcular el color de cada nuevo pixel. FreeImage dispone de varios algoritmos para esa tarea, denominados filtros. Puedes verlos en la tabla 14.

La función FreeImage_Rescale se puede usar tanto para aumentar como para disminuir la escala.

El primer parámetro es la imagen a escalar. El segundo y tercer parámetros definen las dimensiones de la nueva imagen, respectivamente, la anchura y altura. El cuarto parámetro el el filtro a utilizar. Por defecto se usa FILTER_CATMULLROM.

    dib2 = FreeImage_Rescale(dib, FreeImage_GetWidth(dib)*10, FreeImage_GetHeight(dib)*10, FILTER_BOX);
    FreeImage_Save(FIF_JPEG, dib2, "reescala_box.jpg", JPEG_DEFAULT);
    FreeImage_Unload(dib2);

También disponemos de la función FreeImage_RescaleRect que nos permite reescalar solo una parte de la imagen. Los tres primeros parámetros son los mismos que para la función FreeImage_Rescale, los cuatro siguientes definen el rectángulo a reescalar, especificando las coordenadas superior izquierda e inferior derecha de ese rectángulo. El siguiente parámetro el el filtro y el último permite especificar unas banderas con las siguientes opciones:

Flag RescaleRectDescripción
FI_RESCALE_DEFAULT Opción por defecto; no se aplica ninguna de las otras opciones.
FI_RESCALE_TRUE_COLOR Para imágenes no transparentes, convierte a 24-bit si la profundidad de bits de src es menor o igual a 8 (por defecto es una imagen en escala de grises de 8-bit).
FI_RESCALE_OMIT_METADATA No copia los metadatos a la imagen reescalada.

Por ejemplo:

    dib2 = FreeImage_RescaleRect(dib, 256, 256, 76, 89, 25, 25, FILTER_BOX, FI_RESCALE_DEFAULT);
    FreeImage_Save(FIF_JPEG, dib2, "reescala_rec.jpg", JPEG_DEFAULT);
    FreeImage_Unload(dib2);

Miniaturas

Hay una función especial de reescalado, FreeImage_MakeThumbnail, enfocada a la creación de miniaturas.

El primer parámetro es la imagen de origen. El segundo define el tamaño máximo del del cuadrado en el que debe caber la imagen generada. El tercer parámetro, si es TRUE, usará una de las funciones FreeImage_ConvertToXXX para convertir imágenes de alto rango dinámico a 8, 24 o 32 bits.

    dib2 = FreeImage_MakeThumbnail(dib, 64, TRUE);
    FreeImage_Save(FIF_JPEG, dib2, "thumbnail.jpg", JPEG_DEFAULT);
    FreeImage_Unload(dib2);

Ejemplo 8

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 8 FreeImage008.zip 2023-04-26 1485 bytes 325

Colores

Independientemente del formato de la imagen en un fichero, una vez cargada en memoria, FreeImage trabaja con un modelo de color RGB(A) para imágenes en color. Para las imágenes en escala de grises trabaja con un único canal.

El canal alfa no contiene información de color, y se usa para crear máscaras que permiten seleccionar parte de la imagen.

FreeImage define ciertos canales que se pueden consultar en la tabla 15. Veremos a continuación funciones que se pueden aplicar a cada uno de esos canales o combinaciones de canales.

Histogramas

Histograma canal verde
Histograma canal verde

Un histograma es una representación gráfica de las frecuencias de los valores presentes en un determinado dominio.

En una imagen en color de 24 bits, por ejemplo, para la componente verde, podemos contar las veces que aparece cada valor, que estará entre 0 y 255. Esto nos dará una tabla con 256 valores, que podemos representar como una gráfica de barras.

Los histogramas proporcionan información sobre el brillo y contraste, lo oscura o clara que es una imagen, y el modo en que se distribuyen los pixels más claros y oscuros. Se pueden utilizar como datos de entrada para modificar el brillo, o para ecualizar el histograma de una imagen, con lo que se puede aumentar el contraste, o para corregir el tono de color.

Podemos obtener el histograma de una imagen usando la función FreeImage_GetHistogram, indicando en el primer parámetro la imagen, en el segundo el array de DWORDs que recibirá el histograma y en el tercero el canal.

    DWORD histo[256];
...
    FreeImage_GetHistogram(dib, histo, FICC_GREEN);

Ajustar el brillo

Gato -25, 0, 25 brillo
Ejemplo -25, 0, 25 brillo

Se puede ajustar el brillo de una imagen usando la función FreeImage_AdjustBrightness, indicando en el primer parámetro la imagen y en el segundo el porcentaje de ajuste. Este valor es un double que puede tener valores entre -100 y 100. Los valores negativos disminuyen el brillo, y los positivos lo aumentan.

    FreeImage_AdjustBrightness(dib, -25);
    FreeImage_Save(FIF_JPEG, dib, "brillo-25.jpg", JPEG_DEFAULT);

Ajustar el brillo supone modificar los valores de cada pixel en la misma dirección para toda la imagen. O bien se aumenta el valor de todos los pixels o de disminuye.

Ajustar el contraste

Gato -25, 0, 25 contraste
Ejemplo -25, 0, 25 Contraste

Del mismo modo, se puede ajustar el contraste de una imagen usando la función FreeImage_AdjustContrast, indicando en el primer parámetro la imagen y en el segundo el porcentaje de ajuste. Este valor es un double que puede tener valores entre -100 y 100. Los valores negativos disminuyen el contraste, y los positivos lo aumentan.

    FreeImage_AdjustContrast(dib, 25);
    FreeImage_Save(FIF_JPEG, dib, "contraste25.jpg", JPEG_DEFAULT);

Ajustar el contraste supone modificar los valores de cada pixel en direcciones diferentes dependiendo del valor inicial. Por ejemplo, para aumentar el contraste, se aumenta el valor de los pixels con más brillo y se disminuye el de los que tienen menor brillo.

Invertir la imagen

Invertir una imagen equivale a obtener un negativo fotográfico. De modo simétrico, al invertir un negativo se obtendrá una imagen positiva.

Para invertir una imagen disponemos de la función FreeImage_Invert.

Gato negativo
Imagen negativa

Cuando se quiere procesar un negativo fotográfico en color no basta con invertir la imagen. Debido al sustrato de la película y a la diferente sensibilidad a cada componente de color, es necesario hacer una corrección de color adicional.

Ajustar el gamma

Gato 0.8, 1 1.2 gamma
Ejemplo 0.8, 1, 1.2 gamma

Se puede ajustar el gamma de una imagen usando la función FreeImage_AdjustGamma, indicando en el primer parámetro la imagen y en el segundo el valor de ajuste. Este valor es un double que puede tener cualquier valor. El valor 1.0 no modifica la imagen, los valores menores disminuyen el gamma y los mayores lo aumentan.

Ajustar el gamma es parecido a ajustar el brillo, salvo que se aplica una curva logarítmica en lugar de lineal. Esto está relacionado con el modo en que el ojo humano percibe la luminosidad.

    FreeImage_AdjustGamma(dib, 1.2);
    FreeImage_Save(FIF_JPEG, dib, "gamma.jpg", JPEG_DEFAULT);

Ajustar mediante curva

La función FreeImage_AdjustCurve permite modificar cada canal a partir de un histograma. El primer parámetro es la imagen a modificar, el segundo es un puntero a un histograma normalizado, con valores entre 0 y 255, y el tercero es el canal al que se aplica la transformación.

Gato curva logarítmica
Imagen curva logarítmica

Para cada pixel se obtiene el valor en el canal indicado, y se sustituye por el valor en el histograma para ese índice:

channel(x, y) = LUT[channel(x, y)]
    for(int i=0; i<256; i++) lut[i] = (BYTE)(46*std::log(i+1));
    FreeImage_AdjustCurve(dib, lut, FICC_RGB);

Así si por ejemplo el histograma contiene valores iguales al índice, es decir, 0 para el Índice 0, 1 para el 1, etc, el valor del canal no se verá modificado. Si por el contrario, usamos 255 para el índice 0, 254 para el 1, etc, el valor del canal será el negativo, etc.

Todos los ajustes realizados con las funciones anteriores son ajustes prediseñados, es decir, usan tablas prestablecidas.

Esto nos permite hacer ajustes muy finos y diferentes para cada canal para corregir o modificar imágenes.

Crear tabla de transformación

Si tenemos que aplicar varias de las transformaciones anteriores, podemos agrupar algunas de ellas en una única operación. Para ello disponemos de la función FreeImage_GetAdjustColorsLookupTable. Esta función crea una tabla de transformación que se puede usar posteriormente con la función FreeImage_AdjustCurve.

Podemos de este modo aplicar en una única operación un ajuste de brillo, contraste, gamma e inversión.

Para ello, en el primer parámetro pasaremos un puntero al array que contendrá la tabla, que debe ser un array de 256 elementos de tipo BYTE. El segundo parámetro indica el brillo, entre -100 y 100. El tercero el contraste, también entre -100 y 100. El cuarto indica el gamma, que debe ser mayor que cero, valores menores de 1.0 disminuyen y mayores lo aumentan. El quinto parámetro es TRUE para invertir la imagen.

Por ejemplo:

    BYTE lut[256];
...
    FreeImage_GetAdjustColorsLookupTable(lut, -5, 5, 0.9, FALSE);
    FreeImage_AdjustCurve(dib, lut, FICC_RGB);

Equivale a:

    FreeImage_AdjustBrightness(dib, -5);
    FreeImage_AdjustContrast(dib, 5);
    FreeImage_AdjustGamma(dib, 0.9);

Sin embargo es preferible la primera forma, ya que es mucho más rápida, sobre todo con mapas de bits grandes.

Si no necesitamos la tabla de conversión podemos usar directamente la función FreeImage_AdjustColors, que realiza la misma transformación, pero usando una tabla de transformación interna:

    FreeImage_GetAdjustColors(lut, -5, 5, 0.9, FALSE);

Ejemplo 9

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 9 FreeImage009.zip 2023-04-26 1429 bytes 355

Intercambio de colores

Para cambiar el color de todos los pixels de un mismo color en una imagen, o de los pixels de un grupo de colores, podemos usar la función FreeImage_ApplyColorMapping.

En el primer parámetro indicaremos la imagen a procesar, el segundo srccolors es un array de valores RGBQUAD con los colores que se deben sustituir, el tercero, dstcolors, es otro array de RGBQUAD con los nuevos colores, el cuarto el número de colores en los arrays, el quinto es un booleano que indicará si se debe ignorar el canal alfa. El último parámetro, si es TRUE, indicará que el los colores de los dos arrays serán intercambiados. Es decir, los pixels de los colores en srccolors se cambiarán a los colores en dstcolors, y los pixels de los colores en dstcolors se cambiarán a los colores en srccolors.

En caso de mapas de bits sin paleta, los datos de los pixels serán modificados. En caso de mapas de bits con paleta se modificará la paleta.

En caso de que sólo haya que intercambiar dos colores disponemos de la función FreeImage_SwapColors. En realidad, esta función llama a FreeImage_ApplyColorMapping, y asume que los arrays sólo contienen un color y que el parámetro swap es TRUE.

    RGBQUAD color1;
    RGBQUAD color2;
...
    FreeImage_SwapColors(dib, &color1, &color2, TRUE):
    /* Equivale a: */
    FreeImage_ApplyColorMapping(dib, &color1, &color2, 1, TRUE, TRUE);

Para mapas de bits con paleta también disponemos de la función FreeImage_ApplyPaletteIndexMapping que cambia los índices de los pixels. Al contrario que las funciones anteriores, en este caso no se modifica la paleta, sino que se modifica la información de los pixels.

El primer parámetro es la imagen a procesar, el segundo, srcindices, el array de índices a sustituir, el tercero, dstindices, el array de índices con los nuevos valores, el cuarto el número de elementos en los arrays, y el quinto indica si se debe hacer el intercambio. Es decir, los pixels con los índices en srcindices se cambiarán a los índices en dstindices, y los pixels con los índices en dstindices se cambiarán a los índices en srcindices.

También en este caso existe una función para intercambiar sólo una pareja de índices. La función FreeImage_SwapPaletteIndices asume que los arrays de índices sólo contienen un elemento, y que el parámetro swap es TRUE.

    BYTE index1;
    BYTE index2;
...
    FreeImage_SwapPaletteIndices(dib, &index1, &index2):
    /* Equivale a: */
    FreeImage_ApplyPaletteIndexMapping(dib, &index1, &index2, TRUE);

Procesamiento de canales

Por último, podemos obtener una imagen con cualquiera de los canales de una imagen FIF mediante la función FreeImage_GetChannel, o sustituir cualquiera de los canales usando la función FreeImage_SetChannel.

Por ejemplo, podemos intercambiar los canales rojo y verde de una imagen:

    dib_rojo = FreeImage_GetChannel(dib, FICC_RED);
    dib_verde = FreeImage_GetChannel(dib, FICC_GREEN);
    FreeImage_SetChannel(dib, dib_rojo, FICC_GREEN);
    FreeImage_SetChannel(dib, dib_verde, FICC_RED);
    FreeImage_Unload(dib_rojo);
    FreeImage_Unload(dib_verde);

Ejemplo 10

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 10 FreeImage010.zip 2023-04-26 1136 bytes 331