Как я могу установить пиксели в Java BufferedImages, используя цветовые пространства, отличные от ARGB?

Я пишу приложение, которое должно работать с 16-битными цветами 5-5-5 RGB (то есть 5 бит для каждого цвета и один бит заполнения). Для обработки этих изображений я использую BufferedImage, предоставленный AWT. Класс BufferedImage специально позволяет использовать цветовые пространства, отличные от RGB, принимая либо объект ColorModel, либо предопределенную константу типа изображения, одним из которых является формат 5-5-5 пикселей, который мне нужен.

Моя проблема заключается в следующем: в описании метода BufferedImage setRGB() указано, что предоставленные значения цвета предполагаются в цветовой модели RGB по умолчанию, TYPE_INT_ARGB и цветовом пространстве sRGB по умолчанию (согласно странице документации BufferedImage). Никакой другой метод, по-видимому, также не принимает значения, предназначенные для разных цветовых пространств.

Есть ли способ использовать мое нестандартное цветовое пространство непосредственно с BufferedImage, или мне придется полагаться на внутренние механизмы преобразования цвета класса для обработки всех моих цветов? (Или я просто неправильно понимаю/ непонимание чего-то о том, как работает класс?)


person Cameron Scoggins    schedule 06.05.2021    source источник
comment
Вам может понадобиться использовать Raster, например BufferedImage#setData — существует ряд других вспомогательных методов для растра, на которые вы также можете обратить внимание.   -  person MadProgrammer    schedule 06.05.2021
comment
Вызовите getRaster() , затем используйте его методы для работы с пикселями.   -  person Andreas    schedule 06.05.2021


Ответы (1)


BufferedImage.TYPE_USHORT_555_RGB по-прежнему использует полностью стандартное цветовое пространство RGB (на самом деле, оно использует sRGB), поэтому я не думаю, что вам нужно другое цветовое пространство.

Если вы хотите выполнить рисование или другие операции в Java, просто используйте обычные методы, такие как setRGB/getRGB() и createGraphics()/Grapics2D. Все будет правильно преобразовано в и из упакованного формата USHORT_555_RGB для вас.

Например:

BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);

// Do some custom painting
Graphics2D g = image.createGraphics();
g.drawImage(otherImage, 0, 0, null); // image type here does not matter
g.setColor(Color.ORANGE);            // Color in sRGB, but does not matter
g.fillOval(0, 0, w, h);
g.dispose();

image.setRGB(0, h/2, w, 1, new int[w]); // Silly way to create a horizontal black line at the center of the image... Don't do this, use fillRect(0, h/2, 1, w)! ;-)  

// image will still be USHORT_555_RGB *internally*

Однако, если у вас есть пиксельные данные в формате USHORT_555_RGB (т.е. из внешней библиотеки/API/сервиса), может быть быстрее и точнее будет установить эти значения непосредственно в растр/буфер данных. Или если вам нужно передать значения пикселей обратно в ту же библиотеку/API/сервис.

Например, используя Raster:

BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);

// Some fictional API. It's assumed that data.length == w * h
short[] apiPixels = api.getPixelsUSHORT_555_RGB(w, h);

WritableRaster raster = image.getRaster();

// Set short values to image
raster.setDataElements(0, 0, w, h, apiPixels);
        
// Get short values from image
short[] pixels = (short[]) raster.getDataElements(0, 0, w, h, null); // TYPE_USHORT_555_RGB -> always short[]
api.setPixels(pixels, w, h); // Another fictional API

Или, как вариант, используйте DataBuffer:

BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_USHORT_555_RGB);

// Some fictional API. It's assumed that data.length == w * h
short[] apiPixels = api.getPixelsUSHORT_555_RGB(w, h);

DataBufferUShort buffer = (DataBufferUShort) image.getRaster().getDataBuffer(); // TYPE_USHORT_555_RGB -> always DataBufferUShort

// Set short values to image
System.arraycopy(apiPixels, 0, buffer.getData(), 0, apiPixels.length);

// Get short values from image
api.setPixels(buffer.getData(), w, h);

В большинстве случаев не имеет значения, какой метод вы используете, но первый подход (с использованием только Raster) может сохранить управление изображением, что ускорит отображение изображений на экране из вашего Java-процесса.


PS: Если вам действительно нужно другое цветовое пространство (т. е. массив пикселей из внешней библиотеки/API/сервиса использует другое цветовое пространство, и вам нужно просмотреть пиксели в этом цветовом пространстве ), вы можете создать BufferedImage в стиле USHORT_555_RGB с пользовательским цветовым пространством следующим образом:

// Either use one of the built-in color spaces, or load one from disk
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
ColorSpace colorSpaceToo = new ICC_ColorSpace(ICC_Profile.getInstance(Files.newInputStream(new File("/path/to/custom_rgb_profile.icc").toPath())));

// Create a color model using your color space, TYPE_USHORT and 5/5/5 mask, no transparency
ColorModel colorModel = new DirectColorModel(colorSpace, 15, 0x7C00, 0x03E0, 0x001F, 0, false, DataBuffer.TYPE_USHORT);

// And finally, create an image from the color model and a compatible raster
BufferedImage imageToo = new BufferedImage(colorModel, colorModel.createCompatibleWritableRaster(w, h), colorModel.isAlphaPremultiplied(), null);

Просто помните, что поскольку графические операции Java2D и setRGB/getRGB по-прежнему используют sRGB, теперь все операции с вашим изображением будут преобразовываться между вашим цветовым пространством и sRGB. Производительность будет не такой хорошей.

person Harald K    schedule 06.05.2021
comment
Продолжая работать над своим проектом, я понял, что есть способ полностью обойти эту проблему; однако эта информация очень полезна для других частей моего проекта. Я работаю с чтением и записью файлов изображений, в которых используется цвет ushort555; хотя мне не нужна идеальная точность при просмотре их в моей программе, мне нужно будет прочитать такие данные из файла. Растровое решение, которое вы предоставили, отлично подойдет для моих целей. - person Cameron Scoggins; 07.05.2021