2

嗨,我正在为 android 创建一个应用程序,但遇到了如何为位图着色的问题,我正在使用以下简单代码

for(int i=0;i<pixels.length;i++){
          if(pixels[i] == COLOR.WHITE){
           pixels[i]=Color.RED;
        }
 }

其中像素是位图的像素数组但是问题是我在彩色区域的边缘得到了一层薄薄的没有着色的像素我知道这是因为这层白色有点阴影(不完全是白色的,部分是黑色的)我该如何克服这个问题?希望我的问题足够清楚

4

1 回答 1

4

现在,您正在匹配并替换白色的一个特定整数值。但是,在您的原始位图中,白色会渗入其他颜色,因此这些白色块的边缘周围的白色值略有不同。

您需要更改算法以考虑颜色匹配容差。为此,您必须将像素和关键颜色分成三个颜色通道,并单独检查它们之间的差异是否在某个容差值内。

这样,您可以匹配边缘周围的这些白色像素。但即使增加了容差,您也不能只用红色替换匹配的像素。你会得到混叠的硬红色边缘,它看起来并不漂亮。不久前我写了一个类似的算法,并通过在 HSV 颜色空间中进行一些颜色混合来解决这个混叠问题:

public Bitmap changeColor(Bitmap src, int keyColor,
            int replColor, int tolerance) {
        Bitmap copy = src.copy(Bitmap.Config.ARGB_8888, true);
        int width = copy.getWidth();
        int height = copy.getHeight();
        int[] pixels = new int[width * height];
        src.getPixels(pixels, 0, width, 0, 0, width, height);
        int sR = Color.red(keyColor);
        int sG = Color.green(keyColor);
        int sB = Color.blue(keyColor);
        int tR = Color.red(replColor);
        int tG = Color.green(replColor);
        int tB = Color.blue(replColor);
        float[] hsv = new float[3];
        Color.RGBToHSV(tR, tG, tB, hsv);
        float targetHue = hsv[0];
        float targetSat = hsv[1];
        float targetVal = hsv[2];

        for(int i = 0; i < pixels.length; ++i) {
            int pixel = pixels[i];

            if(pixel == keyColor) {
                pixels[i] = replColor;
            } else {
                int pR = Color.red(pixel);
                int pG = Color.green(pixel);
                int pB = Color.blue(pixel);

                int deltaR = Math.abs(pR - sR);
                int deltaG = Math.abs(pG - sG);
                int deltaB = Math.abs(pB - sB);

                if(deltaR <= tolerance && deltaG <= tolerance
                        && deltaB <= tolerance) {
                    Color.RGBToHSV(pR, pG, pB, hsv);
                    hsv[0] = targetHue;
                    hsv[1] = targetSat;
                    hsv[2] *= targetVal;

                    int mixTrgColor = Color.HSVToColor(Color.alpha(pixel),
                            hsv);
                    pixels[i] = mixTrgColor;
                }
            }
        }

        copy.setPixels(pixels, 0, width, 0, 0, width, height);

        return copy;
    }

keyColor 和 replColor 是 ARGB 编码的整数值,例如 Color.WHITE 和 Color.RED。容差是一个从 0 到 255 的值,用于指定每个颜色通道的关键颜色匹配容差。我不得不稍微重写该片段以删除框架细节。我希望我没有犯任何错误。

作为警告:Java(在 Android 上)的图像处理速度非常慢。如果它对您来说不够快,您应该例如用 C 重写算法并使用 NDK。

更新:C中的颜色替换算法

这是用 C 语言编写的相同算法的实现。最后一个函数是将位图的像素数组作为参数的实际算法。您需要使用该函数声明创建一个头文件并设置一些 NDK 编译样板,并使用以下方法声明创建一个附加 Java 类:

native static void changeColor(int[] pixels, int width, int height, int keyColor, int replColor, int tolerance);

C实现:

#include <math.h>

#define MIN(x,y) ((x < y) ? x : y)
#define MAX(x,y) ((x > y) ? x : y)

int clamp_byte(int val) {
    if(val > 255) {
        return 255;
    } else if(val < 0) {
        return 0;
    } else {
        return val;
    }
}

int encode_argb(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
    return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
}

int alpha(int c) {
    return (c >> 24) & 0xFF;
}

int red(int c) {
    return (c >> 16) & 0xFF;
}

int green(int c) {
    return (c >> 8) & 0xFF;
}

int blue(int c) {
    return c & 0xFF;
}

typedef struct struct_hsv {
    uint16_t h;
    uint8_t s;
    uint8_t v;
} hsv;

// http://www.ruinelli.ch/rgb-to-hsv
hsv rgb255_to_hsv(uint8_t r, uint8_t g, uint8_t b) {
    uint8_t min, max, delta;
    hsv result;
    int h;

    min = MIN(r, MIN(g, b));
    max = MAX(r, MAX(g, b));

    result.v = max; // v, 0..255
    delta = max - min; // 0..255, < v

    if(delta != 0 && max != 0) {
        result.s = ((int) delta) * 255 / max; // s, 0..255

        if(r == max) {
            h = (g - b) * 60 / delta; // between yellow & magenta
        } else if(g == max) {
            h = 120 + (b - r) * 60 / delta; // between cyan & yellow
        } else {
            h = 240 + (r - g) * 60 / delta; // between magenta & cyan
        }

        if(h < 0) h += 360;

        result.h = h;
    } else {
        // r = g = b = 0
        result.h = 0;
        result.s = 0;
    }

    return result;
}

int hsv_to_argb(hsv color, uint8_t alpha) {
    int i;
    uint8_t r,g,b;
    float f, p, q, t, h, s, v;

    h = (float) color.h;
    s = (float) color.s;
    v = (float) color.v;

    s /= 255;

    if(s == 0) {
        // achromatic (grey)
        return encode_argb(color.v, color.v, color.v, alpha);
    }

    h /= 60; // sector 0 to 5
    i = floor(h);
    f = h - i; // factorial part of h
    p = (unsigned char) (v * (1 - s));
    q = (unsigned char) (v * (1 - s * f));
    t = (unsigned char) (v * (1 - s * (1 - f)));

    switch(i) {
        case 0:
            r = v;
            g = t;
            b = p;
            break;
        case 1:
            r = q;
            g = v;
            b = p;
            break;
        case 2:
            r = p;
            g = v;
            b = t;
            break;
        case 3:
            r = p;
            g = q;
            b = v;
            break;
        case 4:
            r = t;
            g = p;
            b = v;
            break;
        default: // case 5:
            r = v;
            g = p;
            b = q;
            break;
    }

    return encode_argb(r, g, b, alpha);
}

JNIEXPORT void JNICALL Java_my_package_name_ClassName_changeColor(
        JNIEnv* env, jclass clazz, jintArray bitmapArray, jint width, jint height,
        jint keyColor, jint replColor, jint tolerance) {
    jint* pixels = (*env)->GetPrimitiveArrayCritical(env, bitmapArray, 0);
    int sR = red(keyColor);
    int sG = green(keyColor);
    int sB = blue(keyColor);
    int tR = red(replColor);
    int tG = green(replColor);
    int tB = blue(replColor);
    hsv cHsv = rgb255_to_hsv(tR, tG, tB);
    int targetHue = cHsv.h;
    int targetSat = cHsv.s;
    int targetVal = cHsv.v;
    int i;
    int max = width * height;

    for(i = 0; i < max; ++i) {
        int pixel = pixels[i];

        if(pixel == keyColor) {
            pixels[i] = replColor;
        } else {
            int pR = red(pixel);
            int pG = green(pixel);
            int pB = blue(pixel);

            int deltaR = abs(pR - sR);
            int deltaG = abs(pG - sG);
            int deltaB = abs(pB - sB);

            if(deltaR <= tolerance && deltaG <= tolerance
                    && deltaB <= tolerance) {
                cHsv = rgb255_to_hsv(pR, pG, pB);
                cHsv.h = targetHue;
                cHsv.s = targetSat;
                int newValue = ((int) cHsv.v * targetVal) / 255;
                cHsv.v = newValue;

                int mixTrgColor = hsv_to_argb(cHsv, alpha(pixel));
                pixels[i] = mixTrgColor;
            }
        }
    }

    (*env)->ReleasePrimitiveArrayCritical(env, bitmapArray, pixels, 0);
}
于 2013-10-20T13:47:32.307 回答