Преобразование прозрачного GIF / PNG в JPEG с использованием Java - PullRequest
19 голосов
/ 21 января 2009

Я хотел бы конвертировать GIF-изображения в JPEG с использованием Java. Это прекрасно работает для большинства изображений, но у меня есть простое прозрачное изображение GIF:

Входное изображение GIF http://img292.imageshack.us/img292/2103/indexedtestal7.gif

[Если изображение отсутствует: это синий круг с прозрачными пикселями вокруг него]

Когда я конвертирую это изображение, используя следующий код:

File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
File f = new File("indexed_test.jpg");
ImageIO.write(image, "jpg", f);

Этот код работает без выброса исключения, но приводит к неверному изображению в формате JPEG:

Output jpeg image

[Если изображение отсутствует: IE не может показать JPEG, Firefox показывает изображение с недопустимыми цветами.]

Я использую Java 1.5.

Я также попытался преобразовать пример gif в png с помощью gimp и использовать png в качестве входных данных для кода Java. Результат тот же.

Это ошибка в JDK? Как правильно конвертировать изображения предпочтительно без сторонних библиотек?

UPDATE:

Ответы указывают, что преобразование jpeg не может правильно обрабатывать прозрачность (я все еще думаю, что это ошибка) и предлагают обходной путь для замены прозрачных пикселей предопределенным цветом. Оба предложенных метода довольно сложны, поэтому я реализовал более простой (опубликую в качестве ответа). Я принимаю первый опубликованный ответ с этим обходным путем (Маркус). Я не знаю, какая реализация лучше. Я иду на самый простой, я нашел GIF, где он не работает.

Ответы [ 7 ]

42 голосов
/ 09 октября 2009

Для Java 6 (и 5 тоже, я думаю):

BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
g = bufferedImage.createGraphics();
//Color.WHITE estes the background to white. You can use any other color
g.drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), Color.WHITE, null);
8 голосов
/ 22 января 2009

Как уже упоминалось в ОБНОВЛЕНИИ вопроса, я реализовал более простой способ замены прозрачных пикселей предопределенным цветом:

public static BufferedImage fillTransparentPixels( BufferedImage image, 
                                                   Color fillColor ) {
    int w = image.getWidth();
    int h = image.getHeight();
    BufferedImage image2 = new BufferedImage(w, h, 
        BufferedImage.TYPE_INT_RGB);
    Graphics2D g = image2.createGraphics();
    g.setColor(fillColor);
    g.fillRect(0,0,w,h);
    g.drawRenderedImage(image, null);
    g.dispose();
    return image2;
}

и я вызываю этот метод перед преобразованием jpeg следующим образом:

if( inputImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
    inputImage = fillTransparentPixels(inputImage, Color.WHITE);
}
4 голосов
/ 13 мая 2009

3 месяца с опозданием, но у меня очень похожая проблема (хотя я даже не загружаю gif, но просто генерирую прозрачное изображение - скажем, без фона, цветной фигуры - где при сохранении в jpeg все цвета перепутаны , а не только фон)

Нашел этот фрагмент кода в этой довольно старой ветке списка java2d-интереса , думал, что поделюсь, потому что после быстрого теста он намного более производительный, чем Ваше решение:

        final WritableRaster raster = img.getRaster();
        final WritableRaster newRaster = raster.createWritableChild(0, 0, img.getWidth(), img.getHeight(), 0, 0, new int[]{0, 1, 2});

        // create a ColorModel that represents the one of the ARGB except the alpha channel
        final DirectColorModel cm = (DirectColorModel) img.getColorModel();
        final DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());

        // now create the new buffer that we'll use to write the image
        return new BufferedImage(newCM, newRaster, false, null);

К сожалению, я не могу сказать, что понимаю точно что он делает;)

4 голосов
/ 21 января 2009

Проблема (по крайней мере с преобразованием png в jpg) заключается в том, что цветовая схема не одинакова, потому что jpg не поддерживает прозрачность.

Что мы успешно сделали, так это что-то вроде этого (это извлечено из различных частей кода - так что, пожалуйста, простите за грубость форматирования):

File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
int width = image.getWidth();
int height = image.getHeight();
BufferedImage jpgImage;

//you can probably do this without the headless check if you just use the first block
if (GraphicsEnvironment.isHeadless()) {
  if (image.getType() == BufferedImage.TYPE_CUSTOM) {
      //coerce it to  TYPE_INT_ARGB and cross fingers -- PNGs give a    TYPE_CUSTOM and that doesn't work with
      //trying to create a new BufferedImage
     jpgImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
  } else {
     jpgImage = new BufferedImage(width, height, image.getType());
  }
} else {
     jgpImage =   GraphicsEnvironment.getLocalGraphicsEnvironment().
        getDefaultScreenDevice().getDefaultConfiguration().
        createCompatibleImage(width, height, image.getTransparency()); 
}

//copy the original to the new image
Graphics2D g2 = null;
try {
 g2 = jpg.createGraphics();

 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
 g2.drawImage(image, 0, 0, width, height, null);
}
finally {
   if (g2 != null) {
       g2.dispose();
   }
}

File f = new File("indexed_test.jpg");

ImageIO.write(jpgImage, "jpg", f);

Это работает для png в jpg и gif в jpg. И у вас будет белый фон, где были прозрачные биты. Вы можете изменить это, если g2 закрасит изображение другим цветом перед вызовом drawImage.

3 голосов
/ 05 января 2011

Если вы создадите изображение BufferedImage типа BufferedImage.TYPE_INT_ARGB и сохраните его в JPEG, это приведет к странным вещам. В моем случае цвета окрашены в оранжевый. В других случаях полученное изображение может быть недействительным, и другие читатели откажутся загружать его.

Но если вы создаете изображение типа BufferedImage.TYPE_INT_RGB, то сохранение его в формате JPEG работает нормально.

Я думаю, что это ошибка в программе записи изображений Java JPEG - она ​​должна писать только то, что может, без прозрачности (например, что делает .NET GDI +). Или в худшем случае выдается исключение со значимым сообщением, например, "не могу написать изображение с прозрачностью".

2 голосов
/ 21 января 2009

JPEG не поддерживает прозрачность. Поэтому, даже если вы правильно получите цвет круга, у вас останется черный или белый фон, в зависимости от вашего кодировщика и / или средства визуализации.

1 голос
/ 07 марта 2016
BufferedImage originalImage = ImageIO.read(getContent());
BufferedImage newImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);

    for (int x = 0; x < originalImage.getWidth(); x++) {
        for (int y = 0; y < originalImage.getHeight(); y++) {
            newImage.setRGB(x, y, originalImage.getRGB(x, y));
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...