【问题标题】:Invert bitmap colors反转位图颜色
【发布时间】:2011-06-05 04:51:56
【问题描述】:

我有以下问题。我有一个图表程序,它的设计是黑色的,但图表(我从服务器获取的图像)很浅(它实际上只使用 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() 方法工作得更快吗?

【问题讨论】:

  • 您写道:“它实际上只使用了 5 种颜色”,但您需要小心。您认为的“颜色”可能不是操作系统认为的“颜色”。例如,在 ARGB 32 位中,0xFF0000 看起来像“红色”。但 0xFF0001 也是如此。然而它们是两种不同的颜色。
  • 谢谢,但它实际上只使用了 5 种颜色。由于我可以访问图表的生成位置,因此它们非常简单:)

标签: java android colors


【解决方案1】:

一种快速的方法是使用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 模式下处理图像,这往往会像您想要使用的那样弄乱颜色计算 - 可以肯定的是,您可以确保您的图像同时处于 ARGB8888 模式可变在设置 Xfermode 之前调用 Bitmap chart = chartFromServer.copy(Config.ARGB_8888, true);

澄清:要更改其他颜色,您不必重新加载图像,您只需要创建具有您想要更改的适当颜色的其他油漆,如下所示:

// 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);

【讨论】:

  • 它确实更快,但我如何一次更改所有颜色?我的意思是,如果我只有 2 种颜色(白色、黑色)并且想要反转,则在使所有黑色像素 -> 白色之后,我无法使所有白色像素变黑,因为它会将所有白色像素(旧的和新的)反转为黑色的。所以最后我会得到一个全黑的图像。我想过为黑白使用某种缓冲颜色,但这不会花费很多时间吗?而且,在我多次这样做之后,只会反映最后的更改。
  • 您应该能够使用几乎白色/黑色/绿色/任何颜色和小公差来消除对中间步骤的需要。如果你有两种颜色,比如你想要切换的0xFFFF00000xFF00FF00,你应该能够设置两种带有 Xfermodes 的 Paint,一种可以使用 0xFFFF0000 -> 0xFF00EE00 (looks 绿色,但不完全是 0xFF00FF00true 绿色),然后是 0xFF00FF00 -> 0xFFFF0000。关键是您必须为两种颜色更改设置单独的 Paints,并以正确的顺序将它们应用到图像。
  • 我明白了。如果我理解正确,虽然改变一种颜色可能需要 18 毫秒,但改变 5 种颜色需要大约 90 毫秒,对吧。这实际上是一个非常快速的解决方案,但如果我明天有 10 种颜色,它会比逐像素替换花费更长的时间。有没有办法一次应用多个更改?
  • 如果您的意思是使用单个 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() 时重复。
【解决方案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;
    }
}

你没看错:一百次。在 Core 2 Duo / Mac Mini / OS X 10.4 上测量。

(当然,您需要先访问底层的 int[] 数组,但希望这不难)

我不能强调问题不在于两个 for 循环:在这两种情况下,它都是相同的未优化 for 循环。所以这里的问题确实是 setRGB。

我不知道它适用于 Android,但如果你想要一些性能良好的东西,你可能应该摆脱 setRGB。

【讨论】:

    【解决方案3】:

    如果您只通过调用一次 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 毫秒
    【解决方案4】:

    如果您将其作为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 :(
    • 哦,抱歉,这是一个 java 解决方案……我以为它也可以在 android 上使用……我的错。
    • 它实际上是可用的。位图对象有 setPixel 方法(改变任何像素的值)。只需要遍历所有像素并替换为替代品:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 2018-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    相关资源
    最近更新 更多