2017-01-31 24 views
0

This should have white background Как сохранить изображение в java как java.awt.image.IndexColorModel PNG? Я загружаю индексированный цвет png с помощью ImageIO, обрабатываю его с помощью библиотеки Catalino, которая, к сожалению, преобразует цветовое пространство в java.awt.image.DirectColorModel.Как сохранить индексированный цвет PNG в java

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

private static void testIndexedColor() throws IOException { 
     FastBitmap input = new FastBitmap("test.png"); 
     BufferedImage bi = new BufferedImage(input.getWidth(), input.getHeight(), BufferedImage.TYPE_BYTE_INDEXED); 
     bi.getGraphics().drawImage(input.toBufferedImage(), 0, 0, null); 
     ImageIO.write(bi, "PNG", new File("test_result.png")); 
} 

Но в результате на белом фоне появились незначительные светло-серые пиксельные артефакты, а ИЦП уменьшился. Как правильно преобразовать обратно в индексированный цветовой режим без потери качества и искажения?

+0

Что здесь происходит, так это то, что ваш '' '' '' BufferedImage' создан с по умолчанию 'IndexColorModel' (который бывает того же цвета, что и 256-цветная« веб-безопасная »палитра). Вам нужно получить записи IndexColorModel или палитры в той или иной форме, начиная с 'input'. Если это невозможно, в худшем случае вы можете снова прочитать входной файл, используя «ImageIO», чтобы получить его (медленнее) или даже создать «лучшую» палитру для измененного изображения, используя некоторую форму уменьшения цвета (медленнее) , Не уверен, что произошло с ИЦП (или, если это даже имеет значение). – haraldK

+0

PS: Является ли библиотека, на которую вы ссылаетесь, * Catal ** ** ** без рамки? – haraldK

+1

Также стоит упомянуть, конкретное изображение черно-белое. Когда я открываю его в режиме gimp, он говорит о индексированном цвете. Однако, если я загружу это изображение с помощью ImageIO в BufferedImage и сразу же напишу тот же файл BufferedImage в файл с помощью ImageIO, режим станет мистическим в оттенках серого. Я даже не уверен, что оттенки серого и индексированный цвет являются взаимоисключающими или могут использовать палитру изображений с оттенками серого? И почему это преобразование происходит? –

ответ

1

Предполагая, что я правильно о рамках Каталано, вы должны быть в состоянии переписать свои методы, как это:

private static void testIndexedColor() throws IOException { 
    BufferedImage bi = ImageIO.read(new File("test.png")); 
    FastBitmap input = new FastBitmap(bi); 

    Graphics2D g = bi.createGraphics(); 
    try { 
     g.drawImage(input.toBufferedImage(), 0, 0, null); 
    } 
    finally { 
     g.dispose(); // Good practice ;-) 
    } 

    ImageIO.write(bi, "PNG", new File("test_result.png")); 
} 

По крайней мере, вы должны уйти с фиксированной палитрой и артефактов.

Однако, вероятно, это будет еще изменить PPI (но это не повлияет на пиксели). И даже в некоторых случаях изображение может быть записано как PNG без палитры.


Update: Кажется PNGImageWriter (через PNGMetadata) на самом деле вновь пишет IndexColorModel, содержащий идеальный оттенки серого, в оттенках серого PNG по умолчанию. Обычно это хорошая идея, так как вы уменьшаете размер файла, не записывая блок PLTE. Вы должны быть в состоянии обойти эту проблему, путем передачи метаданных от оригинала, наряду с данными пикселей изображения, чтобы инструктировать писатель держать IndexColorModel (то есть писать PLTE кусок.):

private static void testIndexedColor() throws IOException { 
    File in = new File("test.png"); 
    File out new File("test_result.png"); 

    try (ImageInputStream input = ImageIO.createImageInputStream(in); 
     ImageOutputStream output = ImageIO.createImageOutputStream(out)) { 
     ImageReader reader = ImageIO.getImageReaders(input).next(); // Will fail if no reader 
     reader.setInput(input); 

     ImageWriter writer = ImageIO.getImageWriter(reader); // Will obtain a writer that understands the metadata from the reader 
     writer.setOutput(output); // Will fail if no writer 

     // Now, the important part, we'll read the pixel AND metadata all in one go 
     IIOImage image = reader.readAll(0, null); // PNGs only have a single image, so index 0 is safe 

     // You can now access and modify the image data using: 
     BufferedImage bi = (BufferedImage) image.getRenderedImage(); 
     FastBitmap fb = new FastBitmap(bi); 

     // ...do stuff... 

     Graphics2D g = bi.createGraphics(); 
     try { 
      g.drawImage(fb.toBufferedImage(), 0, 0, null); 
     } 
     finally { 
      g.dispose(); 
     } 

     // Write pixel and metadata back 
     writer.write(null, image, writer.getDefaultWriteParam()); 
    } 
} 

Этот должен (в качестве бонуса) также сохраняйте свой ИЦП как есть.

PS: Для производства кода, вы также хотите dispose() в reader и writer выше, но я оставил его, чтобы сосредоточиться и избежать дальнейшего обсуждения на try/finally. ;-)

+0

Я рад, что вы добавили 'dispose()', но try-finally не требуется. 'G.drawImage' почти невозможно выбросить исключение. – VGR

+0

@VGR Это необходимо в тех случаях, когда у вас заканчивается память. – haraldK

+0

Если вы намерены «восстановить» из OutOfMemoryErrors (на самом деле, вы не можете), вы можете также положить каждую строку кода в try-finally. Когда у вас заканчивается память, выполнение программы эффективно завершается, и единственным полезным действием является выход. – VGR