在图片中找到美国国旗?

Finding the American flag in a picture?

为了纪念 7 月 4 日,我有兴趣找到一种程序化的方法来检测图片中的美国国旗。有一个 earlier and popular question about finding Coca-Cola cans in images 描述了许多解决该问题的好技术,但我不确定它们是否适用于标志,因为

  1. 旗帜在风中飘扬,因此可能会自我遮挡或以其他方式非线性变形(这使得像 SIFT 这样的技术更难使用),并且
  2. 与可口可乐罐不同,美国国旗的星条旗并不是美国国旗独有的,并且可能是 flag of Liberia 的一部分,排除了许多 "line signature" 技术。

是否有特别适合这项任务的标准图像处理或识别技术?

我的方法概括了这个问题,实际上是在蓝色区域附近寻找红白条纹图案(水平或垂直)。因此它适用于只有美国国旗有这种图案的场景。

我的方法在 Java 中开发并使用 Marvin Framework

算法:

  1. 仅保留与美国国旗颜色相同的像素的滤色器。
  2. 找到水平的红白条纹图案
  3. 找到垂直的红白条纹图案
  4. 去除小面积的图案(噪声)
  5. 检查此图案是否被蓝色区域包围
  6. 分割区域。

输入:

滤色器:

标志:

更有趣的是在有很多标志的情况下的表现。

输入:

滤色器:

模式匹配:

标志:

源代码:

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

其他结果:

您可以通过 OpenCV 库使用 'Template Matching'。

这是该方法背后的理论:

Template Matching is a method for searching and finding the location of a template image in a larger image. OpenCV comes with a function cv2.matchTemplate() for this purpose. It simply slides the template image over the input image (as in 2D convolution) and compares the template and patch of input image under the template image.

代码示例和实现说明可以在这里找到: http://docs.opencv.org/master/d4/dc6/tutorial_py_template_matching.html#gsc.tab=0