2016-07-04 32 views
24

In onore del 4 luglio, ero interessato a trovare un modo programmatico per rilevare la bandiera americana in una foto. C'è un earlier and popular question about finding Coca-Cola cans in images che descrive una serie di buone tecniche di questo problema, anche se non sono sicuro che lavorerò per le bandiere perchéTrovare la bandiera americana in una foto?

  1. bandiere svolazzano al vento e quindi potrebbe occludere stessi o comunque deformarsi in modo non lineare (che rende le tecniche come SIFT un po 'più difficili da usare), e
  2. a differenza di una lattina di Coca-Cola, le stelle e strisce della bandiera americana non sono uniche per la bandiera americana e potrebbero far parte, per esempio, dello flag of Liberia, escludendo molte tecniche di "firma di linea".

Esistono tecniche di elaborazione o riconoscimento di immagini standard che sarebbero particolarmente adatte a questo compito?

+3

Se si dispone di un grande insieme di dati per la formazione, la ricerca di: profonda imparare classificazione delle immagini. Ci sono stati grandi progressi su questo fronte negli ultimi anni. – Photon

+1

So che sei un noto membro di SO, ma sembra che 1) non sia stato fatto alcun tentativo. 2) la domanda è troppo ampia. –

+0

@SalvadorDali, amico ha risposto alla sua domanda qui sotto. Buona risposta anche (ho appena fatto il 16esimo upvotes). Penso che Asker sia aperto a più risposte/approcci però. –

risposta

33

Il mio approccio generalizza il problema e in effetti cerca un motivo a strisce rosse e bianche (orizzontale o verticale) vicino a una regione blu. Quindi funziona per scene che solo la bandiera americana ha questo schema.

Il mio approccio è sviluppato in Java e utilizza Marvin Framework.

Algoritmo:

  1. filtro a colori per mantenere solo i pixel dello stesso colore della bandiera americana.
  2. cerca orizzontale rosso e strisce bianche modello
  3. Trova verticale rossa e strisce bianche modello
  4. Rimuovere modelli con piccola area (rumore)
  5. Verifica se questo modello è circondata da una regione blu
  6. Segmento zona .

ingresso:

enter image description here

Filtro Colore:

enter image description here

Bandiera:

enter image description here

Più interessante è la performance nel caso ci siano molte bandiere.

ingresso:

enter image description here

Colour Filter:

enter image description here

Pattern Matching:

enter image description here

Bandiera:

enter image description here

Source Code:

import static marvin.MarvinPluginCollection.*; 

public class AmericanFlag { 

    public AmericanFlag(){ 
     process("./res/flags/", "flag_0", Color.yellow); 
     process("./res/flags/", "flag_1", Color.yellow); 
     process("./res/flags/", "flag_2", Color.yellow); 
     process("./res/flags/", "flag_3", Color.yellow); 
     process("./res/flags/", "flag_4", Color.blue); 
    } 

    private void process(String dir, String fileName, Color color){ 
     MarvinImage originalImage = MarvinImageIO.loadImage(dir+fileName+".jpg"); 
     MarvinImage image = originalImage.clone(); 
     colorFilter(image); 
     MarvinImageIO.saveImage(image, dir+fileName+"_color.png"); 

     MarvinImage output = new MarvinImage(image.getWidth(), image.getHeight()); 
     output.clear(0xFFFFFFFF); 
     findStripsH(image, output); 
     findStripsV(image, output); 
     MarvinImageIO.saveImage(output, dir+fileName+"_1.png"); 

     MarvinImage bin = MarvinColorModelConverter.rgbToBinary(output, 127); 
     morphologicalErosion(bin.clone(), bin, MarvinMath.getTrueMatrix(5, 5)); 
     morphologicalDilation(bin.clone(), bin, MarvinMath.getTrueMatrix(15, 15)); 
     MarvinImageIO.saveImage(bin, dir+fileName+"_2.png"); 

     int[] centroid = getCentroid(bin); 
     image.fillRect(centroid[0], centroid[1], 30, 30, Color.yellow); 

     int area = getMass(bin); 
     boolean blueNeighbors = hasBlueNeighbors(image, bin, centroid[0], centroid[1], area); 

     if(blueNeighbors){ 
      int[] seg = getSegment(bin); 
      for(int i=0; i<4; i++){ 
       originalImage.drawRect(seg[0]+i, seg[1]+i, seg[2]-seg[0], seg[3]-seg[1], color); 
      } 
      MarvinImageIO.saveImage(originalImage, dir+fileName+"_final.png"); 
     } 
    } 

    private boolean hasBlueNeighbors(MarvinImage image, MarvinImage bin, int centerX, int centerY, int area){ 
     int totalBlue=0; 
     int r,g,b; 
     int maxDistance = (int)(Math.sqrt(area)*1.2); 
     for(int y=0; y<image.getHeight(); y++){ 
      for(int x=0; x<image.getWidth(); x++){ 
       r = image.getIntComponent0(x, y); 
       g = image.getIntComponent1(x, y); 
       b = image.getIntComponent2(x, y); 

       if(
        (b == 255 && r == 0 && g == 0) && 
        (MarvinMath.euclideanDistance(x, y, centerX, centerY) < maxDistance) 
       ){ 
        totalBlue++; 
        bin.setBinaryColor(x, y, true); 
       } 
      } 
     } 

     if(totalBlue > area/5){ 
      return true; 
     } 
     return false; 
    } 

    private int[] getCentroid(MarvinImage bin){ 
     long totalX=0, totalY=0, totalPixels=0; 
     for(int y=0; y<bin.getHeight(); y++){ 
      for(int x=0; x<bin.getWidth(); x++){ 

       if(bin.getBinaryColor(x, y)){ 
        totalX += x; 
        totalY += y; 
        totalPixels++; 
       } 
      } 
     } 

     totalPixels = Math.max(1, totalPixels); 
     return new int[]{(int)(totalX/totalPixels), (int)(totalY/totalPixels)}; 
    } 

    private int getMass(MarvinImage bin){ 
     int totalPixels=0; 
     for(int y=0; y<bin.getHeight(); y++){ 
      for(int x=0; x<bin.getWidth(); x++){ 
       if(bin.getBinaryColor(x, y)){ 
        totalPixels++; 
       } 
      } 
     } 

     return totalPixels; 
    } 

    private int[] getSegment(MarvinImage bin){ 
     int x1=-1, x2=-1, y1=-1, y2=-1; 
     for(int y=0; y<bin.getHeight(); y++){ 
      for(int x=0; x<bin.getWidth(); x++){ 
       if(bin.getBinaryColor(x, y)){ 

        if(x1 == -1 || x < x1){ x1 = x; } 
        if(x2 == -1 || x > x2){ x2 = x; } 
        if(y1 == -1 || y < y1){ y1 = y; } 
        if(y2 == -1 || y > y2){ y2 = y; } 
       } 
      } 
     } 
     return new int[]{x1,y1,x2,y2}; 
    } 

    private void findStripsH(MarvinImage imageIn, MarvinImage imageOut){ 

     int strips=0; 
     int totalPixels=0; 
     int r,g,b; 
     int patternStart; 
     boolean cR=true; 
     int patternLength = -1; 
     for(int y=0; y<imageIn.getHeight(); y++){ 
      patternStart = -1; 
      strips = 0; 
      patternLength=-1; 
      for(int x=0; x<imageIn.getWidth(); x++){ 
       r = imageIn.getIntComponent0(x, y); 
       g = imageIn.getIntComponent1(x, y); 
       b = imageIn.getIntComponent2(x, y); 

       if(cR){ 
        if(r == 255 && g == 0 && b == 0){ 
         if(patternStart == -1){ patternStart = x;} 
         totalPixels++; 
        } else{ 
         if(patternLength == -1){ 
          if(totalPixels >=3 && totalPixels <= 100){ 
           patternLength = (int)(totalPixels); 
          } else{ 
           totalPixels=0; patternStart=-1; strips=0; patternLength=-1; 
          } 
         } else{ 
          if(totalPixels >= Math.max(patternLength*0.5,3) && totalPixels <= patternLength * 2){ 
           strips++; 
           totalPixels=1; 
           cR = false; 
          } else{ 
           totalPixels=0; patternStart=-1; strips=0; patternLength=-1; 
          } 
         } 
        } 
       } 
       else{ 
        if(r == 255 && g == 255 && b == 255){ 
         totalPixels++; 
        } else{ 
         if(totalPixels >= Math.max(patternLength*0.5,3) && totalPixels <= patternLength * 2){ 
          strips++; 
          totalPixels=1; 
          cR = true; 
         } else{ 
          totalPixels=0; patternStart=-1; strips=0; patternLength=-1; cR=true; 
         } 
        } 
       } 


       if(strips >= 4){ 
        imageOut.fillRect(patternStart, y, x-patternStart, 2, Color.black); 
        totalPixels=0; patternStart=-1; strips=0; patternLength=-1; cR=true; 
       } 
      } 
     } 
    } 

    private void findStripsV(MarvinImage imageIn, MarvinImage imageOut){ 

     int strips=0; 
     int totalPixels=0; 
     int r,g,b; 
     int patternStart; 
     boolean cR=true; 
     int patternLength = -1; 
     for(int x=0; x<imageIn.getWidth(); x++){ 
      patternStart = -1; 
      strips = 0; 
      patternLength=-1; 
      for(int y=0; y<imageIn.getHeight(); y++){ 
       r = imageIn.getIntComponent0(x, y); 
       g = imageIn.getIntComponent1(x, y); 
       b = imageIn.getIntComponent2(x, y); 

       if(cR){ 
        if(r == 255 && g == 0 && b == 0){ 
         if(patternStart == -1){ patternStart = y;} 
         totalPixels++; 
        } else{ 
         if(patternLength == -1){ 
          if(totalPixels >=3 && totalPixels <= 100){ 
           patternLength = (int)(totalPixels); 
          } else{ 
           totalPixels=0; patternStart=-1; strips=0; patternLength=-1; 
          } 
         } else{ 
          if(totalPixels >= Math.max(patternLength*0.5,3) && totalPixels <= patternLength * 2){ 
           strips++; 
           totalPixels=1; 
           cR = false; 
          } else{ 
           totalPixels=0; patternStart=-1; strips=0; patternLength=-1; 
          } 
         } 
        } 

//     if(maxL != -1 && totalPixels > maxL){ 
//      totalPixels=0; patternStart=-1; strips=0; maxL=-1; 
//     } 
       } 
       else{ 
        if(r == 255 && g == 255 && b == 255){ 
         totalPixels++; 
        } else{ 
         if(totalPixels >= Math.max(patternLength*0.5,3) && totalPixels <= patternLength * 2){ 
          strips++; 
          totalPixels=1; 
          cR = true; 
         } else{ 
          totalPixels=0; patternStart=-1; strips=0; patternLength=-1; cR=true; 
         } 
        } 

//     if(maxL != -1 && totalPixels > maxL){ 
//      totalPixels=0; patternStart=-1; strips=0; maxL=-1; 
//      cR=true; 
//     } 
       } 


       if(strips >= 4){ 
        imageOut.fillRect(x, patternStart, 2, y-patternStart, Color.black); 
        totalPixels=0; patternStart=-1; strips=0; patternLength=-1; cR=true; 
       } 
      } 
     } 
    } 

    private void colorFilter(MarvinImage image){ 

     int r,g,b; 
     boolean isR, isB; 
     for(int y=0; y<image.getHeight(); y++){ 
      for(int x=0; x<image.getWidth(); x++){ 
       r = image.getIntComponent0(x, y); 
       g = image.getIntComponent1(x, y); 
       b = image.getIntComponent2(x, y); 

       isR = (r > 120 && r > g * 1.3 && r > b * 1.3); 
       isB = (b > 30 && b < 150 && b > r * 1.3 && b > g * 1.3); 

       if(isR){ 
        image.setIntColor(x, y, 255,0,0); 
       } else if(isB){ 
        image.setIntColor(x, y, 0,0,255); 
       } else{ 
        image.setIntColor(x, y, 255,255,255); 
       } 
      } 
     } 
    } 

    public static void main(String[] args) { 
     new AmericanFlag(); 
    } 
} 

Altri Risultati:

enter image description here

enter image description here

enter image description here

+1

Ma non l'hai provato con la "bandiera della Liberia" ... o è la nuova sfida? –

+1

La maggior parte degli esempi sembrano non avere né le strisce orizzontali né verticali descritte dall'algoritmo, più come diagonale. È solo una discrepanza nel modo in cui hai formulato il tuo algoritmo? Stai davvero cercando specificamente per strisce orizzontali o verticali o qualsiasi orientamento? O stai unendo in qualche modo orizzontale e verticale per trovare la diagonale? – pabrams

+1

Infatti hanno. Permettetemi di definire questo modello in un altro modo: un'alternanza di colore tra rosso e bianco nella direzione orizzontale o verticale. Una bandiera ruotata di 45 gradi, ad esempio, ha un motivo a strisce sia verticali che orizzontali. Se attraversi la bandiera americana con una linea NON PARALLELA sulle strisce vedrai alternanza tra rosso e bianco. Ma, il modello sarà più intenso quando questa linea è ortogonale alle strisce. Sì, ho unito i risultati orizzontali e verticali. –

1

Si potrebbe utilizzare 'Corrispondenza Template' tramite la libreria OpenCV.

Qui è la teoria dietro l'approccio:

Template Matching è un metodo per la ricerca e trovare la posizione di immagine di un modello in un'immagine più grande. OpenCV viene fornito con una funzione cv2.matchTemplate() per questo scopo. Sposta semplicemente l'immagine modello sull'immagine di input (come in una convoluzione 2D) e confronta il modello e la patch dell'immagine di input sotto l'immagine modello.

esempi di codice e la spiegazione implementazione può essere trovato qui: http://docs.opencv.org/master/d4/dc6/tutorial_py_template_matching.html#gsc.tab=0