5

Is there a fast way in Java to replace all instances in a bitmap of certain colors with other colors?

The image I am working with is a single very large 5616 x 2160 24bit non-transparent unindexed bitmap, although the pixel values of this bitmap will vary.

This is the code I am using at the moment, but it is much too slow: 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);
}

}

Here is an example image: http://www.mediafire.com/?rttpk4o33b3oj74

I was thinking of somehow converting it to an indexed bitmap and then swapping the color indexes, but I couldn't figure out any way to successful assign it/recreate it with a color index with 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

普通的留言:

  • 您转换为索引位图的想法不太可能有帮助,因为(我认为您会发现)转换成本超过了节省。

  • 使用外部应用程序或本机代码库可能会更好地解决问题。

我认为有微优化的空间。例如:

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,即使没有提高性能,可读性的提高也值得这样做。修复缩进也有帮助!

Incidentally, once I removed all of the verbiage, the probable source of your NPE problems became more obvious. Look at the line that I labelled "HERE". Note that the condition is such that the "then" branch is only taken if the ownerColor is null AND the lifeRating is non-zero. If the ownerColor is null AND the lifeRating is zero, you will take the "else" branch and an NPE is inevitable.

于 2012-10-20T01:58:11.197 回答
2

java.awt.image.LookupOp一个合适的实例LookupTable可能会更快。这里引用了一个例子。

ColorConvertOp,如图所示需要一个合适的ColorSpace.

ImageJ,这里提到的,可以用于批处理。

于 2012-10-20T03:01:28.427 回答
2

如果您想真正快速地做到这一点,最好的方法是访问底层像素数据:

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

然后你应该编写你的算法,使它恰好通过这个数组,测试每个 int 并在必要时更改它。

这种方法应该与本机代码一样快。

于 2012-10-20T03:24:33.743 回答