2

各画像が 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..
}

私の現在の考えは、中心から各ピクセルをテストして、それが透明かどうかを確認することでしたが、これを行うためのより高速でクリーンな方法があるかどうか疑問に思っていました.

4

6 に答える 6

3

このコードは私にとってはうまくいきます。アルゴリズムは単純で、画像の左/上/右/下から反復し、透明でない列/行の最初のピクセルを見つけます。次に、トリミングされた画像の新しいコーナーを記憶し、最後に元の画像のサブ画像を返します。

改善できる点があります。

  1. アルゴリズムは、データにアルファバイトがあることを期待しています。存在しない場合、配列外のインデックス例外で失敗します。

  2. アルゴリズムは、画像に少なくとも 1 つの不透明なピクセルがあることを想定しています。画像が完全に透明な場合は失敗します。

    private static BufferedImage trimImage(BufferedImage img) {
    final byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
    int width = img.getWidth();
    int height = img.getHeight();
    int x0, y0, x1, y1;                      // the new corners of the trimmed image
    int i, j;                                // i - horizontal iterator; j - vertical iterator
    leftLoop:
    for (i = 0; i < width; i++) {
        for (j = 0; j < height; j++) {
            if (pixels[(j*width+i)*4] != 0) { // alpha is the very first byte and then every fourth one
                break leftLoop;
            }
        }
    }
    x0 = i;
    topLoop:
    for (j = 0; j < height; j++) {
        for (i = 0; i < width; i++) {
            if (pixels[(j*width+i)*4] != 0) {
                break topLoop;
            }
        }
    }
    y0 = j;
    rightLoop:
    for (i = width-1; i >= 0; i--) {
        for (j = 0; j < height; j++) {
            if (pixels[(j*width+i)*4] != 0) {
                break rightLoop;
            }
        }
    }
    x1 = i+1;
    bottomLoop:
    for (j = height-1; j >= 0; j--) {
        for (i = 0; i < width; i++) {
            if (pixels[(j*width+i)*4] != 0) {
                break bottomLoop;
            }
        }
    }
    y1 = j+1;
    return img.getSubimage(x0, y0, x1-x0, y1-y0);
    

    }

于 2014-06-02T16:25:17.370 に答える
2

これはまさにあなたがすべきことだと思います。ピクセルの配列をループし、アルファをチェックしてから破棄します。たとえば、星の形をしている場合でも、画像のサイズを小さくすることはできませんが、これに注意してください。

于 2010-07-11T20:59:22.967 に答える
1

[こんにちは、次のことを試しました。画像ファイルのidle1.pngは大きな透明なボックスを持つ画像であり、testing.pngは最小の境界ボックスを持つ同じ画像です

'BufferedImage tempImg = (ImageIO.read(new File(fileNPath)));
                WritableRaster tempRaster = tempImg.getAlphaRaster();
                int x1 = getX1(tempRaster);
                int y1 = getY1(tempRaster);
                int x2 = getX2(tempRaster);
                int y2 = getY2(tempRaster);
                System.out.println("x1:"+x1+" y1:"+y1+" x2:"+x2+" y2:"+y2);
                BufferedImage temp = tempImg.getSubimage(x1, y1, x2 - x1, y2 - y1);

                //for idle1.png
                String filePath = fileChooser.getCurrentDirectory() + "\\"+"testing.png";
                System.out.println("filePath:"+filePath);
                ImageIO.write(temp,"png",new File(filePath));

get 関数の場所

public int getY1(WritableRaster raster) { //文字の先頭

    for (int y = 0; y < raster.getHeight(); y++) {
        for (int x = 0; x < raster.getWidth(); x++) {
            if (raster.getSample(x, y,0) != 0) {
                if(y>0) {
                    return y - 1;
                }else{
                    return y;
                }
            }
        }
    }
    return 0;
}

public int getY2(WritableRaster raster) {
    //ground plane of character

    for (int y = raster.getHeight()-1; y > 0; y--) {
        for (int x = 0; x < raster.getWidth(); x++) {
            if (raster.getSample(x, y,0) != 0) {
                return y + 1;
            }
        }
    }
    return 0;
}

public int getX1(WritableRaster raster) {
    //left side of character

    for (int x = 0; x < raster.getWidth(); x++) {
        for (int y = 0; y < raster.getHeight(); y++) {
            if (raster.getSample(x, y,0) != 0) {
                if(x > 0){
                    return x - 1;
                }else{
                    return x;
                }
            }
        }
    }
    return 0;
}

public int getX2(WritableRaster raster) {
    //right side of character

    for (int x = raster.getWidth()-1; x > 0; x--) {
        for (int y = 0; y < raster.getHeight(); y++) {
            if (raster.getSample(x, y,0) != 0) {
                return x + 1;
            }
        }
    }
    return 0;
}'[Look at Idle1.png and the minimum bounding box idle = testing.png][1]

Michael についてご協力いただきありがとうございます。Idle1.png と最小バウンディング ボックス idle = testing.png]画像をご覧ください。

于 2020-09-07T15:53:20.653 に答える
0

シートに既に透明なピクセルがある場合は、BufferedImageによって返されるgetSubimage()も同様です。デフォルトのGraphics2D 複合ルールAlphaComposite.SRC_OVERで、これで十分ですdrawImage()

サブイメージの背景色が異なる場合は、背景と一致する色のアルファ コンポーネントを 0 に設定LookupOpする 4 コンポーネントで を使用します。LookupTable

最後の手段としてのみ、ピクセル ラスターをトラバースします。

補遺: 余分な透明ピクセルは、衝突検出などに干渉する可能性があります。それらをトリミングするには、WritableRaster直接操作する必要があります。中心から外側に向かって作業するのではなく、一度に行または列を変更できるgetPixels()/メソッドのペアを使用して、境界線から始めます。setPixels()行全体または列全体のアルファがゼロの場合は、後でサブイメージを取得するときに削除するようにマークします。

于 2010-07-11T22:09:50.930 に答える