国旗判定器を作る
国旗の判別器を作ります。あらかじめいくつかの国旗画像についてBGR各チャンネルの輝度の比率を求めたデータ(判定用リスト)を作成しておいて、それと比較することでどこの国旗かを判別します。
判定用リストのBGR比率(添え字c)と判定したい画像のBGR比率(添え字t)から下式により距離を求めそれが最も小さいものを一致と判定します。
ただし、国旗の場合は色の比率が同じで並び順や向きが違うだけのものがあるので、画像全体同志を比較しても間違える場合があります。例えばモナコとポーランドやフランスとロシアは画像全体の輝度を比較しても区別がつかない可能性があります。なので①画像全体のBGR比率②画像上半分のBGR比率③画像左半分のBGR比率を求め、①②③の距離の合計が最も小さいものを正解とします。
フランス
ロシア
使った関数・メソッド
- os.listdir() : フォルダ内のファイルのリスト生成
- .split() : 文字列の分割
- cv2.imread() : 画像ファイルの読み出し
- .shape() : オブジェクト形状情報の取得
- numpy.sum() : ndarrayの合計
- .append() : リストへのデータ追加
- cv2.blur() : 画像をぼかす
- cv2.imwrite() : 画像を保存する
環境
準備
国旗画像はこちらのサイト(無料で使えるEPSフリー素材集)からダウンロードさせていただき、jupyter notebookファイル(***.ipynb)と同じディレクトリにflagフォルダを作りそこに保存しました。
コード
import os # osのインポート import cv2 # OpenCVのインポート import numpy as np # numpyをnpという名前でインポート import csv # CSVモジュールのインポート list_bgr = [] # データ格納用リストの生成 dir_path = 'flag/' # 画像ファイルのあるフォルダの相対パス files = os.listdir(dir_path) # フォルダ内のファイルのリスト生成 for i in files: # ファイルの数だけ繰り返す filename = i.split('.')[0] # ファイル名iを'.'で分割し前の部分を取得(拡張子の除去) img = cv2.imread(dir_path+i) # 画像の読み出し height,width = img.shape[0:2] # 画像の高さ、幅の取得 half_height = int(height/2) #画像の高さの半分 half_width = int(width/2) #画像の幅の半分 img_top = img[:half_height] #上半分の画像生成 img_left = img[:,:half_width] #左半分の画像生成 #BGR各チャンネルの比率を求める bgr = np.sum(img ,axis = (0,1)) b_ratio,g_ratio,r_ratio = bgr/np.sum(bgr) #上半分の画像のBGR各チャンネルの比率を求める bgr_top = np.sum(img_top ,axis = (0,1)) b_top_ratio,g_top_ratio,r_top_ratio = bgr_top/np.sum(bgr_top) #左半分の画像のBGR各チャンネルの比率を求める bgr_left = np.sum(img_left ,axis = (0,1)) b_left_ratio,g_left_ratio,r_left_ratio = bgr_left/np.sum(bgr_left) #求めたチャンネル比率をリストに追加 list_bgr.append([filename,b_ratio,g_ratio,r_ratio,b_top_ratio,g_top_ratio,r_top_ratio,b_left_ratio,g_left_ratio,r_left_ratio]) #リストをcsvファイルに保存 with open('flag_check/flag_check.csv', 'w') as file: writer = csv.writer(file, lineterminator='\n') writer.writerows(list_bgr)
実行結果
falg_checkフォルダ内にfalg_check.csvファイルが作られます。
環境
falg_check.csvの中身は
- ファイル名の拡張子を除いたもの (1つ)
- 国旗画像のBGRそれぞれのチャンネルの輝度の比率 (3つ)
- 国旗画像上半分のBGRそれぞれのチャンネルの輝度の比率 (3つ)
- 国旗画像左半分のBGRそれぞれのチャンネルの輝度の比率 (3つ)
の10個のデータからなる行が15行あります。
America,0.3116852184502583,0.2514435494522234,0.43687123209751827,0.34379657025093274,0.2641074721460394,0.3920959576030279,0.3529191176399787,0.2724268270724185,0.3746540552876028
China,0.16002851621690126,0.018300389906075035,0.8216710938770238,0.15352574074172512,0.03622757121477695,0.810246688043498,0.1535735991534046,0.03609563271765373,0.8103307681289417
Cote d'Ivoire,0.23645033540774266,0.39670249737331287,0.3668471672189445,0.23645033540774266,0.39670249737331287,0.3668471672189445,0.15856258532662565,0.3645433973555759,0.47689401731779846
French,0.3557913876801857,0.286192956675377,0.3580156556444373,0.3557913876801857,0.286192956675377,0.3580156556444373,0.4420811338013423,0.3689372816539746,0.1889815845446831
Hungary,0.29717072630032976,0.3178614644239384,0.3849678092757318,0.2544255342091548,0.20164261452473806,0.5439318512661071,0.29717072630032976,0.3178614644239384,0.3849678092757318
India,0.23834233889264014,0.3986363600569844,0.3630213010503755,0.1585064813721861,0.36573220706297976,0.47576131156483414,0.23833313940747497,0.39862388018095546,0.3630429804115696
Ireland,0.2270190621237196,0.4101682987040835,0.3628126391721969,0.2270190621237196,0.4101682987040835,0.3628126391721969,0.32292837170407923,0.4530143465649254,0.22405728173099537
Italy,0.30111956454506095,0.3154697841281623,0.38341065132677676,0.30111956454506095,0.3154697841281623,0.38341065132677676,0.3426493009794041,0.44401374783574954,0.21333695118484636
Japan,0.3161634813055528,0.3161634813055528,0.36767303738889445,0.31626752142313114,0.31626752142313114,0.36746495715373767,0.3161635160519748,0.3161635160519748,0.36767296789605036
Korea,0.33559650177649697,0.32370781945352384,0.3406956787699792,0.3262126058547528,0.31592288882487674,0.35786450532037045,0.33270625565418904,0.3212402153469724,0.34605352899883857
Libya,0.34579439252336447,0.6542056074766355,0.0,0.34579439252336447,0.6542056074766355,0.0,0.34579439252336447,0.6542056074766355,0.0
Malaysia,0.30522216804166274,0.2771933844497218,0.41758444750861545,0.30978991747916296,0.2950339651240494,0.39517611739678765,0.3260010859560982,0.3135074088567246,0.3604915051871771
Monaco,0.28458708669809996,0.2519744487457532,0.4634384645561469,0.13360323886639677,0.0,0.8663967611336032,0.28458708669809996,0.2519744487457532,0.4634384645561469
Poland,0.2844462884619659,0.2517429670077276,0.4638107445303065,0.3333333333333333,0.3333333333333333,0.3333333333333333,0.2844462884619659,0.2517429670077276,0.4638107445303065
Russia,0.3503754506763534,0.26659257050430574,0.3830319788193409,0.3719723853151816,0.3359958707013356,0.2920317439834828,0.3503754506763534,0.26659257050430574,0.3830319788193409
続いて、国旗の画像を与えてCSVファイル 距離の近い順にソートして表示します。また最も距離の近い画像を表示します。
インドの国旗をtest.pngという名前でflag_checkフォルダ内に保存し、コードを実行します。
test.png
コード
# 画像のインライン表示のためのjupiter notebookコマンド %matplotlib inline import cv2 # OpenCVのインポート import numpy as np # numpyをnpという名前でインポート import csv # CSVモジュールのインポート import pprint # pprintモジュールのインポート from matplotlib import pyplot as plt img = cv2.imread('flag_check/test.png') # 画像の読み出し height,width = img.shape[0:2] # 画像の高さ、幅の取得 half_height = int(height/2) #画像の高さの半分 half_width = int(width/2) #画像の幅の半分 img_top = img[:half_height] #上半分の画像生成 img_left = img[:,:half_width] #左半分の画像生成 # BGR各チャンネルの比率を求める bgr = np.sum(img ,axis = (0,1)) b_r,g_r,r_r = bgr/np.sum(bgr) # 上半分の画像のBGR各チャンネルの比率を求める bgr_top = np.sum(img_top ,axis = (0,1)) b_top_r,g_top_r,r_top_r = bgr_top/np.sum(bgr_top) # 左半分の画像のBGR各チャンネルの比率を求める bgr_left = np.sum(img_left ,axis = (0,1)) b_left_r,g_left_r,r_left_r = bgr_left/np.sum(bgr_left) check_result = [] # 比較用リストの生成 with open('flag_check/flag_check.csv') as f: # flag_check.csvを開く reader = csv.reader(f) # test.pngのBGR値とflag_check.csvの各行のBGR値の距離を求めリストcheck_resultに加える for row in reader: filename,b_ratio,g_ratio,r_ratio,b_top_ratio,g_top_ratio,r_top_ratio,b_left_ratio,g_left_ratio,r_left_ratio = row distance = ((b_r-float(b_ratio))**2+(g_r-float(g_ratio))**2+(r_r-float(r_ratio))**2)**0.5 distancet = ((b_top_r-float(b_top_ratio))**2+(g_top_r-float(g_top_ratio))**2+(r_top_r-float(r_top_ratio))**2)**0.5 distancel = ((b_left_r-float(b_left_ratio))**2+(g_left_r-float(g_left_ratio))**2+(r_left_r-float(r_left_ratio))**2)**0.5 distance_sum = distance+distancet+distancel check_result.append([filename,distance_sum,distance,distancet,distancel]) sortsecond = lambda val: val[1] # リスト2番目の数値(インデックス1)を参照する関数 check_result.sort(key=sortsecond) # リスト2番目の数値をキーにしてcheck_resultをソート ans_img = cv2.imread('flag/'+check_result[0][0]+'.png') # check_result一番目の行のfile名を読み出し同じ名前のpngファイルをans_imgへ ans_img_rgb = cv2.cvtColor(ans_img, cv2.COLOR_BGR2RGB) # ans_imgをRGBに変換表示 plt.imshow(ans_img_rgb) # ans_img_rgbを表示 pprint.pprint(check_result,width=120) # リストを表示(行サイズ120文字で折り返し)
実行結果
別の画像で試します。 モナコ国旗をtest.pngとして保存しプログラムを実行します。
test.png
実行結果
以下のサイトを参考にさせていただきました
Pythonのリストのソートまとめ|sort(), sorted(), reverse()