通过 JAI 在 Java 中进行 TIFF 渲染,性能更快
我一直在开发分析显微镜数据的软件,这些数据存储为多层 tiff 文件。在浏览了 StackOverflow 和 JAI 文档之后,我拼凑了一些代码来存储 tiff 堆栈并正确渲染它。
然而,它的性能却相当糟糕。在阅读以下帖子后,我希望能有更快的性能: 任何人都有运气写一个Java 中非常快的 tiff 查看器/编辑器?
不幸的是,它没有我希望的那么好。我没有太多使用外部图像库或 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();
}
我已使用 JAI 将多层 tiff 加载到 imageDecoder 中,并在拖动滑块时显示正确的图层,我使用以下代码:
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 一起使用),缩放它,并将其设置为显示的图像。我认为缓慢的部分是将其转换为缓冲图像并对其进行缩放。有什么办法可以更快地做到这一点吗?
有没有人对如何改进事情有任何建议,或者从类似的经历中获得见解?
任何帮助将不胜感激。
I have been working on software that analyzes microscopy data, which is stored as a multilayer tiff file. After looking through StackOverflow and through the JAI documentation, I hobbled together some code to store the tiff stack and render it correctly.
However, it suffers from some pretty bad performance. I had hoped for some faster performance after reading posts such as this:
Anyone have any luck writing a very fast tiff viewer/editor in Java?
Unfortunately, it's not nearly as good as I would have hoped for. I do not have much experience working with external image libraries, or with Java's Graphics capabilities for that matter, so I'm not sure how I would improve this.
For some context, here is a video of the 'stuttering' I experience while traversing through the tiff stack:
http://www.youtube.com/watch?v=WiR4o6TsqyM&feature=channel_video_title
Note how the frames stutter occasionally as the slider is dragged.
I improved performance when the image is zoomed in by removing smoothing when scaling, but it still isn't as fast I would have liked.
My code is below:
/** 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();
}
I've loaded the multilayer tiff into an imageDecoder with JAI, and to display the correct layer when the slider is dragged, I use the following code:
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;}
Basically, whenever the slider is dragged, I extract a rendered image from the imageStack, convert it to a BufferedImage (so that it can be used with an ImageIcon), scale it, and set it as the displayed image. I think the slow part is converting it to the buffered image, and scaling it. Is there any way to do this faster?
Does anyone have any suggestions for how to improve things, or insight from similar experiences?
Any help would be much appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我遇到了完全相同的问题,最终使用
decodeAsRaster()
传递给转换方法:然后将代码更改为:
速度改进的功劳将归还给@Brent Nash 此处:我们基本上从原始 Raster 中获取像素数组,以及用于 BufferedImage 填充和执行的像素数组一个 System.arraycopy 而不是“昂贵的”image.setData(raster) ,它更便携,但对于每个像素,调用一些方法来检查一致性和类型,虽然我们已经拥有了我们需要的所有信息。
此代码应该对 BufferedImage.TYPE_INT_RGB 有效,尽管我必须承认我无法测试它,但我知道它在 BufferedImage.TYPE_BYTE_GRAY 上工作(并且
byte[]
/DataBufferByte
)。I have run into the exact same problem and ended up using the
decodeAsRaster()
to pass to a convert method:Then change your code to:
The credit of speed improvement is to be given back to @Brent Nash here: we basically get the pixel array from the original Raster and the pixel array for the BufferedImage to fill up and perform a
System.arraycopy
instead of the "expensive"image.setData(raster)
which is more portable but, for each pixel, calls a few methods to check consistency and type, while we already have all the information we need.This code should be valid for the
BufferedImage.TYPE_INT_RGB
, though I must confess I was not able to test it, but I know it's working onBufferedImage.TYPE_BYTE_GRAY
(andbyte[]
/DataBufferByte
).您是否尝试过使用 DisplayJAI,而不是对 JLabel 使用 BufferedImage 和 ImageIcon?
它可以直接显示RenderedImage。
Instead of using BufferedImage and ImageIcon for JLabel, have you tried using DisplayJAI?
it can display RenderedImage directly.
您可以将渲染图像更改为缓冲区,然后将其显示在 j 标签中,因为渲染图像是 jai 图像,而 j 标签仅采用缓冲区图像,并且不要尝试在 jai 中显示它,您可以在 jLabel 中尝试
you can change your renderd image in to buffer and then display it in j label because renderd image is jai image and j label take onley buffer image and do not try to display it in jai you try it in jLabel
// 可以使用下面的代码渲染输出
protected void doGet(HttpServletRequest 请求, HttpServletResponse 响应) 抛出 ServletException, IOException {
// TODO 自动生成的方法存根
System.out.println("GET里面...");
String pageNumb = request.getParameter("page");
System.out.println("pageNumb::"+pageNumb);
字符串文件路径 =“/Users/test/Downloads/Multi_page24bpp.tif”;
// can render the output with the below code
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("inside GET ...");
String pageNumb = request.getParameter("page");
System.out.println("pageNumb:: "+pageNumb);
String filePath = "/Users/test/Downloads/Multi_page24bpp.tif";