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

ImageIO читает несколько разные значения RGB, чем другие методы

Я обнаружил, что у меня разные RGB при использовании Java (& фактически paint.NET), чем я использую ImageMagick, Gimp, Python и Октав. Последние 4 все согласны с eachother, и поэтому я предполагаю, что они верны.

Для этих примеров я использую это тестовое изображение: http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg

Тестирование пикселя x=4144 y=2768

               R    G    B
Java        = (125, 107, 69)
Paint.NET   = (125, 107, 69)
ImageMagick = (128, 106, 67)
Python      = (128, 106, 67)
Octave      = (128, 106, 67)
Gimp        = (128, 106, 67)

Что дает?

Вот быстрый тест с использованием imagemagick:

convert image.jpg -crop 1x1+4144+2768 -depth 8 txt:

выход:

# ImageMagick pixel enumeration: 1,1,65535,srgb
0,0: (32896,27242,17219)  #806A43  srgb(128,106,67)

Здесь приведен код java и python, который также демонстрирует проблему:

import org.apache.commons.io.FileUtils;
import org.junit.Test;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;

public class ImageIOTest {
    @Test
    public void can_read_file() throws IOException, InterruptedException, URISyntaxException {
        File tempFile = File.createTempFile("image", "jpg");
        FileUtils.copyURLToFile(new URL("http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg"), tempFile);

        BufferedImage image = ImageIO.read(tempFile);

        int javaRGB = image.getRGB(4144, 2768);
        int javaRed = (javaRGB >> 16) & 0xFF;
        int javaGreen = (javaRGB >> 8) & 0xFF;
        int javaBlue = (javaRGB >> 0) & 0xFF;
        System.out.printf("rgb: (%d, %d, %d)", javaRed, javaGreen, javaBlue);
    }
}

И вот соответствующий питон script:

from PIL import Image
import sys, urllib, cStringIO

file = cStringIO.StringIO(urllib.urlopen("http://farm3.static.flickr.com/2811/9177301733_9836174725_o.jpg").read())

im = Image.open(file)
pix = im.load()
print pix[4144, 2768]

Я пробовал использовать эту библиотеку 12monkeys в надежде, что это исправит ее, но не кубики. Любые другие идеи, как я могу извлечь правильные значения RGB с помощью java? Конечно, я не первый человек, у которого есть эта проблема!

Update

Я пробовал getRaster().getSample(), но получил тот же неверный результат: System.out.println(raster.getSample(4144, 2768, 0)+","+ raster.getSample(4144, 2768, 1)+","+ raster.getSample(4144, 2768, 2)); output: 125,107,69

Подробнее

Вот какой вывод, который показывает, какие значения RGB декодируются тремя различными инструментами для первых 9 (3x3 квадратных) пикселей в верхнем левом углу изображения. Как вы можете видеть, Python и ImageMagick находятся в унисон. Java иногда совпадает. Я поставил X, где java не согласен...:

Tool          [x, y] = (R , G , B )
ImageIO     : [0, 0] = (86, 90, 93)
Python      : [0, 0] = (86, 90, 93)
ImageMagick : [0, 0] = (86, 90, 93)

ImageIO     : [1, 0] = (86, 90, 93)
Python      : [1, 0] = (86, 90, 93)
ImageMagick : [1, 0] = (86, 90, 93)

ImageIO     : [2, 0] = (90, 91, 95) X
Python      : [2, 0] = (88, 92, 95)
ImageMagick : [2, 0] = (88, 92, 95)

ImageIO     : [0, 1] = (85, 93, 95)
Python      : [0, 1] = (85, 93, 95)
ImageMagick : [0, 1] = (85, 93, 95)

ImageIO     : [1, 1] = (85, 93, 95) X
Python      : [1, 1] = (87, 92, 95)
ImageMagick : [1, 1] = (87, 92, 95)

ImageIO     : [2, 1] = (87, 92, 95)
Python      : [2, 1] = (87, 92, 95)
ImageMagick : [2, 1] = (87, 92, 95)

ImageIO     : [0, 2] = (83, 93, 94)
Python      : [0, 2] = (83, 93, 94)
ImageMagick : [0, 2] = (83, 93, 94)

ImageIO     : [1, 2] = (83, 93, 94) X
Python      : [1, 2] = (84, 92, 94)
ImageMagick : [1, 2] = (84, 92, 94)

ImageIO     : [2, 2] = (83, 91, 93)
Python      : [2, 2] = (83, 91, 93)
ImageMagick : [2, 2] = (83, 91, 93)

Почему Java дает разные значения для некоторых пикселей? Кроме того, существует ли другой (быстрый) способ генерации правильных значений с использованием собственного кода Java?

ОБНОВЛЕНИЕ 2016-09-26:

Я передал свой код, демонстрирующий эту проблему, и переместил его в github (imageio-test), чтобы я мог легко протестировать его на разных машинах. Оказывается, Java совместима как с OSX, так и с Ubuntu Linux, но это были Python, ImageMagick и Octave, которые были непоследовательными. Другими словами, в ящике Linux все инструменты согласуются друг с другом, и поэтому я теперь думаю, что Java был прав все время, и это другие инструменты, которые дают неверные результаты на OSX! Я до сих пор не понимаю, почему, и у меня нет никакого конкретного доказательства того, какие ценности правильные, но я куда-то...

4b9b3361

Ответ 1

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

JPEG потерял

Прежде всего, JPEG - это метод сжатия изображений с потерями. Это означает, что воспроизведение точных данных оригинала невозможно. Или, если хотите, несколько разных значений пикселей могут быть "правильными" в некотором роде.

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

Еще один отличный ответ по аналогичному вопросу гласит: "Стандарт JPEG не требует, чтобы реализации декодера выдавали идентичные выходные образы бит-бит" и цитирует запись в Wikipedia JPEG:

[...] требования к точности для декодирования [...]; выход из эталонного алгоритма не должен превышать:

  • максимум один бит разницы для каждого компонента пикселя
  • низкая среднеквадратичная ошибка по каждому блоку размером 8 × 8 пикселей.
  • очень низкая средняя ошибка по каждому блоку размером 8 × 8 пикселей.
  • очень низкая среднеквадратическая ошибка по всему изображению
  • чрезвычайно низкая средняя ошибка по всему изображению

(Обратите внимание, что выше сказанное относится только к эталонной реализации).

Однако, с некоторой удачей, кажется, что все ваше программное обеспечение/инструменты фактически используют (некоторые версии) libjpeg. Поскольку все они используют libjpeg, источник различий, который вы видите, скорее всего не связан с декодированием JPEG.

Цветовые пространства

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

Кажется, что все программное обеспечение, которое вы используете, фактически отображает значения RGB в цветовом пространстве sRGB. Это, вероятно, самое стандартное и широко используемое цветовое пространство, используемое в массовых вычислениях, так что это не удивительно. Поскольку цветовое пространство всегда является sRGB, источником различий, которые вы видите, скорее всего, не является цветовым пространством.

Профили ICC и согласование цветов

Следующим возможным источником различий цвета является то, что согласование цветов (как это делает модуль соответствия цветов, CMM или система управления цветом, CMS) не является 100% точной наукой (см., например, этот документ о компенсации черной точки или прочитайте некоторые более технические сообщения из Маленький блог CMS).

Скорее всего, программное обеспечение, работающее на Mac OS X, использует Apple CMM, в то время как Java всегда использует Little CMS (от OpenJDK 7 или Oracle JDK/JRE 8), и большинство программ на платформе Linux, скорее всего, также будут использовать открытый источник Маленькая CMS (согласно домашней странице Little CMS "Вы можете найти Little CMS в большинстве дистрибутивов Linux" ). Программное обеспечение в Windows, скорее всего, немного отклонится (я не смог проверить, использует ли Paint.Net Little CMS, Windows, встроенный в CMM или что-то еще). И, конечно, использование Adobe CMM (т.е. Photoshop), скорее всего, будет также отклоняться.

Снова, с некоторой удачей, много тестируемого вами программного обеспечения использует тот же механизм CMM или CMS, Little CMS, так что снова вы будет иметь равные результаты. Но похоже, что некоторые из протестированных вами программ используют разные CMM и являются вероятным источником небольших цветовых различий.

В заключение

Различные значения пикселей, которые вы видите, являются "правильными". Различия связаны с различными реализациями или аппроксимациями алгоритмов в программном обеспечении, но это не обязательно означает, что одно значение является правильным, а другие неверны.

PS: Если вам нужно воспроизвести одни и те же значения на нескольких платформах, используйте один и тот же набор инструментов/одинаковые алгоритмы на всех платформах.

Ответ 2

В соответствии с моим комментарием основное различие между различными приложениями/библиотеками, которые вы использовали для получения значения цвета пикселя, состоит в том, что все они используют разные версии libjpeg - по крайней мере, на Mac OSX.

Когда вы проверяете свой проект Github на определенные версии Ubuntu, вы увидите, что все значения сообщаются одинаково по всем разделам. В этих случаях python ImageMagick и Java JDK/JRE используют одну и ту же реализацию libjpeg.

На Mac, если вы установили jpeg через homebrew или Pillow через pip, тогда вы заметите, что они используют libjpeg v9 (libjpeg.9.dylib), тогда как Java 7 и 8 JDKs приходят со своим собственным libjpeg, который совсем другой.

Octave перечисляет свои зависимости jpeg как libjpeg8-dev.

GIMP, Inkscape, Scribus и т.д. также поставляются вместе со своими собственными. В моем случае GIMP поставляется в комплекте с той же версией, что и python и ImageMagick, что объясняет аналогичные значения (например: /Applications/GIMP.app/Contents/Resources/lib/libjpeg.9.dylib)

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

  • Придерживайтесь той же платформы/стека (как предложено @haraldk). Придерживайтесь разработки/запуска вашего материала на платформах Linux, которые гарантируют, что все они используют одну и ту же версию libjpeg.
  • Привяжите Java-код к той же версии, что и другие приложения, используя - то есть: load libjpeg.9.dylib и используйте это в своем приложении Java. Я не уверен на 100%, как вы это сделаете.
  • Перекомпилируйте JDK, чтобы использовать правильный - вариант, на который ссылается этот ответ, должен использовать openjdk и скомпилировать его с желаемой версией libjpeg, что звучит более достижимо.

Я признаю, что варианты 2 и 3 действительно более сложные версии варианта 1!

Примечание:
Я определенно отвечаю на голосование @haraldk, потому что его вывод почти такой же.

Я также играл с использованием разных профилей icc, и они дают совершенно разные ответы. Поэтому стоит опасаться этого.

Я просто хотел добавить ответ, который добавил больше внимания на реализацию libjpeg, потому что я считаю, что это то, что вас увлекает в вашем конкретном случае.

Update

Собственно, есть еще одно существенное отличие, отмеченное в ответе @haraldk, являющееся различием между CMM и Little CMS. По его словам: Java использует Little CMS, который также используется Ubuntu

На самом деле я думаю, что более вероятно, что это будет ответ.

Ответ 3

Консистенция цвета и профили ICC

Java не учитывает цветовой профиль при загрузке изображения. Также разные ОС отличаются друг от друга цветом RGB.

Вот что пишет Oracle о import java.awt.color:

Как правило, Color или ColorModel будут связаны с профилем ICC, который является либо входным, отображаемым, либо выходным профилем. Существуют другие типы профилей ICC, например. абстрактные профили, профили ссылок на устройства и названные цветовые профили, которые не содержат информации, подходящей для представления цветового пространства цвета, изображения или устройства. Ошибка при попытке создать объект ICC_ColorSpace из несоответствующего профиля ICC.

Профили ICC представляют собой преобразования из цветового пространства профиля (например, монитора) в пространство соединения профиля (PCS). Профили, представляющие интерес для маркировки изображений или цветов, имеют PCS, который является одним из независимых от устройства пространств (одно пространство CIEXYZ и два пространства CIELab), определенные в спецификации формата профиля ICC. Большинство интересующих профилей либо имеют обратимые преобразования, либо явно указывают преобразования, идущие в обоих направлениях. Если объект ICC_ColorSpace должен использоваться таким образом, чтобы преобразование из PCS в собственное пространство профиля и неадекватные данные для правильной реализации преобразования, объект ICC_ColorSpace будет выдавать выходные данные в указанном типе цветового пространства (например, TYPE_RGB, TYPE_CMYK и т.д..), но конкретные значения цвета выходных данных будут undefined.

Детали класса ICC_ColorSpace не важны для простых апплетов, которые рисуют в цветовом пространстве по умолчанию или манипулируют и отображают импортированные изображения с известным цветовым пространством. В большинстве случаев таким апплетам нужно будет получить одно из цветовых пространств по умолчанию через ColorSpace.getInstance(). (выдержка из docs.oracle.com) https://docs.oracle.com/javase/7/docs/api/java/awt/color/ICC_ColorSpace.html

Преобразование цветов в Java

Преобразования цветов управляются типом адресата для чтения и записи изображений. Когда Rasters читаются, преобразование цветового пространства не выполняется, и любой тип назначения игнорируется. Предупреждение отправляется любому слушателю, если в этом случае указан тип адресата. Когда Rasters записываются, любой тип адресата используется для интерпретации полос. Это может привести к записи JFIF или Adobe заголовка или других идентификаторов компонентов, которые записываются в фрейм и заголовки сканирования. Если значения, присутствующие в объекте метаданных, не соответствуют типу назначения, используется тип назначения, и каждому слушателю отправляется предупреждение. (выдержка из docs.oracle.com) https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html

Полезные ссылки

Посмотрите на информацию, касающуюся преобразования RGB. Есть некоторые проблемы с нормализованными цветовыми компонентами float/int от Rolf W. Rasmussen:

http://opensource.apple.com/source/gcc3/gcc3-1041/libjava/java/awt/image/ColorModel.java

Прочитайте The Sad Story of PNG Gamma "Correction" (Проблема в том, что JPEG и TIFF страдают от одной и той же "болезни" ).

https://hsivonen.fi/png-gamma/

Посмотрите на С.О. после. Возможное решение для вас:

В Java конвертация изображения в sRGB делает изображение слишком ярким

Если у вас все еще есть непоследовательные цвета после всех попыток, попробуйте преобразовать изображения в профиль sRGB, но не вставляйте их:

https://imageoptim.com/color-profiles.html

Кроме того, я надеюсь, что книга Кенни Ханта может быть полезной.

введите описание изображения здесь

... и следующий код (опубликованный на www.physicsforums.com) позволяет вам увидеть, как выглядят различные RGB:

import java.awt.*; 
import javax.swing.*; 

public class RGB { 
    public static void main(String[] args) { 
        JFrame frame = new JFrame("RGB"); 
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        RGBpanel panel = new RGBpanel(); 
        frame.getContentPane().add(panel); 
        frame.pack(); 
        frame.setVisible(true); 
    } 
} 

class RGBpanel extends JPanel { 
    public RGBpanel() { 
        setPreferredSize(new Dimension(300,300)); 
        int red = Integer.parseInt(JOptionPane.showInputDialog("Enter red value")); 
        int green = Integer.parseInt(JOptionPane.showInputDialog("Enter green value")); 
        int blue = Integer.parseInt(JOptionPane.showInputDialog("Enter blue value")); 
        Color colour = new Color(red,green,blue); 
        setBackground(colour); 
    } 
}

Рекомендация

Проблема несогласованности цвета связана с цветовыми профилями. Попробуйте присвоить единый цветовой профиль всем изображениям вручную (в Photoshop) или программно (на Java).

Ответ 4

Смотрите здесь: WARNING: Color space tagged as sRGB, without an embedded color profile. Windows and Mac browsers and apps treat the colors randomly.

Изменить: что касается ошибок округления и дисперсии реализации по версии; они просто не соответствуют этому изображению. На Mac есть какая-то магия, которая делает синие и зеленые ярче на кривой соответствия цвета. Исправьте цветовое пространство, соответствие цвета даст тот же результат. Я проголосовал за Энди Федорова, но я также заметил, что никто не дал вам решение... Вы пришли к выводу, что Java верна. Пойдите с этим. Libjpeg не изменился за долгое время. Он стабилен и надежно воспроизводит цвета во многих платформах и средах. Значительные (в любом случае) изменения не были сделаны для декодирования стареющего стандартного jpeg.

Изменить 2: попытаться создать пример, который дает те же значения, что и профиль Mac, на основе вашего проекта. Вам нужен ваш Mac factory профиль ICC от Library/ColorSync/Profiles.

Здесь, где я нахожусь. Здесь приведен пример с профилем sRGB ICC v4. Это технически применение sRGB по sRGB, но оно объясняет концепцию.

private ICC_Profile cp = ICC_Profile.getInstance("src/test/resources/sRGB_ICC_v4_Appearance.icc");
private ICC_ColorSpace cs = new ICC_ColorSpace(cp);

private int[] getRGBUsingImageIO2(File file, int x, int y) throws IOException {
    BufferedImage image = ImageIO.read(file);
    ColorConvertOp cco = new ColorConvertOp( cs, null );
    BufferedImage result = cco.filter( image, null );
    int javaRGB = result.getRGB(x, y);
    int javaRed = (javaRGB >> 16) & 0xFF;
    int javaGreen = (javaRGB >> 8) & 0xFF;
    int javaBlue = (javaRGB >> 0) & 0xFF;

    return new int[]{javaRed, javaGreen, javaBlue};
}
Image IO 1  : [0, 0] = [145, 146, 164] 
Image IO 2  : [0, 0] = [145, 147, 165] 
Image IO 1  : [1, 0] = [137, 138, 156] 
Image IO 2  : [1, 0] = [137, 139, 157] 
Image IO 1  : [2, 0] = [148, 147, 161] 
Image IO 2  : [2, 0] = [148, 148, 162] 
Image IO 1  : [0, 1] = [150, 153, 168] 
Image IO 2  : [0, 1] = [150, 154, 169] 
Image IO 1  : [1, 1] = [138, 141, 156] 
Image IO 2  : [1, 1] = [138, 142, 157] 
Image IO 1  : [2, 1] = [145, 147, 159] 
Image IO 2  : [2, 1] = [145, 148, 160] 
Image IO 1  : [0, 2] = [154, 160, 172] 
Image IO 2  : [0, 2] = [154, 161, 173] 
Image IO 1  : [1, 2] = [146, 152, 164] 
Image IO 2  : [1, 2] = [146, 153, 165] 
Image IO 1  : [2, 2] = [144, 148, 157] 
Image IO 2  : [2, 2] = [144, 149, 158] 

Не могли бы вы передать свой цветовой профиль в свой репозиторий imageio-test?