如何创建JavaFX 16位灰度图像

时间:2017-03-28 11:26:18

标签: java image javafx 16-bit ushort

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

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

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

任何有关如何以良好的运行时效率实现此目标的帮助或指示都将受到赞赏!

1 个答案:

答案 0 :(得分:3)

我不知道这是否正是您所寻找的,也许我不理解您的问题。但是,您可以使用ColorAdjust效果将颜色去饱和,从而将彩色图像转换为灰色图像。

ColorAdjust monochrome = new ColorAdjust();
monochrome.setSaturation(-1);
ImageView gray = new ImageView(new Image(IMAGE_LOC));
gray.setEffect(monochrome);

我不确定为什么可怜的奶奶和爷爷必须褪色为黑白,但一定是这样。

coloradjust

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. 故意使平台的某些部分不透明,以便库开发人员更容易维护核心库实现并随意更改它而不会影响用户程序。
  4. 当然,权衡(如本例所示)可能是缺乏灵活性来尝试实现你想要的目标。

    如果你研究JavaFX中的Image实现,它有四种不同的组件:

    1. javafx.scene.image APIs
    2. 支持图片API的平台实施(prism)(参见source)。
    3. image io libraries用于阅读常见图像格式(png,jpg等)。
    4. 与图形硬件加速器API或软件渲染API的接口,例如es2,最终通常将图像视为硬件图形API(如OpenGL或Direct3D)的纹理输入。
    5. 关于灰度像素格式的有趣之处在于它们不直接暴露在(1)javafx.scene.image API中。但是,棱镜平台实现,图像io库和硬件加速器(2,3和4)确实具有一定程度的内置灰度支持。例如,PNG文件可以具有灰度格式,imageIO将识别格式并将灰度缓冲区直接传递给prism Image class以进行交互。

      硬件加速层也支持灰度缓冲区,如ES2Texture实现中所示。因此,这给出了灰度像素格式编码缓冲器的理想情况,该图像缓冲器能够直接渲染到硬件加速纹理。但是请注意,Prism / ES2渲染系统支持的current pixel format for the grayscale byte buffer用于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中向用户公开。

      可以创建feature request以启用灰度像素格式的公共API,或者您可以在openjfx-dev mailing list上询问它。实现此类功能的最短时间是Java 10.看起来这样的请求可能已经存在,因此您可以对其进行评论或将其提交到openjfx-dev邮件列表以帮助推进它:

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