Более высокая производительность при рендеринге TIFF в Java с помощью JAI - PullRequest
3 голосов
/ 06 декабря 2011

Я работал над программным обеспечением, которое анализирует данные микроскопии, которые хранятся в виде многослойного файла формата TIFF. Просмотрев StackOverflow и документацию JAI, я собрал некоторый код для хранения стека TIFF и правильного его рендеринга.

Тем не менее, он страдает от довольно плохой производительности. Я надеялся на более быструю производительность после прочтения постов, таких как эта: Кому-нибудь посчастливилось написать очень быстрый просмотрщик / редактор tiff на Java?

К сожалению, это не так хорошо, как я бы надеялся. У меня нет большого опыта работы с внешними библиотеками изображений или с графическими возможностями Java в этом отношении, поэтому я не уверен, как бы это улучшить.

Для некоторого контекста, вот видео «заикания», которое я испытываю при прохождении через стек tiff: http://www.youtube.com/watch?v=WiR4o6TsqyM&feature=channel_video_title

Обратите внимание, как кадры иногда заикаются при перемещении ползунка.

Я улучшил производительность при увеличении изображения, удалив сглаживание при масштабировании, но оно все еще не так быстро, как хотелось бы.

Мой код указан ниже:

/** Converts the RenderedImage into a BufferedImage, which is more flexible
 * @param img   The original RenderedImage
 * @return  The new BufferedImage
 */
public BufferedImage convertRenderedImage(RenderedImage img) {
    if (img instanceof BufferedImage) {
        return (BufferedImage)img;  
    }   
    ColorModel cm = img.getColorModel();
    int width = img.getWidth();
    int height = img.getHeight();
    WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
    boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
    Hashtable<String, Object> properties = new Hashtable<String, Object>();
    String[] keys = img.getPropertyNames();
    if (keys!=null) {
        for (int i = 0; i < keys.length; i++) {
            properties.put(keys[i], img.getProperty(keys[i]));
        }
    }
    BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
    img.copyData(raster);
    return result;
}

/** Draws everything on top of the scaled image
 * @param scale The current scale value
 */
private void setScaledImage(double scale) 
{
    //Optimizes the image type for drawing onto the screen
    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice device = env.getDefaultScreenDevice();
    GraphicsConfiguration config = device.getDefaultConfiguration();

    int w = (int)(scale*currImage.getWidth());
    int h = (int)(scale*currImage.getHeight());
    scaled = config.createCompatibleImage(w, h, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = scaled.createGraphics();
    g2.drawImage(currImage, 0, 0, w, h, null);
    g2.dispose();
}

Я загрузил многослойный TIFF в imageDecoder с JAI, и для отображения правильного слоя при перетаскивании ползунка я использую следующий код:

try {
    currImage = convertRenderedImage(imageStack.decodeAsRenderedImage(currentLayerSlider.getValue()-1));
    setScaledImage (scaleSlider.getValue()/100.0);
    curves.changeFrame(currentLayerSlider.getValue());
    drawImageOverlay ();} 
catch (IOException e) {
    e.printStackTrace();
    currImage = null;}

Обычно, когда перетаскивается ползунок, я извлекаю визуализированное изображение из imageStack, преобразую его в BufferedImage (чтобы его можно было использовать с ImageIcon), масштабирую его и устанавливаю в качестве отображаемого изображения. Я думаю, что медленная часть преобразует его в буферизованное изображение и масштабирует его. Есть ли способ сделать это быстрее?

Есть ли у кого-нибудь какие-либо предложения о том, как улучшить положение вещей или понять, как это происходит?

Любая помощь будет высоко ценится.

Ответы [ 4 ]

3 голосов
/ 19 марта 2013

Я столкнулся с точно такой же проблемой и в итоге использовал decodeAsRaster() для перехода к методу конвертации:

public static BufferedImage convertRenderedImage(Raster raster) {
    int w = raster.getWidth();
    int h = raster.getHeight();
    BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    int[] sourcePixels = ((DataBufferInt)raster.getDataBuffer()).getData();
    int[] destPixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
    System.arraycopy(pixels, 0, rasterData, 0, pixels.length); // The speed-up!
    return image;
}

Затем измените свой код на:

currImage = convertRenderedImage(imageStack.decodeAsRaster(currentLayerSlider.getValue()-1));

Кредит улучшения скорости должен быть возвращен @Brent Nash здесь : мы в основном получаем массив пикселей из исходного растра и массив пикселей для BufferedImage, чтобы заполнить и выполнить System.arraycopy вместо «дорогого» * ​​1011 *, который является более переносимым, но для каждого пикселя вызывает несколько методов для проверки согласованности и типа, в то время как у нас уже есть вся необходимая информация.

Этот код должен быть действительным для BufferedImage.TYPE_INT_RGB, хотя я должен признаться, что не смог его протестировать, но я знаю, что он работает на BufferedImage.TYPE_BYTE_GRAYbyte[] / DataBufferByte).

0 голосов
/ 27 сентября 2018

// может обработать вывод с помощью приведенного ниже кода, защищенного void doGet (запрос HttpServletRequest, ответ HttpServletResponse), генерирует ServletException, IOException {// TODO Автоматически сгенерированный заглушка метода System.out.println ("inside GET ...");String pageNumb = request.getParameter ("page");System.out.println ("pageNumb ::" + pageNumb);String filePath = "/Users/test/Downloads/Multi_page24bpp.tif";

    SeekableStream s = new FileSeekableStream(filePath.trim());
    TIFFDecodeParam param = null;
    ImageDecoder dec = ImageCodec.createImageDecoder("tiff", s, param);
    String totNumberOfPage =String.valueOf(dec.getNumPages());
    System.out.println("number of pages:: "+totNumberOfPage);
    RenderedImage op = dec.decodeAsRenderedImage(Integer.parseInt(pageNumb)); 
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ImageIO.write (op, "jpeg", baos);
//    response = ServletActionContext.getResponse();
    response.setContentType("image/jpeg");
    OutputStream out = response.getOutputStream();
    out.write(baos.toByteArray());
    out.flush();
}
0 голосов
/ 04 февраля 2014

вы можете изменить ваше визуализированное изображение в буфер и затем отобразить его в виде метки j, потому что визуализированное изображение - это изображение jai, а метка j берет только буферное изображение и не пытается отобразить его в jai, попробуйте в jLabel

0 голосов
/ 14 марта 2012

Вместо использования BufferedImage и ImageIcon для JLabel, вы пытались использовать DisplayJAI? он может отображать RenderedImage напрямую.

...