2015-09-15 14 views
1

Мне нужно прочитать и обработать большое количество PNG-файлов, которые находятся в оттенках серого. Под этим я подразумеваю, что если они открываются в Photoshop или GIMP, режим изображения - Grayscale - не RGB-изображение с оттенками серого.Чтение черно-белых изображений изображений PNG без искажений

ImageIO, похоже, не достиг этого. Кажется, что все файлы изображений рассматриваются как sRGB. Это изменяет значения оттенков серого. Мне нужно прочитать и обработать эти файлы PNG, где (в моем коде) каждый пиксель имеет точно такое же значение, как если бы я открыл файл в градациях серого в Photoshop или GIMP. Кто-нибудь знает о каком-то программном обеспечении с открытым исходным кодом, которое может это сделать, пожалуйста? Или лучше, как добиться этого, используя ImageIO.

Дополнительная информация:

Я использую getRGB() на BufferedImage. Базовый пиксель в файле изображения равен 0x86. Я понимаю, что это не обязательно соответствует пикселю ARGB, содержащему 0xFF868686, так как это зависит от яркости/гамма. Однако, в отсутствие геттера с аргументом типа гамма, я ожидал, что сопоставление по умолчанию будет соответствовать ARGB = 0xFF868686. Если я использую GIMP или Photoshop для преобразования изображения в оттенках серого, содержащего пиксель со значением 0x86 в RGB, тогда пиксель становится 0xFF868686. Это очевидный дефолт.

Однако ImageIO, похоже, использует странную гамму (независимо от того, нравится вам это или нет) с изображениями в градациях серого, что делает оттенки серого очень легкими после отображения в ARGB. В этом случае 0x86 сопоставляется с 0xFFC0C0C0. Это не только очень легко, но также может привести к значительной потере данных, так как многие значения оттенков серого могут быть сопоставлены с меньшим количеством значений ARGB. Единственный раз, когда это искажение не приведет к потере данных, для очень темных изображений в оттенках серого. Соответствующая гамма зависит от контекста, разные физические среды будут искажать яркость по-разному. Однако при отсутствии контекста отображение: 0x86 -> 0xFF868686 имеет наибольшее значение - свидетельствуйте о выборе, сделанных для GIMP и Photoshop.

Оставив проблему getRGB() с одной стороны, загрузив изображение в градациях серого (используя ImageIO.read (imageFile)), метод getType() BufferedImage возвращает Type = 0 (Custom), а не Type = 10 (TYPE_BYTE_GRAY), как я и ожидал.

Короче говоря, ImageIO, похоже, не обеспечивает приятный и простой способ высокого уровня чтения и управления существующими изображениями в оттенках серого. Я надеялся, что вам не придется возиться под обложками с Rasters, ICC, сэмплированием и т. Д. Также я не хочу, чтобы физически преобразовывать все файлы изображений в оттенках серого в RGB. Все, что я хотел, это метод load() API для BufferedImage, который работает так же, как открытый файл в GIMP или Photoshop. Я не смог этого добиться. Я надеюсь, что это мое невежество, а не ограничение Java ImageIO.

Возможное решение:

После копаться я следующее, чтобы предложить в качестве возможного метода для низкоуровневого доступа к градации серого значения:

final File imageFile = new File("test.png"); 
final BufferedImage image = ImageIO.read(imageFile); 
// --- Confirm that image has ColorSpace Type is GRAY and PixelSize==16 
final Raster raster = image.getData(); 
// --- Confirm that: raster.getTransferType() == DataBuffer.TYPE_BYTE 

for(int x=0, xLimit=image.getWidth(); x < xLimit; x++) { 
    for(int y=0, yLimit=image.getHeight(); y < yLimit; y++) { 

     final Object dataObject = raster.getDataElements(x, y, null); 
     // --- Confirm that dataObject is instance of byte[] 
     final byte[] pixelData = (byte[]) dataObject; 
     // --- Confirm that: pixelData.length == 2 

     final int grayscalePixelValue = pixelData[0] & 0xFF; 
     final int grayscalePixelAlpha = pixelData[1] & 0xFF; 

     // --- Do something with the grayscale pixel data 
    } 
} 

документация Javadoc не велика, поэтому я не могу гарантировать, что это правильно, но, похоже, это работает для меня.

+1

Как вы проверяете значения пикселей вашего изображения? Если вы вызываете 'image.getRGB (x, y)', то есть по определению значения ARGB в цветовом пространстве sRGB (независимо от того, как пиксели хранятся в памяти или на диске). Если вы получаете растровый и базовый буфер данных, вы должны увидеть исходные значения серого. Пожалуйста, напишите какой-нибудь код с тем, что вы пробовали до сих пор, и, возможно, мы сможем его решить. Также опубликуйте хотя бы одно образцовое изображение. :-) – haraldK

+0

Спасибо за ваш добрый ответ. Я добавил дополнительную информацию в оригинальную публикацию. –

+0

Если вы хотите попробовать третью сторону (мой) lib: https://github.com/leonbloy/pngj/ – leonbloy

ответ

0
+1

Если что-то сломано, это Java2D. Все вышесказанное также возникает, если вы создаете новую шкалу «BufferedImage», не нужно загружать ее с помощью «ImageIO». Однако, независимо от какой-либо ошибки с оттенками серого, 'getRGB()' все равно будет возвращать значения ARGB в цветовом пространстве sRGB, и эти значения будут отличаться от ожидаемого OP, потому что он ожидает значений в цветовом пространстве * линейной серой шкалы *. – haraldK

2

В случае, если вы хотите попробовать третью сторону (мой) Lib: https://github.com/leonbloy/pngj/

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

PngReaderByte pngr = new PngReaderByte(new File(filename)); // 
    if (pngr.imgInfo.channels!=1 || pngr.imgInfo.bitDepth != 8 || pngr.imgInfo.indexed) 
     throw new RuntimeException("This method is for gray images"); 
    for (int row = 0; row < pngr.imgInfo.rows; row++) { 
     ImageLineByte line = pngr.readRowByte(); 
     byte [] buf = line.getScanlineByte(); 
     // do what you want 
    } 
    pngr.end();