画像のカラー抽出を頑張った話。
※当記事は2024年3月に執筆した記事で、情報は当時のものになります。
今回の概要
私は普段の業務では、全量が数千ページあるWebサイトの運用を担当しています。
今回はそんな運用業務のなかで、数万件ある画像内のカラーコードを全てチェックしたいという依頼が舞い込んできました。
画像の中に特定の色が入っていた場合、その画像のディレクトリパスを書き出し、一覧化してほしいと言われ、やってやろうじゃないかと意気込んでツール開発に取り組むことになりました。
が、タイトルから察する通り、完全な要望に応えられるチェックツールにならず、自動化と人力の狭間をふらふらと歩くツールになっています。
どうぞ、ドラ◯もんのような生暖かい目でご覧ください。
開発環境
OS:Mac OS
言語:Python 3.9.0
ライブラリ:pandas, extcolors, colormap, svglib, rlPyCairo
※Pillowのバージョンは9.5.0
仕様
ざっくりとツールの概要
画像からカラーコードを抽出し、指定したカラーコードが画像内から見つかった場合に、ディレクトリパスを一覧化する。
自動化のために、ディレクトリを指定して、その配下の画像全てに対して同じ処理をかけていく。
コード詳細
#=== init
dir_path = "/img/test"
for current_dir, sub_dirs, files_list in os.walk(dir_path):
for file_name in files_list:
path = os.path.join(current_dir,file_name)
# png,PNG,jpg,JPG,jpeg,JPEG,gif,ico,webp,svg
flag = path.endswith('.png') or path.endswith('.PNG') or path.endswith('.jpg') or path.endswith('.JPG') or path.endswith('.jpeg') or path.endswith('.JPEG') or path.endswith('.gif') or path.endswith('.ico') or path.endswith('.webp') or path.endswith('.svg')
if flag:
exact_color(path, 900, 36, 2.5)
「dir_path」に検索対象のディレクトリをフルパスで記載する。
「flag」で拡張子を判定するため、対象となる拡張子は全て記載する。
def exact_color(input_image, resize, tolerance, zoom):
#=== Dataframeの作成
# svgファイルだった場合、pngに変換してチェックする。svg以外の時はリサイズの判定を挟む。
if input_image.endswith('.svg'):
drawing = svg2rlg(input_image)
renderPM.drawToFile(drawing, "output_svg.png", fmt="PNG")
img_real = 'output_svg.png'
else:
# リサイズを実施
output_width = resize
img = Image.open(input_image)
if img.size[0] >= resize:
width_per_c = (output_width / float(img.size[0]))
hsize = int((float(img.size[1]) * float(width_per_c)))
img = img.resize((output_width, hsize), Image.ANTIALIAS)
root, ext = os.path.splitext(input_image)
resize_name = 'resize_img' + ext
img.save(resize_name)
else:
resize_name = input_image
img_real = resize_name
# カラーの取得
colors_x = extcolors.extract_from_path(img_real, tolerance=tolerance, limit=15)
# 取得したカラーデータの形式を整形
colors_pre_list = str(colors_x).replace('([(', '').split(', (')[0:-1]
df_rgb = [i.split('), ')[0] + ')' for i in colors_pre_list]
#=== RGBからHEXコードへの変換
df_color_up = [rgb2hex(int(i.split(", ")[0].replace("(", "")),
int(i.split(", ")[1]),
int(i.split(", ")[2].replace(")", ""))) for i in df_rgb]
#Dataframe生成
df_color = pd.DataFrame(df_color_up, columns=['c_code'])
out_color = df_color['c_code']
print("path:" + input_image)
print(out_color)
# 文字コードに合致していたら、テキストファイルにパスを出力(svgの場合があるので、引数のファイルパスをそのまま出力)
for ct in out_color:
if ct == '#1B3B74' or ct =="#004098":
f = open('result.txt', 'a')
f.write(input_image + '\n')
break
extcolorsというライブラリで、CIE76と言う色差式に基づいて画像に使用されている色を検出する。
検出された色は、"[(1,1,1),(2,2,2),(9,9,9)]"のようなテキスト形式で保存されるため、配列の形式に変換してdf_rgbに格納する。
RGBからHEX(16進カラーコード)へ変換し、Dataframeという2次元配列にデータを格納する。
Dataframeのカラーコードの行だけ抜き出し、調査対象のカラーコードと一致するか判定する。
調査対象だった場合、result.txtの最後尾にそのファイルが追加される。
以上をループ処理で全ファイル分行う。
対象ファイル
対象の画像は以下を指定する。
・png,PNG
・jpg,jpeg,JPG,JPEG
・gif
・ico
・webp
・svg
注意事項
Webページのキービジュアルでよくあるサイズが大きすぎる画像だと、1ファイルにかかる処理時間が長くなる。
そのため、幅900px以上の場合は、幅900px(高さは比率保持)にリサイズして処理をかける。
svgファイルはベクター画像のため、jpgやpngなどと違ってそのままの状態だと対応できなかったので、svgは全てpngに変換してカラーコードのチェックを行う。
処理結果
result.txtは以下の通り。
「#1B3B74」と「#004098」が含まれている画像のパスが出力されました。
/img/test/index-img-mail-02--wide.png
/img/test/logo_shinndan.png
/img/test/index_img004.jpg
発生した問題について
意図していた通りにディレクトリのパスが出力されたので、結果の出力自体はうまくいきました。
ただ、検出された画像があまりに少なく、ツール開発段階では100件中5件ほどしか出てきませんでした。(ほんとはもっといっぱいあるのに…)
そこで、カラーコードをログとして出力したところ、それぞれの画像で同じ色だと思っていたものが、微妙に違っていたのです。
例:「#1B3B74」に見えるが、検出結果は「#1B3B73」
あまりのトラップ加減に頭を抱えましたが、切り替えて脳筋戦法を採用します。
そう!カラーコードが微妙に違うなら全部の種類を入れればいいじゃない!
というわけで、対象にするカラーコードを増やし、最終的にはそれっぽいカラーコードを全て詰め込んだプログラムが完成しました。
なんというジャイアン思考…。
そんなツッコミを内心でしつつ、これ以上時間をかけてもコスパに見合わないので、今回は解決としました。
最後に
効率化を目指した末のジャイアン、いかがでしたでしょうか?
もっとスマートな方法を思いついた方はぜひご自身でお試しください。
何事も妥協は必要とはいえ、あまりにも脳筋なやり方になってしまい、完璧な効率化には程遠くなってしまったことは反省です。
個人的に、色差式という計算式で色の検出ができるという学びが、今回の嬉しいポイントでした。
ただ、調べれば調べるほど、数式を見つめて宇宙ネコになっていたので、内容の深い理解については今後の勉強項目のひとつになりました。
ここまで読んでくださり、ありがとうございました。
参考記事
【Python】画像の色抽出(カラーコード抽出)
https://dx-navigation.com/image_color_extraction/
SVGをPDFとPNGへ変換する方法【Python】
https://qiita.com/umi_mori/items/7fdb522401c1f86e0487
この記事を書いた人
狩野真毅
株式会社メンバーズ フロントエンドエンジニア
Webサイト構築でフロントエンドを担当することが主な業務。
コードと友達になるために日々奮闘中。
才野木彩乃
株式会社メンバーズ プロジェクトマネージャー/ディレクター
Webサイト運用/構築でPMやディレクターを担当。
生産性向上や効率化を実現するために日々検討中。
この記事を書いた人

Advent Calendar!
Advent Calendar 2024