Cómo agregar texto a imágenes usando JAVA
Introducción
En ocasiones, es necesario añadir texto a una imagen o a un conjunto de imágenes. Hacer esto de forma manual es sencillo utilizando una herramienta de edición de imágenes. Sin embargo, cuando queremos añadir el mismo texto de la misma manera a un número significativo de fotos, sería muy útil hacerlo programáticamente. En este tutorial rápido, vamos a aprender cómo agregar texto a imágenes usando Java.
1. Overview
Crear y manipular imágenes en programación es una práctica común. En este caso, nos enfocaremos en cómo añadir texto a imágenes de una manera fácil y eficiente utilizando dos enfoques populares: la biblioteca ImageJ y las clases estándar de Java, como BufferedImage
y Graphics
. Beneficiarse de estas técnicas puede facilitar la automatización en proyectos que requieren la manipulación de gráficos.
2. Adding Text to an Image
Para leer una imagen y añadirle texto, podemos usar diferentes clases. A continuación, exploraremos un par de opciones.
2.1. ImagePlus y ImageProcessor
Primero, veamos cómo usar las clases ImagePlus y ImageProcessor que están disponibles en la biblioteca ImageJ. Para usar esta biblioteca, necesitamos incluir esta dependencia en nuestro proyecto:
<dependency>
<groupId>net.imagej</groupId>
<artifactId>ij</artifactId>
<version>1.51h</version>
</dependency>
Para leer la imagen, utilizaremos el método estático openImage
. El resultado de este método se almacenará en la memoria utilizando un objeto ImagePlus
:
ImagePlus image = IJ.openImage(path);
Una vez que tenemos la imagen cargada en memoria, añadiremos texto utilizando la clase ImageProcessor
:
Font font = new Font("Arial", Font.BOLD, 18);
ImageProcessor ip = image.getProcessor();
ip.setColor(Color.GREEN);
ip.setFont(font);
ip.drawString(text, 0, 20);
Con este código, estamos añadiendo el texto especificado en verde en la esquina superior izquierda de la imagen. Notemos que fijamos la posición utilizando los segundos y terceros argumentos del método drawString
, que representan el número de píxeles desde la izquierda y desde la parte superior, respectivamente.
2.2. BufferedImage y Graphics
A continuación, veremos cómo lograr el mismo resultado utilizando las clases BufferedImage y Graphics. La construcción estándar de Java incluye estas clases, así que no necesitamos bibliotecas adicionales.
De la misma manera que utilizamos openImage
de ImageJ, utilizaremos el método read
disponible en ImageIO
:
BufferedImage image = ImageIO.read(new File(path));
Una vez que tenemos la imagen cargada en memoria, añadimos texto utilizando la clase Graphics
:
Font font = new Font("Arial", Font.BOLD, 18);
Graphics g = image.getGraphics();
g.setFont(font);
g.setColor(Color.GREEN);
g.drawString(text, 0, 20);
Ambas alternativas tienen un uso muy similar. En este caso, los segundos y terceros argumentos del método drawString
se especifican de la misma manera que lo hicimos para el método ImageProcessor
.
2.3. Draw Based on AttributedCharacterIterator
El método drawString
disponible en Graphics
permite imprimir el texto utilizando un AttributedCharacterIterator. Esto significa que, en vez de utilizar una cadena simple, podríamos usar texto con algunas propiedades asociadas. Veamos un ejemplo:
Font font = new Font("Arial", Font.BOLD, 18);
AttributedString attributedText = new AttributedString(text);
attributedText.addAttribute(TextAttribute.FONT, font);
attributedText.addAttribute(TextAttribute.FOREGROUND, Color.GREEN);
Graphics g = image.getGraphics();
g.drawString(attributedText.getIterator(), 0, 20);
Este método de imprimir el texto nos permite asociar el formato directamente con la cadena, lo cual es más limpio que cambiar las propiedades del objeto Graphics
cada vez que deseamos modificar el formato.
3. Text Alignment
Ahora que hemos aprendido a añadir un texto simple en la esquina superior izquierda de una imagen, veamos cómo podemos añadir este texto en ciertas posiciones.
3.1. Centered Text
El primer tipo de alineación que vamos a tratar es centrar el texto. Para establecer dinámicamente la posición correcta donde queremos escribir el texto, necesitamos averiguar cierta información:
- Tamaño de la imagen
- Tamaño de la fuente
Esta información se puede obtener muy fácilmente. En el caso del tamaño de la imagen, esta información se puede acceder a través de los métodos getWidth
y getHeight
del objeto BufferedImage
. Por otro lado, para obtener los datos relacionados con el tamaño de la fuente necesitamos usar el objeto FontMetrics
.
Veamos un ejemplo en el que calculamos la posición correcta para nuestro texto y lo dibujamos:
Graphics g = image.getGraphics();
FontMetrics metrics = g.getFontMetrics(font);
int positionX = (image.getWidth() - metrics.stringWidth(text)) / 2;
int positionY = (image.getHeight() - metrics.getHeight()) / 2 + metrics.getAscent();
g.drawString(text, positionX, positionY);
3.2. Text Aligned in the Bottom Right
El siguiente tipo de alineación que veremos es la esquina inferior derecha. En este caso, necesitamos obtener dinámicamente las posiciones correctas:
int positionX = (image.getWidth() - metrics.stringWidth(text));
int positionY = (image.getHeight() - metrics.getHeight()) + metrics.getAscent();
3.3. Text Located in the Top Left
Finalmente, veamos cómo imprimir nuestro texto en la esquina superior izquierda:
int positionX = 0;
int positionY = metrics.getAscent();
El resto de las alineaciones se pueden deducir a partir de las tres que hemos visto.
4. Adapting Text Size Based on Image
Cuando dibujamos el texto en la imagen, podemos encontrar que este texto excede el tamaño de la imagen. Para resolver esto, necesitamos adaptar el tamaño de la fuente que estamos utilizando en base al tamaño de la imagen.
Primero, necesitamos obtener el ancho y alto esperados del texto usando la fuente base. Para lograr esto, haremos uso de las clases FontMetrics, GlyphVector, y Shape.
FontMetrics ruler = graphics.getFontMetrics(baseFont);
GlyphVector vector = baseFont.createGlyphVector(ruler.getFontRenderContext(), text);
Shape outline = vector.getOutline(0, 0);
double expectedWidth = outline.getBounds().getWidth();
double expectedHeight = outline.getBounds().getHeight();
El siguiente paso es verificar si es necesario redimensionar la fuente. Para este propósito, comparemos el tamaño esperado del texto con el tamaño de la imagen:
boolean textFits = image.getWidth() >= expectedWidth && image.getHeight() >= expectedHeight;
Finalmente, si nuestro texto no cabe en la imagen, tenemos que reducir el tamaño de la fuente. Usaremos el método deriveFont
para eso:
double widthBasedFontSize = (baseFont.getSize2D() * image.getWidth()) / expectedWidth;
double heightBasedFontSize = (baseFont.getSize2D() * image.getHeight()) / expectedHeight;
double newFontSize = Math.min(widthBasedFontSize, heightBasedFontSize);
newFont = baseFont.deriveFont(baseFont.getStyle(), (float)newFontSize);
Notemos que necesitamos obtener el nuevo tamaño de fuente basado tanto en el ancho como en el alto y aplicar el más bajo de ellos.
5. Summary
En este artículo, hemos visto cómo escribir texto en una imagen usando diferentes métodos. También hemos aprendido cómo obtener dinámicamente la posición donde queremos imprimir nuestro texto basado en el tamaño de la imagen y las propiedades de la fuente. Por último, hemos visto cómo adaptar el tamaño de la fuente del texto en caso de que exceda el tamaño de la imagen donde estamos dibujándolo.
Conclusiones Prácticas:
- La interacción con la API de gráficos en Java puede ser simple y efectiva al usar
BufferedImage
yGraphics
. - Las bibliotecas externas como ImageJ son útiles cuando se requieren funcionalidades más avanzadas.
- Adaptar el texto para que se ajuste a la imagen es crucial en aplicaciones más complejas que manipulan imágenes.
La comprensión de estos conceptos es esencial para cualquier programador que busque expandir sus habilidades en la manipulación de imágenes en Java. ¡Feliz codificación!