反转位图颜色

7
我有以下问题。我有一个图表程序,它的设计是黑色的,但是图表(从服务器获取的图像)是浅色的(实际上只使用了5种颜色:红色,绿色,白色,黑色和灰色)。
为了适应设计反转,倒置做得很好,唯一的问题是红色和绿色也被反转了(绿色变成粉色,红色变成绿色)。
有没有办法除了这两种颜色以外反转其他所有东西,或者在反转后重新绘制这些颜色?
这些操作的成本如何(因为我经常获取图表更新)?
先谢谢了 :)
更新
我尝试用循环中的setPixel方法替换颜色。
for(int x = 0 ;x < chart.getWidth();x++) {
        for(int y = 0;y < chart.getHeight();y++) {
            final int replacement = getColorReplacement(chart.getPixel(x, y));
            if(replacement != 0) {
                chart.setPixel(x, y, replacement);
            }
        }
    }

很遗憾,这种方法太慢了(约650ms),是否有更快的方法来做这件事,setPixels()方法能更快地工作吗?


3
你写道:"它实际上只使用了5种颜色",但需要小心。你认为的"颜色"可能不是操作系统认为的"颜色"。例如,在ARGB 32位中,0xFF0000看起来像是"红色"。但0xFF0001也是。然而它们是两种不同的颜色。 - Gugussee
谢谢,但它实际上只使用了5种颜色。由于我可以访问生成图表的位置,它们被特别简化了 :) - Alex Orlov
4个回答

8

如果只调用一次getPixels函数将图像数据复制到int数组中,并且不在循环内调用任何函数,那么操作位图的速度会更快。 只需操作数组,然后在最后调用setPixels函数即可。

就像这样:

int length = bitmap.getWidth()*bitmap.getHeight();
int[] array = new int[length];
bitmap.getPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
for (int i=0;i<length;i++){
// If the bitmap is in ARGB_8888 format
  if (array[i] == 0xff000000){
    array[i] = 0xffffffff;
  } else if ...
  }
}
bitmap.setPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());

谢谢,我实际上尝试了类似的方法,但是使用IntBuffer和Bitmap的copyPixelsToBuffer(),但仍然需要大约250毫秒。 您的方法只需要大约100毫秒。 - Alex Orlov

2

如果您将其作为BufferedImage可用,则可以访问其光栅并按照您的意愿进行编辑。

WritableRaster raster = my_image.getRaster();

// Edit all the pixels you wanna change in the raster (green -> red, pink -> green)
// for (x,y) in ...
// raster.setPixel(x, y, ...) 

my_image.setData(raster);

呃..我猜Android没有BufferedImage或者WritableRaster :( - Alex Orlov
哦,抱歉,这是一个Java解决方案...我以为它也可以在Android上使用...我的错。 - dagnelies
它确实可用。Bitmap对象具有setPixel方法(可更改任何像素的值)。只需循环遍历所有像素并替换即可 :) - Alex Orlov

2

我看到您只使用了5种颜色,这很容易。

关于性能,我不知道Android的情况,但我可以告诉你,在Java中使用setRGB比获取数据缓冲区并直接写入int[]要慢得惊人。

当我说“惊人地慢”时,为了让您有一个概念,在OS X 10.4上,以下代码:

for ( int x = 0; x < width; x++ ) {
    for ( int y = 0; y < height; y++ ) {
        img.setRGB(x,y,0xFFFFFFFF);
    }
}

可能比以下内容慢100倍 (!) :

for ( int x = 0; x < width; x++ ) {
    for ( int y = 0; y < height; y++ ) {
        array[y*width+x] = 0xFFFFFFFF;
    }
}

您读得没错:100倍。在Core 2 Duo / Mac Mini / OS X 10.4上测量。
(当然,您需要先访问底层int []数组,但希望这不难)
我无法强调足够问题不是两个for循环:在两种情况下都是相同的未优化的for循环。因此,真正的问题在于setRGB。
我不知道它在Android上是否有效,但如果您想要性能良好的东西,您可能应该摆脱setRGB。

2

一个快速的方法是使用AvoidXfermode来重新绘制你想要更改的颜色 - 然后你可以在任何你想要的颜色之间切换。你只需要像这样做:

// will change red to green
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));

Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);

// rinse, repeat for other colors

您可能需要调整AvoidXfermode的容差,但这比逐像素计算要快得多。此外,请确保您的图表图像处于ARGB8888模式。默认情况下,Android倾向于使用RGB565模式处理图像,这往往会破坏您想要使用的颜色计算 - 为了确保,您可以通过调用Bitmap chart = chartFromServer.copy(Config.ARGB_8888, true);来确保图像既处于ARGB8888模式又可变,然后设置Xfermode。

澄清:要更改其他颜色,您不必重新加载图像,只需创建其他Paints并选择要更改的适当颜色即可,如下所示:

// changes green to red
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));

// changes white to blue
Paint change2 = new Paint();
change2.setColor(Color.BLUE);
change2.setXfermode(new AvoidXfermode(Color.WHITE, 245, AvoidXfermode.Mode.TARGET));

// ... other Paints with other changes you want to apply to this image

Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);
c.drawRect(0, 0, width, height, change2);
//...
c.drawRect(0, 0, width, height, changeN);

它肯定更快,但我怎么才能一次性改变所有颜色呢? 我的意思是,如果我只有两种颜色(白色、黑色)并想要反转,将所有黑色像素变成白色后,我无法将所有白色像素变成黑色,因为这将反转所有白色像素(旧的和新的)为黑色。所以最终我会得到一张完全黑色的图像。 我考虑过使用某种缓冲颜色来处理黑白,但这不会花费很多时间吗? 而且,在我做了这个操作超过一次之后,只有最后的更改被反映出来。 - Alex Orlov
你应该能够使用几乎白色/黑色/绿色/任何颜色和小的容差来消除中间步骤的需要。如果你有两种颜色,比如 0xFFFF00000xFF00FF00 想要切换,你应该能够设置两个带有 Xfermodes 的 Paints,一个将 0xFFFF0000 转换为 0xFF00EE00看起来是绿色,但并不完全是 0xFF00FF00真正的绿色),另一个再将 0xFF00FF00 转换回 0xFFFF0000。关键在于你必须为这两种颜色变化设置单独的 Paints,并按正确顺序应用它们到图像上。 - Kevin Dion
我明白了。如果我理解正确的话,更改一种颜色可能需要18毫秒,而更改5种颜色将需要约90毫秒,对吗?这实际上是一个非常快速的解决方案,但如果明天我有10种颜色,那么它将比逐像素替换需要更长的时间。难道没有一种方法可以同时应用多个更改吗? - Alex Orlov
如果您想使用单个 Paint 进行所有颜色更改,我头脑中暂时想不到明显的方法。但我也不知道时间是否会与整个更改一个颜色所需的时间成正比,因为您只需快速连续运行它们: Canvas c = new Canvas(); c.setBitmap(chart); c.drawRect(0, 0, width, height, change1); c.drawRect(0, 0, width, height, change2); //... c.drawRect(0, 0, width, height, changeN); 我认为18毫秒中的一些时间将用于图像复制和加载,并且在仅重复调用 drawRect() 中不会重复。 - Kevin Dion

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接