1

我有一个生成 16 位灰度图像的应用程序。目前,这些图像生成为数据类型为 DataBuffer.TYPE_USHORT 的 awt BufferedImage。

在我的应用程序中,我从 BufferedImage 中提取 16 位数据,标准化为 8 位,然后使用 canvas.getGraphicsContext2D().getPixelWriter().setPixels(...) 将其渲染到 FX Canvas

它可以工作,但看起来很乱我想知道是否可以实现 JavaFX Image 或 WritableImage,让我们称它为由我的 16 位数据支持的 UShortImage。UShortImage 可以具有标准化级别的 FX 属性,但除此之外可以与任何 JavaFX 图像完全一样使用。

任何有关如何以良好的运行时效率实现这一目标的帮助或指示将不胜感激!

4

1 回答 1

3

我不知道这是否正是您要寻找的,因为也许我不明白您的问题。但是您可以通过使用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 对您不起作用,那么您可以在 WritableImage 上使用 PixelReaders 和 PixelWriters 逐个像素地进行操作。(正如您在问题中提到的那样,我没有看到涉及画布的理由)。有关使用 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); 

是的,你几乎可以做到,但这有点棘手。我同意 Java 8 中现有的 PixelFormat 并不是很容易进行用户扩展,原因如下:

某些方法,例如 WritabelImage 类的 getPixelWriter() 是最终的,因此您甚至无法覆盖它们。Image 基础架构看起来并非旨在以这种方式扩展。

通常,当涉及到 JavaFX 时,这种缺乏可扩展性是故意内置的。一些原因是:

  1. 安全性,通过覆盖核心功能来执行恶意副作用,使人们更难以破坏平台进行恶意操作。
  2. 向应用程序开发人员提供简化的界面,使其更易于理解和使用 API。
  3. 故意使平台的某些部分不透明,以便库开发人员更容易维护核心库实现并随意更改它,而不会影响用户程序。

当然,权衡(在这种情况下)可能是在尝试完成您希望的事情时缺乏灵活性。

如果您查看 JavaFX 中的 Image 实现,它有四个不同的组件:

  1. javafx.scene.image API
  2. 支持图像 API的平台实现 ( prism )(参见源代码)。
  3. 用于读取常见图像格式(png、jpg 等)的图像 io 库。
  4. 图形硬件加速器 API 或软件渲染 API 的接口,例如es2,最终通常将图像作为纹理输入到硬件图形 API(如 OpenGL 或 Direct3D)。

灰度像素格式的有趣之处在于它们没有直接暴露在 (1) javafx.scene.image API 中。但是,prism 平台实现、图像 io 库和硬件加速器(2、3 和 4)确实具有一定程度的内置灰度支持。例如,PNG 文件可能具有灰度格式,imageIO 将识别该格式并将灰度缓冲区直接传递给prism Image 类进行交互。

硬件加速层也支持灰度缓冲区,如ES2Texture实现所示。因此,这为图像能够直接渲染到硬件加速纹理提供了灰度像素格式编码缓冲区的理想情况。但是请注意, Prism/ES2 渲染系统支持的灰度字节缓冲区的当前像素格式是 8 位编码,而不是您理想要求的 16 位编码(尽管关于 prism 像素格式的注释并不是将来可能需要 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,或者您可以在openjfx-dev 邮件列表上询问它。实现此类功能的最短时间框架是 Java 10。看起来这样的请求可能已经存在,因此您可以对其发表评论或在 openjfx-dev 邮件列表中提出它以帮助推进它:

以上信息是否对您当前的问题有所帮助 -> 可能不会:-) 但希望它有助于理解平台及其功能、限制和未来扩展的潜在机制,以充分支持您的要求。

于 2017-03-28T21:14:51.933 回答