今天在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;
}
}
}
}
- 说明:
- 为了直接使用上述算法,所以进行了一维数组和二维数组的相互转换
- 可以执行多次达到旋转多次的效果,不过最好使用不同的算法。