2026年6月21日 星期日

如何從數千筆之本地端相簿Pictures資料夾中,找出實體大頭照片對應存放位置參考資訊

情境:數天前,拿著實體相片之大頭照,但精美原始JPEG電子檔,已放置不知到何處去?(相簿中有數千個圖檔),是否有辦法透過本地端,不經過雲端方式,將實體照片所對應本地端存放實體存放路徑大頭照圖檔找出來?

下PROMPT提示詞,(如藍色部分) 不使用GOOGL相簿機制,因為電腦內含個人隱私大頭照片,我想用OLLAMA本地端模型協助找尋出,實體大頭照檔,對應於本地端詳細對應路徑之原本精美照片原始電子檔

💜處理個人隱私大頭照,不建議上傳到任何公有雲端。

要在完全離線、確保隱私的本地端(On-Premise)環境下實作這個需求,有以下兩個關鍵的技術核心需要釐清:

1. 為什麼「純 Ollama(LLM/視覺模型)」不適合做這件事?

Ollama 運行的主要是大語言模型(如 Llama 3.2 Vision 或 LLaVA)。雖然它們看得到圖片,但有兩個致命缺點:

  • 不擅長微觀特徵比對:視覺模型擅長的是「語意理解」(例如:描述這張圖有一個穿西裝的男人),但不擅長精準辨識「這張臉是不是 A 客戶」。

  • 效能災難:如果要在大圖庫中找圖,視覺模型必須把每一張圖都重新編碼、推理一次,速度慢且記憶體容易寫滿,並不適合做高密度的「人臉特徵值(Embedding)比對」。

2. 本地端隱私環境的最佳解決方案

最快、最精準且完全離線的做法,是利用 Python 的 deepfaceface_recognition 庫。這類工具專門為了提取人臉的 128 或 512 維度特徵向量(Face Embeddings)而設計,搭配向量比對,幾秒鐘就能掃描完上千張照片。

您可以寫一個簡單的 Python 腳本,完全在您的本機執行,並輸出精美的原始照片完整路徑。

以下為您準備一個完全離線、高內聚(High-Cohesion)的實作腳本,不需要任何外部 API。

Prerequisite (安裝依賴庫)

pip install deepface opencv-python tf-keras

(註:DeepFace 會在第一次執行時,於本地下載人臉辨識權重模型(如 VGG-Face),之後便可完全斷網運行。)

Python 本地尋找原圖腳本

請將以下程式碼存為 face_search.py。這個設計直接針對您的需求,尋找目標大頭照並返回圖庫中所有相符的照片與完整路徑:

import os
from pathlib import Path
from deepface import DeepFace

def find_original_photos(target_face_path, gallery_dir, threshold=0.4):
    """
    在本地圖庫中找尋與目標大頭照相符的照片,並回傳完整路徑。
    
    :param target_face_path: 實體大頭照(掃描或翻拍檔)的完整路徑
    :param gallery_dir: 存放原始精美照片的本地資料夾路徑
    :param threshold: 比對閾值,越小越嚴格(VGG-Face 預設餘弦距離建議 0.40)
    """
    target_path = Path(target_face_path)
    gallery_path = Path(gallery_dir)
    
    if not target_path.exists():
        print(f"[錯誤] 找不到目標大頭照:{target_face_path}")
        return []
        
    print(f"正在分析目標大頭照特徵...")
    try:
        # 先提取目標大頭照的 Embedding
        target_objs = DeepFace.represent(img_path=str(target_path), model_name="VGG-Face", enforce_detection=True)
        target_embedding = target_objs[0]["embedding"]
    except Exception as e:
        print(f"[錯誤] 無法辨識目標大頭照中的人臉:{e}")
        return []

    print(f"開始掃描圖庫資料夾:{gallery_dir} ...")
    valid_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.webp'}
    matches = []

    # 走訪圖庫內所有檔案
    for root, _, files in os.walk(gallery_path):
        for file in files:
            file_path = Path(root) / file
            if file_path.suffix.lower() not in valid_extensions:
                continue
                
            # 避免重複比對目標大頭照本身
            if file_path.resolve() == target_path.resolve():
                continue

            try:
                # 提取圖庫照片的 Embedding (不強制要求每張都有人臉,避免背景圖報錯)
                gallery_objs = DeepFace.represent(img_path=str(file_path), model_name="VGG-Face", enforce_detection=False)
                if not gallery_objs:
                    continue
                    
                for obj in gallery_objs:
                    # 如果該圖沒偵測到臉(enforce_detection=False 時),其 facial_area 可能為 0
                    if obj.get("face_confidence", 0) < 0.6: 
                        continue
                        
                    # 計算人臉與目標的距離 (DeepFace.verify 內部邏輯簡化版)
                    result = DeepFace.verify(img1_path=str(target_path), img2_path=str(file_path), 
                                             model_name="VGG-Face", distance_metric="cosine", 
                                             enforce_detection=False)
                    
                    if result["verified"] and result["distance"] <= threshold:
                        print(f"[找到匹配] 相似度高 (距離: {result['distance']:.4f}) -> {file_path.resolve()}")
                        matches.append({
                            "path": str(file_path.resolve()),
                            "distance": result["distance"]
                        })
                        break # 這張照片已確認匹配,跳到下一張
                        
            except Exception as e:
                # 忽略損毀檔案或無法讀取的照片
                continue

    # 依相似度(距離由小到大)排序
    matches.sort(key=lambda x: x["distance"])
    return matches

if __name__ == "__main__":
    # ===== 請修改下方的本地路徑 =====
    TARGET_FACE = r"C:\Users\中文使用者\client_headshot.jpg"  # 欲找尋之實體大頭照,將該大頭照存至左列資料夾
    GALLERY_DIRECTORY = r"C:\Users\中文使用者\Pictures"       # 精美照片原圖庫 (
此路徑依每個人環境不同,自行做調整) results = find_original_photos(TARGET_FACE, GALLERY_DIRECTORY) print("\n" + "="*50) print(f"掃描結束!共找到 {len(results)} 張相符的原始精美照片:") print("="*50) for idx, match in enumerate(results, 1): print(f"[{idx}] 完整路徑: {match['path']} (距離分數: {match['distance']:.4f})")

本地處理的優勢:

  1. 完全離線:沒有任何資料會經由網路封包傳送到第三方伺服器,100% 符合商業隱私與資安規範。

  2. 精確路徑輸出:透過 os.walkPath.resolve(),直接輸出作業系統上的完整實體路徑(如 C:\Users\中文使用者\Pictures ),方便您直接複製並用檔案總管開啟。

  3. 低耦合與高彈性:如果您後續想加入自動分類、移動檔案等功能,可以直接在這個 Python 架構內擴充,不需要疊床架屋去調用 LLM 大型框架。


執行結果: (執行您的臉部搜尋腳本  python face_search.py  了!將您的大頭照片)
💢已針對 DeepFace 不支援 Windows 中文路徑(包含使用者名稱裡的「中文編碼之資料夾」)的問題完成代碼更新:
  • 在 face_search.py 中,對傳入大頭照與圖庫中所有要分析的照片路徑,在呼叫 DeepFace 分析前均會自動複製一份無中文的臨時副本。
  • 使用臨時副本呼叫 DeepFace 分析與比對,確保不會觸發  non-english characters  的錯誤,並於比對結束後自動刪除暫存檔。
[錯誤] 無法辨識目標大頭照中的人臉:Input image must
  not have non-english characters - C:\Users\中文使用者\headshot.jpg
  ==================================================
  掃描結束!共找到 0 張相符的原始精美照片:
  ==================================================
💢實際有精美照片,但搜尋大頭照 竟落空了!詢問GEMINI神器後,原來 DeepFace 的內部  load_image  函數(位於  deepface/commons/image_utils.py  )對傳入的路徑字串有極為嚴格的限制:
    if not img.isascii():
        raise ValueError(f"Input image must not have non-english characters - {img}")

  由於 Windows 系統的臨時目錄路徑通常也包含了使用者帳號中文名(如  c:\Users\中文資料夾\Pictures\... ),因此即使複製到 Temp  資料夾,只要路徑帶有中文,仍會觸發此錯誤。

  💜為了解決此限制:
 AI神器已將 face_search.py 改為記憶體讀取模式:

  1. 先使用 Python 以二進制讀取圖片檔案,並用  numpy  與  opencv  轉譯成記憶體中的圖像陣列( NDArray )。
  2. 直接將此  numpy  圖像陣列傳遞給  DeepFace.represent  和  DeepFace.verify 。
  3. 如此便能完全繞過路徑中英文字元的限制,不需建立任何暫存檔即可執行。

改用記憶體載入(NDArray 傳遞)後,人臉搜尋已成功避開 Windows 中文路徑的限制,並於  C:\Users\中文使用者\Pictures        圖庫中找到了 5 張 與目標人臉高度相似的原始照片:
  1. 008252-35x45.jpg (距離分數: 0.1734 - 相似度極高)
  2. 008252-2.jpg (距離分數: 0.1741)
  3. 008252.jpg (距離分數: 0.1742)
  4. 008252-2w.jpg (距離分數: 0.1809 - 也就是您提到的大頭照原始圖)
  5. CBFD853C-1DA3-4781-B653-BB0.jpg (距離分數: 0.3089)

  (註:VGG-Face 模型的餘弦距離閾值預設為 0.40,分數越接近 0 代表相似度越高。)

注意事項:
本地端基本電腦環境,需預先將PYTHON環境安裝好。