数学原理
二维图片上每一个点,左乘以一个转换矩阵,就可以实现图片绕原点的旋转和拉伸。而这个转换矩阵,就是(1,0)和(0,1)转换后的点组成的。这个其实就是线性变换的思想了,一个线性变换把自然基变成什么样子,就会把其他向量变成什么样子。
说了这么多,那不得来个例子啊,假设我们要绕原点顺时针旋转90°。首先(1,0)绕原点顺时针旋转90°后变成什么了呢?变成了(0,-1),而(0,1)呢,变成了(1,0)。所以这个变换矩阵为:
[
0
1
−
1
0
]
\left[ \begin{matrix} 0 & 1\\ -1 & 0 \end{matrix} \right]
[0−110]
假设将图形上的一个点,坐标为(1,1),转换后的点,用线性代数计算一下:
[
0
1
−
1
0
]
×
[
1
1
]
=
[
1
−
1
]
\left[ \begin{matrix} 0 & 1\\ -1 & 0 \end{matrix} \right]\times \left[ \begin{matrix} 1\\ 1 \end{matrix} \right]=\left[ \begin{matrix} 1\\ -1 \end{matrix} \right]
[0−110]×[11]=[1−1]
这两个点用图像表示就是下面这个样子:
但是这个旋转拉伸操作,是绕着原点进行的。而实际应用图片的旋转,是两个运算,第一个运算是绕着原点的旋转,第二个是图形的平移。可以这样理解,矩阵乘法顺时针旋转90°,其实是绕着原点,把图像旋转到了第三象限,然后只有往上平移才能把图片拉回到第一象限。图形的旋转可以用矩阵与向量的乘法进行计算,图像的平移就使用向量的加法进行计算。
java实现
Java转换代码如下:
public class ImageConvertor {
private Matrix<Integer> matrix;
private Integer[] moveVector;
public ImageConvertor(Matrix<Integer> matrix, Integer[] moveVector) {
this.matrix = matrix;
this.moveVector = moveVector;
}
public BufferedImage convert(BufferedImage source) {
final int width = source.getWidth();
final int height = source.getHeight();
final BufferedImage target = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
final Graphics2D graphics = (Graphics2D) target.getGraphics();
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
final int rgb = source.getRGB(i, j);
// 运用矩阵乘法,算出这个点在哪里
final int sx = i;
final int sy = j;
final Integer[][] integers = matrix.multiply(new IntegerMatrix(new Integer[][]{{sx}, {sy}}));
graphics.setColor(new Color(rgb));
final int x = integers[0][0] + moveVector[0];
final int y = integers[1][0] + moveVector[1];
graphics.drawLine(x, y, x, y);
System.out.println("source:(" + sx + "," + sy + "),target:(" + x + "," + y + ")," + rgb);
}
}
return target;
}
}
测试类如下:
@Test
public void test() throws IOException {
// 顺时针旋转90°,100X100图片
final Integer[][] array = {{0, -1}, {1, 0}};
final IntegerMatrix matrix = new IntegerMatrix(array);
final ImageConvertor imageConvertor = new ImageConvertor(matrix, new Integer[]{99, 0});
final BufferedImage image = ImageIO.read(new File("e.png"));
final BufferedImage target = imageConvertor.convert(image);
ImageIO.write(target, "png", new File("out.png"));
}
效果如下:
Python实现
Python的图形库是opencv-python
pip install opencv-python
代码和Java类似
import numpy as np
from com.youngthing.mathalgorithm.matrix import Matrix
class ImageConvertor:
def __init__(self, matrix: Matrix, move_vector: list):
self.__matrix = matrix
self.__move_vector = move_vector
def convert(self, image):
width = len(image[0])
height = len(image)
target = np.zeros((width, height, 3), np.uint8)
for (y, line) in enumerate(image):
for (x, color) in enumerate(line):
m = Matrix([[x, y]]) * self.__matrix
d = m + self.__move_vector
print(d.lines[0][1], d.lines[0][0], np.uint8(color), color)
target[d.lines[0][1]][d.lines[0][0]] = np.uint8(color)
return target
测试类代码
from com.youngthing.mathalgorithm.matrix import Matrix
import cv2
from com.youngthing.mathalgorithm.image.image_process import ImageConvertor
if __name__ == '__main__':
img = cv2.imread('F:\learn\math-algorithm\e.png')
rotate_matrix = Matrix([[0, 1], [-1, 0]])
move_matrix = Matrix([[99, 0]])
ic = ImageConvertor(rotate_matrix, move_matrix)
new_img = ic.convert(img)
cv2.imshow('img', new_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
执行效果