今天在Leetcode上看到一个旋转图片的算法,想出几种解决方案之后,便将其实际应用于真实图片的操作,在此将过程分享出来,希望对你有所帮助!

本篇博客只涉及90度及其倍数的角度旋转。


0x01.图片旋转算法概述

  • 给定一个 n × n 的二维矩阵表示一个图像,将图像顺时针旋转 90 度。
  • 例如:
    给定 matrix =
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

使其变为:

[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

0x02.图片旋转算法的三种思路

  • 算法的要求简单理解就是,最上面第一行变成最右边第一列,最上面第二行变成最右边第二列,以此类推。

思路一:引入中间矩阵

  • 这种算法是最容易想到的,只需要引入一个中间矩阵,临时存储每次旋转后的结果即可。
  • 具体每一个元素转换的关系式举个例子就可以发现。
  • 实现代码:
public void rotate(int[][] matrix) {
        int n = matrix.length;
        int[][] matrix_new = new int[n][n];
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix_new[j][n - i - 1] = matrix[i][j];
            }
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix[i][j] = matrix_new[i][j];
            }
        }
    }

思路二:原地旋转-四个元素轮换

  • 这一思路来源是:如果不适用辅助的矩阵,那么在原地操作一个元素后,必然会覆盖旋转后替代的第二个元素,要想这个元素不被能保留下来,最好的办法是同时对第二个元素也进行旋转,以此类推,完成一次循环,用临时变量保存中间的值,这样旋转一次就没有造成元素的丢失。
  • 具体每一个元素转换的关系式举个例子就可以发现。
  • 实现代码:
public void rotate2(int[][] matrix) {
        int n = matrix.length;
        for (int i = 0; i < n / 2; ++i) {
            for (int j = 0; j < (n + 1) / 2; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - j - 1][i];
                matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
                matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
                matrix[j][n - i - 1] = temp;
            }
        }
    }

思路三:原地旋转-两次翻转

  • 这应该是最妙的一种方法,对矩阵进行水平轴翻转,再进行主对角线翻转,即可得到旋转后的矩阵。
  • 具体的数学证明略。
  • 实现代码:
public void rotate(int[][] matrix) {
        int n = matrix.length;
        // 水平翻转
        for (int i = 0; i < n / 2; ++i) {
            for (int j = 0; j < n; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - i - 1][j];
                matrix[n - i - 1][j] = temp;
            }
        }
        // 主对角线翻转
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
    }

0x03.实际应用

  • 使用javax.imageio.ImageIO;, java.awt.image.BufferedImage;完成对图片的操作。
  • 具体代码:(采用第三种思路)
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Rotate {

    //输入图片路径
    private static final String INPUT_FILE_PATH="D:\\ATFWUSOSO\\AT-淡蓝简约AT.png";
    //输出图片路径
    private static final String OUTPUT_FILE_PATH="new.png";
    //图片格式
    private static final String IMAGE_TYPE="PNG";
    private static int width;
    private static int height;
    //操作二维数组
    private static int[][] newBufImage;
    //bufImage导出/写入的rgb一维数组
    private static int[] rgbs;

    //一维数组转换成二维数组
    private static void oneToTwo(){
        int k=0;
        for(int i=0;i<width;i++){
            for(int j=0;j<height;j++){
                newBufImage[i][j]=rgbs[k++];
            }
        }
    }

    //二维数组转换成一维数组
    private static void twoToOne(){
        int k=0;
        for(int i=0;i<width;i++){
            for(int j=0;j<height;j++){
                rgbs[k++]=newBufImage[i][j];
            }
        }
    }

    public static void rotate(int[][] matrix) {
        int n = matrix.length;
        // 水平翻转
        for (int i = 0; i < n / 2; ++i) {
            for (int j = 0; j < n; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - i - 1][j];
                matrix[n - i - 1][j] = temp;
            }
        }
        // 主对角线翻转
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
    }

    public static void main(String[] args) throws IOException{
        //读取图片
        BufferedImage bufImage = ImageIO.read(new File(INPUT_FILE_PATH));

        //获取宽高
        width=bufImage.getWidth();
        height=bufImage.getHeight();

        //获取rgb数组
        rgbs=bufImage.getRGB(0,0,width,height,null,0,width);
        newBufImage=new int[width][height];

        //转换成二维数组执行算法
        oneToTwo();
        rotate(newBufImage);
        twoToOne();

        // 把水平镜像后的像素矩阵设置回 bufImage
        bufImage.setRGB(0, 0, width, height, rgbs, 0, width);

        // 把修改过的 bufImage 保存到本地
        ImageIO.write(bufImage, IMAGE_TYPE, new File(OUTPUT_FILE_PATH));

    }

    public static void rotate1(int[][] matrix) {
        int n = matrix.length;
        int[][] matrix_new = new int[n][n];
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix_new[j][n - i - 1] = matrix[i][j];
            }
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix[i][j] = matrix_new[i][j];
            }
        }
    }

    public void static rotate2(int[][] matrix) {
        int n = matrix.length;
        for (int i = 0; i < n / 2; ++i) {
            for (int j = 0; j < (n + 1) / 2; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - j - 1][i];
                matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
                matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
                matrix[j][n - i - 1] = temp;
            }
        }
    }

}
  • 说明:
  • 为了直接使用上述算法,所以进行了一维数组和二维数组的相互转换
  • 可以执行多次达到旋转多次的效果,不过最好使用不同的算法。