Renderscript中的直方图匹配

时间:2015-08-01 00:46:09

标签: android histogram matching renderscript

为了对齐两个灰度图像的强度值(作为进一步处理的第一步),我写了一个Java方法:

  1. 将两个图像的位图转换为包含位图强度的两个int[]数组(我只是将红色分量放在这里,因为它的灰度级,即r = g = b)。

    public static int[] bmpToData(Bitmap bmp){
    int width = bmp.getWidth();
    int height = bmp.getHeight();
    int anzpixel = width*height;
    int [] pixels = new int[anzpixel];
    int [] data = new int[anzpixel];  
    bmp.getPixels(pixels, 0, width, 0, 0, width, height);
    for (int i = 0 ; i < anzpixel ; i++) {
    int p = pixels[i];
    int r = (p & 0xff0000) >> 16;
    //int g = (p & 0xff00) >> 8;
    //int b = p & 0xff;
    data[i] = r;
    }
    return data;
    }
    
  2. 将位图2的累积强度分布与位图1的累积强度分布对齐

        //aligns the intensity distribution of a grayscale picture moving    (given by int[] //data2) the the intensity distribution of a reference picture fixed (given by // int[] data1)
    public static int[] histMatch(int[] data1, int[] data2){
    
       int anzpixel = data1.length;
       int[] histogram_fixed = new int[256];
       int[] histogram_moving = new int[256];
       int[] cumhist_fixed = new int[256];
       int[] cumhist_moving = new int[256];
       int i=0;
       int j=0;
    
       //read intensities of fixed und moving in histogram
       for (int n = 0; n < anzpixel; n++) {
          histogram_fixed[data1[n]]++;
          histogram_moving[data2[n]]++;
       }
    
       // calc cumulated distributions
       cumhist_fixed[0]=histogram_fixed[0];
       cumhist_moving[0]=histogram_moving[0];
       for ( i=1; i < 256; ++i ) {
          cumhist_fixed[i] = cumhist_fixed[i-1]+histogram_fixed[i];
          cumhist_moving[i] = cumhist_moving[i-1]+histogram_moving [i];
       }
    
       // look-up-table lut[]. For each quantile i of the moving picture search     the 
       // value j of the fixed picture where the quantile is the same as that of moving   
       int[] lut = new int[anzpixel];
       j=0;
       for ( i=0; i < 256; ++i ){
          while(cumhist_fixed[j]< cumhist_moving[i]){
             j++;
          }
    
          // check, whether the distance to the next-lower intensity is even lower, and if so, take this value
          if ((j!=0) && ((cumhist_fixed[j-1]- cumhist_fixed[i]) < (cumhist_fixed[j]- cumhist_fixed[i]))){
             lut[i]= (j-1);
          }
          else {
             lut[i]= (j);
          }
       }
    
       // apply the lut[] to moving picture.
       i=0;
       for (int n = 0; n < anzpixel; n++) {
          data2[n]=(int) lut[data2[n]];
       }
       return data2;
    }
    
  3. int[]数组转换回Bitmap。

    public static Bitmap dataToBitmap(int[] data, int width, int heigth) {
     int index=0;
     Bitmap bmp = Bitmap.createBitmap(width, heigth, Bitmap.Config.ARGB_8888);
     for (int x = 0; x < width; x++) {
      for (int y = 0; y < heigth; y++) {
         index=y*width+x;
         int c = data[index];
         bmp.setPixel(x,y,Color.rgb(c, c, c));
         }
     }
     return bmp;
    }
    
  4. 虽然核心过程2)简单快速,但转换步骤1)和3)效率很低。在Renderscript中完成整个事情会更酷。但是,老实说,由于缺少文档,我完全迷失了这一点,虽然Renderscript可能会有很多令人印象深刻的例子,但我没有看到从这些可能性中受益的方法(没有书籍,没有文档) 。任何建议都非常感谢!

1 个答案:

答案 0 :(得分:2)

首先,使用Android Studio“导入示例...”并选择“基本渲染脚本”。这将为您提供一个我们现在将修改的工作项目。

首先,让我们为MainActivity添加更多分配引用。我们将使用它们在Java和Renderscript之间传递图像数据,直方图和LUT。

private Allocation mInAllocation;
private Allocation mInAllocation2;
private Allocation[] mOutAllocations;
private Allocation mHistogramAllocation;
private Allocation mHistogramAllocation2;
private Allocation mLUTAllocation;

然后在onCreate()中加载另一张图片,您还需要将其添加到/ res / drawables /。

    mBitmapIn2 = loadBitmap(R.drawable.cat_480x400);

createScript()中创建其他分配:

    mInAllocation2 = Allocation.createFromBitmap(mRS, mBitmapIn2);
    mHistogramAllocation = Allocation.createSized(mRS, Element.U32(mRS), 256);
    mHistogramAllocation2 = Allocation.createSized(mRS, Element.U32(mRS), 256);
    mLUTAllocation = Allocation.createSized(mRS, Element.U32(mRS), 256);

现在主要部分(在RenderScriptTask中):

            /*
             * Invoke histogram kernel for both images
             */
            mScript.bind_histogram(mHistogramAllocation);
            mScript.forEach_compute_histogram(mInAllocation);

            mScript.bind_histogram(mHistogramAllocation2);
            mScript.forEach_compute_histogram(mInAllocation2);


            /*
             * Variables copied verbatim from your code.
             */
            int []histogram_fixed = new int[256];
            int []histogram_moving = new int[256];
            int[] cumhist_fixed = new int[256];
            int[] cumhist_moving = new int[256];
            int i=0;
            int j=0;

            // copy computed histograms to Java side
            mHistogramAllocation.copyTo(histogram_fixed);
            mHistogramAllocation2.copyTo(histogram_moving);

            // your code again...
            // calc cumulated distributions
            cumhist_fixed[0]=histogram_fixed[0];
            cumhist_moving[0]=histogram_moving[0];

            for ( i=1; i < 256; ++i ) {
                cumhist_fixed[i] = cumhist_fixed[i-1]+histogram_fixed[i];
                cumhist_moving[i] = cumhist_moving[i-1]+histogram_moving [i];
            }

            // look-up-table lut[]. For each quantile i of the moving picture search     the
            // value j of the fixed picture where the quantile is the same as that of moving
            int[] lut = new int[256];
            j=0;
            for ( i=0; i < 256; ++i ){
                while(cumhist_fixed[j]< cumhist_moving[i]){
                    j++;
                }

                // check, whether the distance to the next-lower intensity is even lower, and if so, take this value
                if ((j!=0) && ((cumhist_fixed[j-1]- cumhist_fixed[i]) < (cumhist_fixed[j]- cumhist_fixed[i]))){
                    lut[i]= (j-1);
                }
                else {
                    lut[i]= (j);
                }
            }

            // copy the LUT to Renderscript side
            mLUTAllocation.copyFrom(lut);
            mScript.bind_LUT(mLUTAllocation);

            // Apply LUT to the destination image
            mScript.forEach_apply_histogram(mInAllocation2, mInAllocation2);


            /*
             * Copy to bitmap and invalidate image view
             */
            //mOutAllocations[index].copyTo(mBitmapsOut[index]);

            // copy back to Bitmap in preparation for viewing the results
            mInAllocation2.copyTo((mBitmapsOut[index]));

情侣笔记:

  • 在您的部分代码中,我还修复了LUT分配大小 - 只需要256个位置,
  • 如您所见,我在Java端留下了累积直方图和LUT的计算。由于数据依赖性和小规模的计算,这些很难有效地并行化,但考虑到后者我不认为这是一个问题。

最后,Renderscript代码。唯一不明显的部分是使用rsAtomicInc()来增加直方图箱中的值 - 这是必要的,因为可能有许多线程试图同时增加相同的bin。

#pragma version(1)
#pragma rs java_package_name(com.example.android.basicrenderscript)
#pragma rs_fp_relaxed

int32_t *histogram;
int32_t *LUT;

void __attribute__((kernel)) compute_histogram(uchar4 in)
{
    volatile int32_t *addr = &histogram[in.r];
    rsAtomicInc(addr);
}

uchar4 __attribute__((kernel)) apply_histogram(uchar4 in)
{
    uchar val = LUT[in.r];
    uchar4 result;
    result.r = result.g = result.b = val;
    result.a = in.a;

    return(result);
}