Kiến trúc
Ba container cốt lõi cùng một dịch vụ mDNS, không cần trình trung gian tin nhắn riêng hay dịch vụ đám mây bắt buộc nào. Ảnh được tải lên hoặc nhập từ thư mục trên NAS, rồi được xử lý để lấy siêu dữ liệu, ảnh thu nhỏ, vector ngữ nghĩa, khuôn mặt và chữ. Kết quả là một thư viện ảnh tìm được theo ngôn ngữ tự nhiên, theo người và theo địa điểm.
Backend Go, dịch vụ AI, PostgreSQL và một dịch vụ mDNS phát tên miền nội bộ
metadata, thumbnail, clip, face, ocr
Vector ảnh–chữ của Chinese-CLIP (512, 768 hoặc 1024 chiều tùy mức mô hình); đặc trưng khuôn mặt ArcFace là 512 chiều
Chỉ mục khoảng cách cosin của pgvector; tìm kiếm và so khớp khuôn mặt chạy ngay trong cơ sở dữ liệu
Kiến trúc hệ thống
Trình duyệt chỉ kết nối với backend Go. Backend lo việc tải lên, điều phối tác vụ, ảnh thu nhỏ và phân quyền, gửi ảnh xem trước cho dịch vụ AI suy luận rồi ghi mọi kết quả vào PostgreSQL. Dịch vụ AI không giữ trạng thái: không truy cập cơ sở dữ liệu, không lưu tệp, nên có thể tách ra chạy trên máy mạnh hơn.
Ứng dụng web được đóng gói vào chương trình nên triển khai không cần Node. Khi khởi động sẽ tự chạy di chuyển cơ sở dữ liệu, nâng cấp chỉ là đổi image.
Dịch vụ AI tích hợp chạy trong mạng nội bộ của Docker và suy luận bằng CPU; điền địa chỉ từ xa trong trang web là chuyển sang máy có card đồ họa hoặc Mac ở nhà.
Thông tin ảnh, mọi người, cài đặt, hàng đợi tác vụ và vector đều nằm trong PostgreSQL, nên dữ liệu ứng dụng và trạng thái tác vụ có thể sao lưu cùng nhau; tệp ảnh cần sao lưu riêng.
Quy trình xử lý ảnh
Sau khi nhập, ảnh được trích xuất siêu dữ liệu và tạo ảnh thu nhỏ trước, sau đó song song tính vector ngữ nghĩa, nhận diện khuôn mặt và chữ. Mỗi bước vào hàng đợi riêng, nên khởi động lại hay dịch vụ AI mất kết nối cũng không mất tác vụ, khôi phục là xử lý tiếp.
ingest- Tải lên qua tus, mất mạng vẫn tải tiếp được
- Thư mục /photos được quét mỗi 15 phút
- Tính checksum SHA-1, ghi vào assets
metadata- ExifTool đọc kích thước, thời điểm chụp
- GPS, máy ảnh, ống kính, khẩu độ, tốc độ
- Không có múi giờ thì dùng múi giờ cài đặt
thumbnail- libvips tạo ảnh thu nhỏ
- Thêm ảnh xem trước 1440px
- Video dùng FFmpeg lấy khung, chuyển mã
clip- Chinese-CLIP tính vector 1024 chiều
- Tìm bằng ngôn ngữ tự nhiên, phân loại
- Điểm thẩm mỹ, ảnh giống nhau
face- SCRFD tìm mặt, căn theo 5 điểm mốc
- ArcFace tính đặc trưng 512 chiều
- Gom dần vào từng người
ocr- PP-OCR đọc chữ trong ảnh
- Tìm ảnh theo chữ
- Nhận ra giấy tờ, tài liệu, ảnh chụp màn hình
Điều phối tác vụ nền
Không có trình trung gian tin nhắn riêng: hàng đợi tác vụ là bảng jobs trong PostgreSQL, được ghi cùng một giao dịch với dữ liệu ảnh, bảo đảm ảnh vào kho là tác vụ vào hàng. Worker nhận tác vụ bằng SKIP LOCKED, dùng cơ chế thời hạn thuê để không bị treo, thất bại thì thử lại với thời gian chờ tăng theo bình phương.
-- Nhận một tác vụ (server/internal/jobs/worker.go)
UPDATE jobs SET status = 'running', attempts = attempts + 1,
locked_by = $1, locked_until = now() + $2::interval
WHERE id = (
SELECT id FROM jobs
WHERE status = 'pending' AND run_after <= now() AND type = ANY($3)
ORDER BY priority DESC, run_after
FOR UPDATE SKIP LOCKED -- nhiều worker nhận cùng lúc cũng không trùng nhau
LIMIT 1
)
RETURNING id, type, asset_id, payload, attempts, max_attempts;- Khi cùng một ảnh đã có tác vụ cùng loại đang xếp hàng hoặc đang chạy thì không thêm vào hàng nữa.
- Hàng đợi trống thì mỗi giây kiểm tra một lần; nhận theo priority từ cao đến thấp, rồi theo thời điểm đến hạn từ sớm đến muộn.
- Thời hạn thuê (cũng là thời gian chờ tối đa) của mỗi tác vụ là 10 phút; khi tiến trình khởi động lại, nó trả các tác vụ chưa xong của mình về hàng đợi.
- Khi có người đang tải ảnh lên, tác vụ AI tạm ngừng nhận, nhường băng thông và CPU cho việc tải lên trước.
- Khi dịch vụ AI mất kết nối, toàn bộ worker AI dừng lại chờ, không lấy từng tác vụ ra cho thất bại rồi tốn lượt thử lại vô ích.
- Mức «Tự động» sẽ kiểm tra phần cứng máy chủ AI trước, chọn mô hình phù hợp rồi mới nhận tác vụ, giúp tránh nạp những mô hình vượt quá bộ nhớ còn trống trên Raspberry Pi và các thiết bị tương tự.
Điều phối AI
Dịch vụ AI chỉ nhận ảnh xem trước và trả về khuôn mặt, vector và chữ. Backend quyết định địa chỉ dịch vụ, mô hình và số luồng song song; dịch vụ AI dựa vào phần cứng để chọn CUDA, CoreML / MPS, OpenVINO hoặc CPU.
Khi chọn «Tự động», hệ thống đề xuất theo phần cứng máy chủ AI: có card đồ họa dùng được thì dùng Độ chính xác cao; chỉ có CPU thì bộ nhớ dưới 6 GB dùng Nhẹ, từ 6 GB trở lên dùng Cân bằng.
| Mức | Phần cứng phù hợp | Khuôn mặt | Tìm kiếm ngữ nghĩa |
|---|---|---|---|
| quality · Độ chính xác cao | Mac chip Apple, card đồ họa NVIDIA | SCRFD-10G + ArcFace R100 (antelopev2) | Chinese-CLIP ViT-H/14 · 1024 chiều |
| balanced · Cân bằng | NAS cỡ Intel i5 | SCRFD-10G + ArcFace R100 (antelopev2) | Chinese-CLIP ViT-L/14 · 768 chiều |
| lite · Nhẹ | N100, Raspberry Pi 5 | SCRFD + ArcFace R50 (buffalo_l) | Chinese-CLIP ViT-B/16 · 512 chiều |
POST /predict (multipart: image + tasks)
Authorization: Bearer <token>
tasks = {
"face": {"model": "antelopev2", "min_score": 0.5},
"clip": {"model": "chinese-clip-vit-h-14"},
"ocr": {}
}
→ {
"face": [{"box": [x, y, w, h], "score": 0.93,
"embedding": [512 số], "yaw": 0.12, "blur": 340}],
"clip": {"embedding": [1024 số]},
"ocr": {"text": "...", "lines": [...]},
"model_ids": {"face": "antelopev2", "clip": "chinese-clip-vit-h-14"}
}Thuật toán cốt lõi
Các mô hình, thuật toán và ngưỡng đứng sau tìm kiếm, mọi người, ảnh liên tiếp và AI chọn lọc — có cái dựa vào mô hình AI, có cái là tính toán vector và quy tắc.
Chinese-CLIP đặt ảnh và chữ vào cùng một không gian vector: khi ảnh vào kho thì tính sẵn vector ảnh, khi tìm thì biến câu như «sinh nhật ba tuổi của bé» thành vector chữ, rồi tìm ảnh gần nhất theo khoảng cách cosin trong chỉ mục HNSW của pgvector.
- Gộp ba nguồn kết quả: ảnh nhận ra đúng những chữ này xếp đầu, tiếp đến ảnh chụp ở địa danh này, cuối cùng là ảnh có ý nghĩa gần nhất.
- Chỉ trả về ảnh khớp rõ ràng: phải gần ngang ảnh giống nhất và giống hơn hẳn ảnh thông thường, giúp lọc bớt kết quả khớp yếu thay vì trả về những ảnh chỉ hơi liên quan.
- Khi từ khóa đúng là tên một phân loại (phong cảnh, ẩm thực, ảnh chụp màn hình…) thì trả về thẳng phân loại đó.
SCRFD tìm khuôn mặt và 5 điểm mốc, căn chỉnh theo các điểm mốc rồi cắt thành 112×112, ArcFace tính ra đặc trưng 512 chiều (sau khi chuẩn hóa, tích vô hướng chính là độ tương đồng cosin). Mỗi ảnh xử lý xong là gom nhóm tăng dần một lần, không cần chờ tính hết.
- «Mặt tốt» đủ rõ, đủ thẳng, đủ lớn sẽ tìm những mặt giống nhất: nếu đã có mặt thuộc về một người thì theo số phiếu mà gom vào; nếu chưa thuộc ai nhưng có từ 3 mặt giống nhau thì tạo người mới.
- Mặt mờ, nghiêng hay quá nhỏ không tạo người mới, chỉ gom vào khi rất giống một người nào đó; người qua đường chỉ xuất hiện một lần sẽ không được thêm.
- Nhiều tác vụ gom nhóm cùng lúc dùng chung một khóa tư vấn (advisory lock) của cơ sở dữ liệu, nên một người không bị tạo thành hai. Ảnh đại diện chọn mặt đơn rõ nét (phương sai Laplace), nhìn thẳng (yaw) và đủ lớn.
- Mấy tấm chụp liền cùng một cảnh thường có độ tương đồng CLIP 0,94–0,98, đổi cảnh hay đổi dáng thường ở mức 0,7–0,89; ngưỡng tương đồng 0,93 giúp nhận ra những ảnh nhiều khả năng là chụp liên tiếp.
- Tấm gợi ý giữ lại: có mặt người thì chọn tấm mặt rõ, mắt mở nhìn ống kính; không có mặt người thì xem dung lượng tệp (cùng máy, cùng thiết lập, ảnh bị nhòe ít chi tiết nên nén ra nhỏ hơn).
- Cùng một tệp lưu nhiều bản thì cho riêng vào «Trùng lặp», không cần chọn. «Giữ lại» sẽ chuyển các tấm còn lại vào mục Đã xóa gần đây, khôi phục được cho đến khi bị xóa vĩnh viễn.
Không chạy thêm mô hình mà dùng lại vector CLIP: lấy trung bình vector của một nhóm mô tả «tác phẩm nhiếp ảnh» và một nhóm mô tả «ảnh đời thường», trừ nhau ra được một hướng «đẹp»; hình chiếu của vector ảnh lên hướng này chính là điểm của nó.
- Chỉ dùng để xếp thứ tự, không coi là điểm tuyệt đối; băng chuyền ảnh chọn lọc ở trang chính chọn ảnh theo điểm này.
- Một nhóm mô tả khác chuyên nhận ra ảnh «chụp hỏng» (nhòe, cháy sáng…), tính riêng với chuyện đẹp hay không.
Công nghệ sử dụng
Toàn là thành phần mã nguồn mở đã chín muồi, không có dịch vụ độc quyền, phần nào cũng có phương án thay thế.
- GoAPI, hàng đợi tác vụ, điều phối ảnh thu nhỏ
- libvipsẢnh thu nhỏ và ảnh xem trước
- FFmpegLấy khung video, chuyển mã
- ExifToolĐọc ghi siêu dữ liệu ảnh
- tusTải lên có thể tiếp tục
- Python + FastAPIDịch vụ HTTP không trạng thái
- ONNX RuntimeSuy luận khuôn mặt và chữ
- PyTorchChinese-CLIP
- InsightFaceSCRFD + ArcFace
- RapidOCRNhận dạng chữ PP-OCR
- PostgreSQL 17Dữ liệu ứng dụng và hàng đợi
- pgvectorLưu trữ vector
- HNSWChỉ mục khoảng cách cosin
- ip2regionVị trí IP trong lịch sử đăng nhập (ngoại tuyến)
- NominatimTra ngược địa điểm (tắt được)
- React + TypeScriptỨng dụng web
- Vite + Tailwind CSSĐóng gói và giao diện
- MapLibre GLQuả địa cầu vệ tinh
- Docker ComposeImage amd64 / arm64
- mDNSTên miền nội bộ wanmory.local
Quyền riêng tư và bảo mật
Mặc định, ảnh và kết quả AI đều nằm trong mạng riêng của bạn. Nếu bạn cấu hình dịch vụ AI từ xa, ảnh xem trước sẽ được gửi tới máy chủ đó để xử lý.
Dịch vụ AI chỉ nhận ảnh xem trước 1440 pixel, không lưu tệp nào, không chạm vào cơ sở dữ liệu, tên người và ảnh gốc.
Dịch vụ AI tích hợp chỉ nằm trong mạng nội bộ của Docker, chỉ backend kết nối được; dịch vụ AI từ xa khi đã đặt token thì mọi yêu cầu đều phải kèm token Bearer.
Thành viên chỉ xóa được ảnh mình tải lên, quản trị viên quản lý tất cả; ảnh đã ẩn chỉ mình thấy; lịch sử đăng nhập hiển thị vị trí ước chừng theo IP.
Thư mục NAS có thể gắn chỉ đọc bằng :ro, ứng dụng không sửa được, cũng không xóa được tệp bên trong.
Tải mô hình xong thì nhận diện và tìm kiếm đều chạy cục bộ; các tính năng cần mạng như tra ngược địa điểm, ảnh vệ tinh đều tắt được.
Mô hình khuôn mặt InsightFace chỉ dùng cho mục đích phi thương mại; mô hình không nằm trong image, trước khi tải sẽ hiện giấy phép của từng mô hình ở trang cài đặt.