Проблемы с вращением BufferedImage

У меня проблемы с поворотом изображений в Java с использованием класса AffineTransform.

У меня есть следующий метод создания повернутой (на 90 градусов) копии изображения:

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;
}

Вращение - это простое перечисление со значениями NONE, CLOCKWISE и COUNTERCLOCKWISE.

Здесь отображаются симптомы моих проблем:

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

Итак, вращение работает нормально, но полученные изображения не привязаны к правильным координатам (или как это следует выразить). И поскольку я вообще-то не знаю, что, черт возьми, делаю (моя линейная алгебра слабая), я не знаю, как решить эту проблему самостоятельно.

Я пробовал случайным образом возиться с экземпляром AffineTransform, но это мне не помогло (конечно). Я пробовал гуглить (и искать SO), но все примеры, которые я видел, в основном используют тот же подход, что и я ... который для меня не работает.

Благодарен за совет.


person perp    schedule 13.02.2010    source источник
comment
Эквивалентный вопрос для .NET: stackoverflow.com/questions/2225363 / c-rotate-bitmap-90-градусов   -  person finnw    schedule 13.02.2010


Ответы (4)


Если вы должны выразить преобразование как одно вращение, точка привязки зависит от направления вращения: либо (w/2, w/2), либо (h/2, h/2).

Но, вероятно, проще выразить как translate; rotate; translate, например

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

Также рассмотрите возможность использования getQuadrantRotateInstance вместо getRotateInstance.

person finnw    schedule 13.02.2010
comment
Я понимаю, о чем вы говорите, и это тоже пробовал. Однако проблема не устранена. Их все еще рисуют за пределами целевой области, хотя, так сказать, с другой стороны по горизонтали. Я мог бы предоставить еще несколько скриншотов, если непонятно, к чему я клоню. - person perp; 13.02.2010
comment
@perp, ты прав, исправлено. Я протестировал новую версию, она работает. - person finnw; 13.02.2010
comment
Работает как шарм! Думаю, я тоже лучше понимаю, что происходит сейчас. Спасибо! - person perp; 13.02.2010
comment
Если это может кому-то помочь: после применения этого решения мое изображение все еще было частично обрезано, но только по одной оси. Проблема была не в преобразовании, а в том, что я использовал неправильный тип изображения. Так что подумайте и об этом :) - person ARRG; 07.08.2014

Поскольку вам нужно вращение только на 90 градусов, вы можете избежать использования 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;
}

Это также позволяет избежать обрезки краев прямоугольных изображений.

От: http://snippets.dzone.com/posts/show/2936

person David Tinker    schedule 07.07.2011
comment
Напоминаем, что принудительный прямой доступ к значениям пикселей RGB выводит изображение из ускоренного конвейера в Java2D. Такой подход может дать вам желаемый результат, но он намного медленнее. - person Riyad Kalla; 03.08.2011
comment
Ага. Не знал этого. Спасибо - person David Tinker; 03.08.2011
comment
Я протестировал этот код, и он не просто вращает изображение, он также меняет влево и вправо (как в зеркале) - person Radon8472; 11.08.2015

Вы можете попробовать альтернативный appoach и создать значок из изображения, а затем использовать Значок поворота.

Или вы можете попробовать этот старый код, который я нашел на форумах Sun:

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);
    }
}
person camickr    schedule 13.02.2010

Я не знаю, может ли это быть вашей проблемой.

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

Почему бы не попробовать?

AffineTransform xform = AffineTransform.getRotateInstance(theta);

OR

g.transform(AffineTransform.getRotateInstance(theta));
g.drawImage(img, 0, 0, w/2, h/2, null, null);
person Buhake Sindi    schedule 13.02.2010