2013-07-23 25 views
23

Mam zadanie do wdrożenia filtra Sobela, który jest, jak wiadomo, filtrem przetwarzania obrazu do wykrywania krawędzi. Ale niestety nie mam doświadczenia w dziedzinie przetwarzania obrazu, do tego stopnia, że ​​nawet nie wiem, w jaki sposób obrazy są reprezentowane w komputerze. Absolutnie brak wiedzy w tej dziedzinie.Przetwarzanie obrazu - Implementowanie filtra Sobel

Czytałem niektóre dokumenty i pliki PDF, ale koncentrują się one na wielu tematach, które moim zdaniem mogą nie być im potrzebne do mojego zadania.

Z przyjemnością poznam Twoje sugestie lub jeśli jest jakiś konkretny dokument, PDF, samouczek lub krótki poradnik do tego celu.

Dziękuję

EDIT:

Dziękuję wszystkim :) Efektem naszej pracy można pobrać ze here.

+0

Głosowanie w celu zamknięcia jako rec rec. –

Odpowiedz

23

To całkiem proste, wystarczy zawęzić obraz z filtrem kamiennym. Filtr Sobela ma dwa jądra, jądro w kierunku X i jądro w kierunku y. Jądro w kierunku X wykrywa poziome linie, a jądra w kierunku y wykrywa pionowe linie.

jądra w kierunku x (wielkość jest 3x3)

float kernelx[3][3] = {{-1, 0, 1}, 
         {-2, 0, 2}, 
         {-1, 0, 1}}; 

jądra kierunku Y

float kernely[3][3] = {{-1, -2, -1}, 
         {0, 0, 0}, 
         {1, 2, 1}}; 

Do obliczania splotu na pikselu (x, y), może określić okno których rozmiar jest równa wielkości ziaren (kod źródłowy w celu obliczenia wielkości x i y w wielkości są identyczne)

double magX = 0.0; // this is your magnitude 

for(int a = 0; a < 3; a++) 
{ 
    for(int b = 0; b < 3; b++) 
    {    
     int xn = x + a - 1; 
     int yn = y + b - 1; 

     int index = xn + yn * width; 
     magX += image[index] * kernelx[a][b]; 
    } 
} 

Zauważ, że dane wejściowe są obrazem w skali szarości i mogą być reprezentowane jako tablica 1D podwójnego (Jest to po prostu sztuczka, ponieważ do wartości piksela w współrzędnych (x, y) można uzyskać dostęp za pomocą indeksu = [x + y * szerokość])

aby obliczyć wielkość w pikselach (x, y) podane magX i Magy:

mag = sqrt (magX^2 + Magy^2)

+1

Czy miałoby sens łączenie poziomych i pionowych ziaren poprzez stworzenie jednej części rzeczywistej i części wyobrażonej, a następnie można znaleźć wielkość poprzez uzyskanie abs (wynik)? – endolith

+0

@ azer89 Wątpię, czy proste mnożenie 'image' i' kernelx' będzie działało, ponieważ wymagamy splotu, prawda? – Shailesh

+0

Spójrz na ten blog: [2d splot] (http://www.songho.ca/dsp/convolution/convolution2d_example.html) – Shailesh

4

Sobel Operator Wikipedia strona jest dobrze opisowe o tym, jak wykonaj to. Istnieją inne operatory, takie jak: Roberts cross i Korzystanie z operacji splotu, można zmienić podejście, zmieniając matrycę jądra. Poniżej może pomóc ci implementacja Sobel i Convolution z użyciem Marvin Framework.

Sobel:

public class Sobel extends MarvinAbstractImagePlugin{ 

    // Definitions 
    double[][] matrixSobelX = new double[][]{ 
      {1,  0, -1}, 
      {2,  0, -2}, 
      {1,  0, -1} 
    }; 
    double[][] matrixSobelY = new double[][]{ 
      {-1, -2,  -1}, 
      {0,  0,  0}, 
      {1,  2,  1} 
    }; 

    private MarvinImagePlugin convolution; 

    public void load(){ 
     convolution = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.convolution.jar"); 
    } 

    public MarvinAttributesPanel getAttributesPanel(){ 
     return null; 
    } 
    public void process 
    (
     MarvinImage imageIn, 
     MarvinImage imageOut, 
     MarvinAttributes attrOut, 
     MarvinImageMask mask, 
     boolean previewMode 
    ) 
    { 
     convolution.setAttribute("matrix", matrixSobelX); 
     convolution.process(imageIn, imageOut, null, mask, previewMode); 
     convolution.setAttribute("matrix", matrixSobelY); 
     convolution.process(imageIn, imageOut, null, mask, previewMode); 
    } 
} 

Splot:

public class Convolution extends MarvinAbstractImagePlugin{ 

    private MarvinAttributesPanel attributesPanel; 
    private MarvinAttributes  attributes; 

    public void process 
    (
     MarvinImage imageIn, 
     MarvinImage imageOut, 
     MarvinAttributes attributesOut, 
     MarvinImageMask mask, 
     boolean previewMode 
    ) 
    { 
     double[][] matrix = (double[][])attributes.get("matrix"); 

     if(matrix != null && matrix.length > 0){ 
      for(int y=0; y<imageIn.getHeight(); y++){ 
       for(int x=0; x<imageIn.getWidth(); x++){ 
        applyMatrix(x, y, matrix, imageIn, imageOut); 
       } 
      } 
     } 
    } 

    private void applyMatrix 
    (
     int x, 
     int y, 
     double[][] matrix, 
     MarvinImage imageIn, 
     MarvinImage imageOut 
    ){ 

     int nx,ny; 
     double resultRed=0; 
     double resultGreen=0; 
     double resultBlue=0; 

     int xC=matrix[0].length/2; 
     int yC=matrix.length/2; 

     for(int i=0; i<matrix.length; i++){ 
      for(int j=0; j<matrix[0].length; j++){ 
       if(matrix[i][j] != 0){  
        nx = x + (j-xC); 
        ny = y + (i-yC); 

        if(nx >= 0 && nx < imageOut.getWidth() && ny >= 0 && ny < imageOut.getHeight()){ 

         resultRed += (matrix[i][j]*(imageIn.getIntComponent0(nx, ny))); 
         resultGreen += (matrix[i][j]*(imageIn.getIntComponent1(nx, ny))); 
         resultBlue += (matrix[i][j]*(imageIn.getIntComponent2(nx, ny))); 
        } 


       } 



      } 
     } 

     resultRed = Math.abs(resultRed); 
     resultGreen = Math.abs(resultGreen); 
     resultBlue = Math.abs(resultBlue); 

     // allow the combination of multiple appications 
     resultRed += imageOut.getIntComponent0(x,y); 
     resultGreen += imageOut.getIntComponent1(x,y); 
     resultBlue += imageOut.getIntComponent2(x,y); 

     resultRed = Math.min(resultRed, 255); 
     resultGreen = Math.min(resultGreen, 255); 
     resultBlue = Math.min(resultBlue, 255); 

     resultRed = Math.max(resultRed, 0); 
     resultGreen = Math.max(resultGreen, 0); 
     resultBlue = Math.max(resultBlue, 0); 

     imageOut.setIntColor(x, y, imageIn.getAlphaComponent(x, y), (int)resultRed, (int)resultGreen, (int)resultBlue); 
    } 

    public void load(){ 
     attributes = getAttributes(); 
     attributes.set("matrix", null); 
    } 

    public MarvinAttributesPanel getAttributesPanel(){ 
     if(attributesPanel == null){ 
      attributesPanel = new MarvinAttributesPanel(); 
      attributesPanel.addMatrixPanel("matrixPanel", "matrix", attributes, 3, 3); 
     } 
     return attributesPanel; 
    } 

} 
+0

Witam Jestem nowy w tym stworzyłem główną klasę i dostałem ten błąd, możesz mi powiedzieć jak napisać dla niego główną klasę? Sobel a = new Sobel(); \t \t MarvinImage imgIn = MarvinImageIO.loadImage ("unnamed2.jpg"); \t \t MarvinImage imgOut = MarvinImageIO.loadImage ("test.jpg"); \t a.process (imgIn, imgOut); Wyjątek w wątku "główny" java.lang.NullPointerException \t w Sobel.process (Sobel.java:41) – Abdullah

14

Najprostsze wyjaśnienie operatora Sobel widziałem do tej pory jest z Saush's blog, entuzjastą tech który kiedyś spotkał samego Sobela:

enter image description here

The post opisano w (nie za dużo) szczegóły sposobu realizacji filtra i akcji Ruby kodu źródłowego dla celów demonstracyjnych:

require 'chunky_png' 

class ChunkyPNG::Image 
    def at(x,y) 
    ChunkyPNG::Color.to_grayscale_bytes(self[x,y]).first 
    end 
end 

img = ChunkyPNG::Image.from_file('engine.png') 

sobel_x = [[-1,0,1], 
      [-2,0,2], 
      [-1,0,1]] 

sobel_y = [[-1,-2,-1], 
      [0,0,0], 
      [1,2,1]] 

edge = ChunkyPNG::Image.new(img.width, img.height, ChunkyPNG::Color::TRANSPARENT) 

for x in 1..img.width-2 
    for y in 1..img.height-2 
    pixel_x = (sobel_x[0][0] * img.at(x-1,y-1)) + (sobel_x[0][1] * img.at(x,y-1)) + (sobel_x[0][2] * img.at(x+1,y-1)) + 
       (sobel_x[1][0] * img.at(x-1,y)) + (sobel_x[1][1] * img.at(x,y)) + (sobel_x[1][2] * img.at(x+1,y)) + 
       (sobel_x[2][0] * img.at(x-1,y+1)) + (sobel_x[2][1] * img.at(x,y+1)) + (sobel_x[2][2] * img.at(x+1,y+1)) 

    pixel_y = (sobel_y[0][0] * img.at(x-1,y-1)) + (sobel_y[0][1] * img.at(x,y-1)) + (sobel_y[0][2] * img.at(x+1,y-1)) + 
       (sobel_y[1][0] * img.at(x-1,y)) + (sobel_y[1][1] * img.at(x,y)) + (sobel_y[1][2] * img.at(x+1,y)) + 
       (sobel_y[2][0] * img.at(x-1,y+1)) + (sobel_y[2][1] * img.at(x,y+1)) + (sobel_y[2][2] * img.at(x+1,y+1)) 

    val = Math.sqrt((pixel_x * pixel_x) + (pixel_y * pixel_y)).ceil 
    edge[x,y] = ChunkyPNG::Color.grayscale(val) 
    end 
end 

edge.save('engine_edge.png') 

Input/Output:

+2

Po prostu bezczynnie skopiowałeś i wkleiłeś powyższy kod (lub był błędny w poście na blogu i jest teraz naprawiony?). sobel_x [0] [4] najwyraźniej nigdy nie zadziała. Powinno to być [0] [1] [1] [1] [2] [1], itp. – Doug

+0

Witam, czy mógłbyś wesprzeć naszą społeczność Image Processing na http://area51.stackexchange.com/proposals/86832. Potrzebujemy ludzi, aby głosowali na pytania z mniej niż 10 głosami. Dziękuję Ci. – Royi

2

Gx estymuje gradient w kierunku x (c olumns) i Gy szacuje gradient w kierunku y (wiersze). So Gy wykrywa linie poziome, a Gx wykrywa pionowe linie.

+1

To powinien być komentarz – ketan