【问题标题】:Desaturation effect washes away contrast去饱和效果洗去对比度
【发布时间】:2022-01-24 15:43:57
【问题描述】:

我对 javaFx 的 ColorAdjust 效果有一个非常具体的问题,我正在尝试在图像上应用灰度滤镜,我正在使用 ColorAdjust 效果并设置饱和度 这是我正在尝试做的一个可重复的示例

public class App extends Application {
    @Override
    public void start(Stage ps) {
        Pane root = new Pane();
        root.setMinSize(300, 300);
        
        root.setStyle("-fx-background-color: #40444b;");
        
        ImageView view = new ImageView(new Image("https://res.cloudinary.com/mesa-clone/image/upload/v1642936429/1f914_tydc44.png"));
        view.setTranslateX(5);
        view.setTranslateY(5);
        view.setEffect(new ColorAdjust(0, -1, 0, 0));
        
        root.getChildren().add(view);
        
        ps.setScene(new Scene(root));

        ps.show();
    }
}

现在这段代码完成了它应该做的事情,但我对结果不满意,我想要一个与 web css 灰度过滤器类似的灰度过滤器,它为我的用例产生更好的结果:

<html>

<body style="background-color: #40444b;">
    <img src="https://res.cloudinary.com/mesa-clone/image/upload/v1642936429/1f914_tydc44.png" style="filter: grayscale(100);">
</body>

</html>

[左边是javafx,右边是Web(firefox)]

我知道差异不大,但它对我的用例至关重要,如果有人有更好的想法来获得与灰度过滤器的 web 版本相似的结果,我将不胜感激

【问题讨论】:

    标签: javafx imagefilter


    【解决方案1】:

    IMO,您使用 PixelWriter 解决方案提供的更新可能是您能做的最好的,您应该编辑问题以删除更新并将更新作为答案。

    我意识到更新过程比使用 ColorAdjust 效果稍微复杂一些。

    但我认为你不能使用 ColorAdjust 做你想做的事。

    灰度功能通过RGB multiplication调整颜色:

    double gray = 0.21 * red + 0.71 * green + 0.07 * blue; 
    return Color.color(gray, gray, gray, opacity);
    

    ColorAdjust 通过 HSB 进行调整。

    我可能是错的,但我认为您不能将 HSB 参数提供给 ColorAdjust 以执行等效的 RGB 乘法。

    潜在的更高性能解决方案

    仅当您遇到性能问题并且必须有更好的性能时才花时间。

    您可以使用使用硬件加速的效果管道创建自己的 GrayscaleEffect,如果您需要高性能,这可能是需要考虑的事情。但是这个过程真的很复杂,创建硬件加速效果并不容易,没有相关文档,为此您需要研究 openjfx 存储库中现有的效果代码并根据您的目的进行调整。

    如果硬件加速实现太棘手,您可以使用 fork join pool 并行进行调整以提高速度,直接在字节缓冲区上操作,而不是使用颜色对象。您需要为每个像素应用的算法由灰度函数实现给出。答案中有一个部分示例:

    • javafx argb to grayscale conversion

      int pixel = pixelReader.getArgb(x, y);
      
      int alpha = ((pixel >> 24) & 0xff);
      int red = ((pixel >> 16) & 0xff);
      int green = ((pixel >> 8) & 0xff);
      int blue = (pixel & 0xff);
      
      int grayLevel = (int) (0.2162 * red + 0.7152 * green + 0.0722 * blue);
      int gray = (alpha << 24) + (grayLevel << 16) + (grayLevel << 8) + grayLevel;
      
      grayImage.getPixelWriter().setArgb(x, y, gray);
      

    但这不使用字节缓冲区,直接在像素读取器/写入器的字节缓冲区上工作可能更有效,尽管这有点棘手。

    【讨论】:

    • 因为我要在相对较小的 ~72x72px 的图像上应用效果,我相信 pixelWriter 解决方案在性能方面就足够了,我最关心的是 清洁度 的解决方案,感谢通过 ColorAdjust 解释灰度函数和灰度之间的区别。
    【解决方案2】:

    我可能会建议ColorConvertOp,显示为here,但它比PixelWriter 稍慢,而且不是特别通用。

    作为@jewelsea 的key insight 的插图,请注意未调整图像中手和脸之间的低对比度。通过ColorAdjust 操作可以改变图像的属性作为一个整体。不幸的是,它不能改变图像区域的相对对比度,如您的首选结果所示。

    不过,如果您找到可接受的结果,该示例将允许您根据经验调整属性。

    import javafx.application.Application;
    import javafx.beans.property.DoubleProperty;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Slider;
    import javafx.scene.control.Tooltip;
    import javafx.scene.effect.ColorAdjust;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    /** @see https://stackoverflow.com/q/70821638/230513 */
    public class GrayApp extends Application {
    
        private final Insets insets = new Insets(10);
    
        private Slider createSlider(DoubleProperty dp) {
            Slider s = new Slider(-1, 1, dp.get());
            s.setBlockIncrement(0.1);
            s.setTooltip(new Tooltip(dp.getName()));
            dp.bind(s.valueProperty());
            VBox.setMargin(s, insets);
            return s;
        }
    
        @Override
        public void start(Stage stage) {
            ImageView view = new ImageView(new Image(
                "https://res.cloudinary.com/mesa-clone/image/upload/v1642936429/1f914_tydc44.png"));
            ColorAdjust adjust = new ColorAdjust(0, -1, 0, 0);
            view.setEffect(adjust);
    
            Slider hSlider = createSlider(adjust.hueProperty());
            Slider sSlider = createSlider(adjust.saturationProperty());
            Slider bSlider = createSlider(adjust.brightnessProperty());
            Slider cSlider = createSlider(adjust.contrastProperty());
    
            VBox root = new VBox();
            root.setPadding(insets);
            VBox.setMargin(view, insets);
            root.setAlignment(Pos.CENTER);
            root.setStyle("-fx-background-color: #808080;");
            root.getChildren().addAll(view, hSlider, sSlider, bSlider, cSlider);
            stage.setScene(new Scene(root));
            stage.show();
        }
    }
    

    【讨论】:

    • 有趣的方法,找到一个饱和度/亮度/对比度值的组合,比仅仅调整饱和度提供更好的结果会比保留 2 张图像更干净
    【解决方案3】:

    使用 WritableImage 和 Color 手动将图像转换为灰度。grayscale() 可以提供更好的结果,但它会使颜色和灰度之间的切换过程变得复杂:

    public class App extends Application {
    
        @Override
        public void start(Stage ps) {
            Pane root = new Pane();
            root.setMinSize(300, 300);
    
            root.setStyle("-fx-background-color: #40444b;");
    
            Image image = new Image("https://res.cloudinary.com/mesa-clone/image/upload/v1642936429/1f914_tydc44.png");
            
            ImageView view = new ImageView(grayScale(image));
            
            view.setTranslateX(5);
            view.setTranslateY(5);
    
            root.getChildren().add(view);
    
            ps.setScene(new Scene(root));
            ps.setTitle("javafx grayscale test");
            ps.show();
        }
    
        private static Image grayScale(Image img) {
            WritableImage res = new WritableImage((int) img.getWidth(), (int) img.getHeight());
    
            PixelReader pr = img.getPixelReader();
            PixelWriter pw = res.getPixelWriter();
            for (int y = 0; y < img.getHeight(); y++) {
                for (int x = 0; x < img.getWidth(); x++) {
                    pw.setColor(x, y, pr.getColor(x, y).grayscale());
                }
            }
            return res;
        }
    }
    

    您可以选择是保存过滤后的图像还是每次都生成它,具体取决于这种权衡(增加内存使用以提高性能)是否值得。

    【讨论】:

    猜你喜欢
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-30
    • 1970-01-01
    相关资源
    最近更新 更多