2016-07-04 7 views
24

Na cześć czwartego lipca, byłem zainteresowany znalezieniem programowego sposobu wykrywania flagi amerykańskiej na zdjęciu. Jest earlier and popular question about finding Coca-Cola cans in images który opisuje szereg dobrych technik tego problemu, choć nie jestem pewien, że oni pracują dla flagi boOdnalezienie amerykańskiej flagi na zdjęciu?

  1. flagi trzepoczą na wietrze, a zatem może zamykać się lub w inny sposób deformować nieliniowo (co sprawia, że ​​techniki takie jak SIFT są nieco trudniejsze w użyciu) i, w przeciwieństwie do puszki Coca-Coli, amerykańskie flagi gwiazd i pasków nie są unikalne dla amerykańskiej flagi i mogą być częścią, powiedzmy, flag of Liberia, wykluczając wiele techniki "podpisu linii".

Czy istnieją standardowe techniki przetwarzania lub rozpoznawania obrazów, które byłyby szczególnie przydatne w tym zadaniu?

+3

Jeśli masz duży zbiór danych na szkolenia, szukaj: głębokie nauki klasyfikacji obrazu. W ostatnich latach nastąpił wielki postęp w tym zakresie. – Photon

+1

Wiem, że jesteś dobrze znanym członkiem SO, ale wygląda na to, że 1) żadna próba nie została podana. 2) pytanie jest zbyt szerokie. –

+0

@ SalvadorDali, koleś odpowiedział na własne pytanie poniżej. Dobra odpowiedź też (właśnie zrobiłem 16-te przegrane). Myślę, że Asker jest otwarty na więcej odpowiedzi/podejść. –

Odpowiedz

33

Moje podejście uogólnia problem i faktycznie szuka czerwonego i białego wzoru pasków (poziomego lub pionowego) w pobliżu niebieskiego obszaru. Dlatego działa dla scen, które tylko amerykańska flaga ma ten wzór.

Moje podejście zostało opracowane w języku Java i używa Marvin Framework.

Algorytm:

  1. filtr kolorów na utrzymanie tylko piksele o tym samym kolorze amerykańskiej flagi.
  2. Znajdź poziome czerwone i białe paski wzór
  3. Znajdź pionowe czerwone i białe paski wzór
  4. Usuń wzory z małym obszarze (hałas)
  5. sprawdzić, czy ten wzór jest otoczony niebieskim regionu
  6. Segment obszar .

Wejście:

enter image description here

Filtr Kolor:

enter image description here

Bandera:

enter image description here

Bardziej interesujący jest wynik w przypadku, gdy istnieje wiele flag.

Wejście:

enter image description here

Filtr kolorów:

enter image description here

Wzór Matching:

enter image description here

Bandera:

enter image description here

Kod źródłowy:

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

Inne Wyniki:

enter image description here

enter image description here

enter image description here

+1

Ale nie przetestowałeś go "flagą Liberii" ... czy to nowe wyzwanie? –

+1

Większość przykładów wygląda na to, że nie mają poziomych ani pionowych pasków opisanych przez algorytm - bardziej przypominają przekątną. Czy to tylko rozbieżność w sposobie sformułowania swojego algorytmu? Czy naprawdę szukasz tylko poziome lub pionowe paski, lub jakiejkolwiek orientacji? Czy w jakiś sposób łączysz poziome i pionowe, aby znaleźć przekątną? – pabrams

+1

W rzeczywistości mają. Pozwól mi zdefiniować ten wzór w inny sposób: zmianę koloru między czerwonym i białym w kierunku poziomym lub pionowym. Na przykład flaga obrócona o 45 stopni ma zarówno poziome, jak i pionowe paski. Jeśli przekroczysz amerykańską flagę z linią NIE RÓWNOLEGŁĄ do pasków, zobaczysz przemianę czerwieni i bieli. Wzorzec będzie jednak bardziej intensywny, gdy linia będzie prostopadła do pasków. Tak, połączyłem wyniki w poziomie i w pionie. –

1

Można użyć '' Template Matching poprzez biblioteki OpenCV.

Oto teorię podejścia:

Template Matching jest sposób poszukiwania i znalezienia lokalizacji szablonu obrazek w większej obrazu. OpenCV ma w tym celu funkcję cv2.matchTemplate(). Po prostu przesuwa obraz szablonu o obraz wejściowy (jak w przypadku splotu 2D) i porównuje szablon i poprawkę obrazu wejściowego pod obrazem szablonu.

przykłady kodu i wyjaśnienia wdrożenia można znaleźć tutaj: http://docs.opencv.org/master/d4/dc6/tutorial_py_template_matching.html#gsc.tab=0