見出し画像

画像のカラー抽出を頑張った話。

はじめに

おはようございます。狩野です。
最近業務に取り組む中で効率化のためにとあるツールを作成したので、ナレッジシェアがてら記事を書くことにしました。
最後までのんびりとお付き合いいただけますと幸いです。




今回の概要

私は普段の業務では、全量が数千ページある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


この記事を書いた人

狩野真毅
株式会社メンバーズ第1本部 フロントエンドエンジニア
Webサイト構築でフロントエンドを担当することが主な業務。
コードと友達になるために日々奮闘中。

才野木彩乃
株式会社メンバーズ第1本部 プロジェクトマネージャー/ディレクター
Webサイト運用/構築でPMやディレクターを担当。
生産性向上や効率化を実現するために日々検討中。

この記事が気に入ったらサポートをしてみませんか?