5

特定の色のビットマップ内のすべてのインスタンスを他の色に置き換えるJavaの高速な方法はありますか?

私が使用している画像は、非常に大きな 5616 x 2160 24 ビットの非透明でインデックスなしの単一のビットマップですが、このビットマップのピクセル値はさまざまです。

これは私が現在使用しているコードですが、遅すぎます: http://pastebin.com/UjgwgB0V

public class DisplayImage extends JFrame {

public DisplayImage(boolean resize, boolean mapCountries) throws IOException {
super("Province Map");
File mapProvinceFile = new File("map\\provinces.bmp");
BufferedImage mapProvinceImage = ImageIO.read(mapProvinceFile);

byte[] pixels = (byte[])mapProvinceImage.getRaster().getDataElements(0, 0, mapProvinceImage.getWidth(), mapProvinceImage.getHeight(), null);

if (mapCountries) {
    for (int i = 0; i < Victoria2Stats.provinceDefinitionArray.size(); i++) {
        for (int p = 0; p < pixels.length-3; p = p + 3) {
           if ((byte)Victoria2Stats.provinceDefinitionArray.get(i).rgb[0] == pixels[p]) {
               if ((byte)Victoria2Stats.provinceDefinitionArray.get(i).rgb[1] == pixels[p+1]) {
                   if ((byte)Victoria2Stats.provinceDefinitionArray.get(i).rgb[2] == pixels[p+2]) {
                       try {
                           if ((Victoria2Stats.provinceDataTable[i].ownerColor == null) && !(Victoria2Stats.provinceDataTable[i].lifeRating == 0)) {
                                pixels[p] = (byte)255;
                                pixels[p+1] = (byte)255;
                                pixels[p+2] = (byte)255;
                           } else {
                                pixels[p] = (byte)(Victoria2Stats.provinceDataTable[i].ownerColor.getRed());
                                pixels[p+1] = (byte)(Victoria2Stats.provinceDataTable[i].ownerColor.getBlue());
                                pixels[p+2] = (byte)(Victoria2Stats.provinceDataTable[i].ownerColor.getGreen());
                           }
                       } catch (NullPointerException e) {
                       // I realise this is a bad practice, but it is unrelated to the question and will be fixed later
                       }
                   }
               }
           }
      }
  }
}

BufferedImage buffer = new BufferedImage(mapProvinceImage.getWidth(), mapProvinceImage.getHeight(), mapProvinceImage.getType());
DataBuffer dataBuffer = new DataBufferByte(pixels, pixels.length);

SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE, mapProvinceImage.getWidth(), mapProvinceImage.getHeight(), 3, mapProvinceImage.getWidth()*3, new int[]{0,1,2});
Raster raster = Raster.createRaster(sampleModel, dataBuffer, null);
buffer.setData(raster);

BufferedImage fixedImage = ImageUtils.verticalflip(buffer);
ImageIcon ii = new ImageIcon(fixedImage);
JScrollPane jsp = new JScrollPane(new JLabel(ii));
getContentPane().add(jsp);
setSize(800, 600);
setVisible(true);
}

}

画像の例を次に示します: http://www.mediafire.com/?rttpk4o33b3oj74

どういうわけかそれをインデックス付きビットマップに変換してからカラーインデックスを交換することを考えていましたが、Javaでカラーインデックスを使用して割り当て/再作成する方法がわかりませんでした。

4

4 に答える 4

2

実際、Processing(www.processing.org)を試してみるべきだと思います。非常に少ないコードで、画像の簡単な色変更を行うことができました。

import processing.opengl.PGraphics3D;
//place provinces.bmp in the PROJECTFOLDER/data  
PImage p = loadImage("provinces.bmp"); 

//This just changes the window size, but the full image will be loaded
size(1000,800,P2D); 
PGraphics big = createGraphics(p.width, p.height, P2D);
big.beginDraw();
big.image(p,0,0);
//BEGIN GRAPHICS MANIPULATION
big.loadPixels();
for (int i = 0; i < big.pixels.length; i++) {
  color c = color(big.pixels[i]);
  float r = red(c);
  float g = green(c);
  float b = blue(c);
  //Let's do a simple color change, keeping r the same, setting green equal to old b, and setting blue to 0
  big.pixels[i] = color(r,b,0);
}
//END GRAPHICS MANIPULATION
big.updatePixels();
big.endDraw();
String path = savePath("big.jpg"); //change to tif or something else for uncompressed
big.save(path);
image(big, 0, 0); 

さまざまなカラーマッピングを行う方法についてアドバイスが必要な場合はコメントを残してください。ただし、コメントの周囲のコードを変更できます(「簡単なカラー変更を行いましょう」)

明らかに、これはおそらくあなたが望む正確な色の変更ではありませんが、上記のコードの結果は次のとおりです:http ://www.kapparate.com/hw/big.jpg

参考までに、Javaクラスパスでの処理からcore.jarを含めることにより、非処理JavaアプリでPImageとPGraphicsを使用できます。

于 2012-10-20T03:45:41.547 に答える
2

これを非常に高速に実行したい場合、最良の方法は、基になるピクセルデータにアクセスすることです。

int[] data = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

次に、この配列を1回だけ通過するようにアルゴリズムを記述し、各intをテストし、必要に応じて変更する必要があります。

このアプローチは、ネイティブコードと同じくらい高速である必要があります。

于 2012-10-20T03:24:33.743 に答える
2

java.awt.image.LookupOp適切なのインスタンスは、LookupTableより高速になる場合があります。ここ に 例 を 挙げます.

ColorConvertOpここに示されているには、適切な が必要ですColorSpace

ImageJここに記載されているは、バッチ処理に使用できます。

于 2012-10-20T03:01:28.427 に答える
2

一般的なコメント:

  • インデックス化されたビットマップに変換するというあなたのアイデアは、おそらく役に立ちません。変換コストが節約を上回るからです。

  • 外部アプリケーションまたはネイティブ コード ライブラリを使用して、問題をより適切に解決できる場合があります。

マイクロ最適化の余地があると思います。例えば:

public class DisplayImage extends JFrame {

  public DisplayImage(boolean resize, boolean mapCountries) 
      throws IOException {
    super("Province Map");
    File mapProvinceFile = new File("map\\provinces.bmp");
    BufferedImage mapProvinceImage = ImageIO.read(mapProvinceFile);

    byte[] pixels = (byte[])mapProvinceImage.getRaster().getDataElements(
        0, 0, mapProvinceImage.getWidth(), mapProvinceImage.getHeight(), null);

    if (mapCountries) {
      int len = Victoria2Stats.provinceDefinitionArray.size();
      for (int i = 0; i < len; i++) {
        int[] rgb = Victoria2Stats.provinceDefinitionArray.get(i);
        ProvinceDataTable pdt = Victoria2Stats.provinceDataTable[i];
        for (int p = 0; p < pixels.length-3; p = p + 3) {
          if ((byte) rgb[0] == pixels[p] &&
              (byte) rgb[1] == pixels[p+1] &&
              (byte) rgb[2] == pixels[p+2]) {
            if (pdt.ownerColor == null && pdt.lifeRating != 0) {  // HERE
              pixels[p] = (byte)255;
              pixels[p+1] = (byte)255;
              pixels[p+2] = (byte)255;
            } else {
              pixels[p] = (byte)(pdt.ownerColor.getRed());
              pixels[p+1] = (byte)(pdt.ownerColor.getBlue());
              pixels[p+2] = (byte)(pdt.ownerColor.getGreen());
            }
          }
        }
      }
    }

    BufferedImage buffer = new BufferedImage(
       mapProvinceImage.getWidth(), mapProvinceImage.getHeight(),
       mapProvinceImage.getType());
    DataBuffer dataBuffer = new DataBufferByte(pixels, pixels.length);

    SampleModel sampleModel = new ComponentSampleModel(
       DataBuffer.TYPE_BYTE, mapProvinceImage.getWidth(),
       mapProvinceImage.getHeight(), 3, mapProvinceImage.getWidth()*3,
       new int[]{0,1,2});
    Raster raster = Raster.createRaster(sampleModel, dataBuffer, null);
    buffer.setData(raster);

    BufferedImage fixedImage = ImageUtils.verticalflip(buffer);
    ImageIcon ii = new ImageIcon(fixedImage);
    JScrollPane jsp = new JScrollPane(new JLabel(ii));
    getContentPane().add(jsp);
    setSize(800, 600);
    setVisible(true);
  }
}

IMO、読みやすさの向上により、パフォーマンスが向上しなくても、これは価値があります。インデントを修正することも役立ちます!

ちなみに、言い回しをすべて削除すると、NPE の問題の原因の可能性がより明確になりました。「HERE」とラベルを付けた行を見てください。ownerColor「then」分岐が行われるのは、がnullAND であり、lifeRatingゼロでない場合のみであることに注意してください。ownerColornullかつゼロである場合lifeRatingは、「else」分岐を選択し、NPE は避けられません。

于 2012-10-20T01:58:11.197 に答える