Подтвердить что ты не робот

Преобразование прозрачного gif/png в jpeg с помощью java

Я хотел бы преобразовать 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-изображению:

Выходное изображение jpeg http://img297.imageshack.us/img297/3493/indexedtest1qe5.jpg

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

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

Я также попытался преобразовать образец gif в png с gimp и использовать png в качестве ввода для Java-кода. Результат тот же.

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

UPDATE:

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

4b9b3361

Ответ 1

Для 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);

Ответ 2

Как уже упоминалось в UPDATE вопроса, я реализовал более простой способ замены прозрачных пикселей с предопределенным цветом:

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);
}

Ответ 3

Проблема (по крайней мере, с преобразованием 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.

Ответ 4

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);

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

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));
        }
    }