【问题标题】:BufferedImage swap red and blue channelBufferedImage 交换红蓝通道
【发布时间】:2019-04-07 18:22:28
【问题描述】:

我的目标是交换 java BufferedImage 的红色和蓝色通道。

除了低效地迭代每个像素值和交换通道之外,还有什么方法可以实现这一点?我在想一些按位魔法或一些我不知道的集成功能。

感谢任何帮助。

【问题讨论】:

    标签: java algorithm graphics colors bufferedimage


    【解决方案1】:

    也许你创建了一个 BufferedImage 的新实例,它具有相同的 WritableRaster raster 和相同的属性,但使用了交换颜色的 ColorModel:

    例如:

    ColorModel swappedColorModel = new DirectColorModel(24,
                                                  0x000000ff,   // Red -> Blue
                                                  0x0000ff00,   // Green
                                                  0x00ff0000,   // Blue -> Red
                                                  0x0           // Alpha
                                                  );
    BufferedImage swapedColorImage = new BufferedImage (swappedColorModel,
                                                        originalImage.getRaster(),
                                                        swappedColorModel.isAlphaPremultiplied(),
                                                        properties);
    

    这段代码我没试过

    【讨论】:

    • 如果不是你写的视频,你能链接到解决方案的来源吗?
    • 这对我不起作用。它崩溃了,但我不能将颜色模型与我的图像一起使用。图像为 3BYTE_BGR 格式。也许不存在的 Alpha 通道有问题?
    • @DavidBauer 我认为这个想法是合理的(而且很快!),但是,对于TYPE_3BYTE_BGR,您需要ComponentColorModel,而不是DirectColorModel(与TYPE_INT_* @987654329 一起使用@)。但是,这不会交换数据的通道,只会交换它的显示方式。关于这是否可以,您的问题模棱两可。
    • 如果最终结果相同也没关系。我只想交换图像中的红色和蓝色强度。如何实现这一点并不重要。尽快尝试 ComponentColorModel
    • ComponentColorModel 成功了。我还必须更改 BufferedImage 中使用的栅格。这篇文章也帮了我很多忙:stackoverflow.com/questions/24639986/…
    【解决方案2】:

    这是一个非常快的解决方案,因为它不会真正改变数据,只会改变数据的显示方式。

    诀窍在于通道顺序(字节顺序)由SampleModel 控制。并且您可以在不实际更改数据的情况下更改示例模型,以使相同的数据显示不同。

    如果您已经有一个BufferedImage,创建具有交换通道的示例模型的最简单方法是使用Raster.createWritableChild(...) 方法创建一个新子模型Raster,并指定通道(或“波段”)在最后一个参数中排序。

    bgr.getRaster().createWritableChild(0, 0, bgr.getWidth(), bgr.getHeight(), 0, 0, 
                                        new int[]{2, 1, 0}); // default order is 0, 1, 2
    

    在下面的示例中,图像数据是相同的(如果有疑问,请在克隆图像后尝试移动绘画部分,看看结果是否相同)。仅交换频道:

    public static void main(String[] args) {
        // Original
        final BufferedImage bgr = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR);
    
        // Paint something
        Graphics2D graphics = bgr.createGraphics();
        try {
            graphics.setColor(Color.BLUE);
            graphics.fillRect(0, 0, bgr.getWidth(), bgr.getHeight());
            graphics.setColor(Color.YELLOW);
            graphics.fillRect(0, 0, bgr.getWidth(), bgr.getHeight() / 3);
            graphics.setColor(Color.GREEN);
            graphics.fillRect(0, 0, bgr.getWidth() / 3, bgr.getHeight());
        }
        finally {
            graphics.dispose();
        }
    
        // Clone, and swap BGR -> RGB
        ColorModel colorModel = bgr.getColorModel();
        WritableRaster swapped = bgr.getRaster().createWritableChild(0, 0, bgr.getWidth(), bgr.getHeight(), 0, 0, 
                                                                     new int[]{2, 1, 0}); // default order is 0, 1, 2
        final BufferedImage rgb = new BufferedImage(colorModel, swapped, colorModel.isAlphaPremultiplied(), null);
    
        System.err.println("bgr: " + bgr); // TYPE_3BYTE_BGR (5)
        System.err.println("rgb: " + rgb); // TYPE_CUSTOM (0)
    
        // Display it all
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    
                frame.add(new JLabel(new ImageIcon(bgr)), BorderLayout.WEST);
                frame.add(new JLabel(new ImageIcon(rgb)));
    
                frame.pack();
                frame.setLocationRelativeTo(null);
    
                frame.setVisible(true);
            }
        });
    }
    

    PS:我从 cmets 知道 OP 不需要这个,但是如果您出于某种原因确实需要交换像素数据的通道(即本机库需要左右),最快的可能是要获取数据,循环并交换红色和蓝色(第一个和第三个)组件:

    byte[] data = ((DataBufferByte) bgr.getRaster().getDataBuffer()).getData();
    for (int i = 0; i < data.length; i += 3) {
        // Swap 1st and 3rd component
        byte b = data[i];
        data[i] = data[i + 2];
        data[i + 2] = b;
     }
    

    【讨论】:

      猜你喜欢
      • 2013-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-20
      • 1970-01-01
      • 1970-01-01
      • 2012-08-03
      • 2022-12-19
      相关资源
      最近更新 更多