Pregunta

Tengo dos BufferedImages que cargué desde pngs. El primero contiene una imagen, el segundo una máscara alfa para la imagen.

Quiero crear una imagen combinada de los dos, aplicando la máscara alfa. Mi google-fu me falla.

Sé cómo cargar / guardar las imágenes, solo necesito la broca a la que voy de dos Imágenes de Buffer a una Imagen de Buffer con el canal alfa correcto.

¿Fue útil?

Solución

Su solución podría mejorarse recuperando los datos RGB más de un píxel a la vez (consulte http://java.sun.com/javase/6/docs/api/java/awt/image/BufferedImage.html ), y al no crear tres colores Objetos en cada iteración del bucle interno.

final int width = image.getWidth();
int[] imgData = new int[width];
int[] maskData = new int[width];

for (int y = 0; y < image.getHeight(); y++) {
    // fetch a line of data from each image
    image.getRGB(0, y, width, 1, imgData, 0, 1);
    mask.getRGB(0, y, width, 1, maskData, 0, 1);
    // apply the mask
    for (int x = 0; x < width; x++) {
        int color = imgData[x] & 0x00FFFFFF; // mask away any alpha present
        int maskColor = (maskData[x] & 0x00FF0000) << 8; // shift red into alpha bits
        color |= maskColor;
        imgData[x] = color;
    }
    // replace the data
    image.setRGB(0, y, width, 1, imgData, 0, 1);
}

Otros consejos

Llegué demasiado tarde con esta respuesta, pero tal vez sea útil para alguien de todos modos. Esta es una versión más simple y más eficiente del método de Michael Myers:

public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
    int width = image.getWidth();
    int height = image.getHeight();

    int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
    int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);

    for (int i = 0; i < imagePixels.length; i++)
    {
        int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
        int alpha = maskPixels[i] << 24; // Shift blue to alpha
        imagePixels[i] = color | alpha;
    }

    image.setRGB(0, 0, width, height, imagePixels, 0, width);
}

Al principio, lee todos los píxeles en una matriz, por lo que solo requiere un bucle for. Además, cambia directamente el byte azul al alfa (del color de la máscara), en lugar de enmascarar primero el byte rojo y luego desplazarlo.

Al igual que los otros métodos, se supone que ambas imágenes tienen las mismas dimensiones.

Recientemente jugué un poco con estas cosas, para mostrar una imagen sobre otra, y para que la imagen se desvanezca en gris.
También enmascara una imagen con una máscara con transparencia (¡mi versión anterior de este mensaje!).

Tomé mi pequeño programa de prueba y lo modifiqué un poco para obtener el resultado deseado.

Aquí están los bits relevantes:

TestMask() throws IOException
{
    m_images = new BufferedImage[3];
    m_images[0] = ImageIO.read(new File("E:/Documents/images/map.png"));
    m_images[1] = ImageIO.read(new File("E:/Documents/images/mapMask3.png"));
    Image transpImg = TransformGrayToTransparency(m_images[1]);
    m_images[2] = ApplyTransparency(m_images[0], transpImg);
}

private Image TransformGrayToTransparency(BufferedImage image)
{
    ImageFilter filter = new RGBImageFilter()
    {
        public final int filterRGB(int x, int y, int rgb)
        {
            return (rgb << 8) & 0xFF000000;
        }
    };

    ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
    return Toolkit.getDefaultToolkit().createImage(ip);
}

private BufferedImage ApplyTransparency(BufferedImage image, Image mask)
{
    BufferedImage dest = new BufferedImage(
            image.getWidth(), image.getHeight(),
            BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2 = dest.createGraphics();
    g2.drawImage(image, 0, 0, null);
    AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.DST_IN, 1.0F);
    g2.setComposite(ac);
    g2.drawImage(mask, 0, 0, null);
    g2.dispose();
    return dest;
}

El resto solo muestra las imágenes en un pequeño panel Swing.
Tenga en cuenta que la imagen de la máscara tiene niveles de gris, que el negro se convierte en transparencia total y el blanco se vuelve completamente opaco.

Aunque has resuelto tu problema, pensé que podría compartir mi opinión sobre él. Utiliza un método un poco más de Java-ish, usando clases estándar para procesar / filtrar imágenes.
En realidad, mi método usa un poco más de memoria (para hacer una imagen adicional) y no estoy seguro de que sea más rápido (la medición de los rendimientos respectivos podría ser interesante), pero es un poco más abstracto.
¡Al menos, tienes elección! :-)

Para aquellos que están usando alfa en la imagen original.

Escribí este código en Koltin, el punto clave aquí es que si tienes el alfa en tu imagen original, necesitas multiplicar estos canales.

Versión Koltin:

    val width = this.width
    val imgData = IntArray(width)
    val maskData = IntArray(width)

    for(y in 0..(this.height - 1)) {

      this.getRGB(0, y, width, 1, imgData, 0, 1)
      mask.getRGB(0, y, width, 1, maskData, 0, 1)

      for (x in 0..(this.width - 1)) {

        val maskAlpha = (maskData[x] and 0x000000FF)/ 255f
        val imageAlpha = ((imgData[x] shr 24) and 0x000000FF) / 255f
        val rgb = imgData[x] and 0x00FFFFFF
        val alpha = ((maskAlpha * imageAlpha) * 255).toInt() shl 24
        imgData[x] = rgb or alpha
      }
      this.setRGB(0, y, width, 1, imgData, 0, 1)
    }

Versión de Java (recién traducida de Kotlin)

    int width = image.getWidth();
    int[] imgData = new int[width];
    int[] maskData = new int[width];

    for (int y = 0; y < image.getHeight(); y ++) {

        image.getRGB(0, y, width, 1, imgData, 0, 1);
        mask.getRGB(0, y, width, 1, maskData, 0, 1);

        for (int x = 0; x < image.getWidth(); x ++) {

            //Normalize (0 - 1)
            float maskAlpha = (maskData[x] & 0x000000FF)/ 255f;
            float imageAlpha = ((imgData[x] >> 24) & 0x000000FF) / 255f;

            //Image without alpha channel
            int rgb = imgData[x] & 0x00FFFFFF;

            //Multiplied alpha
            int alpha = ((int) ((maskAlpha * imageAlpha) * 255)) << 24;

            //Add alpha to image
            imgData[x] = rgb | alpha;
        }
        image.setRGB(0, y, width, 1, imgData, 0, 1);
    }

En realidad, lo he descubierto. Probablemente no sea una forma rápida de hacerlo, pero funciona:

for (int y = 0; y < image.getHeight(); y++) {
    for (int x = 0; x < image.getWidth(); x++) {
        Color c = new Color(image.getRGB(x, y));
        Color maskC = new Color(mask.getRGB(x, y));
        Color maskedColor = new Color(c.getRed(), c.getGreen(), c.getBlue(),
            maskC.getRed());
        resultImg.setRGB(x, y, maskedColor.getRGB());
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top