java图像处理之实现任意角度图像旋转

原理及步骤:

1、旋转角度:图像顺时针或逆时针旋转的角度,以θ表示,需要用户输入;

2、旋转中心:一般以图像中心作为旋转中心,周围像素围绕其旋转;

3、画布大小:由于图像旋转后产生的图像宽和高与原始图像不同,需要先计算好旋转后的画布大小。由于一般处理的图像都是矩形阵列,不考虑特殊形状前提下,我们只需要计算原始图像四个角点坐标围绕中心旋转θ角度后的坐标,取X方向最大最小值之差作为画布宽,Y方向最大最小值之差作为画布高,以此生成一幅空图像;

java图像处理之实现任意角度图像旋转_第1张图片

4、像素赋值:得到画布图像之后,将画布坐标系与原始图像坐标系进行统一,即画布中心与原始图像中心都改正为(0,0)位置,将画布上每个像素进行θ角的反方向旋转,即旋转回原始图像位置,如果旋转后的像素落在原始图像坐标范围内,取该位置最近像素RGB值作为画布上对应像素RGB值;

5、空像素处理:由于旋转后,画布上可能有部分区域在原图没有对应范围,这部分像素为空像素,也可以叫做背景像素,这部分像素可以通过自定义背景色进行赋值。如果输出图片格式为jpg,由于Windows图片浏览工具背景色为白色,建议将背景色输出为白色;

6、背景色透明:如果需要将画布中空像素设置为透明,需要在初始化画布时将画布类型设置为TYPE_INT_ARGB,即png图像对应的格式,输出时也输出为png格式图像即可。

图像旋转java实现:

1、由于java中math三角函数都是以弧度进行计算的,需要将角度转换为弧度。

                                              double angle = theta * Math.PI / 180;

2、计算按照逆时针旋转θ角度后画布大小,即X和Y方向旋转后最大最小值。因为旋转为图像中心,由于中心对称,只需要计算对角线上其中一个坐标,对角线另一个取负即可,之后对四个值进行排序并返回排序后数组。取第一和第二象限两个角点进行计算,先计算原图第一象限角点相对坐标系弧度,第二象限相对坐标系弧度=π-第一象限弧度。

获取X方向旋转后坐标数组:先计算第一象限角点相对中心距离radius,计算该角点弧度angle1,通过反余弦计算得到,再通过计算原图本身弧度加上旋弧度angle后的余弦。

private double[] getX(int i, int j, double angle) {
		double results[] = new double[4];
		double radius = Math.sqrt(i * i + j * j);
		double angle1 = Math.acos(i / radius);
		results[0] = radius * Math.cos(angle1 + angle);
		results[1] = radius * Math.cos(Math.PI - angle1 + angle);
		results[2] = -results[0];
		results[3] = -results[1];
		Arrays.sort(results);
		return results;
	}

获取Y方向旋转后坐标数组:

private double[] getY(int i, int j, double angle) {
		double results[] = new double[4];
		double radius = Math.sqrt(i * i + j * j);
		double angle1 = Math.asin(j / radius);
		results[0] = radius * Math.sin(angle1 + angle);
		results[1] = radius * Math.sin(Math.PI - angle1 + angle);
		results[2] = -results[0];
		results[3] = -results[1];
		Arrays.sort(results);
		return results;
	}

3、根据X和Y方向最大最小值作为宽和高生成画布,画布类型根据需要进行设置,需要输出影像背景透明的png图像,类型设置为BufferedImage.TYPE_INT_ARGB,如果原始图像为png格式,也可以直接使用image.getType(),用原始图像格式进行赋值。

int WIDTH = (int) (xCoords[3] - xCoords[0]);
		int HEIGHT = (int) (yCoords[3] - yCoords[0]);
		BufferedImage resultImage = new BufferedImage(WIDTH, HEIGHT, 												BufferedImage.TYPE_INT_ARGB);

4、对画布每一个像素位置进行逐一计算,计算每个位置按照顺时针旋转θ角后位置,判断该位置是否在原图范围内,在原图范围内,则直接取原图上该位置rgb值对画布对应像素进行赋值,原图范围以外的,通过预先传入的背景色参数进行赋值。由于画布上像素相对坐标系弧度是通过反余弦函数计算的,反余弦取值为(0,π),对于第三四象限的像素,通过判断y坐标是否大于0,大于0则直接计算,小于0,说明该像素位于三四象限,通过计算2π减去反余弦后弧度得到该像素相对坐标系弧度。

for (int i = 0; i < WIDTH; i++) {
			for (int j = 0; j < HEIGHT; j++) {
				int x = i - WIDTH / 2;
				int y = HEIGHT / 2 - j;
				double radius = Math.sqrt(x * x + y * y);
				double angle1;
				if (y > 0) {
					angle1 = Math.acos(x / radius);
				} else {
					angle1 = 2 * Math.PI - Math.acos(x / radius);
				}
				x = (int) (radius * Math.cos(angle1 - angle));
				y = (int) (radius * Math.sin(angle1 - angle));
				if (x < (width / 2) & x > -(width / 2) & y < (height / 2) & y > -(height / 2)) {
					int rgb = image.getRGB(x + width / 2, height / 2 - y);
					resultImage.setRGB(i, j, rgb);
				}else {
					int rgb = ((0 & 0xff) << 24) | ((backgroundColor.getRed() & 0xff) << 16) | ((backgroundColor.getGreen() & 0xff) << 8)
							| ((backgroundColor.getBlue() & 0xff));
					resultImage.setRGB(i, j, rgb);
				}
			}
		}

5、完整的程序实现及测试

public class ImageRotate {

	/**
	 * 创建任意角度的旋转图像
	 * @param image
	 * @param theta
	 * @param backgroundColor
	 * @return
	 */
	public BufferedImage rotateImage(BufferedImage image, double theta,Color backgroundColor) {
		int width = image.getWidth();
		int height = image.getHeight();
		double angle = theta * Math.PI / 180; // 度转弧度
		double[] xCoords = getX(width / 2, height / 2, angle);
		double[] yCoords = getY(width / 2, height / 2, angle);
		int WIDTH = (int) (xCoords[3] - xCoords[0]);
		int HEIGHT = (int) (yCoords[3] - yCoords[0]);
		BufferedImage resultImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
		for (int i = 0; i < WIDTH; i++) {
			for (int j = 0; j < HEIGHT; j++) {
				int x = i - WIDTH / 2;
				int y = HEIGHT / 2 - j;
				double radius = Math.sqrt(x * x + y * y);
				double angle1;
				if (y > 0) {
					angle1 = Math.acos(x / radius);
				} else {
					angle1 = 2 * Math.PI - Math.acos(x / radius);
				}
				x = (int) (radius * Math.cos(angle1 - angle));
				y = (int) (radius * Math.sin(angle1 - angle));
				if (x < (width / 2) & x > -(width / 2) & y < (height / 2) & y > -(height / 2)) {
					int rgb = image.getRGB(x + width / 2, height / 2 - y);
					resultImage.setRGB(i, j, rgb);
				}else {
					int rgb = ((0 & 0xff) << 24) | ((backgroundColor.getRed() & 0xff) << 16) | ((backgroundColor.getGreen() & 0xff) << 8)
							| ((backgroundColor.getBlue() & 0xff));
					resultImage.setRGB(i, j, rgb);
				}
			}
		}
		return resultImage;
	}

	// 获取四个角点旋转后Y方向坐标
	private double[] getY(int i, int j, double angle) {
		double results[] = new double[4];
		double radius = Math.sqrt(i * i + j * j);
		double angle1 = Math.asin(j / radius);
		results[0] = radius * Math.sin(angle1 + angle);
		results[1] = radius * Math.sin(Math.PI - angle1 + angle);
		results[2] = -results[0];
		results[3] = -results[1];
		Arrays.sort(results);
		return results;
	}

	// 获取四个角点旋转后X方向坐标
	private double[] getX(int i, int j, double angle) {
		double results[] = new double[4];
		double radius = Math.sqrt(i * i + j * j);
		double angle1 = Math.acos(i / radius);
		results[0] = radius * Math.cos(angle1 + angle);
		results[1] = radius * Math.cos(Math.PI - angle1 + angle);
		results[2] = -results[0];
		results[3] = -results[1];
		Arrays.sort(results);
		return results;
	}

	public static void main(String[] args) throws Exception {
		File input = new File("C:/Users/admin/Desktop/1.jpg");
		File output = new File("C:/Users/admin/Desktop/2.png");
		BufferedImage image = ImageIO.read(input);
		Color bgColor = new Color(255, 255, 255);
		BufferedImage result = new ImageRotate().rotateImage(image, 45,bgColor);
		ImageIO.write(result, "png", output);
	}
}

测试图像:

java图像处理之实现任意角度图像旋转_第2张图片

旋转45度后生成的png图像:

 

java图像处理之实现任意角度图像旋转_第3张图片

 

你可能感兴趣的:(Java图像处理)