この記事は、以下のような方を想定しています。

  • Adobe Stock審査結果の画像を分析に使いたい方
  • Adobe Stock審査結果の画像を自動で仕分けたい方
  • Adobe Stock審査結果のNG画像をNG理由別に仕分けたい方
  • PythonとExcelを組み合わせた自動化ツールを作りたい方

また、以下のようなことが可能となります。

  • Adobe Stock審査結果の画像を審査OK、審査NGに自動仕分け
  • Adobe Stock審査結果の画像が審査NGの場合、NG理由別に自動仕分け
  • Adobe Stock審査結果の画像が審査中の場合、元の画像配置フォルダから移動させない
  • 上記の結果、分析が容易になる
前記事では、history CSVによる累積管理でExcelへのSTATUS自動反映が安定しました。 これにより analysis_results.xlsx のSTATUS列には OK / NG / EXAM / 空白 の4種類の値が揃うようになりました。 今回はこのSTATUSを活用して、画像ファイルそのものを審査結果に応じたフォルダへ自動仕分けする処理を実装した記録です。 最終的な仕様に落ち着くまでに複数回の試行錯誤があったため、その経緯も含めて詳しく書き残します。

ほかの方がどのようにしてらっしゃるのか分かりませんが、分析のためにOK画像とNG画像を管理されている方はいらっしゃるのでは?と思います。

本記事の処理を行うことで以下のようなことが可能なりました。

Adobeストックフォト審査を行った画像は、image_stockphoto_updone_examafterに配置しておき、審査結果後にOKNG収集を行い(この記事で実装)、 その後に本記事の処理を行います。すると以下のように画像の仕分けが可能となり、後々の分析に活かすことが可能になります。

NG 理由:知的財産 氷の垂れ幕や掛け軸も対象ってことをこの分析で知りました

Adobe Stockの審査後自動仕分け 審査結果NG画面 知的財産

NG 理由:類似コンテンツ ここは仕方ない

Adobe Stockの審査後自動仕分け 審査結果NG画面 類似コンテンツ

NG 理由:品質 この部分を改善したい

Adobe Stockの審査後自動仕分け 審査結果NG画面 品質

OK なぜOKだったかを理解し、再現性のある状態にしたい

Adobe Stockの審査後自動仕分け 審査結果OK画面

なぜ仕分け処理が必要になったのか

審査に出した画像は特定のフォルダに入っています。 審査結果はExcelで管理できるようになりましたが、特定フォルダ上では全画像が混在したままという状態が続いていました。

この状態で困る点は複数あります。

  • ExcelでOKNG管理はできるようになったが、実際の画像が何がOKで何がNGなのか一目でわからない
  • 「この画像はOK?NG?」という確認のたびにExcelとファイラーを行き来する手間が発生する
  • 画像数が1,000件・2,000件と増えるほどこの問題が深刻になる

一方で、ExcelのSTATUS列はすでに正確に管理されています。 OKNG の確定したファイルだけを対応するフォルダに移動すれば、 フォルダの状態とExcelの状態を一致させることができます。 この処理を furiwake_okng.py(Python)と run_furiwake_okng.bat(Windows bat)として実装しました。

最初の設計:3分類(OK / NG / OTHER)

最初に検討した仕様は、STATUS値によって3つのフォルダに振り分けるシンプルな設計でした。

STATUS値振り分け先フォルダ
OKfuriwake/ok へ移動
NGfuriwake/ng へ移動
空白 / EXAM / その他すべてfuriwake/other へ移動

設定ファイルは専用の stockphoto_furiwake.json として独立して作成しました。 バッチファイル名は run_furiwake_okng.bat、Pythonスクリプトは furiwake_okng.py として実装を進めます。

Pythonスクリプトの主な処理の流れは以下の通りです。

  1. コマンドライン引数でJSONファイルのパスを受け取る
  2. JSONの furiwake セクションから設定を読み込む
  3. pandasで analysis_results.xlsx を読み込み、ファイル名→STATUSのdictを構築
  4. ソースフォルダの画像ファイル(.jpg/.jpeg/.png/.webp等)を一覧取得
  5. 各ファイルのSTATUSを引いて対応先フォルダへコピーまたは移動
  6. 処理結果をコンソールとログファイルに出力

実際の analysis_results.xlsx(1,388行)を使ったスモークテストを実施しました。 テスト用ダミーファイルはSTATUS空白の行に対応するファイル名で作成したため、 全件がOTHERに仕分けられ、OKフォルダ、NGフォルダは空になりました。 これはこの時点の仕様通りの正常動作ですが、直後にOTHERの扱いについて見直しが必要だと判断します。

仕様変更①:OTHERをなくしてSKIPに

スモークテストの結果を見て、「空白のときはOTHERフォルダに移動するのではなく、元のフォルダに残したままにしたい」という方針変更を行いました。

考えてみると、空白 = まだ審査中または審査前の画像です。 これをOTHERフォルダに移動してしまうと以下の問題が起きます。

  • 後でSTATUSが確定したとき、ソースフォルダに画像が存在しないため再仕分けができない
  • 「処理済みのOTHER」と「新たに追加された未処理ファイル」が混在して区別できなくなる
  • EXAMページから消えてSTATUSが更新されるまでの間、画像がOTHERフォルダに移動されてしまう
  • OTHERフォルダが際限なく肥大化し、管理の意味がなくなる

正しい設計は、確定したSTATUS(OKとNG)のファイルだけを動かし、未確定は手を付けないという考え方です。 OTHERフォルダを廃止し、空白・EXAM・その他はすべてSKIPとして処理対象外にしました。

STATUS値変更前の動作変更後の動作
OKokフォルダへokフォルダへ(変更なし)
NGngフォルダへngフォルダへ(変更なし)
空白 / EXAM / その他otherフォルダへ移動SKIP(元フォルダに残す)

この変更に合わせて、JSONの dest キーから "other" エントリを削除し、 Pythonスクリプトの振り分けロジックも「対象外ファイルは何もしない」に修正しました。 ログ出力には [SKIP] ファイル名 (status=blank) のようにSKIPラベルと理由を付けて、 どのファイルが対象外になったかをひと目で確認できるようにしています。

変更後のスモークテスト結果

実データのExcelからOK・NG・空白それぞれ1件ずつダミーファイルを作成して検証しました。
[OK] 0507 (23).jpg → okフォルダに配置済み
[NG] 0507 (14).jpg → ngフォルダに配置済み
[SKIP] 0507 (10).jpg (status=blank) → ソースフォルダに残留
サマリー:Skipped (blank/EXAM/other): 1 / Errors: 0 と正常出力を確認。

仕様変更②:設定をstockphoto.jsonに統合

これは仕様変更というより、当初から予定していた対応となります。 当初は stockphoto_furiwake.json という専用設定ファイルを作成していました(既存の各処理に影響を出さないため)が、 既存の stockphoto.json に統合しました。

このプロジェクトの stockphoto.json にはすでに以下のセクションが定義されています。

セクション名対応するbat/スクリプト用途
upscaylrun_upscayl_filerename.bat高解像度化処理の設定
analyzerun_analyze_stock.bat品質スコア算出・AI-NGチェックの設定
exam_ok_ng_collectrun_collect_exam_ok_ng.bat審査結果収集・Excel反映の設定
furiwake(今回追加)run_furiwake_okng.batOK/NG仕分け処理の設定

batスクリプトが増えるたびに設定ファイルも増えていくと、どのスクリプトがどの設定ファイルを参照しているか把握しづらくなります。 フォルダパスを変更する際に複数のJSONを修正する必要が生まれ、修正漏れも起きやすくなります。 stockphoto.json を唯一の設定管理ファイルとして育てることで、 全batの設定をまとめて確認・変更できるという構造を維持します。

統合の作業内容は以下の通りです。

  1. stockphoto.json の末尾(閉じブレース直前)に "furiwake": { ... } セクションを追記
  2. JSON構文チェック(python3 -c "import json; json.load(open(...))")でエラーなしを確認
  3. run_furiwake_okng.bat の CONFIG 変数を stockphoto_furiwake.jsonstockphoto.json に変更
  4. furiwake_okng.py のエラーメッセージも stockphoto.json 表記に統一
  5. 専用の stockphoto_furiwake.json は不要になったため廃止

仕様変更③:Adobe StockのNG画像を理由別フォルダへ自動振り分け

ここまでの実装でOK/NGの2フォルダへの仕分けは完成していましたが、 運用していくうちに「NG理由がわからない」という課題が見えてきました。

NG理由は、数種類があることが分かっています。特に対処したいのは「品質」です。

これらをひとつの NG フォルダにまとめてしまうと、後の分析・対処の際にまた仕分け直しが必要になります。 最初からNG理由ごとのサブフォルダに振り分ける設計にすることで、 フォルダを開くだけで理由別の傾向が把握できる状態にしようと考えました。

具体的なフォルダ構造は以下のようになります。

STATUSNG理由配置先フォルダ
OK…\OK\
NG品質の問題…\NG\品質の問題\
NGアドビコレクションで類似するコンテンツ…\NG\アドビコレクションで類似するコンテンツ\
NG(空白)…\NG\
空白・EXAMSKIP(元フォルダに残る)

サブフォルダは存在しなければ os.makedirs(dest_dir, exist_ok=True) で自動作成します。 新しいNG理由が追加されても設定変更不要で自動対応できます。

フォルダ名に使えない文字(\ / : * ? " < > |)はアンダースコアに置換する sanitize_foldername() 関数を実装しています。 将来的にNG理由の文言にこれらの文字が含まれても安全にフォルダが作成されます。

stockphoto.jsonへのng_reason_col追加

NG理由列の列インデックスを ng_reason_col キーとしてJSONに外出しました。 Excelの列構成が変わっても、JSONを修正するだけでスクリプトの変更は不要です。

動作確認:NG理由別サブフォルダ振り分けのログ出力例

[SKIP] 0507 (10).jpg (status=blank)
[NG/品質の問題] 0507 (14).jpg
[OK] 0507 (23).jpg
[NG/アドビコレクションで類似するコンテンツ] 0516 (6).jpg

サマリー:
NG total : 2
  NG/品質の問題 : 1
  NG/アドビコレクションで類似するコンテンツ : 1

最終的に以下の画像のような状態となりました。審査申請した画像をimage_stockphoto_updone_examafterフォルダ配下に配置しておきます。 そのあと、仕分け処理を動かすとOKやNGに仕分けられます。

Adobe Stockの自動収集システムのフォルダ構成画面

furiwake_okng.pyの実装詳細

完成した furiwake_okng.py の処理フローを整理します。

処理の流れ(完成版)

  • コマンドライン引数で stockphoto.json のパスを受け取る
  • JSONの furiwake セクションを読み込む
  • pandasで analysis_results.xlsx を読み込み(header=1dtype=str
  • ファイル名、ステータス、NG理由でdict構築(dict:Pythonのデータ構造)
  • ソースフォルダの対象拡張子ファイルを一覧取得してソート
  • STATUSを引き、OK→OKの出力先、NG→NGの出力先、かつサブフォルダ自動作成、それ以外→SKIP
  • 処理結果をコンソールに出力し、log_file に追記保存
  • 最後にサマリーを表示(OK/NG合計/NG理由別内訳/SKIP/not-in-Excel/Error/Total件数)

OK、NGの振り分け処理の部分は以下の内容です。


        if s == ok_val:
            # Move/copy to dest.ok
            dest_dir  = dest_ok
            label     = "OK"
            counts["ok"] += 1

        elif s == ng_val:
            # Determine NG reason subfolder
            if ng_reason:
                subfolder = sanitize_foldername(ng_reason)
            else:
                subfolder = "reason_unknown"

            dest_dir = os.path.join(dest_ng, subfolder)
            os.makedirs(dest_dir, exist_ok=True)

            label = f"NG/{subfolder}"
            counts["ng"] += 1
            ng_reason_counts[subfolder] = ng_reason_counts.get(subfolder, 0) + 1
        

ログ出力の形式は以下のようになります。実行後に何が起きたかをひと目で確認できます。

出力ラベル意味
[OK][OK] 0514_023.jpgOKフォルダに移動済み
[NG/理由名][NG/品質の問題] 0514_047.jpgNG理由別サブフォルダに移動済み
[SKIP][SKIP] 0514_088.jpg (status=blank)空白のためスキップ(元フォルダに残る)
[SKIP][SKIP] 0514_099.jpg (status=EXAM)審査中のためスキップ
[ERROR][ERROR] 0514_111.jpg: [Errno 2] ...移動失敗(パス不在・権限不足等)

発生したエラーと対処

開発・作業中に発生したエラーを記録します。

① ツールの必須パラメータ欠落エラー

ファイル編集ツール(str_replace)の使用時に、必須パラメータである path を指定し忘れるという操作ミスが発生しました。 エラーメッセージは Input validation errors occurred: path: Field required です。 コードには一切影響しないツール操作上のミスです。 代替手段として bash_tool 経由で sed -i コマンドを実行することで同じ編集を行いました。

② ExcelのヘッダーrowがデフォルトのIndex=0でないことによる列ズレ

analysis_results.xlsx は1行目にシート説明のような情報が入っており、実際の列ヘッダーは2行目(0始まりインデックス1)にあります。 pd.read_excel() のデフォルト(header=0)で読み込むと列名がずれ、ファイル名列やSTATUS列が正しく取得できませんでした。 header=1 を明示的に指定することで解決しています。 JSONの header_row キーに値を外出しているため、Excelのフォーマットが変わっても設定変更のみで対応できます。

③ 列インデックスの確認が必要

pandasで iloc アクセスする場合、列はアルファベット(B列、T列)ではなく0始まりの整数で指定します。 B列=インデックス1、T列=インデックス19 という対応を実際にExcelを読み込んで確認してから設定しました。 print('Columns:', list(df.columns)) で列名一覧を出力し、目的の列が何番目かを確認するのが確実です。

完成した全体構成と運用フロー

今回作成・更新したファイルの一覧です。

ファイル名配置場所内容
run_furiwake_okng.batbat\新規作成。stockphoto.json を参照して furiwake_okng.py を呼び出す
furiwake_okng.pybat\python\新規作成。OK/NG仕分けのメイン処理(Logger・load_config・run_furiwake)
stockphoto.jsonbat\furiwake セクションを追記。stockphoto_furiwake.json は廃止

batファイルの動作フローは以下の通りです。

  1. bat\stockphoto.json の存在確認
  2. bat\python\furiwake_okng.py の存在確認
  3. stockphoto.jsonexam_ok_ng_collect.python_exe からpython実行パスを取得(なければ python コマンドを使用)
  4. 設定情報(CONFIGパス・スクリプトパス・Python)をコンソールに表示
  5. furiwake_okng.py stockphoto.json を実行
  6. 終了コードに応じて成功/エラーメッセージを表示してpause

運用上の推奨実行順序

仕分け処理はExcelのSTATUS列の現在値を参照して動作します。 Excelが古い状態のままで仕分けを実行すると、本来OKになっているはずの画像がSKIPされ続けてしまいます。 バッチメニューでの実行順序は ①収集(run_collect_exam_ok_ng.bat)→ ②Excel反映 → ③仕分け(run_furiwake_okng.bat) の順に統一することが必要です。

今後はOKになった画像・NGになった画像のスコア分布を照合し、どのスコア帯でOKになりやすいか・NGの傾向は何かの分析へと進む予定です。 仕分け処理によってOKフォルダ・NGフォルダにファイルが物理的に集まるようになるため、 サムネイルを一覧しながら傾向を把握しやすくなります。 スコアと審査通過率の相関が見えてくれば、品質チェックの閾値設定にフィードバックでき、 将来的な「申請前自動品質フィルタ」の精度向上につながります。 データが蓄積されるほど分析の精度が上がるため、この仕組みが今後の改善サイクルの基盤になります。

FAQ

空白(未審査)の画像はどうなりますか?

SKIPされます。スクリプトはSTATUS列がOKまたはNGの行だけを処理対象とし、空白・EXAM・その他は何も行わずソースフォルダに残したままにします。

NGのサブフォルダはどのように決まりますか?

ExcelのNG理由列(ng_reason_col で指定した列)の値をそのままフォルダ名として使用します。フォルダに使えない文字(\ / : * ? など)はアンダースコアに置換するsanitize処理が入っており、NG理由が空の場合は reason_unknown フォルダに振り分けられます。フォルダは存在しなければ自動作成されます。