Как создать 16-битные изображения в оттенках серого JavaFX

У меня есть приложение, которое генерирует 16-битные изображения в оттенках серого. В настоящее время эти изображения генерируются как awt BufferedImage с типом данных DataBuffer.TYPE_USHORT.

В моем приложении я извлекаю 16-битные данные из BufferedImage, нормализую до 8-битных, а затем визуализирую их в FX Canvas с помощью canvas.getGraphicsContext2D().getPixelWriter().setPixels(...)

Это работает, но выглядит грязно. Мне интересно, можно ли реализовать изображение JavaFX или WritableImage, давайте назовем его UShortImage, который поддерживается моими 16-битными данными. UShortImage может иметь свойства FX для уровней нормализации, но в остальном может использоваться точно так же, как любое изображение JavaFX.

Будем признательны за любую помощь или указания о том, как добиться этого с хорошей эффективностью времени выполнения!


person Michael Ellis    schedule 28.03.2017    source источник


Ответы (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, являются окончательными, поэтому вы даже не можете их переопределить. Инфраструктура образов выглядит так, как будто она не предназначена для такого расширения.

Обычно, когда дело доходит до JavaFX, эта нехватка расширяемости преднамеренно встроена. Некоторые причины:

  1. Безопасность, чтобы людям было труднее подрывать платформу и делать что-то злонамеренное, переопределяя основные функции, чтобы также выполнять вредоносные побочные эффекты.
  2. Предоставьте разработчику приложения упрощенный интерфейс, чтобы упростить понимание и использование API.
  3. Намеренно сделайте некоторые части платформы непрозрачными, чтобы разработчикам библиотек было легче поддерживать реализацию основной библиотеки и изменять ее по своему усмотрению, не затрагивая пользовательские программы.

Конечно, компромиссом (как в данном случае) может быть недостаток гибкости в попытках достичь желаемого.

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

  1. API javafx.scene.image< /а>.
  2. Реализация платформы (prism) поддержка API изображений (см. источник).
  3. библиотеки ввода-вывода изображений для чтения распространенных форматов изображений (png, jpg и т. д.).
  4. Интерфейсы к API графического аппаратного ускорителя или API рендеринга программного обеспечения, например es2, который, в конечном счете, обычно обрабатывает изображение как входную текстуру для аппаратного графического API, такого как OpenGL или Direct3D.

Интересная вещь о форматах пикселей в градациях серого заключается в том, что они не отображаются напрямую в (1) API-интерфейсах javafx.scene.image. Однако реализация платформы prism, библиотеки ввода-вывода изображений и аппаратные ускорители (2, 3 и 4) имеют некоторый уровень встроенной поддержки оттенков серого. Например, файл PNG может иметь формат оттенков серого, и imageIO распознает формат и передаст буфер оттенков серого непосредственно в класс изображения prism для интерпретации.

Буферы оттенков серого также поддерживаются уровнем аппаратного ускорения, как показано на 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 для форматов пикселей в градациях серого, или запросить об этом на странице < href="http://mail.openjdk.java.net/mailman/listinfo/openjfx-dev" rel="nofollow noreferrer">список рассылки openjfx-dev. Кратчайшим сроком реализации таких функций будет Java 10. Похоже, что такой запрос может уже существовать, поэтому вы можете прокомментировать его или добавить в список рассылки openjfx-dev, чтобы помочь продвинуть его вперед:

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

person jewelsea    schedule 28.03.2017
comment
Спасибо за ваше предложение о помощи, но моя проблема заключается не в преобразовании цвета в оттенки серого, а в том, как создать изображения JavFX, которые поддерживаются 16-битными данными, то есть данными изображения, где пиксели могут принимать значение интенсивности от 0 до 2 ^ 16-1. Данные изображения в этом формате должны быть нормализованы к чему-то, что может быть представлено на обычных дисплеях, и это означает нормализацию базовых данных в один из ранее существующих типов изображений JavaFX (это я могу сделать), но мне интересно, есть ли какие-либо элегантные решения уже написаны. Тем не менее, еще раз спасибо за ваш вклад. - person Michael Ellis; 29.03.2017
comment
Как использование PixelReaders и PixelWriters с WritableImage не позволяет вам делать то, что вы хотите? Можете ли вы предоставить ссылку на справочную документацию, в которой описывается, что вы пытаетесь сделать? - person jewelsea; 29.03.2017
comment
Мне интересно, что вы подразумеваете под одним из ранее существовавших типов изображений JavaFX? изображение JavaFX не имеет тип изображения. Он может читать из различных форматов, таких как jpg, png и т. д., но в API нет типа изображения. - person jewelsea; 29.03.2017
comment
Да, когда я указал тип изображения, было бы более уместно сказать тип пикселя, т.е. FX поддерживает типы пикселей, определенные PixelFormat, такие как BYTE_BGRA и BYTE_BGRA_PRE; Я хочу добавить поддержку нового типа пикселей, который имеет один образец на пиксель, который представляет собой значение данных без знака 16. pixelFormat = PixelFormat.createByteIndexedInstance(LUT); getPixelWriter().setPixels(0, 0, w, h, pixelFormat, normalizedImageData, 0, w); .... - person Michael Ellis; 29.03.2017
comment
Он работает, даже удовлетворяет мои потребности, но это не так. Это подрывает контракт WritableImage. Например, PixelWriter, возвращенный из изображения, записывает в базовый WriteableImage, но не обновляет базовые данные. (не то, чтобы мне это нужно, но все же приятно, чтобы все было правильно). Кажется, должен быть лучший способ расширить Image и связанные с ним классы, чтобы делать то, что я хочу, но я не понимаю, как это сделать. Некоторые методы, например, getPixelWriter() класса WritabelImage, являются окончательными, поэтому вы даже не можете их переопределить. Инфраструктура образов выглядит так, как будто она не предназначена для такого расширения. - person Michael Ellis; 29.03.2017