【问题标题】:Convert short[] into a grayscale image将 short[] 转换为灰度图像
【发布时间】:2012-02-04 14:32:08
【问题描述】:

我正在使用 aparapi 编写一个Buddhabrot 分形生成器。我让它的 OpenCL 部分工作,产生一个代表每个像素的一维数组。我将最终图像的尺寸作为最终整数,并编写了代码来获取该数组中任意点的索引。我想将此保存为图像,并尝试将 BufferedImage 与 TYPE_USHORT_GRAY 一起使用。到目前为止,这是我所拥有的:

    BufferedImage image=new BufferedImage(VERTICAL_PIXELS, HORIZONTAL_PIXELS, BufferedImage.TYPE_USHORT_GRAY);
    for(int i=0; i<VERTICAL_PIXELS; i++)
        for(int k=0; k<HORIZONTAL_PIXELS; k++)
            image.setRGB(k, i, normalized[getArrayIndex(k,i,HORIZONTAL_PIXELS)]);

问题是,我不知道将 RGB 设置为什么。我需要做什么?

【问题讨论】:

    标签: java bufferedimage grayscale


    【解决方案1】:

    这里的问题是setRGB() 想要一个 0xRRGGBB 颜色值。 BufferedImage 喜欢假装图像是 RGB,无论数据存储为什么。您实际上可以访问内部 DataBufferShort(使用 getTile(0, 0).getDataBuffer()),但要弄清楚它是如何布局的可能会很棘手。

    如果您已经在 short[] 中保存了像素,则更简单的解决方案可能是将它们复制到 int[] 中,而不是将其卡在 MemoryImageSource 中:

    int[] buffer = /* pixels */;
    
    ColorModel model = new ComponentColorModel(
       ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] { 16 }, 
       false, true, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);
    
    Image image = Toolkit.getDefaultToolkit().createImage(
       new MemoryImageSource(VERTICAL_PIXELS, HORIZONTAL_PIXELS, 
                             model, buffer, 0, VERTICAL_PIXELS));
    

    这种方法的优点是您可以控制底层像素阵列。您可以更改该数组并在您的MemoryImageSource 上调用newPixels(),它会实时更新。它还为您提供了定义灰度以外的自己的调色板的完整功能:

    int[] cmap = new int[65536];
    for(int i = 0; i < 65536; ++i) {
    
        cmap[i] = (((i % 10000) * 256 / 10000) << 16) 
                | (((i % 20000) * 256 / 20000) << 8)
                | (((i % 40000) * 256 / 40000) << 0);
    }
    ColorModel model = new IndexColorModel(16, 65536, cmap, 0, false, -1, DataBuffer.TYPE_USHORT);
    

    如果您只想在屏幕上显示图像,这种方法可以正常工作:

    JFrame frame = new JFrame();
    frame.getContentPane().add(new JLabel(new ImageIcon(image)));
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    

    但是,如果您想将其写入文件并保留每像素一个短的格式(例如,加载到 Matlab 中),那么您就不走运了。最好的办法是将其绘制成BufferedImage 并用ImageIO 保存,这将保存为RGB。

    如果你最后肯定需要BufferedImage,另一种方法是自己应用调色板,计算RGB值,然后将它们复制到图像中:

    short[] data = /* your data */;
    int[] cmap = /* as above */;
    int[] rgb = new int[data.length];
    
    for(int i = i; i < rgb.length; ++i) {
       rgb[i] = cmap[data[i]];
    }
    
    BufferedImage image = new BufferedImage(
       VERTICAL_PIXELS, HORIZONTAL_PIXELS, 
       BufferedImage.TYPE_INT_RGB);
    
    image.setRGB(0, 0, VERTICAL_PIXELS, HORIZONTAL_PIXELS,
       pixels, 0, VERTICAL_PIXELS);
    

    【讨论】:

    • 我这样做了,然后意识到我的绘图代码是 FUBAR,到处都有 ArrayOutOfBounds 异常。不过,看着它,它似乎可以正常工作。我需要做的就是将它输出为.png,我已经(希望)成功地完成了。我会将其标记为正确,因为它似乎可以正常工作。
    • 当这将像素放入图像中时,它是以从左侧开始并向右移动的垂直线还是从顶部开始向下移动的水平线绘制它们?因为我的部分工作代码将图像顺时针旋转了 90 度,我认为这不是绘图的错。
    • 没关系!我不得不将 MemoryImageSource 部分中的 200 更改为 HORIZONTAL_PIXELS。效果很好,谢谢!
    • 很抱歉 - 我正在使用固定大小的 200x200 图像,然后我尝试切换您的常量,但我错过了一个。
    • 我这样做了,效果很好。问题是,使用 img.getGraphics().drawImage(image, 0, 0, null); 绘制大(2000x2000)图像需要很长时间。以便它们可以使用 ImageIO 保存。有更快的方法吗?
    【解决方案2】:

    作为参考,此示例显示了两种不同的BufferedImage 类型如何解释相同的 16 位数据。您可以将鼠标悬停在图像上以查看像素值。

    附录:为了详细说明解释这个词,请注意setRGB() 试图在给定的ColorModel 中找到与指定值最接近的匹配项。

    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.GridLayout;
    import java.awt.Point;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import java.awt.image.BufferedImage;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    /** @see http://stackoverflow.com/questions/8765004 */
    public class BufferedImageTest extends JPanel {
    
        private static final int SIZE = 256;
        private static final Random r = new Random();
        private final BufferedImage image;
    
        public BufferedImageTest(int type) {
            image = new BufferedImage(SIZE, SIZE, type);
            this.setPreferredSize(new Dimension(SIZE, SIZE));
            for (int row = 0; row < SIZE; row++) {
                for (int col = 0; col < SIZE; col++) {
                    image.setRGB(col, row, 0xff00 << 16 | row << 8 | col);
                }
            }
            this.addMouseMotionListener(new MouseAdapter() {
    
                @Override
                public void mouseMoved(MouseEvent e) {
                    Point p = e.getPoint();
                    int x = p.x * SIZE / getWidth();
                    int y = p.y * SIZE / getHeight();
                    int c = image.getRGB(x, y);
                    setToolTipText(x + "," + y + ": "
                        + String.format("%08X", c));
                }
            });
        }
    
        @Override
        protected void paintComponent(Graphics g) {
            g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
        }
    
        static private void display() {
            JFrame f = new JFrame("BufferedImageTest");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setLayout(new GridLayout(1, 0));
            f.add(new BufferedImageTest(BufferedImage.TYPE_INT_ARGB));
            f.add(new BufferedImageTest(BufferedImage.TYPE_USHORT_GRAY));
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    display();
                }
            });
        }
    }
    

    【讨论】:

    • 从技术上讲,这些不显示相同的 16 位数据 - 它们在强制转换为 USHORT 灰度或 RGB 时显示相同的 RGB 值。这是因为 setRGB() 将颜色转换为图像的调色板 - 它不只是写入像素值。如果它写入像素值,则灰度值将在底部为白色。
    猜你喜欢
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 2014-02-26
    相关资源
    最近更新 更多