この記事は、以下のような方を想定しています。
- Adobe Stock審査結果の画像を分析に使いたい方
- Adobe Stock審査結果の画像を自動で仕分けたい方
- Adobe Stock審査結果のNG画像をNG理由別に仕分けたい方
- PythonとExcelを組み合わせた自動化ツールを作りたい方
また、以下のようなことが可能となります。
- Adobe Stock審査結果の画像を審査OK、審査NGに自動仕分け
- Adobe Stock審査結果の画像が審査NGの場合、NG理由別に自動仕分け
- Adobe Stock審査結果の画像が審査中の場合、元の画像配置フォルダから移動させない
- 上記の結果、分析が容易になる
analysis_results.xlsx のSTATUS列には OK / NG / EXAM / 空白 の4種類の値が揃うようになりました。
今回はこのSTATUSを活用して、画像ファイルそのものを審査結果に応じたフォルダへ自動仕分けする処理を実装した記録です。
最終的な仕様に落ち着くまでに複数回の試行錯誤があったため、その経緯も含めて詳しく書き残します。
ほかの方がどのようにしてらっしゃるのか分かりませんが、分析のためにOK画像とNG画像を管理されている方はいらっしゃるのでは?と思います。
本記事の処理を行うことで以下のようなことが可能なりました。
Adobeストックフォト審査を行った画像は、image_stockphoto_updone_examafterに配置しておき、審査結果後にOKNG収集を行い(この記事で実装)、 その後に本記事の処理を行います。すると以下のように画像の仕分けが可能となり、後々の分析に活かすことが可能になります。
NG 理由:知的財産 氷の垂れ幕や掛け軸も対象ってことをこの分析で知りました

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

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

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

なぜ仕分け処理が必要になったのか
審査に出した画像は特定のフォルダに入っています。 審査結果はExcelで管理できるようになりましたが、特定フォルダ上では全画像が混在したままという状態が続いていました。
この状態で困る点は複数あります。
- ExcelでOKNG管理はできるようになったが、実際の画像が何がOKで何がNGなのか一目でわからない
- 「この画像はOK?NG?」という確認のたびにExcelとファイラーを行き来する手間が発生する
- 画像数が1,000件・2,000件と増えるほどこの問題が深刻になる
一方で、ExcelのSTATUS列はすでに正確に管理されています。
OK と NG の確定したファイルだけを対応するフォルダに移動すれば、
フォルダの状態とExcelの状態を一致させることができます。
この処理を furiwake_okng.py(Python)と run_furiwake_okng.bat(Windows bat)として実装しました。
最初の設計:3分類(OK / NG / OTHER)
最初に検討した仕様は、STATUS値によって3つのフォルダに振り分けるシンプルな設計でした。
| STATUS値 | 振り分け先フォルダ |
|---|---|
OK | furiwake/ok へ移動 |
NG | furiwake/ng へ移動 |
空白 / EXAM / その他すべて | furiwake/other へ移動 |
設定ファイルは専用の stockphoto_furiwake.json として独立して作成しました。
バッチファイル名は run_furiwake_okng.bat、Pythonスクリプトは furiwake_okng.py として実装を進めます。
Pythonスクリプトの主な処理の流れは以下の通りです。
- コマンドライン引数でJSONファイルのパスを受け取る
- JSONの
furiwakeセクションから設定を読み込む - pandasで
analysis_results.xlsxを読み込み、ファイル名→STATUSのdictを構築 - ソースフォルダの画像ファイル(.jpg/.jpeg/.png/.webp等)を一覧取得
- 各ファイルのSTATUSを引いて対応先フォルダへコピーまたは移動
- 処理結果をコンソールとログファイルに出力
実際の 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値 | 変更前の動作 | 変更後の動作 |
|---|---|---|
OK | okフォルダへ | okフォルダへ(変更なし) |
NG | ngフォルダへ | 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/スクリプト | 用途 |
|---|---|---|
upscayl | run_upscayl_filerename.bat | 高解像度化処理の設定 |
analyze | run_analyze_stock.bat | 品質スコア算出・AI-NGチェックの設定 |
exam_ok_ng_collect | run_collect_exam_ok_ng.bat | 審査結果収集・Excel反映の設定 |
furiwake(今回追加) | run_furiwake_okng.bat | OK/NG仕分け処理の設定 |
batスクリプトが増えるたびに設定ファイルも増えていくと、どのスクリプトがどの設定ファイルを参照しているか把握しづらくなります。
フォルダパスを変更する際に複数のJSONを修正する必要が生まれ、修正漏れも起きやすくなります。
stockphoto.json を唯一の設定管理ファイルとして育てることで、
全batの設定をまとめて確認・変更できるという構造を維持します。
統合の作業内容は以下の通りです。
stockphoto.jsonの末尾(閉じブレース直前)に"furiwake": { ... }セクションを追記- JSON構文チェック(
python3 -c "import json; json.load(open(...))")でエラーなしを確認 run_furiwake_okng.batの CONFIG 変数をstockphoto_furiwake.json→stockphoto.jsonに変更furiwake_okng.pyのエラーメッセージもstockphoto.json表記に統一- 専用の
stockphoto_furiwake.jsonは不要になったため廃止
仕様変更③:Adobe StockのNG画像を理由別フォルダへ自動振り分け
ここまでの実装でOK/NGの2フォルダへの仕分けは完成していましたが、 運用していくうちに「NG理由がわからない」という課題が見えてきました。
NG理由は、数種類があることが分かっています。特に対処したいのは「品質」です。
これらをひとつの NG フォルダにまとめてしまうと、後の分析・対処の際にまた仕分け直しが必要になります。
最初からNG理由ごとのサブフォルダに振り分ける設計にすることで、
フォルダを開くだけで理由別の傾向が把握できる状態にしようと考えました。
具体的なフォルダ構造は以下のようになります。
| STATUS | NG理由 | 配置先フォルダ |
|---|---|---|
| OK | — | …\OK\ |
| NG | 品質の問題 | …\NG\品質の問題\ |
| NG | アドビコレクションで類似するコンテンツ | …\NG\アドビコレクションで類似するコンテンツ\ |
| NG | (空白) | …\NG\ |
| 空白・EXAM | — | SKIP(元フォルダに残る) |
サブフォルダは存在しなければ 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に仕分けられます。

furiwake_okng.pyの実装詳細
完成した furiwake_okng.py の処理フローを整理します。
処理の流れ(完成版)
- コマンドライン引数で
stockphoto.jsonのパスを受け取る - JSONの
furiwakeセクションを読み込む - pandasで
analysis_results.xlsxを読み込み(header=1、dtype=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.jpg | OKフォルダに移動済み |
[NG/理由名] | [NG/品質の問題] 0514_047.jpg | NG理由別サブフォルダに移動済み |
[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.bat | bat\ | 新規作成。stockphoto.json を参照して furiwake_okng.py を呼び出す |
furiwake_okng.py | bat\python\ | 新規作成。OK/NG仕分けのメイン処理(Logger・load_config・run_furiwake) |
stockphoto.json | bat\ | furiwake セクションを追記。stockphoto_furiwake.json は廃止 |
batファイルの動作フローは以下の通りです。
bat\stockphoto.jsonの存在確認bat\python\furiwake_okng.pyの存在確認stockphoto.jsonのexam_ok_ng_collect.python_exeからpython実行パスを取得(なければpythonコマンドを使用)- 設定情報(CONFIGパス・スクリプトパス・Python)をコンソールに表示
furiwake_okng.py stockphoto.jsonを実行- 終了コードに応じて成功/エラーメッセージを表示して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 フォルダに振り分けられます。フォルダは存在しなければ自動作成されます。