Преобразовать короткий [] в изображение в градациях серого - PullRequest
6 голосов
/ 07 января 2012

Я пишу фрактальный генератор Буддхаброта, используя апарапи. Я получил часть 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. Что мне нужно сделать?

Ответы [ 2 ]

7 голосов
/ 07 января 2012

Проблема в том, что setRGB() хочет значение цвета 0xRRGGBB. BufferedImage любит притворяться, что изображение RGB, независимо от того, как хранятся данные. На самом деле вы можете добраться до внутреннего DataBufferShortgetTile(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));

Преимущество этого подхода заключается в том, что вы контролируете базовый массив пикселей. Вы можете внести изменения в этот массив и вызвать newPixels() на вашем MemoryImageSource, и он обновится в реальном времени. Это также дает вам полную возможность определять свою собственную палитру, отличную от оттенков серого:

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);
3 голосов
/ 07 января 2012

Для справки этот пример показывает, как два разных типа BufferedImage интерпретируют одни и те же 16-битные данные. Вы можете навести курсор мыши на изображения, чтобы увидеть значения пикселей.

Приложение: Чтобы уточнить слово интерпретировать , обратите внимание, что setRGB() пытается найти наиболее близкое совпадение с указанным значением в данном ColorModel.

BufferedImageTest

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();
            }
        });
    }
}
...