Как создать 16-битные изображения в оттенках серого JavaFX
У меня есть приложение, которое генерирует 16-битные полутоновые изображения. В настоящее время эти изображения генерируются как AWT BufferedImage с типом данных DataBuffer.TYPE_USHORT.
В моем приложении я извлекаю 16-битные данные из BufferedImage, нормализую до 8-битных, а затем визуализирую их в FX Canvas с canvas.getGraphicsContext2D(). GetPixelWriter(). SetPixels(...)
Это работает, но выглядит грязно, мне интересно, возможно ли реализовать JavaFX Image или WritableImage, давайте назовем это UShortImage, которое поддерживается моими 16-битными данными. UShortImage может иметь свойства FX для уровней нормализации, но в противном случае может использоваться точно так же, как любое изображение JavaFX.
Буду признателен за любую помощь или указатели о том, как добиться этого с хорошей эффективностью времени выполнения!
1 ответ
Я не знаю, если это именно то, что вы ищете, возможно, я не понимаю ваш вопрос. Но вы можете преобразовать цветное изображение в серое, обесцветив цвет с помощью эффекта ColorAdjust.
ColorAdjust monochrome = new ColorAdjust();
monochrome.setSaturation(-1);
ImageView gray = new ImageView(new Image(IMAGE_LOC));
gray.setEffect(monochrome);
Я не уверен, почему бедные бабушка и дедушка должны исчезнуть в черно-белом, но это должно быть так.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class GrayScale extends Application {
@Override
public void start(Stage stage) {
ColorAdjust monochrome = new ColorAdjust();
monochrome.setSaturation(-1);
Image image = new Image(IMAGE_LOC);
ImageView color = new ImageView(image);
ImageView gray = new ImageView(image);
gray.setEffect(monochrome);
HBox layout = new HBox(10, color, gray);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
scene.setFill(Color.BLACK);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
// source: http://www.photoshopessentials.com/photo-editing/black-and-white-tutorials/desaturate/
private static final String IMAGE_LOC =
"http://pe-images.s3.amazonaws.com/photo-effects/black-and-white/grandparents.jpg";
}
Если ColorAdjust не работает для вас, то вы можете просто делать попиксельные основы, используя PixelReaders и PixelWriters на WritableImage. (Я не вижу причин для включения холста, как вы упоминаете в своем вопросе). Для связанной реализации, использующей WritableImage, смотрите:
Возможно, вы также ищете расширение PixelFormat с новым типом и расширение PixelReader для чтения вашего буфера данных, что может быть возможным, хотя я полагаю, что для выполнения того, что вы хотите, не понадобится:
PixelReader reader = new GrayScalePixelReader();
reader.getPixels(0, 0, W, H,
GrayScalePixelFormat.instance(), grayScaleByteBuffer, 0, scanlineStride);
Image img = new WritableImage(reader, W, H);
где GrayScalePixelReader и GrayScalePixelFormat - это новые классы, которые вы создаете с помощью расширения.
FX поддерживает типы пикселей, определенные PixelFormat, такие как BYTE_BGRA и BYTE_BGRA_PRE; Я ищу, чтобы добавить поддержку для нового типа пикселей, который имеет один образец на пиксель, который является 16-значным значением без знака.
pixelFormat = PixelFormat.createByteIndexedInstance(LUT); getPixelWriter().setPixels(0, 0, w, h, pixelFormat, normalisedImageData, 0, w);
Да, вы можете почти сделать это, но это немного сложно. Я согласен с тем, что существующий PixelFormat в Java 8 на самом деле не очень легко расширяемый пользователем по причинам, указанным вами:
Некоторые методы, например, getPixelWriter() класса WritabelImage являются окончательными, поэтому вы даже не можете их переопределить. Инфраструктура Image выглядит так, как будто она не предназначена для такого расширения.
Обычно, когда дело доходит до JavaFX, это отсутствие расширяемости встроено специально. Некоторые причины:
- Безопасность, чтобы людям было сложнее подорвать платформу и сделать что-то вредоносное, переопределяя основные функции и выполняя вредоносные побочные эффекты.
- Предоставьте разработчику упрощенный интерфейс, чтобы было проще понять и использовать API.
- Умышленно делайте некоторые части платформы непрозрачными, чтобы разработчикам библиотеки было проще поддерживать реализацию базовой библиотеки и изменять ее по своему усмотрению, не влияя на пользовательские программы.
Конечно, компромиссом (как в этом случае) может быть отсутствие гибкости в попытках выполнить то, что вы хотите.
Если вы посмотрите на реализацию Image в JavaFX, она имеет четыре разных компонента:
- API javafx.scene.image.
- Реализация платформы ( призма) поддерживает API изображений (см. Источник).
- Библиотека изображений для чтения распространенных форматов изображений (PNG, JPG и т. Д.).
- Интерфейсы для API-интерфейсов аппаратного ускорения графики или API рендеринга программного обеспечения, например es2, которые в конечном итоге обычно обрабатывают изображение как текстурный ввод для аппаратного API-интерфейса графики, такого как OpenGL или Direct3D.
Интересная вещь о пиксельных форматах в градациях серого состоит в том, что они не отображаются напрямую в (1) API-интерфейсах javafx.scene.image. Однако реализация платформы prism, библиотеки изображений io и аппаратные ускорители (2, 3 и 4) действительно имеют некоторый уровень встроенной поддержки оттенков серого. Например, файл PNG может иметь формат градаций серого, и imageIO распознает формат и передаст буфер градаций серого непосредственно в класс изображений призмы для интерпретации.
Буферы в градациях серого также поддерживаются уровнем аппаратного ускорения, как видно из реализации ES2Texture. Таким образом, это дает идеальную ситуацию с буфером, закодированным в формате пикселя в градациях серого, для того, чтобы изображение можно было напрямую визуализировать с аппаратно ускоренной текстурой. Однако обратите внимание, что текущий формат пикселей для байтового буфера в градациях серого, поддерживаемый системой рендеринга Prism/ES2, предназначен для 8-битной кодировки, а не для 16-битной кодировки, которую вы в идеале запрашиваете (хотя комментарии к формату призменных пикселей не 16-битный тип может понадобиться в будущем;-):
// L8, A8 types:
// NOTE : we might need L8A8 16-bit type
BYTE_GRAY (DataType.BYTE, 1, true, true),
Таким образом, это означает, что большая часть инфраструктуры, которую вы ищете, которая позволяет прямую интерпретацию буфера закодированных изображений в градациях серого с помощью среды выполнения JavaFX, уже существует, однако она находится внутри com.sun
классы и приватный API и не выставляются напрямую публично javafx.scene.image
API-интерфейсы.
Можно создать запрос функции, чтобы включить общедоступный API для пиксельных форматов в оттенках серого, или вы можете запросить его в списке рассылки openjfx-dev. Кратчайшие сроки для реализации таких функций - Java 10. Похоже, такой запрос уже может существовать, так что вы можете прокомментировать его или включить в список рассылки openjfx-dev, чтобы помочь продвинуть его вперед:
Помогает ли приведенная выше информация в любом случае решить текущую проблему ->, вероятно, нет:-) Но, надеюсь, она поможет понять платформу и ее возможности, ограничения и потенциальные механизмы для будущего расширения для полной поддержки ваших требований.