Java Advanced Imaging API中的Fast Perspective转换

为了我的程序的需要,我创建了一个设备来扭曲图像并将其放在地图上(我的程序是基于地图的程序)。

我编写了自己的机制,使用放置在图像上的三个点放置和扭曲图像,放置在地图上的三个点,然后我只是创建一个AffineTransform,将第一个三角形转换为第二个,实际上将图像准确放置在我想要的位置并进行转换以适合用户的需求。

问题是,使用AffineTransforms,您只能执行最基本的转换。 您可以翻译,旋转,缩放和倾斜图像。 这对于大多数情况来说已经足够了,但我最近想要实现类似于Photoshop的自由变换的4点变换。

我做了一些研究,发现了JAI的Perspective变换,在WarpPerspective的帮助下,我可以实现我想要的。 我最初有一个问题,即变换图像的结果背景是透明的,但我也找到了解决方案。 这是我的代码:

package com.hampton.utils; import java.awt.Dimension; import java.awt.RenderingHints; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import javax.media.jai.ImageLayout; import javax.media.jai.Interpolation; import javax.media.jai.JAI; import javax.media.jai.PerspectiveTransform; import javax.media.jai.PlanarImage; import javax.media.jai.RasterFactory; import javax.media.jai.RenderedOp; import javax.media.jai.WarpPerspective; import javax.media.jai.operator.CompositeDescriptor; /** * @author Savvas Dalkitsis */ public class PerspectiveTransformation { public static PlanarImage getPrespective(PlanarImage img) { int numBands = img.getSampleModel().getNumBands(); ParameterBlock pb = new ParameterBlock(); pb.add(new Float(img.getWidth())).add(new Float(img.getHeight())); pb.add(new Byte[]{new Byte((byte)0xFF)}); RenderedOp alpha = JAI.create("constant", pb); pb = new ParameterBlock(); pb.addSource(img).addSource(img); pb.add(alpha).add(alpha).add(Boolean.FALSE); pb.add(CompositeDescriptor.DESTINATION_ALPHA_LAST); SampleModel sm = RasterFactory.createComponentSampleModel(img.getSampleModel(), DataBuffer.TYPE_BYTE, img.getTileWidth(), img.getTileHeight(), numBands + 1); ColorSpace cs = ColorSpace.getInstance(numBands == 1 ? ColorSpace.CS_GRAY : ColorSpace.CS_sRGB); ColorModel cm = RasterFactory.createComponentColorModel(DataBuffer.TYPE_BYTE, cs, true, false, Transparency.BITMASK); ImageLayout il = new ImageLayout(); il.setSampleModel(sm).setColorModel(cm); RenderingHints rh = new RenderingHints(JAI.KEY_IMAGE_LAYOUT, il); RenderedOp srca = JAI.create("composite", pb, rh); Dimension d = new Dimension(img.getWidth(),img.getHeight()); PerspectiveTransform p = PerspectiveTransform.getQuadToQuad(0, 0, d.width, 0, d.width, d.height, 0, d.height, 100, 0, 300, 0, d.width, d.height, 0, d.height); WarpPerspective wp = new WarpPerspective(p); pb = (new ParameterBlock()).addSource(srca); pb.add(wp); pb.add(Interpolation.getInstance(Interpolation.INTERP_BICUBIC)); PlanarImage i = (PlanarImage)JAI.create("warp",pb); return i; } } 

它的工作原理(为了简单起见,我对变换进行了硬编码,之后我将它与我的变换系统结合起来,这次我将使用4个点而不是3个点)。

我遇到的问题是,到目前为止,我的旧方法只需要存储一个AffineTransform(将图像映射到地图),然后简单地执行g2.drawImage(img,transform),一切都很好。 这真的很快,它就像一个魅力。 我现在的问题是创建转换的PlanarImage需要花费很多时间(2-3秒),因此对实时使用没有用。 我知道我可以简单地保存变换后的图像,然后绘制真正快速的图像,但我想要的原因是在我的3点变换机制中,我提供了所得到的变换的实时视图。 当用户拖动每个点时,他立即看到结果图像。

那么我的问题是,有没有一种快速的方法在Graphics2D中使用WarpPerspectives? 我正在寻找的东西类似于Graphics2D draw(Image,AffineTransform)方法。 或者,如果您有第三方图像转换库,其执行速度非常快,也欢迎使用。

尝试关闭双三次插值 – 这应该可以节省一些时间,但会产生较低质量的结果。

在3D加速普及之前,游戏使用智能技巧以透视方式绘制图像。 维基百科有一篇很好的文章详细解释了这一点。 我不知道使用这些概念的基于Java的库,但是你可以很容易地实现仿射映射版本 – 只需将原始图像分成几个三角形,并使用不同的仿射变换映射每个三角形。

如果你想要非常流畅,正确,抗锯齿的预览,我建议使用像Java3D或JOGL这样的3D库。