关于算法:在图片中查找美国国旗?

Finding the American flag in a picture?

为了纪念7月4日,我有兴趣找到一种在图片中探测美国国旗的程序性方法。关于在图片中找到可口可乐罐,有一个早先流行的问题描述了许多解决这个问题的好方法,尽管我不确定它们是否适用于旗帜,因为

  • 旗子在风中飘动,因此可能会自我封闭或非线性变形(这使得筛选等技术更难使用),以及
  • 与可口可乐罐不同的是,美国国旗的星条旗不是美国国旗独有的,也可能是利比里亚国旗的一部分,排除了许多"线签名"技术。
  • 是否有任何标准的图像处理或识别技术特别适合这项任务?


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

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

    算法:

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

    enter image description here

    滤色器:

    enter image description here

    旗帜:

    enter image description here

    更有趣的是,在有许多标志的情况下的性能。

    输入:

    enter image description here

    滤色器:

    enter image description here

    图案匹配:

    enter image description here

    旗帜:

    enter image description here

    源代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    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();
        }
    }

    其他结果:

    enter image description here

    enter image description here

    enter image description here


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

    以下是该方法背后的理论:

    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