documents

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit fe6c54aabc61136ed01058a64622f6c6cadb6948
parent 34692f6596bbc144e19ad80aca6f81123cb22b0d
Author: minerva-jupiter <ryouturn@gmail.com>
Date:   Sun, 24 May 2026 11:17:25 +0900

feat: add documentation and video processing utilities

- Add Y-shirt sewing instructions (Y-shirt_Sewing_Instructions.md).
- Implement video similarity detection (similar_video.py) and image noise generation (rand-noise-image.py).
- Add AI-based image upscaling script (upscale_image.py) and Nushell script for audio conversion (wav2flac.nu).
- Improve `mov2mp4.py` with cross-platform file path handling, case-insensitive extension support, and updated batch configuration.

Diffstat:
AY-shirt_Sewing_Instructions.md | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A__pycache__/mov2mp4.cpython-312.pyc | 0
Mbatch_mov2mp4.py | 6+++---
Mmov2mp4.py | 91+++++++++++++++++++++++++------------------------------------------------------
Aoutput_espcn.jpg | 0
Arand-noise-image.py | 21+++++++++++++++++++++
Asimilar_video.py | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aupscale_image.py | 40++++++++++++++++++++++++++++++++++++++++
Awav2flac.nu | 24++++++++++++++++++++++++
9 files changed, 462 insertions(+), 65 deletions(-)

diff --git a/Y-shirt_Sewing_Instructions.md b/Y-shirt_Sewing_Instructions.md @@ -0,0 +1,84 @@ +# Yシャツ縫製手順書(右前身頃ファスナー仕様) + +布の切断(裁断)が完了した状態から、完成に至るまでの詳細な縫製手順である。 +右前身頃に大型ファスナーを搭載する変則的な仕様に合わせ、平面の状態でファスナー処理を完了させる効率的な工程で構成している。 + +--- + +## 1. パーツの下準備(芯地貼り・アイロン) + +立体的な縫製に移る前に、すべてのパーツに芯地を貼り、アイロンで折り目をつけて成形する。 + +### 1-1. 接着芯の貼付 +* **表衿・台衿:** 表衿全体、および台衿(表・裏の一方または両方)に接着芯を貼る。 +* **カフス・剣ボロ:** 表カフス、および剣ボロの表側に接着芯を貼る。 +* **表前立て:** 左前身頃の表前立て部分(または別布の前立て)に接着芯を貼る。 +* **ファスナー取付位置(重要):** 右前身頃のファスナーを縫い付ける位置の裏側に、生地の伸びやヨレを防ぐため、あらかじめ幅広の接着芯を貼って完全に補強する。 + +### 1-2. アイロンによる折り込み +* **表前立て(左前身頃):** 仕様に合わせ、前立ての端を規定の幅でアイロンで折る。 +* **右前身頃(ファスナー側):** ファスナーを挟み込む、または叩きつけるための端処理(三つ折りなど)に合わせてアイロンで折る。 +* **カフス・台衿:** 身頃や袖に縫い付ける側の縫い代を、あらかじめ片方だけアイロンで内側に折っておく。 + +--- + +## 2. 部分縫い(各パーツの組み立て・ファスナー取付) + +身頃が完全に平らな状態で、各パーツの組み立ておよび前立て・ファスナーの処理を行う。 + +### 2-1. 前立て処理および右前身頃へのファスナー取付 +1. **左前身頃(表前立て)の縫製:** 折り込んだ前立ての両端にステッチをかけ、左前立てを仕上げる。 +2. **右前身頃へのファスナー取付:** * 芯地で補強した右前身頃のファスナー位置に、ファスナーを正確に配置する。 + * 片おさえ(ファスナー押え)を使用し、ズレや波打ちがないよう注意しながら縫い付ける。 + * ※上端(台衿との境界)および下端(裾の三つ折り位置)の縫い代を計算し、ファスナーのエレメント(務歯)が干渉しない位置に配置する。必要に応じて上端のエレメントを抜いておく。 +3. **左右のバランス確認:** 左前身頃と右前身頃を重ね合わせ、襟ぐりや裾のラインが完全に一致しているか確認し、仮止め等で位置を確定させる。 + +### 2-2. 衿(カラー)の作成 +1. **衿羽の縫い合わせ:** 表衿と裏衿を中表に合わせ、外周(上端と左右)を縫い合わせる。角の縫い代を落として表に返し、形を整えて周囲にステッチをかける。 +2. **台衿への挟み込み:** 完成した衿羽を、2枚の台衿(表・裏)の間に挟み込む。 +3. **台衿の縫い合わせ:** 台衿の上側のカーブを縫い合わせ、表に返してアイロンで整える(下端は身頃に取り付けるため開けておく)。 + +### 2-3. 袖口(剣ボロ)の処理 +1. **スリットの処理:** 袖口のあき(スリット)を入れ、剣ボロ(表側)と下ボロ(裏側)をそれぞれ縫い付ける。 +2. **剣ボロの叩きつけ:** 剣ボロの先端を五角形に折り込み、袖の表側に叩きつけてステッチで固定する。あきの止まり部分(カンヌキ位置)を四角やバツ印のステッチで補強縫いする。 + +### 2-4. カフスの作成 +1. **カフス布の縫い合わせ:** カフス布2枚(表・裏)を中表に合わせ、外周(下端と左右)を縫い合わせる。 +2. **成形:** 表に返し、形を整えて周囲にステッチをかける(上端は袖口に縫い付けるため開けておく)。 + +--- + +## 3. まとめ縫い(身頃の組み立て・合体) + +各パーツを身頃と結合し、立体的に組み上げていく。右身頃のファスナーの重みで生地が引っ張られやすいため、地縫いの際は常に平らな状態を意識する。 + +### 3-1. ヨークと身頃の縫い合わせ +1. **後身頃の処理:** 後身頃にタックやギャザーがある場合は、あらかじめ畳んで仮止めする。 +2. **後身頃とヨークの結合:** 後身頃を2枚のヨーク(表ヨーク・裏ヨーク)で挟み込み、3枚一緒に縫い合わせる(地縫い)。 +3. **前身頃とヨークの結合(どんでん返し):** ヨークを上に起こし、前身頃の肩とヨーク(表・裏)を縫い合わせる。一般的なシャツの手法(どんでん返し)を用い、肩の縫い代をヨークの内部に完全に格納する。 +4. **コバステッチ:** ヨークの端(後身頃側・肩側)にステッチをかける。 + +### 3-2. 衿の取り付け +1. **台衿(外側)の縫い付け:** 身頃の襟ぐりに、作成済みの台衿(外側・表側)を縫い合わせる。 +2. **台衿(内側)の仕上げ:** 内側の台衿の縫い代を被せ、台衿の周囲にぐるりと落としミシン、またはコバステッチをかけて固定する。 + +### 3-3. 袖の取り付け +1. **アームホールの縫合:** 身頃を広げた状態で、肩口(アームホール)に袖を縫い合わせる。 +2. **端処理:** 縫い代は折り伏せ縫い、または巻伏せ縫いで処理する。 + +### 3-4. 袖下から脇縫い +1. **一気縫い:** 袖口から袖下、脇の下を通って裾に至るラインを、中表で一気に縫い合わせる。 +2. **端処理:** 同様に折り伏せ縫い等の手法で端を処理する。 + +### 3-5. カフスの取り付け +1. **袖口の仮止め:** 袖口のギャザーやタックを寄せて仮止めする。 +2. **カフスの縫合:** 袖口に作成済みのカフスを差し込み、表側から縫い合わせる。 +3. **裏側の仕上げ:** 内側のカフスの裏側を被せ、ステッチをかけて仕上げる。 + +--- + +## 4. 仕上げ + +1. **裾の処理:** 裾のカーブを三つ折り(またはヘム引き仕様)にし、ステッチをかける。右前身頃のファスナー下端との干渉に注意し、綺麗に収める。 +2. **ボタンホールとボタン:** 左前立て、台衿、カフス、剣ボロ(必要に応じて)にボタンホールを開け、対応する位置にボタンを縫い付ける。 +3. **最終プレス:** 全体の糸くずを入念に取り除き、アイロンでプレスして完成となる。 diff --git a/__pycache__/mov2mp4.cpython-312.pyc b/__pycache__/mov2mp4.cpython-312.pyc Binary files differ. diff --git a/batch_mov2mp4.py b/batch_mov2mp4.py @@ -47,10 +47,10 @@ def run_batch_conversion(parent_dir, start_range, end_range): # --- 設定項目 --- # 親フォルダのパス -PARENT_FOLDER = r"E:\Pictures\他人作品\ホロ" +PARENT_FOLDER = r"E:\Pictures\他人作品\ホロ\aliceholic13" # 処理対象のフォルダ名の範囲 -START_DIR = "2025010601" -END_DIR = "2026042405" +START_DIR = "1322528" +END_DIR = "3035118" if __name__ == "__main__": run_batch_conversion(PARENT_FOLDER, START_DIR, END_DIR) diff --git a/mov2mp4.py b/mov2mp4.py @@ -1,3 +1,4 @@ + import glob import json import os @@ -5,9 +6,6 @@ import subprocess def get_video_info(file_path): - """ - ffprobe を使用して動画のコーデック名とビットレートを取得します。 - """ cmd = [ "ffprobe", "-v", @@ -31,52 +29,47 @@ def get_video_info(file_path): video_stream = data.get("streams", [{}])[0] codec_name = video_stream.get("codec_name", "unknown") - # ビットレートは stream か format のいずれかから取得を試みる bitrate = video_stream.get("bit_rate") if not bitrate or bitrate == "N/A": bitrate = data.get("format", {}).get("bit_rate") return codec_name, int(bitrate) if bitrate and bitrate != "N/A" else None except Exception as e: - print(f" ⚠️ 動画情報の取得に失敗しました: {e}") + print(f" ⚠️ 動画情報の取得に失敗しました: {e}") return "unknown", None def convert_mov_to_mp4_h265_nvenc(input_dir, output_dir=None): - # 入力フォルダーの存在を確認 if not os.path.isdir(input_dir): print(f"エラー: 入力フォルダー '{input_dir}' が見つかりません。") return - # 出力フォルダーを設定。指定がなければ入力フォルダーと同じ if output_dir is None: output_dir = input_dir - # 出力フォルダーが存在しない場合は作成 if not os.path.exists(output_dir): os.makedirs(output_dir) print(f"出力フォルダー '{output_dir}' を作成しました。") - # 動画ファイルを検索 (.mov と .mp4) video_files = [] - for ext in ["*.mov", "*.mp4"]: + # 拡張子の大文字小文字を網羅するため、globの検索パターンを修正 + for ext in ["*.mov", "*.MOV", "*.mp4", "*.MP4", "*.mkv", "*.MKV"]: video_files.extend(glob.glob(os.path.join(input_dir, ext))) if not video_files: print(f"'{input_dir}' 内に動画ファイルが見つかりませんでした。") return - video_files.sort() + # 重複してヒットする可能性を排除 + video_files = sorted(list(set(os.path.abspath(f) for f in video_files))) print(f"--- 変換処理を開始します (合計 {len(video_files)} ファイル) ---") converted_count = 0 - # スキップ判定用のしきい値 (5Mbps) - BITRATE_THRESHOLD = 5_000_000 + BITRATE_THRESHOLD = 1_000_000 for input_file_path in video_files: file_name_without_ext, ext = os.path.splitext(os.path.basename(input_file_path)) - # 入力がmp4の場合、一時的なファイル名を使用して衝突を避ける is_mp4_input = ext.lower() == ".mp4" if is_mp4_input: output_file_path = os.path.join( @@ -84,33 +77,28 @@ def convert_mov_to_mp4_h265_nvenc(input_dir, output_dir=None): ) else: output_file_path = os.path.join(output_dir, file_name_without_ext + ".mp4") + + output_file_path = os.path.abspath(output_file_path) print(f"\n🔍 解析中: '{os.path.basename(input_file_path)}' ...") codec, bitrate = get_video_info(input_file_path) - # 判定1: 既にHEVC(H.265)か if codec in ["hevc", "h265"]: - print(f" ⏭️ スキップ: 既に HEVC コーデックです。") + print(f" ⏭️ スキップ: 既に HEVC コーデックです。") continue - # 判定2: ビットレートが低すぎないか if bitrate and bitrate < BITRATE_THRESHOLD: print( - f" ⏭️ スキップ: ビットレートが低いため変換のメリットがありません ({bitrate / 1_000_000:.2f} Mbps < {BITRATE_THRESHOLD / 1_000_000} Mbps)" + f" ⏭️ スキップ: ビットレートが低いため変換のメリットがありません ({bitrate / 1_000_000:.2f} Mbps < {BITRATE_THRESHOLD / 1_000_000} Mbps)" ) continue - # 出力先の最終的なファイル名が既に存在するかチェック(入力ファイル自体は除く) - final_output_path = os.path.join(output_dir, file_name_without_ext + ".mp4") - if os.path.exists(final_output_path) and os.path.abspath( - final_output_path - ) != os.path.abspath(input_file_path): - print( - f"\n❌ エラー: 出力予定のファイル '{final_output_path}' が既に存在します。" - ) - print( - "既存のファイルを上書きしないよう、このファイルの処理をスキップします。" - ) + final_output_path = os.path.abspath(os.path.join(output_dir, file_name_without_ext + ".mp4")) + + # Windowsの仕様に合わせ、比較時にすべて小文字化して判定する + if os.path.exists(final_output_path) and final_output_path.lower() != input_file_path.lower(): + print(f"\n❌ エラー: 出力予定のファイル '{final_output_path}' が既に存在します。") + print("既存のファイルを上書きしないよう、このファイルの処理をスキップします。") continue ffmpeg_command = [ @@ -124,9 +112,7 @@ def convert_mov_to_mp4_h265_nvenc(input_dir, output_dir=None): output_file_path, ] - print( - f"\n✅ 変換中: '{os.path.basename(input_file_path)}' -> '{os.path.basename(output_file_path)}'" - ) + print(f"\n✅ 変換中: '{os.path.basename(input_file_path)}' -> '{os.path.basename(output_file_path)}'") try: subprocess.run( @@ -136,61 +122,42 @@ def convert_mov_to_mp4_h265_nvenc(input_dir, output_dir=None): stderr=subprocess.PIPE, encoding="utf-8", ) - print(" ✨ 変換成功。") + print(" ✨ 変換成功。") - # サイズ比較と整理 input_size = os.path.getsize(input_file_path) output_size = os.path.getsize(output_file_path) - # 閾値(元のサイズの90%未満になっていれば採用) THRESHOLD = 0.9 ratio = output_size / input_size if input_size > 0 else 1 if ratio < THRESHOLD: - # 元ファイルを削除 os.remove(input_file_path) - # 入力がmp4だった場合は一時ファイルを本来の名前にリネーム + # すでに小文字のfinal_output_pathが存在し、それが大文字のinput_file_pathであった場合は + # os.removeで消えているため、安全にrename可能 if is_mp4_input: os.rename(output_file_path, final_output_path) - print(f" 🗑️ サイズが十分に削減されたため、元ファイルを整理しました。") - print( - f" ({input_size:,} bytes -> {output_size:,} bytes, 削減率: {1 - ratio:.1%})" - ) + print(f" 🗑️ サイズが十分に削減されたため、元ファイルを整理しました。") + print(f" ({input_size:,} bytes -> {output_size:,} bytes, 削減率: {1 - ratio:.1%})") converted_count += 1 else: - # 削減が不十分なら変換後(一時)ファイルを削除 os.remove(output_file_path) - print( - f" ⚠️ サイズ削減が不十分なため、変換後ファイルを破棄し元ファイルを維持します。" - ) + print(f" ⚠️ サイズ削減が不十分なため、変換後ファイルを破棄し元ファイルを維持します。") print(f" (比率: {ratio:.1%} / 閾値: {THRESHOLD:.1%})") except subprocess.CalledProcessError as e: - print(f" ❌ エラーが発生しました: '{os.path.basename(input_file_path)}'") - print( - f" FFmpeg出力:\n{e.stderr}..." - ) # エラーメッセージの最初500文字を表示 + print(f" ❌ エラーが発生しました: '{os.path.basename(input_file_path)}'") + print(f" FFmpeg出力:\n{e.stderr}...") except FileNotFoundError: - print( - " ❌ エラー: FFmpegがインストールされていないか、PATHが通っていません。" - ) - return # FFmpegが見つからない場合は処理を中断 + print(" ❌ エラー: FFmpegがインストールされていないか、PATHが通っていません。") + return print(f"\n--- 処理完了: {converted_count} 個のファイルが正常に変換されました ---") -# --- 以下を convert.py の一番下に追加してください --- - -# 変換対象のフォルダーパス -INPUT_FOLDER = r"E:\Pictures\他人作品\ホロ" - -# 変換後のファイルを保存するフォルダーパス (NoneにするとINPUT_FOLDERと同じになります) +INPUT_FOLDER = r"E:\Pictures\他人作品\ホロ\temp" OUTPUT_FOLDER = None -# OUTPUT_FOLDER = r"E:\Pictures\他人作品\ホロ\2025112602" if __name__ == "__main__": - # Windowsの場合、パスは r"C:\Users\User\Videos" のように記述すると安全です - # Mac/Linuxの場合、パスは "/Users/user/Movies/Source" のように記述します convert_mov_to_mp4_h265_nvenc(INPUT_FOLDER, OUTPUT_FOLDER) diff --git a/output_espcn.jpg b/output_espcn.jpg Binary files differ. diff --git a/rand-noise-image.py b/rand-noise-image.py @@ -0,0 +1,21 @@ + +import random +from PIL import Image + +def change_random_pixels(image_path, output_path, iterations, color_hex="#2e1b1e"): + color = tuple(int(color_hex.lstrip('#')[i:i+2], 16) for i in (0, 2, 4)) + + with Image.open(image_path) as img: + img = img.convert("RGB") + pixels = img.load() + width, height = img.size + + for _ in range(iterations): + x = random.randint(0, width - 1) + y = random.randint(0, height - 1) + pixels[x, y] = color + + img.save(output_path) + +if __name__ == "__main__": + change_random_pixels("input.png", "output.png", 200000) diff --git a/similar_video.py b/similar_video.py @@ -0,0 +1,261 @@ +import os +import json +from pathlib import Path +import cv2 +import numpy as np +from PIL import Image +import imagehash +import ctypes +from ctypes import wintypes + +def get_video_properties(video_path, root_path): + cap = cv2.VideoCapture(str(video_path)) + if not cap.isOpened(): + return {} + + width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) + height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) + fps = cap.get(cv2.CAP_PROP_FPS) + total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) + + duration = total_frames / fps if fps > 0 else 0 + file_size_mb = os.path.getsize(video_path) / (1024 * 1024) + + cap.release() + + try: + rel_path = str(video_path.relative_to(root_path)) + except ValueError: + rel_path = str(video_path) + + return { + 'rel_path': rel_path, + 'resolution': f"{width}x{height}", + 'duration': f"{duration:.2f}s", + 'fps': f"{fps:.2f}", + 'size': f"{file_size_mb:.2f} MB" + } + +def calculate_video_phash(video_path, sample_frames=10): + cap = cv2.VideoCapture(str(video_path)) + if not cap.isOpened(): + return None + + total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) + if total_frames <= 0: + cap.release() + return None + + frame_indices = np.linspace(0, total_frames - 1, sample_frames, dtype=int) + hashes = [] + + for idx in frame_indices: + cap.set(cv2.CAP_PROP_POS_FRAMES, idx) + ret, frame = cap.read() + if not ret: + continue + + frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) + pil_img = Image.fromarray(frame_rgb) + + try: + hash_val = imagehash.phash(pil_img) + hashes.append(hash_val) + except Exception: + continue + + cap.release() + return hashes + +def load_or_calculate_hashes(root_dir, video_paths, sample_frames=10): + cache_path = Path(root_dir) / ".video_phash_cache.json" + cache_data = {} + + if cache_path.exists(): + try: + with open(cache_path, "r", encoding="utf-8") as f: + cache_data = json.load(f) + except Exception: + pass + + video_hashes = {} + updated_cache = {} + cache_changed = False + + for path in video_paths: + try: + stat = path.stat() + mtime = stat.st_mtime + size = stat.st_size + except Exception: + continue + + str_path = str(path) + + if str_path in cache_data: + cached = cache_data[str_path] + if cached.get('mtime') == mtime and cached.get('size') == size: + if cached['hashes']: + video_hashes[path] = [imagehash.hex_to_hash(h) for h in cached['hashes']] + updated_cache[str_path] = cached + continue + + hashes = calculate_video_phash(path, sample_frames) + + hash_strings = [str(h) for h in hashes] if hashes else [] + if hashes: + video_hashes[path] = hashes + + updated_cache[str_path] = { + 'mtime': mtime, + 'size': size, + 'hashes': hash_strings + } + cache_changed = True + + if len(cache_data) != len(updated_cache): + cache_changed = True + + if cache_changed: + try: + with open(cache_path, "w", encoding="utf-8") as f: + json.dump(updated_cache, f, indent=2, ensure_ascii=False) + except Exception: + pass + + return video_hashes + +def open_in_explorer(file_paths): + if not file_paths: + return + + shell32 = ctypes.windll.shell32 + ole32 = ctypes.windll.ole32 + + ole32.CoInitialize(None) + + dir_path = str(file_paths[0].parent) + + # 関数の引数と戻り値の型を厳密に定義 + ILCreateFromPathW = shell32.ILCreateFromPathW + ILCreateFromPathW.restype = ctypes.c_void_p + ILCreateFromPathW.argtypes = [wintypes.LPCWSTR] + + ILFree = shell32.ILFree + ILFree.argtypes = [ctypes.c_void_p] + + SHOpenFolderAndSelectItems = shell32.SHOpenFolderAndSelectItems + SHOpenFolderAndSelectItems.restype = ctypes.HRESULT + SHOpenFolderAndSelectItems.argtypes = [ctypes.c_void_p, wintypes.UINT, ctypes.c_void_p, wintypes.DWORD] + + dir_pidl = ILCreateFromPathW(dir_path) + if not dir_pidl: + ole32.CoUninitialize() + return + + file_pidls = [] + for p in file_paths: + pidl = ILCreateFromPathW(str(p)) + if pidl: + file_pidls.append(pidl) + + if file_pidls: + # c_void_p の配列として正確に確保 + pidl_array = (ctypes.c_void_p * len(file_pidls))(*file_pidls) + SHOpenFolderAndSelectItems(dir_pidl, len(file_pidls), ctypes.byref(pidl_array), 0) + + for pidl in file_pidls: + ILFree(pidl) + + ILFree(dir_pidl) + ole32.CoUninitialize() + +def group_similar_videos(video_hashes, threshold=15): + paths = list(video_hashes.keys()) + num_videos = len(paths) + + parent_map = {p: p for p in paths} + + def find(p): + if parent_map[p] == p: + return p + parent_map[p] = find(parent_map[p]) + return parent_map[p] + + def union(p1, p2): + root1 = find(p1) + root2 = find(p2) + if root1 != root2: + parent_map[root2] = root1 + + for i in range(num_videos): + for j in range(i + 1, num_videos): + path_a = paths[i] + path_b = paths[j] + + hashes_a = video_hashes[path_a] + hashes_b = video_hashes[path_b] + + min_len = min(len(hashes_a), len(hashes_b)) + if min_len == 0: + continue + + distances = [hashes_a[k] - hashes_b[k] for k in range(min_len)] + avg_distance = sum(distances) / min_len + + if avg_distance <= threshold: + union(path_a, path_b) + + groups = {} + for p in paths: + root = find(p) + if root not in groups: + groups[root] = [] + groups[root].append(p) + + return [g for g in groups.values() if len(g) > 1] + +def display_table(properties_list): + headers = ["Relative Path", "Resolution", "Duration", "FPS", "File Size"] + col_widths = [max(len(str(p.get(h, ''))) for p in properties_list) for h in ['rel_path', 'resolution', 'duration', 'fps', 'size']] + col_widths = [max(w, len(h)) for w, h in zip(col_widths, headers)] + + header_str = " | ".join(f"{h:<{w}}" for h, w in zip(headers, col_widths)) + print("-" * len(header_str)) + print(header_str) + print("-" * len(header_str)) + + for props in properties_list: + row_str = " | ".join(f"{str(props.get(k, '')):<{w}}" for k, w in zip(['rel_path', 'resolution', 'duration', 'fps', 'size'], col_widths)) + print(row_str) + print("-" * len(header_str)) + +if __name__ == '__main__': + target_folder = r"E:\Pictures\他人作品\ホロ" + root_path = Path(target_folder) + + video_extensions = {'.mp4', '.avi', '.mkv', '.mov', '.flv', '.wmv'} + video_paths = [] + + for p in root_path.rglob('*'): + if p.suffix.lower() in video_extensions: + video_paths.append(p) + + video_hashes = load_or_calculate_hashes(root_path, video_paths, sample_frames=10) + groups = group_similar_videos(video_hashes, threshold=12) + + if not groups: + print("類似動画は検出されませんでした。") + else: + for idx, group in enumerate(groups, 1): + print(f"\n[Match Group {idx}/{len(groups)}] 一致数: {len(group)}") + + props_list = [get_video_properties(p, root_path) for p in group] + display_table(props_list) + + open_in_explorer(group) + + if idx < len(groups): + input("\n次のグループを表示するには Enter キーを押してください...") + else: + print("\nすべてのグループの処理が完了しました。") diff --git a/upscale_image.py b/upscale_image.py @@ -0,0 +1,40 @@ + +# import cv2 + +# def upscale_image(image_path, output_path, model_path): +# sr = cv2.dnn_superres.DnnSuperResImpl_create() +# sr.readModel(model_path) +# sr.setModel("espcn", 4) + +# image = cv2.imread(image_path) +# result = sr.upsample(image) +# cv2.imwrite(output_path, result) + +# if __name__ == "__main__": +# upscale_image(r"C:\Users\ryout\Downloads\DSCN1883.JPG", "output_espcn.jpg", r"C:\Users\ryout\Downloads\ESPCN_x4.pb") +# + + +import cv2 +from realesrgan import RealESRGANer +from basicsr.archs.rrdbnet_arch import RRDBNet + +def super_resolve_ai(image_path, output_path): + model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4) + + upscaler = RealESRGANer( + scale=4, + model_path='https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth', + model=model, + tile=0, + tile_pad=10, + pre_pad=0, + half=False + ) + + img = cv2.imread(image_path, cv2.IMREAD_COLOR) + output, _ = upscaler.enhance(img, outscale=4) + cv2.imwrite(output_path, output) + +if __name__ == "__main__": + super_resolve_ai(r"C:\Users\ryout\Downloads\DSCN1883.JPG", "output_realesrgan.jpg") diff --git a/wav2flac.nu b/wav2flac.nu @@ -0,0 +1,24 @@ +def main [ + path: path # 変換対象のディレクトリパス +] { + if not ($path | path exists) { + print $"Error: Path '($path)' does not exist." + return + } + + let targets = (ls ([$path, "*.wav"] | path join)) + + if ($targets | is-empty) { + print "No .wav files found." + return + } + + $targets | par-each { |it| + let output = ($it.name | str replace --regex '(?i)\.wav$' '.flac') + print $"Converting: ($it.name) -> ($output)" + + ffmpeg -i $it.name -compression_level 8 -loglevel error $output + } + + print "Done." +}