简体   繁体   English

在Android上逐像素更改png颜色的最佳方法是什么?

[英]What is the best way to change png color pixel by pixel on Android?

I need to change a particular color of a PNG stored in the SDCard. 我需要更改SDCard中存储的PNG的特定颜色。 I can't use tintcolor over a bitmap or over any other object because this will tint all the image not a particular pixel color. 我不能在位图或任何其他对象上使用tintcolor,因为这将为所有图像着色而不是特定的像素颜色。

Why do I need to do this? 为什么我需要这样做?
I'm trying to develop an avatar application and I want to be able to change the hair of the avatar with any color I select from a palette. 我正在尝试开发头像应用程序,我希望能够使用从调色板中选择的任何颜色来更改头像的头发。 This Hair have two colors, one for the border and the other for the rest of the hair. 该头发有两种颜色,一种用于边界,另一种用于其余的头发。 I just want to change the air color and keep the border one. 我只想更改空气颜色并保持边框为一。

This a simple case but there could be more than one color in the image. 这是一种简单的情况,但是图像中可能有多种颜色。

I was looking up for a solution. 我正在寻找解决方案。 This is the only thing I found (meaby there could be more but I am not lucky): 这是我发现的唯一内容(方法可能更多,但我并不幸运):

Android Bitmap: Convert transparent pixels to a color Android位图:将透明像素转换为颜色

And this is what it is exposed there: 这是它在此处公开的内容:

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

I want to know if there is a better way to do this. 我想知道是否有更好的方法可以做到这一点。 Something like a: 像这样的东西:

bipmapImage.changeColor(originalColor, newColor);

I don't know if using a loop to check pixel a pixel is a good performance. 我不知道使用循环检查像素是否是一个好的性能。 Imagine a 1080 x 1080 image. 想象一下1080 x 1080的图像。

Thanks in advance. 提前致谢。

You can get better performance compared to getPixel() and setPixel() by calling copyPixelsToBuffer() to copy the pixels to a buffer, then modifying the pixel values in the buffer, and finally calling copyPixelsFromBuffer() to copy from the buffer back to the bitmap: getPixel()setPixel()相比,您可以通过调用copyPixelsToBuffer()将像素复制到缓冲区中,然后修改缓冲区中的像素值,最后调用copyPixelsFromBuffer()将缓冲区中的像素复制回到位图:

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;    
}

However, there are extra complications: the bitmap must use the ARGB_8888 pixel format (if it's different you will need to write extra code to handle that) and you need to catch out of memory exceptions that can occur when allocating the IntBuffer . 但是,还有一些额外的复杂性:位图必须使用ARGB_8888像素格式(如果不同,则需要编写额外的代码来处理),并且您需要捕获分配IntBuffer时可能发生的内存异常。 You should first profile the code using setPixels() and see if the speed is unacceptable. 您应该首先使用setPixels()分析代码,然后查看速度是否不能接受。

This is also likely not the fastest solution, which will probably be to use a native function call in a library. 这也可能不是最快的解决方案,可能是在库中使用本机函数调用。 But it will still be faster than setPixel() and you don't need to add a library to your project. 但是它仍然比setPixel()更快,并且您不需要在项目中添加库。

You'd better use OpenCV Matrix APIs especially for performance (and compactness). 您最好使用OpenCV Matrix API,特别是为了提高性能(和紧凑性)。

Check the OpenCV Android tutorial here . 此处查看OpenCV Android教程。

Assume you've installed the OpenCV features, you can change the color of some particular areas in your image. 假设您已经安装了OpenCV功能,则可以更改图像中某些特定区域的颜色。

(You should understand the Mat feature first.) (您应该首先了解Mat功能。)

Actually I haven't used OpenCV with Android. 实际上,我还没有在Android上使用OpenCV。
Here's some example codes to change your hair color to red in C++ : 这是一些示例代码,用于在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);


The Android version code will be not that different I think. 我认为Android版本代码不会有太大不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM