在 javafx 中创建图像叠加掩码
Create an image-overlay mask in javafx
我正在尝试做一件简单的事情。我有一个二值图像,我只想将二值图像叠加在彩色图像上,但是二值图像中的白色像素应该是红色的,黑色透明的。
我很习惯 JavaFx,但我坚持使用这个。我知道我可以通过使用 PixelReader 遍历所有像素来实现它,但我确信有更简单的方法。我尝试使用某种混合效果,但到目前为止运气不好。
我认为它应该与此类似:
How to Blend two Image in javaFX
我想到了这个:
图片 image = new Image("/circle.jpg", false);
ImageView iv = new ImageView(图像);
Image mask = new Image("/mask.jpg", false);
ImageView ivMask = new ImageView(mask);
Rectangle r = new Rectangle(mask.getWidth(), mask.getHeight());
r.setFill(Color.RED);
r.setBlendMode(BlendMode.MULTIPLY); // sets the white area red
Group g = new Group(ivMask, r); // sets the white area red
// this is not working as expected
iv.setBlendMode(BlendMode.DIFFERENCE);
Group g2 = new Group(iv, g);
感谢您的任何建议!
如果您认为逐像素处理比仅创建叠加层更快,请告诉我。
pixel-reader 的解决方案是:
Pane root = new Pane();
// read the underlaying image
root.getChildren().add(new ImageView(new Image("/src.jpg")));
Image mask = new Image("/mask.jpg");
PixelReader pixelReader = mask.getPixelReader();
Canvas resultCanvas = new Canvas();
root.getChildren().add(resultCanvas);
GraphicsContext resultLayer = resultCanvas.getGraphicsContext2D();
for (int y = 0; y < mask.getHeight(); y++) {
for (int x = 0; x < mask.getWidth(); x++) {
if( pixelReader.getColor(x, y).equals(Color.WHITE) ){
resultLayer.fillRect(x, y, 1.0, 1.0);
}
}
}
干杯!
你做错了什么
差异运算符不是基于是否设置像素的二元差异,而是 RGB 分量的差异,因此您将得到 multi-colored 叠加层,而不是实心红色叠加层,因为混合图像的 RGB 分量的差异因像素而异。
背景
您正在尝试使用混合模式执行类似于 masked bit-blit operation 的操作(基本上,基于黑底白字遮罩的像素数据的 OR 然后 AND)。尽管在 JavaFX 8 中使用 built-in 混合有点棘手,但这是可能的。
您可以创建功能请求以在混合 API 中为 bit-blt 样式基础提供额外支持,并公开完整的 porter duff compositing implementation like Swing has 以便底层混合引擎有一点更强大,可能更容易使用。
备选方案
首选的做法是 pre-process 在像 photoshop 这样的图像编辑器中将你的蒙版转换为 alpha 通道 - 然后你可以将你的蒙版叠加在你的原始和默认合成模式将采用它。
要使启用 alpha 的蒙版变为红色,您可以只使用 mask.setBlendMode(BlendMode.RED)
(或者您可以 pre-color 在图像编辑器中使用蒙版,然后再在您的程序中使用它)。
另一种选择是您在问题中使用的 PixelReader 解决方案(如果您无法 pre-convert 您的遮罩使用 alpha,我认为这很好)。
混合操作可以在适当的硬件上进行硬件加速。因此,如果您经常使用混合,可能会更快(但您必须在大图像上非常快速地进行许多混合 运行 才能真正注意到任何类型的性能差异)。
使用混合操作的示例解决方案
示例输出
输入图像
original.jpg
stencil.jpg
代码
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Blended extends Application {
@Override
public void start(Stage stage) {
Image original = new Image(
getClass().getResourceAsStream("original.jpg")
);
Image stencil = new Image(
getClass().getResourceAsStream("stencil.jpg")
);
// first invert the stencil so that it is black on white rather than white on black.
Rectangle whiteRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
whiteRect.setFill(Color.WHITE);
whiteRect.setBlendMode(BlendMode.DIFFERENCE);
Group inverted = new Group(
new ImageView(stencil),
whiteRect
);
// overlay the black portions of the inverted mask onto the image.
inverted.setBlendMode(BlendMode.MULTIPLY);
Group overlaidBlack = new Group(
new ImageView(original),
inverted
);
// create a new mask with a red tint (red on black).
Rectangle redRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
redRect.setFill(Color.RED);
redRect.setBlendMode(BlendMode.MULTIPLY);
Group redStencil = new Group(
new ImageView(stencil),
redRect
);
// overlay the red mask on to the image.
redStencil.setBlendMode(BlendMode.ADD);
Group overlaidRed = new Group(
overlaidBlack,
redStencil
);
// display the original, composite image and stencil.
HBox layout = new HBox(10);
layout.getChildren().addAll(
new ImageView(original),
overlaidRed,
new ImageView(stencil)
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
我正在尝试做一件简单的事情。我有一个二值图像,我只想将二值图像叠加在彩色图像上,但是二值图像中的白色像素应该是红色的,黑色透明的。 我很习惯 JavaFx,但我坚持使用这个。我知道我可以通过使用 PixelReader 遍历所有像素来实现它,但我确信有更简单的方法。我尝试使用某种混合效果,但到目前为止运气不好。 我认为它应该与此类似: How to Blend two Image in javaFX
我想到了这个: 图片 image = new Image("/circle.jpg", false); ImageView iv = new ImageView(图像);
Image mask = new Image("/mask.jpg", false);
ImageView ivMask = new ImageView(mask);
Rectangle r = new Rectangle(mask.getWidth(), mask.getHeight());
r.setFill(Color.RED);
r.setBlendMode(BlendMode.MULTIPLY); // sets the white area red
Group g = new Group(ivMask, r); // sets the white area red
// this is not working as expected
iv.setBlendMode(BlendMode.DIFFERENCE);
Group g2 = new Group(iv, g);
感谢您的任何建议! 如果您认为逐像素处理比仅创建叠加层更快,请告诉我。
pixel-reader 的解决方案是:
Pane root = new Pane();
// read the underlaying image
root.getChildren().add(new ImageView(new Image("/src.jpg")));
Image mask = new Image("/mask.jpg");
PixelReader pixelReader = mask.getPixelReader();
Canvas resultCanvas = new Canvas();
root.getChildren().add(resultCanvas);
GraphicsContext resultLayer = resultCanvas.getGraphicsContext2D();
for (int y = 0; y < mask.getHeight(); y++) {
for (int x = 0; x < mask.getWidth(); x++) {
if( pixelReader.getColor(x, y).equals(Color.WHITE) ){
resultLayer.fillRect(x, y, 1.0, 1.0);
}
}
}
干杯!
你做错了什么
差异运算符不是基于是否设置像素的二元差异,而是 RGB 分量的差异,因此您将得到 multi-colored 叠加层,而不是实心红色叠加层,因为混合图像的 RGB 分量的差异因像素而异。
背景
您正在尝试使用混合模式执行类似于 masked bit-blit operation 的操作(基本上,基于黑底白字遮罩的像素数据的 OR 然后 AND)。尽管在 JavaFX 8 中使用 built-in 混合有点棘手,但这是可能的。
您可以创建功能请求以在混合 API 中为 bit-blt 样式基础提供额外支持,并公开完整的 porter duff compositing implementation like Swing has 以便底层混合引擎有一点更强大,可能更容易使用。
备选方案
首选的做法是 pre-process 在像 photoshop 这样的图像编辑器中将你的蒙版转换为 alpha 通道 - 然后你可以将你的蒙版叠加在你的原始和默认合成模式将采用它。
要使启用 alpha 的蒙版变为红色,您可以只使用 mask.setBlendMode(BlendMode.RED)
(或者您可以 pre-color 在图像编辑器中使用蒙版,然后再在您的程序中使用它)。
另一种选择是您在问题中使用的 PixelReader 解决方案(如果您无法 pre-convert 您的遮罩使用 alpha,我认为这很好)。
混合操作可以在适当的硬件上进行硬件加速。因此,如果您经常使用混合,可能会更快(但您必须在大图像上非常快速地进行许多混合 运行 才能真正注意到任何类型的性能差异)。
使用混合操作的示例解决方案
示例输出
输入图像
original.jpg
stencil.jpg
代码
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.effect.BlendMode;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Blended extends Application {
@Override
public void start(Stage stage) {
Image original = new Image(
getClass().getResourceAsStream("original.jpg")
);
Image stencil = new Image(
getClass().getResourceAsStream("stencil.jpg")
);
// first invert the stencil so that it is black on white rather than white on black.
Rectangle whiteRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
whiteRect.setFill(Color.WHITE);
whiteRect.setBlendMode(BlendMode.DIFFERENCE);
Group inverted = new Group(
new ImageView(stencil),
whiteRect
);
// overlay the black portions of the inverted mask onto the image.
inverted.setBlendMode(BlendMode.MULTIPLY);
Group overlaidBlack = new Group(
new ImageView(original),
inverted
);
// create a new mask with a red tint (red on black).
Rectangle redRect = new Rectangle(stencil.getWidth(), stencil.getHeight());
redRect.setFill(Color.RED);
redRect.setBlendMode(BlendMode.MULTIPLY);
Group redStencil = new Group(
new ImageView(stencil),
redRect
);
// overlay the red mask on to the image.
redStencil.setBlendMode(BlendMode.ADD);
Group overlaidRed = new Group(
overlaidBlack,
redStencil
);
// display the original, composite image and stencil.
HBox layout = new HBox(10);
layout.getChildren().addAll(
new ImageView(original),
overlaidRed,
new ImageView(stencil)
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch();
}
}