Зачистка альфа-канала из изображений

Я хочу вырезать альфа-канал (прозрачный фон) из PNG, а затем записать их в виде изображений JPEG. Вернее, я бы хотел сделать прозрачные пиксели белыми. Я попробовал два метода, оба из которых терпят неудачу по-разному:

Подход 1:

BufferedImage rgbCopy = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = rgbCopy.createGraphics();
graphics.drawImage(inputImage, 0, 0, Color.WHITE, null);
graphics.dispose();
return rgbCopy;

Результат: изображение имеет розовый фон.

Подход 2:

final WritableRaster raster = inputImage.getRaster();
final WritableRaster newRaster = raster.createWritableChild(0, 0, inputImage.getWidth(), inputImage.getHeight(), 0, 0, new int[]{0, 1, 2});
ColorModel newCM = new ComponentColorModel(inputImage.getColorModel().getColorSpace(), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
return new BufferedImage(newCM, newRaster, false, null);

Результат: изображение имеет черный фон.

В обоих случаях входное изображение представляет собой PNG, а выходное изображение записывается в формате JPEG следующим образом: ImageIO.write(bufferedImage, "jpg", buffer), В случае, если это уместно: это на Java 8, и я использую библиотеку twelvemonkeys, чтобы изменить размер изображения перед тем, как записать его в формате JPEG.

Я экспериментировал с несколькими вариантами приведенного выше кода, но не повезло. Существует ряд предыдущих вопросов, в которых предлагается приведенный выше код, но в данном случае он не работает.

2 ответа

Решение

Благодаря ответу Роба мы теперь знаем, почему цвета перепутаны.

Проблема двоякая:

  • По умолчанию JPEGImageWriter тот ImageIO использует для записи JPEG, не записывает JPEG с альфой так, как понимает другое программное обеспечение (это известная проблема).
  • При прохождении null как пункт назначения ResampleOp.filter(src, dest) и метод фильтра FILTER_TRIANGLEновый BufferedImage будет создан, с альфа (на самом деле, BufferedImage.TYPE_INT_ARGB).

Снятие альфа после повторной выборки будет работать. Тем не менее, есть другой подход, который может быть быстрее и сэкономить память. То есть вместо прохождения null пункт назначения, пройдите BufferedImage соответствующего размера и типа:

public static void main(String[] args) throws IOException {
    // Read input
    File input = new File(args[0]);
    BufferedImage inputImage = ImageIO.read(input);

    // Make any transparent parts white
    if (inputImage.getTransparency() == Transparency.TRANSLUCENT) {
        // NOTE: For BITMASK images, the color model is likely IndexColorModel,
        // and this model will contain the "real" color of the transparent parts
        // which is likely a better fit than unconditionally setting it to white.

        // Fill background  with white
        Graphics2D graphics = inputImage.createGraphics();
        try {
            graphics.setComposite(AlphaComposite.DstOver); // Set composite rules to paint "behind"
            graphics.setPaint(Color.WHITE);
            graphics.fillRect(0, 0, inputImage.getWidth(), inputImage.getHeight());
        }
        finally {
            graphics.dispose();
        }
    }

    // Resample to fixed size
    int width = 100;
    int height = 100;

    BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_TRIANGLE);

    // Using explicit destination, resizedImg will be of TYPE_INT_RGB
    BufferedImage resizedImg = resampler.filter(inputImage, new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB));

    // Write output as JPEG
    ImageIO.write(resizedImg, "JPEG", new File(input.getParent(), input.getName().replace('.', '_') + ".jpg"));
}

Отвечаю на это сам, потому что это, вероятно, поможет всем, кто сталкивается с этой ошибкой. haraldk был прав, был еще один фрагмент кода, который вызывал проблему с альфа-каналом. Код в подходе 1 правильный и работает.

Тем не мение.

Если после удаления альфа-канала вы измените размер изображения, используя twelvemonkeys ResampleOp, следующим образом:

BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_TRIANGLE);
BufferedImage resizedImg = resampler.filter(rgbImage, null);

Это заставляет альфа-канал быть розовым.

Решение состоит в том, чтобы изменить размер изображения перед удалением альфа-канала.

Другие вопросы по тегам