uvで仮想環境を作って
uv pip install mlx-vlm torch
して
uv run Jrename_mlx.py [画像ファイル名]
でリネームします。
自動化などは元ネタの面倒なスクショ命名はローカルvlmにやらせよう on Macを参照あれ
Last active
May 14, 2025 14:43
-
-
Save Shinichi-Ohki/a2e08e774b9f56f751d3263370b8f016 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# This script is a modified version of the one published at https://zenn.dev/wmoto_ai/articles/58e6bcf58d6032 | |
# このスクリプトは https://zenn.dev/wmoto_ai/articles/58e6bcf58d6032 で公開されたものを変更したものです | |
import sys | |
import os | |
import warnings | |
import logging | |
import re | |
import datetime | |
import argparse | |
# すべての警告を抑制 | |
warnings.filterwarnings('ignore') | |
logging.getLogger().setLevel(logging.ERROR) | |
logging.getLogger('transformers').setLevel(logging.ERROR) | |
import mlx.core as mx | |
from mlx_vlm import load, generate | |
from mlx_vlm.prompt_utils import apply_chat_template | |
from mlx_vlm.utils import load_config | |
tz_jst = 9 | |
def extract_datetime(filename): | |
# 正規表現パターン | |
pattern = r"スクリーンショット (\d{4})-(\d{2})-(\d{2}) (\d+)\.(\d+)\.(\d+)(?: ([0-9]+))?(?:(([0-9]+)))?" | |
# 正規表現で検索 | |
match = re.search(pattern, filename) | |
if match: | |
year = int(match.group(1)) | |
month = int(match.group(2)) | |
day = int(match.group(3)) | |
hour = int(match.group(4)) | |
minute = int(match.group(5)) | |
second = int(match.group(6)) | |
index1 = int(match.group(7)) if match.group(7) else None | |
index2 = int(match.group(8)) if match.group(8) else None | |
return year, month, day, hour, minute, second, index1, index2 | |
else: | |
raise ValueError("ファイル名から日時を抽出できませんでした") | |
def suppress_output(): | |
"""標準出力と標準エラー出力を抑制するコンテキストマネージャー""" | |
class DummyFile: | |
def write(self, x): pass | |
def flush(self): pass | |
return DummyFile() | |
def sanitize_filename(filename: str) -> str: | |
""" | |
OS で利用できない文字などを除去・置換して簡易的にサニタイズする。 | |
""" | |
sanitized = re.sub(r'[\\/:*?"<>|]+', '_', filename) | |
sanitized = sanitized.strip().strip('.') | |
if len(sanitized) > 100: | |
sanitized = sanitized[:100] | |
return sanitized | |
def main(): | |
parser = argparse.ArgumentParser(description="Rename an image file based on Qwen2-VL inference") | |
parser.add_argument("image_path", type=str, help="Path to the image file") | |
parser.add_argument("--resize-shape", type=int, nargs="+", default=[512, 512], | |
help="Resize shape for the image (e.g. --resize-shape 512 512). Default is 512x512.") | |
args = parser.parse_args() | |
# print(args) | |
image_path = args.image_path | |
try: | |
# 標準エラー出力と標準出力を一時的に抑制 | |
original_stderr = sys.stderr | |
original_stdout = sys.stdout | |
sys.stderr = suppress_output() | |
sys.stdout = suppress_output() | |
# モデルとプロセッサのロード | |
model_path = 'mlx-community/Qwen2.5-VL-3B-Instruct-4bit' | |
model, processor = load(model_path) | |
config = load_config(model_path) | |
# 標準出力と標準エラー出力を復元 | |
sys.stderr = original_stderr | |
sys.stdout = original_stdout | |
# メッセージの設定 このへんは適宜ご自身の用途でアレンジください | |
messages = [ | |
{ | |
"role": "system", | |
"content": """あなたは画像を説明するファイル名を生成するプロフェッショナルです。 | |
'Cap_'で始めて、その後に日本語で画像を説明する単語を連結してください | |
以下のルールを必ず守ってください: | |
- 'Cap_'ではじめる | |
- 簡潔で、かつ説明的で | |
- 文章ではなく複数の単語で | |
- 単語は4つまで | |
- 英語が一般的な単語は英語で | |
- 例: 「Cap_Pythonスクリプト」, 「Cap_電機回路図」, 「Cap_横スクロールゲーム」, 「Cap_ラーメンとチャーハンと餃子」, 「Cap_チャットのログ」""" | |
}, | |
{ | |
"role": "user", | |
"content": "この画像の説明を生成してください。" | |
} | |
] | |
# チャットテンプレートの適用 | |
formatted_prompt = apply_chat_template(processor, config, messages, num_images=1) | |
# リサイズ形状を設定 | |
resize_shape = None | |
if args.resize_shape is not None: | |
if len(args.resize_shape) == 1: | |
resize_shape = (args.resize_shape[0], args.resize_shape[0]) | |
elif len(args.resize_shape) == 2: | |
resize_shape = (args.resize_shape[0], args.resize_shape[1]) | |
else: | |
raise ValueError("--resize-shape must have 1 or 2 integers.") | |
# 画像の説明を生成 | |
output = generate( | |
model, | |
processor, | |
formatted_prompt, | |
[image_path], | |
verbose=False, | |
temperature=0.7, | |
max_tokens=100, | |
resize_shape=resize_shape, | |
use_fast=True | |
) | |
description = output[0].strip() | |
print(description) | |
# ファイル名から年月日時分秒を取得 | |
year, month, day, hour, minute, second, index1, index2 = extract_datetime(image_path) | |
# 拡張子を抽出 | |
extension = os.path.splitext(image_path)[1] | |
# 説明文をファイル名に使える形へサニタイズ | |
safe_description = sanitize_filename(description) | |
# 日本時間(東京)を取得 | |
tz = datetime.timezone(datetime.timedelta(hours=tz_jst)) | |
now_japan = datetime.datetime.now(tz) | |
# date_str = now_japan.strftime('%Y-%m-%d_%H-%M-%S') | |
date_str = f"{year:04}-{month:02}-{day:02}_{hour:02}-{minute:02}-{second:02}" | |
date_str = f"{date_str}_{index1}" if index1 else date_str | |
date_str = f"{date_str}_({index2})" if index2 else date_str | |
# 新しいファイル名を作成 | |
new_file_name = f"{date_str}_{safe_description}{extension}" | |
new_file_path = os.path.join(os.path.dirname(image_path), new_file_name) | |
# リネーム実行 | |
os.rename(image_path, new_file_path) | |
print(f"ファイルをリネームしました: {new_file_path}") | |
except Exception as e: | |
# エラー時に標準出力と標準エラー出力を確実に復元 | |
if sys.stderr != original_stderr: | |
sys.stderr = original_stderr | |
if sys.stdout != original_stdout: | |
sys.stdout = original_stdout | |
print(f"エラーが発生しました: {str(e)}") | |
sys.exit(1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment