2013-07-23 14 views
23

Ho un compito per implementare il filtro Sobel che è, come noto, un filtro di elaborazione delle immagini per il rilevamento dei bordi. Ma sfortunatamente, non ho esperienza nel campo dell'elaborazione delle immagini, nella misura in cui non so nemmeno come siano rappresentate le immagini nel computer. Totalmente nessuna conoscenza in questo campo.Elaborazione delle immagini - Implementazione del filtro Sobel

Ho letto alcuni documenti e PDF ma si concentrano su molti argomenti che ritengo che potrei non averne bisogno per il mio compito.

Sarei felice di conoscere i vostri suggerimenti o se c'è qualche particolare documento, PDF, tutorial o guida rapida per questo scopo.

Grazie

EDIT:

Grazie a tutti :) Il risultato del nostro lavoro può essere scaricato da here.

+0

Voting per chiudere come risorsa rec. –

risposta

23

È abbastanza semplice, devi solo convoluare la tua immagine con un filtro sobel. Un filtro Sobel ha due kernel, kernel x-direction e kernel y-direction. Il kernel della direzione x rileva linee orizzontali, mentre i kernel di direzione y rilevano linee verticali.

direzione x kernel (la dimensione è 3x3)

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

kernel direzione y

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

Per calcolare la convoluzione a pixel (x, y), definire una finestra la cui dimensione è pari alla dimensione del kernel (codice sorgente per calcolare grandezza in x e la grandezza in y sono identici):

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]; 
    } 
} 

Si noti che l'input è un'immagine in scala di grigio e può essere rappresentato come una matrice 1D di doppio (Questo è solo un trucco, poiché è possibile accedere a un valore di pixel in coordinate (x, y) con index = [x + y * width])

per calcolare grandezza in pixel (x, y) data magX e Mago:

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

+1

avrebbe senso unire i kernel orizzontali e verticali facendone una la parte reale e una la parte immaginaria, e quindi si può trovare la magnitudine ottenendo gli addominali (il risultato)? – endolith

+0

@ azer89 Dubito che la semplice moltiplicazione di 'image' e' kernelx' funzionerà, dal momento che richiede la convoluzione, giusto? – Shailesh

+0

Guarda questo blog: [2d convoluzione] (http://www.songho.ca/dsp/convolution/convolution2d_example.html) – Shailesh

4

Sobel Operator pagina di Wikipedia è ben descrittiva su come eseguirlo. Esistono altri operatori come Roberts cross e Prewitt

Utilizzando l'operazione di convoluzione, è possibile cambiare approccio cambiando la matrice del kernel. Di seguito, l'implementazione di Sobel e Convolution utilizzando Marvin Framework può essere di aiuto.

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

convoluzione:

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

Ciao, io sono nuovo a questo ho creato la classe principale e ho ottenuto questo errore puoi dirmi come scrivere la classe principale per questo? Sobel a = new Sobel(); \t \t MarvinImage imgIn = MarvinImageIO.loadImage ("unnamed2.jpg"); \t \t MarvinImage imgOut = MarvinImageIO.loadImage ("test.jpg"); \t \t a.processo (imgIn, imgOut); Eccezione nel thread "main" java.lang.NullPointerException \t in Sobel.process (Sobel.java:41) – Abdullah

14

Il più semplice spiegazione dell'operatore Sobel che ho visto a questa data è da Saush's blog, un appassionato di tecnologia chi ha incontrato Sobel in persona:

enter image description here

The post descrive (non troppi) Dettagli come implementare il filtro, e condivide Rubino codice sorgente per scopi dimostrativi:

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

Solo pigramente, hai copiato e incollato il codice sopra sbagliato (o era sbagliato nel post del blog ed è ora fisso?). sobel_x [0] [4] ovviamente non funzionerà mai. Dovrebbe essere [0] [1] [1] [1] [2] [1], ecc. – Doug

+0

Ciao, potresti sostenere la nostra comunità di elaborazione delle immagini su http://area51.stackexchange.com/proposals/86832. Abbiamo bisogno che la gente elimini le domande con meno di 10 voti. Grazie. – Royi

2

Gx sta stimando il gradiente nella direzione x (c olumns) e Gy sta valutando il gradiente nella direzione y (righe). Quindi Gy rileva linee orizzontali e Gx rileva linee verticali.

+1

Questo dovrebbe essere un commento – ketan