2010-02-13 6 views
10

J'ai des problèmes de rotation des images en Java à l'aide de la classe AffineTransform.Problèmes lors de la rotation de BufferedImage

I ont la méthode suivante pour créer une rotation (90 degrés) copie d'une image:

private BufferedImage createRotatedCopy(BufferedImage img, Rotation rotation) { 
    int w = img.getWidth(); 
    int h = img.getHeight(); 

    BufferedImage rot = new BufferedImage(h, w, BufferedImage.TYPE_INT_RGB); 

    double theta; 
    switch (rotation) { 
     case CLOCKWISE: 
      theta = Math.PI/2; 
      break; 
     case COUNTERCLOCKWISE: 
      theta = -Math.PI/2; 
      break; 
     default: 
      throw new AssertionError(); 
    } 

    AffineTransform xform = AffineTransform.getRotateInstance(theta, w/2, h/2); 
    Graphics2D g = (Graphics2D) rot.createGraphics(); 
    g.drawImage(img, xform, null); 
    g.dispose(); 

    return rot; 
} 

rotation est un simple enum avec les valeurs NONE, sens horaire et antihoraire.

Les symptômes de mes problèmes sont affichés ici:

http://perp.se/so/rotate_problems.html

Ainsi, la rotation fonctionne bien, mais les images qui en résultent ne sont pas ancrés aux coordonnées correctes (ou comment on doit le mettre). Et puisque je ne sais pas vraiment ce que je fais en premier lieu (mon algèbre linéaire est faible), je ne sais pas comment résoudre cela par moi-même.

J'ai essayé avec un peu de bidouillage aléatoire avec l'instance AffineTransform, mais cela ne m'a pas aidé (bien sûr). J'ai essayé de googler (et de chercher SO), mais tous les exemples que j'ai vus utilisent essentiellement la même approche que moi ... ce qui ne fonctionne pas pour moi.

Reconnaissant pour le conseil.

+1

question équivalente pour .NET: http://stackoverflow.com/questions/2225363/c-rotate-bitmap-90-degrees – finnw

Répondre

16

Si vous devez exprimer la transformation en une seule rotation, le point d'ancrage dépend du sens de rotation: Soit (w/2, w/2) ou (h/2, h/2).

Mais il est probablement plus simple d'exprimer translate; rotate; translate, par ex.

AffineTransform xform = new AffineTransform(); 
xform.translate(0.5*h, 0.5*w); 
xform.rotate(theta); 
xform.translate(-0.5*w, -0.5*h); 

Pensez également à utiliser getQuadrantRotateInstance au lieu de getRotateInstance.

+0

Je comprends ce que vous dites, et j'ai aussi essayé. Le problème persiste cependant.Ils sont toujours attirés "en dehors" de la zone cible, bien que de "l'autre côté" horizontalement, pour ainsi dire. Je pourrais fournir d'autres captures d'écran si je ne comprends pas très bien ce que je veux dire. – perp

+0

@perp, vous avez raison, corrigé. J'ai testé la nouvelle version et cela fonctionne. – finnw

+0

Fonctionne comme un charme! Je pense que je comprends mieux ce qui se passe maintenant. Merci! – perp

0

Je ne sais pas si cela pourrait être votre problème.

AffineTransform xform = AffineTransform.getRotateInstance(theta, w/2, h/2); 

Pourquoi ne pas essayer?

AffineTransform xform = AffineTransform.getRotateInstance(theta); 

OU

g.transform(AffineTransform.getRotateInstance(theta)); 
g.drawImage(img, 0, 0, w/2, h/2, null, null); 
+0

Je l'ai essayé. :-) – perp

1

Vous pouvez essayer une autre approche et créer une icône à partir de l'image, puis utiliser un Rotated Icon.

Ou vous pouvez essayer ce vieux code que je trouve dans les forums Sun: rotation

import java.awt.*; 
import java.awt.geom.*; 
import java.awt.image.*; 
import java.io.*; 
import java.net.*; 
import javax.imageio.*; 
import javax.swing.*; 

public class RotateImage { 
    public static void main(String[] args) throws IOException { 
     URL url = new URL("https://blogs.oracle.com/jag/resource/JagHeadshot-small.jpg"); 
     BufferedImage original = ImageIO.read(url); 
     GraphicsConfiguration gc = getDefaultConfiguration(); 
     BufferedImage rotated1 = tilt(original, -Math.PI/2, gc); 
     BufferedImage rotated2 = tilt(original, +Math.PI/4, gc); 
     BufferedImage rotated3 = tilt(original, Math.PI, gc); 
     display(original, rotated1, rotated2, rotated3); 
    } 

    public static BufferedImage tilt(BufferedImage image, double angle, GraphicsConfiguration gc) { 
     double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle)); 
     int w = image.getWidth(), h = image.getHeight(); 
     int neww = (int)Math.floor(w*cos+h*sin), newh = (int)Math.floor(h*cos+w*sin); 
     int transparency = image.getColorModel().getTransparency(); 
     BufferedImage result = gc.createCompatibleImage(neww, newh, transparency); 
     Graphics2D g = result.createGraphics(); 
     g.translate((neww-w)/2, (newh-h)/2); 
     g.rotate(angle, w/2, h/2); 
     g.drawRenderedImage(image, null); 
     return result; 
    } 

    public static GraphicsConfiguration getDefaultConfiguration() { 
     GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 
     GraphicsDevice gd = ge.getDefaultScreenDevice(); 
     return gd.getDefaultConfiguration(); 
    } 

    public static void display(BufferedImage im1, BufferedImage im2, BufferedImage im3, BufferedImage im4) { 
     JPanel cp = new JPanel(new GridLayout(2,2)); 
     addImage(cp, im1, "original"); 
     addImage(cp, im2, "rotate -PI/2"); 
     addImage(cp, im3, "rotate +PI/4"); 
     addImage(cp, im4, "rotate PI"); 

     JFrame f = new JFrame("RotateImage"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setContentPane(cp); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 

    static void addImage(Container cp, BufferedImage im, String title) { 
     JLabel lbl = new JLabel(new ImageIcon(im)); 
     lbl.setBorder(BorderFactory.createTitledBorder(title)); 
     cp.add(lbl); 
    } 
} 
3

Puisque vous avez seulement besoin de 90 degrés vous pouvez éviter d'utiliser les trucs AffineTransform:

public BufferedImage rotate90DX(BufferedImage bi) { 
    int width = bi.getWidth(); 
    int height = bi.getHeight(); 
    BufferedImage biFlip = new BufferedImage(height, width, bi.getType()); 
    for(int i=0; i<width; i++) 
     for(int j=0; j<height; j++) 
      biFlip.setRGB(height-1-j, width-1-i, bi.getRGB(i, j)); 
    return biFlip; 
} 

Cela évite aussi découper les bords des images rectangulaires.

De: http://snippets.dzone.com/posts/show/2936

+3

Juste un heads-up, forçant un accès direct aux valeurs de pixels RVB prend l'image hors du pipeline accéléré dans Java2D. Cette approche peut vous donner le résultat que vous voulez, mais c'est beaucoup plus lent. –

+0

Aha. Je ne le savais pas. Merci –

+0

J'ai testé ce code et il ne fait pas simplement tourner l'image, il change aussi à gauche et à droite (comme dans un miroir) – Radon8472

Questions connexes