如何使用一个像素的 Sobel 矩阵

How Use Sobel matrix with one pixel

我想在我的 android 应用程序中使用 Sobel 运算符。但是我不明白怎么用一个像素。

int sobel_x[][] = {{-1, 0, 1},
        {-2, 0, 2},
        {-1, 0, 1}};
int sobel_y[][] = {{-1, -2, -1},
        {0, 0, 0},
        {1, 2, 1}};
Bitmap source = ImageHelper.GetBitmapGromUri(Path);
    int w = source.getWidth();
    int h = source.getHeight();
    int[] pixels;
    pixels = new int[h * w];
    source.getPixels(pixels, 0, w, 1, 1, w - 1, h - 1);
    for(int i = 0;i < pixels.length;i++){
          ...
    }

我尝试使用 get/setPixel。但是非常非常慢。

个别像素访问绝对不是正确的方法!

我假设您已将源转换为灰度,因此它本质上是一个字节数组。我建议您使用类似下面的方法来提取数据:

int w = source.getWidth();
int h = source.getHeight();
ByteBuffer buffer = ByteBuffer.allocate( w * h );
source.copyPixelsToBuffer( buffer );
final byte[] bitmapData = buffer.array()

这为您提供了源数据。您现在可以对其应用 Sobel 过滤器。请注意,您要将生成的输出字节写入新的 byte 数组,然后将其转换回图像。

byte[] output = new byte[ w * h ];
for( int y=1; y<h-1; y++ ) {
    for( int x=1; x<w-1; x++ ) {
        int idx = y * w + x;

        // Apply Sobel filter
        byte sx = (byte) ((-1 * bitmapData[idx-w-1]) + ( 1 * bitmapData[idx-w+1] ) + (-2 * bitmapData[idx-1]) + ( 2 * bitmapData[idx+1] ) + (-1 * bitmapData[idx+w-1]) + ( 1 * bitmapData[idx+w+1] ) );

        output[idx] = sx;
    }
}

对于水平过滤器,您也可以这样做。

已编辑 以将输出类型从 int 更正为 byte

好消息和坏消息。以下工作但是...

Android,由于某些特殊原因,不允许您创建 8 位灰度图像。这意味着您必须以 ARGB_8888 格式创建灰度。这可能是您之前版本中的错误所在,我们以字节读取数据,而实际上它不是。

下面的代码有效,我只在模拟器上 运行 它与您的图像相对应 可笑 慢(11​​ 秒)。当然你的图片很大,但我想这仍然是减慢速度的方式。

我强烈建议考虑使用 OpenCV Java 库,因为与 Android 位图 class!

不同,它们速度快且内存效率高
public class Sobel {
    private static Bitmap toGreyScale( Bitmap source ) {
        Bitmap greyScaleBitmap = Bitmap.createBitmap(
                source.getWidth(), source.getHeight(),
                Bitmap.Config.ARGB_8888);

        Canvas c = new Canvas(greyScaleBitmap);
        Paint p = new Paint();
        ColorMatrix cm = new ColorMatrix();

        cm.setSaturation(0);
        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(cm);
        p.setColorFilter(filter);
        c.drawBitmap(source, 0, 0, p);
        return greyScaleBitmap;
    }

    public static void doSobel( Bitmap source) {

        Bitmap grey = toGreyScale(source);

        int w = grey.getWidth();
        int h = grey.getHeight();
        // Allocate 4 times as much data as is necessary because Android.
        int sz = w * h;

        IntBuffer buffer = IntBuffer.allocate( sz );
        grey.copyPixelsToBuffer( buffer );
        final int[] bitmapData = buffer.array();

        int[] output = new int[ w * h ];
        for( int y=1; y<h-1; y++ ) {
            for( int x=1; x<w-1; x++ ) {
                int idx = (y * w + x );

                // Apply Sobel filter
                int tl = (bitmapData[idx - w - 1]) & 0xFF;
                int tr = (bitmapData[idx - w + 1]) & 0xFF;
                int l = (bitmapData[idx - 1]) & 0xFF;
                int r = (bitmapData[idx + 1]) & 0xFF;
                int bl = (bitmapData[idx + w - 1]) & 0xFF;
                int br = (bitmapData[idx + w + 1]) & 0xFF;

                int sx = (int) ( tr - tl + 2 * ( r - l ) + br - bl );
                sx = sx & 0xFF;

                // Put back into ARG and B bytes
                output[toIdx] = (sx << 24) | ( sx << 16) | (sx << 8) | sx;
            }
        }

        source.copyPixelsFromBuffer( IntBuffer.wrap(output));
    }
}