如何在Java中实现Matlab imrotate功能,旋转二维像素数组表示的图像
Java实现Matlab imrotate功能的问题修复与方案
核心问题原因
- 你提供的代码返回全0像素的直接原因是:
g2d.drawImage(img, 0, 0, this);核心像素拷贝行被注释,新创建的BufferedImage未写入任何源图像数据。 - 对
Graphics2D的功能存在误解:Graphics2D的绘制操作本质是修改目标BufferedImage的光栅像素数据,和前端可视化渲染没有绑定关系,处理完成的BufferedImage可以直接提取像素数组使用,不需要实际输出到显示设备。
修复方案1:调整原有BufferedImage实现
这个方案和Matlab imrotate的默认行为对齐,支持插值设置,性能也经过优化:
import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; public static BufferedImage rotateImage(BufferedImage img, double angle) { double rads = Math.toRadians(angle); double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads)); int w = img.getWidth(); int h = img.getHeight(); // 计算旋转后图像的尺寸 int newWidth = (int) Math.floor(w * cos + h * sin); int newHeight = (int) Math.floor(h * cos + w * sin); // 指纹图像为灰度图可改用TYPE_BYTE_GRAY,减少内存占用 BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = rotated.createGraphics(); // 设置插值模式,对应Matlab imrotate的插值参数: // VALUE_INTERPOLATION_NEAREST_NEIGHBOR 对应最近邻插值 // VALUE_INTERPOLATION_BILINEAR 对应双线性插值 g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); AffineTransform at = new AffineTransform(); // 平移使旋转后的图像居中 at.translate((newWidth - w) / 2.0, (newHeight - h) / 2.0); // 绕原图像中心旋转 at.rotate(rads, w / 2.0, h / 2.0); g2d.setTransform(at); // 已加载完成的BufferedImage传入null作为ImageObserver即可 g2d.drawImage(img, 0, 0, null); g2d.dispose(); return rotated; }
处理完成后你可以通过以下代码将BufferedImage转为二维int数组:
public static int[][] bufferedImageToArray(BufferedImage img) { int w = img.getWidth(); int h = img.getHeight(); int[][] arr = new int[h][w]; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { arr[y][x] = img.getRGB(x, y); // 灰度图可直接提取灰度值:arr[y][x] = (img.getRGB(x,y) >> 16) & 0xff; } } return arr; }
修复方案2:直接操作二维数组实现旋转
如果你不想依赖BufferedImage接口,可以直接通过坐标反向映射实现旋转,避免空像素孔洞:
/** * 二维数组图像旋转 * @param src 原图像数组,维度为[height][width] * @param angle 旋转角度(角度制) * @param fillValue 超出原图像范围的填充值,对应Matlab imrotate的填充参数 * @return 旋转后的图像数组 */ public static int[][] rotateArray(int[][] src, double angle, int fillValue) { int h = src.length; int w = src[0].length; double rads = Math.toRadians(angle); double sin = Math.sin(rads); double cos = Math.cos(rads); // 计算新尺寸 int newW = (int) Math.floor(Math.abs(w * cos) + Math.abs(h * sin)); int newH = (int) Math.floor(Math.abs(h * cos) + Math.abs(w * sin)); int[][] dst = new int[newH][newW]; // 原图像中心坐标 double cx = w / 2.0; double cy = h / 2.0; // 新图像中心坐标 double ncx = newW / 2.0; double ncy = newH / 2.0; for (int y = 0; y < newH; y++) { for (int x = 0; x < newW; x++) { // 反向映射:计算新坐标对应原图像的坐标 double ox = (x - ncx) * cos + (y - ncy) * sin + cx; double oy = -(x - ncx) * sin + (y - ncy) * cos + cy; // 坐标在原图像范围内则赋值,否则用填充值 if (ox >= 0 && ox < w && oy >=0 && oy < h) { // 最近邻插值直接取整,需要双线性可自行补充插值逻辑 dst[y][x] = src[(int) Math.round(oy)][(int) Math.round(ox)]; } else { dst[y][x] = fillValue; } } } return dst; }
这个方案完全不需要依赖Java的图形接口,直接操作像素数组,更贴合你直接处理子图数组的需求。
内容的提问来源于stack exchange,提问作者quangtrank9
相关产品推荐
相关产品推荐

