3

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

我编写了自己的机制来放置和扭曲图像,使用放置在图像上的三个点,放置在地图上的三个点,然后我简单地创建一个 AffineTransform 将第一个三角形转换为第二个,效果将图像准确放置在我想要的位置并转换为适合用户想要的位置。

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

我做了一些研究,发现了 JAI 的透视变换,在 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) 方法的东西。或者,如果您有一个执行速度非常快的第三方图像转换库,那也将受到欢迎。

4

1 回答 1

2

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

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

如果您想要真正流畅、正确、抗锯齿的预览,我建议您使用 Java3D o JOGL 之类的 3D 库。

于 2010-03-25T01:55:38.720 回答