使用 AffineTransform 旋转图像

Posted

技术标签:

【中文标题】使用 AffineTransform 旋转图像【英文标题】:Rotating Image with AffineTransform 【发布时间】:2013-12-15 00:57:50 【问题描述】:

我有一个名为Airplane 的课程。在这个类里面我有变量 img 这是一个 BufferedImage 类型。更重要的是我得到了类WorldMap,它覆盖了函数paintComponent(Graphics g)

@Override
public void paintComponent(Graphics g) 
    Graphics2D g2d = (Graphics2D) g;
    g2d.drawImage(mapa, 0, 0, getWidth(), getHeight(), null); 
    drawAirplanes(g2d);

函数drawAirplanes() 如下所示:

private void drawAirplane(Graphics2D g)
    for(Samolot i: s)
        i.rotateAirplane();
        g.drawImage(i.getImg(),i.getX(),i.getY(),(int)i.getDim().getWidth(),(int)i.getDim().getHeight(),  null);
    

它只需要 1) 旋转飞机 (BufferedImage inside Airplane 对象) 2) 画他。

我的 Airplane.rotateAirplane() 函数如下所示:

 public void rotateSamolot() 
   AffineTransform tx = new AffineTransform();

   tx.translate(10,10); //10, 10 is height and width of img divide by 2
   tx.rotate(Math.PI / 2);
   tx.translate(-10,-10); 

   AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);

   BufferedImage newImage =new BufferedImage(20, 20, img.getType()); //20, 20 is a height and width of img ofc
   op.filter(img, newImage);

       this.img = newImage;
 

ofc 当我运行我的程序时,只绘制了mapa 对象。当我删除这条车道时

this.img = newImage;

我的飞机也有,但没有旋转。

【问题讨论】:

我不确定您是否要翻译 AffineTransform,您应该改为使用 rotate 方法提供旋转点,该方法通常是中心点,但这取决于您。我也不会将旋转的结果分配回主图像,否则你会复合旋转...... 我“认为”基本上发生了什么,是您的图像被旋转“超出”可用空间的范围...... 你可以看看this example 我忘记保存原始图像 - 这是真的。但仍然不明白我如何更正我的代码以不将其旋转到我的空间范围之外。有任何想法吗?我将在几秒钟后查看该链接 问了 15 个问题。没有一个答案被接受。你显然不欣赏你在问题上得到的帮助,所以我想我会跳过这个。 【参考方案1】:

主要问题(我可以看到)是 Graphics 上下文的翻译,它偏移了旋转将发生的位置。

默认情况下,我“认为”旋转发生在 Graphics 上下文的左上角(0x0 位置所在的位置,您已将其转换为其他内容),这可能会导致图像被旋转出框架(或可视区域)

你应该提供一个旋转发生的“锚”点,通常,中心是我个人的偏好。

以下示例仅包含一个主图像(由于尺寸限制,我不得不对其进行缩放,但您可能不需要它)。然后我使用它来生成一个“旋转”实例,该实例的大小可以让图像适合其中。这对 trig 来说很有趣 - 我从某个地方偷了代码,所以感谢那个开发人员。

该示例允许您单击任何位置,它将更改旋转轴心,因此您可以看到发生了什么。默认位置是窗格的中心...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class SampleRotation 

    public static void main(String[] args) 
        new SampleRotation();
    

    public SampleRotation() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) 
                    ex.printStackTrace();
                

                final RotationPane rotationPane = new RotationPane();
                final JSlider slider = new JSlider(0, 100);
                slider.addChangeListener(new ChangeListener() 
                    @Override
                    public void stateChanged(ChangeEvent e) 
                        double angle = 720d * (slider.getValue() / 100d);
                        rotationPane.setAngle(angle);
                    
                );
                slider.setValue(0);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(rotationPane);
                frame.add(slider, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            
        );
    

    public class RotationPane extends JPanel 

        private BufferedImage img;
        private BufferedImage rotated;
        private double angle;
        private Point clickPoint;

        public RotationPane() 
            try 
                img = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/issue459.jpg"));
                BufferedImage scaled = new BufferedImage(img.getWidth() / 2, img.getHeight() / 2, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = scaled.createGraphics();
                g2d.setTransform(AffineTransform.getScaleInstance(0.5d, 0.5d));
                g2d.drawImage(img, 0, 0, this);
                g2d.dispose();
                img = scaled;
                setAngle(0d);
             catch (IOException ex) 
                ex.printStackTrace();
            

            addMouseListener(new MouseAdapter() 

                @Override
                public void mouseClicked(MouseEvent e) 
                    clickPoint = e.getPoint();
                    repaint();
                

            );

        

        public void setAngle(double angle) 
            this.angle = angle;

            double rads = Math.toRadians(getAngle());
            double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
            int w = img.getWidth();
            int h = img.getHeight();
            int newWidth = (int) Math.floor(w * cos + h * sin);
            int newHeight = (int) Math.floor(h * cos + w * sin);

            rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = rotated.createGraphics();
            AffineTransform at = new AffineTransform();
            at.translate((newWidth - w) / 2, (newHeight - h) / 2);

            int x = clickPoint == null ? w / 2 : clickPoint.x;
            int y = clickPoint == null ? h / 2 : clickPoint.y;

            at.rotate(rads, x, y);
            g2d.setTransform(at);
            g2d.drawImage(img, 0, 0, this);
            g2d.setColor(Color.RED);
            g2d.drawRect(0, 0, newWidth - 1, newHeight - 1);
            g2d.dispose();

            repaint();
        

        public double getAngle() 
            return angle;
        

        @Override
        public Dimension getPreferredSize() 
            return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(this), img.getHeight(this));
        

        @Override
        protected void paintComponent(Graphics g) 
            super.paintComponent(g);
            if (rotated != null) 
                Graphics2D g2d = (Graphics2D) g.create();

                int x = (getWidth() - rotated.getWidth()) / 2;
                int y = (getHeight() - rotated.getHeight()) / 2;
                g2d.drawImage(rotated, x, y, this);

                g2d.setColor(Color.RED);

                x = clickPoint == null ? getWidth() / 2 : clickPoint.x;
                y = clickPoint == null ? getHeight()/ 2 : clickPoint.y;

                x -= 5;
                y -= 5;

                g2d.drawOval(x, y, 10, 10);
                g2d.dispose();
            
                
        

【讨论】:

【参考方案2】:

当您已经进行了旋转时,您不能简单地翻译回来。您可以做的是:通过 Graphics g2 = g.create() 创建一个新的 Graphics 对象,在 g2 上进行平移/旋转,然后将 g2 扔掉。这样它不会影响原始的 Graphics g 状态。

【讨论】:

【参考方案3】:

这对我有用(有点从这里和那里复制):

public BufferedImage rotateImag (BufferedImage imag, int n)  //n rotation in gradians

        double rotationRequired = Math.toRadians (n);
        double locationX = imag.getWidth() / 2;
        double locationY = imag.getHeight() / 2;
        AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
        AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);         
       BufferedImage newImage =new BufferedImage(imag.getWidth(), imag.getHeight(), imag.getType()); 
       op.filter(imag, newImage);

           //this.img = newImage;
       return(newImage);
     

【讨论】:

以上是关于使用 AffineTransform 旋转图像的主要内容,如果未能解决你的问题,请参考以下文章

方阵原地顺时针旋转90度

围绕中心点旋转一个点

Unity基础-Transform

JAVA使用AffineTransform缩放字体

OpenCV中的「透视变换 / 投影变换 / 单应性」—cv.warpPerspectivecv.findHomography

e675. 翻转缓冲图像