2

几天前我刚刚开始学习 RenderScript。我设法创建了一些简单的图像处理过滤器,例如灰度、颜色变化。现在我正在研究 Canny 边缘过滤器,但没有成功。

问题:ImageView为什么显示黑色图像以及如何解决?

我正在使用由arekolek github制作的 Canny egde 过滤器的实现

可选:我可以更快地计算它吗?

我以在我单击设备上的图像时运行的方法“runEdgeFilter(...)”中编写的所有代码结束,以确保我不会在其他地方弄乱 imageView。到目前为止我使用的代码。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v8.renderscript.*;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class MainActivity extends AppCompatActivity {

    private static final float THRESHOLD_MULT_LOW = 0.66f * 0.00390625f;
    private static final float THRESHOLD_MULT_HIGH = 1.33f * 0.00390625f;

    private ImageView imageView;
    private Bitmap img;
    private boolean setThresholds = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView) findViewById(R.id.imageView);
        img = BitmapFactory.decodeResource(getResources(), R.drawable.test_img_no_dpi2);
        imageView.setImageBitmap(img);
    }

    public void imageClicked(View view) {
        runEdgeFilter(img, this);
    }

    private void runEdgeFilter(Bitmap image, Context context) {
        int width = image.getWidth();
        int height = image.getHeight();

        RenderScript rs = RenderScript.create(context);

        Allocation allocationIn = Allocation.createFromBitmap(rs, image);

        Type.Builder tb;

        tb = new Type.Builder(rs, Element.F32(rs)).setX(width).setY(height);
        Allocation allocationBlurred = Allocation.createTyped(rs, tb.create());
        Allocation allocationMagnitude = Allocation.createTyped(rs, tb.create());

        tb = new Type.Builder(rs, Element.I32(rs)).setX(width).setY(height);
        Allocation allocationDirection = Allocation.createTyped(rs, tb.create());
        Allocation allocationEdge = Allocation.createTyped(rs, tb.create());

        tb = new Type.Builder(rs, Element.I32(rs)).setX(256);
        Allocation allocationHistogram = Allocation.createTyped(rs, tb.create());

        tb = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
        Allocation allocationOut = Allocation.createTyped(rs, tb.create());

        ScriptC_edge edgeFilter = new ScriptC_edge(rs);

        ScriptIntrinsicHistogram histogram = ScriptIntrinsicHistogram.create(rs, Element.U8(rs));

        histogram.setOutput(allocationHistogram);

        edgeFilter.invoke_set_histogram(allocationHistogram);
        edgeFilter.invoke_set_blur_input(allocationIn);
        edgeFilter.invoke_set_compute_gradient_input(allocationBlurred);
        edgeFilter.invoke_set_suppress_input(allocationMagnitude, allocationDirection);
        edgeFilter.invoke_set_hysteresis_input(allocationEdge);
        edgeFilter.invoke_set_thresholds(0.2f, 0.6f);

        histogram.forEach_Dot(allocationIn);

        int[] histogramOutput = new int[256];

        allocationHistogram.copyTo(histogramOutput);


        if(setThresholds) {
            int median = width * height / 2;
            for (int i = 0; i < 256; ++i) {
                median -= histogramOutput[i];
                if (median < 1) {
                    edgeFilter.invoke_set_thresholds(i * THRESHOLD_MULT_LOW, i * THRESHOLD_MULT_HIGH);
                    break;
                }
            }
        }

        edgeFilter.forEach_blur(allocationBlurred);
        edgeFilter.forEach_compute_gradient(allocationMagnitude);
        edgeFilter.forEach_suppress(allocationEdge);
        edgeFilter.forEach_hysteresis(allocationOut);

        allocationOut.copyTo(image);

        allocationIn.destroy();
        allocationMagnitude.destroy();
        allocationBlurred.destroy();
        allocationDirection.destroy();
        allocationEdge.destroy();
        allocationHistogram.destroy();
        allocationOut.destroy();
        histogram.destroy();
        edgeFilter.destroy();
        rs.destroy();

        imageView.setImageBitmap(image);
    }
}

渲染脚本边缘.rs:

#pragma version(1)
#pragma rs java_package_name(com.lukasz.edgeexamplers)
#pragma rs_fp_relaxed

#include "rs_debug.rsh"

static rs_allocation raw, magnitude, blurred, direction, candidates;
static float low, high;
static const uint32_t zero = 0;

void set_blur_input(rs_allocation u8_buf) {
    raw = u8_buf;
}

void set_compute_gradient_input(rs_allocation f_buf) {
    blurred = f_buf;
}

void set_suppress_input(rs_allocation f_buf, rs_allocation i_buf) {
    magnitude = f_buf;
    direction = i_buf;
}

void set_hysteresis_input(rs_allocation i_buf) {
    candidates = i_buf;
}

void set_thresholds(float l, float h) {
    low = l;
    high = h;
}

inline static float getElementAt_uchar_to_float(rs_allocation a, uint32_t x,
        uint32_t y) {
    return rsGetElementAt_uchar(a, x, y) / 255.0f;
}

static rs_allocation histogram;

void set_histogram(rs_allocation h) {
    histogram = h;
}

uchar4 __attribute__((kernel)) addhisto(uchar in, uint32_t x, uint32_t y) {
    int px = (x - 100) / 2;
    if (px > -1 && px < 256) {
        int v = log((float) rsGetElementAt_int(histogram, (uint32_t) px)) * 30;
        int py = (400 - y);
        if (py > -1 && v > py) {
            in = 255;
        }
        if (py == -1) {
            in = 255;
        }
    }
    uchar4 out = { in, in, in, 255 };
    return out;
}

uchar4 __attribute__((kernel)) copy(uchar in) {
    uchar4 out = { in, in, in, 255 };
    return out;
}

uchar4 __attribute__((kernel)) blend(uchar4 in, uint32_t x, uint32_t y) {
    uchar r = rsGetElementAt_uchar(raw, x, y);
    uchar4 out = { r, r, r, 255 };
    return max(out, in);
}

float __attribute__((kernel)) blur(uint32_t x, uint32_t y) {
    float pixel = 0;

    pixel += 2 * getElementAt_uchar_to_float(raw, x - 2, y - 2);
    pixel += 4 * getElementAt_uchar_to_float(raw, x - 1, y - 2);
    pixel += 5 * getElementAt_uchar_to_float(raw, x, y - 2);
    pixel += 4 * getElementAt_uchar_to_float(raw, x + 1, y - 2);
    pixel += 2 * getElementAt_uchar_to_float(raw, x + 2, y - 2);

    pixel += 4 * getElementAt_uchar_to_float(raw, x - 2, y - 1);
    pixel += 9 * getElementAt_uchar_to_float(raw, x - 1, y - 1);
    pixel += 12 * getElementAt_uchar_to_float(raw, x, y - 1);
    pixel += 9 * getElementAt_uchar_to_float(raw, x + 1, y - 1);
    pixel += 4 * getElementAt_uchar_to_float(raw, x + 2, y - 1);

    pixel += 5 * getElementAt_uchar_to_float(raw, x - 2, y);
    pixel += 12 * getElementAt_uchar_to_float(raw, x - 1, y);
    pixel += 15 * getElementAt_uchar_to_float(raw, x, y);
    pixel += 12 * getElementAt_uchar_to_float(raw, x + 1, y);
    pixel += 5 * getElementAt_uchar_to_float(raw, x + 2, y);

    pixel += 4 * getElementAt_uchar_to_float(raw, x - 2, y + 1);
    pixel += 9 * getElementAt_uchar_to_float(raw, x - 1, y + 1);
    pixel += 12 * getElementAt_uchar_to_float(raw, x, y + 1);
    pixel += 9 * getElementAt_uchar_to_float(raw, x + 1, y + 1);
    pixel += 4 * getElementAt_uchar_to_float(raw, x + 2, y + 1);

    pixel += 2 * getElementAt_uchar_to_float(raw, x - 2, y + 2);
    pixel += 4 * getElementAt_uchar_to_float(raw, x - 1, y + 2);
    pixel += 5 * getElementAt_uchar_to_float(raw, x, y + 2);
    pixel += 4 * getElementAt_uchar_to_float(raw, x + 1, y + 2);
    pixel += 2 * getElementAt_uchar_to_float(raw, x + 2, y + 2);

    pixel /= 159;

    return pixel;
}

float __attribute__((kernel)) compute_gradient(uint32_t x, uint32_t y) {
    float gx = 0;

    gx -= rsGetElementAt_float(blurred, x - 1, y - 1);
    gx -= rsGetElementAt_float(blurred, x - 1, y) * 2;
    gx -= rsGetElementAt_float(blurred, x - 1, y + 1);
    gx += rsGetElementAt_float(blurred, x + 1, y - 1);
    gx += rsGetElementAt_float(blurred, x + 1, y) * 2;
    gx += rsGetElementAt_float(blurred, x + 1, y + 1);

    float gy = 0;

    gy += rsGetElementAt_float(blurred, x - 1, y - 1);
    gy += rsGetElementAt_float(blurred, x, y - 1) * 2;
    gy += rsGetElementAt_float(blurred, x + 1, y - 1);
    gy -= rsGetElementAt_float(blurred, x - 1, y + 1);
    gy -= rsGetElementAt_float(blurred, x, y + 1) * 2;
    gy -= rsGetElementAt_float(blurred, x + 1, y + 1);

    int d = ((int) round(atan2pi(gy, gx) * 4.0f) + 4) % 4;
    rsSetElementAt_int(direction, d, x, y);
    return hypot(gx, gy);
}

int __attribute__((kernel)) suppress(uint32_t x, uint32_t y) {
    int d = rsGetElementAt_int(direction, x, y);
    float g = rsGetElementAt_float(magnitude, x, y);
    if (d == 0) {
        // horizontal, check left and right
        float a = rsGetElementAt_float(magnitude, x - 1, y);
        float b = rsGetElementAt_float(magnitude, x + 1, y);
        return a < g && b < g ? 1 : 0;
    } else if (d == 2) {
        // vertical, check above and below
        float a = rsGetElementAt_float(magnitude, x, y - 1);
        float b = rsGetElementAt_float(magnitude, x, y + 1);
        return a < g && b < g ? 1 : 0;
    } else if (d == 1) {
        // NW-SE
        float a = rsGetElementAt_float(magnitude, x - 1, y - 1);
        float b = rsGetElementAt_float(magnitude, x + 1, y + 1);
        return a < g && b < g ? 1 : 0;
    } else {
        // NE-SW
        float a = rsGetElementAt_float(magnitude, x + 1, y - 1);
        float b = rsGetElementAt_float(magnitude, x - 1, y + 1);
        return a < g && b < g ? 1 : 0;
    }
}

static const int NON_EDGE = 0b000;
static const int LOW_EDGE = 0b001;
static const int MED_EDGE = 0b010;
static const int HIG_EDGE = 0b100;

inline static int getEdgeType(uint32_t x, uint32_t y) {
    int e = rsGetElementAt_int(candidates, x, y);
    float g = rsGetElementAt_float(magnitude, x, y);
    if (e == 1) {
        if (g < low)
            return LOW_EDGE;
        if (g > high)
            return HIG_EDGE;
        return MED_EDGE;
    }
    return NON_EDGE;
}

uchar4 __attribute__((kernel)) hysteresis(uint32_t x, uint32_t y) {
    uchar4 white = { 255, 255, 255, 255 };
    uchar4 red = { 255, 0, 0, 255 };
    uchar4 black = { 0, 0, 0, 255 };
    int type = getEdgeType(x, y);
    if (type) {
        if (type & LOW_EDGE) {
            return black;
        }
        if (type & HIG_EDGE) {
            //rsDebug("wh : x=", x);
            //rsDebug("wh : y=", y);
            return white;
        }

        // it's medium, check nearest neighbours
        type = getEdgeType(x - 1, y - 1);
        type |= getEdgeType(x, y - 1);
        type |= getEdgeType(x + 1, y - 1);
        type |= getEdgeType(x - 1, y);
        type |= getEdgeType(x + 1, y);
        type |= getEdgeType(x - 1, y + 1);
        type |= getEdgeType(x, y + 1);
        type |= getEdgeType(x + 1, y + 1);

        if (type & HIG_EDGE) {
            //rsDebug("wh : x=", x);
            //rsDebug("wh : y=", y);
            return white;
        }

        if (type & MED_EDGE) {
            // check further
            type = getEdgeType(x - 2, y - 2);
            type |= getEdgeType(x - 1, y - 2);
            type |= getEdgeType(x, y - 2);
            type |= getEdgeType(x + 1, y - 2);
            type |= getEdgeType(x + 2, y - 2);
            type |= getEdgeType(x - 2, y - 1);
            type |= getEdgeType(x + 2, y - 1);
            type |= getEdgeType(x - 2, y);
            type |= getEdgeType(x + 2, y);
            type |= getEdgeType(x - 2, y + 1);
            type |= getEdgeType(x + 2, y + 1);
            type |= getEdgeType(x - 2, y + 2);
            type |= getEdgeType(x - 1, y + 2);
            type |= getEdgeType(x, y + 2);
            type |= getEdgeType(x + 1, y + 2);
            type |= getEdgeType(x + 2, y + 2);

            if (type & HIG_EDGE) {
                //rsDebug("wh : x=", x);
                //rsDebug("wh : y=", y);
                return white;
            }
        }
    }
    return black;
}

经过一番调试,我发现:

uchar4 __attribute__((kernel)) hysteresis(uint32_t x, uint32_t y) {...}

返回白色和黑色像素,因此我认为 renderscript 可以正常工作。输出与我之前成功分配给位图的渲染脚本过滤器(uchar4)的类型相同。我不知道我做错了什么。

我的 logcat 也会打印:

V/RenderScript_jni: RS compat mode 
V/RenderScript_jni: Unable to load libRSSupportIO.so, USAGE_IO not supported
V/RenderScript_jni: Unable to load BLAS lib, ONLY BNNM will be supported: java.lang.UnsatisfiedLinkError: Couldn't load blasV8 from loader dalvik.system.PathClassLoader[dexPath=/data/app/com.lukasz.edgeexamplers-20.apk,libraryPath=/data/app-lib/com.lukasz.edgeexamplers-20]: findLibrary returned null
E/RenderScript: Couldn't load libRSSupportIO.so

在每个使用渲染脚本的程序中,但其他程序即使出现此警告也能正常工作。

更新#1

正如@Stephen Hines提到的,阅读越界存在问题。我想我现在通过更改这些行来修复它(没有弄乱渲染脚本):

edgeFilter.forEach_blur(allocationBlurred);
edgeFilter.forEach_compute_gradient(allocationMagnitude);
edgeFilter.forEach_suppress(allocationEdge);
edgeFilter.forEach_hysteresis(allocationOut);

进入:

Script.LaunchOptions sLaunchOpt = new Script.LaunchOptions();
sLaunchOpt.setX(2, width - 3);
sLaunchOpt.setY(2, height - 3);
edgeFilter.forEach_blur(allocationBlurred, sLaunchOpt);
edgeFilter.forEach_compute_gradient(allocationMagnitude, sLaunchOpt);
edgeFilter.forEach_suppress(allocationEdge, sLaunchOpt);
edgeFilter.forEach_hysteresis(allocationOut, sLaunchOpt);

但是我的问题仍然没有解决。输出如前所述为黑色。

4

0 回答 0