2015-05-07 4 views
1

Я хочу использовать Sobel Оператор в приложении для 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. Но очень медленно.

ответ

2

Хорошие новости и плохие новости. Следующие работы, но ...

Android, по какой-то своеобразной причине, не позволяет создать 8-битное изображение с серой шкалой. Это означает, что вам необходимо создать оттенок серого в формате ARGB_8888. Вероятно, это было неправильно в вашей предыдущей версии, мы читаем данные как байты, когда это не так.

Код ниже работает, и я только запускаю его на эмуляторе против вашего изображения, где находится смешно slow (11 секунд). Конечно, ваш образ очень большой, но это все равно, я думаю, способ замедлить.

Я бы настоятельно рекомендовал рассмотреть использование библиотек Java OpenCV, поскольку они бывают быстрыми и эффективными с точки зрения памяти, в отличие от класса Bitmap Android!

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)); 
    } 
} 
+0

Да. Это работает. В моем телефоне - 0 сек. Но 'toIdx' ==' idx'?и результат не годится :([результат] (http://i59.tinypic.com/s6j42x.jpg) – NickDevil

1

Индивидуальный доступ к пикселям определенно не подходит для правильного пути!

Я предполагаю, что вы преобразовали исходный код в оттенки серого и поэтому это по существу массив байтов. Я предлагаю вам извлечь из него что-то вроде следующего:

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 в байте

+0

'int [] output = новый байт [w * h];' - ошибка. если используется массив int. как установить? 'Result.setPixels (выход, 0, ш, 0,0, ж, з);' ??? если массив байтов - 'result.copyPixelsFromBuffer (output);' ?? – NickDevil

+0

@NickDevil сделать вывод int [] и использовать 'Bitmap.createBitmap (int [] colors, int offset, int stride, int width, int height, Bitmap.Config config)' или аналогичный. Или сделайте вывод байта [] и используйте 'copyPixelsFromBuffer()' –

+0

свою работу, но результат его не хорош ... [результат] (http://i61.tinypic.com/10ogqad.jpg) и [source] (http : //i58.tinypic.com/i3gg0i.png) – NickDevil

Смежные вопросы