This repository contains a working CA-Jaccard person re-identification setup and a Gradio demo app for interactive retrieval, re-ranking, and visualization.
All commands assume they are run from the repository root with the project environment active.
Create a Python 3.11+ environment, activate it, and install the required packages:
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txtFor faster package resolution and installation, you can use uv:
pip install uv
uv pip install -r requirements.txtThe demo app only requires Market1501:
python scripts/download_datasets.py market1501By default, archives are stored in data/_downloads and Market1501 is extracted to:
data/market1501/Market-1501-v15.09.15
Downloaded archives are removed after successful extraction. Use --keep-archives if you want to keep the zip file:
python scripts/download_datasets.py market1501 --keep-archivesMSMT17 is only needed for the reproduction experiments:
python scripts/download_datasets.py msmt17Download the three Market1501 checkpoints used by the demo app:
python scripts/download_pretrained_models.py demoThis writes:
pretrained_models/market_resnet50_model_120_rank1_945.pthpretrained_models/CC_market1501_81.0.tarpretrained_models/CC+CAJ_market1501_84.8.tar
The BoT checkpoint is downloaded as split Google Drive zip parts, combined, extracted, then the staged zip files are removed. Use --keep-archives if you want to keep the downloaded parts:
python scripts/download_pretrained_models.py demo --keep-archivesTo download only one demo weight:
python scripts/download_pretrained_models.py market-bot
python scripts/download_pretrained_models.py cc-market
python scripts/download_pretrained_models.py caj-marketThe ImageNet ResNet50 initialization is only needed for training and experiment reproduction:
python scripts/download_pretrained_models.py resnet50This writes:
pretrained_models/resnet50-11ad3fa6.pth
The Gradio demo provides a quick way to inspect person retrieval behavior on Market1501.
Key features:
- Query: upload a person image or let the app randomly select a query image from the dataset. The app returns the 10 most similar gallery images and can save the result list as JSON for later use.
- Re-rank: load a saved query result JSON from the Query page and apply CA-Jaccard re-ranking to reorder the candidates.
- Visualization: project image features into clusters so you can visually compare how different methods group people and inspect image metadata directly on the plot.
The app expects Market1501 at data/market1501/Market-1501-v15.09.15 and these checkpoints in pretrained_models/:
market_resnet50_model_120_rank1_945.pthCC_market1501_81.0.tarCC+CAJ_market1501_84.8.tar
To start it:
python app/main.pyThe web interface is available at http://localhost:7860 by default:
- Query:
http://localhost:7860 - Re-rank:
http://localhost:7860/rerank - Visualization:
http://localhost:7860/visual
The first query may take longer because gallery features are extracted and cached under .cache/gallery_features. Saved query result JSON files are stored under .cache/rank_lists and can be loaded from the Re-rank page.
The following sections are for reproducing the CA-Jaccard paper analyses:
- Table 3: CKRNNs/CLQE/CAJ ablations
- Figure 3: neighbor analysis over clustering epochs
- Figure 4: parameter analysis
This setup is only needed if you want to reproduce MSMT17 BoT re-ranking experiments. It is not required for the demo app.
CAJ uses data/msmt17/MSMT17_V1/{train,test}. The bundled BoT loader expects data/msmt17/MSMT17_V2/{mask_train_v2,mask_test_v2}. If your files are in the CAJ layout, create a BoT-compatible layout with symlinks:
python scripts/adapt_msmt17_for_bot.pyUse --copy only if symlinks are not acceptable:
python scripts/adapt_msmt17_for_bot.py --copyTo train BoT on MSMT17:
cd thirdparty/bot
python tools/train.py \
--config_file configs/softmax_triplet_with_center_msmt17.ymlUse configs/softmax_triplet_with_center_msmt17.yml for the CA-Jaccard paper's BoT setting. It is the all-tricks BoT setup with BNNeck, cross entropy, triplet loss, and center loss. The main hyperparameters are 256x128 images, Adam, learning rate 0.00035, batch size 64, 120 epochs, steps [40, 70], random erasing 0.5, label smoothing on, and center loss weight 0.0005.
The config assumes ImageNet ResNet50 weights at: pretrained_models/resnet50-11ad3fa6.pth. If that file is elsewhere, override it:
cd thirdparty/bot
python tools/train.py \
--config_file configs/softmax_triplet_with_center_msmt17.yml \
MODEL.PRETRAIN_PATH "../../pretrained_models/resnet50-11ad3fa6.pth"To enable faster training, you can set SOLVER.EVAL_PERIOD to 120 either in the config file or via command line. This evaluates only at the end of training, which is sufficient for Table 3's re-ranking ablation. The default SOLVER.EVAL_PERIOD of 40 evaluates every 40 epochs, which is useful for monitoring training progress and selecting a checkpoint.
The resulting checkpoint can be evaluated by CAJ test.py with --checkpoint-format bot. In this mode, test.py builds BoT's own ResNet50 BNNeck model from thirdparty/bot, loads the BoT checkpoint directly, normalizes test features by default, and then passes those features through CAJ's evaluation or re-ranking code. This avoids evaluating BoT weights through CAJ's different ResNet implementation.
Clustering ablation for Market1501:
python scripts/run_tab3_ablation.py \
--dataset market1501 \
--scene clusteringClustering ablation for MSMT17:
python scripts/run_tab3_ablation.py \
--dataset msmt17 \
--scene clusteringTo skip clustering training and evaluate existing CAJ checkpoints, pass a checkpoint path. The path may include {dataset} and {variant} placeholders:
python scripts/run_tab3_ablation.py \
--dataset market1501 \
--scene clustering \
--cluster-checkpoint "logs/experiments/tab3/clustering/{dataset}/{variant}/model_best.pth.tar"BoT re-ranking ablation needs a BoT checkpoint:
python scripts/run_tab3_ablation.py \
--dataset market1501 \
--scene reranking \
--bot-checkpoint pretrained_models/market_resnet50_model_120_rank1_945.pthFor MSMT17, use the BoT checkpoint you trained:
python scripts/run_tab3_ablation.py \
--dataset msmt17 \
--scene reranking \
--bot-checkpoint pretrained_models/msmt17_resnet50_model_120_rank1_750.pthThe runner forwards --checkpoint-format bot --bot-neck-feat after to test.py. This matches the BoT config's TEST.NECK_FEAT: 'after' and TEST.FEAT_NORM: 'yes'. To reproduce a different BoT test setting, use:
python scripts/run_tab3_ablation.py \
--dataset market1501 \
--scene reranking \
--bot-checkpoint pretrained_models/market_resnet50_model_120_rank1_945.pth \
--bot-neck-feat beforeUse --bot-no-feat-norm only if you intentionally want to disable BoT's default feature normalization.
Use --dry-run first to inspect commands. Clustering runs retrain models, while re-ranking only evaluates a pretrained checkpoint.
For clustering runs, --jaccard-memory {auto,dense,sparse} is forwarded to
train_caj.py. The default is auto; use sparse when the dense Jaccard
distance matrix is too large for available memory, or dense to force the
original dense path.
The runner writes:
- commands:
results/tab3_commands.csv - parsed metrics after real runs:
results/tab3_results.csv, includingmAP,Rank-1, andRank-5when present in the log
Market1501 is the default dataset:
python scripts/run_fig3_neighbors.pyMSMT17:
python scripts/run_fig3_neighbors.py --dataset msmt17Each variant writes a neighbor_analysis.csv under its log directory. The CSV contains:
avg_inter_camera_proportionavg_inter_camera_weightavg_same_id_accuracyavg_same_id_weight
The implementation excludes each sample itself from its neighbor statistics to avoid a trivial same-ID intra-camera self-match.
--jaccard-memory {auto,dense,sparse} controls the clustering Jaccard distance
memory strategy used by train_caj.py. The default is auto; use sparse for
lower memory usage on large datasets.
Clustering sweep:
python scripts/run_fig4_params.py \
--dataset market1501 \
--scene clustering \
--sweep allRe-ranking sweep with BoT:
python scripts/run_fig4_params.py \
--dataset market1501 \
--scene reranking \
--bot-checkpoint pretrained_models/market_resnet50_model_120_rank1_945.pthUseful narrower sweeps:
python scripts/run_fig4_params.py --sweep k1-intra --dry-run
python scripts/run_fig4_params.py --sweep k1-inter --dry-run
python scripts/run_fig4_params.py --sweep k2 --dry-runThe default values are:
k1-intra:1,5,10,15,20,25,40k1-inter:5,10,15,20,25,30,40k2-intra/k2-inter:1/5,2/4,3/3,4/2,5/1
For clustering sweeps, --jaccard-memory {auto,dense,sparse} is forwarded to
train_caj.py. The default is auto; use sparse if parameter sweeps run out
of memory while building the Jaccard distance matrix.
The runner writes:
- commands:
results/fig4_commands.csv - parsed metrics after real runs:
results/fig4_results.csv
Plot Figure 3-style curves from neighbor-analysis CSV files:
python scripts/plot_experiments.py \
--kind fig3 \
--input logs/experiments/fig3_neighbors/market1501/*/neighbor_analysis.csvPlot Figure 4-style curves from parsed sweep results:
python scripts/plot_experiments.py \
--kind fig4 \
--input results/fig4_results.csv \
--metric mAPRender a Table 3-style Markdown table:
python scripts/plot_experiments.py \
--kind tab3 \
--input results/tab3_results.csvFigures and tables are saved to results/figures by default.
This repository builds on the official CA-Jaccard implementation and includes code adapted from Cluster Contrast and BoT for person re-identification experiments.
If you use CA-Jaccard in your research, cite the original paper:
@inproceedings{yiyu2024caj,
title={CA-Jaccard: Camera-aware Jaccard Distance for Person Re-identification},
author={Chen, Yiyu and Fan, Zheyi and Chen, Zhaoru and Zhu, Yixuan},
booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
year={2024}
}