Hệ thống tìm kiếm và truy xuất video thông minh sử dụng CLIP embedding và Elasticsearch cho tìm kiếm transcript. Hỗ trợ nộp kết quả lên evaluation server.
- Tổng quan
- Kiến trúc hệ thống
- Yêu cầu hệ thống
- Cài đặt
- Cấu hình
- Sử dụng
- Cấu trúc thư mục
- API Endpoints
- Troubleshooting
Hệ thống cho phép:
- Tìm kiếm video bằng mô tả văn bản (text query)
- Tìm kiếm transcript trong nội dung video
- Auto-extract transcripts bằng OpenAI Whisper (99 ngôn ngữ)
- Xem preview video khi hover chuột
- Điều hướng frame-by-frame chính xác
- Nộp kết quả lên evaluation server với session ID
- Quản lý metadata video (FPS, duration, keyframes)
┌─────────────────┐
│ Web UI │ ← Flask + Vanilla JavaScript
│ (templates/) │
└────────┬────────┘
│
┌────────▼────────┐
│ Backend │
│ (Flask API) │ ← app.py, retrieval_system.py
└────────┬────────┘
│
┌────┴────┬─────────────┬──────────────┐
▼ ▼ ▼ ▼
┌───────┐ ┌────────┐ ┌──────────┐ ┌──────────────┐
│Milvus │ │Elastic │ │Video │ │Evaluation │
│Vector │ │Search │ │Storage │ │Server │
│ DB │ │(Trans) │ │(MP4) │ │(Submit API) │
└───────┘ └────────┘ └──────────┘ └──────────────┘
Công nghệ sử dụng:
- Frontend: HTML5, CSS3, Vanilla JavaScript (ES6 modules)
- Backend: Flask (Python 3.8+)
- Vector DB: Milvus (image-text embeddings)
- Text Search: Elasticsearch (transcript + OCR + captions)
- Search Strategy: Hybrid search (Milvus + ES), dual-query VN + EN, fusion ranking (max-score / RRF), rerank stage-2 (SigLIP2/VLM), score threshold + full ranked results
- Full Results Output: Trả về toàn bộ keyframes tương đồng (không giới hạn top-K), hỗ trợ pagination/scroll/cursor + lọc theo min_score để tránh quá tải
- Translation (VI→EN): NLLB-200 distilled (nhanh) hoặc SeamlessM4T v2 (đa năng)
- Video Processing: OpenCV, FFmpeg
- Embedding Model: OpenCLIP ViT-B/32 (baseline) → SigLIP2 g/NaFlex hoặc OpenCLIP ViT-H/14 hoặc jina-clip-v2
- Captioning (optional): VLM (Qwen2-VL / InternVL2 / LLaVA / BLIP) → index ES
- Transcript Extraction: OpenAI Whisper (ASR đa ngôn ngữ)
- OCR: PaddleOCR 3.x (PP-OCRv5 / PaddleOCR-VL)
Note: Cải tiến model và hệ thống nhưng chưa áp dụng nâng cấp.
- Python 3.8+
- Docker Desktop (Milvus, Elasticsearch containers)
- Git (để clone repository)
- 8GB RAM tối thiểu (khuyến nghị 16GB)
- 10GB dung lượng trống (cho models và data)
git clone <repository-url>
cd Retrieval_SystemWindows (cmd):
python -m venv .venv
.venv\Scripts\activate
python -m scripts.run_whisper_pipeline --language viLinux/macOS:
python3 -m venv .venv
source .venv/bin/activate
python -m backend.apppip install --upgrade pip setuptools wheel
pip install -r requirements.txtdocker compose up -dChờ ~2-5 phút để các containers khởi động hoàn tất. Kiểm tra status:
docker compose psTất cả services phải ở trạng thái "Up" hoặc "healthy".
python -m scripts.setup_environment --allLệnh này sẽ:
- Kiểm tra Python packages
- Kiểm tra Docker containers
- Tạo các thư mục cần thiết
- Download CLIP model weights
Các cấu hình quan trọng:
# Video & Data Paths
VIDEOS_DIR = "data/videos" # Thư mục chứa video MP4
KEYFRAMES_DIR = "data/keyframes" # Thư mục keyframes
TRANSCRIPTS_DIR = "data/transcripts" # Thư mục JSON transcripts
EMBEDDINGS_DIR = "data/embeddings" # Vector embeddings
# Database Connections
MILVUS_HOST = "localhost"
MILVUS_PORT = "19530"
ELASTICSEARCH_HOST = "localhost:9200"
# CLIP Model
CLIP_MODEL = "ViT-B/32"
CLIP_PRETRAINED = "openai"
# Evaluation Server
SESSION_ID = "CXTDKFpWUS8QDARbbM6n61yJJ7Yiu9eL" # Session ID của bạn
EVALUATION_ID = "76f9d8a8-e30d-4840-9865-a09ad24859a6"
EVALUATION_SERVER = "http://192.168.20.156:5601"- Đổi
SESSION_IDvàEVALUATION_IDthành giá trị của bạn - Kiểm tra
EVALUATION_SERVERcó đúng IP không
Cách đơn giản nhất:
python -m scripts.run_whisper_pipelineHoặc dùng batch file (Windows):
tools\run_whisper.batScript này sẽ:
- Extract transcripts từ video bằng Whisper
- Tự động detect ngôn ngữ (hoặc chỉ định cụ thể)
- Lưu transcripts với timestamps chính xác
- Index vào Elasticsearch để search
Tùy chọn nâng cao:
# Chọn model size (tiny/base/small/medium/large)
python -m scripts.run_whisper_pipeline --model small
# Chỉ định ngôn ngữ (tăng tốc độ và độ chính xác)
python -m scripts.run_whisper_pipeline --language en
# Test với 1 video
python -m scripts.extract_transcripts --single-video L01_V001Chi tiết: Xem WHISPER_GUIDE.md
Nếu đã có transcripts (CSV hoặc Whisper JSON):
python -m backend.ingest_dataQuá trình này sẽ:
- Extract keyframes từ video (mỗi X giây)
- Tính CLIP embeddings cho keyframes
- Index embeddings vào Milvus
- Index transcripts (CSV hoặc JSON) vào Elasticsearch
- Lưu metadata (FPS, duration, frame mapping)
Thời gian: ~5-10 phút cho 100 videos (tùy thuộc hardware)
Lưu ý: Backend tự động hỗ trợ cả hai format transcript:
- CSV (legacy):
Start,End,Text - JSON (Whisper):
video_id,language,segments[]
Muốn bổ sung transcript mới mà vẫn giữ dữ liệu cũ?
python -m backend.ingest_data --append-transcriptsChế độ này chỉ thêm hoặc cập nhật transcript mới (dựa trên _id của Elasticsearch) và không xóa index cũ.
python -m backend.appServer sẽ chạy tại: http://localhost:5000
- Mở trình duyệt: http://localhost:5000
- Click "Connect" → Kết nối đến evaluation server (hiển thị session ID)
- Nhập query: Mô tả video cần tìm (VD: "person walking on the street")
- Click "Search" → Xem kết quả
- Hover chuột lên card → Xem video preview
- Click card → Mở video player modal
- Dùng ◀ ▶ → Điều chỉnh frame chính xác
- Click "SubmitFrame" → Nộp lên server
Retrieval_System/
│
├── backend/ # Core Python backend
│ ├── app.py # Flask web server (main entry)
│ ├── retrieval_system.py # Search engine logic
│ ├── ingest_data.py # Data ingestion pipeline
│ ├── config.py # Configuration settings
│ └── submit.py # Submit API helper
│
├── utils/ # Utility modules
│ ├── elasticsearch_client.py
│ ├── text_encoder.py # CLIP text encoder
│ └── video_metadata.py # Video metadata helpers
│
├── scripts/ # Setup & processing scripts
│ ├── setup_environment.py # Environment verification
│ ├── extract_keyframes.py # Keyframe extraction
│ └── compute_embeddings.py # CLIP embedding computation
│
├── tools/ # Optional tools
│ ├── hls.py # HLS streaming converter (unused)
│ ├── open_clip_torch.py # CLIP import shim
│ └── run_ingest.bat # Windows batch script
│
├── templates/ # HTML templates
│ └── index.html # Main web UI
│
├── static/ # Frontend assets
│ ├── style.css # Global styles
│ └── js/
│ ├── main.js # Main app logic
│ ├── video-player.js # Video modal & controls
│ ├── results.js # Search results display
│ ├── api.js # API communication
│ └── elements.js # DOM element references
│
├── data/ # Data storage (gitignored)
│ ├── videos/ # MP4 video files
│ ├── keyframes/ # Extracted keyframes (webp)
│ │ └── maps/ # Frame→seconds mapping (CSV)
│ ├── transcripts/ # JSON transcript files
│ └── embeddings/ # CLIP embeddings (NPZ)
│
├── volumes/ # Docker persistent volumes
│ ├── milvus/
│ ├── es_data/
│ └── mongo_data/
│
├── docker-compose.yml # Docker services definition
├── requirements.txt # Python dependencies
└── README.md # This file
- Mục đích: Hiển thị giao diện web chính
- Response: HTML page
- Mục đích: Tìm kiếm video
- Request body:
{ "description": "person walking", "top_k": 100 } - Response:
[ { "video_id": "L01_V001", "keyframe_index": 150, "clip_score": 0.8542, "fps": 25.0, "start_seconds": 6.0 } ]
- Mục đích: Kết nối evaluation server, lấy session info
- Request body:
{} - Response:
{ "sessionId": "CXTDKFpWUS8QDARbbM6n61yJJ7Yiu9eL", "evaluationId": "76f9d8a8-e30d-4840-9865-a09ad24859a6" }
- Mục đích: Nộp kết quả lên evaluation server
- Request body:
{ "sessionId": "xxx", "evaluationId": "yyy", "videoId": "L01_V001", "timeMs": 6000 } - Response:
{ "status": "success", "remote_response": {...} }
- Mục đích: Stream video MP4
- Response: Video file (binary)
- Mục đích: Lấy ảnh keyframe
- Response: Image file (webp)
Nguyên nhân: Docker container chưa khởi động hoặc port bị chặn
Giải pháp:
# Kiểm tra container status
docker compose ps
# Restart containers
docker compose restart
# Xem logs
docker compose logs milvus-standaloneGiải pháp:
# Kiểm tra Elasticsearch health
curl http://localhost:9200/_cluster/health
# Restart nếu cần
docker compose restart elasticsearchNguyên nhân: Chưa có video trong data/videos/
Giải pháp:
- Copy video MP4 vào
data/videos/ - Chạy lại ingest:
python -m backend.ingest_data
Nguyên nhân: JavaScript cache hoặc event listener issue
Giải pháp:
- Hard refresh: Ctrl+Shift+R (Windows) hoặc Cmd+Shift+R (Mac)
- Mở Console (F12) → Kiểm tra errors
- Kiểm tra
video-player.jscó logupdateFrameInfo calledkhông
Nguyên nhân: Session ID không đúng hoặc đã expire
Giải pháp:
- Click "Connect" lại để refresh session
- Kiểm tra
backend/config.py→SESSION_IDcó đúng không - Kiểm tra evaluation server có online không:
curl http://192.168.20.156:5601/api/health
Giải pháp:
- Kiểm tra video file tồn tại:
data/videos/L01_V001.mp4 - Kiểm tra format: Phải là MP4 (H.264)
- Convert nếu cần:
ffmpeg -i input.mp4 -c:v libx264 -c:a aac output.mp4
# Trong backend/ingest_data.py
BULK_CHUNK_SIZE = 2000 # Tăng lên 5000 nếu RAM đủ# Trong scripts/extract_keyframes.py
KEYFRAME_INTERVAL = 1.0 # Tăng lên 2.0 để giảm số frame# Tăng memory cho Docker Desktop
# Settings → Resources → Memory: 8GB+Contributions are welcome! Vui lòng:
- Fork repository
- Tạo feature branch (
git checkout -b feature/AmazingFeature) - Commit changes (
git commit -m 'Add AmazingFeature') - Push to branch (
git push origin feature/AmazingFeature) - Mở Pull Request
Dự án này chỉ phục vụ mục đích học tập và nghiên cứu.
Cách tính thời gian từ keyframe index:
start_seconds = keyframe_index / fps
time_ms = round(start_seconds * 1000)Ví dụ:
- Video FPS: 25
- Keyframe index: 150
- → Start time: 150 / 25 = 6.0 seconds = 6000 ms
User clicks "Submit"
↓
JavaScript gets currentTime from <video>
↓
timeMs = Math.round(currentTime * 1000)
↓
POST /api/submit with {sessionId, evaluationId, videoId, timeMs}
↓
Flask backend forwards to evaluation server
↓
Response displayed to user
Nếu gặp vấn đề:
- Kiểm tra Troubleshooting section
- Xem logs:
docker compose logs -f - Mở Console (F12) → Tab Console để xem JavaScript errors
- Check backend logs trong terminal đang chạy
python -m backend.app
Happy Searching! 🎬🔍