VectorGallery 是一个可本地部署的多模态图片向量检索系统,基于 cn_clip、FastAPI、Flask 和 Qdrant,支持以文搜图、以图搜图、图文融合检索,以及结合 CoverGroup.cfg 的尺寸匹配重排。
Note
README 主要面向开发者:如果你要运行、部署、调试或二次开发这个项目,建议先从 Docker Compose 流程开始。
- 功能特性
- 架构概览
- 快速开始:Docker Compose
- 本地 Python 运行
- Web 使用方式
- CLI 使用方式
- Python API 参考
- 配置项
- CoverGroup.cfg 与尺寸匹配
- 测试与代码检查
- 安全与运行产物
- 路线图
- 贡献
- 许可证
- 以文搜图:输入文本描述,检索语义相近的图片。
- 以图搜图:上传或传入参考图片,检索视觉相似图片。
- 图文融合搜图:文本和图片分别检索,再通过
rrf或weighted晚融合合并结果。 - 尺寸匹配重排:索引时读取
CoverGroup.cfg的 bbox 信息,搜索时可按目标宽高过滤并重排。 - Web + CLI 双入口:既可以通过浏览器使用,也可以通过命令行索引和搜索。
- 容器化部署:
docker-compose.yml编排 Qdrant、模型服务和 Flask Web 应用。
graph TD
A[Web UI 或 CLI] --> B[client 业务层]
B --> C[FastAPI cn_clip 模型服务]
B --> D[Qdrant 向量数据库]
C --> B
D --> B
运行链路:
Flask Web UI / CLI
-> client 业务层
-> FastAPI cn_clip 模型服务
-> Qdrant 向量数据库
主要目录:
| 路径 | 说明 |
|---|---|
client/app/ |
CLI 入口、Flask Web 应用和 Web 启动脚本 |
client/core/ |
ImageIndexer、SearchEngine 等索引/检索核心流程 |
client/adapters/ |
Qdrant 与模型服务 HTTP 客户端 |
client/processing/ |
图片扫描、格式校验、CoverGroup.cfg 解析 |
client/templates/ |
Flask Web 页面模板 |
server/ |
FastAPI + cn_clip 模型推理服务 |
docker-compose.yml |
Qdrant、模型服务、Web 应用一键编排 |
Compose 会启动三个服务:
| 服务 | 作用 | 对外端口 |
|---|---|---|
qdrant |
向量数据库,存储图片向量和 metadata payload | 6333, 6334 |
model-server |
FastAPI + cn_clip embedding 服务 |
8000 |
client-web |
Flask Web 搜索界面 | 5000 |
默认情况下,Compose 会把仓库内的 ./images 挂载到容器内 /data/images。
如果要使用本机其他图片目录,新建 .env:
VECTORGALLERY_IMAGE_DIR=<YOUR_IMAGE_DIRECTORY>Windows 示例:
VECTORGALLERY_IMAGE_DIR=E:/TESTLinux/macOS 示例:
VECTORGALLERY_IMAGE_DIR=/home/me/imagesdocker compose up -d --build首次启动 model-server 可能需要下载或缓存 cn_clip 模型,耗时会比较久。查看状态和日志:
docker compose ps
docker compose logs -fWeb 访问地址:
http://localhost:5000
docker compose exec client-web python -m client.app.main index --directory /data/images如果需要显式指定 CoverGroup.cfg,请先把该文件所在目录挂载到容器中,再传入容器内路径:
docker compose exec client-web python -m client.app.main index \
--directory /data/images \
--covergroup-cfg /data/CoverGroup.cfg# 停止容器,保留 Qdrant 数据、模型缓存和缩略图缓存
docker compose down
# 停止并删除持久化 volume;索引数据和模型缓存会被清理,慎用
docker compose down -v展开本地 Python 启动步骤
建议先创建虚拟环境,然后在仓库根目录执行:
pip install -r client/requirements.txt
pip install -r server/requirements.txt
pip install -r requirements-dev.txtdocker run -p 6333:6333 -p 6334:6334 qdrant/qdrant:v1.12.5请从 server/ 目录启动,避免 uvicorn server:app 解析到错误模块:
cd server
uvicorn server:app --host 0.0.0.0 --port 8000Windows 可使用:
cd server
run.bat模型服务健康检查:
http://127.0.0.1:8000/health
回到仓库根目录执行:
python -m client.app.run_web访问:
http://localhost:5000
-
启动服务并完成索引。
-
打开浏览器访问:
http://localhost:5000 -
在页面中选择搜索方式:
- 输入文本:以文搜图。
- 上传图片:以图搜图。
- 同时输入文本并上传图片:图文融合搜图。
-
如需要尺寸匹配,填写目标宽高和相关权重参数。
Web 端主要接口:
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/ |
Web 首页 |
POST |
/search |
搜索接口;支持 JSON 文本搜索和 form-data 图片/图文搜索 |
GET |
/thumbnail?picid=... |
返回缩略图或原图 |
GET |
/image/<path> |
返回本地图片文件 |
GET |
/health |
Web 应用健康检查 |
Warning
/image/<path> 会按路径返回本地存在且扩展名允许的图片文件。若要把 Flask 服务暴露到不可信网络,请先增加路径白名单或访问控制。
Important
python -m client.app.main 会先检查 Qdrant 和模型 API,再解析命令。因此即使只是查看帮助,也可能因为 127.0.0.1:6333 或 127.0.0.1:8000 不可用而失败。
常用命令:
# 查看系统信息
python -m client.app.main info
# 索引默认图片目录
python -m client.app.main index
# 推荐:显式指定图片目录
python -m client.app.main index --directory /path/to/images
# 显式指定 CoverGroup.cfg
python -m client.app.main index --directory /path/to/images --covergroup-cfg /path/to/CoverGroup.cfg
# 文本搜索
python -m client.app.main search-text "猫咪"
# 图片搜索
python -m client.app.main search-image /path/to/query/image.jpg
# 图文融合搜索
python -m client.app.main search-multimodal --image /path/to/query/image.jpg --query "红色跑车"
# 自定义返回数量和阈值
python -m client.app.main search-text "风景" --top-k 20 --threshold 0.4CLI 参数概览:
| 命令 | 关键参数 | 说明 |
|---|---|---|
index |
--directory, --covergroup-cfg |
扫描目录、生成图片向量并写入 Qdrant |
search-text |
query, --top-k, --threshold |
文本搜图 |
search-image |
image, --top-k, --threshold |
图片搜图 |
search-multimodal |
--image, --query, --fusion-method, --rrf-k |
图文融合搜图 |
info |
无 | 查看集合状态、向量维度、默认搜索配置 |
test |
无 | 运行项目自定义测试/诊断路径,不等同于完整 pytest 套件 |
from client.core.search_engine import SearchEngine
search_engine = SearchEngine()
# 文本搜索
results = search_engine.search_by_text("猫咪", top_k=10, score_threshold=0.4)
# 图片搜索
results = search_engine.search_by_image_path("/path/to/query.jpg", top_k=10)
# 图文融合搜索
results = search_engine.search_multimodal_late_fusion(
query_text="红色跑车",
image_path="/path/to/query.jpg",
top_k=20,
fusion_method="rrf",
)常用方法:
| 方法 | 说明 |
|---|---|
search_by_text(...) |
根据文本检索图片 |
search_by_image_path(...) |
根据图片路径检索图片 |
search_by_image_data(...) |
根据图片字节数据检索图片 |
search_multimodal_late_fusion(...) |
图文晚融合检索,支持 rrf 和 weighted |
advanced_search(...) |
根据传入参数自动选择文本、图片或图文搜索 |
get_database_stats() |
获取 Qdrant 集合与搜索配置摘要 |
from client.core.indexer import ImageIndexer
indexer = ImageIndexer()
success_count, error_count = indexer.index_images(
image_dir="/path/to/images",
covergroup_cfg_path="/path/to/CoverGroup.cfg",
)常用方法:
| 方法 | 说明 |
|---|---|
index_images(image_dir=None, batch_size=50, covergroup_cfg_path=None) |
批量索引图片目录 |
index_single_image(image_path) |
索引单张图片 |
reindex_all_images(image_dir=None, covergroup_cfg_path=None) |
清空集合后重新索引 |
get_indexing_progress() |
获取当前索引统计信息 |
主要运行配置来自 client/config.py,多数可通过环境变量覆盖。
| 配置键 | 默认值 | 说明 |
|---|---|---|
QDRANT_HOST |
127.0.0.1 |
Qdrant host |
QDRANT_PORT |
6333 |
Qdrant port |
COLLECTION_NAME |
image_search |
Qdrant collection 名称 |
VECTOR_SIZE |
512 |
cn_clip 向量维度 |
MULTIMODAL_SERVER_URL |
http://127.0.0.1:8000 |
模型服务地址 |
IMAGE_DIR |
E:\TEST |
默认图片目录 |
DEFAULT_TOP_K |
20 |
默认返回数量 |
SIMILARITY_THRESHOLD |
0.4 |
默认相似度阈值 |
SEARCH_FUSION_METHOD |
rrf |
默认图文融合策略 |
SEARCH_RRF_K |
60 |
RRF 参数 |
SEARCH_TEXT_WEIGHT |
0.5 |
文本分支权重 |
SEARCH_IMAGE_WEIGHT |
0.5 |
图片分支权重 |
SEARCH_CANDIDATE_MULTIPLIER |
5 |
候选池倍数 |
SEARCH_MIN_CANDIDATE_K |
100 |
最小候选数 |
Compose 中 client-web 会覆盖部分配置:
QDRANT_HOST=qdrant
QDRANT_PORT=6333
COLLECTION_NAME=image_search
MULTIMODAL_SERVER_URL=http://model-server:8000
IMAGE_DIR=/data/imagesCoverGroup.cfg 用于提供图片或 cover group 的 bbox 宽高信息。索引时,ImageIndexer 会解析它并把 bbox metadata 写入 Qdrant payload;搜索时,SearchEngine 可按 target_width / target_height 进行强过滤,并结合语义分和尺寸分重排。
索引入口的路径规则:
- 显式传入
--covergroup-cfg/covergroup_cfg_path时,使用该路径。 - 未显式传入时,使用
图片目录的父目录/CoverGroup.cfg。 - 只有底层路径解析没有显式 cfg 且没有图片目录时,才使用
Config.COVERGROUP_CFG_PATH。
如果解析失败,索引会记录警告并继续执行;对应图片的 bbox metadata 为空或为 0。
尺寸匹配配置:
| 配置键 | 默认值 | 说明 |
|---|---|---|
SIZE_TOLERANCE |
50.0 |
宽高强约束容差,单位为像素 |
SEARCH_SEMANTIC_WEIGHT |
0.7 |
语义分权重 |
SEARCH_SIZE_WEIGHT |
0.3 |
尺寸分权重 |
SIZE_SCORE_ALPHA |
2.0 |
面积差异衰减系数 |
SIZE_SCORE_BETA |
3.0 |
宽高比差异衰减系数 |
运行默认测试:
python -m pytest运行单个测试:
python -m pytest client/tests/test_size_filter.py
python -m pytest client/tests/test_size_filter.py::TestCoverGroupParser::test_parse_cfg_structure
python -m unittest client.tests.test_size_filter.TestCoverGroupParser.test_parse_cfg_structure兼容 unittest discovery:
python -m unittest discover -s client/tests -p "test*.py"显式 opt-in 的集成测试需要设置环境变量:
VECTORGALLERY_RUN_INDEX_INTEGRATION=1
VECTORGALLERY_INTEGRATION_PROJECT_DIR=<YOUR_PROJECT_DIR>
VECTORGALLERY_INDEX_IMAGE_DIR=<YOUR_IMAGE_DIR>代码检查和格式化:
python -m ruff check .
python -m ruff format .不应提交的运行时或工具缓存:
client/thumbnails/__pycache__/.pyc.pytest_cache/.ruff_cache/*.pt模型权重- Qdrant volume 数据
Caution
模型权重和向量数据库数据可能较大,不建议直接纳入普通 Git 管理。如果确实需要版本化模型资产,请先设计 Git LFS 或外部制品管理方案。
- 文本搜图
- 图片搜图
- 图文晚融合检索
- Docker Compose 编排
-
CoverGroup.cfg尺寸匹配 - 索引去重策略
- Web 文件访问路径白名单
- 更完整的模型资产管理方案
欢迎提交 issue 或 pull request。建议遵循以下约定:
-
功能改动前先确认影响范围,尤其是索引、搜索和 Web 文件访问逻辑。
-
提交前运行:
python -m pytest python -m ruff check . -
README 示例应与
client/config.py、docker-compose.yml和 CLI 参数保持一致。 -
不要把运行缓存、缩略图、模型权重或 Qdrant 数据提交到仓库。
本项目使用 MIT License。详见 LICENSE。