BufferedImage сообщает о неверном типе цветовой модели
Я использую некоторые сторонние программы для преобразования изображений в PDF, и я заметил некоторые завышенные размеры файлов. Немного покопавшись, я убедился, что цветовая модель для снимков не сохранилась. Черно-белые (1 бит) изображения преобразовывались в цветовые модели RGB.
Копание в библиотеке показывает некоторое обнаружение цветовой модели:
switch (awtColorSpace.getType()) {
case ColorSpace.TYPE_RGB:
return PDDeviceRGB.INSTANCE;
case ColorSpace.TYPE_GRAY:
return PDDeviceGray.INSTANCE;
case ColorSpace.TYPE_CMYK:
return PDDeviceCMYK.INSTANCE;
default:
throw new UnsupportedOperationException("color space not implemented: "
+ awtColorSpace.getType());
}
Эти изображения всегда возвращались в виде RGB. Я решил написать несколько тестов, и они, похоже, подтверждают это:
package com.acme;
import org.junit.Test;
import javax.imageio.ImageIO;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.*;
public class ImageColorDetectionTest {
@Test
public void colorImage() throws Exception {
// Colorspace: sRGB, Depth: 8-bit, Channel depth: Red: 8-bit Green: 8-bit Blue: 8-bit
BufferedImage image = readImage("/color.png");
assertEquals(ColorSpace.TYPE_RGB, image.getColorModel().getColorSpace().getType());
}
@Test
public void greyscaleImage() throws Exception {
// Colorspace: Gray, Depth: 8-bit, Channel depth: Gray: 8-bit
BufferedImage image = readImage("/greyscale.png");
assertEquals(ColorSpace.TYPE_GRAY, image.getColorModel().getColorSpace().getType());
}
@Test
public void blackAndWhiteImage() throws Exception {
// Colorspace: Gray, Depth: 8/1-bit, Channel depth: Gray: 1-bit
BufferedImage image = readImage("/bw.png");
assertEquals(ColorSpace.TYPE_GRAY, image.getColorModel().getColorSpace().getType());
}
protected BufferedImage readImage(String path) throws IOException {
try (InputStream content = this.getClass().getResourceAsStream(path)) {
return ImageIO.read(content);
}
}
}
Тест blackAndWhiteImage всегда терпит неудачу. Тип цветового пространства-5 (RGB). Это ошибка в JDK или я упускаю что-то фундаментальное здесь?
Тестовые изображения:
Цветовое пространство: sRGB, глубина: 8 бит, глубина канала: Красный: 8 бит зеленый: 8 бит синий: 8 бит
Цветовое пространство: серый, глубина: 8 бит, глубина канала: серый: 8 бит
Цветовое пространство: серый, глубина: 8/1 бит, глубина канала: серый: 1 бит
Imagemagick Идентификация:
magick identify -verbose bw.png
Image: bw.png
Format: PNG (Portable Network Graphics)
Mime type: image/png
Class: PseudoClass
Geometry: 329x247+0+0
Units: Undefined
Type: Bilevel
Base type: Palette
Endianess: Undefined
Colorspace: Gray
Depth: 8/1-bit
Channel depth:
Gray: 1-bit
Channel statistics:
Pixels: 81263
Gray:
min: 0 (0)
max: 255 (1)
mean: 110.66 (0.433961)
standard deviation: 126.384 (0.495623)
kurtosis: -1.92901
skewness: 0.266484
entropy: 0.98738
Colors: 2
Histogram:
45998: ( 0, 0, 0) #000000 gray(0)
35265: (255,255,255) #FFFFFF gray(255)
Colormap entries: 2
Colormap:
0: ( 0, 0, 0,255) #000000FF graya(0,1)
1: (255,255,255,255) #FFFFFFFF graya(255,1)
Rendering intent: Undefined
Gamma: 0.45455
Chromaticity:
red primary: (0.64,0.33)
green primary: (0.3,0.6)
blue primary: (0.15,0.06)
white point: (0.3127,0.329)
Matte color: grey74
Background color: white
Border color: srgb(223,223,223)
Transparent color: none
Interlace: None
Intensity: Undefined
Compose: Over
Page geometry: 329x247+0+0
Dispose: Undefined
Iterations: 0
Compression: Zip
Orientation: Undefined
Properties:
date:create: 2017-06-22T09:33:09-05:00
date:modify: 2017-06-22T09:33:09-05:00
png:bKGD: chunk was found (see Background color, above)
png:cHRM: chunk was found (see Chromaticity, above)
png:gAMA: gamma=0.45455 (See Gamma, above)
png:IHDR.bit-depth-orig: 1
png:IHDR.bit_depth: 1
png:IHDR.color-type-orig: 0
png:IHDR.color_type: 0 (Grayscale)
png:IHDR.interlace_method: 0 (Not interlaced)
png:IHDR.width,height: 329, 247
png:text: 2 tEXt/zTXt/iTXt chunks were found
png:tIME: 2017-06-21T10:12:36Z
signature: 689d59f57ef9b4d58011f92e26f937d9d58cf1ca1ccbcaad6bad7bdd0552fcfa
Artifacts:
verbose: true
Tainted: False
Filesize: 3.69KB
Number pixels: 81.3K
User time: 0.000u
Elapsed time: 0:01.000
Version: ImageMagick 7.0.5-4 Q16 x86_64 2017-03-25 http://www.imagemagick.org
2 ответа:
Я вижу, что у вас уже есть приемлемый ответ, но я все равно попытаюсь объяснить это.. Это не ошибка. Но я согласен, иногда это противоречит интуиции.
Ваш тест использует цветовое пространство декодированного представления изображения в памяти и сравнивает его с ожидаемым цветовым пространством из закодированного файла. Когда файл декодируется (используяImageIO.read
в вашем примере), плагинImageReader
обычно преобразует изображение в представление в памяти, которое быстро рисуется на экране. Этот может сильно отличаться от представления, которое наиболее эффективно при хранении на диске. Например, изображение в оттенках серого, использующее менее 8 бит на образец, обычно преобразуется вIndexColorModel
, Даже если файл PNG не содержит фрагментаPLTE
. И,IndexColorModel
всегда использует цветовое пространство sRGB (тип RGB), даже если оно содержит только серые значения. Это не имеет значения для отображаемых пикселей, которые будут черно-белыми независимо, но это имеет значение для вашего теста.Это так можно получить цветовое пространство, которое было фактически закодировано в файле, используя API
ImageIO
:try (ImageInputStream content = ImageIO.createImageInputStream(this.getClass().getResourceAsStream(path))) { ImageReader reader = ImageIO.getImageReaders(input).next(); // Assumes PNGImageReader is always there reader.setInput(input); IIOMetadata metadata = reader.getImageMetadata(0); Node nativeTree = metadata.getAsTree(metadata.getNativeMetadataFormatName()); Node standardTree = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName); // ... Get color space information as needed using DOM traversal }
Я пропустил чтение фактических значений, поскольку оно становится довольно многословным,но это довольно прямолинейно. Все значения равны
String
s. см.IIOMetadata
документация класса для деталей.Метаданные для файла
bw.png
содержат следующее в двух различных выходных представлениях.Собственные метаданные:
<javax_imageio_png_1.0> <IHDR width="329" height="247" bitDepth="1" colorType="Grayscale" compressionMethod="deflate" filterMethod="adaptive" interlaceMethod="none"/> <bKGD> <bKGD_Grayscale gray="1"/> </bKGD> <cHRM whitePointX="31270" whitePointY="32900" redX="64000" redY="33000" greenX="30000" greenY="60000" blueX="15000" blueY="6000"/> <gAMA value="45455"/> <tIME year="2017" month="6" day="21" hour="10" minute="12" second="36"/> </javax_imageio_png_1.0>
Стандартные" плагин-нейтральные " метаданные (пропуск нерелевантные значения):
<javax_imageio_1.0> <Chroma> <ColorSpaceType name="GRAY"/> <NumChannels value="1"/> <Gamma value="0.45455"/> <BlackIsZero value="TRUE"/> <BackgroundColor red="1" green="1" blue="1"/> </Chroma> <Compression ... /> <Data> <PlanarConfiguration value="PixelInterleaved"/> <SampleFormat value="UnsignedIntegral"/> <BitsPerSample value="1"/> </Data> <Dimension ... /> <Document ... /> <Transparency> <Alpha value="none"/> </Transparency> </javax_imageio_1.0>
Если ваши реальные изображения представляют собой TIFF или несколько других форматов, вероятно, лучше всего использовать стандартный формат метаданных, получив имя
ColorSpaceType
из узлаChroma
.
Я думаю, что это исходит из вашего файла
bw.png
. Как я понимаю, 1-битные PNG - это либо оттенки серого, либо индексированные (палитра), и индексированные будут использовать пространство RGB, поэтому у вас будет 2 цвета (#000000
и#ffffff
). Проверьте, какой инструмент вы используете для создания PNG, и посмотрите, дает ли он вам выбор между оттенками серого и индексированными. Вы также можете посмотреть на фрагменты PNG, чтобы убедиться, что файл создан так, как вы ожидаете.Это может быть полезно: TweakPNG