【问题标题】:What is the best way to change png color pixel by pixel on Android?在Android上逐像素更改png颜色的最佳方法是什么?
【发布时间】:2017-01-06 03:43:13
【问题描述】:

我需要更改存储在 SDCard 中的 PNG 的特定颜色。我不能在位图或任何其他对象上使用 tintcolor,因为这会使所有图像着色而不是特定的像素颜色。

为什么我需要这样做?
我正在尝试开发一个头像应用程序,我希望能够使用我从调色板中选择的任何颜色来更改头像的头发。这头发有两种颜色,一种用于边缘,另一种用于其余头发。我只想改变空气颜色并保留边框。

这是一个简单的例子,但图像中可能有不止一种颜色。

我正在寻找解决方案。这是我发现的唯一东西(可能还有更多,但我不走运):

Android Bitmap: Convert transparent pixels to a color

这就是它在那里暴露的内容:

Bitmap b = ...;
for(int x = 0; x<b.getWidth(); x++){
    for(int y = 0; y<b.getHeight(); y++){
        if(b.getPixel(x, y) == Color.TRANSPARENT){
            b.setPixel(x, y, Color.WHITE);
        }
     }
}

我想知道是否有更好的方法来做到这一点。有点像:

bipmapImage.changeColor(originalColor, newColor);

我不知道使用循环来检查像素是否是一个很好的表现。想象一下 1080 x 1080 的图像。

提前致谢。

【问题讨论】:

  • 您是否使用透明度来指示哪些像素是头发?
  • 您最好使用 OpenCV Matrix API,尤其是在性能(和紧凑性)方面。
  • @samgak 我没有使用透明度,因为我可以在同一张图像中更改多种颜色。
  • @SuhyeonLee,你有例子吗?请
  • 在您确定使用该示例的性能不佳之前,您应该像这样实现它并测量所花费的时间。

标签: android performance colors bitmap pixels


【解决方案1】:

您最好使用OpenCV Matrix API,尤其是为了提高性能(和紧凑性)。

查看OpenCV Android 教程here

假设您已经安装了OpenCV 功能,您可以更改图像中某些特定区域的颜色。

(您应该先了解Mat 功能。)

其实我还没有在 Android 上使用过 OpenCV。
下面是一些在 C++ 中将头发颜色更改为红色的示例代码:

// 1. Loading
Mat src = imread("yourImagePath/yourOriginalImage.jpg");  // This code will automatically loads image to Mat with 3-channel(BGR) format
Mat mask = imread("yourImagePath/yourHairMaskImage.png", CV_GRAYSCALE);  // This code will automatically loads mask image to Mat with 1-channel(grayscale) format

// 2. Splitting RGB channel into 3 separate channels
vector<Mat> channels;   // C++ version of ArrayList<Mat>
split(src, channels);   // Automatically splits channels and adds them to channels. The size of channels = 3

// 3-1. Adjusting red color
Mat adjustRed = channels[0]*1.5;

// 3-2. Copy the adjusted pixels into org Mat with mask
channels[2] = adjustRed & mask + channels[0] & ~mask;

// 4. Merging channels
Mat dst;
merge(channels, dst);   // dst is re-created with 3-channel(BGR).
// Note that OpenCV applies BGR by default if your array size is 3,
// even if actual order is different. In this case this makes sense.

// 5. Saving
imwrite("yourImagePath/yourDstImage.jpg", dst);


Android 版本代码我认为不会有什么不同。

【讨论】:

  • 我接受 samgak 的回答(目前),因为这是做我想做的事的直接方式。我正在评估你的想法,也许我的想法并不像你展示的那样完全,但你的想法可能是一个很好的开始,这可能是谈论性能的最佳方式。谢谢大家。
【解决方案2】:

通过调用copyPixelsToBuffer()将像素复制到缓冲区,然后修改缓冲区中的像素值,最后调用copyPixelsFromBuffer()从缓冲区复制,您可以获得比getPixel()setPixel()更好的性能回到位图:

boolean changeColor(Bitmap bitmap, int originalColor, int newColor)
{
    // bitmap must be mutable and 32 bits per pixel:
    if((bitmap.getConfig() != Bitmap.Config.ARGB_8888) || !bitmap.isMutable())
        return false;

    int pixelCount = bitmap.getWidth() * bitmap.getHeight();
    IntBuffer buffer = IntBuffer.allocate(pixelCount);
    bitmap.copyPixelsToBuffer(buffer);
    int[] array = buffer.array();
    for(int i = 0; i < pixelCount; i++)
    {
        if(array[i] == originalColor)
            array[i] = newColor;
    }
    bitmap.copyPixelsFromBuffer(buffer);

    return true;    
}

但是,还有一些额外的复杂性:位图必须使用ARGB_8888 像素格式(如果不同,您将需要编写额外的代码来处理它)并且您需要捕获分配时可能发生的内存不足异常IntBuffer。您应该首先使用setPixels() 分析代码,看看速度是否无法接受。

这也可能不是最快的解决方案,它可能是在库中使用本机函数调用。但它仍然会比setPixel() 更快,并且您不需要将库添加到您的项目中。

【讨论】:

  • 做你的例子,但是得到的图像是不同颜色的,而不是预期的,你觉得是什么原因?
  • @Сергей 最可能的原因是您的 int 值中的 rgba 顺序与 Android Bitmap 类所期望的不同。颜色应该像这样形成 int color = (a developer.android.com/reference/android/graphics/Color.html
猜你喜欢
  • 1970-01-01
  • 2016-01-13
  • 2021-12-31
  • 2012-12-15
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多