在图片中找到美国国旗?

Posted

技术标签:

【中文标题】在图片中找到美国国旗?【英文标题】:Finding the American flag in a picture? 【发布时间】:2016-11-06 11:02:34 【问题描述】:

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

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

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

【问题讨论】:

如果你有大数据集进行训练,搜索:深度学习图像分类。近年来,这方面取得了很大进展。 我知道您是 SO 的知名成员,但看起来 1) 没有尝试。 2)问题太宽泛了。 @SalvadorDali,老兄,他在下面回答了自己的问题。答案也很好(我刚刚获得了第 16 次投票)。我认为 Asker 对更多答案/方法持开放态度。 @VC.One 伙计,你到底在哪里看到他回答他的问题?写出好的答案的人完全是另一个人。 @SalvadorDali,我的错,我从您评论的时间戳中认为您正在回复 Bounty Asker。这一切都是那么的超现实...... 【参考方案1】:

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

我的方法是用 Java 开发的,使用 Marvin Framework。

算法:

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

输入:

滤色器:

标志:

更有趣的是在flag多的情况下的表现。

输入

滤色器:

模式匹配:

标志:

源代码:

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

其他结果:

【讨论】:

但是你没有用“利比里亚国旗”来测试它……还是这是新的挑战? 大多数示例看起来既没有算法描述的水平条纹也没有垂直条纹——更像是对角线。这只是您对算法的措辞方式的差异吗?您是否真的专门寻找水平或垂直条纹,或任何方向?还是您以某种方式合并水平和垂直以找到对角线? 事实上他们有。让我用另一种方式来定义这种模式:在水平或垂直方向上红色和白色之间的颜色交替。例如,旋转 45 度的旗帜具有垂直和水平条纹图案。如果你用一条与条纹不平行的线穿过美国国旗,你会看到红色和白色之间的交替。但是,当这条线与条纹正交时,图案会更加强烈。是的,我合并了水平和垂直结果。【参考方案2】:

您可以通过 OpenCV 库使用“模板匹配”。

这是该方法背后的理论:

模板匹配是一种搜索和查找位置的方法 在更大的图像中的模板图像。 OpenCV自带一个函数 cv2.matchTemplate() 用于此目的。它只是滑动模板 输入图像上的图像(如在 2D 卷积中)并比较 模板图像下输入图像的模板和补丁。

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

【讨论】:

以上是关于在图片中找到美国国旗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Android 的 LISTVIEW 中为每个原​​始图像添加不同的图像?

Java爬虫之下载全世界国家的国旗图片

红警2中如何建立类似于国旗,闸门之类的设备

美国亚马逊图片打不开

超喜庆的微信渐变国旗头像,最全版!

美国人登月有几次