Android Renderscript,从位图

时间:2016-04-14 17:54:13

标签: android image image-processing bitmap renderscript

我正在处理基于图片的应用,并且我在使用Renderscript的问题上受阻。

我的目的在理论上非常简单,我想从用户加载的图像中删除白色背景,以在另一个我设置为背景的图像上显示它们。更具体地说,我想要的是模拟在纸质画布(也是图片)上打印用户上传图形的效果。

我无法假设用户能够使用Alpha通道上传漂亮的PNG,其中一个要求是使用JPG。

我一直试图用RenderScripts来解决这个问题,这样就可以将alpha 0设置为R,G和B等于或大于240的任何东西:

#pragma version(1)
#pragma rs java_package_name(mypackagename)
rs_allocation gIn;
rs_allocation gOut;
rs_script gScript;

const static float th = 239.f/256.f;


void root(const uchar4 v_in, uchar4 v_out, const void* usrData, uint32_t x,uint32_t y){
float4 f4 = rsUnpackColor8888(*v_in);


if(f4.r > th  && f4.g > th && f4.b > th)
{
    f4.a = 0;
}


   *v_out =  rsPackColorTo8888(f4);

}

void filter() {
    rsForEach(gScript, gIn, gOut);
}

但结果并不令人满意,主要有两个原因:

  • 如果照片背景上没有白色渐变,则脚本会产生难看的噪音效果
  • 靠近边缘的阴影图像会产生靠近边缘的噪点效果

据我所知,从alpha 0到alpha 1的传递过于极端,我尝试了不同的解决方案,当R,G,B组件的总和减少时,线性增加alpha,但我仍然有噪声像素和块周围。

使用纯白色或常规背景(例如Google主页的快照),它可以很好地工作,但是使用照片距离可接受的任何东西都很远。

我认为,如果我能够处理一个" line"像素或一个"块"像素而不是一个像素,它可以更容易检测平坦的背景,并避免达到渐变,但我不太了解renderscripts这样做。

有人能指出我正确的方向吗?

PS

我无法使用PorterDuff并相乘,因为背景和前景具有不同的尺寸,而且因为我需要能够在应用效果后将上传的图像拖动到背景画布周围。如果我将图像与背景区域相乘,则结果图像会导致背景的一部分也会移动。

1 个答案:

答案 0 :(得分:2)

如果我做对了,你想确定当前像素是否是基于相邻像素的线/块的白色背景。

您可以尝试使用rsGetElementAt。例如,要处理原始代码中的一行:

#pragma version(1)
#pragma rs java_package_name(mypackagename)
rs_allocation gIn;
rs_allocation gOut;
rs_script gScript;

const static float th = 239.f/256.f;

void root(const uchar4 v_in, uchar4 v_out, const void* usrData, uint32_t x,uint32_t y){
    float4 f4 = rsUnpackColor8888(*v_in);
    uint32_t width = rsAllocationGetDimX(gIn);
    // E.g: Processing a line from x to x+5.
    bool isBackground = true;
    for (uint32_t i=0; i<=5 && x+i<width; i++) {
        uchar4 nPixel_u4 = rsGetElementAt_uchar4(gIn, x+i, y);
        float4 nPixel_f4 = rsUnpackColor8888(nPixel_u4);

        if(nPixel_f4.r <= th  || nPixel_f4.g <= th || nPixel_f4.b <= th) {
            isBackground = false;
            break;
        }
    }
    if (isBackground) {
        f4.a = 0.0f;
        *v_out =  rsPackColorTo8888(f4);
    }
}

void filter() {
    rsForEach(gScript, gIn, gOut);
}

这只是一个简单的例子,说明如何使用rsGetElementAt从全局分配中的给定位置获取数据。有一个相应的rsSetElementAt用于将数据保存到全局分配。我希望它对你的项目有所帮助。