通过删除java中的透明像素将图像裁剪为最小尺寸

Rug*_*ggs 1 java image crop image-processing

我有一个精灵表,每个图像都以32x32单元为中心.实际图像不是32x32,而是略小.我想做的是拍摄一个单元格并裁剪透明像素,使图像尽可能小.

我将如何在Java(JDK 6)中做到这一点?

以下是我目前如何将瓷砖表拆分为单元格的示例:

BufferedImage tilesheet = ImageIO.read(getClass().getResourceAsStream("/sheet.png");
for (int i = 0; i < 15; i++) {
  Image img = tilesheet.getSubimage(i * 32, 0, 32, 32);
  // crop here..
}
Run Code Online (Sandbox Code Playgroud)

我目前的想法是测试中心的每个像素,看看它是否透明,但我想知道是否会有更快/更清洁的方法.

Ole*_*lov 5

在透明背景上的图像

有一个简单的解决方案 - 扫描每个像素.该算法具有恒定的性能 O(w•h).

private static BufferedImage trimImage(BufferedImage image) {
    int width = image.getWidth();
    int height = image.getHeight();
    int top = height / 2;
    int bottom = top;
    int left = width / 2 ;
    int right = left;
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            if (image.getRGB(x, y) != 0){
                top    = Math.min(top, x);
                bottom = Math.max(bottom, x);
                left   = Math.min(left, x);
                right  = Math.max(right, x);
            }
        }
    }
    return image.getSubimage(left, top, right - left, bottom - top);
}
Run Code Online (Sandbox Code Playgroud)

但这更有效:

private static BufferedImage trimImage(BufferedImage image) {
    WritableRaster raster = image.getAlphaRaster();
    int width = raster.getWidth();
    int height = raster.getHeight();
    int left = 0;
    int top = 0;
    int right = width - 1;
    int bottom = height - 1;
    int minRight = width - 1;
    int minBottom = height - 1;

    top:
    for (;top < bottom; top++){
        for (int x = 0; x < width; x++){
            if (raster.getSample(x, top, 0) != 0){
                minRight = x;
                minBottom = top;
                break top;
            }
        }
    }

    left:
    for (;left < minRight; left++){
        for (int y = height - 1; y > top; y--){
            if (raster.getSample(left, y, 0) != 0){
                minBottom = y;
                break left;
            }
        }
    }

    bottom:
    for (;bottom > minBottom; bottom--){
        for (int x = width - 1; x >= left; x--){
            if (raster.getSample(x, bottom, 0) != 0){
                minRight = x;
                break bottom;
            }
        }
    }

    right:
    for (;right > minRight; right--){
        for (int y = bottom; y >= top; y--){
            if (raster.getSample(right, y, 0) != 0){
                break right;
            }
        }
    }

    return image.getSubimage(left, top, right - left + 1, bottom - top + 1);
}
Run Code Online (Sandbox Code Playgroud)

该算法遵循pepan的答案(见上文),并且效率提高2到4倍.不同之处在于:它从不扫描任何像素两次,并尝试在每个阶段收缩搜索范围.

方法在最坏情况下的表现是 O(w•h–a•b)