手動モザイク処理ツール。起動時に input/ の画像を output/ へコピーし、右のサムネイル一覧から選んだ画像に、ブラシ・消しゴム・魔法の杖・黒線ブラシでモザイクをかけて output/ を更新する。
Python 3.10 が必要なのだ。
setup.bat
venv/ を作成して Pillow・numpy を自動インストールするのだ。
run.bat
input/フォルダに処理したい画像(PNG / JPG / JPEG / WEBP / BMP、拡張子の大文字小文字は問わない)を置くrun.batを起動- 起動した時点で
input/の全画像がoutput/にコピーされる - 右のサムネイル一覧から画像を選ぶ(← → ↑ ↓ キーでも移動できる)
- モザイクをかけたい箇所を塗る。塗った時点で
output/のファイルが更新される
保存ボタンは無いのだ。塗れば勝手に保存されるし、塗らなければ起動時にコピーされたままの状態で残る。好きな順番で好きな画像だけ処理して、あとは閉じればいいのだ。
output/ に同名ファイルが既にある画像はコピーされない。前回までの作業結果が上書きされることはないのだ。
コピーはバイナリコピー(shutil.copy2)なので、一度も編集していない画像は JPEG でも劣化しない。1バイトも変わらずそのまま残る。
input/ と output/ の中身をまとめて消したいときは clear.bat を使うのだ。確認プロンプトは出ず、実行した瞬間に削除される(.gitkeep だけは残る)。
左のコントロールパネルで選択する。選んだツールが左クリック/ドラッグに割り当たるのだ。
| ツール | 内容 |
|---|---|
| ブラシ | 筆の形にモザイク塗り |
| 消しゴム | 筆の形にモザイクを消す |
| 魔法の杖 | クリック点の色を基準にフラッドフィルでモザイク化。クリックのみでドラッグは効かない |
| 黒線ブラシ | ドラッグした筆の形の中の暗い(黒線)ピクセルだけモザイク化 |
| 筆の太さ | スライダーで 3〜50 px(初期値 20)。ブラシ・消しゴム・黒線ブラシで有効(魔法の杖は無関係) |
| 筆の形 | 丸 / 四角。ブラシ・消しゴム・黒線ブラシで有効(魔法の杖は無関係) |
太さ・形のどちらも、変えてもツールは切り替わらないのだ。消しゴムや黒線ブラシを使っている途中でも調整できる。
筆の太さは半径側の値なのだ。実際に塗れる大きさは「太さ × 2 + 1」px になる。
| 太さ | 塗れる大きさ | 丸の面積 | 四角の面積 |
|---|---|---|---|
| 3 | 7 × 7 px | 37px | 49px |
| 5 | 11 × 11 px | 97px | 121px |
| 20 | 41 × 41 px | 1313px | 1681px |
丸と四角を切り替えると、同じ太さでも四角の方が塗れる面積は約 1.27 倍(4/π)になるのだ。
最小の 3px は細かい修正用なのだ。ズームしてから使うと画像上ではさらに細くなる(太さは画面上の px なので、2倍にズームすれば画像上の半径は 1.5px になる)。
| 操作 | 内容 |
|---|---|
| 左クリック / ドラッグ | 選択中のツールを適用 |
| 右クリックドラッグ | 画像をパン |
| スクロール | ズームイン / アウト(マウス位置中心、0.05〜20倍・1操作あたり 1.15 倍) |
| ← → ↑ ↓ | 前 / 次の画像へ移動。端で止まる(ループしない)。左右と上下は同じ動作 |
| サムネイルをクリック | その画像へ移動 |
| Ctrl+Z / Undo ボタン | 1操作戻す。マウスを押した時点のマスクへ戻るので、ドラッグ1ストロークが1段 |
矢印キーはスライダーやボタンにフォーカスがある状態でも画像移動になるのだ(スライダーの値は動かない)。
履歴は画像ごとではなくアプリ全体で1本なのだ。別の画像に移っても履歴は消えない。
Undo を押すと、その操作をした画像へ自動で移動してから戻す。 例えば a → b(2回塗る)→ c と作業した後に Undo を4回押すと、
1回目: c の操作を戻す(c にいるまま)
2回目: b へ移動して、2回目の操作を戻す
3回目: b の1回目の操作を戻す
4回目: a へ移動して、a の操作を戻す
段数の上限は無いのだ。マスクは PNG 圧縮して積むので、履歴が伸びてもメモリはほとんど増えない(300×300 で50段ためて 36.5KB。素の画像で持つと 4.3MB になる計算)。
なお履歴が残るのはアプリを起動している間だけなのだ。
ウィンドウをリサイズすると画像は自動でフィット表示に戻るのだ。
起動時のウィンドウは 1440×880 px なのだ。画面がそれより小さい場合は、はみ出さないよう画面サイズから幅 80px・高さ 100px を引いた大きさに縮められる。
ステータス表示はパネル下端に固定されているので、ウィンドウを低くしても消えないのだ。代わりに Undo ボタンが先に隠れる。境目は実測で、ファイル名が長くステータスが2行に折り返すと 818px、短ければ 788px なのだ。起動時の 880px なら両方見えている。隠れても Ctrl+Z は効くのだ。
output/ にある画像を名前順にサムネイルで縦1列に並べるのだ。input/ に無い画像(過去の作業結果だけが残っているもの)も並ぶ。そのため input/ を空にしても、output/ に画像が残っていれば起動して編集できる(この場合はモザイク済みの画像が「元画像」になる)。
input/ と output/ の両方が空のときだけ、警告を出して終了するのだ。
| 表示 | 意味 |
|---|---|
| 緑の枠 | 現在編集中の画像 |
| 緑の丸 | このセッションでモザイクを塗った画像 |
サムネイルはバックグラウンドで生成されるので、枚数が多くても起動は待たされないのだ(100枚で約3秒かけて順に埋まる。その間も操作できる)。
一覧の上でホイールを回すとスクロールする。矢印キーで移動したときは、選択中の画像が見えるよう自動でスクロールするのだ。
塗り終えた(マウスを離した)0.4秒後に output/ へ書き出すのだ。続けて塗っている間は書き出しをまとめるので、ストロークのたびに引っかかることはない。
画像を切り替えるときと、ウィンドウを閉じるときは、待たずにその場で保存するのだ。
別の画像に移ってから戻ってくると、さっき塗ったモザイクはそのまま残っていて、消しゴムで剥がすことも Ctrl+Z で戻すこともできるのだ。マスクは画像ごとにメモリへ持っている。
メモリはほとんど食わないのだ。マスクは2値のベタ塗りなので PNG に圧縮すると約180分の1になる(1024×1024 で 1MB → 6KB)。500枚持っても 3MB 程度なのだ。
マスクを保持するのはアプリを起動している間だけなのだ。次に起動したときは output/ のモザイク済み画像が「元画像」になるので、前回のモザイクを消しゴムで剥がすことはできない。
やり直したいときは output/ の該当ファイルを消して起動し直すのだ。input/ の原本は変更されないので、そこから再度コピーされる。
左のコントロールパネルからリアルタイムに変更できるのだ。
| スライダー | 範囲 | 初期値 | 内容 |
|---|---|---|---|
| 筆の太さ | 3〜50 | 20 | ブラシ・消しゴム・黒線ブラシの半径 px |
| 色差 tolerance | 1〜100 | 25 | 魔法の杖が同色とみなす RGB の最大差 |
| 境界膨張 | 0〜200 | 25 | 魔法の杖の結果を広げる量 |
| 輝度しきい値 | 0〜255 | 80 | 黒線ブラシがこの輝度以下のピクセルを黒線とみなす(輝度 = ITU-R BT.601 の 0.299R + 0.587G + 0.114B) |
| 膨張 | 0〜200 | 25 | 黒線ブラシの結果を広げる量 |
膨張スライダーの値は相対スケールなのだ。実際のピクセル数は次式で、値 100 のとき画像幅の 1/100 px になる。
膨張px = floor(floor(画像幅 / 100) × スライダー値 / 100)
例:幅 1000px の画像でスライダー 25 → floor(10 × 0.25) = 2px。0px になる場合は膨張しないのだ。
| 項目 | 値 |
|---|---|
| ブロックサイズ | 画像幅の 1/100(最小 4px) |
| 縮小 / 拡大方式 | 縮小 BOX → 拡大 NEAREST |
| 保存先 | 入力と同じファイル名・拡張子で output/ へ |
| JPEG の品質 | 95(編集した画像のみ。未編集はバイナリコピーのまま無劣化) |
保存時にメタデータを引き継いでいないため、編集した画像からは EXIF と PNG のテキストチャンクが落ちる。
- EXIF(撮影日時・位置情報・端末情報)は消える。プライバシー面では都合が良いのだ
- Stable Diffusion 生成画像の
parametersチャンク(プロンプト・シード)も消える。プロンプトを残したい画像は、モザイク前の原画を別途保管しておくのだ(input/はアプリ側から変更しないので、そこが原本になる) - JPEG は再エンコードされるため、モザイクをかけていない領域も含めて画質が劣化する(品質 95)
一度も塗っていない画像は書き出しを行わないので、起動時にバイナリコピーされた状態のままなのだ。メタデータも画質もそのまま残る。
settings.json でスライダーの起動時デフォルト値を変更できるのだ。
{
"wand_tolerance": 25,
"wand_dilate_scale": 25,
"line_threshold": 80,
"line_dilate_scale": 25,
"brush_shape": "circle",
"brush_size": 20
}| キー | 説明 | デフォルト |
|---|---|---|
wand_tolerance |
魔法の杖の色差 tolerance(1〜100) | 25 |
wand_dilate_scale |
魔法の杖の境界膨張スケール(0〜200、100 = 画像幅の 1/100 px) | 25 |
line_threshold |
黒線ブラシの輝度しきい値(0〜255、この値以下を黒線とみなす) | 80 |
line_dilate_scale |
黒線ブラシの膨張スケール(0〜200、100 = 画像幅の 1/100 px) | 25 |
brush_shape |
起動時の筆の形("circle" / "square") |
"circle" |
brush_size |
起動時の筆の太さ(3〜50 px) | 20 |
ファイルが無い・JSON が壊れている場合はコード内のデフォルト値にフォールバックする。書かれていないキーだけデフォルトで補われるのだ。brush_shape に "circle" "square" 以外を書いた場合は "circle" に戻され、brush_size は 3〜50 の範囲にクランプされる(どちらも起動はする)。
mosaic/
├── app.py # メインアプリ
├── settings.json # スライダーの初期値
├── run.bat # 起動スクリプト
├── setup.bat # 初回セットアップ(venv 作成・パッケージインストール)
├── clear.bat # input / output のファイルを一括削除
├── requirements.txt
├── .python-version # 想定 Python バージョンの記録(setup.bat 側は py -3.10 を直書き)
├── input/ # 処理対象画像を置く(.gitkeep 以外は git 管理外)
└── output/ # 保存先(.gitkeep 以外は git 管理外)