Add files using upload-large-folder tool
Browse files- .env.example +31 -0
- .gitattributes +1 -35
- .gitignore +69 -0
- CITATION.cff +69 -0
- PIPELINE_README.md +224 -0
- README.md +245 -0
- approach/__init__.py +0 -0
- approach/app_metadata.py +95 -0
- approach/config.py +156 -0
- approach/method_gemini.py +655 -0
- approach/providers.py +149 -0
- approach/reflection.py +227 -0
- approach/requirements.txt +79 -0
- approach/run_vlm.py +164 -0
- dataset/2detection.py +21 -0
- dataset/2polyseg.py +52 -0
- dataset/clean.py +55 -0
- dataset/create_test_set_folder.py +39 -0
- dataset/dataset_process.sh +32 -0
- dataset/fold_app.csv +1553 -0
- dataset/fold_cat.csv +1553 -0
- dataset/fold_genre.csv +1553 -0
- dataset/gen_cat_fold.py +58 -0
- dataset/get_union_test.py +52 -0
- dataset/get_val_gt.py +66 -0
- dataset/interactable_coco.py +19 -0
- dataset/list-union3.txt +1 -0
- dataset/list_union3.txt +1 -0
- dataset/split_coco.py +166 -0
- environment.reference.yml +35 -0
- requirements.txt +5 -0
- tests/test_ape_stage.py +176 -0
- tests/test_app_metadata_cache.py +124 -0
- tests/test_cli_entrypoints.py +147 -0
- tests/test_config_providers.py +131 -0
- tests/test_evaluate_temp_paths.py +109 -0
- tests/test_evaluation_config.py +26 -0
- tests/test_evaluation_tools.py +145 -0
- tests/test_merge_predictions.py +62 -0
- tests/test_paired_bootstrap.py +233 -0
- tests/test_pipeline_utils.py +123 -0
- tests/test_reflection.py +147 -0
- tests/test_release_hygiene.py +151 -0
- tests/test_release_scripts.py +153 -0
- tests/test_run_ape.py +105 -0
- tests/test_run_vlm.py +83 -0
- tests/test_semantic_matching_cache.py +121 -0
- tests/test_vlm_adapter.py +52 -0
- util/llm_gemini.py +14 -0
- util/llm_third_party.py +60 -0
.env.example
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Example variable names only. Do not put real credentials in this file.
|
| 2 |
+
# In this workspace, use openrouter-run for OpenRouter operations.
|
| 3 |
+
|
| 4 |
+
# Main/legacy provider paths
|
| 5 |
+
# OPENROUTER_API_KEY=<inject through a secret manager or process environment>
|
| 6 |
+
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
|
| 7 |
+
OPENROUTER_MODEL=openai/gpt-5.6-sol
|
| 8 |
+
ORIENT_MODEL_PROFILE=default
|
| 9 |
+
# OPENAI_API_KEY=
|
| 10 |
+
# GOOGLE_API_KEY=
|
| 11 |
+
# ANTHROPIC_API_KEY=
|
| 12 |
+
# ZHIPU_API_KEY=
|
| 13 |
+
ORIENTER_APP_METADATA_CACHE=
|
| 14 |
+
|
| 15 |
+
# Semantic evaluation cache. Keep offline/read-only for historical reproduction.
|
| 16 |
+
# Set ZHIPU_API_KEY only when intentionally expanding the cache for new labels.
|
| 17 |
+
ORIENTER_EMBEDDING_CACHE=
|
| 18 |
+
ORIENTER_EMBEDDING_OFFLINE=1
|
| 19 |
+
ORIENTER_EMBEDDING_READONLY=1
|
| 20 |
+
|
| 21 |
+
# Optional baseline/provider paths
|
| 22 |
+
# ARK_API_KEY=
|
| 23 |
+
# DASHSCOPE_API_KEY=
|
| 24 |
+
# DEEPSEEK_API_KEY=
|
| 25 |
+
# THEB_API_KEY=
|
| 26 |
+
# INTERNVL_API_KEY=
|
| 27 |
+
CLAUDE_BASELINE_MODEL=anthropic/claude-sonnet-4.5
|
| 28 |
+
GPT4O_BASELINE_MODEL=openai/gpt-4o-2024-08-06
|
| 29 |
+
GPT5_BASELINE_MODEL=openai/gpt-5.6-sol
|
| 30 |
+
GEMINI_BASELINE_MODEL=google/gemini-2.5-pro
|
| 31 |
+
D3_DATASET_ROOT=
|
.gitattributes
CHANGED
|
@@ -1,35 +1 @@
|
|
| 1 |
-
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
approach/ovod/APE/ape_d_model_final.pth filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Credentials and local configuration
|
| 2 |
+
.env
|
| 3 |
+
.env.*
|
| 4 |
+
!.env.example
|
| 5 |
+
AGENTS.md
|
| 6 |
+
*.pem
|
| 7 |
+
*.key
|
| 8 |
+
|
| 9 |
+
# Python/editor state
|
| 10 |
+
__pycache__/
|
| 11 |
+
*.py[cod]
|
| 12 |
+
.pytest_cache/
|
| 13 |
+
.mypy_cache/
|
| 14 |
+
.ipynb_checkpoints/
|
| 15 |
+
.vscode/
|
| 16 |
+
.idea/
|
| 17 |
+
.DS_Store
|
| 18 |
+
.nfs*
|
| 19 |
+
|
| 20 |
+
# Virtual environments and package/build output
|
| 21 |
+
.venv/
|
| 22 |
+
venv/
|
| 23 |
+
env/
|
| 24 |
+
build/
|
| 25 |
+
dist/
|
| 26 |
+
*.egg-info/
|
| 27 |
+
|
| 28 |
+
# Logs, caches, and scratch output
|
| 29 |
+
*.log
|
| 30 |
+
*.tmp
|
| 31 |
+
*.bak
|
| 32 |
+
.cache/
|
| 33 |
+
log/
|
| 34 |
+
logs/
|
| 35 |
+
fastimg/
|
| 36 |
+
wandb/
|
| 37 |
+
work_dirs/
|
| 38 |
+
**/output/
|
| 39 |
+
**/outputs/
|
| 40 |
+
**/runs/
|
| 41 |
+
|
| 42 |
+
# Model and archive payloads
|
| 43 |
+
*.pt
|
| 44 |
+
*.pth
|
| 45 |
+
!approach/ovod/APE/ape_d_model_final.pth
|
| 46 |
+
*.ckpt
|
| 47 |
+
*.onnx
|
| 48 |
+
*.bin
|
| 49 |
+
*.pkl
|
| 50 |
+
*.zip
|
| 51 |
+
*.7z
|
| 52 |
+
*.tar
|
| 53 |
+
*.tar.gz
|
| 54 |
+
|
| 55 |
+
# Data payloads (keep scripts and small split metadata in Git)
|
| 56 |
+
/data/
|
| 57 |
+
dataset/data/
|
| 58 |
+
|
| 59 |
+
# Generated model/API and evaluation artifacts
|
| 60 |
+
approach/vlm/*.jsonl
|
| 61 |
+
approach/llm/*.json
|
| 62 |
+
approach/ovod/*.json
|
| 63 |
+
evaluation/embedding_dict*.json
|
| 64 |
+
evaluation/results/
|
| 65 |
+
evaluation/eval_results/
|
| 66 |
+
evaluation/autotest_study/results*/
|
| 67 |
+
evaluation/eval_context/results/
|
| 68 |
+
evaluation/eval_latex/**/*.csv
|
| 69 |
+
evaluation/eval_latex/**/*.tex
|
CITATION.cff
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cff-version: 1.2.0
|
| 2 |
+
message: "If you use Orienter, please cite the accompanying paper."
|
| 3 |
+
title: "Orienter: Context-Sensitive GUI Grounding for Automated XR Testing"
|
| 4 |
+
type: software
|
| 5 |
+
authors:
|
| 6 |
+
- family-names: Li
|
| 7 |
+
given-names: Shuqing
|
| 8 |
+
orcid: "https://orcid.org/0000-0001-6323-1402"
|
| 9 |
+
- family-names: Li
|
| 10 |
+
given-names: Binchang
|
| 11 |
+
orcid: "https://orcid.org/0009-0008-5995-4040"
|
| 12 |
+
- family-names: Liu
|
| 13 |
+
given-names: Yepang
|
| 14 |
+
orcid: "https://orcid.org/0000-0001-8147-8126"
|
| 15 |
+
- family-names: Gao
|
| 16 |
+
given-names: Cuiyun
|
| 17 |
+
orcid: "https://orcid.org/0000-0003-4774-2434"
|
| 18 |
+
- family-names: Zhang
|
| 19 |
+
given-names: Jianping
|
| 20 |
+
orcid: "https://orcid.org/0009-0005-5978-8888"
|
| 21 |
+
- family-names: Cheung
|
| 22 |
+
given-names: Shing-Chi
|
| 23 |
+
orcid: "https://orcid.org/0000-0002-3508-7172"
|
| 24 |
+
- family-names: Lyu
|
| 25 |
+
given-names: Michael R.
|
| 26 |
+
orcid: "https://orcid.org/0000-0002-3666-5798"
|
| 27 |
+
abstract: >-
|
| 28 |
+
Orienter grounds context-sensitive, user-interactable GUI elements in
|
| 29 |
+
extended-reality screenshots for automated XR testing.
|
| 30 |
+
keywords:
|
| 31 |
+
- extended reality
|
| 32 |
+
- GUI grounding
|
| 33 |
+
- software testing
|
| 34 |
+
- vision-language models
|
| 35 |
+
preferred-citation:
|
| 36 |
+
type: article
|
| 37 |
+
title: "Look Before You Leap: Context-Sensitive GUI Grounding for Boosting Automated Extended Reality (XR) Testing"
|
| 38 |
+
authors:
|
| 39 |
+
- family-names: Li
|
| 40 |
+
given-names: Shuqing
|
| 41 |
+
orcid: "https://orcid.org/0000-0001-6323-1402"
|
| 42 |
+
- family-names: Li
|
| 43 |
+
given-names: Binchang
|
| 44 |
+
orcid: "https://orcid.org/0009-0008-5995-4040"
|
| 45 |
+
- family-names: Liu
|
| 46 |
+
given-names: Yepang
|
| 47 |
+
orcid: "https://orcid.org/0000-0001-8147-8126"
|
| 48 |
+
- family-names: Gao
|
| 49 |
+
given-names: Cuiyun
|
| 50 |
+
orcid: "https://orcid.org/0000-0003-4774-2434"
|
| 51 |
+
- family-names: Zhang
|
| 52 |
+
given-names: Jianping
|
| 53 |
+
orcid: "https://orcid.org/0009-0005-5978-8888"
|
| 54 |
+
- family-names: Cheung
|
| 55 |
+
given-names: Shing-Chi
|
| 56 |
+
orcid: "https://orcid.org/0000-0002-3508-7172"
|
| 57 |
+
- family-names: Lyu
|
| 58 |
+
given-names: Michael R.
|
| 59 |
+
orcid: "https://orcid.org/0000-0002-3666-5798"
|
| 60 |
+
journal: "Proceedings of the ACM on Software Engineering"
|
| 61 |
+
volume: 3
|
| 62 |
+
issue: FSE
|
| 63 |
+
number: FSE127
|
| 64 |
+
start: 2858
|
| 65 |
+
end: 2881
|
| 66 |
+
month: 6
|
| 67 |
+
year: 2026
|
| 68 |
+
doi: "10.1145/3808134"
|
| 69 |
+
url: "https://doi.org/10.1145/3808134"
|
PIPELINE_README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Orienter pipeline guide
|
| 2 |
+
|
| 3 |
+
This guide maps the public commands to the method described in the paper. It distinguishes the configurable release path from historical experiment scripts and precomputed artifacts.
|
| 4 |
+
|
| 5 |
+
## Data contracts
|
| 6 |
+
|
| 7 |
+
The pipeline uses three small JSON formats.
|
| 8 |
+
|
| 9 |
+
Question manifest (`questions.jsonl`):
|
| 10 |
+
|
| 11 |
+
```json
|
| 12 |
+
{"question_id": 0, "image": "123_4.jpg", "image_id": 123004, "text": "Identify interactable elements."}
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
Candidate output (`candidates.jsonl`):
|
| 16 |
+
|
| 17 |
+
```json
|
| 18 |
+
{"question_id": 0, "text": {"objects": {"red button": "round red button with white text"}}, "model_id": "openai/gpt-5.6-sol"}
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
Detection output (`predictions.json`):
|
| 22 |
+
|
| 23 |
+
```json
|
| 24 |
+
[{"image_id": 123004, "category_id": "red button", "category_name": "red button", "bbox": [10, 20, 30, 40], "score": 0.91}]
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
Bounding boxes use COCO `x, y, width, height`. `image_id` is derived from `<app_id>_<frame>.ext` by zero-padding a frame in `[0, 999]` to three digits. Filenames must contain exactly one underscore between numeric app ID and numeric frame ID.
|
| 28 |
+
|
| 29 |
+
Keep all mounted inputs and generated artifacts outside the repository, then create the question manifest:
|
| 30 |
+
|
| 31 |
+
```bash
|
| 32 |
+
export ORIENTER_DATA_DIR=/absolute/path/to/orienter_data
|
| 33 |
+
export ORIENTER_RUN_DIR=/absolute/path/to/orienter_run
|
| 34 |
+
mkdir -p "$ORIENTER_RUN_DIR"
|
| 35 |
+
|
| 36 |
+
python -B scripts/generate_questions.py \
|
| 37 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 38 |
+
--output "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 39 |
+
--prompt "Identify interactable elements." \
|
| 40 |
+
--metadata-template "$ORIENTER_RUN_DIR/app_metadata.json"
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Screenshot filenames must match `<numeric_app_id>_<numeric_frame>.<supported_ext>` and use a supported image extension. The manifest is sorted by derived `image_id` so repeated generation is deterministic. Fill the generated metadata template with a non-empty app name and description for every app ID before Stage 1.
|
| 44 |
+
|
| 45 |
+
## Stage 0: external assets
|
| 46 |
+
|
| 47 |
+
Before a live run, provide:
|
| 48 |
+
|
| 49 |
+
- XR screenshots and a matching question manifest;
|
| 50 |
+
- application metadata reachable from Steam, or a pinned JSON/JSONL metadata cache;
|
| 51 |
+
- the APE-L\(_D\) repository environment (the checkpoint itself is bundled);
|
| 52 |
+
- an OpenRouter credential for the selected LMM profile;
|
| 53 |
+
- the bundled ground-truth split matching the experiment, plus a reviewed semantic-embedding cache. Credentials are needed only when intentionally adding embeddings for previously unseen labels.
|
| 54 |
+
|
| 55 |
+
The repository includes the main checkpoint and selected evaluation ground truth. It intentionally excludes the screenshot payload, private context data, generated predictions, experiment results, and the semantic embedding cache. Exact requirements are listed in `docs/ASSETS.md`.
|
| 56 |
+
|
| 57 |
+
## Stage 1: context-sensitive candidate mining
|
| 58 |
+
|
| 59 |
+
Run:
|
| 60 |
+
|
| 61 |
+
```bash
|
| 62 |
+
openrouter-run python -B -m approach.run_vlm \
|
| 63 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 64 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 65 |
+
--output "$ORIENTER_RUN_DIR/candidates.jsonl" \
|
| 66 |
+
--profile default \
|
| 67 |
+
--app-metadata-cache "$ORIENTER_RUN_DIR/app_metadata.json"
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
`approach/run_vlm.py` selects the configured model profile, loads each screenshot, combines screenshot and application context with the research prompt, and atomically checkpoints JSONL responses. It accepts:
|
| 71 |
+
|
| 72 |
+
- `--start-index/--end-index` for a contiguous range;
|
| 73 |
+
- `--shard-index/--num-shards` for deterministic modulo sharding;
|
| 74 |
+
- `--resume` for completed `question_id` values;
|
| 75 |
+
- `--app-metadata-cache` for a pinned JSON/JSONL app metadata cache;
|
| 76 |
+
- `--ablation` for the historical no-interactability prompt.
|
| 77 |
+
|
| 78 |
+
Range and shard modes cannot be combined. Every selected output path receives a range/shard suffix.
|
| 79 |
+
|
| 80 |
+
The default profile uses OpenRouter. The optional paper profiles preserve the evaluated identifiers for GPT-4o-2024-08-06, Claude 3.5 Sonnet, and Gemini 1.5 Pro. Because provider catalogs can retire historical routes, archive the resolved provider/model metadata with every reproduction; if a paper route is unavailable, supply a custom YAML/JSON profile or direct vendor adapter and document the substitution.
|
| 81 |
+
|
| 82 |
+
For reproducible runs, provide `--app-metadata-cache` or set `ORIENTER_APP_METADATA_CACHE`. The cache may be a JSON object keyed by app ID or JSONL records with `app_id`, `app_name`/`name`/`title`, and `app_description`/`description`. When this option is set, duplicate or missing app IDs fail before the provider call and the runner does not fall back to live Steam pages.
|
| 83 |
+
|
| 84 |
+
## Stage 2: APE-L_D visual grounding
|
| 85 |
+
|
| 86 |
+
Run:
|
| 87 |
+
|
| 88 |
+
```bash
|
| 89 |
+
python -B -m approach.run_ape \
|
| 90 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 91 |
+
--candidates "$ORIENTER_RUN_DIR/candidates.jsonl" \
|
| 92 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 93 |
+
--output "$ORIENTER_RUN_DIR/predictions.json" \
|
| 94 |
+
--visualization-dir "$ORIENTER_RUN_DIR/ape_visualizations" \
|
| 95 |
+
--ape-root approach/ovod/APE \
|
| 96 |
+
--ape-config configs/LVISCOCOCOCOSTUFF_O365_OID_VGR_SA1B_REFCOCO_GQA_PhraseCut_Flickr30k/ape_deta/ape_deta_vitl_eva02_clip_vlf_lsj1024_cp_16x4_1080k.py \
|
| 97 |
+
--ape-checkpoint ape_d_model_final.pth \
|
| 98 |
+
--threshold 0.15
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
The runner:
|
| 102 |
+
|
| 103 |
+
1. accepts candidate `text` as a JSON object or JSON string;
|
| 104 |
+
2. converts object descriptions to APE referring expressions;
|
| 105 |
+
3. invokes APE once per selected screenshot;
|
| 106 |
+
4. enriches every result with `image_id` and `category_id`;
|
| 107 |
+
5. writes one atomic JSON document plus a progress sidecar;
|
| 108 |
+
6. records per-image failures separately without corrupting successful predictions.
|
| 109 |
+
|
| 110 |
+
This fixes the historical failure mode where `all_ape_res` was cleared inside the loop, multiple JSON documents were dumped into one file, and the raw `ape_item` was appended instead of the enriched item.
|
| 111 |
+
|
| 112 |
+
## PII.5–PII.7 reflection
|
| 113 |
+
|
| 114 |
+
Enable reflection with:
|
| 115 |
+
|
| 116 |
+
```bash
|
| 117 |
+
openrouter-run python -B -m approach.run_ape ... \
|
| 118 |
+
--enable-reflection \
|
| 119 |
+
--reflection-profile default \
|
| 120 |
+
--max-reflection-iterations 10
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
The implementation follows the paper:
|
| 124 |
+
|
| 125 |
+
- **PII.5, mirroring-based regional verification:** crop each detected element inside its bbox and send the original scene plus crop as two separate images. The crop keeps its original pixel dimensions and orientation; it is not enlarged, resized, or flipped.
|
| 126 |
+
- **PII.6, unsuccessful-detection reflection:** send the full scene with current boxes so the advisor can identify missed elements and incorrect regions.
|
| 127 |
+
- **PII.7, advisor feedback:** require structured `verified`, `needs_refinement`, and `feedback` fields. False positives listed in `needs_refinement` are removed; verified detections are retained; feedback descriptions are grounded again by APE.
|
| 128 |
+
|
| 129 |
+
The loop stops when no refinement or feedback remains, or after at most 10 advisor rounds by default. Reflection is opt-in because it incurs additional API cost. If the advisor call fails in the configurable runner, the original APE detections are preserved and the reflection error is recorded.
|
| 130 |
+
|
| 131 |
+
## Parallel shards and validated merge
|
| 132 |
+
|
| 133 |
+
Example four-way detection:
|
| 134 |
+
|
| 135 |
+
```bash
|
| 136 |
+
for shard in 0 1 2 3; do
|
| 137 |
+
python -B -m approach.run_ape ... --num-shards 4 --shard-index "$shard"
|
| 138 |
+
done
|
| 139 |
+
```
|
| 140 |
+
|
| 141 |
+
Each output is named `.shardNN-of-NN.json`; each progress sidecar also includes screenshots with zero detections. Merge with:
|
| 142 |
+
|
| 143 |
+
```bash
|
| 144 |
+
python -B -m evaluation.tools.merge_predictions \
|
| 145 |
+
--inputs "$ORIENTER_RUN_DIR/predictions.shard00-of-04.json" \
|
| 146 |
+
"$ORIENTER_RUN_DIR/predictions.shard01-of-04.json" \
|
| 147 |
+
"$ORIENTER_RUN_DIR/predictions.shard02-of-04.json" \
|
| 148 |
+
"$ORIENTER_RUN_DIR/predictions.shard03-of-04.json" \
|
| 149 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 150 |
+
--output "$ORIENTER_RUN_DIR/predictions.merged.json"
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
The merge fails closed on overlapping completed images, duplicate detections, missing progress files, and missing expected images. `--allow-incomplete` exists for deliberate partial diagnostics.
|
| 154 |
+
|
| 155 |
+
## Stage 3: task conversion and evaluation
|
| 156 |
+
|
| 157 |
+
Convert the merged list explicitly:
|
| 158 |
+
|
| 159 |
+
```bash
|
| 160 |
+
python -B -m evaluation.tools.to_pred \
|
| 161 |
+
--task all \
|
| 162 |
+
--input "$ORIENTER_RUN_DIR/predictions.merged.json" \
|
| 163 |
+
--output "$ORIENTER_RUN_DIR/predictions.json"
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
This produces separate `semantics`, `interactable`, and `interaction` files. The task can no longer be changed accidentally by two consecutive assignments in source code.
|
| 167 |
+
|
| 168 |
+
The remaining evaluation sequence is:
|
| 169 |
+
|
| 170 |
+
1. `evaluation/filter_box.py` applies NMS and geometric filtering;
|
| 171 |
+
2. `evaluation/filter_test_prediction.py` selects the requested split;
|
| 172 |
+
3. `evaluation/evaluate_coco.py` computes modified COCO metrics;
|
| 173 |
+
4. `evaluation/context_eval.py` performs context-sensitive semantic matching;
|
| 174 |
+
5. `evaluation/autotest_study/` performs the RQ3 interaction simulation.
|
| 175 |
+
|
| 176 |
+
The historical shell scripts retain paper experiment names, but their data stems depend on external prediction/GT assets. Prefer explicit Python tool arguments for new runs.
|
| 177 |
+
|
| 178 |
+
### Offline semantic evaluation
|
| 179 |
+
|
| 180 |
+
Semantic evaluation can run without an API call by mounting the reviewed historical cache described in `evaluation/cache_manifest.json`:
|
| 181 |
+
|
| 182 |
+
```bash
|
| 183 |
+
export ORIENTER_EMBEDDING_CACHE=/absolute/path/to/embedding_dict.json
|
| 184 |
+
export ORIENTER_EMBEDDING_OFFLINE=1
|
| 185 |
+
export ORIENTER_EMBEDDING_READONLY=1
|
| 186 |
+
unset ZHIPU_API_KEY
|
| 187 |
+
|
| 188 |
+
python -B evaluation/evaluate_coco.py \
|
| 189 |
+
-gt /absolute/path/to/semantics_test.json \
|
| 190 |
+
-dt /absolute/path/to/predictions.json \
|
| 191 |
+
-d s -s -l "$ORIENTER_RUN_DIR/semantic_eval.csv"
|
| 192 |
+
```
|
| 193 |
+
|
| 194 |
+
Offline mode fails before any provider call when a category is absent from the cache. Keep the reviewed cache read-only. If a genuinely new model introduces an uncached label, expand a separate working copy deliberately with `ZHIPU_API_KEY`, review it, record a new checksum, and then freeze that new version.
|
| 195 |
+
|
| 196 |
+
Verify the mounted historical cache against the frozen manifest before evaluation:
|
| 197 |
+
|
| 198 |
+
```bash
|
| 199 |
+
python -B scripts/verify_assets.py \
|
| 200 |
+
--repo-root . \
|
| 201 |
+
--skip-checkpoint \
|
| 202 |
+
--embedding-cache /absolute/path/to/embedding_dict.json
|
| 203 |
+
```
|
| 204 |
+
|
| 205 |
+
The legacy `make_sample_gt.py` path keeps only image IDs present in a method's prediction file. It is retained for historical protocol compatibility, but evaluates different methods on different GT subsets. New comparisons must score every method against the same complete GT and report prediction coverage. Historical predictions, generated CSVs, and validation summaries are not bundled.
|
| 206 |
+
|
| 207 |
+
## Legacy experiment provenance
|
| 208 |
+
|
| 209 |
+
`approach/method*.py` contains commented branches for earlier datasets, ablations, machines, and model variants. Those comments were retained because a disabled block may represent another pipeline stage rather than dead code.
|
| 210 |
+
|
| 211 |
+
For release use:
|
| 212 |
+
|
| 213 |
+
- `run_vlm.py` replaces source-comment switching for screenshot-context generation;
|
| 214 |
+
- `run_ape.py` replaces source-comment switching for detector paths, ranges, shards, and reflection;
|
| 215 |
+
- explicit model profiles replace provider/model edits;
|
| 216 |
+
- the merge and conversion tools replace hand-edited evaluation state.
|
| 217 |
+
|
| 218 |
+
The legacy entrypoints remain available for provenance and now contain the APE accumulation fix and CLI range/shard controls, but they are not the recommended fresh-run interface.
|
| 219 |
+
|
| 220 |
+
## Verification boundary
|
| 221 |
+
|
| 222 |
+
Offline tests cover provider payloads, model aliases, PII.5 crop fidelity, false-positive removal, loop termination, APE accumulation/resume, shard isolation/merge, evaluation conversion, fail-closed semantic-cache access, and process-isolated evaluator temporary files.
|
| 223 |
+
|
| 224 |
+
The bundled tests verify evaluation mechanics without retaining prior prediction or metric artifacts. Paper-scale validation still requires fresh model inference, fixed-GT comparisons, and separately archived provenance. Selected evaluation ground truth is bundled, but the screenshot payload and authoritative public asset manifest are external.
|
README.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
tags:
|
| 3 |
+
- computer-vision
|
| 4 |
+
- object-detection
|
| 5 |
+
- multimodal
|
| 6 |
+
- extended-reality
|
| 7 |
+
- graphical-user-interface
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Orienter
|
| 11 |
+
|
| 12 |
+
Official code release for **“Look Before You Leap: Context-Sensitive GUI Grounding for Boosting Automated Extended Reality (XR) Testing.”**
|
| 13 |
+
|
| 14 |
+
[Paper](https://doi.org/10.1145/3808134) · [Project page](https://sites.google.com/view/gui-orienter) · [Pipeline guide](PIPELINE_README.md) · [Environment](docs/ENVIRONMENT.md) · [Required assets](docs/ASSETS.md) · [Model manifest](docs/MODEL_MANIFEST.md) · [Release checklist](docs/RELEASE_CHECKLIST.md)
|
| 15 |
+
|
| 16 |
+
Orienter detects context-sensitive, user-interactable GUI elements in XR screenshots. The public pipeline has three explicit stages:
|
| 17 |
+
|
| 18 |
+
1. an LMM combines application context and the current screenshot to mine interactable element descriptions;
|
| 19 |
+
2. APE-L\(_D\) grounds those descriptions, with the optional PII.5–PII.7 reflection loop described in the paper;
|
| 20 |
+
3. the evaluation tools convert, merge, filter, and score predictions.
|
| 21 |
+
|
| 22 |
+
The repository contains source code, selected evaluation ground truth, and the APE-L\(_D\) checkpoint needed by the main detector. Dataset image payloads, private data, API credentials, experiment logs, generated predictions, and previous experimental results are deliberately excluded.
|
| 23 |
+
|
| 24 |
+
## Release status
|
| 25 |
+
|
| 26 |
+
The offline control path and utilities are unit-tested. Maintainer validation has exercised one-image live paths for the default OpenRouter VLM, APE-L\(_D\), and the PII.5–PII.7 advisor loop without retaining generated artifacts in this public copy. Historical experiment outputs and validation result tables are not bundled.
|
| 27 |
+
|
| 28 |
+
A fresh-clone paper-scale inference reproduction still requires the external assets in [docs/ASSETS.md](docs/ASSETS.md), especially the dataset image payload, provider access, and a validated CUDA/APE environment.
|
| 29 |
+
|
| 30 |
+
The paper is published in PACMSE, Volume 3, Issue FSE, Article FSE127 ([DOI 10.1145/3808134](https://doi.org/10.1145/3808134)). Do not publish this directory until the repository license, third-party revision audit, and final public model/data links are complete. Machine-readable citation metadata is available in [`CITATION.cff`](CITATION.cff).
|
| 31 |
+
|
| 32 |
+
## Repository layout
|
| 33 |
+
|
| 34 |
+
| Path | Purpose |
|
| 35 |
+
| --- | --- |
|
| 36 |
+
| `approach/run_vlm.py` | configurable screenshot-context and candidate generation |
|
| 37 |
+
| `approach/run_ape.py` | configurable APE detection and optional reflection |
|
| 38 |
+
| `approach/reflection.py` | paper-aligned PII.5, PII.6, and PII.7 loop |
|
| 39 |
+
| `approach/config.py` | OpenRouter and paper model profiles |
|
| 40 |
+
| `approach/method*.py` | legacy experiment entrypoints retained for provenance |
|
| 41 |
+
| `approach/ovod/` | vendored APE, Grounding DINO, and related detector code |
|
| 42 |
+
| `evaluation/tools/` | prediction conversion and validated shard merge |
|
| 43 |
+
| `evaluation/` | detection, semantic, context, and interaction evaluation |
|
| 44 |
+
| `dataset/` | dataset conversion and split-generation scripts |
|
| 45 |
+
| `tests/` | offline regression tests for release-critical logic |
|
| 46 |
+
|
| 47 |
+
## Installation
|
| 48 |
+
|
| 49 |
+
The supported public environment targets Python 3.10 or later so it can use a currently supported Pillow release with the latest security fixes:
|
| 50 |
+
|
| 51 |
+
```bash
|
| 52 |
+
python -B -m venv .venv
|
| 53 |
+
source .venv/bin/activate
|
| 54 |
+
python -B -m pip install --upgrade pip
|
| 55 |
+
python -B -m pip install -r requirements.txt
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
`requirements.txt` covers the release utilities and provider adapters. APE-L\(_D\) has a separate CUDA/PyTorch stack under `approach/ovod/APE`; the checkpoint is bundled at `approach/ovod/APE/ape_d_model_final.pth` and recorded in [docs/MODEL_MANIFEST.md](docs/MODEL_MANIFEST.md). The public tree intentionally excludes compiled APE extensions, build directories, and egg-info metadata, so build/install the APE extension inside the target CUDA environment before running Stage 2. See [docs/ENVIRONMENT.md](docs/ENVIRONMENT.md) and `environment.reference.yml` for the tested historical environment, a public compatibility starting point, build commands, and verification boundary. The reference YAML is not a cross-platform lockfile or a security attestation. The historical combined dependency snapshot remains at `approach/requirements.txt`, but it is not a clean lockfile.
|
| 59 |
+
|
| 60 |
+
## Model configuration
|
| 61 |
+
|
| 62 |
+
The default provider is OpenRouter. In this project workspace, use `openrouter-run` so the key is injected by the local credential wrapper and never written to source files, logs, or shell history:
|
| 63 |
+
|
| 64 |
+
```bash
|
| 65 |
+
openrouter-run python -B -m approach.run_vlm --help
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
For a public fresh clone outside this workspace, inject `OPENROUTER_API_KEY` through the runner's secret manager or process environment. Do not commit `.env`, shell history, logs, or generated manifests containing credential values.
|
| 69 |
+
|
| 70 |
+
The default profile uses `openai/gpt-5.6-sol`, the current flagship model configured for this release. Override it without editing code:
|
| 71 |
+
|
| 72 |
+
```bash
|
| 73 |
+
export OPENROUTER_MODEL=openai/gpt-5.6-sol
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
Paper-oriented profiles remain available through OpenRouter:
|
| 77 |
+
|
| 78 |
+
| Profile | Model |
|
| 79 |
+
| --- | --- |
|
| 80 |
+
| `paper_openai` / `gpt4v` | `openai/gpt-4o-2024-08-06` |
|
| 81 |
+
| `paper_claude` / `claude35sonnet` | `anthropic/claude-3.5-sonnet` |
|
| 82 |
+
| `paper_gemini` / `gemini15pro` | `google/gemini-pro-1.5` |
|
| 83 |
+
|
| 84 |
+
Profiles are defined in `approach/config.py`. Library callers can override the catalog with a YAML or JSON profile file; CLI users select a named profile and may override the default model through `OPENROUTER_MODEL`. The paper profiles preserve the evaluated identifiers for GPT-4o-2024-08-06, Claude 3.5 Sonnet, and Gemini 1.5 Pro. Provider catalogs may retire historical routes; if one is unavailable, use a custom profile or direct vendor access and record the substitution instead of silently changing the paper profile. The client records only the credential variable name in run manifests; it never writes the credential value.
|
| 85 |
+
|
| 86 |
+
## Quick start
|
| 87 |
+
|
| 88 |
+
The commands below show the complete public interface. They require the external files listed in [docs/ASSETS.md](docs/ASSETS.md). Keep screenshots, metadata, prompts, predictions, visualizations, and evaluation files outside the repository:
|
| 89 |
+
|
| 90 |
+
```bash
|
| 91 |
+
export ORIENTER_DATA_DIR=/absolute/path/to/orienter_data
|
| 92 |
+
export ORIENTER_RUN_DIR=/absolute/path/to/orienter_run
|
| 93 |
+
mkdir -p "$ORIENTER_RUN_DIR"
|
| 94 |
+
```
|
| 95 |
+
|
| 96 |
+
Build a question manifest from mounted screenshots:
|
| 97 |
+
|
| 98 |
+
```bash
|
| 99 |
+
python -B scripts/generate_questions.py \
|
| 100 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 101 |
+
--output "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 102 |
+
--prompt "Identify interactable elements." \
|
| 103 |
+
--metadata-template "$ORIENTER_RUN_DIR/app_metadata.json"
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
Screenshot filenames must match `<numeric_app_id>_<numeric_frame>.<supported_ext>`, for example `123_4.jpg`; additional underscores are not supported. The frame must be in `[0, 999]` and is zero-padded to three digits when deriving `image_id`. Fill every generated `app_name` and `app_description` field in `$ORIENTER_RUN_DIR/app_metadata.json` before Stage 1; blank or missing records fail closed.
|
| 107 |
+
|
| 108 |
+
Validate the mounted inputs and the metadata coverage:
|
| 109 |
+
|
| 110 |
+
```bash
|
| 111 |
+
python -B scripts/verify_assets.py \
|
| 112 |
+
--repo-root . \
|
| 113 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 114 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 115 |
+
--app-metadata-cache "$ORIENTER_RUN_DIR/app_metadata.json"
|
| 116 |
+
```
|
| 117 |
+
|
| 118 |
+
Generate candidate descriptions:
|
| 119 |
+
|
| 120 |
+
```bash
|
| 121 |
+
openrouter-run python -B -m approach.run_vlm \
|
| 122 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 123 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 124 |
+
--output "$ORIENTER_RUN_DIR/candidates.jsonl" \
|
| 125 |
+
--profile default \
|
| 126 |
+
--app-metadata-cache "$ORIENTER_RUN_DIR/app_metadata.json"
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
`--app-metadata-cache` accepts JSON or JSONL records keyed by `app_id` and avoids live Steam page drift. When supplied, missing app IDs fail closed. Omit it only for exploratory runs where live Steam lookup is acceptable.
|
| 130 |
+
|
| 131 |
+
### Data privacy
|
| 132 |
+
|
| 133 |
+
Stage 1 sends each source screenshot to the selected OpenRouter/provider model. When reflection is enabled, PII.5 additionally sends the bounding-box crop and PII.6 sends the annotated full scene. Do not run private, personally identifying, confidential, or otherwise unauthorized screenshots through a remote provider. Review the selected provider's retention and data-processing terms, obtain the necessary permission, and use an approved local/provider deployment when screenshots cannot leave your environment. Generated candidates, predictions, traces, and error files may also reveal image labels or local filenames; keep them outside the release repository.
|
| 134 |
+
|
| 135 |
+
Ground candidates with APE-L\(_D\):
|
| 136 |
+
|
| 137 |
+
```bash
|
| 138 |
+
python -B -m approach.run_ape \
|
| 139 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 140 |
+
--candidates "$ORIENTER_RUN_DIR/candidates.jsonl" \
|
| 141 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 142 |
+
--output "$ORIENTER_RUN_DIR/predictions.json" \
|
| 143 |
+
--visualization-dir "$ORIENTER_RUN_DIR/ape_visualizations" \
|
| 144 |
+
--ape-root approach/ovod/APE \
|
| 145 |
+
--ape-checkpoint ape_d_model_final.pth
|
| 146 |
+
```
|
| 147 |
+
|
| 148 |
+
The released checkpoint is SHA-256 verified before loading. A different checkpoint is rejected unless `--trust-custom-checkpoint` is supplied explicitly after independently verifying its source; PyTorch checkpoints are executable pickle-based artifacts and must not be loaded from an untrusted source.
|
| 149 |
+
|
| 150 |
+
Enable the paper reflection loop explicitly because it incurs additional LMM calls:
|
| 151 |
+
|
| 152 |
+
```bash
|
| 153 |
+
openrouter-run python -B -m approach.run_ape \
|
| 154 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 155 |
+
--candidates "$ORIENTER_RUN_DIR/candidates.jsonl" \
|
| 156 |
+
--images-dir "$ORIENTER_DATA_DIR/images" \
|
| 157 |
+
--output "$ORIENTER_RUN_DIR/predictions.json" \
|
| 158 |
+
--visualization-dir "$ORIENTER_RUN_DIR/ape_visualizations" \
|
| 159 |
+
--ape-checkpoint ape_d_model_final.pth \
|
| 160 |
+
--enable-reflection \
|
| 161 |
+
--reflection-profile default \
|
| 162 |
+
--max-reflection-iterations 10
|
| 163 |
+
```
|
| 164 |
+
|
| 165 |
+
PII.5 sends the original scene and the bbox crop as separate images. The crop is neither enlarged nor flipped. PII.6 sends the full scene with current boxes, and PII.7 returns structured verification/refinement feedback. Rejected false positives are removed before the next detector call; verified detections are retained.
|
| 166 |
+
|
| 167 |
+
## Ranges, shards, resume, and merge
|
| 168 |
+
|
| 169 |
+
Use either a range or deterministic modulo shards; the two selection modes are intentionally mutually exclusive. Output filenames receive a selection suffix automatically, so parallel jobs cannot overwrite each other.
|
| 170 |
+
|
| 171 |
+
```bash
|
| 172 |
+
python -B -m approach.run_ape ... --start-index 0 --end-index 100
|
| 173 |
+
python -B -m approach.run_ape ... --num-shards 4 --shard-index 0
|
| 174 |
+
python -B -m approach.run_ape ... --num-shards 4 --shard-index 1
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
Add `--resume` to continue from an existing output. Progress sidecars record completed images even when a valid inference produces zero detections.
|
| 178 |
+
|
| 179 |
+
Merge shards only after validating overlap and coverage:
|
| 180 |
+
|
| 181 |
+
```bash
|
| 182 |
+
python -B -m evaluation.tools.merge_predictions \
|
| 183 |
+
--inputs "$ORIENTER_RUN_DIR/predictions.shard00-of-04.json" \
|
| 184 |
+
"$ORIENTER_RUN_DIR/predictions.shard01-of-04.json" \
|
| 185 |
+
"$ORIENTER_RUN_DIR/predictions.shard02-of-04.json" \
|
| 186 |
+
"$ORIENTER_RUN_DIR/predictions.shard03-of-04.json" \
|
| 187 |
+
--questions "$ORIENTER_RUN_DIR/questions.jsonl" \
|
| 188 |
+
--output "$ORIENTER_RUN_DIR/predictions.merged.json"
|
| 189 |
+
```
|
| 190 |
+
|
| 191 |
+
The merge command fails on overlapping shard progress, duplicate detections, missing sidecars, or incomplete expected image coverage.
|
| 192 |
+
|
| 193 |
+
## Evaluation
|
| 194 |
+
|
| 195 |
+
Convert prediction fields explicitly for each task:
|
| 196 |
+
|
| 197 |
+
```bash
|
| 198 |
+
python -B -m evaluation.tools.to_pred \
|
| 199 |
+
--task semantics \
|
| 200 |
+
--input "$ORIENTER_RUN_DIR/predictions.merged.json" \
|
| 201 |
+
--output "$ORIENTER_RUN_DIR/predictions.semantics.json"
|
| 202 |
+
```
|
| 203 |
+
|
| 204 |
+
`--task` accepts `semantics`, `interactable`, `interaction`, or `all`; `all` writes separate outputs and never overwrites one task with another. The remaining metric scripts and expected ground-truth layouts are documented in [PIPELINE_README.md](PIPELINE_README.md).
|
| 205 |
+
|
| 206 |
+
## Tests
|
| 207 |
+
|
| 208 |
+
Run the release-critical offline suite from the repository root:
|
| 209 |
+
|
| 210 |
+
```bash
|
| 211 |
+
python -B scripts/smoke_control_path.py
|
| 212 |
+
python scripts/run_tests.py
|
| 213 |
+
```
|
| 214 |
+
|
| 215 |
+
The deterministic control-path smoke uses a generated image plus injected provider/detector functions; it does not call a paid API or load APE weights. The unit tests are also offline. The final release gate additionally requires a real one-image inference, reflection smoke test, and evaluation smoke test after the external materials are supplied.
|
| 216 |
+
|
| 217 |
+
## Legacy experiment scripts
|
| 218 |
+
|
| 219 |
+
`approach/method.py`, `method_fastuse.py`, `method_claude.py`, and `method_gemini.py` preserve historical branches and commented stage variants so experiment provenance is not lost. Their active APE paths now produce valid accumulated JSON and accept range/shard controls; `method.py` and `method_fastuse.py` also accept explicit data/checkpoint/output paths and reflection flags.
|
| 220 |
+
|
| 221 |
+
For new runs, prefer `run_vlm.py` and `run_ape.py`. They make stage selection, paths, model profiles, app metadata caches, ranges, and shards configurable instead of requiring source edits.
|
| 222 |
+
|
| 223 |
+
## Data, licenses, and citation
|
| 224 |
+
|
| 225 |
+
Dataset images, annotations, bundled weights, and vendored third-party code may use different licenses. Publish every external asset with its stable URL, version, SHA-256, license, and expected local path. See [docs/THIRD_PARTY.md](docs/THIRD_PARTY.md) and [docs/MODEL_MANIFEST.md](docs/MODEL_MANIFEST.md).
|
| 226 |
+
|
| 227 |
+
The paper is published in *Proceedings of the ACM on Software Engineering*, Volume 3, Issue FSE, Article FSE127, pages 2858–2881, in 2026:
|
| 228 |
+
|
| 229 |
+
```bibtex
|
| 230 |
+
@article{li2026look,
|
| 231 |
+
author = {Shuqing Li and Binchang Li and Yepang Liu and Cuiyun Gao and
|
| 232 |
+
Jianping Zhang and Shing-Chi Cheung and Michael R. Lyu},
|
| 233 |
+
title = {Look Before You Leap: Context-Sensitive GUI Grounding for
|
| 234 |
+
Boosting Automated Extended Reality (XR) Testing},
|
| 235 |
+
journal = {Proceedings of the ACM on Software Engineering},
|
| 236 |
+
year = {2026},
|
| 237 |
+
volume = {3},
|
| 238 |
+
number = {FSE},
|
| 239 |
+
pages = {2858--2881},
|
| 240 |
+
articleno = {FSE127},
|
| 241 |
+
doi = {10.1145/3808134}
|
| 242 |
+
}
|
| 243 |
+
```
|
| 244 |
+
|
| 245 |
+
`CITATION.cff` contains the same preferred paper citation. The top-level code license and final Hugging Face repository links remain publication decisions; licenses for the paper, data, model, and vendored source are separate and must not be inferred from one another.
|
approach/__init__.py
ADDED
|
File without changes
|
approach/app_metadata.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Load and validate pinned application metadata used by Orienter prompts."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
from collections.abc import Mapping
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def _reject_duplicate_keys(pairs):
|
| 9 |
+
result = {}
|
| 10 |
+
for key, value in pairs:
|
| 11 |
+
if key in result:
|
| 12 |
+
raise ValueError(f"Duplicate key in metadata cache: {key}")
|
| 13 |
+
result[key] = value
|
| 14 |
+
return result
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _loads_json(text: str):
|
| 18 |
+
return json.loads(text, object_pairs_hook=_reject_duplicate_keys)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _validated_record(app_id, record):
|
| 22 |
+
normalized_id = str(app_id)
|
| 23 |
+
if not isinstance(record, Mapping):
|
| 24 |
+
raise ValueError(
|
| 25 |
+
f"metadata cache record for app_id {normalized_id} must be a JSON object"
|
| 26 |
+
)
|
| 27 |
+
if "app_id" in record and str(record["app_id"]) != normalized_id:
|
| 28 |
+
raise ValueError(
|
| 29 |
+
f"metadata cache key {normalized_id} conflicts with record app_id {record['app_id']}"
|
| 30 |
+
)
|
| 31 |
+
app_name = record.get("app_name") or record.get("name") or record.get("title")
|
| 32 |
+
app_description = record.get("app_description") or record.get("description")
|
| 33 |
+
if not isinstance(app_name, str) or not app_name.strip():
|
| 34 |
+
raise ValueError(f"metadata cache record for app_id {normalized_id} is missing app_name")
|
| 35 |
+
if not isinstance(app_description, str) or not app_description.strip():
|
| 36 |
+
raise ValueError(
|
| 37 |
+
f"metadata cache record for app_id {normalized_id} is missing app_description"
|
| 38 |
+
)
|
| 39 |
+
return dict(record)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _add_record(cache, app_id, record):
|
| 43 |
+
normalized_id = str(app_id)
|
| 44 |
+
if normalized_id in cache:
|
| 45 |
+
raise ValueError(f"Duplicate app_id in metadata cache: {normalized_id}")
|
| 46 |
+
cache[normalized_id] = _validated_record(normalized_id, record)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def load_app_metadata_cache(path):
|
| 50 |
+
"""Return a validated app-id mapping from JSON, JSON-list, or JSONL input."""
|
| 51 |
+
if path is None:
|
| 52 |
+
return None
|
| 53 |
+
|
| 54 |
+
path = Path(path)
|
| 55 |
+
if path.suffix.lower() == ".jsonl":
|
| 56 |
+
cache = {}
|
| 57 |
+
with path.open(encoding="utf-8") as file:
|
| 58 |
+
for line_number, line in enumerate(file, start=1):
|
| 59 |
+
if not line.strip():
|
| 60 |
+
continue
|
| 61 |
+
record = _loads_json(line)
|
| 62 |
+
if not isinstance(record, Mapping) or "app_id" not in record:
|
| 63 |
+
raise ValueError(
|
| 64 |
+
f"metadata JSONL record on line {line_number} must include app_id"
|
| 65 |
+
)
|
| 66 |
+
_add_record(cache, record["app_id"], record)
|
| 67 |
+
return cache
|
| 68 |
+
|
| 69 |
+
loaded = _loads_json(path.read_text(encoding="utf-8"))
|
| 70 |
+
if isinstance(loaded, list):
|
| 71 |
+
cache = {}
|
| 72 |
+
for index, record in enumerate(loaded):
|
| 73 |
+
if not isinstance(record, Mapping) or "app_id" not in record:
|
| 74 |
+
raise ValueError(f"metadata JSON list record {index} must include app_id")
|
| 75 |
+
_add_record(cache, record["app_id"], record)
|
| 76 |
+
return cache
|
| 77 |
+
if not isinstance(loaded, Mapping):
|
| 78 |
+
raise ValueError(f"Metadata cache must be a JSON object, list, or JSONL: {path}")
|
| 79 |
+
|
| 80 |
+
cache = {}
|
| 81 |
+
for app_id, record in loaded.items():
|
| 82 |
+
_add_record(cache, app_id, record)
|
| 83 |
+
return cache
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def get_app_metadata(app_id, metadata_cache):
|
| 87 |
+
"""Return the validated app name and description for one app ID."""
|
| 88 |
+
normalized_id = str(app_id)
|
| 89 |
+
record = metadata_cache.get(normalized_id)
|
| 90 |
+
if record is None:
|
| 91 |
+
raise KeyError(f"app_id {normalized_id} is missing from the app metadata cache")
|
| 92 |
+
record = _validated_record(normalized_id, record)
|
| 93 |
+
app_name = record.get("app_name") or record.get("name") or record.get("title")
|
| 94 |
+
app_description = record.get("app_description") or record.get("description")
|
| 95 |
+
return app_name, app_description
|
approach/config.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
from dataclasses import dataclass, field
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from typing import Any, Dict, Mapping, Optional
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1"
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@dataclass(frozen=True)
|
| 12 |
+
class ProviderConfig:
|
| 13 |
+
name: str
|
| 14 |
+
provider: str
|
| 15 |
+
model: str
|
| 16 |
+
base_url: str
|
| 17 |
+
api_key_env: str
|
| 18 |
+
router_options: Dict[str, Any] = field(default_factory=dict)
|
| 19 |
+
|
| 20 |
+
@property
|
| 21 |
+
def api_key(self) -> str:
|
| 22 |
+
return os.environ.get(self.api_key_env, "")
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
DEFAULT_PROFILES: Dict[str, ProviderConfig] = {
|
| 26 |
+
"default": ProviderConfig(
|
| 27 |
+
name="default",
|
| 28 |
+
provider="openrouter",
|
| 29 |
+
model=os.environ.get("OPENROUTER_MODEL", "openai/gpt-5.6-sol"),
|
| 30 |
+
base_url=OPENROUTER_BASE_URL,
|
| 31 |
+
api_key_env="OPENROUTER_API_KEY",
|
| 32 |
+
),
|
| 33 |
+
"best_value": ProviderConfig(
|
| 34 |
+
name="best_value",
|
| 35 |
+
provider="openrouter",
|
| 36 |
+
model=os.environ.get("OPENROUTER_BEST_VALUE_MODEL", "openai/gpt-5.6-terra"),
|
| 37 |
+
base_url=OPENROUTER_BASE_URL,
|
| 38 |
+
api_key_env="OPENROUTER_API_KEY",
|
| 39 |
+
),
|
| 40 |
+
"paper_openai": ProviderConfig(
|
| 41 |
+
name="paper_openai",
|
| 42 |
+
provider="openrouter",
|
| 43 |
+
model="openai/gpt-4o-2024-08-06",
|
| 44 |
+
base_url=OPENROUTER_BASE_URL,
|
| 45 |
+
api_key_env="OPENROUTER_API_KEY",
|
| 46 |
+
),
|
| 47 |
+
"paper_claude": ProviderConfig(
|
| 48 |
+
name="paper_claude",
|
| 49 |
+
provider="openrouter",
|
| 50 |
+
model="anthropic/claude-3.5-sonnet",
|
| 51 |
+
base_url=OPENROUTER_BASE_URL,
|
| 52 |
+
api_key_env="OPENROUTER_API_KEY",
|
| 53 |
+
),
|
| 54 |
+
"paper_gemini": ProviderConfig(
|
| 55 |
+
name="paper_gemini",
|
| 56 |
+
provider="openrouter",
|
| 57 |
+
model="google/gemini-pro-1.5",
|
| 58 |
+
base_url=OPENROUTER_BASE_URL,
|
| 59 |
+
api_key_env="OPENROUTER_API_KEY",
|
| 60 |
+
),
|
| 61 |
+
"fastuse_experimental": ProviderConfig(
|
| 62 |
+
name="fastuse_experimental",
|
| 63 |
+
provider="openrouter",
|
| 64 |
+
model=os.environ.get("OPENROUTER_FASTUSE_MODEL", "google/gemini-3.1-pro-preview"),
|
| 65 |
+
base_url=OPENROUTER_BASE_URL,
|
| 66 |
+
api_key_env="OPENROUTER_API_KEY",
|
| 67 |
+
),
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
PROFILE_ALIASES = {
|
| 72 |
+
"openrouter": "default",
|
| 73 |
+
"gpt4v": "paper_openai",
|
| 74 |
+
"gpt4v_abl": "paper_openai",
|
| 75 |
+
"claude35sonnet": "paper_claude",
|
| 76 |
+
"gemini15pro": "paper_gemini",
|
| 77 |
+
"gemini31pro": "fastuse_experimental",
|
| 78 |
+
"gemini-3-flash-preview-nothinking": "default",
|
| 79 |
+
"gemini-3-flash-preview-nothinking_abl": "default",
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def _load_yaml_or_json(path: Path) -> Mapping[str, Any]:
|
| 84 |
+
text = path.read_text()
|
| 85 |
+
if path.suffix.lower() == ".json":
|
| 86 |
+
return json.loads(text)
|
| 87 |
+
try:
|
| 88 |
+
import yaml
|
| 89 |
+
except ImportError as exc:
|
| 90 |
+
raise RuntimeError("Install PyYAML or use a JSON config file.") from exc
|
| 91 |
+
loaded = yaml.safe_load(text)
|
| 92 |
+
return loaded or {}
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def _profile_from_mapping(name: str, values: Mapping[str, Any]) -> ProviderConfig:
|
| 96 |
+
return ProviderConfig(
|
| 97 |
+
name=name,
|
| 98 |
+
provider=values["provider"],
|
| 99 |
+
model=values["model"],
|
| 100 |
+
base_url=values.get("base_url", OPENROUTER_BASE_URL),
|
| 101 |
+
api_key_env=values.get("api_key_env", "OPENROUTER_API_KEY"),
|
| 102 |
+
router_options=dict(values.get("router_options", {})),
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def load_profiles(path: Optional[str] = None) -> Dict[str, ProviderConfig]:
|
| 107 |
+
profiles = dict(DEFAULT_PROFILES)
|
| 108 |
+
if not path:
|
| 109 |
+
return profiles
|
| 110 |
+
|
| 111 |
+
raw = _load_yaml_or_json(Path(path))
|
| 112 |
+
raw_profiles = raw.get("profiles", raw)
|
| 113 |
+
for name, values in raw_profiles.items():
|
| 114 |
+
profiles[name] = _profile_from_mapping(name, values)
|
| 115 |
+
return profiles
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def get_model_profile(name: str = "default", config_path: Optional[str] = None) -> ProviderConfig:
|
| 119 |
+
profile_name = PROFILE_ALIASES.get(name, name)
|
| 120 |
+
profiles = load_profiles(config_path)
|
| 121 |
+
if profile_name not in profiles:
|
| 122 |
+
valid = ", ".join(sorted(profiles))
|
| 123 |
+
raise ValueError(f"Unknown model profile '{name}'. Valid profiles: {valid}")
|
| 124 |
+
return profiles[profile_name]
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def validate_model_available(profile: ProviderConfig, models_catalog: Optional[Any] = None) -> bool:
|
| 128 |
+
if models_catalog is None:
|
| 129 |
+
return True
|
| 130 |
+
if isinstance(models_catalog, Mapping):
|
| 131 |
+
models_catalog = models_catalog.get("data", models_catalog.keys())
|
| 132 |
+
model_ids = {
|
| 133 |
+
entry.get("id") if isinstance(entry, Mapping) else str(entry)
|
| 134 |
+
for entry in models_catalog
|
| 135 |
+
}
|
| 136 |
+
if profile.model not in model_ids:
|
| 137 |
+
raise ValueError(f"Model {profile.model!r} was not found in the supplied catalog.")
|
| 138 |
+
return True
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def sanitize_manifest(
|
| 142 |
+
profile: ProviderConfig,
|
| 143 |
+
request_payload: Mapping[str, Any],
|
| 144 |
+
response_payload: Optional[Mapping[str, Any]] = None,
|
| 145 |
+
) -> Dict[str, Any]:
|
| 146 |
+
response_payload = response_payload or {}
|
| 147 |
+
return {
|
| 148 |
+
"provider": profile.provider,
|
| 149 |
+
"profile": profile.name,
|
| 150 |
+
"request_model": request_payload.get("model"),
|
| 151 |
+
"response_model": response_payload.get("model"),
|
| 152 |
+
"base_url": profile.base_url,
|
| 153 |
+
"api_key_env": profile.api_key_env,
|
| 154 |
+
"router_options": dict(profile.router_options),
|
| 155 |
+
"usage": response_payload.get("usage", {}),
|
| 156 |
+
}
|
approach/method_gemini.py
ADDED
|
@@ -0,0 +1,655 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import re
|
| 3 |
+
import sys
|
| 4 |
+
import json
|
| 5 |
+
import time
|
| 6 |
+
import random
|
| 7 |
+
import string
|
| 8 |
+
import argparse
|
| 9 |
+
import pandas as pd
|
| 10 |
+
import copy
|
| 11 |
+
import logging
|
| 12 |
+
from tqdm import tqdm
|
| 13 |
+
import PIL
|
| 14 |
+
from PIL import ImageFile
|
| 15 |
+
PIL.ImageFile.LOAD_TRUNCATED_IMAGES = True
|
| 16 |
+
|
| 17 |
+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 18 |
+
sys.path.append(BASE_DIR)
|
| 19 |
+
from approach.pipeline_utils import (
|
| 20 |
+
enrich_ape_results,
|
| 21 |
+
output_path_for_selection,
|
| 22 |
+
select_jsonl_lines,
|
| 23 |
+
write_json_atomic,
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
os.environ['TORCH_HOME'] = './.cache'
|
| 27 |
+
os.environ['HF_HOME'] = './.cache'
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
vlm = 'llava7b'
|
| 31 |
+
llm = 'gpt_3.5_turbo'
|
| 32 |
+
ovod = 'grounding_dino'
|
| 33 |
+
|
| 34 |
+
vlm_prompt = ''
|
| 35 |
+
|
| 36 |
+
### Configurable
|
| 37 |
+
host_device = os.getenv('ORIENTER_LEGACY_HOST', 'local')
|
| 38 |
+
perspective = 'all_perspective'
|
| 39 |
+
# perspective = 'direct_back'
|
| 40 |
+
# perspective = 'direct_front'
|
| 41 |
+
# perspective = 'direct_side'
|
| 42 |
+
# perspective = 'direct_top'
|
| 43 |
+
# perspective = 'eyelevel'
|
| 44 |
+
# perspective = 'overlook'
|
| 45 |
+
### Configurable
|
| 46 |
+
|
| 47 |
+
llava_path = os.path.join(BASE_DIR, 'approach/vlm/LLaVA/llava/eval')
|
| 48 |
+
grounding_dino_path = os.path.join(BASE_DIR, 'approach/ovod/GroundingDINO')
|
| 49 |
+
ape_path = os.path.join(BASE_DIR, 'approach/ovod/APE')
|
| 50 |
+
# TODO: data on the CUHK server
|
| 51 |
+
# images_dir = os.path.join(BASE_DIR, 'dataset/data/coco_merged/images/interactable')
|
| 52 |
+
# icse
|
| 53 |
+
# images_dir = os.path.join(BASE_DIR, 'dataset/data/coco_merged/images/test_set_merged')
|
| 54 |
+
# icse_rebuttal
|
| 55 |
+
# images_dir = os.path.join(BASE_DIR, 'dataset/data/coco_angleview/images/all_perspective')
|
| 56 |
+
# fse
|
| 57 |
+
images_dir = os.path.join(BASE_DIR, 'dataset/data/coco_det/images/union3')
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
# def generate_grounding_dino_command(ovod_image_path, ovod_output_dir, ovod_candidates):
|
| 61 |
+
# gdino_object_str = ' . '.join(ovod_candidates)
|
| 62 |
+
|
| 63 |
+
# gdino_command = f'''
|
| 64 |
+
# python demo/inference_on_a_image.py \
|
| 65 |
+
# -c groundingdino/config/GroundingDINO_SwinT_OGC.py \
|
| 66 |
+
# -p weights/groundingdino_swint_ogc.pth \
|
| 67 |
+
# -i {ovod_image_path} \
|
| 68 |
+
# -o "{ovod_output_dir}" \
|
| 69 |
+
# -t "{gdino_object_str}"
|
| 70 |
+
# '''
|
| 71 |
+
# return gdino_command
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def generate_question_file(img_folder, dst_path):
|
| 75 |
+
# Ensure the directory exists
|
| 76 |
+
if not os.path.exists(img_folder):
|
| 77 |
+
print(f"Error: Directory {img_folder} does not exist.")
|
| 78 |
+
return
|
| 79 |
+
|
| 80 |
+
# List all files in the directory
|
| 81 |
+
all_files = os.listdir(img_folder)
|
| 82 |
+
|
| 83 |
+
# Filter out files that are not images (based on extension). You can add more if needed.
|
| 84 |
+
image_extensions = [".jpg", ".jpeg", ".png", ".bmp", ".gif", ".tiff"]
|
| 85 |
+
image_files = [f for f in all_files if any(f.lower().endswith(ext) for ext in image_extensions)]
|
| 86 |
+
|
| 87 |
+
# Open the output file for writing
|
| 88 |
+
with open(dst_path, 'w') as out_file:
|
| 89 |
+
for index, img_file in enumerate(image_files):
|
| 90 |
+
data = {
|
| 91 |
+
"question_id": index,
|
| 92 |
+
"image": img_file,
|
| 93 |
+
"text": vlm_prompt,
|
| 94 |
+
"category": "detail"
|
| 95 |
+
}
|
| 96 |
+
out_file.write(json.dumps(data) + '\n')
|
| 97 |
+
|
| 98 |
+
print(f"Processed {len(image_files)} images. Output saved to {dst_path}.")
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def generate_answer_id(length=20):
|
| 102 |
+
# Define the characters that can be used in the string
|
| 103 |
+
characters = string.ascii_letters + string.digits
|
| 104 |
+
# Generate a random string of the specified length
|
| 105 |
+
answer_id = ''.join(random.choice(characters) for _ in range(length))
|
| 106 |
+
return answer_id
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def method(vlm=vlm, llm=llm, ovod=ovod, start_index=None, end_index=None, shard_index=None, num_shards=None):
|
| 110 |
+
# vlm_question_path = os.path.join(BASE_DIR, 'approach/vlm/sampled_vlm_questions.jsonl')
|
| 111 |
+
# vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/icse_test_set_merged_vlm_questions_from671.jsonl')
|
| 112 |
+
# icse
|
| 113 |
+
# complete_vlm_question_abl_i_path = os.path.join(BASE_DIR, f'approach/vlm/icse_test_set_merged_complete_vlm_questions_ablation_interactability.jsonl')
|
| 114 |
+
|
| 115 |
+
# icse
|
| 116 |
+
# complete_vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/icse_test_set_merged_complete_vlm_questions.jsonl')
|
| 117 |
+
# vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/gpt4v_failure_questions.jsonl')
|
| 118 |
+
# icse_rebuttal
|
| 119 |
+
# vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/icse_rbt_{perspective}_vlm_questions.jsonl')
|
| 120 |
+
# complete_vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/icse_rbt_{perspective}_complete_vlm_questions.jsonl')
|
| 121 |
+
# fse
|
| 122 |
+
# vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/fse_union3_{vlm}_questions.jsonl')
|
| 123 |
+
complete_vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/realfse_union3_complete_{vlm}_questions.jsonl')
|
| 124 |
+
vlm_question_path = complete_vlm_question_path
|
| 125 |
+
# vlm_question_path = os.path.join(BASE_DIR, f'approach/vlm/gpt4v_failure_questions.jsonl')
|
| 126 |
+
# playground/data/coco2014_val_qa_eval/qa90_questions.jsonl
|
| 127 |
+
# vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/{host_device}_icse_test_set_merged_{vlm}_answer_slowerspeed_1.jsonl')
|
| 128 |
+
# vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/{host_device}_icse_test_set_merged_{vlm}_answer_higherspeed_r3_1_from195.jsonl')
|
| 129 |
+
|
| 130 |
+
# icse
|
| 131 |
+
# vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/cuhk_icse_test_set_merged_{vlm}_answer.jsonl')
|
| 132 |
+
# llava_vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/{host_device}_icse_test_set_merged_{vlm}_answer.jsonl')
|
| 133 |
+
# gemini_vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/{host_device}_icse_test_set_merged_{vlm}_answer_ablation_no_interactability_r1_0.jsonl')
|
| 134 |
+
# gpt4_results_answer_path = os.path.join(BASE_DIR, f'approach/vlm/aaa_cuhk_icse_test_set_merged_gpt4v_answer_higherspeed_r3_0.jsonl')
|
| 135 |
+
# interactability_abl_results_answer_path = os.path.join(BASE_DIR, f'approach/vlm/aaa_hit_icse_test_set_merged_gemini_answer_ablation_no_interactability_r1_0.jsonl')
|
| 136 |
+
# icse_rebuttal
|
| 137 |
+
# vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/icse_rbt_{perspective}_{vlm}_answer.jsonl')
|
| 138 |
+
# llava_vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/{host_device}_icse_rbt_{perspective}_{vlm}_answer.jsonl')
|
| 139 |
+
# gemini_vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/{host_device}_icse_rbt_{perspective}_{vlm}_answer.jsonl')
|
| 140 |
+
# fse
|
| 141 |
+
vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/realfse_uninon3_{vlm}_answer.jsonl')
|
| 142 |
+
llava_vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/{host_device}_fse_uninon3_{vlm}_answer.jsonl')
|
| 143 |
+
gemini_vlm_answer_path = os.path.join(BASE_DIR, f'approach/vlm/realfse_union3_{vlm}_answer.jsonl')
|
| 144 |
+
|
| 145 |
+
# icse
|
| 146 |
+
# gpt4_results_answer_path = os.path.join(BASE_DIR, f'approach/vlm/aaa_icse_rbt_{perspective}_gpt4v_answer.jsonl')
|
| 147 |
+
# icse_rebuttal
|
| 148 |
+
# gpt4_results_answer_path = os.path.join(BASE_DIR, f'approach/vlm/icse_rbt_{perspective}_{vlm}_answer_265_528.jsonl')
|
| 149 |
+
# fse
|
| 150 |
+
gpt4_results_answer_path = os.path.join(BASE_DIR, f'approach/vlm/realfse_uninon3_gpt4v_answer.jsonl')
|
| 151 |
+
# print(gpt4_results_answer_path)
|
| 152 |
+
# interactability_abl_results_answer_path = os.path.join(BASE_DIR, f'approach/vlm/aaa_icse_rbt_{perspective}_answer.jsonl')
|
| 153 |
+
|
| 154 |
+
# icse
|
| 155 |
+
# # /path/to/answer-file-our.jsonl
|
| 156 |
+
# # llm_candidate_path = os.path.join(BASE_DIR, f'approach/llm/{vlm}_{llm}_cancidate_objects.json')
|
| 157 |
+
# interactable_object_path = os.path.join(BASE_DIR, f'approach/llm/icse_test_set_merged_{vlm}_{llm}_interactable_objects.json')
|
| 158 |
+
# ovod_output_dir = os.path.join(BASE_DIR, f'approach/ovod/output/icse_test_set_merged_{ovod}')
|
| 159 |
+
# # oovd_object_bbox_path = os.path.join(BASE_DIR, f'approach/ovod/icse_test_set_merged_{vlm}_{llm}_{ovod}_object_bbox_gpu3_2.json')
|
| 160 |
+
# # oovd_object_bbox_path = os.path.join(BASE_DIR, f'approach/ovod/icse_test_set_merged_{vlm}_{llm}_{ovod}_ablation_no_interactability_object_bbox_gpu0.json')
|
| 161 |
+
# oovd_object_bbox_path = os.path.join(BASE_DIR, f'approach/ovod/icse_test_set_merged_{vlm}_{llm}_{ovod}_ablation_no_feedback_object_bbox_gpu2.json')
|
| 162 |
+
|
| 163 |
+
# icse_rebuttal
|
| 164 |
+
# interactable_object_path = os.path.join(BASE_DIR, f'approach/llm/icse_rbt_{perspective}_{vlm}_{llm}_interactable_objects.json')
|
| 165 |
+
# ovod_output_dir = os.path.join(BASE_DIR, f'approach/ovod/output/icse_rbt_{perspective}_{ovod}')
|
| 166 |
+
# # oovd_object_bbox_path = os.path.join(BASE_DIR, f'approach/ovod/icse_test_set_merged_{vlm}_{llm}_{ovod}_object_bbox_gpu3_2.json')
|
| 167 |
+
# # oovd_object_bbox_path = os.path.join(BASE_DIR, f'approach/ovod/icse_test_set_merged_{vlm}_{llm}_{ovod}_ablation_no_interactability_object_bbox_gpu0.json')
|
| 168 |
+
# oovd_object_bbox_path = os.path.join(BASE_DIR, f'approach/ovod/icse_rbt_{perspective}_{vlm}_{llm}_{ovod}_object_bbox_gpu3_265_528.json')
|
| 169 |
+
|
| 170 |
+
gpu = '3'
|
| 171 |
+
# fse
|
| 172 |
+
interactable_object_path = os.path.join(BASE_DIR, f'approach/llm/realfse_union3_{vlm}_{llm}_interactable_objects.json') # TODO: Tentatively unused
|
| 173 |
+
ovod_output_dir = os.path.join(BASE_DIR, f'approach/ovod/output/realfse_union3_{ovod}')
|
| 174 |
+
# GPU 0
|
| 175 |
+
oovd_object_bbox_path = os.path.join(BASE_DIR, f'approach/ovod/realfse_union3_{vlm}_{ovod}_object_bbox_gpu{gpu}.json')
|
| 176 |
+
oovd_object_bbox_path = output_path_for_selection(
|
| 177 |
+
oovd_object_bbox_path,
|
| 178 |
+
start_index=start_index,
|
| 179 |
+
end_index=end_index,
|
| 180 |
+
shard_index=shard_index,
|
| 181 |
+
num_shards=num_shards,
|
| 182 |
+
)
|
| 183 |
+
|
| 184 |
+
llava7b_model_path = os.path.join(BASE_DIR, 'approach/vlm/LLaVA/checkpoints/llava-v1.5-7b')
|
| 185 |
+
llava7b_cmd = f'''
|
| 186 |
+
python model_vqa.py \
|
| 187 |
+
--model-path {llava7b_model_path} \
|
| 188 |
+
--question-file \
|
| 189 |
+
{complete_vlm_question_path} \
|
| 190 |
+
--image-folder \
|
| 191 |
+
{images_dir} \
|
| 192 |
+
--answers-file \
|
| 193 |
+
{llava_vlm_answer_path}
|
| 194 |
+
'''
|
| 195 |
+
|
| 196 |
+
# STEP #0
|
| 197 |
+
# First time of running
|
| 198 |
+
# TODO: Check whether it is first-time running
|
| 199 |
+
# generate_question_file(images_dir, vlm_question_path)
|
| 200 |
+
|
| 201 |
+
# STEP #1
|
| 202 |
+
# VLM - Get local context
|
| 203 |
+
# print(f'VLM {vlm} analysis begins ...')
|
| 204 |
+
# vlm_start_time = time.time()
|
| 205 |
+
# logging.info(f'STEP #1 VLM {vlm} analysis started at %s', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
|
| 206 |
+
# if vlm == 'llava7b':
|
| 207 |
+
# original_path = os.path.dirname(__file__)
|
| 208 |
+
# os.chdir(llava_path)
|
| 209 |
+
# os.system(llava7b_cmd)
|
| 210 |
+
# os.chdir(original_path)
|
| 211 |
+
# elif vlm == 'generate_q_file':
|
| 212 |
+
# from approach.vlm.gpt4v.gpt4v import process_image_q
|
| 213 |
+
# with open(vlm_question_path, 'r') as q_file, open(complete_vlm_question_path, 'w') as cq_file: # Open answer file in append mode
|
| 214 |
+
# qfile_lines = q_file.readlines()
|
| 215 |
+
# key_idx = 0
|
| 216 |
+
# for line in tqdm(qfile_lines):
|
| 217 |
+
# key_idx = (key_idx + 1) % 4
|
| 218 |
+
|
| 219 |
+
# ans_item = {}
|
| 220 |
+
|
| 221 |
+
# line_data = json.loads(line)
|
| 222 |
+
# image_path = os.path.join(images_dir, line_data['image'])
|
| 223 |
+
# image_question = line_data['text']
|
| 224 |
+
|
| 225 |
+
# if vlm == 'gpt4v_abl':
|
| 226 |
+
# gpt4v_ablation = True
|
| 227 |
+
# elif vlm == 'gpt4v':
|
| 228 |
+
# gpt4v_ablation = False
|
| 229 |
+
# gpt4v_q = process_image_q(image_question, image_path, key_idx)
|
| 230 |
+
# data = {
|
| 231 |
+
# "question_id": line_data['question_id'],
|
| 232 |
+
# "image": line_data['image'],
|
| 233 |
+
# "text": gpt4v_q,
|
| 234 |
+
# "category": "detail"
|
| 235 |
+
# }
|
| 236 |
+
# cq_file.write(json.dumps(data) + '\n')
|
| 237 |
+
# elif vlm == 'gpt4v' or vlm == 'gpt4v_abl' or vlm == 'claude35sonnet' or vlm == 'gemini15pro':
|
| 238 |
+
# from approach.vlm.gpt4v.gpt4v import process_image
|
| 239 |
+
# with open(vlm_question_path, 'r') as q_file, open(vlm_answer_path, 'a') as a_file: # Open answer file in append mode
|
| 240 |
+
# qfile_lines = q_file.readlines()
|
| 241 |
+
# key_idx = 0
|
| 242 |
+
# for line in tqdm(qfile_lines):
|
| 243 |
+
# key_idx = (key_idx + 1) % 4
|
| 244 |
+
|
| 245 |
+
# ans_item = {}
|
| 246 |
+
|
| 247 |
+
# line_data = json.loads(line)
|
| 248 |
+
# image_path = os.path.join(images_dir, line_data['image'])
|
| 249 |
+
# image_question = line_data['text']
|
| 250 |
+
|
| 251 |
+
# gpt4v_ablation = False
|
| 252 |
+
# if vlm == 'gpt4v_abl':
|
| 253 |
+
# gpt4v_ablation = True
|
| 254 |
+
# elif vlm == 'gpt4v':
|
| 255 |
+
# gpt4v_ablation = False
|
| 256 |
+
# gpt4v_res = process_image(vlm, image_question, image_path, gpt4v_ablation, key_idx)
|
| 257 |
+
# print(gpt4v_res)
|
| 258 |
+
# # time.sleep(3)
|
| 259 |
+
|
| 260 |
+
# ans_item = {
|
| 261 |
+
# "question_id": line_data['question_id'],
|
| 262 |
+
# "prompt": '',
|
| 263 |
+
# "text": gpt4v_res,
|
| 264 |
+
# "answer_id": generate_answer_id(),
|
| 265 |
+
# "model_id": vlm,
|
| 266 |
+
# "metadata": {}
|
| 267 |
+
# }
|
| 268 |
+
|
| 269 |
+
# a_file.write(json.dumps(ans_item) + '\n')
|
| 270 |
+
# a_file.flush()
|
| 271 |
+
# elif vlm == 'bing':
|
| 272 |
+
# from approach.vlm.bing.bing import context_conversation
|
| 273 |
+
# with open(vlm_question_path, 'r') as q_file, open(vlm_answer_path, 'a') as a_file: # Open answer file in append mode
|
| 274 |
+
# for line in tqdm(q_file):
|
| 275 |
+
# ans_item = {}
|
| 276 |
+
|
| 277 |
+
# line_data = json.loads(line)
|
| 278 |
+
# image_path = os.path.join(images_dir, line_data['image'])
|
| 279 |
+
# image_question = line_data['text']
|
| 280 |
+
|
| 281 |
+
# bing_res = context_conversation(image_question, image_path)
|
| 282 |
+
# print(bing_res)
|
| 283 |
+
# time.sleep(15)
|
| 284 |
+
|
| 285 |
+
# ans_item = {
|
| 286 |
+
# "question_id": line_data['question_id'],
|
| 287 |
+
# "prompt": image_question,
|
| 288 |
+
# "text": bing_res,
|
| 289 |
+
# "answer_id": generate_answer_id(),
|
| 290 |
+
# "model_id": vlm,
|
| 291 |
+
# "metadata": {}
|
| 292 |
+
# }
|
| 293 |
+
|
| 294 |
+
# a_file.write(json.dumps(ans_item) + '\n')
|
| 295 |
+
# elif vlm == 'gemini':
|
| 296 |
+
# import pathlib
|
| 297 |
+
# import textwrap
|
| 298 |
+
|
| 299 |
+
# import google.generativeai as genai
|
| 300 |
+
|
| 301 |
+
# GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "[GOOGLE_API_KEY]")
|
| 302 |
+
# genai.configure(api_key=GOOGLE_API_KEY)
|
| 303 |
+
|
| 304 |
+
# safety_settings = [
|
| 305 |
+
# {
|
| 306 |
+
# "category": "HARM_CATEGORY_DANGEROUS",
|
| 307 |
+
# "threshold": "BLOCK_NONE",
|
| 308 |
+
# },
|
| 309 |
+
# {
|
| 310 |
+
# "category": "HARM_CATEGORY_HARASSMENT",
|
| 311 |
+
# "threshold": "BLOCK_NONE",
|
| 312 |
+
# },
|
| 313 |
+
# {
|
| 314 |
+
# "category": "HARM_CATEGORY_HATE_SPEECH",
|
| 315 |
+
# "threshold": "BLOCK_NONE",
|
| 316 |
+
# },
|
| 317 |
+
# {
|
| 318 |
+
# "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
| 319 |
+
# "threshold": "BLOCK_NONE",
|
| 320 |
+
# },
|
| 321 |
+
# {
|
| 322 |
+
# "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 323 |
+
# "threshold": "BLOCK_NONE",
|
| 324 |
+
# },
|
| 325 |
+
# ]
|
| 326 |
+
|
| 327 |
+
# # before fse
|
| 328 |
+
# # model = genai.GenerativeModel('gemini-pro-vision')
|
| 329 |
+
# # fse
|
| 330 |
+
# model = genai.GenerativeModel('gemini-1.5-pro')
|
| 331 |
+
|
| 332 |
+
# with open(vlm_question_path, 'r') as q_file, open(gemini_vlm_answer_path, 'a') as a_file: # Open answer file in append mode
|
| 333 |
+
# qfile_lines = q_file.readlines()[0:2]
|
| 334 |
+
# for line in tqdm(qfile_lines):
|
| 335 |
+
|
| 336 |
+
# ans_item = {}
|
| 337 |
+
|
| 338 |
+
# line_data = json.loads(line)
|
| 339 |
+
# image_path = os.path.join(images_dir, line_data['image'])
|
| 340 |
+
# image_question = line_data['text']
|
| 341 |
+
|
| 342 |
+
# gemini_response = model.generate_content([image_question, PIL.Image.open(image_path)], safety_settings=safety_settings)
|
| 343 |
+
# gemini_response.resolve()
|
| 344 |
+
# try:
|
| 345 |
+
# gemini_response = gemini_response.text
|
| 346 |
+
# ans_item = {
|
| 347 |
+
# "question_id": line_data['question_id'],
|
| 348 |
+
# "prompt": image_question,
|
| 349 |
+
# "text": gemini_response,
|
| 350 |
+
# "answer_id": generate_answer_id(),
|
| 351 |
+
# "model_id": vlm,
|
| 352 |
+
# "metadata": {}
|
| 353 |
+
# }
|
| 354 |
+
|
| 355 |
+
# a_file.write(json.dumps(ans_item) + '\n')
|
| 356 |
+
# a_file.flush()
|
| 357 |
+
# except Exception as e:
|
| 358 |
+
# print(f"Error for image {image_path}. Error: {e}")
|
| 359 |
+
# # print(gemini_response)
|
| 360 |
+
# time.sleep(30)
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
# elif vlm.endswith('_pass'):
|
| 365 |
+
# print(f'Passing VLM {vlm} ...')
|
| 366 |
+
# else:
|
| 367 |
+
# raise Exception('Unrecognized VLM!')
|
| 368 |
+
|
| 369 |
+
# logging.info(f'STEP #1 VLM {vlm} analysis completed at %s', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
|
| 370 |
+
# logging.info(f'STEP #1 VLM {vlm} analysis total time taken: {time.time() - vlm_start_time} seconds')
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
# Get label candidates
|
| 377 |
+
# if llm == 'gpt_3.5_turbo':
|
| 378 |
+
# from approach.llm.gpt_polling import infer_object_candidates
|
| 379 |
+
# elif llm == 'llama2':
|
| 380 |
+
# from approach.llm.llama import infer_object_candidates
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
# STEP #3
|
| 384 |
+
# if llm == 'gpt_3.5_turbo' or llm == 'gpt_3.5_turbo_abl':
|
| 385 |
+
# from approach.llm.gpt_polling import infer_objects
|
| 386 |
+
|
| 387 |
+
# if llm == 'gpt_3.5_turbo_abl':
|
| 388 |
+
# gpt35_ablation = True
|
| 389 |
+
# elif llm == 'gpt_3.5_turbo':
|
| 390 |
+
# gpt35_ablation = False
|
| 391 |
+
# infer_objects(vlm_question_path, vlm_answer_path, interactable_object_path, gpt35_ablation)
|
| 392 |
+
# elif llm == 'llama2':
|
| 393 |
+
# from approach.llm.llama import infer_objects
|
| 394 |
+
# exit(0)
|
| 395 |
+
# elif llm.endswith('_pass'):
|
| 396 |
+
# print(f'Passing LLM {llm} ...')
|
| 397 |
+
# else:
|
| 398 |
+
# raise Exception('Unrecognized LLM!')
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
# STEP #2
|
| 406 |
+
# Open-vocabulary object detection
|
| 407 |
+
ovod_start_time = time.time()
|
| 408 |
+
logging.info(f'STEP #2 OVOD {ovod} analysis started at %s', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
|
| 409 |
+
vlm_questions = {}
|
| 410 |
+
with open(complete_vlm_question_path, 'r') as q_file:
|
| 411 |
+
for line in q_file:
|
| 412 |
+
line_data = json.loads(line)
|
| 413 |
+
vlm_questions[line_data['question_id']] = line_data
|
| 414 |
+
# print(line_data['question_id'])
|
| 415 |
+
|
| 416 |
+
if ovod == 'grounding_dino':
|
| 417 |
+
from approach.ovod.GroundingDINO.demo.inference_on_a_image import process_grounding_dino
|
| 418 |
+
|
| 419 |
+
original_path = os.path.dirname(__file__)
|
| 420 |
+
os.chdir(grounding_dino_path)
|
| 421 |
+
|
| 422 |
+
# with open(llm_candidate_path, 'r') as llm_candidate_file, open(oovd_object_bbox_path, 'w') as oovd_file:
|
| 423 |
+
with open(interactable_object_path, 'r') as llm_candidate_file, open(oovd_object_bbox_path, 'w') as oovd_file:
|
| 424 |
+
llm_candidate = json.load(llm_candidate_file)
|
| 425 |
+
all_oovd_res = {}
|
| 426 |
+
for image_index in llm_candidate.keys():
|
| 427 |
+
print(image_index)
|
| 428 |
+
# TODO:
|
| 429 |
+
if (image_index != '1160'):
|
| 430 |
+
ovod_image_path = os.path.join(images_dir, vlm_questions[int(image_index)]["image"])
|
| 431 |
+
ovod_candidates = llm_candidate[image_index]['interactable_objects']
|
| 432 |
+
# grounding_dino_command = generate_grounding_dino_command(ovod_image_path, ovod_output_dir, ovod_candidates)
|
| 433 |
+
# print(grounding_dino_command)
|
| 434 |
+
# os.system(grounding_dino_command)
|
| 435 |
+
|
| 436 |
+
gdino_res = process_grounding_dino(
|
| 437 |
+
config_file='groundingdino/config/GroundingDINO_SwinT_OGC.py',
|
| 438 |
+
checkpoint_path='weights/groundingdino_swint_ogc.pth',
|
| 439 |
+
image_path=ovod_image_path,
|
| 440 |
+
ovod_candidates=ovod_candidates,
|
| 441 |
+
output_dir=ovod_output_dir,
|
| 442 |
+
box_threshold=0.3,
|
| 443 |
+
text_threshold=0.25,
|
| 444 |
+
token_spans=None,
|
| 445 |
+
cpu_only=False
|
| 446 |
+
)
|
| 447 |
+
# print(gdino_res)
|
| 448 |
+
|
| 449 |
+
object_oovd_item = copy.deepcopy(llm_candidate[image_index])
|
| 450 |
+
object_oovd_item['oovd_result'] = gdino_res
|
| 451 |
+
print(object_oovd_item)
|
| 452 |
+
all_oovd_res[image_index] = object_oovd_item
|
| 453 |
+
|
| 454 |
+
json.dump(all_oovd_res, oovd_file, indent=4)
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
os.chdir(original_path)
|
| 458 |
+
|
| 459 |
+
elif ovod == 'ape_d' or ovod == 'ape_d_abl':
|
| 460 |
+
from approach.ovod.APE.demo.ape_inference import run_ape_model_inference
|
| 461 |
+
|
| 462 |
+
original_path = os.path.dirname(__file__)
|
| 463 |
+
os.chdir(ape_path)
|
| 464 |
+
|
| 465 |
+
# with open(llm_candidate_path, 'r') as llm_candidate_file, open(oovd_object_bbox_path, 'w') as oovd_file:
|
| 466 |
+
|
| 467 |
+
all_ape_res = []
|
| 468 |
+
# General
|
| 469 |
+
with open(vlm_answer_path, 'r') as llm_candidate_file, open(oovd_object_bbox_path, 'w') as oovd_file:
|
| 470 |
+
# GPT-4 eval
|
| 471 |
+
# with open(gpt4_results_answer_path, 'r') as llm_candidate_file, open(oovd_object_bbox_path, 'w') as oovd_file:
|
| 472 |
+
# Gemini abl i eval
|
| 473 |
+
# with open(interactability_abl_results_answer_path, 'r') as llm_candidate_file, open(oovd_object_bbox_path, 'w') as oovd_file:
|
| 474 |
+
|
| 475 |
+
# llm_candidate = json.load(llm_candidate_file)
|
| 476 |
+
all_oovd_res = {}
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
# candidate_lines = llm_candidate_file.readlines()[265:528]
|
| 480 |
+
# GPU 0
|
| 481 |
+
# candidate_lines = llm_candidate_file.readlines()[0:230]
|
| 482 |
+
# candidate_lines = llm_candidate_file.readlines()[230:460]
|
| 483 |
+
# candidate_lines = llm_candidate_file.readlines()[460:690]
|
| 484 |
+
candidate_lines = select_jsonl_lines(
|
| 485 |
+
llm_candidate_file.readlines(),
|
| 486 |
+
start_index=start_index,
|
| 487 |
+
end_index=end_index,
|
| 488 |
+
shard_index=shard_index,
|
| 489 |
+
num_shards=num_shards,
|
| 490 |
+
)
|
| 491 |
+
# candidate_lines = llm_candidate_file.readlines()
|
| 492 |
+
for line in tqdm(candidate_lines):
|
| 493 |
+
ans_item = {}
|
| 494 |
+
|
| 495 |
+
line_data = json.loads(line)
|
| 496 |
+
|
| 497 |
+
# for image_index in llm_candidate.keys():
|
| 498 |
+
# print(image_index)
|
| 499 |
+
# TODO:
|
| 500 |
+
# if (image_index != '1160'):
|
| 501 |
+
# image_index = line_data['question_id']
|
| 502 |
+
|
| 503 |
+
image_name = vlm_questions[line_data['question_id']]["image"]
|
| 504 |
+
ovod_image_path = os.path.join(images_dir, image_name)
|
| 505 |
+
|
| 506 |
+
# General
|
| 507 |
+
# try:
|
| 508 |
+
# if line_data['text'].startswith(" ```json"):
|
| 509 |
+
# ovod_candidates = json.loads(line_data['text'][8:-4])['objects']
|
| 510 |
+
# elif line_data['text'].startswith(' {\"objects\"'):
|
| 511 |
+
# ovod_candidates = json.loads(line_data['text'])['objects']
|
| 512 |
+
# else:
|
| 513 |
+
# print(f"Error for decoding IVO json for {image_name}.")
|
| 514 |
+
# except Exception as e:
|
| 515 |
+
# print(f"Error for decoding IVO json for image {image_name}. Error: {e}")
|
| 516 |
+
# continue
|
| 517 |
+
|
| 518 |
+
|
| 519 |
+
# GPT-4v
|
| 520 |
+
if line_data['text']:
|
| 521 |
+
ovod_candidates = line_data['text']['objects']
|
| 522 |
+
else:
|
| 523 |
+
continue
|
| 524 |
+
|
| 525 |
+
|
| 526 |
+
all_res = []
|
| 527 |
+
for ocd in ovod_candidates.keys():
|
| 528 |
+
referring_expr_str = ovod_candidates[ocd]
|
| 529 |
+
|
| 530 |
+
translator = str.maketrans('', '', string.punctuation)
|
| 531 |
+
if type(referring_expr_str) is str:
|
| 532 |
+
referring_expr_str = referring_expr_str.translate(translator)
|
| 533 |
+
else:
|
| 534 |
+
# dict
|
| 535 |
+
referring_expr_str = ' '.join(referring_expr_str.values())
|
| 536 |
+
referring_expr_str = referring_expr_str.translate(translator)
|
| 537 |
+
referring_expr_str = f'{ocd}: {referring_expr_str}'
|
| 538 |
+
# print(image_name, referring_expr_str)
|
| 539 |
+
all_res.append(referring_expr_str)
|
| 540 |
+
|
| 541 |
+
if ovod == 'ape_d':
|
| 542 |
+
ape_threshold = 0.15
|
| 543 |
+
elif ovod == 'ape_d_abl':
|
| 544 |
+
ape_threshold = 0.1
|
| 545 |
+
|
| 546 |
+
ape_res = []
|
| 547 |
+
try:
|
| 548 |
+
ape_res = run_ape_model_inference(
|
| 549 |
+
config_file='configs/LVISCOCOCOCOSTUFF_O365_OID_VGR_SA1B_REFCOCO_GQA_PhraseCut_Flickr30k/ape_deta/ape_deta_vitl_eva02_clip_vlf_lsj1024_cp_16x4_1080k.py',
|
| 550 |
+
input_path=ovod_image_path,
|
| 551 |
+
# GPU 0
|
| 552 |
+
output_path=f'./realfse_{vlm}_{ovod}_gpu0123',
|
| 553 |
+
confidence_threshold=ape_threshold,
|
| 554 |
+
text_prompt=', '.join(all_res),
|
| 555 |
+
with_box=True,
|
| 556 |
+
with_mask=False,
|
| 557 |
+
with_sseg=False,
|
| 558 |
+
opts=[
|
| 559 |
+
"train.init_checkpoint='./ape_d_model_final.pth'",
|
| 560 |
+
"model.model_language.cache_dir=''",
|
| 561 |
+
"model.model_vision.select_box_nums_for_evaluation=500",
|
| 562 |
+
"model.model_vision.text_feature_bank_reset=True",
|
| 563 |
+
"model.model_vision.backbone.net.xattn=False",
|
| 564 |
+
"model.model_vision.transformer.encoder.pytorch_attn=True",
|
| 565 |
+
"model.model_vision.transformer.decoder.pytorch_attn=True"
|
| 566 |
+
]
|
| 567 |
+
)
|
| 568 |
+
except Exception as e:
|
| 569 |
+
print(f"Error for image {image_name}. Error: {e}")
|
| 570 |
+
|
| 571 |
+
all_ape_res.extend(enrich_ape_results(ape_res, image_name, extract_image_id))
|
| 572 |
+
# object_oovd_item = copy.deepcopy(llm_candidate[image_index])
|
| 573 |
+
# object_oovd_item['ape_result'] = all_ape_res
|
| 574 |
+
# # print(object_oovd_item)
|
| 575 |
+
# all_oovd_res[image_index] = object_oovd_item
|
| 576 |
+
|
| 577 |
+
write_json_atomic(oovd_object_bbox_path, all_ape_res)
|
| 578 |
+
|
| 579 |
+
|
| 580 |
+
os.chdir(original_path)
|
| 581 |
+
|
| 582 |
+
elif ovod.endswith('_pass'):
|
| 583 |
+
print(f'Passing OVOD {ovod} ...')
|
| 584 |
+
else:
|
| 585 |
+
raise Exception('Unrecognized OVOD!')
|
| 586 |
+
|
| 587 |
+
logging.info(f'STEP #2 OVOD {ovod} analysis completed at %s', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
|
| 588 |
+
logging.info(f'STEP #2 OVOD {ovod} analysis total time taken: {time.time() - ovod_start_time} seconds')
|
| 589 |
+
|
| 590 |
+
# icse & fse
|
| 591 |
+
def extract_image_id(image_name):
|
| 592 |
+
# Split the image name into parts and form the image_id accordingly
|
| 593 |
+
parts = image_name.split('_')
|
| 594 |
+
print(image_name)
|
| 595 |
+
base, extension = parts[1].split('.')
|
| 596 |
+
return int(parts[0] + base.zfill(3))
|
| 597 |
+
# icse_rebuttal
|
| 598 |
+
# def extract_image_id(image_name):
|
| 599 |
+
# # Split the image name into parts by underscore
|
| 600 |
+
# parts = image_name.split('_')
|
| 601 |
+
# # Extracting the first part as the base and the numeric portion of the third part before the file extension
|
| 602 |
+
# base = parts[0] # This will give '625470'
|
| 603 |
+
# numeric_part = parts[2].split('.')[0] # This will give 'b3'
|
| 604 |
+
# # Removing non-numeric characters from 'b3'
|
| 605 |
+
# numeric_part = ''.join(filter(str.isdigit, numeric_part))
|
| 606 |
+
# # Zfill is used to ensure the numeric part has at least 3 digits, then combining with base
|
| 607 |
+
# print(int(base + numeric_part.zfill(3)))
|
| 608 |
+
# return int(base + numeric_part.zfill(3))
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
def main():
|
| 612 |
+
vlms = ['llava7b', 'bing', 'gpt4v']
|
| 613 |
+
llms = ['llama2', 'gpt_3.5_turbo']
|
| 614 |
+
ovods = ['grounding_dino', 'glip', 'ape_d']
|
| 615 |
+
|
| 616 |
+
|
| 617 |
+
# python method.py -v gpt4v -l gpt_3.5_turbo -o ape_d
|
| 618 |
+
# python method.py -v gpt4v -l gpt_3.5_turbo -o ape_d_abl
|
| 619 |
+
# python method.py -v claude35sonnet -l gpt_3.5_turbo -o ape_d
|
| 620 |
+
# python method.py -v gemini -l gpt_3.5_turbo -o ape_d
|
| 621 |
+
if __name__=='__main__':
|
| 622 |
+
# main()
|
| 623 |
+
|
| 624 |
+
parser = argparse.ArgumentParser("Method", add_help=True)
|
| 625 |
+
parser.add_argument("--vlm", "-v", type=str, required=True, help="vlm")
|
| 626 |
+
parser.add_argument("--llm", "-l", type=str, required=True, help="llm")
|
| 627 |
+
parser.add_argument("--ovod", "-o", type=str, required=True, help="ovod")
|
| 628 |
+
parser.add_argument("--start-index", type=int, default=None, help="first JSONL row to process")
|
| 629 |
+
parser.add_argument("--end-index", type=int, default=None, help="exclusive JSONL row end")
|
| 630 |
+
parser.add_argument("--shard-index", type=int, default=None, help="zero-based shard index")
|
| 631 |
+
parser.add_argument("--num-shards", type=int, default=None, help="total number of shards")
|
| 632 |
+
args = parser.parse_args()
|
| 633 |
+
|
| 634 |
+
gpu = '3'
|
| 635 |
+
stage = '2'
|
| 636 |
+
idx = '0001'
|
| 637 |
+
logging.basicConfig(format='%(asctime)s - %(levelname)s - %(name)s - %(message)s',
|
| 638 |
+
datefmt='%m/%d/%Y %H:%M:%S',
|
| 639 |
+
level=logging.INFO,
|
| 640 |
+
# gpu
|
| 641 |
+
filename=f'../log/realfse/240910_{args.vlm}_{stage}_g{gpu}_{idx}.log'
|
| 642 |
+
)
|
| 643 |
+
|
| 644 |
+
method(
|
| 645 |
+
args.vlm,
|
| 646 |
+
args.llm,
|
| 647 |
+
args.ovod,
|
| 648 |
+
start_index=args.start_index,
|
| 649 |
+
end_index=args.end_index,
|
| 650 |
+
shard_index=args.shard_index,
|
| 651 |
+
num_shards=args.num_shards,
|
| 652 |
+
)
|
| 653 |
+
|
| 654 |
+
# CUDA_VISIBLE_DEVICES=0 python method.py -v gpt4v -l gpt_3.5_turbo -o ape_d > ../log/realfse/240910_gpt4v_2_g0_0001.txt
|
| 655 |
+
# CUDA_VISIBLE_DEVICES=3 python method_gemini.py -v gemini15pro -l gpt_3.5_turbo -o ape_d > ../log/realfse/240910_gemini15pro_2_g3_0001.txt
|
approach/providers.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from dataclasses import dataclass
|
| 3 |
+
from urllib import request
|
| 4 |
+
from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, Union
|
| 5 |
+
|
| 6 |
+
from approach.config import ProviderConfig
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
PostFn = Callable[..., Any]
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@dataclass(frozen=True)
|
| 13 |
+
class EncodedImage:
|
| 14 |
+
data: str
|
| 15 |
+
media_type: str = "image/jpeg"
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class UrlLibResponse:
|
| 19 |
+
def __init__(self, status: int, body: bytes):
|
| 20 |
+
self.status_code = status
|
| 21 |
+
self._body = body
|
| 22 |
+
|
| 23 |
+
def raise_for_status(self):
|
| 24 |
+
if self.status_code >= 400:
|
| 25 |
+
raise RuntimeError(f"HTTP request failed with status {self.status_code}")
|
| 26 |
+
|
| 27 |
+
def json(self):
|
| 28 |
+
return json.loads(self._body.decode("utf-8"))
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def default_post(url: str, headers: Mapping[str, str], json: Mapping[str, Any], timeout: int):
|
| 32 |
+
req = request.Request(
|
| 33 |
+
url,
|
| 34 |
+
data=json_dumps_bytes(json),
|
| 35 |
+
headers=dict(headers),
|
| 36 |
+
method="POST",
|
| 37 |
+
)
|
| 38 |
+
with request.urlopen(req, timeout=timeout) as response:
|
| 39 |
+
return UrlLibResponse(response.status, response.read())
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def json_dumps_bytes(payload: Mapping[str, Any]) -> bytes:
|
| 43 |
+
return json.dumps(payload).encode("utf-8")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def make_multimodal_content(
|
| 47 |
+
text: str,
|
| 48 |
+
image_b64s: Optional[Iterable[Union[str, EncodedImage]]] = None,
|
| 49 |
+
) -> List[Dict[str, Any]]:
|
| 50 |
+
content = [{"type": "text", "text": text}]
|
| 51 |
+
for image in image_b64s or []:
|
| 52 |
+
encoded = image if isinstance(image, EncodedImage) else EncodedImage(data=image)
|
| 53 |
+
content.append(
|
| 54 |
+
{
|
| 55 |
+
"type": "image_url",
|
| 56 |
+
"image_url": {
|
| 57 |
+
"url": f"data:{encoded.media_type};base64,{encoded.data}"
|
| 58 |
+
},
|
| 59 |
+
}
|
| 60 |
+
)
|
| 61 |
+
return content
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class OpenAICompatibleChatClient:
|
| 65 |
+
def __init__(self, profile: ProviderConfig, post: Optional[PostFn] = None):
|
| 66 |
+
self.profile = profile
|
| 67 |
+
self.post = post or default_post
|
| 68 |
+
|
| 69 |
+
def build_payload(
|
| 70 |
+
self,
|
| 71 |
+
prompt: str,
|
| 72 |
+
image_b64s: Optional[Iterable[Union[str, EncodedImage]]] = None,
|
| 73 |
+
response_format: Optional[Mapping[str, Any]] = None,
|
| 74 |
+
temperature: float = 0,
|
| 75 |
+
max_tokens: int = 4096,
|
| 76 |
+
) -> Dict[str, Any]:
|
| 77 |
+
return self.build_messages_payload(
|
| 78 |
+
[
|
| 79 |
+
{
|
| 80 |
+
"role": "user",
|
| 81 |
+
"content": make_multimodal_content(prompt, image_b64s),
|
| 82 |
+
}
|
| 83 |
+
],
|
| 84 |
+
response_format=response_format,
|
| 85 |
+
temperature=temperature,
|
| 86 |
+
max_tokens=max_tokens,
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
def build_messages_payload(
|
| 90 |
+
self,
|
| 91 |
+
messages: Iterable[Mapping[str, Any]],
|
| 92 |
+
response_format: Optional[Mapping[str, Any]] = None,
|
| 93 |
+
temperature: float = 0,
|
| 94 |
+
max_tokens: int = 4096,
|
| 95 |
+
) -> Dict[str, Any]:
|
| 96 |
+
payload: Dict[str, Any] = {
|
| 97 |
+
"model": self.profile.model,
|
| 98 |
+
"temperature": temperature,
|
| 99 |
+
"messages": [dict(message) for message in messages],
|
| 100 |
+
"max_tokens": max_tokens,
|
| 101 |
+
}
|
| 102 |
+
if response_format:
|
| 103 |
+
payload["response_format"] = dict(response_format)
|
| 104 |
+
if self.profile.router_options:
|
| 105 |
+
payload["provider"] = dict(self.profile.router_options)
|
| 106 |
+
return payload
|
| 107 |
+
|
| 108 |
+
def chat_completion(self, payload: Mapping[str, Any]) -> Mapping[str, Any]:
|
| 109 |
+
if not self.profile.api_key:
|
| 110 |
+
raise RuntimeError(f"Missing API key in environment variable {self.profile.api_key_env}")
|
| 111 |
+
headers = {
|
| 112 |
+
"Content-Type": "application/json",
|
| 113 |
+
"Authorization": f"Bearer {self.profile.api_key}",
|
| 114 |
+
}
|
| 115 |
+
response = self.post(
|
| 116 |
+
f"{self.profile.base_url.rstrip('/')}/chat/completions",
|
| 117 |
+
headers=headers,
|
| 118 |
+
json=dict(payload),
|
| 119 |
+
timeout=120,
|
| 120 |
+
)
|
| 121 |
+
response.raise_for_status()
|
| 122 |
+
return response.json()
|
| 123 |
+
|
| 124 |
+
def complete_json(
|
| 125 |
+
self,
|
| 126 |
+
prompt: str,
|
| 127 |
+
image_b64s: Optional[Iterable[Union[str, EncodedImage]]] = None,
|
| 128 |
+
response_format: Optional[Mapping[str, Any]] = None,
|
| 129 |
+
) -> Any:
|
| 130 |
+
payload = self.build_payload(prompt, image_b64s, response_format=response_format)
|
| 131 |
+
return self.complete_json_payload(payload)
|
| 132 |
+
|
| 133 |
+
def complete_json_messages(
|
| 134 |
+
self,
|
| 135 |
+
messages: Iterable[Mapping[str, Any]],
|
| 136 |
+
response_format: Optional[Mapping[str, Any]] = None,
|
| 137 |
+
) -> Any:
|
| 138 |
+
payload = self.build_messages_payload(
|
| 139 |
+
messages,
|
| 140 |
+
response_format=response_format,
|
| 141 |
+
)
|
| 142 |
+
return self.complete_json_payload(payload)
|
| 143 |
+
|
| 144 |
+
def complete_json_payload(self, payload: Mapping[str, Any]) -> Any:
|
| 145 |
+
response = self.chat_completion(payload)
|
| 146 |
+
content = response["choices"][0]["message"]["content"].strip()
|
| 147 |
+
if content.startswith("```"):
|
| 148 |
+
content = content.strip("`").split("\n", 1)[-1]
|
| 149 |
+
return json.loads(content)
|
approach/reflection.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import io
|
| 2 |
+
import base64
|
| 3 |
+
import json
|
| 4 |
+
import math
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
from typing import Callable, Dict, Iterable, List, Sequence
|
| 7 |
+
|
| 8 |
+
from PIL import Image, ImageDraw
|
| 9 |
+
|
| 10 |
+
from approach.config import get_model_profile
|
| 11 |
+
from approach.providers import EncodedImage, OpenAICompatibleChatClient
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
Detection = Dict[str, object]
|
| 15 |
+
Advisor = Callable[..., Dict[str, object]]
|
| 16 |
+
Miner = Callable[[List[Dict[str, object]]], Sequence[str]]
|
| 17 |
+
Detector = Callable[[Sequence[str], List[Detection]], List[Detection]]
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
PII5_PROMPT = (
|
| 21 |
+
"PII.5 Mirroring-Based Regional Verification. Compare the original VR scene "
|
| 22 |
+
"and the cropped detected element. Verify whether the crop is a correct "
|
| 23 |
+
"user-interactable game element and whether it violates XR spatial constraints."
|
| 24 |
+
)
|
| 25 |
+
PII6_PROMPT = (
|
| 26 |
+
"PII.6 Unsuccessful Detection Reflection. Inspect the original scene with "
|
| 27 |
+
"current detection boxes and identify missing user-interactable CDs."
|
| 28 |
+
)
|
| 29 |
+
PII7_PROMPT = (
|
| 30 |
+
"PII.7 Advisor Feedback. Return JSON with verified, needs_refinement, and feedback. "
|
| 31 |
+
"Each feedback item must be a concise new or revised CD referring expression that "
|
| 32 |
+
"the detector can ground in the original scene."
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
@dataclass(frozen=True)
|
| 37 |
+
class VerificationInput:
|
| 38 |
+
prompt: str
|
| 39 |
+
original_image: Image.Image
|
| 40 |
+
crop_image: Image.Image
|
| 41 |
+
detection: Detection
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def crop_detection(image: Image.Image, bbox: Sequence[float]) -> Image.Image:
|
| 45 |
+
if len(bbox) != 4:
|
| 46 |
+
raise ValueError("bbox must contain [x, y, width, height]")
|
| 47 |
+
x, y, width, height = bbox
|
| 48 |
+
left = max(0, math.floor(x))
|
| 49 |
+
top = max(0, math.floor(y))
|
| 50 |
+
right = min(image.width, math.ceil(x + width))
|
| 51 |
+
bottom = min(image.height, math.ceil(y + height))
|
| 52 |
+
if right <= left or bottom <= top:
|
| 53 |
+
raise ValueError(f"invalid bbox after clipping: {bbox}")
|
| 54 |
+
return image.crop((left, top, right, bottom))
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def make_successful_detection_request(image: Image.Image, detection: Detection) -> VerificationInput:
|
| 58 |
+
return VerificationInput(
|
| 59 |
+
prompt=PII5_PROMPT,
|
| 60 |
+
original_image=image,
|
| 61 |
+
crop_image=crop_detection(image, detection["bbox"]),
|
| 62 |
+
detection=detection,
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def draw_detections(image: Image.Image, detections: Iterable[Detection]) -> Image.Image:
|
| 67 |
+
annotated = image.copy()
|
| 68 |
+
draw = ImageDraw.Draw(annotated)
|
| 69 |
+
for idx, detection in enumerate(detections, start=1):
|
| 70 |
+
x, y, width, height = detection["bbox"]
|
| 71 |
+
box = (x, y, x + width, y + height)
|
| 72 |
+
draw.rectangle(box, outline="red", width=2)
|
| 73 |
+
draw.text((x, y), str(detection.get("category_name", idx)), fill="red")
|
| 74 |
+
return annotated
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def image_to_png_bytes(image: Image.Image) -> bytes:
|
| 78 |
+
buffer = io.BytesIO()
|
| 79 |
+
image.save(buffer, format="PNG")
|
| 80 |
+
return buffer.getvalue()
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def image_to_base64(image: Image.Image) -> str:
|
| 84 |
+
return base64.b64encode(image_to_png_bytes(image)).decode("utf-8")
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
REFLECTION_RESPONSE_FORMAT = {
|
| 88 |
+
"type": "json_schema",
|
| 89 |
+
"json_schema": {
|
| 90 |
+
"name": "orienter_reflection",
|
| 91 |
+
"strict": True,
|
| 92 |
+
"schema": {
|
| 93 |
+
"type": "object",
|
| 94 |
+
"properties": {
|
| 95 |
+
"verified": {"type": "array", "items": {"type": "integer"}},
|
| 96 |
+
"needs_refinement": {"type": "array", "items": {"type": "integer"}},
|
| 97 |
+
"feedback": {"type": "array", "items": {"type": "string"}},
|
| 98 |
+
},
|
| 99 |
+
"required": ["verified", "needs_refinement", "feedback"],
|
| 100 |
+
"additionalProperties": False,
|
| 101 |
+
},
|
| 102 |
+
},
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def build_reflection_prompt(
|
| 107 |
+
verification_requests: Sequence[VerificationInput],
|
| 108 |
+
pii5_prompt: str,
|
| 109 |
+
pii6_prompt: str,
|
| 110 |
+
pii7_prompt: str,
|
| 111 |
+
) -> str:
|
| 112 |
+
detections = [
|
| 113 |
+
{
|
| 114 |
+
"index": idx,
|
| 115 |
+
"bbox": request.detection.get("bbox"),
|
| 116 |
+
"category_name": request.detection.get("category_name"),
|
| 117 |
+
"score": request.detection.get("score", request.detection.get("probability")),
|
| 118 |
+
}
|
| 119 |
+
for idx, request in enumerate(verification_requests)
|
| 120 |
+
]
|
| 121 |
+
return (
|
| 122 |
+
f"{pii5_prompt}\n"
|
| 123 |
+
"For each detected CD, compare the original scene image with its cropped region image. "
|
| 124 |
+
"Mark false positives or boxes that do not match the intended interactable element as needs_refinement.\n\n"
|
| 125 |
+
f"{pii6_prompt}\n"
|
| 126 |
+
"Also inspect the annotated full-scene image for missing interactable CDs or wrong boxes.\n\n"
|
| 127 |
+
f"{pii7_prompt}\n"
|
| 128 |
+
"Return only JSON. Images are ordered as: (1) the boxed full scene for PII.6, "
|
| 129 |
+
"then (2) one original-scene and bbox-crop pair per detection, in detection-index order.\n\n"
|
| 130 |
+
f"Detections:\n{json.dumps(detections, ensure_ascii=True, indent=2)}"
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def openai_compatible_advisor(model_profile: str = "default") -> Advisor:
|
| 135 |
+
profile = get_model_profile(model_profile)
|
| 136 |
+
client = OpenAICompatibleChatClient(profile)
|
| 137 |
+
|
| 138 |
+
def advisor(**kwargs) -> Dict[str, object]:
|
| 139 |
+
verification_requests = kwargs["verification_requests"]
|
| 140 |
+
unsuccessful_detection_image = kwargs["unsuccessful_detection_image"]
|
| 141 |
+
image_b64s = [
|
| 142 |
+
EncodedImage(image_to_base64(unsuccessful_detection_image), "image/png")
|
| 143 |
+
]
|
| 144 |
+
for request in verification_requests:
|
| 145 |
+
image_b64s.append(
|
| 146 |
+
EncodedImage(image_to_base64(request.original_image), "image/png")
|
| 147 |
+
)
|
| 148 |
+
image_b64s.append(
|
| 149 |
+
EncodedImage(image_to_base64(request.crop_image), "image/png")
|
| 150 |
+
)
|
| 151 |
+
prompt = build_reflection_prompt(
|
| 152 |
+
verification_requests,
|
| 153 |
+
kwargs["pii5_prompt"],
|
| 154 |
+
kwargs["pii6_prompt"],
|
| 155 |
+
kwargs["pii7_prompt"],
|
| 156 |
+
)
|
| 157 |
+
return client.complete_json(
|
| 158 |
+
prompt,
|
| 159 |
+
image_b64s,
|
| 160 |
+
response_format=REFLECTION_RESPONSE_FORMAT,
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
return advisor
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def run_reflection_loop(
|
| 167 |
+
image: Image.Image,
|
| 168 |
+
detections: List[Detection],
|
| 169 |
+
miner: Miner,
|
| 170 |
+
detector: Detector,
|
| 171 |
+
advisor: Advisor,
|
| 172 |
+
max_iterations: int = 10,
|
| 173 |
+
) -> Dict[str, object]:
|
| 174 |
+
if max_iterations <= 0:
|
| 175 |
+
raise ValueError("max_iterations must be positive")
|
| 176 |
+
|
| 177 |
+
current_detections = list(detections)
|
| 178 |
+
trace = []
|
| 179 |
+
max_reached = False
|
| 180 |
+
|
| 181 |
+
for iteration in range(1, max_iterations + 1):
|
| 182 |
+
verification_requests = [
|
| 183 |
+
make_successful_detection_request(image, detection)
|
| 184 |
+
for detection in current_detections
|
| 185 |
+
]
|
| 186 |
+
unsuccessful_scene = draw_detections(image, current_detections)
|
| 187 |
+
advice = advisor(
|
| 188 |
+
verification_requests=verification_requests,
|
| 189 |
+
unsuccessful_detection_image=unsuccessful_scene,
|
| 190 |
+
pii5_prompt=PII5_PROMPT,
|
| 191 |
+
pii6_prompt=PII6_PROMPT,
|
| 192 |
+
pii7_prompt=PII7_PROMPT,
|
| 193 |
+
)
|
| 194 |
+
trace.append(
|
| 195 |
+
{
|
| 196 |
+
"iteration": iteration,
|
| 197 |
+
"verified": advice.get("verified", []),
|
| 198 |
+
"needs_refinement": advice.get("needs_refinement", []),
|
| 199 |
+
"feedback": advice.get("feedback", []),
|
| 200 |
+
}
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
feedback = advice.get("feedback", [])
|
| 204 |
+
needs_refinement = advice.get("needs_refinement", [])
|
| 205 |
+
if not feedback and not needs_refinement:
|
| 206 |
+
break
|
| 207 |
+
|
| 208 |
+
rejected_indices = {
|
| 209 |
+
index
|
| 210 |
+
for index in needs_refinement
|
| 211 |
+
if isinstance(index, int) and 0 <= index < len(current_detections)
|
| 212 |
+
}
|
| 213 |
+
retained_detections = [
|
| 214 |
+
detection
|
| 215 |
+
for index, detection in enumerate(current_detections)
|
| 216 |
+
if index not in rejected_indices
|
| 217 |
+
]
|
| 218 |
+
candidates = list(miner(trace))
|
| 219 |
+
current_detections = detector(candidates, retained_detections)
|
| 220 |
+
else:
|
| 221 |
+
max_reached = True
|
| 222 |
+
|
| 223 |
+
return {
|
| 224 |
+
"detections": current_detections,
|
| 225 |
+
"trace": trace,
|
| 226 |
+
"max_iterations_reached": max_reached,
|
| 227 |
+
}
|
approach/requirements.txt
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
addict==2.4.0
|
| 2 |
+
aiohttp==3.8.6
|
| 3 |
+
APScheduler==3.10.0
|
| 4 |
+
av==11.0.0
|
| 5 |
+
beautifulsoup4==4.12.2
|
| 6 |
+
BingImageCreator==0.5.0
|
| 7 |
+
black==23.12.1
|
| 8 |
+
caffe2==0.8.1
|
| 9 |
+
certifi==2023.7.22
|
| 10 |
+
cityscapesscripts==2.2.2
|
| 11 |
+
click==8.0.1
|
| 12 |
+
clip==0.2.0
|
| 13 |
+
cloudpickle==3.0.0
|
| 14 |
+
deepspeed==0.9.5
|
| 15 |
+
docutils==0.20.1
|
| 16 |
+
einops==0.7.0
|
| 17 |
+
emoji==2.9.0
|
| 18 |
+
fairscale==0.4.13
|
| 19 |
+
fastapi==0.108.0
|
| 20 |
+
fiftyone==0.23.2
|
| 21 |
+
fire==0.5.0
|
| 22 |
+
flash_attn==2.4.2
|
| 23 |
+
Flask==2.1.1
|
| 24 |
+
fvcore==0.1.5.post20221221
|
| 25 |
+
gradio==4.12.0
|
| 26 |
+
gunicorn==19.9.0
|
| 27 |
+
httpx==0.26.0
|
| 28 |
+
huggingface_hub==0.17.3
|
| 29 |
+
hydra-core==1.3.2
|
| 30 |
+
iopath==0.1.10
|
| 31 |
+
ipdb==0.13.13
|
| 32 |
+
lxml==4.9.2
|
| 33 |
+
matplotlib==3.8.0
|
| 34 |
+
mmcv==2.1.0
|
| 35 |
+
mmdet==3.2.0
|
| 36 |
+
nltk==3.8.1
|
| 37 |
+
numpy==1.26.2
|
| 38 |
+
omegaconf==2.3.0
|
| 39 |
+
onnx==1.15.0
|
| 40 |
+
openai==0.28.1
|
| 41 |
+
pandas==2.1.4
|
| 42 |
+
peft==0.4.0
|
| 43 |
+
Pillow==10.0.1
|
| 44 |
+
Pillow==10.1.0
|
| 45 |
+
prompt_toolkit==3.0.43
|
| 46 |
+
psutil==5.9.5
|
| 47 |
+
pycocotools==2.0.7
|
| 48 |
+
Pygments==2.16.1
|
| 49 |
+
pytorch_lightning==2.1.3
|
| 50 |
+
PyYAML==6.0.1
|
| 51 |
+
PyYAML==6.0.1
|
| 52 |
+
ray==2.9.0
|
| 53 |
+
recommonmark==0.7.1
|
| 54 |
+
redis==3.5.3
|
| 55 |
+
Requests==2.31.0
|
| 56 |
+
rich==13.7.0
|
| 57 |
+
scipy==1.11.4
|
| 58 |
+
selenium==4.16.0
|
| 59 |
+
setuptools==68.0.0
|
| 60 |
+
Shapely==2.0.2
|
| 61 |
+
shortuuid==1.0.11
|
| 62 |
+
slack_sdk==3.26.1
|
| 63 |
+
Sphinx==7.2.6
|
| 64 |
+
sphinx_rtd_theme==2.0.0
|
| 65 |
+
supervision==0.17.1
|
| 66 |
+
tabulate==0.9.0
|
| 67 |
+
termcolor==2.4.0
|
| 68 |
+
torch==2.0.1
|
| 69 |
+
torchvision==0.15.2
|
| 70 |
+
tqdm==4.66.1
|
| 71 |
+
transformers==4.31.0
|
| 72 |
+
typer==0.9.0
|
| 73 |
+
typing_extensions==4.9.0
|
| 74 |
+
uvicorn==0.25.0
|
| 75 |
+
websockets==11.0.3
|
| 76 |
+
Werkzeug==2.1.0
|
| 77 |
+
yapf==0.40.2
|
| 78 |
+
timm
|
| 79 |
+
opencv-python
|
approach/run_vlm.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Configurable screenshot-context mining entrypoint for Orienter."""
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
import os
|
| 6 |
+
import sys
|
| 7 |
+
import tempfile
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
if __package__ in {None, ""}:
|
| 11 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
| 12 |
+
|
| 13 |
+
from approach.config import get_model_profile
|
| 14 |
+
from approach.pipeline_utils import (
|
| 15 |
+
output_path_for_selection,
|
| 16 |
+
resolve_image_path,
|
| 17 |
+
select_jsonl_lines,
|
| 18 |
+
write_json_atomic,
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def _load_jsonl(path: Path):
|
| 23 |
+
if not path.exists():
|
| 24 |
+
return []
|
| 25 |
+
with path.open(encoding="utf-8") as file:
|
| 26 |
+
return [json.loads(line) for line in file if line.strip()]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _write_jsonl_atomic(path: Path, records):
|
| 30 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 31 |
+
descriptor, temporary = tempfile.mkstemp(
|
| 32 |
+
prefix=".orienter-",
|
| 33 |
+
suffix=".jsonl.tmp",
|
| 34 |
+
dir=path.parent,
|
| 35 |
+
)
|
| 36 |
+
try:
|
| 37 |
+
with os.fdopen(descriptor, "w", encoding="utf-8") as file:
|
| 38 |
+
for record in records:
|
| 39 |
+
file.write(json.dumps(record, ensure_ascii=False) + "\n")
|
| 40 |
+
os.replace(temporary, path)
|
| 41 |
+
except Exception:
|
| 42 |
+
if os.path.exists(temporary):
|
| 43 |
+
os.unlink(temporary)
|
| 44 |
+
raise
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def build_parser():
|
| 48 |
+
parser = argparse.ArgumentParser(description=__doc__)
|
| 49 |
+
parser.add_argument("--questions", type=Path, required=True)
|
| 50 |
+
parser.add_argument("--images-dir", type=Path, required=True)
|
| 51 |
+
parser.add_argument("--output", type=Path, required=True)
|
| 52 |
+
parser.add_argument("--profile", default="default")
|
| 53 |
+
parser.add_argument(
|
| 54 |
+
"--app-metadata-cache",
|
| 55 |
+
type=Path,
|
| 56 |
+
default=(
|
| 57 |
+
Path(os.environ["ORIENTER_APP_METADATA_CACHE"])
|
| 58 |
+
if os.environ.get("ORIENTER_APP_METADATA_CACHE")
|
| 59 |
+
else None
|
| 60 |
+
),
|
| 61 |
+
help="optional JSON/JSONL app metadata cache keyed by app_id; missing app IDs fail closed",
|
| 62 |
+
)
|
| 63 |
+
parser.add_argument("--ablation", action="store_true")
|
| 64 |
+
parser.add_argument("--start-index", type=int)
|
| 65 |
+
parser.add_argument("--end-index", type=int)
|
| 66 |
+
parser.add_argument("--shard-index", type=int)
|
| 67 |
+
parser.add_argument("--num-shards", type=int)
|
| 68 |
+
parser.add_argument("--resume", action="store_true")
|
| 69 |
+
return parser
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def run(args, processor=None):
|
| 73 |
+
app_metadata_cache = None
|
| 74 |
+
uses_default_processor = processor is None
|
| 75 |
+
if processor is None:
|
| 76 |
+
from approach.vlm.gpt4v.gpt4v import load_app_metadata_cache, process_image
|
| 77 |
+
|
| 78 |
+
app_metadata_cache = load_app_metadata_cache(args.app_metadata_cache)
|
| 79 |
+
processor = process_image
|
| 80 |
+
|
| 81 |
+
questions = _load_jsonl(args.questions)
|
| 82 |
+
selected = select_jsonl_lines(
|
| 83 |
+
questions,
|
| 84 |
+
start_index=args.start_index,
|
| 85 |
+
end_index=args.end_index,
|
| 86 |
+
shard_index=args.shard_index,
|
| 87 |
+
num_shards=args.num_shards,
|
| 88 |
+
)
|
| 89 |
+
output = Path(
|
| 90 |
+
output_path_for_selection(
|
| 91 |
+
str(args.output),
|
| 92 |
+
start_index=args.start_index,
|
| 93 |
+
end_index=args.end_index,
|
| 94 |
+
shard_index=args.shard_index,
|
| 95 |
+
num_shards=args.num_shards,
|
| 96 |
+
)
|
| 97 |
+
)
|
| 98 |
+
answers = _load_jsonl(output) if args.resume else []
|
| 99 |
+
completed = {answer["question_id"] for answer in answers}
|
| 100 |
+
errors = []
|
| 101 |
+
profile = get_model_profile(args.profile)
|
| 102 |
+
_write_jsonl_atomic(output, answers)
|
| 103 |
+
|
| 104 |
+
for index, question in enumerate(selected):
|
| 105 |
+
question_id = question["question_id"]
|
| 106 |
+
if question_id in completed:
|
| 107 |
+
continue
|
| 108 |
+
image_path = resolve_image_path(args.images_dir, question["image"])
|
| 109 |
+
try:
|
| 110 |
+
processor_args = [
|
| 111 |
+
args.profile,
|
| 112 |
+
question.get("text", ""),
|
| 113 |
+
str(image_path),
|
| 114 |
+
args.ablation,
|
| 115 |
+
index,
|
| 116 |
+
]
|
| 117 |
+
if uses_default_processor:
|
| 118 |
+
result = processor(*processor_args, metadata_cache=app_metadata_cache)
|
| 119 |
+
else:
|
| 120 |
+
result = processor(*processor_args)
|
| 121 |
+
if not result:
|
| 122 |
+
raise RuntimeError("provider returned an empty response")
|
| 123 |
+
answers.append(
|
| 124 |
+
{
|
| 125 |
+
"question_id": question_id,
|
| 126 |
+
"prompt": question.get("text", ""),
|
| 127 |
+
"text": result,
|
| 128 |
+
"model_id": profile.model,
|
| 129 |
+
"metadata": {"profile": profile.name, "provider": profile.provider},
|
| 130 |
+
}
|
| 131 |
+
)
|
| 132 |
+
completed.add(question_id)
|
| 133 |
+
_write_jsonl_atomic(output, answers)
|
| 134 |
+
except Exception as exc:
|
| 135 |
+
errors.append(
|
| 136 |
+
{
|
| 137 |
+
"question_id": question_id,
|
| 138 |
+
"image": question["image"],
|
| 139 |
+
"error_type": type(exc).__name__,
|
| 140 |
+
"message": str(exc),
|
| 141 |
+
}
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
error_path = output.with_suffix(".errors.json")
|
| 145 |
+
write_json_atomic(str(error_path), errors)
|
| 146 |
+
return {
|
| 147 |
+
"output": str(output),
|
| 148 |
+
"records_selected": len(selected),
|
| 149 |
+
"completed": len(answers),
|
| 150 |
+
"errors": len(errors),
|
| 151 |
+
"profile": profile.name,
|
| 152 |
+
"model": profile.model,
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def main(argv=None):
|
| 157 |
+
args = build_parser().parse_args(argv)
|
| 158 |
+
report = run(args)
|
| 159 |
+
print(json.dumps(report, indent=2))
|
| 160 |
+
return 1 if report["errors"] else 0
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
if __name__ == "__main__":
|
| 164 |
+
raise SystemExit(main())
|
dataset/2detection.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import argparse
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
parser = argparse.ArgumentParser()
|
| 6 |
+
parser.add_argument('--input', type=str, required=True)
|
| 7 |
+
parser.add_argument('--output', type=str, required=True)
|
| 8 |
+
args = parser.parse_args()
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
if __name__ == '__main__':
|
| 12 |
+
with open(args.input, 'r') as f:
|
| 13 |
+
data = json.load(f)
|
| 14 |
+
|
| 15 |
+
for ann in data['annotations']:
|
| 16 |
+
ann.pop('segmentation')
|
| 17 |
+
ann['iscrowd'] = 0
|
| 18 |
+
|
| 19 |
+
os.makedirs(os.path.dirname(args.output), exist_ok=True)
|
| 20 |
+
with open(args.output, 'w') as f:
|
| 21 |
+
json.dump(data, f)
|
dataset/2polyseg.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import argparse
|
| 3 |
+
import numpy as np
|
| 4 |
+
import cv2
|
| 5 |
+
import json
|
| 6 |
+
from tqdm import tqdm
|
| 7 |
+
import pycocotools.mask as mask_util
|
| 8 |
+
|
| 9 |
+
parser = argparse.ArgumentParser()
|
| 10 |
+
parser.add_argument('--input', type=str, required=True)
|
| 11 |
+
parser.add_argument('--output', type=str, required=True)
|
| 12 |
+
args = parser.parse_args()
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def rle_to_polygon(rle) -> (list, list, float):
|
| 16 |
+
mask = mask_util.decode(rle)
|
| 17 |
+
mask = np.where(mask > 0.5, 1, 0)
|
| 18 |
+
mask = np.ascontiguousarray(mask, dtype=np.uint8)
|
| 19 |
+
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)
|
| 20 |
+
polygon = []
|
| 21 |
+
segs = []
|
| 22 |
+
contours = [contour for contour in contours if contour.size >= 6]
|
| 23 |
+
for contour in contours:
|
| 24 |
+
_contour = contour.flatten().tolist()
|
| 25 |
+
_contour.append(_contour[0])
|
| 26 |
+
_contour.append(_contour[1])
|
| 27 |
+
polygon.extend(_contour)
|
| 28 |
+
segs.append(contour.flatten().tolist())
|
| 29 |
+
RLEs = mask_util.frPyObjects(segs, mask.shape[0], mask.shape[1])
|
| 30 |
+
RLE = mask_util.merge(RLEs)
|
| 31 |
+
area = float(mask_util.area(RLE))
|
| 32 |
+
return polygon, area
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def main(args):
|
| 36 |
+
with open(args.input, 'r') as f:
|
| 37 |
+
ann_data = json.load(f)
|
| 38 |
+
for ann in tqdm(ann_data['annotations'], total=len(ann_data['annotations'])):
|
| 39 |
+
rle_data = ann['segmentation']
|
| 40 |
+
# encodedRLE = mask_util.frPyObjects(rle_data, rle_data['size'][0], rle_data['size'][1])
|
| 41 |
+
# polygon, area = rle_to_polygon(encodedRLE)
|
| 42 |
+
polygon, area = rle_to_polygon(rle_data)
|
| 43 |
+
ann['segmentation'] = [polygon]
|
| 44 |
+
ann['area'] = area
|
| 45 |
+
ann['iscrowd'] = 0
|
| 46 |
+
os.makedirs(os.path.dirname(args.output), exist_ok=True)
|
| 47 |
+
with open(args.output, 'w') as f:
|
| 48 |
+
json.dump(ann_data, f)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
if __name__ == '__main__':
|
| 52 |
+
main(args)
|
dataset/clean.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# clean up the merged COCO dataset
|
| 2 |
+
from os.path import join as pjoin
|
| 3 |
+
import json
|
| 4 |
+
import pycocotools.mask as mask_util
|
| 5 |
+
|
| 6 |
+
ROOT = 'data/coco_merged/annotations'
|
| 7 |
+
JSONS = ['interaction.json', 'interactable.json', 'semantics.json']
|
| 8 |
+
|
| 9 |
+
# filter noise produced by labeling tool, denote mask in compressed RLE format
|
| 10 |
+
def fix_mask(data):
|
| 11 |
+
for anno in data['annotations']:
|
| 12 |
+
mask = mask_util.frPyObjects(anno['segmentation'], 540, 960)
|
| 13 |
+
mask = mask_util.decode(mask)
|
| 14 |
+
mask[:, -1] = mask[:, -2]
|
| 15 |
+
mask[-1:, :] = mask[-2, :]
|
| 16 |
+
RLE = mask_util.encode(mask)
|
| 17 |
+
bbox = mask_util.toBbox(RLE)
|
| 18 |
+
bbox = bbox.astype(int)
|
| 19 |
+
area = int(bbox[2] * bbox[3])
|
| 20 |
+
RLE['counts'] = RLE['counts'].decode('utf-8')
|
| 21 |
+
anno['segmentation'] = RLE
|
| 22 |
+
anno['bbox'] = bbox.tolist()
|
| 23 |
+
anno['area'] = area
|
| 24 |
+
|
| 25 |
+
# remove redundant annotations
|
| 26 |
+
def remove_redundant(data):
|
| 27 |
+
total = len(data['annotations'])
|
| 28 |
+
anno_list = []
|
| 29 |
+
|
| 30 |
+
for anno in data['annotations']:
|
| 31 |
+
anno['id']=0
|
| 32 |
+
if anno not in anno_list:
|
| 33 |
+
anno_list.append(anno)
|
| 34 |
+
|
| 35 |
+
for anno in anno_list:
|
| 36 |
+
anno['id'] = anno_list.index(anno) + 1
|
| 37 |
+
|
| 38 |
+
data['annotations'] = anno_list
|
| 39 |
+
print(f'{total - len(anno_list)} redundant annotations removed')
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def main():
|
| 43 |
+
json_files = [pjoin(ROOT, j) for j in JSONS]
|
| 44 |
+
for j in json_files:
|
| 45 |
+
with open(j, 'r') as f:
|
| 46 |
+
data = json.load(f)
|
| 47 |
+
fix_mask(data)
|
| 48 |
+
remove_redundant(data)
|
| 49 |
+
new_j = j.replace('.json', '_cleaned.json')
|
| 50 |
+
with open(new_j, 'w') as f:
|
| 51 |
+
json.dump(data, f, indent=4)
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
if __name__ == '__main__':
|
| 55 |
+
main()
|
dataset/create_test_set_folder.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import shutil
|
| 3 |
+
|
| 4 |
+
# Define the paths
|
| 5 |
+
image_directory = './data/coco_det/images/semantics' # The directory containing the images
|
| 6 |
+
txt_file_path = './list_union3.txt' # The text file with indexes
|
| 7 |
+
new_directory = './data/coco_det/images/union3' # The directory to copy matching images
|
| 8 |
+
|
| 9 |
+
# Create the new directory if it doesn't exist
|
| 10 |
+
if not os.path.exists(new_directory):
|
| 11 |
+
os.makedirs(new_directory)
|
| 12 |
+
|
| 13 |
+
# Load the comma-separated indexes from the text file
|
| 14 |
+
with open(txt_file_path, 'r') as file:
|
| 15 |
+
content = file.read().strip() # Read the entire file content
|
| 16 |
+
indexes = content.split(', ') # Split by commas to get a list of indexes
|
| 17 |
+
|
| 18 |
+
# Iterate over the files in the image directory
|
| 19 |
+
for image_file in os.listdir(image_directory):
|
| 20 |
+
if image_file.endswith(".jpg"):
|
| 21 |
+
# Parse the appid and index from the image filename (example: 269170_1.jpg)
|
| 22 |
+
image_appid, image_index = image_file.split("_")[0], image_file.split("_")[1].split(".")[0].zfill(3)
|
| 23 |
+
|
| 24 |
+
# Combine appid and index to match the format in the text file (example: 269170001)
|
| 25 |
+
image_key = f"{image_appid}{image_index}"
|
| 26 |
+
|
| 27 |
+
# If the constructed key matches any entry in the text file, copy the image
|
| 28 |
+
if image_key in indexes:
|
| 29 |
+
source_path = os.path.join(image_directory, image_file)
|
| 30 |
+
destination_path = os.path.join(new_directory, image_file)
|
| 31 |
+
|
| 32 |
+
# Copy the image
|
| 33 |
+
shutil.copy(source_path, destination_path)
|
| 34 |
+
print(f"Copied: {image_file}")
|
| 35 |
+
|
| 36 |
+
print("All matching images have been copied.")
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
|
dataset/dataset_process.sh
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# # obtain interactability dataset
|
| 2 |
+
# python interactable_coco.py --input ./data/coco_merged/annotations/semantics.json --output ./data/coco_merged/annotations/interactable.json
|
| 3 |
+
# ls ./data/coco_merged/images/interactable
|
| 4 |
+
# if [ $? -ne 0 ]; then
|
| 5 |
+
# cp -r ./data/coco_merged/images/semantics ./data/coco_merged/images/interactable
|
| 6 |
+
# fi
|
| 7 |
+
|
| 8 |
+
# # map interaction dataset from 61 to 54 classes
|
| 9 |
+
# python map_interaction.py
|
| 10 |
+
|
| 11 |
+
# # generate detection only annotations
|
| 12 |
+
# python 2detection.py --input ./data/coco_merged/annotations/interactable.json --output ./data/coco_det/annotations/interactable.json
|
| 13 |
+
# python 2detection.py --input ./data/coco_merged/annotations/interaction.json --output ./data/coco_det/annotations/interaction.json
|
| 14 |
+
# python 2detection.py --input ./data/coco_merged/annotations/semantics.json --output ./data/coco_det/annotations/semantics.json
|
| 15 |
+
|
| 16 |
+
# # generate correct segmentation annotations with polygon format
|
| 17 |
+
# python 2polyseg.py --input ./data/coco_merged/annotations/interactable.json --output ./data/coco_seg/annotations/interactable.json
|
| 18 |
+
# python 2polyseg.py --input ./data/coco_merged/annotations/interaction.json --output ./data/coco_seg/annotations/interaction.json
|
| 19 |
+
# python 2polyseg.py --input ./data/coco_merged/annotations/semantics.json --output ./data/coco_seg/annotations/semantics.json
|
| 20 |
+
|
| 21 |
+
# # split dataset into train
|
| 22 |
+
# export FORMAT=det
|
| 23 |
+
# export TASK=semantics
|
| 24 |
+
# python split_coco.py \
|
| 25 |
+
# --ann_file ./data/coco_$FORMAT/annotations/$TASK.json \
|
| 26 |
+
# --img_dir ./data/coco_merged/images/$TASK/ \
|
| 27 |
+
# --output_path ./data/split_$TASK/ \
|
| 28 |
+
# --fold_file './fold_app.csv' \
|
| 29 |
+
# --train_fold '0,1,4,5,7,9' \
|
| 30 |
+
# --val_fold '3' \
|
| 31 |
+
# --test_fold '2,6,8'
|
| 32 |
+
# # use --gen to generate fold_file
|
dataset/fold_app.csv
ADDED
|
@@ -0,0 +1,1553 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
img_id,fold
|
| 2 |
+
1337530001,0
|
| 3 |
+
2025000010,0
|
| 4 |
+
2025000009,0
|
| 5 |
+
2025000008,0
|
| 6 |
+
2025000007,0
|
| 7 |
+
2025000006,0
|
| 8 |
+
2025000005,0
|
| 9 |
+
2025000004,0
|
| 10 |
+
2025000003,0
|
| 11 |
+
2025000002,0
|
| 12 |
+
2025000001,0
|
| 13 |
+
1960620016,0
|
| 14 |
+
1960620015,0
|
| 15 |
+
1960620014,0
|
| 16 |
+
1960620013,0
|
| 17 |
+
1960620012,0
|
| 18 |
+
1960620011,0
|
| 19 |
+
1960620010,0
|
| 20 |
+
1960620008,0
|
| 21 |
+
1960620002,0
|
| 22 |
+
451980001,0
|
| 23 |
+
451980002,0
|
| 24 |
+
451980003,0
|
| 25 |
+
1150310001,0
|
| 26 |
+
451980018,0
|
| 27 |
+
451980017,0
|
| 28 |
+
1245640021,0
|
| 29 |
+
451980015,0
|
| 30 |
+
451980014,0
|
| 31 |
+
451980013,0
|
| 32 |
+
451980012,0
|
| 33 |
+
451980011,0
|
| 34 |
+
451980010,0
|
| 35 |
+
1960620001,0
|
| 36 |
+
451980009,0
|
| 37 |
+
451980007,0
|
| 38 |
+
451980006,0
|
| 39 |
+
451980005,0
|
| 40 |
+
451980004,0
|
| 41 |
+
1150310002,0
|
| 42 |
+
1150310003,0
|
| 43 |
+
1150310004,0
|
| 44 |
+
1150310005,0
|
| 45 |
+
1150310006,0
|
| 46 |
+
451980008,0
|
| 47 |
+
1601810001,0
|
| 48 |
+
1601810002,0
|
| 49 |
+
1601810003,0
|
| 50 |
+
1245640001,0
|
| 51 |
+
1245640002,0
|
| 52 |
+
1245640003,0
|
| 53 |
+
1245640004,0
|
| 54 |
+
1245640005,0
|
| 55 |
+
1245640006,0
|
| 56 |
+
1245640007,0
|
| 57 |
+
1245640008,0
|
| 58 |
+
1245640009,0
|
| 59 |
+
1245640038,0
|
| 60 |
+
1245640010,0
|
| 61 |
+
1245640012,0
|
| 62 |
+
1245640013,0
|
| 63 |
+
1245640014,0
|
| 64 |
+
1245640015,0
|
| 65 |
+
1245640016,0
|
| 66 |
+
1245640017,0
|
| 67 |
+
1245640018,0
|
| 68 |
+
1245640019,0
|
| 69 |
+
1245640020,0
|
| 70 |
+
1245640011,0
|
| 71 |
+
1337530082,0
|
| 72 |
+
1245640037,0
|
| 73 |
+
1217930003,0
|
| 74 |
+
1612820001,0
|
| 75 |
+
1612820002,0
|
| 76 |
+
1245640036,0
|
| 77 |
+
1245640035,0
|
| 78 |
+
1245640034,0
|
| 79 |
+
1245640033,0
|
| 80 |
+
1245640032,0
|
| 81 |
+
1245640031,0
|
| 82 |
+
1337530002,0
|
| 83 |
+
1217930004,0
|
| 84 |
+
1245640030,0
|
| 85 |
+
1245640028,0
|
| 86 |
+
1245640027,0
|
| 87 |
+
1245640026,0
|
| 88 |
+
1245640025,0
|
| 89 |
+
1245640024,0
|
| 90 |
+
1245640023,0
|
| 91 |
+
1217930005,0
|
| 92 |
+
1217930001,0
|
| 93 |
+
1217930002,0
|
| 94 |
+
1245640029,0
|
| 95 |
+
1337530081,0
|
| 96 |
+
451980016,0
|
| 97 |
+
1337530079,0
|
| 98 |
+
1337530033,0
|
| 99 |
+
1337530034,0
|
| 100 |
+
1337530035,0
|
| 101 |
+
1337530036,0
|
| 102 |
+
1337530037,0
|
| 103 |
+
1337530038,0
|
| 104 |
+
1337530039,0
|
| 105 |
+
1337530040,0
|
| 106 |
+
1337530041,0
|
| 107 |
+
1337530042,0
|
| 108 |
+
1337530043,0
|
| 109 |
+
1337530044,0
|
| 110 |
+
1337530045,0
|
| 111 |
+
1337530046,0
|
| 112 |
+
1337530047,0
|
| 113 |
+
1337530048,0
|
| 114 |
+
1337530049,0
|
| 115 |
+
1337530050,0
|
| 116 |
+
1337530051,0
|
| 117 |
+
1337530052,0
|
| 118 |
+
1337530053,0
|
| 119 |
+
1337530054,0
|
| 120 |
+
1337530055,0
|
| 121 |
+
1337530056,0
|
| 122 |
+
1337530080,0
|
| 123 |
+
1337530032,0
|
| 124 |
+
1337530031,0
|
| 125 |
+
1337530030,0
|
| 126 |
+
1337530029,0
|
| 127 |
+
1337530003,0
|
| 128 |
+
1337530004,0
|
| 129 |
+
1337530005,0
|
| 130 |
+
1337530006,0
|
| 131 |
+
1337530007,0
|
| 132 |
+
1337530008,0
|
| 133 |
+
1337530009,0
|
| 134 |
+
1337530010,0
|
| 135 |
+
1337530011,0
|
| 136 |
+
1337530012,0
|
| 137 |
+
1337530013,0
|
| 138 |
+
1337530014,0
|
| 139 |
+
660520014,0
|
| 140 |
+
1337530015,0
|
| 141 |
+
1337530017,0
|
| 142 |
+
1337530018,0
|
| 143 |
+
1337530019,0
|
| 144 |
+
1337530020,0
|
| 145 |
+
1337530021,0
|
| 146 |
+
1337530022,0
|
| 147 |
+
1337530023,0
|
| 148 |
+
1337530024,0
|
| 149 |
+
1337530025,0
|
| 150 |
+
1337530026,0
|
| 151 |
+
1337530027,0
|
| 152 |
+
1337530028,0
|
| 153 |
+
1337530016,0
|
| 154 |
+
660520013,0
|
| 155 |
+
660520015,0
|
| 156 |
+
1337530058,0
|
| 157 |
+
1337530057,0
|
| 158 |
+
1337530059,0
|
| 159 |
+
1337530060,0
|
| 160 |
+
1337530061,0
|
| 161 |
+
1337530062,0
|
| 162 |
+
1337530063,0
|
| 163 |
+
1337530064,0
|
| 164 |
+
1337530065,0
|
| 165 |
+
1337530066,0
|
| 166 |
+
1337530067,0
|
| 167 |
+
1337530068,0
|
| 168 |
+
1337530069,0
|
| 169 |
+
1337530070,0
|
| 170 |
+
1337530071,0
|
| 171 |
+
1337530072,0
|
| 172 |
+
1337530073,0
|
| 173 |
+
1337530074,0
|
| 174 |
+
1337530075,0
|
| 175 |
+
1337530076,0
|
| 176 |
+
1337530077,0
|
| 177 |
+
1337530078,0
|
| 178 |
+
660520001,0
|
| 179 |
+
660520002,0
|
| 180 |
+
1245640022,0
|
| 181 |
+
660520006,0
|
| 182 |
+
660520008,0
|
| 183 |
+
660520009,0
|
| 184 |
+
660520010,0
|
| 185 |
+
660520005,0
|
| 186 |
+
660520004,0
|
| 187 |
+
660520011,0
|
| 188 |
+
660520003,0
|
| 189 |
+
660520012,0
|
| 190 |
+
660520007,0
|
| 191 |
+
1595490004,1
|
| 192 |
+
1512840006,1
|
| 193 |
+
1512840005,1
|
| 194 |
+
1512840004,1
|
| 195 |
+
1512840003,1
|
| 196 |
+
1512840002,1
|
| 197 |
+
1512840001,1
|
| 198 |
+
1595490014,1
|
| 199 |
+
1512840007,1
|
| 200 |
+
1595490001,1
|
| 201 |
+
1595490017,1
|
| 202 |
+
1595490018,1
|
| 203 |
+
1595490019,1
|
| 204 |
+
1595490020,1
|
| 205 |
+
1295570001,1
|
| 206 |
+
1595490013,1
|
| 207 |
+
1595490012,1
|
| 208 |
+
1595490011,1
|
| 209 |
+
1595490010,1
|
| 210 |
+
1595490009,1
|
| 211 |
+
1595490016,1
|
| 212 |
+
1595490008,1
|
| 213 |
+
1595490007,1
|
| 214 |
+
1595490006,1
|
| 215 |
+
1595490003,1
|
| 216 |
+
1595490005,1
|
| 217 |
+
1595490015,1
|
| 218 |
+
1931980002,1
|
| 219 |
+
1730290004,1
|
| 220 |
+
457380018,1
|
| 221 |
+
457380017,1
|
| 222 |
+
457380016,1
|
| 223 |
+
457380015,1
|
| 224 |
+
457380014,1
|
| 225 |
+
457380013,1
|
| 226 |
+
457380012,1
|
| 227 |
+
457380011,1
|
| 228 |
+
457380010,1
|
| 229 |
+
457380009,1
|
| 230 |
+
812460003,1
|
| 231 |
+
812460004,1
|
| 232 |
+
812460005,1
|
| 233 |
+
812460006,1
|
| 234 |
+
812460007,1
|
| 235 |
+
457380008,1
|
| 236 |
+
457380007,1
|
| 237 |
+
457380006,1
|
| 238 |
+
457380005,1
|
| 239 |
+
457380019,1
|
| 240 |
+
457380004,1
|
| 241 |
+
812460002,1
|
| 242 |
+
716260021,1
|
| 243 |
+
716260006,1
|
| 244 |
+
716260005,1
|
| 245 |
+
716260004,1
|
| 246 |
+
716260003,1
|
| 247 |
+
716260002,1
|
| 248 |
+
716260001,1
|
| 249 |
+
631660001,1
|
| 250 |
+
716260009,1
|
| 251 |
+
716260010,1
|
| 252 |
+
716260011,1
|
| 253 |
+
716260012,1
|
| 254 |
+
716260013,1
|
| 255 |
+
716260014,1
|
| 256 |
+
716260015,1
|
| 257 |
+
716260016,1
|
| 258 |
+
716260017,1
|
| 259 |
+
716260018,1
|
| 260 |
+
716260019,1
|
| 261 |
+
716260020,1
|
| 262 |
+
812460001,1
|
| 263 |
+
457380003,1
|
| 264 |
+
457380002,1
|
| 265 |
+
457380001,1
|
| 266 |
+
1931980009,1
|
| 267 |
+
1931980008,1
|
| 268 |
+
716260007,1
|
| 269 |
+
1931980006,1
|
| 270 |
+
954160002,1
|
| 271 |
+
1931980005,1
|
| 272 |
+
1931980010,1
|
| 273 |
+
1931980004,1
|
| 274 |
+
954160001,1
|
| 275 |
+
1931980001,1
|
| 276 |
+
1730290005,1
|
| 277 |
+
1730290003,1
|
| 278 |
+
1730290002,1
|
| 279 |
+
1730290001,1
|
| 280 |
+
1931980003,1
|
| 281 |
+
1931980011,1
|
| 282 |
+
1931980007,1
|
| 283 |
+
1931980013,1
|
| 284 |
+
812460008,1
|
| 285 |
+
812460009,1
|
| 286 |
+
812460010,1
|
| 287 |
+
1931980012,1
|
| 288 |
+
716260008,1
|
| 289 |
+
1931980014,1
|
| 290 |
+
815280012,2
|
| 291 |
+
998660003,2
|
| 292 |
+
998660002,2
|
| 293 |
+
998660001,2
|
| 294 |
+
815280011,2
|
| 295 |
+
815280010,2
|
| 296 |
+
815280001,2
|
| 297 |
+
815280002,2
|
| 298 |
+
815280003,2
|
| 299 |
+
815280004,2
|
| 300 |
+
815280005,2
|
| 301 |
+
815280006,2
|
| 302 |
+
815280009,2
|
| 303 |
+
815280018,2
|
| 304 |
+
815280017,2
|
| 305 |
+
815280016,2
|
| 306 |
+
815280015,2
|
| 307 |
+
815280014,2
|
| 308 |
+
815280013,2
|
| 309 |
+
815280008,2
|
| 310 |
+
815280007,2
|
| 311 |
+
1652800010,2
|
| 312 |
+
622310002,2
|
| 313 |
+
438100004,2
|
| 314 |
+
438100003,2
|
| 315 |
+
438100002,2
|
| 316 |
+
438100001,2
|
| 317 |
+
2193270001,2
|
| 318 |
+
2020950003,2
|
| 319 |
+
2020950002,2
|
| 320 |
+
2020950001,2
|
| 321 |
+
1652800001,2
|
| 322 |
+
438100005,2
|
| 323 |
+
1652800002,2
|
| 324 |
+
1652800004,2
|
| 325 |
+
1652800005,2
|
| 326 |
+
1652800006,2
|
| 327 |
+
1652800007,2
|
| 328 |
+
1652800008,2
|
| 329 |
+
998660004,2
|
| 330 |
+
1652800013,2
|
| 331 |
+
1652800012,2
|
| 332 |
+
1652800011,2
|
| 333 |
+
1652800003,2
|
| 334 |
+
622310001,2
|
| 335 |
+
438100006,2
|
| 336 |
+
438100008,2
|
| 337 |
+
591680013,2
|
| 338 |
+
591680012,2
|
| 339 |
+
591680011,2
|
| 340 |
+
591680010,2
|
| 341 |
+
591680009,2
|
| 342 |
+
591680008,2
|
| 343 |
+
591680007,2
|
| 344 |
+
591680006,2
|
| 345 |
+
591680005,2
|
| 346 |
+
438100007,2
|
| 347 |
+
591680004,2
|
| 348 |
+
1652800009,2
|
| 349 |
+
591680001,2
|
| 350 |
+
1394410001,2
|
| 351 |
+
1394410002,2
|
| 352 |
+
1394410003,2
|
| 353 |
+
1394410004,2
|
| 354 |
+
1394410005,2
|
| 355 |
+
438100010,2
|
| 356 |
+
438100009,2
|
| 357 |
+
591680003,2
|
| 358 |
+
591680002,2
|
| 359 |
+
998660005,2
|
| 360 |
+
1157070006,2
|
| 361 |
+
1157070001,2
|
| 362 |
+
1157070002,2
|
| 363 |
+
1157070003,2
|
| 364 |
+
1157070008,2
|
| 365 |
+
1157070007,2
|
| 366 |
+
1157070004,2
|
| 367 |
+
1157070005,2
|
| 368 |
+
898080011,3
|
| 369 |
+
898080010,3
|
| 370 |
+
898080009,3
|
| 371 |
+
898080008,3
|
| 372 |
+
898080007,3
|
| 373 |
+
898080006,3
|
| 374 |
+
898080005,3
|
| 375 |
+
898080004,3
|
| 376 |
+
1337060040,3
|
| 377 |
+
898080003,3
|
| 378 |
+
898080002,3
|
| 379 |
+
898080001,3
|
| 380 |
+
1485260001,3
|
| 381 |
+
1485260002,3
|
| 382 |
+
1485260003,3
|
| 383 |
+
1485260004,3
|
| 384 |
+
1485260005,3
|
| 385 |
+
1486660001,3
|
| 386 |
+
1486660002,3
|
| 387 |
+
1486660003,3
|
| 388 |
+
1404360029,3
|
| 389 |
+
1404360027,3
|
| 390 |
+
1404360022,3
|
| 391 |
+
1404360025,3
|
| 392 |
+
898080013,3
|
| 393 |
+
898080012,3
|
| 394 |
+
1404360001,3
|
| 395 |
+
1404360002,3
|
| 396 |
+
1404360003,3
|
| 397 |
+
1404360004,3
|
| 398 |
+
1404360005,3
|
| 399 |
+
1404360006,3
|
| 400 |
+
1404360007,3
|
| 401 |
+
1404360026,3
|
| 402 |
+
1404360009,3
|
| 403 |
+
1404360011,3
|
| 404 |
+
1404360013,3
|
| 405 |
+
1404360015,3
|
| 406 |
+
1404360016,3
|
| 407 |
+
1404360018,3
|
| 408 |
+
1404360019,3
|
| 409 |
+
1486660004,3
|
| 410 |
+
1404360023,3
|
| 411 |
+
1404360024,3
|
| 412 |
+
1404360010,3
|
| 413 |
+
1486660005,3
|
| 414 |
+
1486660010,3
|
| 415 |
+
1486660008,3
|
| 416 |
+
1825460004,3
|
| 417 |
+
1825460003,3
|
| 418 |
+
1825460002,3
|
| 419 |
+
1825460001,3
|
| 420 |
+
528580004,3
|
| 421 |
+
528580003,3
|
| 422 |
+
528580002,3
|
| 423 |
+
528580001,3
|
| 424 |
+
1801560011,3
|
| 425 |
+
1801560010,3
|
| 426 |
+
1801560009,3
|
| 427 |
+
1801560008,3
|
| 428 |
+
1801560007,3
|
| 429 |
+
1801560006,3
|
| 430 |
+
1801560005,3
|
| 431 |
+
1801560004,3
|
| 432 |
+
1801560003,3
|
| 433 |
+
1801560002,3
|
| 434 |
+
1801560001,3
|
| 435 |
+
1825460005,3
|
| 436 |
+
1825460006,3
|
| 437 |
+
1825460007,3
|
| 438 |
+
1825460008,3
|
| 439 |
+
1486660009,3
|
| 440 |
+
898080014,3
|
| 441 |
+
1486660011,3
|
| 442 |
+
1486660012,3
|
| 443 |
+
1486660013,3
|
| 444 |
+
1486660014,3
|
| 445 |
+
1825460012,3
|
| 446 |
+
1825460011,3
|
| 447 |
+
731790010,3
|
| 448 |
+
1486660007,3
|
| 449 |
+
731790009,3
|
| 450 |
+
731790007,3
|
| 451 |
+
731790006,3
|
| 452 |
+
731790005,3
|
| 453 |
+
731790004,3
|
| 454 |
+
731790003,3
|
| 455 |
+
731790002,3
|
| 456 |
+
731790001,3
|
| 457 |
+
1825460010,3
|
| 458 |
+
1825460009,3
|
| 459 |
+
731790008,3
|
| 460 |
+
898080015,3
|
| 461 |
+
1486660006,3
|
| 462 |
+
1394210010,3
|
| 463 |
+
1337060006,3
|
| 464 |
+
1337060005,3
|
| 465 |
+
1337060004,3
|
| 466 |
+
1337060003,3
|
| 467 |
+
1337060002,3
|
| 468 |
+
1337060001,3
|
| 469 |
+
1337060041,3
|
| 470 |
+
1337060007,3
|
| 471 |
+
1337060027,3
|
| 472 |
+
1337060030,3
|
| 473 |
+
1337060031,3
|
| 474 |
+
1337060032,3
|
| 475 |
+
1337060033,3
|
| 476 |
+
898080036,3
|
| 477 |
+
1337060034,3
|
| 478 |
+
1337060035,3
|
| 479 |
+
1337060029,3
|
| 480 |
+
1337060008,3
|
| 481 |
+
1337060009,3
|
| 482 |
+
1337060010,3
|
| 483 |
+
1337060019,3
|
| 484 |
+
1337060020,3
|
| 485 |
+
1337060021,3
|
| 486 |
+
1337060018,3
|
| 487 |
+
1337060017,3
|
| 488 |
+
1337060016,3
|
| 489 |
+
1337060015,3
|
| 490 |
+
1337060022,3
|
| 491 |
+
1337060023,3
|
| 492 |
+
1337060024,3
|
| 493 |
+
898080016,3
|
| 494 |
+
1337060025,3
|
| 495 |
+
1337060026,3
|
| 496 |
+
1337060014,3
|
| 497 |
+
1337060013,3
|
| 498 |
+
1337060012,3
|
| 499 |
+
1337060011,3
|
| 500 |
+
1337060036,3
|
| 501 |
+
1337060039,3
|
| 502 |
+
1337060028,3
|
| 503 |
+
898080029,3
|
| 504 |
+
898080025,3
|
| 505 |
+
898080024,3
|
| 506 |
+
898080023,3
|
| 507 |
+
898080022,3
|
| 508 |
+
898080021,3
|
| 509 |
+
898080020,3
|
| 510 |
+
898080019,3
|
| 511 |
+
898080018,3
|
| 512 |
+
898080028,3
|
| 513 |
+
898080026,3
|
| 514 |
+
898080030,3
|
| 515 |
+
898080031,3
|
| 516 |
+
898080032,3
|
| 517 |
+
898080033,3
|
| 518 |
+
898080017,3
|
| 519 |
+
898080027,3
|
| 520 |
+
1394210002,3
|
| 521 |
+
1394210001,3
|
| 522 |
+
1394210008,3
|
| 523 |
+
1394210009,3
|
| 524 |
+
898080035,3
|
| 525 |
+
1394210006,3
|
| 526 |
+
1394210007,3
|
| 527 |
+
1394210004,3
|
| 528 |
+
1394210003,3
|
| 529 |
+
898080034,3
|
| 530 |
+
1394210005,3
|
| 531 |
+
457550003,4
|
| 532 |
+
1705030007,4
|
| 533 |
+
457550002,4
|
| 534 |
+
457550001,4
|
| 535 |
+
457550004,4
|
| 536 |
+
1705030006,4
|
| 537 |
+
457550005,4
|
| 538 |
+
1705030008,4
|
| 539 |
+
1805510025,4
|
| 540 |
+
1705030010,4
|
| 541 |
+
1805510024,4
|
| 542 |
+
1805510026,4
|
| 543 |
+
1172280004,4
|
| 544 |
+
1172280003,4
|
| 545 |
+
1172280002,4
|
| 546 |
+
1172280001,4
|
| 547 |
+
1172280009,4
|
| 548 |
+
1705030011,4
|
| 549 |
+
1705030012,4
|
| 550 |
+
1705030013,4
|
| 551 |
+
1705030009,4
|
| 552 |
+
1705030001,4
|
| 553 |
+
1805510020,4
|
| 554 |
+
1805510022,4
|
| 555 |
+
1248270013,4
|
| 556 |
+
1248270012,4
|
| 557 |
+
1248270011,4
|
| 558 |
+
1248270010,4
|
| 559 |
+
1248270009,4
|
| 560 |
+
1248270008,4
|
| 561 |
+
1248270007,4
|
| 562 |
+
1248270006,4
|
| 563 |
+
1248270005,4
|
| 564 |
+
1248270004,4
|
| 565 |
+
1248270003,4
|
| 566 |
+
1248270002,4
|
| 567 |
+
1248270001,4
|
| 568 |
+
1705030005,4
|
| 569 |
+
1705030004,4
|
| 570 |
+
1705030003,4
|
| 571 |
+
1705030016,4
|
| 572 |
+
463290001,4
|
| 573 |
+
1805510001,4
|
| 574 |
+
1805510002,4
|
| 575 |
+
1805510003,4
|
| 576 |
+
1805510021,4
|
| 577 |
+
1705030002,4
|
| 578 |
+
1805510019,4
|
| 579 |
+
1805510018,4
|
| 580 |
+
1805510017,4
|
| 581 |
+
1805510016,4
|
| 582 |
+
1805510015,4
|
| 583 |
+
1805510014,4
|
| 584 |
+
1805510023,4
|
| 585 |
+
1805510013,4
|
| 586 |
+
1805510011,4
|
| 587 |
+
1805510010,4
|
| 588 |
+
1805510009,4
|
| 589 |
+
1805510008,4
|
| 590 |
+
1805510007,4
|
| 591 |
+
1805510006,4
|
| 592 |
+
1805510005,4
|
| 593 |
+
1805510004,4
|
| 594 |
+
1805510012,4
|
| 595 |
+
463290002,4
|
| 596 |
+
667010003,4
|
| 597 |
+
463290004,4
|
| 598 |
+
1178780007,4
|
| 599 |
+
1178780008,4
|
| 600 |
+
1178780009,4
|
| 601 |
+
1178780010,4
|
| 602 |
+
1178780011,4
|
| 603 |
+
1178780012,4
|
| 604 |
+
1705030014,4
|
| 605 |
+
463290003,4
|
| 606 |
+
1178780014,4
|
| 607 |
+
910190007,4
|
| 608 |
+
910190006,4
|
| 609 |
+
910190005,4
|
| 610 |
+
910190004,4
|
| 611 |
+
910190003,4
|
| 612 |
+
910190002,4
|
| 613 |
+
910190001,4
|
| 614 |
+
1172280005,4
|
| 615 |
+
1178780016,4
|
| 616 |
+
1178780015,4
|
| 617 |
+
1382350005,4
|
| 618 |
+
1382350004,4
|
| 619 |
+
1382350003,4
|
| 620 |
+
1382350002,4
|
| 621 |
+
1382350001,4
|
| 622 |
+
1382350008,4
|
| 623 |
+
1382350007,4
|
| 624 |
+
1382350006,4
|
| 625 |
+
1178780006,4
|
| 626 |
+
1178780005,4
|
| 627 |
+
1178780013,4
|
| 628 |
+
1705030015,4
|
| 629 |
+
667010001,4
|
| 630 |
+
1805510032,4
|
| 631 |
+
1805510031,4
|
| 632 |
+
667010002,4
|
| 633 |
+
667010004,4
|
| 634 |
+
667010005,4
|
| 635 |
+
667010006,4
|
| 636 |
+
667010007,4
|
| 637 |
+
667010008,4
|
| 638 |
+
667010010,4
|
| 639 |
+
667010011,4
|
| 640 |
+
667010012,4
|
| 641 |
+
1805510033,4
|
| 642 |
+
1172280006,4
|
| 643 |
+
667010009,4
|
| 644 |
+
1172280008,4
|
| 645 |
+
463290005,4
|
| 646 |
+
1172280007,4
|
| 647 |
+
463290007,4
|
| 648 |
+
463290008,4
|
| 649 |
+
1805510027,4
|
| 650 |
+
1805510028,4
|
| 651 |
+
463290006,4
|
| 652 |
+
1805510030,4
|
| 653 |
+
1178780004,4
|
| 654 |
+
1178780003,4
|
| 655 |
+
1178780002,4
|
| 656 |
+
1178780001,4
|
| 657 |
+
1805510029,4
|
| 658 |
+
1707840007,5
|
| 659 |
+
1707840006,5
|
| 660 |
+
1707840005,5
|
| 661 |
+
1707840004,5
|
| 662 |
+
1707840003,5
|
| 663 |
+
2224020003,5
|
| 664 |
+
1707840001,5
|
| 665 |
+
2224020002,5
|
| 666 |
+
2224020001,5
|
| 667 |
+
1707840008,5
|
| 668 |
+
1707840002,5
|
| 669 |
+
2224020030,5
|
| 670 |
+
2224020024,5
|
| 671 |
+
2224020028,5
|
| 672 |
+
2224020023,5
|
| 673 |
+
2224020022,5
|
| 674 |
+
2224020021,5
|
| 675 |
+
2224020020,5
|
| 676 |
+
2224020019,5
|
| 677 |
+
2224020018,5
|
| 678 |
+
2224020017,5
|
| 679 |
+
2224020016,5
|
| 680 |
+
2224020015,5
|
| 681 |
+
2224020014,5
|
| 682 |
+
2224020029,5
|
| 683 |
+
2224020013,5
|
| 684 |
+
2224020011,5
|
| 685 |
+
2224020010,5
|
| 686 |
+
2224020025,5
|
| 687 |
+
2224020008,5
|
| 688 |
+
2224020007,5
|
| 689 |
+
2224020006,5
|
| 690 |
+
2224020005,5
|
| 691 |
+
2224020004,5
|
| 692 |
+
2224020026,5
|
| 693 |
+
2224020027,5
|
| 694 |
+
2224020012,5
|
| 695 |
+
2224020009,5
|
| 696 |
+
2308940007,5
|
| 697 |
+
1695870005,5
|
| 698 |
+
726910008,5
|
| 699 |
+
726910007,5
|
| 700 |
+
726910006,5
|
| 701 |
+
726910005,5
|
| 702 |
+
726910004,5
|
| 703 |
+
726910003,5
|
| 704 |
+
726910002,5
|
| 705 |
+
726910001,5
|
| 706 |
+
726910009,5
|
| 707 |
+
605850001,5
|
| 708 |
+
513490001,5
|
| 709 |
+
497820003,5
|
| 710 |
+
497820002,5
|
| 711 |
+
497820001,5
|
| 712 |
+
2308940001,5
|
| 713 |
+
2308940004,5
|
| 714 |
+
2308940003,5
|
| 715 |
+
2308940002,5
|
| 716 |
+
513490002,5
|
| 717 |
+
726910010,5
|
| 718 |
+
726910011,5
|
| 719 |
+
758210001,5
|
| 720 |
+
1334900005,5
|
| 721 |
+
1334900004,5
|
| 722 |
+
1334900003,5
|
| 723 |
+
1334900002,5
|
| 724 |
+
1334900001,5
|
| 725 |
+
1334900006,5
|
| 726 |
+
1695870006,5
|
| 727 |
+
758210014,5
|
| 728 |
+
758210013,5
|
| 729 |
+
758210012,5
|
| 730 |
+
758210011,5
|
| 731 |
+
758210010,5
|
| 732 |
+
758210009,5
|
| 733 |
+
758210008,5
|
| 734 |
+
758210007,5
|
| 735 |
+
758210005,5
|
| 736 |
+
758210004,5
|
| 737 |
+
758210003,5
|
| 738 |
+
758210002,5
|
| 739 |
+
2308940017,5
|
| 740 |
+
2308940016,5
|
| 741 |
+
758210006,5
|
| 742 |
+
2308940014,5
|
| 743 |
+
2224020045,5
|
| 744 |
+
2224020044,5
|
| 745 |
+
2224020043,5
|
| 746 |
+
2224020042,5
|
| 747 |
+
2224020041,5
|
| 748 |
+
2224020040,5
|
| 749 |
+
2224020039,5
|
| 750 |
+
2224020038,5
|
| 751 |
+
2224020046,5
|
| 752 |
+
2224020037,5
|
| 753 |
+
2224020035,5
|
| 754 |
+
2224020034,5
|
| 755 |
+
1695870004,5
|
| 756 |
+
2308940015,5
|
| 757 |
+
2224020033,5
|
| 758 |
+
1695870003,5
|
| 759 |
+
1695870002,5
|
| 760 |
+
1695870001,5
|
| 761 |
+
2224020032,5
|
| 762 |
+
2224020047,5
|
| 763 |
+
2224020036,5
|
| 764 |
+
2224020049,5
|
| 765 |
+
2308940013,5
|
| 766 |
+
2308940012,5
|
| 767 |
+
2308940011,5
|
| 768 |
+
2308940010,5
|
| 769 |
+
2308940009,5
|
| 770 |
+
2224020048,5
|
| 771 |
+
2308940008,5
|
| 772 |
+
2308940006,5
|
| 773 |
+
2308940005,5
|
| 774 |
+
2224020031,5
|
| 775 |
+
2224020057,5
|
| 776 |
+
2224020056,5
|
| 777 |
+
2224020050,5
|
| 778 |
+
2224020051,5
|
| 779 |
+
2224020052,5
|
| 780 |
+
2224020055,5
|
| 781 |
+
2224020058,5
|
| 782 |
+
2224020054,5
|
| 783 |
+
2224020053,5
|
| 784 |
+
2057000131,6
|
| 785 |
+
2057000130,6
|
| 786 |
+
2057000129,6
|
| 787 |
+
2057000128,6
|
| 788 |
+
2057000126,6
|
| 789 |
+
2057000125,6
|
| 790 |
+
2057000124,6
|
| 791 |
+
2057000123,6
|
| 792 |
+
2057000127,6
|
| 793 |
+
2057000132,6
|
| 794 |
+
2057000134,6
|
| 795 |
+
2057000148,6
|
| 796 |
+
2057000135,6
|
| 797 |
+
2057000136,6
|
| 798 |
+
2057000137,6
|
| 799 |
+
2057000138,6
|
| 800 |
+
2057000140,6
|
| 801 |
+
2057000141,6
|
| 802 |
+
2057000142,6
|
| 803 |
+
2057000143,6
|
| 804 |
+
2057000145,6
|
| 805 |
+
2057000146,6
|
| 806 |
+
2057000147,6
|
| 807 |
+
2057000149,6
|
| 808 |
+
2057000133,6
|
| 809 |
+
2057000139,6
|
| 810 |
+
2057000107,6
|
| 811 |
+
2057000121,6
|
| 812 |
+
2057000093,6
|
| 813 |
+
2057000150,6
|
| 814 |
+
2057000094,6
|
| 815 |
+
2057000095,6
|
| 816 |
+
2057000096,6
|
| 817 |
+
2057000097,6
|
| 818 |
+
2057000098,6
|
| 819 |
+
2057000099,6
|
| 820 |
+
2057000100,6
|
| 821 |
+
2057000101,6
|
| 822 |
+
2057000102,6
|
| 823 |
+
2057000103,6
|
| 824 |
+
2057000104,6
|
| 825 |
+
2057000122,6
|
| 826 |
+
2057000105,6
|
| 827 |
+
2057000108,6
|
| 828 |
+
2057000109,6
|
| 829 |
+
2057000110,6
|
| 830 |
+
2057000111,6
|
| 831 |
+
2057000112,6
|
| 832 |
+
2057000113,6
|
| 833 |
+
2057000114,6
|
| 834 |
+
2057000115,6
|
| 835 |
+
2057000116,6
|
| 836 |
+
2057000117,6
|
| 837 |
+
2057000118,6
|
| 838 |
+
2057000119,6
|
| 839 |
+
2057000120,6
|
| 840 |
+
2057000106,6
|
| 841 |
+
2057000151,6
|
| 842 |
+
997760004,6
|
| 843 |
+
2057000153,6
|
| 844 |
+
997760008,6
|
| 845 |
+
997760007,6
|
| 846 |
+
997760006,6
|
| 847 |
+
997760005,6
|
| 848 |
+
997760003,6
|
| 849 |
+
997760002,6
|
| 850 |
+
997760001,6
|
| 851 |
+
891960010,6
|
| 852 |
+
891960009,6
|
| 853 |
+
2089520003,6
|
| 854 |
+
2089520004,6
|
| 855 |
+
891960008,6
|
| 856 |
+
997760009,6
|
| 857 |
+
891960007,6
|
| 858 |
+
891960005,6
|
| 859 |
+
891960004,6
|
| 860 |
+
891960003,6
|
| 861 |
+
891960002,6
|
| 862 |
+
891960001,6
|
| 863 |
+
790750006,6
|
| 864 |
+
790750005,6
|
| 865 |
+
790750004,6
|
| 866 |
+
790750003,6
|
| 867 |
+
790750002,6
|
| 868 |
+
790750001,6
|
| 869 |
+
2057000092,6
|
| 870 |
+
891960006,6
|
| 871 |
+
997760010,6
|
| 872 |
+
997760011,6
|
| 873 |
+
997760012,6
|
| 874 |
+
2057000154,6
|
| 875 |
+
2057000155,6
|
| 876 |
+
2057000156,6
|
| 877 |
+
2057000157,6
|
| 878 |
+
2057000158,6
|
| 879 |
+
2057000159,6
|
| 880 |
+
2057000160,6
|
| 881 |
+
2057000161,6
|
| 882 |
+
2057000162,6
|
| 883 |
+
2057000163,6
|
| 884 |
+
2057000164,6
|
| 885 |
+
2057000165,6
|
| 886 |
+
2057000166,6
|
| 887 |
+
2057000167,6
|
| 888 |
+
2057000168,6
|
| 889 |
+
2057000169,6
|
| 890 |
+
2057000170,6
|
| 891 |
+
2057000171,6
|
| 892 |
+
2057000172,6
|
| 893 |
+
2057000173,6
|
| 894 |
+
2057000174,6
|
| 895 |
+
2057000175,6
|
| 896 |
+
2057000176,6
|
| 897 |
+
2057000177,6
|
| 898 |
+
2057000178,6
|
| 899 |
+
2089520001,6
|
| 900 |
+
2089520002,6
|
| 901 |
+
2057000152,6
|
| 902 |
+
2057000091,6
|
| 903 |
+
2057000144,6
|
| 904 |
+
2057000089,6
|
| 905 |
+
1269890008,6
|
| 906 |
+
1269890007,6
|
| 907 |
+
1269890006,6
|
| 908 |
+
1269890005,6
|
| 909 |
+
1269890004,6
|
| 910 |
+
1269890003,6
|
| 911 |
+
1269890009,6
|
| 912 |
+
1388030001,6
|
| 913 |
+
1269890001,6
|
| 914 |
+
1456280001,6
|
| 915 |
+
1456280002,6
|
| 916 |
+
1456280003,6
|
| 917 |
+
1456280004,6
|
| 918 |
+
1480650001,6
|
| 919 |
+
1269890002,6
|
| 920 |
+
1269890010,6
|
| 921 |
+
2057000001,6
|
| 922 |
+
2057000002,6
|
| 923 |
+
2057000012,6
|
| 924 |
+
2057000011,6
|
| 925 |
+
2057000010,6
|
| 926 |
+
2057000009,6
|
| 927 |
+
2057000008,6
|
| 928 |
+
2057000007,6
|
| 929 |
+
2057000006,6
|
| 930 |
+
1561560011,6
|
| 931 |
+
2057000004,6
|
| 932 |
+
2057000003,6
|
| 933 |
+
1269890015,6
|
| 934 |
+
1269890014,6
|
| 935 |
+
1269890013,6
|
| 936 |
+
1269890012,6
|
| 937 |
+
1269890011,6
|
| 938 |
+
1480650002,6
|
| 939 |
+
1480650003,6
|
| 940 |
+
1561560032,6
|
| 941 |
+
1561560031,6
|
| 942 |
+
1561560022,6
|
| 943 |
+
1561560021,6
|
| 944 |
+
1561560020,6
|
| 945 |
+
1561560019,6
|
| 946 |
+
1561560018,6
|
| 947 |
+
1561560017,6
|
| 948 |
+
1561560016,6
|
| 949 |
+
1561560015,6
|
| 950 |
+
1561560014,6
|
| 951 |
+
1561560013,6
|
| 952 |
+
1561560006,6
|
| 953 |
+
1561560007,6
|
| 954 |
+
1561560008,6
|
| 955 |
+
1561560009,6
|
| 956 |
+
1561560010,6
|
| 957 |
+
1561560023,6
|
| 958 |
+
2057000013,6
|
| 959 |
+
1561560024,6
|
| 960 |
+
2057000090,6
|
| 961 |
+
1561560030,6
|
| 962 |
+
1561560001,6
|
| 963 |
+
1561560002,6
|
| 964 |
+
1561560003,6
|
| 965 |
+
1480650004,6
|
| 966 |
+
1480650005,6
|
| 967 |
+
1480650006,6
|
| 968 |
+
1480650007,6
|
| 969 |
+
1480650008,6
|
| 970 |
+
1561560004,6
|
| 971 |
+
1561560005,6
|
| 972 |
+
1561560029,6
|
| 973 |
+
1561560028,6
|
| 974 |
+
1561560027,6
|
| 975 |
+
1561560026,6
|
| 976 |
+
1561560025,6
|
| 977 |
+
2057000014,6
|
| 978 |
+
2057000005,6
|
| 979 |
+
2057000016,6
|
| 980 |
+
2057000068,6
|
| 981 |
+
2057000067,6
|
| 982 |
+
2057000066,6
|
| 983 |
+
2057000065,6
|
| 984 |
+
2057000064,6
|
| 985 |
+
2057000063,6
|
| 986 |
+
2057000062,6
|
| 987 |
+
2057000061,6
|
| 988 |
+
2057000060,6
|
| 989 |
+
2057000015,6
|
| 990 |
+
2057000058,6
|
| 991 |
+
2057000057,6
|
| 992 |
+
2057000056,6
|
| 993 |
+
2057000055,6
|
| 994 |
+
2057000054,6
|
| 995 |
+
2057000069,6
|
| 996 |
+
2057000070,6
|
| 997 |
+
2057000071,6
|
| 998 |
+
2057000072,6
|
| 999 |
+
2057000088,6
|
| 1000 |
+
2057000087,6
|
| 1001 |
+
2057000086,6
|
| 1002 |
+
2057000085,6
|
| 1003 |
+
2057000084,6
|
| 1004 |
+
2057000083,6
|
| 1005 |
+
2057000082,6
|
| 1006 |
+
2057000053,6
|
| 1007 |
+
2057000081,6
|
| 1008 |
+
2057000079,6
|
| 1009 |
+
2057000078,6
|
| 1010 |
+
2057000077,6
|
| 1011 |
+
2057000076,6
|
| 1012 |
+
2057000075,6
|
| 1013 |
+
2057000074,6
|
| 1014 |
+
2057000073,6
|
| 1015 |
+
2057000080,6
|
| 1016 |
+
2057000052,6
|
| 1017 |
+
2057000059,6
|
| 1018 |
+
2057000050,6
|
| 1019 |
+
2057000032,6
|
| 1020 |
+
2057000031,6
|
| 1021 |
+
2057000030,6
|
| 1022 |
+
2057000029,6
|
| 1023 |
+
2057000028,6
|
| 1024 |
+
2057000051,6
|
| 1025 |
+
2057000027,6
|
| 1026 |
+
2057000026,6
|
| 1027 |
+
2057000025,6
|
| 1028 |
+
2057000024,6
|
| 1029 |
+
2057000023,6
|
| 1030 |
+
2057000022,6
|
| 1031 |
+
2057000021,6
|
| 1032 |
+
2057000020,6
|
| 1033 |
+
2057000019,6
|
| 1034 |
+
2057000018,6
|
| 1035 |
+
2057000017,6
|
| 1036 |
+
2057000033,6
|
| 1037 |
+
2057000034,6
|
| 1038 |
+
1561560012,6
|
| 1039 |
+
2057000036,6
|
| 1040 |
+
2057000035,6
|
| 1041 |
+
2057000044,6
|
| 1042 |
+
2057000045,6
|
| 1043 |
+
2057000046,6
|
| 1044 |
+
2057000047,6
|
| 1045 |
+
2057000043,6
|
| 1046 |
+
2057000049,6
|
| 1047 |
+
2057000048,6
|
| 1048 |
+
2057000042,6
|
| 1049 |
+
2057000041,6
|
| 1050 |
+
2057000040,6
|
| 1051 |
+
2057000039,6
|
| 1052 |
+
2057000038,6
|
| 1053 |
+
2057000037,6
|
| 1054 |
+
1063530027,7
|
| 1055 |
+
1063530026,7
|
| 1056 |
+
1063530025,7
|
| 1057 |
+
1063530028,7
|
| 1058 |
+
1063530043,7
|
| 1059 |
+
1063530040,7
|
| 1060 |
+
1063530041,7
|
| 1061 |
+
1550280038,7
|
| 1062 |
+
1063530039,7
|
| 1063 |
+
1063530038,7
|
| 1064 |
+
1063530037,7
|
| 1065 |
+
1063530036,7
|
| 1066 |
+
1063530035,7
|
| 1067 |
+
1063530034,7
|
| 1068 |
+
1063530033,7
|
| 1069 |
+
1063530032,7
|
| 1070 |
+
1063530031,7
|
| 1071 |
+
1063530030,7
|
| 1072 |
+
1063530042,7
|
| 1073 |
+
1063530029,7
|
| 1074 |
+
490250005,7
|
| 1075 |
+
1063530045,7
|
| 1076 |
+
1550280045,7
|
| 1077 |
+
1550280046,7
|
| 1078 |
+
1550280047,7
|
| 1079 |
+
490250019,7
|
| 1080 |
+
490250018,7
|
| 1081 |
+
490250017,7
|
| 1082 |
+
490250015,7
|
| 1083 |
+
490250014,7
|
| 1084 |
+
490250013,7
|
| 1085 |
+
490250012,7
|
| 1086 |
+
490250011,7
|
| 1087 |
+
490250010,7
|
| 1088 |
+
490250009,7
|
| 1089 |
+
490250008,7
|
| 1090 |
+
490250007,7
|
| 1091 |
+
490250006,7
|
| 1092 |
+
1063530024,7
|
| 1093 |
+
1550280044,7
|
| 1094 |
+
1063530044,7
|
| 1095 |
+
1550280043,7
|
| 1096 |
+
1550280041,7
|
| 1097 |
+
1063530046,7
|
| 1098 |
+
1063530047,7
|
| 1099 |
+
1063530048,7
|
| 1100 |
+
1063530049,7
|
| 1101 |
+
1063530050,7
|
| 1102 |
+
1063530051,7
|
| 1103 |
+
1063530052,7
|
| 1104 |
+
1063530053,7
|
| 1105 |
+
533970003,7
|
| 1106 |
+
533970002,7
|
| 1107 |
+
533970001,7
|
| 1108 |
+
529150004,7
|
| 1109 |
+
529150003,7
|
| 1110 |
+
529150002,7
|
| 1111 |
+
529150001,7
|
| 1112 |
+
1550280039,7
|
| 1113 |
+
1550280040,7
|
| 1114 |
+
1550280042,7
|
| 1115 |
+
1063530023,7
|
| 1116 |
+
1550280018,7
|
| 1117 |
+
1550280036,7
|
| 1118 |
+
1550280006,7
|
| 1119 |
+
1550280007,7
|
| 1120 |
+
1550280008,7
|
| 1121 |
+
1550280009,7
|
| 1122 |
+
1550280010,7
|
| 1123 |
+
1550280011,7
|
| 1124 |
+
1550280012,7
|
| 1125 |
+
1550280013,7
|
| 1126 |
+
1550280014,7
|
| 1127 |
+
1550280005,7
|
| 1128 |
+
1550280015,7
|
| 1129 |
+
1550280017,7
|
| 1130 |
+
490250004,7
|
| 1131 |
+
1550280019,7
|
| 1132 |
+
1550280020,7
|
| 1133 |
+
1550280021,7
|
| 1134 |
+
1550280022,7
|
| 1135 |
+
1550280023,7
|
| 1136 |
+
1550280024,7
|
| 1137 |
+
1550280025,7
|
| 1138 |
+
1550280016,7
|
| 1139 |
+
1550280004,7
|
| 1140 |
+
1550280003,7
|
| 1141 |
+
1550280002,7
|
| 1142 |
+
1264160017,7
|
| 1143 |
+
1264160016,7
|
| 1144 |
+
1264160015,7
|
| 1145 |
+
1033960001,7
|
| 1146 |
+
1033960002,7
|
| 1147 |
+
1033960003,7
|
| 1148 |
+
1033960004,7
|
| 1149 |
+
1033960005,7
|
| 1150 |
+
1033960006,7
|
| 1151 |
+
1063530001,7
|
| 1152 |
+
1481400001,7
|
| 1153 |
+
1481400002,7
|
| 1154 |
+
1481400003,7
|
| 1155 |
+
1264160014,7
|
| 1156 |
+
1264160013,7
|
| 1157 |
+
1264160012,7
|
| 1158 |
+
1264160011,7
|
| 1159 |
+
1264160010,7
|
| 1160 |
+
1264160009,7
|
| 1161 |
+
1264160008,7
|
| 1162 |
+
1550280001,7
|
| 1163 |
+
720300018,7
|
| 1164 |
+
1550280037,7
|
| 1165 |
+
720300017,7
|
| 1166 |
+
720300015,7
|
| 1167 |
+
1063530013,7
|
| 1168 |
+
1063530014,7
|
| 1169 |
+
1063530015,7
|
| 1170 |
+
1063530016,7
|
| 1171 |
+
1063530017,7
|
| 1172 |
+
1063530018,7
|
| 1173 |
+
1063530019,7
|
| 1174 |
+
1063530020,7
|
| 1175 |
+
1063530021,7
|
| 1176 |
+
1063530012,7
|
| 1177 |
+
1063530022,7
|
| 1178 |
+
1550280027,7
|
| 1179 |
+
1550280028,7
|
| 1180 |
+
1550280029,7
|
| 1181 |
+
1550280030,7
|
| 1182 |
+
1550280031,7
|
| 1183 |
+
1550280032,7
|
| 1184 |
+
1550280033,7
|
| 1185 |
+
1550280034,7
|
| 1186 |
+
1550280035,7
|
| 1187 |
+
1550280026,7
|
| 1188 |
+
1063530011,7
|
| 1189 |
+
1063530010,7
|
| 1190 |
+
1063530009,7
|
| 1191 |
+
720300014,7
|
| 1192 |
+
720300013,7
|
| 1193 |
+
720300012,7
|
| 1194 |
+
720300011,7
|
| 1195 |
+
720300010,7
|
| 1196 |
+
720300009,7
|
| 1197 |
+
720300008,7
|
| 1198 |
+
720300007,7
|
| 1199 |
+
720300006,7
|
| 1200 |
+
720300005,7
|
| 1201 |
+
720300004,7
|
| 1202 |
+
720300003,7
|
| 1203 |
+
720300002,7
|
| 1204 |
+
720300001,7
|
| 1205 |
+
1063530002,7
|
| 1206 |
+
1063530003,7
|
| 1207 |
+
1063530004,7
|
| 1208 |
+
1063530005,7
|
| 1209 |
+
1063530006,7
|
| 1210 |
+
1063530007,7
|
| 1211 |
+
1063530008,7
|
| 1212 |
+
720300016,7
|
| 1213 |
+
490250003,7
|
| 1214 |
+
490250016,7
|
| 1215 |
+
490250001,7
|
| 1216 |
+
1063530069,7
|
| 1217 |
+
1063530070,7
|
| 1218 |
+
1063530071,7
|
| 1219 |
+
1063530072,7
|
| 1220 |
+
1063530073,7
|
| 1221 |
+
1063530074,7
|
| 1222 |
+
1063530075,7
|
| 1223 |
+
1063530076,7
|
| 1224 |
+
1063530077,7
|
| 1225 |
+
1063530078,7
|
| 1226 |
+
1063530079,7
|
| 1227 |
+
1063530080,7
|
| 1228 |
+
1063530081,7
|
| 1229 |
+
1063530083,7
|
| 1230 |
+
490250002,7
|
| 1231 |
+
1676620007,7
|
| 1232 |
+
1264160007,7
|
| 1233 |
+
1264160006,7
|
| 1234 |
+
1264160005,7
|
| 1235 |
+
1676620006,7
|
| 1236 |
+
1676620005,7
|
| 1237 |
+
1676620004,7
|
| 1238 |
+
1264160004,7
|
| 1239 |
+
1264160003,7
|
| 1240 |
+
1264160002,7
|
| 1241 |
+
1264160001,7
|
| 1242 |
+
1676620003,7
|
| 1243 |
+
1676620002,7
|
| 1244 |
+
1676620001,7
|
| 1245 |
+
1063530068,7
|
| 1246 |
+
1063530067,7
|
| 1247 |
+
1063530082,7
|
| 1248 |
+
1063530066,7
|
| 1249 |
+
1063530061,7
|
| 1250 |
+
1063530062,7
|
| 1251 |
+
1063530063,7
|
| 1252 |
+
1063530064,7
|
| 1253 |
+
1063530065,7
|
| 1254 |
+
1063530058,7
|
| 1255 |
+
1063530057,7
|
| 1256 |
+
1063530056,7
|
| 1257 |
+
1063530055,7
|
| 1258 |
+
1063530054,7
|
| 1259 |
+
1063530059,7
|
| 1260 |
+
1550280054,7
|
| 1261 |
+
1550280048,7
|
| 1262 |
+
1550280049,7
|
| 1263 |
+
1550280050,7
|
| 1264 |
+
1550280055,7
|
| 1265 |
+
1063530060,7
|
| 1266 |
+
1550280051,7
|
| 1267 |
+
1550280052,7
|
| 1268 |
+
1550280053,7
|
| 1269 |
+
1026760019,8
|
| 1270 |
+
1026760020,8
|
| 1271 |
+
1026760021,8
|
| 1272 |
+
1026760022,8
|
| 1273 |
+
1026760027,8
|
| 1274 |
+
1026760024,8
|
| 1275 |
+
1026760017,8
|
| 1276 |
+
1026760025,8
|
| 1277 |
+
1026760016,8
|
| 1278 |
+
1026760026,8
|
| 1279 |
+
1026760018,8
|
| 1280 |
+
1026760023,8
|
| 1281 |
+
1453730002,8
|
| 1282 |
+
1906880002,8
|
| 1283 |
+
1906880001,8
|
| 1284 |
+
1862180007,8
|
| 1285 |
+
1026760015,8
|
| 1286 |
+
1862180006,8
|
| 1287 |
+
1862180005,8
|
| 1288 |
+
1862180004,8
|
| 1289 |
+
1862180003,8
|
| 1290 |
+
1862180002,8
|
| 1291 |
+
1862180001,8
|
| 1292 |
+
1250210001,8
|
| 1293 |
+
1250210002,8
|
| 1294 |
+
1906880003,8
|
| 1295 |
+
1026760014,8
|
| 1296 |
+
1026760011,8
|
| 1297 |
+
1026760012,8
|
| 1298 |
+
2077870001,8
|
| 1299 |
+
2077870002,8
|
| 1300 |
+
2077870003,8
|
| 1301 |
+
2077870004,8
|
| 1302 |
+
2077870005,8
|
| 1303 |
+
1453730014,8
|
| 1304 |
+
1453730013,8
|
| 1305 |
+
1453730012,8
|
| 1306 |
+
1453730011,8
|
| 1307 |
+
1453730010,8
|
| 1308 |
+
1453730009,8
|
| 1309 |
+
1453730008,8
|
| 1310 |
+
1453730007,8
|
| 1311 |
+
1453730006,8
|
| 1312 |
+
1453730005,8
|
| 1313 |
+
1453730004,8
|
| 1314 |
+
1453730003,8
|
| 1315 |
+
1026760006,8
|
| 1316 |
+
1026760005,8
|
| 1317 |
+
1026760004,8
|
| 1318 |
+
1026760003,8
|
| 1319 |
+
1453730001,8
|
| 1320 |
+
1026760010,8
|
| 1321 |
+
1026760009,8
|
| 1322 |
+
1026760008,8
|
| 1323 |
+
1026760007,8
|
| 1324 |
+
866540001,8
|
| 1325 |
+
866540003,8
|
| 1326 |
+
866540004,8
|
| 1327 |
+
1026760013,8
|
| 1328 |
+
866540005,8
|
| 1329 |
+
866540007,8
|
| 1330 |
+
866540008,8
|
| 1331 |
+
866540009,8
|
| 1332 |
+
866540010,8
|
| 1333 |
+
866540011,8
|
| 1334 |
+
866540002,8
|
| 1335 |
+
1250210003,8
|
| 1336 |
+
1026760002,8
|
| 1337 |
+
866540006,8
|
| 1338 |
+
1250210004,8
|
| 1339 |
+
1026760001,8
|
| 1340 |
+
600140014,8
|
| 1341 |
+
600140012,8
|
| 1342 |
+
600140013,8
|
| 1343 |
+
1250210005,8
|
| 1344 |
+
269170002,8
|
| 1345 |
+
269170003,8
|
| 1346 |
+
269170004,8
|
| 1347 |
+
269170005,8
|
| 1348 |
+
269170006,8
|
| 1349 |
+
269170007,8
|
| 1350 |
+
269170009,8
|
| 1351 |
+
600140015,8
|
| 1352 |
+
600140016,8
|
| 1353 |
+
600140017,8
|
| 1354 |
+
600140018,8
|
| 1355 |
+
600140019,8
|
| 1356 |
+
600140020,8
|
| 1357 |
+
600140021,8
|
| 1358 |
+
600140022,8
|
| 1359 |
+
600140023,8
|
| 1360 |
+
600140024,8
|
| 1361 |
+
1113370001,8
|
| 1362 |
+
600140011,8
|
| 1363 |
+
600140010,8
|
| 1364 |
+
269170008,8
|
| 1365 |
+
600140009,8
|
| 1366 |
+
1113370002,8
|
| 1367 |
+
269170001,8
|
| 1368 |
+
1113370003,8
|
| 1369 |
+
1113370004,8
|
| 1370 |
+
600140001,8
|
| 1371 |
+
1250210007,8
|
| 1372 |
+
1250210008,8
|
| 1373 |
+
1250210009,8
|
| 1374 |
+
600140002,8
|
| 1375 |
+
1250210006,8
|
| 1376 |
+
600140003,8
|
| 1377 |
+
600140008,8
|
| 1378 |
+
600140007,8
|
| 1379 |
+
1250210011,8
|
| 1380 |
+
1250210010,8
|
| 1381 |
+
1250210012,8
|
| 1382 |
+
600140006,8
|
| 1383 |
+
600140005,8
|
| 1384 |
+
600140004,8
|
| 1385 |
+
2163140007,9
|
| 1386 |
+
2163140008,9
|
| 1387 |
+
2163140009,9
|
| 1388 |
+
2163140006,9
|
| 1389 |
+
2163140010,9
|
| 1390 |
+
2163140011,9
|
| 1391 |
+
2163140012,9
|
| 1392 |
+
518580019,9
|
| 1393 |
+
518580020,9
|
| 1394 |
+
518580021,9
|
| 1395 |
+
518580022,9
|
| 1396 |
+
518580023,9
|
| 1397 |
+
518580008,9
|
| 1398 |
+
518580001,9
|
| 1399 |
+
518580002,9
|
| 1400 |
+
518580003,9
|
| 1401 |
+
518580004,9
|
| 1402 |
+
518580005,9
|
| 1403 |
+
518580006,9
|
| 1404 |
+
518580007,9
|
| 1405 |
+
2163140013,9
|
| 1406 |
+
2163140016,9
|
| 1407 |
+
2163140015,9
|
| 1408 |
+
2163140014,9
|
| 1409 |
+
2163140005,9
|
| 1410 |
+
518580024,9
|
| 1411 |
+
2163140004,9
|
| 1412 |
+
518580015,9
|
| 1413 |
+
2163140002,9
|
| 1414 |
+
982710007,9
|
| 1415 |
+
982710008,9
|
| 1416 |
+
982710009,9
|
| 1417 |
+
982710010,9
|
| 1418 |
+
982710011,9
|
| 1419 |
+
982710012,9
|
| 1420 |
+
982710013,9
|
| 1421 |
+
982710014,9
|
| 1422 |
+
982710015,9
|
| 1423 |
+
1298890009,9
|
| 1424 |
+
1298890010,9
|
| 1425 |
+
982710006,9
|
| 1426 |
+
1298890012,9
|
| 1427 |
+
1298890014,9
|
| 1428 |
+
1298890015,9
|
| 1429 |
+
1298890016,9
|
| 1430 |
+
1298890017,9
|
| 1431 |
+
1298890018,9
|
| 1432 |
+
1298890019,9
|
| 1433 |
+
1298890020,9
|
| 1434 |
+
1298890021,9
|
| 1435 |
+
518580011,9
|
| 1436 |
+
518580010,9
|
| 1437 |
+
518580009,9
|
| 1438 |
+
1298890013,9
|
| 1439 |
+
2163140003,9
|
| 1440 |
+
982710005,9
|
| 1441 |
+
982710003,9
|
| 1442 |
+
2163140001,9
|
| 1443 |
+
1210650002,9
|
| 1444 |
+
1210650001,9
|
| 1445 |
+
518580018,9
|
| 1446 |
+
518580017,9
|
| 1447 |
+
518580016,9
|
| 1448 |
+
518580014,9
|
| 1449 |
+
1270910001,9
|
| 1450 |
+
1270910002,9
|
| 1451 |
+
1270910003,9
|
| 1452 |
+
1270910004,9
|
| 1453 |
+
982710004,9
|
| 1454 |
+
1270910005,9
|
| 1455 |
+
1298890002,9
|
| 1456 |
+
1298890003,9
|
| 1457 |
+
1298890004,9
|
| 1458 |
+
1298890005,9
|
| 1459 |
+
1298890006,9
|
| 1460 |
+
1298890007,9
|
| 1461 |
+
1298890008,9
|
| 1462 |
+
518580013,9
|
| 1463 |
+
518580012,9
|
| 1464 |
+
982710001,9
|
| 1465 |
+
982710002,9
|
| 1466 |
+
1298890001,9
|
| 1467 |
+
2163140017,9
|
| 1468 |
+
714100001,9
|
| 1469 |
+
2163140019,9
|
| 1470 |
+
1210650018,9
|
| 1471 |
+
1210650017,9
|
| 1472 |
+
1210650016,9
|
| 1473 |
+
1210650015,9
|
| 1474 |
+
1210650014,9
|
| 1475 |
+
343740003,9
|
| 1476 |
+
1210650012,9
|
| 1477 |
+
1210650011,9
|
| 1478 |
+
1210650010,9
|
| 1479 |
+
1210650009,9
|
| 1480 |
+
1210650008,9
|
| 1481 |
+
1210650007,9
|
| 1482 |
+
1210650006,9
|
| 1483 |
+
1210650005,9
|
| 1484 |
+
1210650004,9
|
| 1485 |
+
1210650003,9
|
| 1486 |
+
2163140058,9
|
| 1487 |
+
343740002,9
|
| 1488 |
+
343740001,9
|
| 1489 |
+
1210650019,9
|
| 1490 |
+
1210650020,9
|
| 1491 |
+
714100002,9
|
| 1492 |
+
714100003,9
|
| 1493 |
+
714100004,9
|
| 1494 |
+
714100005,9
|
| 1495 |
+
714100006,9
|
| 1496 |
+
714100007,9
|
| 1497 |
+
714100008,9
|
| 1498 |
+
1210650028,9
|
| 1499 |
+
2163140057,9
|
| 1500 |
+
1210650027,9
|
| 1501 |
+
1210650025,9
|
| 1502 |
+
1210650024,9
|
| 1503 |
+
2293180001,9
|
| 1504 |
+
2293180002,9
|
| 1505 |
+
2293180003,9
|
| 1506 |
+
1210650023,9
|
| 1507 |
+
1210650022,9
|
| 1508 |
+
1210650021,9
|
| 1509 |
+
1210650026,9
|
| 1510 |
+
2163140018,9
|
| 1511 |
+
2163140056,9
|
| 1512 |
+
2163140054,9
|
| 1513 |
+
2163140032,9
|
| 1514 |
+
2163140031,9
|
| 1515 |
+
2163140030,9
|
| 1516 |
+
2163140029,9
|
| 1517 |
+
2163140028,9
|
| 1518 |
+
2163140027,9
|
| 1519 |
+
2163140026,9
|
| 1520 |
+
2163140025,9
|
| 1521 |
+
2163140024,9
|
| 1522 |
+
2163140023,9
|
| 1523 |
+
2163140022,9
|
| 1524 |
+
2163140021,9
|
| 1525 |
+
2163140020,9
|
| 1526 |
+
343740004,9
|
| 1527 |
+
343740005,9
|
| 1528 |
+
343740006,9
|
| 1529 |
+
343740007,9
|
| 1530 |
+
2163140033,9
|
| 1531 |
+
2163140034,9
|
| 1532 |
+
1488730001,9
|
| 1533 |
+
2163140035,9
|
| 1534 |
+
2163140053,9
|
| 1535 |
+
2163140052,9
|
| 1536 |
+
2163140051,9
|
| 1537 |
+
2163140050,9
|
| 1538 |
+
2163140049,9
|
| 1539 |
+
2163140048,9
|
| 1540 |
+
2163140047,9
|
| 1541 |
+
2163140046,9
|
| 1542 |
+
2163140055,9
|
| 1543 |
+
2163140045,9
|
| 1544 |
+
2163140043,9
|
| 1545 |
+
2163140042,9
|
| 1546 |
+
2163140041,9
|
| 1547 |
+
2163140040,9
|
| 1548 |
+
2163140039,9
|
| 1549 |
+
2163140038,9
|
| 1550 |
+
2163140037,9
|
| 1551 |
+
2163140036,9
|
| 1552 |
+
2163140044,9
|
| 1553 |
+
1210650013,9
|
dataset/fold_cat.csv
ADDED
|
@@ -0,0 +1,1553 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
img_id,fold
|
| 2 |
+
1210650020,0
|
| 3 |
+
866540008,0
|
| 4 |
+
1453730013,0
|
| 5 |
+
343740001,0
|
| 6 |
+
1550280034,0
|
| 7 |
+
2057000087,0
|
| 8 |
+
667010003,0
|
| 9 |
+
1150310003,0
|
| 10 |
+
1270910005,0
|
| 11 |
+
1394210007,0
|
| 12 |
+
2057000101,0
|
| 13 |
+
1337060005,0
|
| 14 |
+
1063530027,0
|
| 15 |
+
600140004,0
|
| 16 |
+
1063530050,0
|
| 17 |
+
1337530072,0
|
| 18 |
+
1550280019,0
|
| 19 |
+
2057000175,0
|
| 20 |
+
1298890018,0
|
| 21 |
+
1705030005,0
|
| 22 |
+
2057000158,0
|
| 23 |
+
1245640028,0
|
| 24 |
+
2025000002,0
|
| 25 |
+
457380004,0
|
| 26 |
+
518580020,0
|
| 27 |
+
2057000141,0
|
| 28 |
+
1063530025,0
|
| 29 |
+
1561560017,0
|
| 30 |
+
490250002,0
|
| 31 |
+
997760003,0
|
| 32 |
+
2057000165,0
|
| 33 |
+
1172280005,0
|
| 34 |
+
1801560005,0
|
| 35 |
+
790750006,0
|
| 36 |
+
812460002,0
|
| 37 |
+
1337060017,0
|
| 38 |
+
982710011,0
|
| 39 |
+
1337530025,0
|
| 40 |
+
1595490014,0
|
| 41 |
+
1245640023,0
|
| 42 |
+
2293180003,0
|
| 43 |
+
2057000146,0
|
| 44 |
+
1063530013,0
|
| 45 |
+
898080033,0
|
| 46 |
+
1337530054,0
|
| 47 |
+
1245640031,0
|
| 48 |
+
2057000066,0
|
| 49 |
+
815280010,0
|
| 50 |
+
2057000168,0
|
| 51 |
+
982710013,0
|
| 52 |
+
490250001,0
|
| 53 |
+
1486660008,0
|
| 54 |
+
1178780004,0
|
| 55 |
+
600140016,0
|
| 56 |
+
1026760002,0
|
| 57 |
+
1113370003,0
|
| 58 |
+
910190006,0
|
| 59 |
+
2025000001,0
|
| 60 |
+
898080014,0
|
| 61 |
+
1595490011,0
|
| 62 |
+
1825460005,0
|
| 63 |
+
1550280040,0
|
| 64 |
+
2163140037,0
|
| 65 |
+
1337060013,0
|
| 66 |
+
2057000070,0
|
| 67 |
+
1705030004,0
|
| 68 |
+
457380002,0
|
| 69 |
+
2057000100,0
|
| 70 |
+
898080011,0
|
| 71 |
+
533970002,0
|
| 72 |
+
1210650022,0
|
| 73 |
+
2057000156,0
|
| 74 |
+
2057000014,0
|
| 75 |
+
891960004,0
|
| 76 |
+
898080012,0
|
| 77 |
+
2057000036,0
|
| 78 |
+
716260007,0
|
| 79 |
+
1063530024,0
|
| 80 |
+
1245640012,0
|
| 81 |
+
1026760003,0
|
| 82 |
+
2057000140,0
|
| 83 |
+
898080008,0
|
| 84 |
+
866540002,0
|
| 85 |
+
1026760014,0
|
| 86 |
+
1269890008,0
|
| 87 |
+
2057000113,0
|
| 88 |
+
1337530082,0
|
| 89 |
+
2057000019,0
|
| 90 |
+
1801560002,0
|
| 91 |
+
1337530073,0
|
| 92 |
+
2163140019,0
|
| 93 |
+
1550280047,0
|
| 94 |
+
2057000149,0
|
| 95 |
+
1801560007,0
|
| 96 |
+
866540003,0
|
| 97 |
+
1270910004,0
|
| 98 |
+
591680003,0
|
| 99 |
+
1270910001,0
|
| 100 |
+
1178780003,0
|
| 101 |
+
1150310006,0
|
| 102 |
+
1550280011,0
|
| 103 |
+
2224020045,0
|
| 104 |
+
2057000060,0
|
| 105 |
+
997760008,0
|
| 106 |
+
1550280003,0
|
| 107 |
+
463290001,0
|
| 108 |
+
1248270006,0
|
| 109 |
+
451980012,0
|
| 110 |
+
1906880002,0
|
| 111 |
+
1931980009,0
|
| 112 |
+
1480650002,0
|
| 113 |
+
1172280007,0
|
| 114 |
+
726910011,0
|
| 115 |
+
998660004,0
|
| 116 |
+
1453730001,0
|
| 117 |
+
815280014,0
|
| 118 |
+
898080006,0
|
| 119 |
+
457550001,0
|
| 120 |
+
490250012,0
|
| 121 |
+
591680007,0
|
| 122 |
+
2057000005,0
|
| 123 |
+
343740007,0
|
| 124 |
+
2057000003,0
|
| 125 |
+
1512840005,0
|
| 126 |
+
997760002,0
|
| 127 |
+
1337530040,0
|
| 128 |
+
457380009,0
|
| 129 |
+
1456280004,0
|
| 130 |
+
1382350003,0
|
| 131 |
+
2057000093,0
|
| 132 |
+
1248270003,0
|
| 133 |
+
2057000151,0
|
| 134 |
+
1705030009,0
|
| 135 |
+
1269890007,0
|
| 136 |
+
1337530066,0
|
| 137 |
+
1063530043,0
|
| 138 |
+
1486660006,0
|
| 139 |
+
898080021,0
|
| 140 |
+
1595490015,0
|
| 141 |
+
2057000067,0
|
| 142 |
+
716260006,0
|
| 143 |
+
660520010,0
|
| 144 |
+
591680001,0
|
| 145 |
+
731790004,0
|
| 146 |
+
1178780008,0
|
| 147 |
+
1245640027,0
|
| 148 |
+
716260018,0
|
| 149 |
+
1337060032,0
|
| 150 |
+
2057000096,0
|
| 151 |
+
1245640033,0
|
| 152 |
+
1862180003,0
|
| 153 |
+
898080032,0
|
| 154 |
+
2163140023,0
|
| 155 |
+
1805510019,0
|
| 156 |
+
1550280048,0
|
| 157 |
+
910190002,0
|
| 158 |
+
891960002,0
|
| 159 |
+
726910008,0
|
| 160 |
+
2057000012,0
|
| 161 |
+
451980007,0
|
| 162 |
+
1481400002,0
|
| 163 |
+
2224020058,0
|
| 164 |
+
1337530060,0
|
| 165 |
+
1561560019,0
|
| 166 |
+
1488730001,0
|
| 167 |
+
1595490016,0
|
| 168 |
+
1269890014,0
|
| 169 |
+
1337530044,0
|
| 170 |
+
1931980014,0
|
| 171 |
+
2308940007,0
|
| 172 |
+
1805510014,0
|
| 173 |
+
1404360015,0
|
| 174 |
+
716260008,0
|
| 175 |
+
2057000057,0
|
| 176 |
+
1337060036,0
|
| 177 |
+
1707840003,0
|
| 178 |
+
2057000129,0
|
| 179 |
+
1550280015,0
|
| 180 |
+
2025000003,0
|
| 181 |
+
518580008,0
|
| 182 |
+
2163140020,0
|
| 183 |
+
1550280002,0
|
| 184 |
+
716260020,0
|
| 185 |
+
1801560009,0
|
| 186 |
+
1862180006,0
|
| 187 |
+
1652800006,0
|
| 188 |
+
1695870003,0
|
| 189 |
+
2163140002,0
|
| 190 |
+
1931980006,0
|
| 191 |
+
2057000132,0
|
| 192 |
+
2057000091,0
|
| 193 |
+
891960001,0
|
| 194 |
+
1801560003,0
|
| 195 |
+
1172280006,0
|
| 196 |
+
660520015,0
|
| 197 |
+
898080002,0
|
| 198 |
+
1026760022,0
|
| 199 |
+
667010007,0
|
| 200 |
+
438100006,0
|
| 201 |
+
457380018,0
|
| 202 |
+
343740004,0
|
| 203 |
+
1245640007,0
|
| 204 |
+
1337530002,0
|
| 205 |
+
1652800003,0
|
| 206 |
+
528580001,0
|
| 207 |
+
1245640021,0
|
| 208 |
+
815280007,0
|
| 209 |
+
1480650007,0
|
| 210 |
+
2057000170,0
|
| 211 |
+
1063530080,0
|
| 212 |
+
2163140010,0
|
| 213 |
+
714100008,0
|
| 214 |
+
2057000038,0
|
| 215 |
+
1453730011,0
|
| 216 |
+
600140001,0
|
| 217 |
+
720300005,0
|
| 218 |
+
605850001,0
|
| 219 |
+
1550280044,0
|
| 220 |
+
269170004,0
|
| 221 |
+
1550280046,0
|
| 222 |
+
1805510031,0
|
| 223 |
+
2057000157,0
|
| 224 |
+
1707840005,0
|
| 225 |
+
726910009,0
|
| 226 |
+
910190005,0
|
| 227 |
+
1561560009,0
|
| 228 |
+
451980017,0
|
| 229 |
+
2163140008,0
|
| 230 |
+
1561560030,0
|
| 231 |
+
1595490013,0
|
| 232 |
+
720300009,0
|
| 233 |
+
891960005,0
|
| 234 |
+
997760001,0
|
| 235 |
+
2057000172,0
|
| 236 |
+
1931980012,0
|
| 237 |
+
2057000125,0
|
| 238 |
+
490250019,0
|
| 239 |
+
2057000085,0
|
| 240 |
+
1705030016,0
|
| 241 |
+
1730290001,0
|
| 242 |
+
529150001,0
|
| 243 |
+
518580009,0
|
| 244 |
+
1157070005,0
|
| 245 |
+
2057000016,0
|
| 246 |
+
1453730014,0
|
| 247 |
+
2224020001,0
|
| 248 |
+
1063530034,0
|
| 249 |
+
2057000090,0
|
| 250 |
+
1250210001,0
|
| 251 |
+
269170008,0
|
| 252 |
+
790750004,0
|
| 253 |
+
1705030015,0
|
| 254 |
+
1150310002,0
|
| 255 |
+
1337530004,0
|
| 256 |
+
982710004,0
|
| 257 |
+
451980010,0
|
| 258 |
+
1337530030,0
|
| 259 |
+
2163140045,0
|
| 260 |
+
1248270002,0
|
| 261 |
+
343740005,0
|
| 262 |
+
2057000164,0
|
| 263 |
+
457380001,0
|
| 264 |
+
2057000139,0
|
| 265 |
+
660520013,0
|
| 266 |
+
1394410004,0
|
| 267 |
+
1805510022,0
|
| 268 |
+
982710015,0
|
| 269 |
+
438100010,0
|
| 270 |
+
1250210009,0
|
| 271 |
+
731790007,0
|
| 272 |
+
660520001,0
|
| 273 |
+
1337060041,0
|
| 274 |
+
716260019,0
|
| 275 |
+
1337530012,0
|
| 276 |
+
716260011,0
|
| 277 |
+
898080034,0
|
| 278 |
+
1561560020,0
|
| 279 |
+
1337060039,0
|
| 280 |
+
1269890015,0
|
| 281 |
+
2308940008,0
|
| 282 |
+
1157070006,0
|
| 283 |
+
1245640017,0
|
| 284 |
+
2163140052,0
|
| 285 |
+
1298890005,0
|
| 286 |
+
457380005,0
|
| 287 |
+
463290006,0
|
| 288 |
+
518580012,0
|
| 289 |
+
1707840002,0
|
| 290 |
+
2057000072,0
|
| 291 |
+
720300014,0
|
| 292 |
+
2057000116,0
|
| 293 |
+
1298890007,0
|
| 294 |
+
463290007,0
|
| 295 |
+
451980018,0
|
| 296 |
+
1245640018,0
|
| 297 |
+
2077870005,0
|
| 298 |
+
2057000044,0
|
| 299 |
+
1337530071,0
|
| 300 |
+
982710010,0
|
| 301 |
+
1337060006,0
|
| 302 |
+
1801560011,0
|
| 303 |
+
1825460008,0
|
| 304 |
+
1334900001,0
|
| 305 |
+
2057000073,0
|
| 306 |
+
1063530037,0
|
| 307 |
+
866540005,0
|
| 308 |
+
1805510013,0
|
| 309 |
+
2163140056,0
|
| 310 |
+
1931980008,0
|
| 311 |
+
1026760009,0
|
| 312 |
+
2057000105,0
|
| 313 |
+
898080013,0
|
| 314 |
+
1453730007,0
|
| 315 |
+
2057000144,0
|
| 316 |
+
1178780013,0
|
| 317 |
+
1063530028,0
|
| 318 |
+
2057000011,0
|
| 319 |
+
1063530051,0
|
| 320 |
+
2057000098,0
|
| 321 |
+
1178780007,0
|
| 322 |
+
2308940004,0
|
| 323 |
+
1150310001,0
|
| 324 |
+
1595490005,0
|
| 325 |
+
2057000025,0
|
| 326 |
+
2057000114,0
|
| 327 |
+
1245640001,0
|
| 328 |
+
2163140036,0
|
| 329 |
+
1453730012,0
|
| 330 |
+
1063530082,0
|
| 331 |
+
2308940006,0
|
| 332 |
+
1862180004,0
|
| 333 |
+
2057000147,0
|
| 334 |
+
1210650009,0
|
| 335 |
+
490250011,0
|
| 336 |
+
1705030012,0
|
| 337 |
+
1550280050,0
|
| 338 |
+
1264160014,0
|
| 339 |
+
1931980013,0
|
| 340 |
+
2057000034,0
|
| 341 |
+
2089520002,0
|
| 342 |
+
1337530026,0
|
| 343 |
+
2163140001,0
|
| 344 |
+
731790003,0
|
| 345 |
+
2057000099,0
|
| 346 |
+
716260014,0
|
| 347 |
+
1026760021,0
|
| 348 |
+
2077870004,0
|
| 349 |
+
667010006,0
|
| 350 |
+
1601810002,0
|
| 351 |
+
1550280033,0
|
| 352 |
+
1561560008,0
|
| 353 |
+
2163140003,0
|
| 354 |
+
1063530039,0
|
| 355 |
+
982710003,0
|
| 356 |
+
1595490012,0
|
| 357 |
+
1480650001,0
|
| 358 |
+
1801560004,0
|
| 359 |
+
898080020,0
|
| 360 |
+
1026760006,0
|
| 361 |
+
1113370001,0
|
| 362 |
+
2293180002,0
|
| 363 |
+
1063530064,0
|
| 364 |
+
1264160016,0
|
| 365 |
+
1652800008,0
|
| 366 |
+
1960620001,0
|
| 367 |
+
790750001,0
|
| 368 |
+
1486660004,0
|
| 369 |
+
2020950002,0
|
| 370 |
+
1026760018,0
|
| 371 |
+
1550280006,0
|
| 372 |
+
1178780014,0
|
| 373 |
+
2057000109,0
|
| 374 |
+
2057000137,0
|
| 375 |
+
898080007,0
|
| 376 |
+
1172280001,0
|
| 377 |
+
667010008,0
|
| 378 |
+
2293180001,0
|
| 379 |
+
2163140032,0
|
| 380 |
+
997760005,0
|
| 381 |
+
660520004,0
|
| 382 |
+
1250210010,0
|
| 383 |
+
2025000008,0
|
| 384 |
+
982710007,0
|
| 385 |
+
1404360011,0
|
| 386 |
+
1334900002,0
|
| 387 |
+
1805510029,0
|
| 388 |
+
1178780006,0
|
| 389 |
+
1805510005,0
|
| 390 |
+
1512840007,0
|
| 391 |
+
1172280003,0
|
| 392 |
+
1250210007,0
|
| 393 |
+
1404360026,0
|
| 394 |
+
812460001,0
|
| 395 |
+
1157070002,0
|
| 396 |
+
1595490009,0
|
| 397 |
+
1337060003,0
|
| 398 |
+
1595490003,0
|
| 399 |
+
438100005,0
|
| 400 |
+
1862180001,0
|
| 401 |
+
1245640019,0
|
| 402 |
+
1404360003,0
|
| 403 |
+
1210650019,0
|
| 404 |
+
1707840006,0
|
| 405 |
+
1248270001,0
|
| 406 |
+
1550280017,0
|
| 407 |
+
898080009,0
|
| 408 |
+
2057000022,0
|
| 409 |
+
2057000035,0
|
| 410 |
+
2308940009,0
|
| 411 |
+
1113370004,0
|
| 412 |
+
1245640013,0
|
| 413 |
+
1486660003,0
|
| 414 |
+
1337530032,0
|
| 415 |
+
726910001,0
|
| 416 |
+
667010002,0
|
| 417 |
+
451980016,0
|
| 418 |
+
2089520001,0
|
| 419 |
+
2057000076,0
|
| 420 |
+
2163140041,0
|
| 421 |
+
1550280024,0
|
| 422 |
+
2163140035,0
|
| 423 |
+
2163140005,0
|
| 424 |
+
451980004,0
|
| 425 |
+
463290005,0
|
| 426 |
+
2057000161,0
|
| 427 |
+
1595490017,0
|
| 428 |
+
998660001,0
|
| 429 |
+
457380019,0
|
| 430 |
+
1337530043,0
|
| 431 |
+
1595490018,0
|
| 432 |
+
2057000136,0
|
| 433 |
+
2057000167,0
|
| 434 |
+
1210650018,0
|
| 435 |
+
1825460007,0
|
| 436 |
+
982710002,0
|
| 437 |
+
1561560027,0
|
| 438 |
+
1652800001,0
|
| 439 |
+
2057000148,0
|
| 440 |
+
529150002,0
|
| 441 |
+
1960620016,0
|
| 442 |
+
1063530078,0
|
| 443 |
+
667010011,0
|
| 444 |
+
1960620010,0
|
| 445 |
+
1337530019,0
|
| 446 |
+
1404360025,0
|
| 447 |
+
1210650021,0
|
| 448 |
+
667010012,0
|
| 449 |
+
600140011,0
|
| 450 |
+
731790001,0
|
| 451 |
+
490250009,0
|
| 452 |
+
2057000020,0
|
| 453 |
+
815280004,0
|
| 454 |
+
2057000145,0
|
| 455 |
+
1063530047,0
|
| 456 |
+
1453730002,0
|
| 457 |
+
1705030010,0
|
| 458 |
+
1337530007,0
|
| 459 |
+
2163140044,0
|
| 460 |
+
1805510028,0
|
| 461 |
+
2057000102,0
|
| 462 |
+
910190003,0
|
| 463 |
+
1394210003,0
|
| 464 |
+
2057000043,0
|
| 465 |
+
2057000108,0
|
| 466 |
+
529150003,0
|
| 467 |
+
451980008,0
|
| 468 |
+
2057000150,0
|
| 469 |
+
1456280001,0
|
| 470 |
+
1595490010,0
|
| 471 |
+
2057000041,0
|
| 472 |
+
716260021,0
|
| 473 |
+
2163140051,0
|
| 474 |
+
1453730005,0
|
| 475 |
+
591680002,0
|
| 476 |
+
1486660001,0
|
| 477 |
+
451980014,0
|
| 478 |
+
518580007,0
|
| 479 |
+
1652800010,0
|
| 480 |
+
1178780001,0
|
| 481 |
+
2057000084,0
|
| 482 |
+
2057000021,0
|
| 483 |
+
1210650007,0
|
| 484 |
+
997760012,0
|
| 485 |
+
1931980005,0
|
| 486 |
+
1337530062,0
|
| 487 |
+
982710012,0
|
| 488 |
+
1550280008,0
|
| 489 |
+
1063530041,0
|
| 490 |
+
1486660013,0
|
| 491 |
+
1394210005,0
|
| 492 |
+
720300007,0
|
| 493 |
+
1210650017,0
|
| 494 |
+
1337530076,0
|
| 495 |
+
1269890005,0
|
| 496 |
+
600140003,0
|
| 497 |
+
1245640020,0
|
| 498 |
+
1033960004,0
|
| 499 |
+
1157070003,0
|
| 500 |
+
815280018,0
|
| 501 |
+
1245640029,0
|
| 502 |
+
1033960002,0
|
| 503 |
+
1210650010,0
|
| 504 |
+
528580002,0
|
| 505 |
+
1269890009,0
|
| 506 |
+
1269890013,0
|
| 507 |
+
982710009,0
|
| 508 |
+
457550005,0
|
| 509 |
+
815280003,0
|
| 510 |
+
1033960006,0
|
| 511 |
+
898080004,0
|
| 512 |
+
457380012,0
|
| 513 |
+
716260009,0
|
| 514 |
+
1157070004,0
|
| 515 |
+
1550280023,0
|
| 516 |
+
451980011,0
|
| 517 |
+
716260010,0
|
| 518 |
+
2057000163,0
|
| 519 |
+
1456280002,0
|
| 520 |
+
1337530064,0
|
| 521 |
+
457380006,0
|
| 522 |
+
716260001,0
|
| 523 |
+
2057000032,0
|
| 524 |
+
1033960005,0
|
| 525 |
+
2057000071,0
|
| 526 |
+
1705030013,0
|
| 527 |
+
451980001,0
|
| 528 |
+
1063530081,0
|
| 529 |
+
898080035,0
|
| 530 |
+
2057000055,0
|
| 531 |
+
2057000024,0
|
| 532 |
+
1337530055,0
|
| 533 |
+
1063530076,0
|
| 534 |
+
269170003,0
|
| 535 |
+
1561560023,0
|
| 536 |
+
1210650008,0
|
| 537 |
+
1394210009,0
|
| 538 |
+
2224020031,0
|
| 539 |
+
1250210002,0
|
| 540 |
+
1801560001,0
|
| 541 |
+
622310002,0
|
| 542 |
+
1026760004,0
|
| 543 |
+
1245640002,0
|
| 544 |
+
1245640035,0
|
| 545 |
+
898080010,0
|
| 546 |
+
1178780005,0
|
| 547 |
+
1805510027,0
|
| 548 |
+
2057000039,0
|
| 549 |
+
1561560018,0
|
| 550 |
+
1825460012,0
|
| 551 |
+
2077870002,0
|
| 552 |
+
1337530065,0
|
| 553 |
+
2057000008,0
|
| 554 |
+
1805510023,0
|
| 555 |
+
891960007,0
|
| 556 |
+
2057000112,0
|
| 557 |
+
1394210006,0
|
| 558 |
+
2057000069,0
|
| 559 |
+
1337530042,0
|
| 560 |
+
667010010,0
|
| 561 |
+
997760010,0
|
| 562 |
+
1805510033,0
|
| 563 |
+
1456280003,0
|
| 564 |
+
2057000154,0
|
| 565 |
+
2163140009,0
|
| 566 |
+
490250006,0
|
| 567 |
+
1394210004,0
|
| 568 |
+
2057000131,0
|
| 569 |
+
1337530001,0
|
| 570 |
+
1801560010,0
|
| 571 |
+
1805510007,0
|
| 572 |
+
726910006,0
|
| 573 |
+
1063530060,0
|
| 574 |
+
457380007,0
|
| 575 |
+
1063530046,0
|
| 576 |
+
1269890004,0
|
| 577 |
+
1264160017,0
|
| 578 |
+
2163140021,0
|
| 579 |
+
2163140054,0
|
| 580 |
+
529150004,0
|
| 581 |
+
898080016,0
|
| 582 |
+
815280002,0
|
| 583 |
+
1063530055,0
|
| 584 |
+
1245640008,0
|
| 585 |
+
457380014,0
|
| 586 |
+
2089520004,0
|
| 587 |
+
660520007,0
|
| 588 |
+
1063530038,0
|
| 589 |
+
1404360013,0
|
| 590 |
+
790750005,0
|
| 591 |
+
1337530016,0
|
| 592 |
+
2057000045,0
|
| 593 |
+
2057000176,0
|
| 594 |
+
1512840004,0
|
| 595 |
+
731790008,0
|
| 596 |
+
1805510004,0
|
| 597 |
+
1026760001,0
|
| 598 |
+
490250010,0
|
| 599 |
+
1676620001,0
|
| 600 |
+
1269890006,0
|
| 601 |
+
1217930001,0
|
| 602 |
+
866540011,0
|
| 603 |
+
1298890002,0
|
| 604 |
+
720300004,0
|
| 605 |
+
518580017,0
|
| 606 |
+
1026760012,0
|
| 607 |
+
1250210005,0
|
| 608 |
+
1337530036,0
|
| 609 |
+
1337530031,0
|
| 610 |
+
1486660014,0
|
| 611 |
+
1337060034,0
|
| 612 |
+
2057000092,0
|
| 613 |
+
866540007,0
|
| 614 |
+
720300013,0
|
| 615 |
+
1026760015,0
|
| 616 |
+
2308940011,0
|
| 617 |
+
600140015,0
|
| 618 |
+
1561560024,0
|
| 619 |
+
1485260003,0
|
| 620 |
+
898080015,0
|
| 621 |
+
518580014,0
|
| 622 |
+
1612820001,0
|
| 623 |
+
2057000110,0
|
| 624 |
+
1931980001,0
|
| 625 |
+
1601810003,0
|
| 626 |
+
2057000130,0
|
| 627 |
+
1695870001,0
|
| 628 |
+
720300010,0
|
| 629 |
+
998660005,0
|
| 630 |
+
2163140047,0
|
| 631 |
+
2057000160,0
|
| 632 |
+
1486660011,0
|
| 633 |
+
2163140031,0
|
| 634 |
+
667010001,0
|
| 635 |
+
1337060040,0
|
| 636 |
+
1595490020,0
|
| 637 |
+
2057000018,0
|
| 638 |
+
1595490007,0
|
| 639 |
+
1245640003,0
|
| 640 |
+
1033960003,0
|
| 641 |
+
1825460004,0
|
| 642 |
+
1337530059,0
|
| 643 |
+
866540004,0
|
| 644 |
+
1337530038,0
|
| 645 |
+
1394210008,0
|
| 646 |
+
1063530030,0
|
| 647 |
+
2057000089,0
|
| 648 |
+
518580022,0
|
| 649 |
+
1825460001,0
|
| 650 |
+
1550280020,0
|
| 651 |
+
1337530035,0
|
| 652 |
+
1337060028,0
|
| 653 |
+
2057000080,0
|
| 654 |
+
1245640004,0
|
| 655 |
+
1705030006,0
|
| 656 |
+
1485260004,0
|
| 657 |
+
1157070007,0
|
| 658 |
+
2057000051,0
|
| 659 |
+
1250210008,0
|
| 660 |
+
2057000115,0
|
| 661 |
+
1063530031,0
|
| 662 |
+
1264160001,0
|
| 663 |
+
343740006,0
|
| 664 |
+
731790005,0
|
| 665 |
+
660520006,0
|
| 666 |
+
2057000111,0
|
| 667 |
+
1063530068,0
|
| 668 |
+
1512840001,0
|
| 669 |
+
2224020002,0
|
| 670 |
+
2057000128,0
|
| 671 |
+
2057000037,0
|
| 672 |
+
898080017,0
|
| 673 |
+
622310001,0
|
| 674 |
+
2057000103,0
|
| 675 |
+
1404360019,0
|
| 676 |
+
1178780011,0
|
| 677 |
+
1337060029,0
|
| 678 |
+
2163140026,0
|
| 679 |
+
1730290002,0
|
| 680 |
+
1264160015,0
|
| 681 |
+
898080024,0
|
| 682 |
+
660520014,0
|
| 683 |
+
1486660005,0
|
| 684 |
+
2163140013,0
|
| 685 |
+
1550280032,0
|
| 686 |
+
2193270001,0
|
| 687 |
+
2163140018,0
|
| 688 |
+
2057000068,0
|
| 689 |
+
2163140049,0
|
| 690 |
+
1486660009,0
|
| 691 |
+
490250008,0
|
| 692 |
+
716260013,0
|
| 693 |
+
1337530051,0
|
| 694 |
+
1550280016,0
|
| 695 |
+
2163140027,0
|
| 696 |
+
720300016,0
|
| 697 |
+
1705030008,0
|
| 698 |
+
2057000106,0
|
| 699 |
+
1210650006,0
|
| 700 |
+
1337530081,0
|
| 701 |
+
1730290004,0
|
| 702 |
+
1337530033,0
|
| 703 |
+
1485260002,0
|
| 704 |
+
457380015,0
|
| 705 |
+
2057000054,0
|
| 706 |
+
1652800012,0
|
| 707 |
+
2025000007,0
|
| 708 |
+
2057000077,0
|
| 709 |
+
1612820002,0
|
| 710 |
+
891960008,0
|
| 711 |
+
1063530040,0
|
| 712 |
+
1652800009,0
|
| 713 |
+
1862180007,0
|
| 714 |
+
1063530071,0
|
| 715 |
+
600140006,0
|
| 716 |
+
1063530035,0
|
| 717 |
+
1245640024,0
|
| 718 |
+
457380016,0
|
| 719 |
+
2057000026,0
|
| 720 |
+
1960620015,0
|
| 721 |
+
451980002,0
|
| 722 |
+
1695870006,0
|
| 723 |
+
1264160002,0
|
| 724 |
+
660520011,0
|
| 725 |
+
463290004,0
|
| 726 |
+
1063530077,0
|
| 727 |
+
2057000017,0
|
| 728 |
+
1404360001,0
|
| 729 |
+
1862180002,0
|
| 730 |
+
631660001,0
|
| 731 |
+
1250210003,0
|
| 732 |
+
1026760025,0
|
| 733 |
+
898080003,0
|
| 734 |
+
2057000153,0
|
| 735 |
+
1063530056,0
|
| 736 |
+
997760011,0
|
| 737 |
+
1178780012,0
|
| 738 |
+
1550280028,0
|
| 739 |
+
898080029,0
|
| 740 |
+
1298890003,0
|
| 741 |
+
1337530029,0
|
| 742 |
+
1960620012,0
|
| 743 |
+
667010009,0
|
| 744 |
+
518580021,0
|
| 745 |
+
731790010,0
|
| 746 |
+
1931980002,0
|
| 747 |
+
1695870005,0
|
| 748 |
+
2308940014,0
|
| 749 |
+
497820002,0
|
| 750 |
+
1825460006,0
|
| 751 |
+
2057000171,0
|
| 752 |
+
997760007,0
|
| 753 |
+
910190007,0
|
| 754 |
+
2057000095,0
|
| 755 |
+
1382350005,0
|
| 756 |
+
1217930004,0
|
| 757 |
+
1026760005,0
|
| 758 |
+
2057000002,0
|
| 759 |
+
2308940002,0
|
| 760 |
+
1337530047,0
|
| 761 |
+
1217930003,0
|
| 762 |
+
2308940010,0
|
| 763 |
+
2163140030,0
|
| 764 |
+
1337530039,0
|
| 765 |
+
982710014,0
|
| 766 |
+
513490001,0
|
| 767 |
+
910190004,0
|
| 768 |
+
998660002,0
|
| 769 |
+
451980005,0
|
| 770 |
+
997760009,0
|
| 771 |
+
1210650015,0
|
| 772 |
+
451980003,0
|
| 773 |
+
2057000097,0
|
| 774 |
+
898080025,0
|
| 775 |
+
982710001,0
|
| 776 |
+
1931980007,0
|
| 777 |
+
716260012,0
|
| 778 |
+
2089520003,0
|
| 779 |
+
1825460011,0
|
| 780 |
+
1178780016,0
|
| 781 |
+
2308940012,0
|
| 782 |
+
898080036,0
|
| 783 |
+
2057000107,0
|
| 784 |
+
497820001,0
|
| 785 |
+
457380003,0
|
| 786 |
+
2057000050,0
|
| 787 |
+
1113370002,0
|
| 788 |
+
1063530045,0
|
| 789 |
+
343740003,0
|
| 790 |
+
1063530049,0
|
| 791 |
+
1178780010,0
|
| 792 |
+
1269890010,0
|
| 793 |
+
1404360024,0
|
| 794 |
+
2057000031,0
|
| 795 |
+
269170006,0
|
| 796 |
+
1550280007,0
|
| 797 |
+
1063530072,0
|
| 798 |
+
1652800011,0
|
| 799 |
+
2057000033,0
|
| 800 |
+
1707840001,0
|
| 801 |
+
2308940003,0
|
| 802 |
+
910190001,0
|
| 803 |
+
2057000015,0
|
| 804 |
+
1960620008,0
|
| 805 |
+
2057000173,0
|
| 806 |
+
2057000166,0
|
| 807 |
+
1805510032,0
|
| 808 |
+
2057000086,0
|
| 809 |
+
1595490006,0
|
| 810 |
+
1178780009,0
|
| 811 |
+
714100003,0
|
| 812 |
+
1481400001,0
|
| 813 |
+
1337530052,0
|
| 814 |
+
891960003,0
|
| 815 |
+
1707840004,0
|
| 816 |
+
997760004,0
|
| 817 |
+
1210650004,0
|
| 818 |
+
815280016,0
|
| 819 |
+
451980006,0
|
| 820 |
+
1550280009,0
|
| 821 |
+
1960620013,0
|
| 822 |
+
1485260001,0
|
| 823 |
+
714100002,0
|
| 824 |
+
660520002,0
|
| 825 |
+
1150310005,0
|
| 826 |
+
1337530011,0
|
| 827 |
+
1705030014,0
|
| 828 |
+
2057000048,0
|
| 829 |
+
1550280021,0
|
| 830 |
+
891960006,0
|
| 831 |
+
731790002,0
|
| 832 |
+
716260017,0
|
| 833 |
+
1705030001,0
|
| 834 |
+
1063530053,0
|
| 835 |
+
1652800013,0
|
| 836 |
+
720300006,0
|
| 837 |
+
815280001,0
|
| 838 |
+
269170002,0
|
| 839 |
+
866540009,0
|
| 840 |
+
2224020003,0
|
| 841 |
+
490250013,0
|
| 842 |
+
1382350001,0
|
| 843 |
+
1512840002,0
|
| 844 |
+
716260015,0
|
| 845 |
+
600140014,0
|
| 846 |
+
1210650003,0
|
| 847 |
+
1805510020,0
|
| 848 |
+
2057000065,0
|
| 849 |
+
1248270007,0
|
| 850 |
+
438100009,0
|
| 851 |
+
490250018,0
|
| 852 |
+
1337530074,0
|
| 853 |
+
2057000075,0
|
| 854 |
+
1157070008,0
|
| 855 |
+
2163140017,0
|
| 856 |
+
1337530053,0
|
| 857 |
+
1931980004,0
|
| 858 |
+
1453730008,0
|
| 859 |
+
1801560008,0
|
| 860 |
+
1595490004,0
|
| 861 |
+
1063530061,0
|
| 862 |
+
1337060027,0
|
| 863 |
+
2057000056,0
|
| 864 |
+
1248270009,0
|
| 865 |
+
1825460010,0
|
| 866 |
+
1063530063,0
|
| 867 |
+
1805510024,0
|
| 868 |
+
1404360027,0
|
| 869 |
+
1550280026,0
|
| 870 |
+
1063530005,0
|
| 871 |
+
2308940001,0
|
| 872 |
+
269170005,0
|
| 873 |
+
1394210010,0
|
| 874 |
+
1705030002,0
|
| 875 |
+
1245640022,0
|
| 876 |
+
1550280055,0
|
| 877 |
+
891960009,0
|
| 878 |
+
1652800007,0
|
| 879 |
+
438100002,0
|
| 880 |
+
2057000104,0
|
| 881 |
+
1931980003,0
|
| 882 |
+
1486660002,0
|
| 883 |
+
269170007,0
|
| 884 |
+
1337530021,0
|
| 885 |
+
1337530020,0
|
| 886 |
+
1561560029,0
|
| 887 |
+
1033960001,0
|
| 888 |
+
2057000023,0
|
| 889 |
+
1269890011,0
|
| 890 |
+
1245640011,0
|
| 891 |
+
1906880001,0
|
| 892 |
+
2020950001,0
|
| 893 |
+
1601810001,0
|
| 894 |
+
457380011,0
|
| 895 |
+
1394210001,0
|
| 896 |
+
2308940016,0
|
| 897 |
+
1486660007,0
|
| 898 |
+
438100001,0
|
| 899 |
+
1550280014,0
|
| 900 |
+
1550280025,0
|
| 901 |
+
1550280004,0
|
| 902 |
+
1705030007,0
|
| 903 |
+
997760006,0
|
| 904 |
+
720300018,0
|
| 905 |
+
1026760024,0
|
| 906 |
+
866540006,0
|
| 907 |
+
898080005,0
|
| 908 |
+
1805510030,0
|
| 909 |
+
1063530003,0
|
| 910 |
+
1298890001,0
|
| 911 |
+
1210650001,0
|
| 912 |
+
2057000052,0
|
| 913 |
+
1250210011,0
|
| 914 |
+
1394410003,0
|
| 915 |
+
1906880003,0
|
| 916 |
+
1250210006,0
|
| 917 |
+
2057000094,0
|
| 918 |
+
1337530080,0
|
| 919 |
+
1561560022,0
|
| 920 |
+
1248270013,0
|
| 921 |
+
2057000042,0
|
| 922 |
+
1217930005,0
|
| 923 |
+
1298890017,0
|
| 924 |
+
815280009,0
|
| 925 |
+
2057000061,0
|
| 926 |
+
720300011,0
|
| 927 |
+
714100001,0
|
| 928 |
+
1486660010,0
|
| 929 |
+
1337530028,0
|
| 930 |
+
898080023,0
|
| 931 |
+
2057000081,0
|
| 932 |
+
1270910003,0
|
| 933 |
+
2308940017,0
|
| 934 |
+
1337530008,0
|
| 935 |
+
2057000047,0
|
| 936 |
+
2057000135,0
|
| 937 |
+
1245640038,0
|
| 938 |
+
1561560026,0
|
| 939 |
+
1337060002,0
|
| 940 |
+
731790009,0
|
| 941 |
+
1026760016,0
|
| 942 |
+
1264160013,0
|
| 943 |
+
716260004,0
|
| 944 |
+
2057000079,0
|
| 945 |
+
1337530058,0
|
| 946 |
+
1561560004,0
|
| 947 |
+
1801560006,0
|
| 948 |
+
1404360002,0
|
| 949 |
+
1652800004,1
|
| 950 |
+
1652800005,1
|
| 951 |
+
1707840008,1
|
| 952 |
+
591680008,1
|
| 953 |
+
1394410001,1
|
| 954 |
+
1394410002,1
|
| 955 |
+
1394410005,1
|
| 956 |
+
1063530006,1
|
| 957 |
+
1063530017,1
|
| 958 |
+
438100004,1
|
| 959 |
+
1063530020,1
|
| 960 |
+
1063530022,1
|
| 961 |
+
438100007,1
|
| 962 |
+
438100008,1
|
| 963 |
+
1063530026,1
|
| 964 |
+
1063530033,1
|
| 965 |
+
1270910002,1
|
| 966 |
+
1695870002,1
|
| 967 |
+
1695870004,1
|
| 968 |
+
1063530036,1
|
| 969 |
+
1063530042,1
|
| 970 |
+
1063530044,1
|
| 971 |
+
2057000001,1
|
| 972 |
+
660520003,1
|
| 973 |
+
2057000006,1
|
| 974 |
+
1063530054,1
|
| 975 |
+
660520008,1
|
| 976 |
+
2057000010,1
|
| 977 |
+
1063530058,1
|
| 978 |
+
660520012,1
|
| 979 |
+
1063530062,1
|
| 980 |
+
1295570001,1
|
| 981 |
+
2224020049,1
|
| 982 |
+
533970001,1
|
| 983 |
+
533970003,1
|
| 984 |
+
1063530066,1
|
| 985 |
+
513490002,1
|
| 986 |
+
1063530075,1
|
| 987 |
+
343740002,1
|
| 988 |
+
2057000040,1
|
| 989 |
+
2057000046,1
|
| 990 |
+
2057000049,1
|
| 991 |
+
1150310004,1
|
| 992 |
+
2057000058,1
|
| 993 |
+
2057000078,1
|
| 994 |
+
2057000082,1
|
| 995 |
+
1337530015,1
|
| 996 |
+
1337060001,1
|
| 997 |
+
1337060004,1
|
| 998 |
+
1862180005,1
|
| 999 |
+
1337060014,1
|
| 1000 |
+
716260016,1
|
| 1001 |
+
457380017,1
|
| 1002 |
+
1337060016,1
|
| 1003 |
+
1157070001,1
|
| 1004 |
+
731790006,1
|
| 1005 |
+
1337530045,1
|
| 1006 |
+
1337060031,1
|
| 1007 |
+
1337060033,1
|
| 1008 |
+
1172280002,1
|
| 1009 |
+
1481400003,1
|
| 1010 |
+
1172280004,1
|
| 1011 |
+
1172280008,1
|
| 1012 |
+
1172280009,1
|
| 1013 |
+
891960010,1
|
| 1014 |
+
1337530056,1
|
| 1015 |
+
1595490001,1
|
| 1016 |
+
1394210002,1
|
| 1017 |
+
1453730003,1
|
| 1018 |
+
1250210004,1
|
| 1019 |
+
1337530068,1
|
| 1020 |
+
1453730006,1
|
| 1021 |
+
2057000152,1
|
| 1022 |
+
2057000155,1
|
| 1023 |
+
1250210012,1
|
| 1024 |
+
1337530078,1
|
| 1025 |
+
2057000159,1
|
| 1026 |
+
1676620002,1
|
| 1027 |
+
1595490019,1
|
| 1028 |
+
2308940005,1
|
| 1029 |
+
1485260005,1
|
| 1030 |
+
451980009,1
|
| 1031 |
+
2057000169,1
|
| 1032 |
+
1931980010,1
|
| 1033 |
+
2308940013,1
|
| 1034 |
+
451980013,1
|
| 1035 |
+
2308940015,1
|
| 1036 |
+
451980015,1
|
| 1037 |
+
2057000174,1
|
| 1038 |
+
2057000177,1
|
| 1039 |
+
600140019,1
|
| 1040 |
+
898080001,1
|
| 1041 |
+
1264160012,1
|
| 1042 |
+
1217930002,1
|
| 1043 |
+
490250007,1
|
| 1044 |
+
898080030,1
|
| 1045 |
+
1298890015,1
|
| 1046 |
+
898080031,1
|
| 1047 |
+
490250016,1
|
| 1048 |
+
714100005,1
|
| 1049 |
+
518580013,1
|
| 1050 |
+
790750002,1
|
| 1051 |
+
790750003,1
|
| 1052 |
+
518580018,1
|
| 1053 |
+
518580019,1
|
| 1054 |
+
1512840003,1
|
| 1055 |
+
1550280005,1
|
| 1056 |
+
1512840006,1
|
| 1057 |
+
1026760007,1
|
| 1058 |
+
1026760008,1
|
| 1059 |
+
1026760010,1
|
| 1060 |
+
1245640010,1
|
| 1061 |
+
1026760013,1
|
| 1062 |
+
1730290003,1
|
| 1063 |
+
1730290005,1
|
| 1064 |
+
1550280022,1
|
| 1065 |
+
1245640025,1
|
| 1066 |
+
1245640026,1
|
| 1067 |
+
1026760026,1
|
| 1068 |
+
1026760027,1
|
| 1069 |
+
1245640030,1
|
| 1070 |
+
1178780002,1
|
| 1071 |
+
497820003,1
|
| 1072 |
+
1550280038,1
|
| 1073 |
+
1705030003,1
|
| 1074 |
+
1550280054,1
|
| 1075 |
+
1705030011,1
|
| 1076 |
+
1805510017,1
|
| 1077 |
+
1805510018,1
|
| 1078 |
+
815280005,1
|
| 1079 |
+
1805510025,1
|
| 1080 |
+
815280011,1
|
| 1081 |
+
815280013,1
|
| 1082 |
+
815280015,1
|
| 1083 |
+
815280017,1
|
| 1084 |
+
1210650016,1
|
| 1085 |
+
998660003,1
|
| 1086 |
+
2163140028,1
|
| 1087 |
+
1561560003,1
|
| 1088 |
+
2163140040,1
|
| 1089 |
+
2163140046,1
|
| 1090 |
+
1269890001,1
|
| 1091 |
+
667010004,1
|
| 1092 |
+
1561560021,1
|
| 1093 |
+
667010005,1
|
| 1094 |
+
1561560025,1
|
| 1095 |
+
1269890012,1
|
| 1096 |
+
1561560028,1
|
| 1097 |
+
1561560032,1
|
| 1098 |
+
866540001,1
|
| 1099 |
+
1960620002,1
|
| 1100 |
+
720300001,1
|
| 1101 |
+
720300002,1
|
| 1102 |
+
866540010,1
|
| 1103 |
+
1960620011,1
|
| 1104 |
+
1960620014,1
|
| 1105 |
+
720300015,1
|
| 1106 |
+
2020950003,1
|
| 1107 |
+
438100003,2
|
| 1108 |
+
1388030001,2
|
| 1109 |
+
457380008,2
|
| 1110 |
+
457380010,2
|
| 1111 |
+
457380013,2
|
| 1112 |
+
457550002,2
|
| 1113 |
+
457550003,2
|
| 1114 |
+
457550004,2
|
| 1115 |
+
1453730004,2
|
| 1116 |
+
1453730009,2
|
| 1117 |
+
1453730010,2
|
| 1118 |
+
600140002,2
|
| 1119 |
+
1676620003,2
|
| 1120 |
+
1676620004,2
|
| 1121 |
+
1676620005,2
|
| 1122 |
+
1676620006,2
|
| 1123 |
+
1676620007,2
|
| 1124 |
+
600140005,2
|
| 1125 |
+
600140007,2
|
| 1126 |
+
600140008,2
|
| 1127 |
+
600140009,2
|
| 1128 |
+
600140010,2
|
| 1129 |
+
600140012,2
|
| 1130 |
+
600140013,2
|
| 1131 |
+
600140017,2
|
| 1132 |
+
600140018,2
|
| 1133 |
+
600140020,2
|
| 1134 |
+
600140021,2
|
| 1135 |
+
600140022,2
|
| 1136 |
+
600140023,2
|
| 1137 |
+
600140024,2
|
| 1138 |
+
1178780015,2
|
| 1139 |
+
1486660012,2
|
| 1140 |
+
1652800002,2
|
| 1141 |
+
591680004,2
|
| 1142 |
+
591680005,2
|
| 1143 |
+
591680006,2
|
| 1144 |
+
1707840007,2
|
| 1145 |
+
591680009,2
|
| 1146 |
+
591680010,2
|
| 1147 |
+
591680011,2
|
| 1148 |
+
591680012,2
|
| 1149 |
+
591680013,2
|
| 1150 |
+
2224020004,2
|
| 1151 |
+
2224020005,2
|
| 1152 |
+
2224020006,2
|
| 1153 |
+
2224020007,2
|
| 1154 |
+
2224020008,2
|
| 1155 |
+
2224020009,2
|
| 1156 |
+
2224020010,2
|
| 1157 |
+
2224020011,2
|
| 1158 |
+
2224020012,2
|
| 1159 |
+
2224020013,2
|
| 1160 |
+
2224020014,2
|
| 1161 |
+
2224020015,2
|
| 1162 |
+
2224020016,2
|
| 1163 |
+
2224020017,2
|
| 1164 |
+
2224020018,2
|
| 1165 |
+
2224020019,2
|
| 1166 |
+
2224020020,2
|
| 1167 |
+
2224020021,2
|
| 1168 |
+
2224020022,2
|
| 1169 |
+
2224020023,2
|
| 1170 |
+
2224020024,2
|
| 1171 |
+
2224020025,2
|
| 1172 |
+
2224020026,2
|
| 1173 |
+
2224020027,2
|
| 1174 |
+
2224020028,2
|
| 1175 |
+
2224020029,2
|
| 1176 |
+
2224020030,2
|
| 1177 |
+
2224020032,2
|
| 1178 |
+
2224020033,2
|
| 1179 |
+
2224020034,2
|
| 1180 |
+
2224020035,2
|
| 1181 |
+
2224020036,2
|
| 1182 |
+
2224020037,2
|
| 1183 |
+
2224020038,2
|
| 1184 |
+
2224020039,2
|
| 1185 |
+
2224020040,2
|
| 1186 |
+
2224020041,2
|
| 1187 |
+
2224020042,2
|
| 1188 |
+
2224020043,2
|
| 1189 |
+
2224020044,2
|
| 1190 |
+
2224020046,2
|
| 1191 |
+
2224020047,2
|
| 1192 |
+
2224020048,2
|
| 1193 |
+
2224020050,2
|
| 1194 |
+
2224020051,2
|
| 1195 |
+
2224020052,2
|
| 1196 |
+
2224020053,2
|
| 1197 |
+
2224020054,2
|
| 1198 |
+
2224020055,2
|
| 1199 |
+
2224020056,2
|
| 1200 |
+
2224020057,2
|
| 1201 |
+
716260002,2
|
| 1202 |
+
716260003,2
|
| 1203 |
+
716260005,2
|
| 1204 |
+
1337060007,2
|
| 1205 |
+
1337060008,2
|
| 1206 |
+
1337060009,2
|
| 1207 |
+
1337060010,2
|
| 1208 |
+
1337060011,2
|
| 1209 |
+
1337060012,2
|
| 1210 |
+
1337060015,2
|
| 1211 |
+
1337060018,2
|
| 1212 |
+
1337060019,2
|
| 1213 |
+
1337060020,2
|
| 1214 |
+
1337060021,2
|
| 1215 |
+
1337060022,2
|
| 1216 |
+
1337060023,2
|
| 1217 |
+
1337060024,2
|
| 1218 |
+
1337060025,2
|
| 1219 |
+
1337060026,2
|
| 1220 |
+
1337060030,2
|
| 1221 |
+
1337060035,2
|
| 1222 |
+
954160001,2
|
| 1223 |
+
954160002,2
|
| 1224 |
+
1561560001,2
|
| 1225 |
+
1561560002,2
|
| 1226 |
+
1561560005,2
|
| 1227 |
+
1561560006,2
|
| 1228 |
+
1561560007,2
|
| 1229 |
+
1561560010,2
|
| 1230 |
+
1561560011,2
|
| 1231 |
+
1561560012,2
|
| 1232 |
+
1561560013,2
|
| 1233 |
+
1561560014,2
|
| 1234 |
+
1561560015,2
|
| 1235 |
+
1561560016,2
|
| 1236 |
+
1269890002,2
|
| 1237 |
+
1269890003,2
|
| 1238 |
+
1561560031,2
|
| 1239 |
+
726910002,2
|
| 1240 |
+
726910003,2
|
| 1241 |
+
726910004,2
|
| 1242 |
+
726910005,2
|
| 1243 |
+
726910007,2
|
| 1244 |
+
726910010,2
|
| 1245 |
+
2025000004,2
|
| 1246 |
+
2025000005,2
|
| 1247 |
+
2025000006,2
|
| 1248 |
+
2057000004,2
|
| 1249 |
+
2057000007,2
|
| 1250 |
+
2025000009,2
|
| 1251 |
+
2025000010,2
|
| 1252 |
+
2057000009,2
|
| 1253 |
+
660520005,2
|
| 1254 |
+
2057000013,2
|
| 1255 |
+
660520009,2
|
| 1256 |
+
2057000027,2
|
| 1257 |
+
2057000028,2
|
| 1258 |
+
2057000029,2
|
| 1259 |
+
2057000030,2
|
| 1260 |
+
2057000053,2
|
| 1261 |
+
2057000059,2
|
| 1262 |
+
2057000062,2
|
| 1263 |
+
2057000063,2
|
| 1264 |
+
2057000064,2
|
| 1265 |
+
2057000074,2
|
| 1266 |
+
2057000083,2
|
| 1267 |
+
2057000088,2
|
| 1268 |
+
1382350002,2
|
| 1269 |
+
1382350004,2
|
| 1270 |
+
2057000117,2
|
| 1271 |
+
2057000118,2
|
| 1272 |
+
2057000119,2
|
| 1273 |
+
2057000120,2
|
| 1274 |
+
2057000121,2
|
| 1275 |
+
2057000122,2
|
| 1276 |
+
2057000123,2
|
| 1277 |
+
2057000124,2
|
| 1278 |
+
1382350006,2
|
| 1279 |
+
2057000126,2
|
| 1280 |
+
2057000127,2
|
| 1281 |
+
1382350007,2
|
| 1282 |
+
1382350008,2
|
| 1283 |
+
2057000133,2
|
| 1284 |
+
2057000134,2
|
| 1285 |
+
2057000138,2
|
| 1286 |
+
2057000142,2
|
| 1287 |
+
2057000143,2
|
| 1288 |
+
2057000162,2
|
| 1289 |
+
1931980011,2
|
| 1290 |
+
2057000178,2
|
| 1291 |
+
1264160003,2
|
| 1292 |
+
1264160004,2
|
| 1293 |
+
1264160005,2
|
| 1294 |
+
1264160006,2
|
| 1295 |
+
1264160007,2
|
| 1296 |
+
1264160008,2
|
| 1297 |
+
1264160009,2
|
| 1298 |
+
1264160010,2
|
| 1299 |
+
1264160011,2
|
| 1300 |
+
898080018,2
|
| 1301 |
+
1480650003,2
|
| 1302 |
+
1298890004,2
|
| 1303 |
+
1480650004,2
|
| 1304 |
+
1298890006,2
|
| 1305 |
+
1480650005,2
|
| 1306 |
+
1298890008,2
|
| 1307 |
+
1298890009,2
|
| 1308 |
+
1298890010,2
|
| 1309 |
+
1480650006,2
|
| 1310 |
+
1298890012,2
|
| 1311 |
+
1298890013,2
|
| 1312 |
+
1298890014,2
|
| 1313 |
+
1480650008,2
|
| 1314 |
+
1298890016,2
|
| 1315 |
+
898080026,2
|
| 1316 |
+
898080027,2
|
| 1317 |
+
1298890019,2
|
| 1318 |
+
1298890020,2
|
| 1319 |
+
1298890021,2
|
| 1320 |
+
1334900006,2
|
| 1321 |
+
1334900003,2
|
| 1322 |
+
1334900004,2
|
| 1323 |
+
1334900005,2
|
| 1324 |
+
714100004,2
|
| 1325 |
+
714100006,2
|
| 1326 |
+
714100007,2
|
| 1327 |
+
1550280001,2
|
| 1328 |
+
1404360004,2
|
| 1329 |
+
1245640005,2
|
| 1330 |
+
1245640006,2
|
| 1331 |
+
1404360005,2
|
| 1332 |
+
1404360006,2
|
| 1333 |
+
1245640009,2
|
| 1334 |
+
1404360007,2
|
| 1335 |
+
1026760011,2
|
| 1336 |
+
1404360009,2
|
| 1337 |
+
1404360010,2
|
| 1338 |
+
1245640014,2
|
| 1339 |
+
1245640015,2
|
| 1340 |
+
1245640016,2
|
| 1341 |
+
1026760017,2
|
| 1342 |
+
1404360016,2
|
| 1343 |
+
1026760019,2
|
| 1344 |
+
1026760020,2
|
| 1345 |
+
1404360018,2
|
| 1346 |
+
1404360022,2
|
| 1347 |
+
1026760023,2
|
| 1348 |
+
1404360023,2
|
| 1349 |
+
1550280018,2
|
| 1350 |
+
269170001,2
|
| 1351 |
+
1550280027,2
|
| 1352 |
+
269170009,2
|
| 1353 |
+
1404360029,2
|
| 1354 |
+
1550280029,2
|
| 1355 |
+
1550280030,2
|
| 1356 |
+
1245640032,2
|
| 1357 |
+
1550280031,2
|
| 1358 |
+
1245640034,2
|
| 1359 |
+
1550280035,2
|
| 1360 |
+
1245640036,2
|
| 1361 |
+
1245640037,2
|
| 1362 |
+
1550280036,2
|
| 1363 |
+
1550280037,2
|
| 1364 |
+
1550280039,2
|
| 1365 |
+
1550280041,2
|
| 1366 |
+
1550280042,2
|
| 1367 |
+
1550280043,2
|
| 1368 |
+
1550280045,2
|
| 1369 |
+
1550280049,2
|
| 1370 |
+
1805510001,2
|
| 1371 |
+
1550280051,2
|
| 1372 |
+
1550280052,2
|
| 1373 |
+
1550280053,2
|
| 1374 |
+
1805510002,2
|
| 1375 |
+
1805510003,2
|
| 1376 |
+
1805510006,2
|
| 1377 |
+
1805510008,2
|
| 1378 |
+
1805510009,2
|
| 1379 |
+
1805510010,2
|
| 1380 |
+
1805510011,2
|
| 1381 |
+
1805510012,2
|
| 1382 |
+
1805510015,2
|
| 1383 |
+
1805510016,2
|
| 1384 |
+
1805510021,2
|
| 1385 |
+
1805510026,2
|
| 1386 |
+
1210650002,2
|
| 1387 |
+
1210650005,2
|
| 1388 |
+
1210650011,2
|
| 1389 |
+
1210650012,2
|
| 1390 |
+
1210650013,2
|
| 1391 |
+
1210650014,2
|
| 1392 |
+
1550280010,2
|
| 1393 |
+
528580003,2
|
| 1394 |
+
2163140004,2
|
| 1395 |
+
528580004,2
|
| 1396 |
+
2163140006,2
|
| 1397 |
+
1210650023,2
|
| 1398 |
+
1210650024,2
|
| 1399 |
+
1210650025,2
|
| 1400 |
+
1210650026,2
|
| 1401 |
+
1210650027,2
|
| 1402 |
+
1210650028,2
|
| 1403 |
+
1550280012,2
|
| 1404 |
+
1550280013,2
|
| 1405 |
+
2163140007,2
|
| 1406 |
+
2163140011,2
|
| 1407 |
+
2163140012,2
|
| 1408 |
+
2163140014,2
|
| 1409 |
+
2163140015,2
|
| 1410 |
+
2163140016,2
|
| 1411 |
+
2163140022,2
|
| 1412 |
+
2163140024,2
|
| 1413 |
+
2163140025,2
|
| 1414 |
+
2163140029,2
|
| 1415 |
+
2163140033,2
|
| 1416 |
+
2163140034,2
|
| 1417 |
+
2163140038,2
|
| 1418 |
+
2163140039,2
|
| 1419 |
+
2163140042,2
|
| 1420 |
+
2163140043,2
|
| 1421 |
+
2163140048,2
|
| 1422 |
+
758210001,2
|
| 1423 |
+
2163140050,2
|
| 1424 |
+
758210002,2
|
| 1425 |
+
758210003,2
|
| 1426 |
+
2163140053,2
|
| 1427 |
+
758210004,2
|
| 1428 |
+
2163140055,2
|
| 1429 |
+
758210005,2
|
| 1430 |
+
2163140057,2
|
| 1431 |
+
2163140058,2
|
| 1432 |
+
758210006,2
|
| 1433 |
+
758210007,2
|
| 1434 |
+
758210008,2
|
| 1435 |
+
758210009,2
|
| 1436 |
+
758210010,2
|
| 1437 |
+
758210011,2
|
| 1438 |
+
758210012,2
|
| 1439 |
+
758210013,2
|
| 1440 |
+
758210014,2
|
| 1441 |
+
1063530001,2
|
| 1442 |
+
1063530002,2
|
| 1443 |
+
1063530004,2
|
| 1444 |
+
1063530007,2
|
| 1445 |
+
1063530008,2
|
| 1446 |
+
1063530009,2
|
| 1447 |
+
1063530010,2
|
| 1448 |
+
1063530011,2
|
| 1449 |
+
1063530012,2
|
| 1450 |
+
1063530014,2
|
| 1451 |
+
1063530015,2
|
| 1452 |
+
1063530016,2
|
| 1453 |
+
1063530018,2
|
| 1454 |
+
1063530019,2
|
| 1455 |
+
1063530021,2
|
| 1456 |
+
1063530023,2
|
| 1457 |
+
1063530029,2
|
| 1458 |
+
1063530032,2
|
| 1459 |
+
1063530048,2
|
| 1460 |
+
1063530052,2
|
| 1461 |
+
898080019,2
|
| 1462 |
+
1063530057,2
|
| 1463 |
+
1063530059,2
|
| 1464 |
+
1063530065,2
|
| 1465 |
+
1063530067,2
|
| 1466 |
+
1063530069,2
|
| 1467 |
+
1063530070,2
|
| 1468 |
+
898080022,2
|
| 1469 |
+
1063530073,2
|
| 1470 |
+
1063530074,2
|
| 1471 |
+
1063530079,2
|
| 1472 |
+
1063530083,2
|
| 1473 |
+
898080028,2
|
| 1474 |
+
463290002,2
|
| 1475 |
+
1337530003,2
|
| 1476 |
+
463290003,2
|
| 1477 |
+
1337530005,2
|
| 1478 |
+
1337530006,2
|
| 1479 |
+
463290008,2
|
| 1480 |
+
1337530009,2
|
| 1481 |
+
1337530010,2
|
| 1482 |
+
1337530013,2
|
| 1483 |
+
1337530014,2
|
| 1484 |
+
1337530017,2
|
| 1485 |
+
1337530018,2
|
| 1486 |
+
1337530022,2
|
| 1487 |
+
1337530023,2
|
| 1488 |
+
1337530024,2
|
| 1489 |
+
1337530027,2
|
| 1490 |
+
1337530034,2
|
| 1491 |
+
1248270004,2
|
| 1492 |
+
1337530037,2
|
| 1493 |
+
1248270005,2
|
| 1494 |
+
1248270008,2
|
| 1495 |
+
1337530041,2
|
| 1496 |
+
1248270010,2
|
| 1497 |
+
1248270011,2
|
| 1498 |
+
1248270012,2
|
| 1499 |
+
1337530046,2
|
| 1500 |
+
1337530048,2
|
| 1501 |
+
1337530049,2
|
| 1502 |
+
1337530050,2
|
| 1503 |
+
1337530057,2
|
| 1504 |
+
1337530061,2
|
| 1505 |
+
1337530063,2
|
| 1506 |
+
1337530067,2
|
| 1507 |
+
1337530069,2
|
| 1508 |
+
1337530070,2
|
| 1509 |
+
1595490008,2
|
| 1510 |
+
1337530075,2
|
| 1511 |
+
1337530077,2
|
| 1512 |
+
1337530079,2
|
| 1513 |
+
982710005,2
|
| 1514 |
+
982710006,2
|
| 1515 |
+
982710008,2
|
| 1516 |
+
490250003,2
|
| 1517 |
+
490250004,2
|
| 1518 |
+
490250005,2
|
| 1519 |
+
490250014,2
|
| 1520 |
+
490250015,2
|
| 1521 |
+
490250017,2
|
| 1522 |
+
1825460002,2
|
| 1523 |
+
1825460003,2
|
| 1524 |
+
518580001,2
|
| 1525 |
+
518580002,2
|
| 1526 |
+
518580003,2
|
| 1527 |
+
518580004,2
|
| 1528 |
+
518580005,2
|
| 1529 |
+
1825460009,2
|
| 1530 |
+
518580010,2
|
| 1531 |
+
518580011,2
|
| 1532 |
+
518580006,2
|
| 1533 |
+
518580015,2
|
| 1534 |
+
518580016,2
|
| 1535 |
+
518580023,2
|
| 1536 |
+
518580024,2
|
| 1537 |
+
815280006,2
|
| 1538 |
+
815280008,2
|
| 1539 |
+
815280012,2
|
| 1540 |
+
2077870001,2
|
| 1541 |
+
2077870003,2
|
| 1542 |
+
720300003,2
|
| 1543 |
+
812460003,2
|
| 1544 |
+
812460004,2
|
| 1545 |
+
812460005,2
|
| 1546 |
+
812460006,2
|
| 1547 |
+
720300008,2
|
| 1548 |
+
812460007,2
|
| 1549 |
+
812460008,2
|
| 1550 |
+
812460009,2
|
| 1551 |
+
720300012,2
|
| 1552 |
+
812460010,2
|
| 1553 |
+
720300017,2
|
dataset/fold_genre.csv
ADDED
|
@@ -0,0 +1,1553 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
img_id,fold
|
| 2 |
+
343740001,0
|
| 3 |
+
343740002,0
|
| 4 |
+
343740003,0
|
| 5 |
+
343740004,0
|
| 6 |
+
343740005,0
|
| 7 |
+
343740006,0
|
| 8 |
+
343740007,0
|
| 9 |
+
457380001,0
|
| 10 |
+
457380002,0
|
| 11 |
+
457380003,0
|
| 12 |
+
457380004,0
|
| 13 |
+
457380005,0
|
| 14 |
+
457380006,0
|
| 15 |
+
457380007,0
|
| 16 |
+
457380008,0
|
| 17 |
+
457380009,0
|
| 18 |
+
457380010,0
|
| 19 |
+
457380011,0
|
| 20 |
+
457380012,0
|
| 21 |
+
457380013,0
|
| 22 |
+
457380014,0
|
| 23 |
+
457380015,0
|
| 24 |
+
457380016,0
|
| 25 |
+
457380017,0
|
| 26 |
+
457380018,0
|
| 27 |
+
457380019,0
|
| 28 |
+
463290001,0
|
| 29 |
+
463290002,0
|
| 30 |
+
463290003,0
|
| 31 |
+
463290004,0
|
| 32 |
+
463290005,0
|
| 33 |
+
463290006,0
|
| 34 |
+
463290007,0
|
| 35 |
+
463290008,0
|
| 36 |
+
513490001,0
|
| 37 |
+
513490002,0
|
| 38 |
+
518580001,0
|
| 39 |
+
518580002,0
|
| 40 |
+
518580003,0
|
| 41 |
+
518580004,0
|
| 42 |
+
518580005,0
|
| 43 |
+
518580006,0
|
| 44 |
+
518580007,0
|
| 45 |
+
518580008,0
|
| 46 |
+
518580009,0
|
| 47 |
+
518580010,0
|
| 48 |
+
518580011,0
|
| 49 |
+
518580012,0
|
| 50 |
+
518580013,0
|
| 51 |
+
518580014,0
|
| 52 |
+
518580015,0
|
| 53 |
+
518580016,0
|
| 54 |
+
518580017,0
|
| 55 |
+
518580018,0
|
| 56 |
+
518580019,0
|
| 57 |
+
518580020,0
|
| 58 |
+
518580021,0
|
| 59 |
+
518580022,0
|
| 60 |
+
518580023,0
|
| 61 |
+
518580024,0
|
| 62 |
+
528580001,0
|
| 63 |
+
528580002,0
|
| 64 |
+
528580003,0
|
| 65 |
+
528580004,0
|
| 66 |
+
605850001,0
|
| 67 |
+
622310001,0
|
| 68 |
+
622310002,0
|
| 69 |
+
660520001,0
|
| 70 |
+
660520002,0
|
| 71 |
+
660520003,0
|
| 72 |
+
660520004,0
|
| 73 |
+
660520005,0
|
| 74 |
+
660520006,0
|
| 75 |
+
660520007,0
|
| 76 |
+
660520008,0
|
| 77 |
+
660520009,0
|
| 78 |
+
660520010,0
|
| 79 |
+
660520011,0
|
| 80 |
+
660520012,0
|
| 81 |
+
660520013,0
|
| 82 |
+
660520014,0
|
| 83 |
+
660520015,0
|
| 84 |
+
667010001,0
|
| 85 |
+
667010002,0
|
| 86 |
+
667010003,0
|
| 87 |
+
667010004,0
|
| 88 |
+
667010005,0
|
| 89 |
+
667010006,0
|
| 90 |
+
667010007,0
|
| 91 |
+
667010008,0
|
| 92 |
+
667010009,0
|
| 93 |
+
667010010,0
|
| 94 |
+
667010011,0
|
| 95 |
+
667010012,0
|
| 96 |
+
716260001,0
|
| 97 |
+
716260002,0
|
| 98 |
+
716260003,0
|
| 99 |
+
716260004,0
|
| 100 |
+
716260005,0
|
| 101 |
+
716260006,0
|
| 102 |
+
716260007,0
|
| 103 |
+
716260008,0
|
| 104 |
+
716260009,0
|
| 105 |
+
716260010,0
|
| 106 |
+
716260011,0
|
| 107 |
+
716260012,0
|
| 108 |
+
716260013,0
|
| 109 |
+
716260014,0
|
| 110 |
+
716260015,0
|
| 111 |
+
716260016,0
|
| 112 |
+
716260017,0
|
| 113 |
+
716260018,0
|
| 114 |
+
716260019,0
|
| 115 |
+
716260020,0
|
| 116 |
+
716260021,0
|
| 117 |
+
720300001,0
|
| 118 |
+
720300002,0
|
| 119 |
+
720300003,0
|
| 120 |
+
720300004,0
|
| 121 |
+
720300005,0
|
| 122 |
+
720300006,0
|
| 123 |
+
720300007,0
|
| 124 |
+
720300008,0
|
| 125 |
+
720300009,0
|
| 126 |
+
720300010,0
|
| 127 |
+
720300011,0
|
| 128 |
+
720300012,0
|
| 129 |
+
720300013,0
|
| 130 |
+
720300014,0
|
| 131 |
+
720300015,0
|
| 132 |
+
720300016,0
|
| 133 |
+
720300017,0
|
| 134 |
+
720300018,0
|
| 135 |
+
726910001,0
|
| 136 |
+
726910002,0
|
| 137 |
+
726910003,0
|
| 138 |
+
726910004,0
|
| 139 |
+
726910005,0
|
| 140 |
+
726910006,0
|
| 141 |
+
726910007,0
|
| 142 |
+
726910008,0
|
| 143 |
+
726910009,0
|
| 144 |
+
726910010,0
|
| 145 |
+
726910011,0
|
| 146 |
+
790750001,0
|
| 147 |
+
790750002,0
|
| 148 |
+
790750003,0
|
| 149 |
+
790750004,0
|
| 150 |
+
790750005,0
|
| 151 |
+
790750006,0
|
| 152 |
+
812460001,0
|
| 153 |
+
812460002,0
|
| 154 |
+
812460003,0
|
| 155 |
+
812460004,0
|
| 156 |
+
812460005,0
|
| 157 |
+
812460006,0
|
| 158 |
+
812460007,0
|
| 159 |
+
812460008,0
|
| 160 |
+
812460009,0
|
| 161 |
+
812460010,0
|
| 162 |
+
815280001,0
|
| 163 |
+
815280002,0
|
| 164 |
+
815280003,0
|
| 165 |
+
815280004,0
|
| 166 |
+
815280005,0
|
| 167 |
+
815280006,0
|
| 168 |
+
815280007,0
|
| 169 |
+
815280008,0
|
| 170 |
+
815280009,0
|
| 171 |
+
815280010,0
|
| 172 |
+
815280011,0
|
| 173 |
+
815280012,0
|
| 174 |
+
815280013,0
|
| 175 |
+
815280014,0
|
| 176 |
+
815280015,0
|
| 177 |
+
815280016,0
|
| 178 |
+
815280017,0
|
| 179 |
+
815280018,0
|
| 180 |
+
866540001,0
|
| 181 |
+
866540002,0
|
| 182 |
+
866540003,0
|
| 183 |
+
866540004,0
|
| 184 |
+
866540005,0
|
| 185 |
+
866540006,0
|
| 186 |
+
866540007,0
|
| 187 |
+
866540008,0
|
| 188 |
+
866540009,0
|
| 189 |
+
866540010,0
|
| 190 |
+
866540011,0
|
| 191 |
+
898080001,0
|
| 192 |
+
898080002,0
|
| 193 |
+
898080003,0
|
| 194 |
+
898080004,0
|
| 195 |
+
898080005,0
|
| 196 |
+
898080006,0
|
| 197 |
+
898080007,0
|
| 198 |
+
898080008,0
|
| 199 |
+
898080009,0
|
| 200 |
+
898080010,0
|
| 201 |
+
898080011,0
|
| 202 |
+
898080012,0
|
| 203 |
+
898080013,0
|
| 204 |
+
898080014,0
|
| 205 |
+
898080015,0
|
| 206 |
+
898080016,0
|
| 207 |
+
898080017,0
|
| 208 |
+
898080018,0
|
| 209 |
+
898080019,0
|
| 210 |
+
898080020,0
|
| 211 |
+
898080021,0
|
| 212 |
+
898080022,0
|
| 213 |
+
898080023,0
|
| 214 |
+
898080024,0
|
| 215 |
+
898080025,0
|
| 216 |
+
898080026,0
|
| 217 |
+
898080027,0
|
| 218 |
+
898080028,0
|
| 219 |
+
898080029,0
|
| 220 |
+
898080030,0
|
| 221 |
+
898080031,0
|
| 222 |
+
898080032,0
|
| 223 |
+
898080033,0
|
| 224 |
+
898080034,0
|
| 225 |
+
898080035,0
|
| 226 |
+
898080036,0
|
| 227 |
+
910190001,0
|
| 228 |
+
910190002,0
|
| 229 |
+
910190003,0
|
| 230 |
+
910190004,0
|
| 231 |
+
910190005,0
|
| 232 |
+
910190006,0
|
| 233 |
+
910190007,0
|
| 234 |
+
954160001,0
|
| 235 |
+
954160002,0
|
| 236 |
+
982710001,0
|
| 237 |
+
982710002,0
|
| 238 |
+
982710003,0
|
| 239 |
+
982710004,0
|
| 240 |
+
982710005,0
|
| 241 |
+
982710006,0
|
| 242 |
+
982710007,0
|
| 243 |
+
982710008,0
|
| 244 |
+
982710009,0
|
| 245 |
+
982710010,0
|
| 246 |
+
982710011,0
|
| 247 |
+
982710012,0
|
| 248 |
+
982710013,0
|
| 249 |
+
982710014,0
|
| 250 |
+
982710015,0
|
| 251 |
+
1026760001,0
|
| 252 |
+
1026760002,0
|
| 253 |
+
1026760003,0
|
| 254 |
+
1026760004,0
|
| 255 |
+
1026760005,0
|
| 256 |
+
1026760006,0
|
| 257 |
+
1026760007,0
|
| 258 |
+
1026760008,0
|
| 259 |
+
1026760009,0
|
| 260 |
+
1026760010,0
|
| 261 |
+
1026760011,0
|
| 262 |
+
1026760012,0
|
| 263 |
+
1026760013,0
|
| 264 |
+
1026760014,0
|
| 265 |
+
1026760015,0
|
| 266 |
+
1026760016,0
|
| 267 |
+
1026760017,0
|
| 268 |
+
1026760018,0
|
| 269 |
+
1026760019,0
|
| 270 |
+
1026760020,0
|
| 271 |
+
1026760021,0
|
| 272 |
+
1026760022,0
|
| 273 |
+
1026760023,0
|
| 274 |
+
1026760024,0
|
| 275 |
+
1026760025,0
|
| 276 |
+
1026760026,0
|
| 277 |
+
1026760027,0
|
| 278 |
+
1033960001,0
|
| 279 |
+
1033960002,0
|
| 280 |
+
1033960003,0
|
| 281 |
+
1033960004,0
|
| 282 |
+
1033960005,0
|
| 283 |
+
1033960006,0
|
| 284 |
+
1063530001,0
|
| 285 |
+
1063530002,0
|
| 286 |
+
1063530003,0
|
| 287 |
+
1063530004,0
|
| 288 |
+
1063530005,0
|
| 289 |
+
1063530006,0
|
| 290 |
+
1063530007,0
|
| 291 |
+
1063530008,0
|
| 292 |
+
1063530009,0
|
| 293 |
+
1063530010,0
|
| 294 |
+
1063530011,0
|
| 295 |
+
1063530012,0
|
| 296 |
+
1063530013,0
|
| 297 |
+
1063530014,0
|
| 298 |
+
1063530015,0
|
| 299 |
+
1063530016,0
|
| 300 |
+
1063530017,0
|
| 301 |
+
1063530018,0
|
| 302 |
+
1063530019,0
|
| 303 |
+
1063530020,0
|
| 304 |
+
1063530021,0
|
| 305 |
+
1063530022,0
|
| 306 |
+
1063530023,0
|
| 307 |
+
1063530024,0
|
| 308 |
+
1063530025,0
|
| 309 |
+
1063530026,0
|
| 310 |
+
1063530027,0
|
| 311 |
+
1063530028,0
|
| 312 |
+
1063530029,0
|
| 313 |
+
1063530030,0
|
| 314 |
+
1063530031,0
|
| 315 |
+
1063530032,0
|
| 316 |
+
1063530033,0
|
| 317 |
+
1063530034,0
|
| 318 |
+
1063530035,0
|
| 319 |
+
1063530036,0
|
| 320 |
+
1063530037,0
|
| 321 |
+
1063530038,0
|
| 322 |
+
1063530039,0
|
| 323 |
+
1063530040,0
|
| 324 |
+
1063530041,0
|
| 325 |
+
1063530042,0
|
| 326 |
+
1063530043,0
|
| 327 |
+
1063530044,0
|
| 328 |
+
1063530045,0
|
| 329 |
+
1063530046,0
|
| 330 |
+
1063530047,0
|
| 331 |
+
1063530048,0
|
| 332 |
+
1063530049,0
|
| 333 |
+
1063530050,0
|
| 334 |
+
1063530051,0
|
| 335 |
+
1063530052,0
|
| 336 |
+
1063530053,0
|
| 337 |
+
1063530054,0
|
| 338 |
+
1063530055,0
|
| 339 |
+
1063530056,0
|
| 340 |
+
1063530057,0
|
| 341 |
+
1063530058,0
|
| 342 |
+
1063530059,0
|
| 343 |
+
1063530060,0
|
| 344 |
+
1063530061,0
|
| 345 |
+
1063530062,0
|
| 346 |
+
1063530063,0
|
| 347 |
+
1063530064,0
|
| 348 |
+
1063530065,0
|
| 349 |
+
1063530066,0
|
| 350 |
+
1063530067,0
|
| 351 |
+
1063530068,0
|
| 352 |
+
1063530069,0
|
| 353 |
+
1063530070,0
|
| 354 |
+
1063530071,0
|
| 355 |
+
1063530072,0
|
| 356 |
+
1063530073,0
|
| 357 |
+
1063530074,0
|
| 358 |
+
1063530075,0
|
| 359 |
+
1063530076,0
|
| 360 |
+
1063530077,0
|
| 361 |
+
1063530078,0
|
| 362 |
+
1063530079,0
|
| 363 |
+
1063530080,0
|
| 364 |
+
1063530081,0
|
| 365 |
+
1063530082,0
|
| 366 |
+
1063530083,0
|
| 367 |
+
1113370001,0
|
| 368 |
+
1113370002,0
|
| 369 |
+
1113370003,0
|
| 370 |
+
1113370004,0
|
| 371 |
+
1157070001,0
|
| 372 |
+
1157070002,0
|
| 373 |
+
1157070003,0
|
| 374 |
+
1157070004,0
|
| 375 |
+
1157070005,0
|
| 376 |
+
1157070006,0
|
| 377 |
+
1157070007,0
|
| 378 |
+
1157070008,0
|
| 379 |
+
1172280001,0
|
| 380 |
+
1172280002,0
|
| 381 |
+
1172280003,0
|
| 382 |
+
1172280004,0
|
| 383 |
+
1172280005,0
|
| 384 |
+
1172280006,0
|
| 385 |
+
1172280007,0
|
| 386 |
+
1172280008,0
|
| 387 |
+
1172280009,0
|
| 388 |
+
1210650001,0
|
| 389 |
+
1210650002,0
|
| 390 |
+
1210650003,0
|
| 391 |
+
1210650004,0
|
| 392 |
+
1210650005,0
|
| 393 |
+
1210650006,0
|
| 394 |
+
1210650007,0
|
| 395 |
+
1210650008,0
|
| 396 |
+
1210650009,0
|
| 397 |
+
1210650010,0
|
| 398 |
+
1210650011,0
|
| 399 |
+
1210650012,0
|
| 400 |
+
1210650013,0
|
| 401 |
+
1210650014,0
|
| 402 |
+
1210650015,0
|
| 403 |
+
1210650016,0
|
| 404 |
+
1210650017,0
|
| 405 |
+
1210650018,0
|
| 406 |
+
1210650019,0
|
| 407 |
+
1210650020,0
|
| 408 |
+
1210650021,0
|
| 409 |
+
1210650022,0
|
| 410 |
+
1210650023,0
|
| 411 |
+
1210650024,0
|
| 412 |
+
1210650025,0
|
| 413 |
+
1210650026,0
|
| 414 |
+
1210650027,0
|
| 415 |
+
1210650028,0
|
| 416 |
+
1217930001,0
|
| 417 |
+
1217930002,0
|
| 418 |
+
1217930003,0
|
| 419 |
+
1217930004,0
|
| 420 |
+
1217930005,0
|
| 421 |
+
1245640001,0
|
| 422 |
+
1245640002,0
|
| 423 |
+
1245640003,0
|
| 424 |
+
1245640004,0
|
| 425 |
+
1245640005,0
|
| 426 |
+
1245640006,0
|
| 427 |
+
1245640007,0
|
| 428 |
+
1245640008,0
|
| 429 |
+
1245640009,0
|
| 430 |
+
1245640010,0
|
| 431 |
+
1245640011,0
|
| 432 |
+
1245640012,0
|
| 433 |
+
1245640013,0
|
| 434 |
+
1245640014,0
|
| 435 |
+
1245640015,0
|
| 436 |
+
1245640016,0
|
| 437 |
+
1245640017,0
|
| 438 |
+
1245640018,0
|
| 439 |
+
1245640019,0
|
| 440 |
+
1245640020,0
|
| 441 |
+
1245640021,0
|
| 442 |
+
1245640022,0
|
| 443 |
+
1245640023,0
|
| 444 |
+
1245640024,0
|
| 445 |
+
1245640025,0
|
| 446 |
+
1245640026,0
|
| 447 |
+
1245640027,0
|
| 448 |
+
1245640028,0
|
| 449 |
+
1245640029,0
|
| 450 |
+
1245640030,0
|
| 451 |
+
1245640031,0
|
| 452 |
+
1245640032,0
|
| 453 |
+
1245640033,0
|
| 454 |
+
1245640034,0
|
| 455 |
+
1245640035,0
|
| 456 |
+
1245640036,0
|
| 457 |
+
1245640037,0
|
| 458 |
+
1245640038,0
|
| 459 |
+
1248270001,0
|
| 460 |
+
1248270002,0
|
| 461 |
+
1248270003,0
|
| 462 |
+
1248270004,0
|
| 463 |
+
1248270005,0
|
| 464 |
+
1248270006,0
|
| 465 |
+
1248270007,0
|
| 466 |
+
1248270008,0
|
| 467 |
+
1248270009,0
|
| 468 |
+
1248270010,0
|
| 469 |
+
1248270011,0
|
| 470 |
+
1248270012,0
|
| 471 |
+
1248270013,0
|
| 472 |
+
1250210001,0
|
| 473 |
+
1250210002,0
|
| 474 |
+
1250210003,0
|
| 475 |
+
1250210004,0
|
| 476 |
+
1250210005,0
|
| 477 |
+
1250210006,0
|
| 478 |
+
1250210007,0
|
| 479 |
+
1250210008,0
|
| 480 |
+
1250210009,0
|
| 481 |
+
1250210010,0
|
| 482 |
+
1250210011,0
|
| 483 |
+
1250210012,0
|
| 484 |
+
1269890001,0
|
| 485 |
+
1269890002,0
|
| 486 |
+
1269890003,0
|
| 487 |
+
1269890004,0
|
| 488 |
+
1269890005,0
|
| 489 |
+
1269890006,0
|
| 490 |
+
1269890007,0
|
| 491 |
+
1269890008,0
|
| 492 |
+
1269890009,0
|
| 493 |
+
1269890010,0
|
| 494 |
+
1269890011,0
|
| 495 |
+
1269890012,0
|
| 496 |
+
1269890013,0
|
| 497 |
+
1269890014,0
|
| 498 |
+
1269890015,0
|
| 499 |
+
1295570001,0
|
| 500 |
+
1298890001,0
|
| 501 |
+
1298890002,0
|
| 502 |
+
1298890003,0
|
| 503 |
+
1298890004,0
|
| 504 |
+
1298890005,0
|
| 505 |
+
1298890006,0
|
| 506 |
+
1298890007,0
|
| 507 |
+
1298890008,0
|
| 508 |
+
1298890009,0
|
| 509 |
+
1298890010,0
|
| 510 |
+
1298890012,0
|
| 511 |
+
1298890013,0
|
| 512 |
+
1298890014,0
|
| 513 |
+
1298890015,0
|
| 514 |
+
1298890016,0
|
| 515 |
+
1298890017,0
|
| 516 |
+
1298890018,0
|
| 517 |
+
1298890019,0
|
| 518 |
+
1298890020,0
|
| 519 |
+
1298890021,0
|
| 520 |
+
1334900001,0
|
| 521 |
+
1334900002,0
|
| 522 |
+
1334900003,0
|
| 523 |
+
1334900004,0
|
| 524 |
+
1334900005,0
|
| 525 |
+
1334900006,0
|
| 526 |
+
1337530001,0
|
| 527 |
+
1337530002,0
|
| 528 |
+
1337530003,0
|
| 529 |
+
1337530004,0
|
| 530 |
+
1337530005,0
|
| 531 |
+
1337530006,0
|
| 532 |
+
1337530007,0
|
| 533 |
+
1337530008,0
|
| 534 |
+
1337530009,0
|
| 535 |
+
1337530010,0
|
| 536 |
+
1337530011,0
|
| 537 |
+
1337530012,0
|
| 538 |
+
1337530013,0
|
| 539 |
+
1337530014,0
|
| 540 |
+
1337530015,0
|
| 541 |
+
1337530016,0
|
| 542 |
+
1337530017,0
|
| 543 |
+
1337530018,0
|
| 544 |
+
1337530019,0
|
| 545 |
+
1337530020,0
|
| 546 |
+
1337530021,0
|
| 547 |
+
1337530022,0
|
| 548 |
+
1337530023,0
|
| 549 |
+
1337530024,0
|
| 550 |
+
1337530025,0
|
| 551 |
+
1337530026,0
|
| 552 |
+
1337530027,0
|
| 553 |
+
1337530028,0
|
| 554 |
+
1337530029,0
|
| 555 |
+
1337530030,0
|
| 556 |
+
1337530031,0
|
| 557 |
+
1337530032,0
|
| 558 |
+
1337530033,0
|
| 559 |
+
1337530034,0
|
| 560 |
+
1337530035,0
|
| 561 |
+
1337530036,0
|
| 562 |
+
1337530037,0
|
| 563 |
+
1337530038,0
|
| 564 |
+
1337530039,0
|
| 565 |
+
1337530040,0
|
| 566 |
+
1337530041,0
|
| 567 |
+
1337530042,0
|
| 568 |
+
1337530043,0
|
| 569 |
+
1337530044,0
|
| 570 |
+
1337530045,0
|
| 571 |
+
1337530046,0
|
| 572 |
+
1337530047,0
|
| 573 |
+
1337530048,0
|
| 574 |
+
1337530049,0
|
| 575 |
+
1337530050,0
|
| 576 |
+
1337530051,0
|
| 577 |
+
1337530052,0
|
| 578 |
+
1337530053,0
|
| 579 |
+
1337530054,0
|
| 580 |
+
1337530055,0
|
| 581 |
+
1337530056,0
|
| 582 |
+
1337530057,0
|
| 583 |
+
1337530058,0
|
| 584 |
+
1337530059,0
|
| 585 |
+
1337530060,0
|
| 586 |
+
1337530061,0
|
| 587 |
+
1337530062,0
|
| 588 |
+
1337530063,0
|
| 589 |
+
1337530064,0
|
| 590 |
+
1337530065,0
|
| 591 |
+
1337530066,0
|
| 592 |
+
1337530067,0
|
| 593 |
+
1337530068,0
|
| 594 |
+
1337530069,0
|
| 595 |
+
1337530070,0
|
| 596 |
+
1337530071,0
|
| 597 |
+
1337530072,0
|
| 598 |
+
1337530073,0
|
| 599 |
+
1337530074,0
|
| 600 |
+
1337530075,0
|
| 601 |
+
1337530076,0
|
| 602 |
+
1337530077,0
|
| 603 |
+
1337530078,0
|
| 604 |
+
1337530079,0
|
| 605 |
+
1337530080,0
|
| 606 |
+
1337530081,0
|
| 607 |
+
1337530082,0
|
| 608 |
+
1382350001,0
|
| 609 |
+
1382350002,0
|
| 610 |
+
1382350003,0
|
| 611 |
+
1382350004,0
|
| 612 |
+
1382350005,0
|
| 613 |
+
1382350006,0
|
| 614 |
+
1382350007,0
|
| 615 |
+
1382350008,0
|
| 616 |
+
1394210001,0
|
| 617 |
+
1394210002,0
|
| 618 |
+
1394210003,0
|
| 619 |
+
1394210004,0
|
| 620 |
+
1394210005,0
|
| 621 |
+
1394210006,0
|
| 622 |
+
1394210007,0
|
| 623 |
+
1394210008,0
|
| 624 |
+
1394210009,0
|
| 625 |
+
1394210010,0
|
| 626 |
+
1394410001,0
|
| 627 |
+
1394410002,0
|
| 628 |
+
1394410003,0
|
| 629 |
+
1394410004,0
|
| 630 |
+
1394410005,0
|
| 631 |
+
1404360001,0
|
| 632 |
+
1404360002,0
|
| 633 |
+
1404360003,0
|
| 634 |
+
1404360004,0
|
| 635 |
+
1404360005,0
|
| 636 |
+
1404360006,0
|
| 637 |
+
1404360007,0
|
| 638 |
+
1404360009,0
|
| 639 |
+
1404360010,0
|
| 640 |
+
1404360011,0
|
| 641 |
+
1404360013,0
|
| 642 |
+
1404360015,0
|
| 643 |
+
1404360016,0
|
| 644 |
+
1404360018,0
|
| 645 |
+
1404360019,0
|
| 646 |
+
1404360022,0
|
| 647 |
+
1404360023,0
|
| 648 |
+
1404360024,0
|
| 649 |
+
1404360025,0
|
| 650 |
+
1404360026,0
|
| 651 |
+
1404360027,0
|
| 652 |
+
1404360029,0
|
| 653 |
+
1453730001,0
|
| 654 |
+
1453730002,0
|
| 655 |
+
1453730003,0
|
| 656 |
+
1453730004,0
|
| 657 |
+
1453730005,0
|
| 658 |
+
1453730006,0
|
| 659 |
+
1453730007,0
|
| 660 |
+
1453730008,0
|
| 661 |
+
1453730009,0
|
| 662 |
+
1453730010,0
|
| 663 |
+
1453730011,0
|
| 664 |
+
1453730012,0
|
| 665 |
+
1453730013,0
|
| 666 |
+
1453730014,0
|
| 667 |
+
1456280001,0
|
| 668 |
+
1456280002,0
|
| 669 |
+
1456280003,0
|
| 670 |
+
1456280004,0
|
| 671 |
+
1480650001,0
|
| 672 |
+
1480650002,0
|
| 673 |
+
1480650003,0
|
| 674 |
+
1480650004,0
|
| 675 |
+
1480650005,0
|
| 676 |
+
1480650006,0
|
| 677 |
+
1480650007,0
|
| 678 |
+
1480650008,0
|
| 679 |
+
1481400001,0
|
| 680 |
+
1481400002,0
|
| 681 |
+
1481400003,0
|
| 682 |
+
1485260001,0
|
| 683 |
+
1485260002,0
|
| 684 |
+
1485260003,0
|
| 685 |
+
1485260004,0
|
| 686 |
+
1485260005,0
|
| 687 |
+
1486660001,0
|
| 688 |
+
1486660002,0
|
| 689 |
+
1486660003,0
|
| 690 |
+
1486660004,0
|
| 691 |
+
1486660005,0
|
| 692 |
+
1486660006,0
|
| 693 |
+
1486660007,0
|
| 694 |
+
1486660008,0
|
| 695 |
+
1486660009,0
|
| 696 |
+
1486660010,0
|
| 697 |
+
1486660011,0
|
| 698 |
+
1486660012,0
|
| 699 |
+
1486660013,0
|
| 700 |
+
1486660014,0
|
| 701 |
+
1550280001,0
|
| 702 |
+
1550280002,0
|
| 703 |
+
1550280003,0
|
| 704 |
+
1550280004,0
|
| 705 |
+
1550280005,0
|
| 706 |
+
1550280006,0
|
| 707 |
+
1550280007,0
|
| 708 |
+
1550280008,0
|
| 709 |
+
1550280009,0
|
| 710 |
+
1550280010,0
|
| 711 |
+
1550280011,0
|
| 712 |
+
1550280012,0
|
| 713 |
+
1550280013,0
|
| 714 |
+
1550280014,0
|
| 715 |
+
1550280015,0
|
| 716 |
+
1550280016,0
|
| 717 |
+
1550280017,0
|
| 718 |
+
1550280018,0
|
| 719 |
+
1550280019,0
|
| 720 |
+
1550280020,0
|
| 721 |
+
1550280021,0
|
| 722 |
+
1550280022,0
|
| 723 |
+
1550280023,0
|
| 724 |
+
1550280024,0
|
| 725 |
+
1550280025,0
|
| 726 |
+
1550280026,0
|
| 727 |
+
1550280027,0
|
| 728 |
+
1550280028,0
|
| 729 |
+
1550280029,0
|
| 730 |
+
1550280030,0
|
| 731 |
+
1550280031,0
|
| 732 |
+
1550280032,0
|
| 733 |
+
1550280033,0
|
| 734 |
+
1550280034,0
|
| 735 |
+
1550280035,0
|
| 736 |
+
1550280036,0
|
| 737 |
+
1550280037,0
|
| 738 |
+
1550280038,0
|
| 739 |
+
1550280039,0
|
| 740 |
+
1550280040,0
|
| 741 |
+
1550280041,0
|
| 742 |
+
1550280042,0
|
| 743 |
+
1550280043,0
|
| 744 |
+
1550280044,0
|
| 745 |
+
1550280045,0
|
| 746 |
+
1550280046,0
|
| 747 |
+
1550280047,0
|
| 748 |
+
1550280048,0
|
| 749 |
+
1550280049,0
|
| 750 |
+
1550280050,0
|
| 751 |
+
1550280051,0
|
| 752 |
+
1550280052,0
|
| 753 |
+
1550280053,0
|
| 754 |
+
1550280054,0
|
| 755 |
+
1550280055,0
|
| 756 |
+
1561560001,0
|
| 757 |
+
1561560002,0
|
| 758 |
+
1561560003,0
|
| 759 |
+
1561560004,0
|
| 760 |
+
1561560005,0
|
| 761 |
+
1561560006,0
|
| 762 |
+
1561560007,0
|
| 763 |
+
1561560008,0
|
| 764 |
+
1561560009,0
|
| 765 |
+
1561560010,0
|
| 766 |
+
1561560011,0
|
| 767 |
+
1561560012,0
|
| 768 |
+
1561560013,0
|
| 769 |
+
1561560014,0
|
| 770 |
+
1561560015,0
|
| 771 |
+
1561560016,0
|
| 772 |
+
1561560017,0
|
| 773 |
+
1561560018,0
|
| 774 |
+
1561560019,0
|
| 775 |
+
1561560020,0
|
| 776 |
+
1561560021,0
|
| 777 |
+
1561560022,0
|
| 778 |
+
1561560023,0
|
| 779 |
+
1561560024,0
|
| 780 |
+
1561560025,0
|
| 781 |
+
1561560026,0
|
| 782 |
+
1561560027,0
|
| 783 |
+
1561560028,0
|
| 784 |
+
1561560029,0
|
| 785 |
+
1561560030,0
|
| 786 |
+
1561560031,0
|
| 787 |
+
1561560032,0
|
| 788 |
+
1595490001,0
|
| 789 |
+
1595490003,0
|
| 790 |
+
1595490004,0
|
| 791 |
+
1595490005,0
|
| 792 |
+
1595490006,0
|
| 793 |
+
1595490007,0
|
| 794 |
+
1595490008,0
|
| 795 |
+
1595490009,0
|
| 796 |
+
1595490010,0
|
| 797 |
+
1595490011,0
|
| 798 |
+
1595490012,0
|
| 799 |
+
1595490013,0
|
| 800 |
+
1595490014,0
|
| 801 |
+
1595490015,0
|
| 802 |
+
1595490016,0
|
| 803 |
+
1595490017,0
|
| 804 |
+
1595490018,0
|
| 805 |
+
1595490019,0
|
| 806 |
+
1595490020,0
|
| 807 |
+
1601810001,0
|
| 808 |
+
1601810002,0
|
| 809 |
+
1601810003,0
|
| 810 |
+
1612820001,0
|
| 811 |
+
1612820002,0
|
| 812 |
+
1652800001,0
|
| 813 |
+
1652800002,0
|
| 814 |
+
1652800003,0
|
| 815 |
+
1652800004,0
|
| 816 |
+
1652800005,0
|
| 817 |
+
1652800006,0
|
| 818 |
+
1652800007,0
|
| 819 |
+
1652800008,0
|
| 820 |
+
1652800009,0
|
| 821 |
+
1652800010,0
|
| 822 |
+
1652800011,0
|
| 823 |
+
1652800012,0
|
| 824 |
+
1652800013,0
|
| 825 |
+
1676620001,0
|
| 826 |
+
1676620002,0
|
| 827 |
+
1676620003,0
|
| 828 |
+
1676620004,0
|
| 829 |
+
1676620005,0
|
| 830 |
+
1676620006,0
|
| 831 |
+
1676620007,0
|
| 832 |
+
1695870001,0
|
| 833 |
+
1695870002,0
|
| 834 |
+
1695870003,0
|
| 835 |
+
1695870004,0
|
| 836 |
+
1695870005,0
|
| 837 |
+
1695870006,0
|
| 838 |
+
1705030001,0
|
| 839 |
+
1705030002,0
|
| 840 |
+
1705030003,0
|
| 841 |
+
1705030004,0
|
| 842 |
+
1705030005,0
|
| 843 |
+
1705030006,0
|
| 844 |
+
1705030007,0
|
| 845 |
+
1705030008,0
|
| 846 |
+
1705030009,0
|
| 847 |
+
1705030010,0
|
| 848 |
+
1705030011,0
|
| 849 |
+
1705030012,0
|
| 850 |
+
1705030013,0
|
| 851 |
+
1705030014,0
|
| 852 |
+
1705030015,0
|
| 853 |
+
1705030016,0
|
| 854 |
+
1707840001,0
|
| 855 |
+
1707840002,0
|
| 856 |
+
1707840003,0
|
| 857 |
+
1707840004,0
|
| 858 |
+
1707840005,0
|
| 859 |
+
1707840006,0
|
| 860 |
+
1707840007,0
|
| 861 |
+
1707840008,0
|
| 862 |
+
1801560001,0
|
| 863 |
+
1801560002,0
|
| 864 |
+
1801560003,0
|
| 865 |
+
1801560004,0
|
| 866 |
+
1801560005,0
|
| 867 |
+
1801560006,0
|
| 868 |
+
1801560007,0
|
| 869 |
+
1801560008,0
|
| 870 |
+
1801560009,0
|
| 871 |
+
1801560010,0
|
| 872 |
+
1801560011,0
|
| 873 |
+
1906880001,0
|
| 874 |
+
1906880002,0
|
| 875 |
+
1906880003,0
|
| 876 |
+
1931980001,0
|
| 877 |
+
1931980002,0
|
| 878 |
+
1931980003,0
|
| 879 |
+
1931980004,0
|
| 880 |
+
1931980005,0
|
| 881 |
+
1931980006,0
|
| 882 |
+
1931980007,0
|
| 883 |
+
1931980008,0
|
| 884 |
+
1931980009,0
|
| 885 |
+
1931980010,0
|
| 886 |
+
1931980011,0
|
| 887 |
+
1931980012,0
|
| 888 |
+
1931980013,0
|
| 889 |
+
1931980014,0
|
| 890 |
+
2020950001,0
|
| 891 |
+
2020950002,0
|
| 892 |
+
2020950003,0
|
| 893 |
+
2077870001,0
|
| 894 |
+
2077870002,0
|
| 895 |
+
2077870003,0
|
| 896 |
+
2077870004,0
|
| 897 |
+
2077870005,0
|
| 898 |
+
2163140001,0
|
| 899 |
+
2163140002,0
|
| 900 |
+
2163140003,0
|
| 901 |
+
2163140004,0
|
| 902 |
+
2163140005,0
|
| 903 |
+
2163140006,0
|
| 904 |
+
2163140007,0
|
| 905 |
+
2163140008,0
|
| 906 |
+
2163140009,0
|
| 907 |
+
2163140010,0
|
| 908 |
+
2163140011,0
|
| 909 |
+
2163140012,0
|
| 910 |
+
2163140013,0
|
| 911 |
+
2163140014,0
|
| 912 |
+
2163140015,0
|
| 913 |
+
2163140016,0
|
| 914 |
+
2163140017,0
|
| 915 |
+
2163140018,0
|
| 916 |
+
2163140019,0
|
| 917 |
+
2163140020,0
|
| 918 |
+
2163140021,0
|
| 919 |
+
2163140022,0
|
| 920 |
+
2163140023,0
|
| 921 |
+
2163140024,0
|
| 922 |
+
2163140025,0
|
| 923 |
+
2163140026,0
|
| 924 |
+
2163140027,0
|
| 925 |
+
2163140028,0
|
| 926 |
+
2163140029,0
|
| 927 |
+
2163140030,0
|
| 928 |
+
2163140031,0
|
| 929 |
+
2163140032,0
|
| 930 |
+
2163140033,0
|
| 931 |
+
2163140034,0
|
| 932 |
+
2163140035,0
|
| 933 |
+
2163140036,0
|
| 934 |
+
2163140037,0
|
| 935 |
+
2163140038,0
|
| 936 |
+
2163140039,0
|
| 937 |
+
2163140040,0
|
| 938 |
+
2163140041,0
|
| 939 |
+
2163140042,0
|
| 940 |
+
2163140043,0
|
| 941 |
+
2163140044,0
|
| 942 |
+
2163140045,0
|
| 943 |
+
2163140046,0
|
| 944 |
+
2163140047,0
|
| 945 |
+
2163140048,0
|
| 946 |
+
2163140049,0
|
| 947 |
+
2163140050,0
|
| 948 |
+
2163140051,0
|
| 949 |
+
2163140052,0
|
| 950 |
+
2163140053,0
|
| 951 |
+
2163140054,0
|
| 952 |
+
2163140055,0
|
| 953 |
+
2163140056,0
|
| 954 |
+
2163140057,0
|
| 955 |
+
2163140058,0
|
| 956 |
+
2193270001,0
|
| 957 |
+
2293180001,0
|
| 958 |
+
2293180002,0
|
| 959 |
+
2293180003,0
|
| 960 |
+
438100001,1
|
| 961 |
+
438100002,1
|
| 962 |
+
438100003,1
|
| 963 |
+
438100004,1
|
| 964 |
+
438100005,1
|
| 965 |
+
438100006,1
|
| 966 |
+
438100007,1
|
| 967 |
+
438100008,1
|
| 968 |
+
438100009,1
|
| 969 |
+
438100010,1
|
| 970 |
+
529150001,1
|
| 971 |
+
529150002,1
|
| 972 |
+
529150003,1
|
| 973 |
+
529150004,1
|
| 974 |
+
533970001,1
|
| 975 |
+
533970002,1
|
| 976 |
+
533970003,1
|
| 977 |
+
600140001,1
|
| 978 |
+
600140002,1
|
| 979 |
+
600140003,1
|
| 980 |
+
600140004,1
|
| 981 |
+
600140005,1
|
| 982 |
+
600140006,1
|
| 983 |
+
600140007,1
|
| 984 |
+
600140008,1
|
| 985 |
+
600140009,1
|
| 986 |
+
600140010,1
|
| 987 |
+
600140011,1
|
| 988 |
+
600140012,1
|
| 989 |
+
600140013,1
|
| 990 |
+
600140014,1
|
| 991 |
+
600140015,1
|
| 992 |
+
600140016,1
|
| 993 |
+
600140017,1
|
| 994 |
+
600140018,1
|
| 995 |
+
600140019,1
|
| 996 |
+
600140020,1
|
| 997 |
+
600140021,1
|
| 998 |
+
600140022,1
|
| 999 |
+
600140023,1
|
| 1000 |
+
600140024,1
|
| 1001 |
+
631660001,1
|
| 1002 |
+
714100001,1
|
| 1003 |
+
714100002,1
|
| 1004 |
+
714100003,1
|
| 1005 |
+
714100004,1
|
| 1006 |
+
714100005,1
|
| 1007 |
+
714100006,1
|
| 1008 |
+
714100007,1
|
| 1009 |
+
714100008,1
|
| 1010 |
+
1178780001,1
|
| 1011 |
+
1178780002,1
|
| 1012 |
+
1178780003,1
|
| 1013 |
+
1178780004,1
|
| 1014 |
+
1178780005,1
|
| 1015 |
+
1178780006,1
|
| 1016 |
+
1178780007,1
|
| 1017 |
+
1178780008,1
|
| 1018 |
+
1178780009,1
|
| 1019 |
+
1178780010,1
|
| 1020 |
+
1178780011,1
|
| 1021 |
+
1178780012,1
|
| 1022 |
+
1178780013,1
|
| 1023 |
+
1178780014,1
|
| 1024 |
+
1178780015,1
|
| 1025 |
+
1178780016,1
|
| 1026 |
+
1337060001,1
|
| 1027 |
+
1337060002,1
|
| 1028 |
+
1337060003,1
|
| 1029 |
+
1337060004,1
|
| 1030 |
+
1337060005,1
|
| 1031 |
+
1337060006,1
|
| 1032 |
+
1337060007,1
|
| 1033 |
+
1337060008,1
|
| 1034 |
+
1337060009,1
|
| 1035 |
+
1337060010,1
|
| 1036 |
+
1337060011,1
|
| 1037 |
+
1337060012,1
|
| 1038 |
+
1337060013,1
|
| 1039 |
+
1337060014,1
|
| 1040 |
+
1337060015,1
|
| 1041 |
+
1337060016,1
|
| 1042 |
+
1337060017,1
|
| 1043 |
+
1337060018,1
|
| 1044 |
+
1337060019,1
|
| 1045 |
+
1337060020,1
|
| 1046 |
+
1337060021,1
|
| 1047 |
+
1337060022,1
|
| 1048 |
+
1337060023,1
|
| 1049 |
+
1337060024,1
|
| 1050 |
+
1337060025,1
|
| 1051 |
+
1337060026,1
|
| 1052 |
+
1337060027,1
|
| 1053 |
+
1337060028,1
|
| 1054 |
+
1337060029,1
|
| 1055 |
+
1337060030,1
|
| 1056 |
+
1337060031,1
|
| 1057 |
+
1337060032,1
|
| 1058 |
+
1337060033,1
|
| 1059 |
+
1337060034,1
|
| 1060 |
+
1337060035,1
|
| 1061 |
+
1337060036,1
|
| 1062 |
+
1337060039,1
|
| 1063 |
+
1337060040,1
|
| 1064 |
+
1337060041,1
|
| 1065 |
+
1960620001,1
|
| 1066 |
+
1960620002,1
|
| 1067 |
+
1960620008,1
|
| 1068 |
+
1960620010,1
|
| 1069 |
+
1960620011,1
|
| 1070 |
+
1960620012,1
|
| 1071 |
+
1960620013,1
|
| 1072 |
+
1960620014,1
|
| 1073 |
+
1960620015,1
|
| 1074 |
+
1960620016,1
|
| 1075 |
+
2089520001,1
|
| 1076 |
+
2089520002,1
|
| 1077 |
+
2089520003,1
|
| 1078 |
+
2089520004,1
|
| 1079 |
+
2308940001,1
|
| 1080 |
+
2308940002,1
|
| 1081 |
+
2308940003,1
|
| 1082 |
+
2308940004,1
|
| 1083 |
+
2308940005,1
|
| 1084 |
+
2308940006,1
|
| 1085 |
+
2308940007,1
|
| 1086 |
+
2308940008,1
|
| 1087 |
+
2308940009,1
|
| 1088 |
+
2308940010,1
|
| 1089 |
+
2308940011,1
|
| 1090 |
+
2308940012,1
|
| 1091 |
+
2308940013,1
|
| 1092 |
+
2308940014,1
|
| 1093 |
+
2308940015,1
|
| 1094 |
+
2308940016,1
|
| 1095 |
+
2308940017,1
|
| 1096 |
+
269170001,2
|
| 1097 |
+
269170002,2
|
| 1098 |
+
269170003,2
|
| 1099 |
+
269170004,2
|
| 1100 |
+
269170005,2
|
| 1101 |
+
269170006,2
|
| 1102 |
+
269170007,2
|
| 1103 |
+
269170008,2
|
| 1104 |
+
269170009,2
|
| 1105 |
+
451980001,2
|
| 1106 |
+
451980002,2
|
| 1107 |
+
451980003,2
|
| 1108 |
+
451980004,2
|
| 1109 |
+
451980005,2
|
| 1110 |
+
451980006,2
|
| 1111 |
+
451980007,2
|
| 1112 |
+
451980008,2
|
| 1113 |
+
451980009,2
|
| 1114 |
+
451980010,2
|
| 1115 |
+
451980011,2
|
| 1116 |
+
451980012,2
|
| 1117 |
+
451980013,2
|
| 1118 |
+
451980014,2
|
| 1119 |
+
451980015,2
|
| 1120 |
+
451980016,2
|
| 1121 |
+
451980017,2
|
| 1122 |
+
451980018,2
|
| 1123 |
+
457550001,2
|
| 1124 |
+
457550002,2
|
| 1125 |
+
457550003,2
|
| 1126 |
+
457550004,2
|
| 1127 |
+
457550005,2
|
| 1128 |
+
490250001,2
|
| 1129 |
+
490250002,2
|
| 1130 |
+
490250003,2
|
| 1131 |
+
490250004,2
|
| 1132 |
+
490250005,2
|
| 1133 |
+
490250006,2
|
| 1134 |
+
490250007,2
|
| 1135 |
+
490250008,2
|
| 1136 |
+
490250009,2
|
| 1137 |
+
490250010,2
|
| 1138 |
+
490250011,2
|
| 1139 |
+
490250012,2
|
| 1140 |
+
490250013,2
|
| 1141 |
+
490250014,2
|
| 1142 |
+
490250015,2
|
| 1143 |
+
490250016,2
|
| 1144 |
+
490250017,2
|
| 1145 |
+
490250018,2
|
| 1146 |
+
490250019,2
|
| 1147 |
+
497820001,2
|
| 1148 |
+
497820002,2
|
| 1149 |
+
497820003,2
|
| 1150 |
+
591680001,2
|
| 1151 |
+
591680002,2
|
| 1152 |
+
591680003,2
|
| 1153 |
+
591680004,2
|
| 1154 |
+
591680005,2
|
| 1155 |
+
591680006,2
|
| 1156 |
+
591680007,2
|
| 1157 |
+
591680008,2
|
| 1158 |
+
591680009,2
|
| 1159 |
+
591680010,2
|
| 1160 |
+
591680011,2
|
| 1161 |
+
591680012,2
|
| 1162 |
+
591680013,2
|
| 1163 |
+
731790001,2
|
| 1164 |
+
731790002,2
|
| 1165 |
+
731790003,2
|
| 1166 |
+
731790004,2
|
| 1167 |
+
731790005,2
|
| 1168 |
+
731790006,2
|
| 1169 |
+
731790007,2
|
| 1170 |
+
731790008,2
|
| 1171 |
+
731790009,2
|
| 1172 |
+
731790010,2
|
| 1173 |
+
758210001,2
|
| 1174 |
+
758210002,2
|
| 1175 |
+
758210003,2
|
| 1176 |
+
758210004,2
|
| 1177 |
+
758210005,2
|
| 1178 |
+
758210006,2
|
| 1179 |
+
758210007,2
|
| 1180 |
+
758210008,2
|
| 1181 |
+
758210009,2
|
| 1182 |
+
758210010,2
|
| 1183 |
+
758210011,2
|
| 1184 |
+
758210012,2
|
| 1185 |
+
758210013,2
|
| 1186 |
+
758210014,2
|
| 1187 |
+
891960001,2
|
| 1188 |
+
891960002,2
|
| 1189 |
+
891960003,2
|
| 1190 |
+
891960004,2
|
| 1191 |
+
891960005,2
|
| 1192 |
+
891960006,2
|
| 1193 |
+
891960007,2
|
| 1194 |
+
891960008,2
|
| 1195 |
+
891960009,2
|
| 1196 |
+
891960010,2
|
| 1197 |
+
997760001,2
|
| 1198 |
+
997760002,2
|
| 1199 |
+
997760003,2
|
| 1200 |
+
997760004,2
|
| 1201 |
+
997760005,2
|
| 1202 |
+
997760006,2
|
| 1203 |
+
997760007,2
|
| 1204 |
+
997760008,2
|
| 1205 |
+
997760009,2
|
| 1206 |
+
997760010,2
|
| 1207 |
+
997760011,2
|
| 1208 |
+
997760012,2
|
| 1209 |
+
998660001,2
|
| 1210 |
+
998660002,2
|
| 1211 |
+
998660003,2
|
| 1212 |
+
998660004,2
|
| 1213 |
+
998660005,2
|
| 1214 |
+
1150310001,2
|
| 1215 |
+
1150310002,2
|
| 1216 |
+
1150310003,2
|
| 1217 |
+
1150310004,2
|
| 1218 |
+
1150310005,2
|
| 1219 |
+
1150310006,2
|
| 1220 |
+
1264160001,2
|
| 1221 |
+
1264160002,2
|
| 1222 |
+
1264160003,2
|
| 1223 |
+
1264160004,2
|
| 1224 |
+
1264160005,2
|
| 1225 |
+
1264160006,2
|
| 1226 |
+
1264160007,2
|
| 1227 |
+
1264160008,2
|
| 1228 |
+
1264160009,2
|
| 1229 |
+
1264160010,2
|
| 1230 |
+
1264160011,2
|
| 1231 |
+
1264160012,2
|
| 1232 |
+
1264160013,2
|
| 1233 |
+
1264160014,2
|
| 1234 |
+
1264160015,2
|
| 1235 |
+
1264160016,2
|
| 1236 |
+
1264160017,2
|
| 1237 |
+
1270910001,2
|
| 1238 |
+
1270910002,2
|
| 1239 |
+
1270910003,2
|
| 1240 |
+
1270910004,2
|
| 1241 |
+
1270910005,2
|
| 1242 |
+
1388030001,2
|
| 1243 |
+
1488730001,2
|
| 1244 |
+
1512840001,2
|
| 1245 |
+
1512840002,2
|
| 1246 |
+
1512840003,2
|
| 1247 |
+
1512840004,2
|
| 1248 |
+
1512840005,2
|
| 1249 |
+
1512840006,2
|
| 1250 |
+
1512840007,2
|
| 1251 |
+
1730290001,2
|
| 1252 |
+
1730290002,2
|
| 1253 |
+
1730290003,2
|
| 1254 |
+
1730290004,2
|
| 1255 |
+
1730290005,2
|
| 1256 |
+
1805510001,2
|
| 1257 |
+
1805510002,2
|
| 1258 |
+
1805510003,2
|
| 1259 |
+
1805510004,2
|
| 1260 |
+
1805510005,2
|
| 1261 |
+
1805510006,2
|
| 1262 |
+
1805510007,2
|
| 1263 |
+
1805510008,2
|
| 1264 |
+
1805510009,2
|
| 1265 |
+
1805510010,2
|
| 1266 |
+
1805510011,2
|
| 1267 |
+
1805510012,2
|
| 1268 |
+
1805510013,2
|
| 1269 |
+
1805510014,2
|
| 1270 |
+
1805510015,2
|
| 1271 |
+
1805510016,2
|
| 1272 |
+
1805510017,2
|
| 1273 |
+
1805510018,2
|
| 1274 |
+
1805510019,2
|
| 1275 |
+
1805510020,2
|
| 1276 |
+
1805510021,2
|
| 1277 |
+
1805510022,2
|
| 1278 |
+
1805510023,2
|
| 1279 |
+
1805510024,2
|
| 1280 |
+
1805510025,2
|
| 1281 |
+
1805510026,2
|
| 1282 |
+
1805510027,2
|
| 1283 |
+
1805510028,2
|
| 1284 |
+
1805510029,2
|
| 1285 |
+
1805510030,2
|
| 1286 |
+
1805510031,2
|
| 1287 |
+
1805510032,2
|
| 1288 |
+
1805510033,2
|
| 1289 |
+
1825460001,2
|
| 1290 |
+
1825460002,2
|
| 1291 |
+
1825460003,2
|
| 1292 |
+
1825460004,2
|
| 1293 |
+
1825460005,2
|
| 1294 |
+
1825460006,2
|
| 1295 |
+
1825460007,2
|
| 1296 |
+
1825460008,2
|
| 1297 |
+
1825460009,2
|
| 1298 |
+
1825460010,2
|
| 1299 |
+
1825460011,2
|
| 1300 |
+
1825460012,2
|
| 1301 |
+
1862180001,2
|
| 1302 |
+
1862180002,2
|
| 1303 |
+
1862180003,2
|
| 1304 |
+
1862180004,2
|
| 1305 |
+
1862180005,2
|
| 1306 |
+
1862180006,2
|
| 1307 |
+
1862180007,2
|
| 1308 |
+
2025000001,2
|
| 1309 |
+
2025000002,2
|
| 1310 |
+
2025000003,2
|
| 1311 |
+
2025000004,2
|
| 1312 |
+
2025000005,2
|
| 1313 |
+
2025000006,2
|
| 1314 |
+
2025000007,2
|
| 1315 |
+
2025000008,2
|
| 1316 |
+
2025000009,2
|
| 1317 |
+
2025000010,2
|
| 1318 |
+
2057000001,2
|
| 1319 |
+
2057000002,2
|
| 1320 |
+
2057000003,2
|
| 1321 |
+
2057000004,2
|
| 1322 |
+
2057000005,2
|
| 1323 |
+
2057000006,2
|
| 1324 |
+
2057000007,2
|
| 1325 |
+
2057000008,2
|
| 1326 |
+
2057000009,2
|
| 1327 |
+
2057000010,2
|
| 1328 |
+
2057000011,2
|
| 1329 |
+
2057000012,2
|
| 1330 |
+
2057000013,2
|
| 1331 |
+
2057000014,2
|
| 1332 |
+
2057000015,2
|
| 1333 |
+
2057000016,2
|
| 1334 |
+
2057000017,2
|
| 1335 |
+
2057000018,2
|
| 1336 |
+
2057000019,2
|
| 1337 |
+
2057000020,2
|
| 1338 |
+
2057000021,2
|
| 1339 |
+
2057000022,2
|
| 1340 |
+
2057000023,2
|
| 1341 |
+
2057000024,2
|
| 1342 |
+
2057000025,2
|
| 1343 |
+
2057000026,2
|
| 1344 |
+
2057000027,2
|
| 1345 |
+
2057000028,2
|
| 1346 |
+
2057000029,2
|
| 1347 |
+
2057000030,2
|
| 1348 |
+
2057000031,2
|
| 1349 |
+
2057000032,2
|
| 1350 |
+
2057000033,2
|
| 1351 |
+
2057000034,2
|
| 1352 |
+
2057000035,2
|
| 1353 |
+
2057000036,2
|
| 1354 |
+
2057000037,2
|
| 1355 |
+
2057000038,2
|
| 1356 |
+
2057000039,2
|
| 1357 |
+
2057000040,2
|
| 1358 |
+
2057000041,2
|
| 1359 |
+
2057000042,2
|
| 1360 |
+
2057000043,2
|
| 1361 |
+
2057000044,2
|
| 1362 |
+
2057000045,2
|
| 1363 |
+
2057000046,2
|
| 1364 |
+
2057000047,2
|
| 1365 |
+
2057000048,2
|
| 1366 |
+
2057000049,2
|
| 1367 |
+
2057000050,2
|
| 1368 |
+
2057000051,2
|
| 1369 |
+
2057000052,2
|
| 1370 |
+
2057000053,2
|
| 1371 |
+
2057000054,2
|
| 1372 |
+
2057000055,2
|
| 1373 |
+
2057000056,2
|
| 1374 |
+
2057000057,2
|
| 1375 |
+
2057000058,2
|
| 1376 |
+
2057000059,2
|
| 1377 |
+
2057000060,2
|
| 1378 |
+
2057000061,2
|
| 1379 |
+
2057000062,2
|
| 1380 |
+
2057000063,2
|
| 1381 |
+
2057000064,2
|
| 1382 |
+
2057000065,2
|
| 1383 |
+
2057000066,2
|
| 1384 |
+
2057000067,2
|
| 1385 |
+
2057000068,2
|
| 1386 |
+
2057000069,2
|
| 1387 |
+
2057000070,2
|
| 1388 |
+
2057000071,2
|
| 1389 |
+
2057000072,2
|
| 1390 |
+
2057000073,2
|
| 1391 |
+
2057000074,2
|
| 1392 |
+
2057000075,2
|
| 1393 |
+
2057000076,2
|
| 1394 |
+
2057000077,2
|
| 1395 |
+
2057000078,2
|
| 1396 |
+
2057000079,2
|
| 1397 |
+
2057000080,2
|
| 1398 |
+
2057000081,2
|
| 1399 |
+
2057000082,2
|
| 1400 |
+
2057000083,2
|
| 1401 |
+
2057000084,2
|
| 1402 |
+
2057000085,2
|
| 1403 |
+
2057000086,2
|
| 1404 |
+
2057000087,2
|
| 1405 |
+
2057000088,2
|
| 1406 |
+
2057000089,2
|
| 1407 |
+
2057000090,2
|
| 1408 |
+
2057000091,2
|
| 1409 |
+
2057000092,2
|
| 1410 |
+
2057000093,2
|
| 1411 |
+
2057000094,2
|
| 1412 |
+
2057000095,2
|
| 1413 |
+
2057000096,2
|
| 1414 |
+
2057000097,2
|
| 1415 |
+
2057000098,2
|
| 1416 |
+
2057000099,2
|
| 1417 |
+
2057000100,2
|
| 1418 |
+
2057000101,2
|
| 1419 |
+
2057000102,2
|
| 1420 |
+
2057000103,2
|
| 1421 |
+
2057000104,2
|
| 1422 |
+
2057000105,2
|
| 1423 |
+
2057000106,2
|
| 1424 |
+
2057000107,2
|
| 1425 |
+
2057000108,2
|
| 1426 |
+
2057000109,2
|
| 1427 |
+
2057000110,2
|
| 1428 |
+
2057000111,2
|
| 1429 |
+
2057000112,2
|
| 1430 |
+
2057000113,2
|
| 1431 |
+
2057000114,2
|
| 1432 |
+
2057000115,2
|
| 1433 |
+
2057000116,2
|
| 1434 |
+
2057000117,2
|
| 1435 |
+
2057000118,2
|
| 1436 |
+
2057000119,2
|
| 1437 |
+
2057000120,2
|
| 1438 |
+
2057000121,2
|
| 1439 |
+
2057000122,2
|
| 1440 |
+
2057000123,2
|
| 1441 |
+
2057000124,2
|
| 1442 |
+
2057000125,2
|
| 1443 |
+
2057000126,2
|
| 1444 |
+
2057000127,2
|
| 1445 |
+
2057000128,2
|
| 1446 |
+
2057000129,2
|
| 1447 |
+
2057000130,2
|
| 1448 |
+
2057000131,2
|
| 1449 |
+
2057000132,2
|
| 1450 |
+
2057000133,2
|
| 1451 |
+
2057000134,2
|
| 1452 |
+
2057000135,2
|
| 1453 |
+
2057000136,2
|
| 1454 |
+
2057000137,2
|
| 1455 |
+
2057000138,2
|
| 1456 |
+
2057000139,2
|
| 1457 |
+
2057000140,2
|
| 1458 |
+
2057000141,2
|
| 1459 |
+
2057000142,2
|
| 1460 |
+
2057000143,2
|
| 1461 |
+
2057000144,2
|
| 1462 |
+
2057000145,2
|
| 1463 |
+
2057000146,2
|
| 1464 |
+
2057000147,2
|
| 1465 |
+
2057000148,2
|
| 1466 |
+
2057000149,2
|
| 1467 |
+
2057000150,2
|
| 1468 |
+
2057000151,2
|
| 1469 |
+
2057000152,2
|
| 1470 |
+
2057000153,2
|
| 1471 |
+
2057000154,2
|
| 1472 |
+
2057000155,2
|
| 1473 |
+
2057000156,2
|
| 1474 |
+
2057000157,2
|
| 1475 |
+
2057000158,2
|
| 1476 |
+
2057000159,2
|
| 1477 |
+
2057000160,2
|
| 1478 |
+
2057000161,2
|
| 1479 |
+
2057000162,2
|
| 1480 |
+
2057000163,2
|
| 1481 |
+
2057000164,2
|
| 1482 |
+
2057000165,2
|
| 1483 |
+
2057000166,2
|
| 1484 |
+
2057000167,2
|
| 1485 |
+
2057000168,2
|
| 1486 |
+
2057000169,2
|
| 1487 |
+
2057000170,2
|
| 1488 |
+
2057000171,2
|
| 1489 |
+
2057000172,2
|
| 1490 |
+
2057000173,2
|
| 1491 |
+
2057000174,2
|
| 1492 |
+
2057000175,2
|
| 1493 |
+
2057000176,2
|
| 1494 |
+
2057000177,2
|
| 1495 |
+
2057000178,2
|
| 1496 |
+
2224020001,2
|
| 1497 |
+
2224020002,2
|
| 1498 |
+
2224020003,2
|
| 1499 |
+
2224020004,2
|
| 1500 |
+
2224020005,2
|
| 1501 |
+
2224020006,2
|
| 1502 |
+
2224020007,2
|
| 1503 |
+
2224020008,2
|
| 1504 |
+
2224020009,2
|
| 1505 |
+
2224020010,2
|
| 1506 |
+
2224020011,2
|
| 1507 |
+
2224020012,2
|
| 1508 |
+
2224020013,2
|
| 1509 |
+
2224020014,2
|
| 1510 |
+
2224020015,2
|
| 1511 |
+
2224020016,2
|
| 1512 |
+
2224020017,2
|
| 1513 |
+
2224020018,2
|
| 1514 |
+
2224020019,2
|
| 1515 |
+
2224020020,2
|
| 1516 |
+
2224020021,2
|
| 1517 |
+
2224020022,2
|
| 1518 |
+
2224020023,2
|
| 1519 |
+
2224020024,2
|
| 1520 |
+
2224020025,2
|
| 1521 |
+
2224020026,2
|
| 1522 |
+
2224020027,2
|
| 1523 |
+
2224020028,2
|
| 1524 |
+
2224020029,2
|
| 1525 |
+
2224020030,2
|
| 1526 |
+
2224020031,2
|
| 1527 |
+
2224020032,2
|
| 1528 |
+
2224020033,2
|
| 1529 |
+
2224020034,2
|
| 1530 |
+
2224020035,2
|
| 1531 |
+
2224020036,2
|
| 1532 |
+
2224020037,2
|
| 1533 |
+
2224020038,2
|
| 1534 |
+
2224020039,2
|
| 1535 |
+
2224020040,2
|
| 1536 |
+
2224020041,2
|
| 1537 |
+
2224020042,2
|
| 1538 |
+
2224020043,2
|
| 1539 |
+
2224020044,2
|
| 1540 |
+
2224020045,2
|
| 1541 |
+
2224020046,2
|
| 1542 |
+
2224020047,2
|
| 1543 |
+
2224020048,2
|
| 1544 |
+
2224020049,2
|
| 1545 |
+
2224020050,2
|
| 1546 |
+
2224020051,2
|
| 1547 |
+
2224020052,2
|
| 1548 |
+
2224020053,2
|
| 1549 |
+
2224020054,2
|
| 1550 |
+
2224020055,2
|
| 1551 |
+
2224020056,2
|
| 1552 |
+
2224020057,2
|
| 1553 |
+
2224020058,2
|
dataset/gen_cat_fold.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import random
|
| 3 |
+
import pandas as pd
|
| 4 |
+
|
| 5 |
+
DATASET_PATH = './data/coco_det/annotations/semantics.json'
|
| 6 |
+
CONTEXT_DATASET_PATH = './data/coco_context/annotations/context.json'
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def parse_img_id(img_id):
|
| 10 |
+
app_id = int(str(img_id)[:-3])
|
| 11 |
+
img = int(str(img_id)[-3:])
|
| 12 |
+
return app_id, img
|
| 13 |
+
|
| 14 |
+
with open(DATASET_PATH, 'r') as f:
|
| 15 |
+
dataset = json.load(f)
|
| 16 |
+
|
| 17 |
+
cat_name_cat = {cat['name']: cat for cat in dataset['categories']}
|
| 18 |
+
|
| 19 |
+
img_ids = set([img['id'] for img in dataset['images']])
|
| 20 |
+
|
| 21 |
+
app_id_img = {}
|
| 22 |
+
|
| 23 |
+
for img in dataset['images']:
|
| 24 |
+
app_id, img = parse_img_id(img['id'])
|
| 25 |
+
if app_id not in app_id_img:
|
| 26 |
+
app_id_img[app_id] = []
|
| 27 |
+
app_id_img[app_id].append(img)
|
| 28 |
+
|
| 29 |
+
with open(CONTEXT_DATASET_PATH, 'r') as f:
|
| 30 |
+
context_dataset = json.load(f)
|
| 31 |
+
|
| 32 |
+
context_cat_name = [cat['name'] for cat in context_dataset['categories'] if not cat['name'].endswith('-n')]
|
| 33 |
+
context_cat_id = [cat['id'] for cat in dataset['categories'] if cat['name'] in context_cat_name]
|
| 34 |
+
|
| 35 |
+
context_related_img_ids = []
|
| 36 |
+
|
| 37 |
+
for ann in dataset['annotations']:
|
| 38 |
+
if ann['category_id'] in context_cat_id:
|
| 39 |
+
context_related_img_ids.append(ann['image_id'])
|
| 40 |
+
|
| 41 |
+
context_related_img_ids = set(context_related_img_ids)
|
| 42 |
+
|
| 43 |
+
# Get the images that are not already in the context_related_img set
|
| 44 |
+
remaining_img_ids = img_ids - context_related_img_ids
|
| 45 |
+
|
| 46 |
+
# Split the remaining images into two groups with a ratio of 6:1
|
| 47 |
+
num_train = int(len(remaining_img_ids) * 6 / 7)
|
| 48 |
+
train_images = random.sample(remaining_img_ids, num_train)
|
| 49 |
+
val_images = remaining_img_ids - set(train_images)
|
| 50 |
+
|
| 51 |
+
print(len(train_images), len(val_images), len(context_related_img_ids))
|
| 52 |
+
|
| 53 |
+
df_data = []
|
| 54 |
+
df_data.extend([[img_id, 0] for img_id in train_images])
|
| 55 |
+
df_data.extend([[img_id, 1] for img_id in val_images])
|
| 56 |
+
df_data.extend([[img_id, 2] for img_id in context_related_img_ids])
|
| 57 |
+
df = pd.DataFrame(df_data, columns=['img_id', 'fold'])
|
| 58 |
+
df.to_csv('./fold_context.csv', index=False)
|
dataset/get_union_test.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
+
FORMAT = 'det'
|
| 4 |
+
|
| 5 |
+
TASK = 'interactable'
|
| 6 |
+
# TASK = 'semantics'
|
| 7 |
+
# TASK = 'interaction'
|
| 8 |
+
|
| 9 |
+
DATASET_PATH = f'./data/coco_{FORMAT}/annotations/{TASK}.json'
|
| 10 |
+
TEST_613_PATH = f'../evaluation/gts/{FORMAT}/{TASK}/613.json'
|
| 11 |
+
TEST_GENRE_PATH = f'../evaluation/gts/{FORMAT}/{TASK}/genre.json'
|
| 12 |
+
TEST_CAT_PATH = f'../evaluation/gts/{FORMAT}/{TASK}/cat.json'
|
| 13 |
+
UNION_PATH = f'../evaluation/gts/{FORMAT}/{TASK}/union_test.json'
|
| 14 |
+
UNION3_PATH = f'../evaluation/gts/{FORMAT}/{TASK}/union3_test.json'
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def get_union_test(fullset, testset1, testset2):
|
| 18 |
+
|
| 19 |
+
union_test = {'images': [], 'categories': fullset['categories'], 'annotations': []}
|
| 20 |
+
|
| 21 |
+
img_ids = []
|
| 22 |
+
|
| 23 |
+
for img in testset1['images']:
|
| 24 |
+
img_ids.append(img['id'])
|
| 25 |
+
union_test['images'].append(img)
|
| 26 |
+
for img in testset2['images']:
|
| 27 |
+
if img['id'] not in img_ids:
|
| 28 |
+
union_test['images'].append(img)
|
| 29 |
+
img_ids.append(img['id'])
|
| 30 |
+
|
| 31 |
+
for ann in fullset['annotations']:
|
| 32 |
+
if ann['image_id'] in img_ids:
|
| 33 |
+
union_test['annotations'].append(ann)
|
| 34 |
+
|
| 35 |
+
return union_test
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
with open(DATASET_PATH, 'r') as f:
|
| 39 |
+
dataset = json.load(f)
|
| 40 |
+
with open(TEST_613_PATH, 'r') as f:
|
| 41 |
+
test_613 = json.load(f)
|
| 42 |
+
with open(TEST_GENRE_PATH, 'r') as f:
|
| 43 |
+
test_genre = json.load(f)
|
| 44 |
+
with open(TEST_CAT_PATH, 'r') as f:
|
| 45 |
+
test_cat = json.load(f)
|
| 46 |
+
|
| 47 |
+
union_test = get_union_test(dataset, test_613, test_genre)
|
| 48 |
+
with open(UNION_PATH, 'w') as f:
|
| 49 |
+
json.dump(union_test, f, indent=4)
|
| 50 |
+
union3_test = get_union_test(dataset, union_test, test_cat)
|
| 51 |
+
with open(UNION3_PATH, 'w') as f:
|
| 52 |
+
json.dump(union3_test, f, indent=4)
|
dataset/get_val_gt.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import shutil
|
| 3 |
+
|
| 4 |
+
# SPLIT = '613'
|
| 5 |
+
# TRAIN_FOLD = '0,1,4,5,7,9'
|
| 6 |
+
# VAL_FOLD = '3'
|
| 7 |
+
# TEST_FOLD = '2,6,8'
|
| 8 |
+
# SPLIT_FILE = 'fold_app.csv'
|
| 9 |
+
|
| 10 |
+
# SPLIT = 'genre'
|
| 11 |
+
# TRAIN_FOLD = '0'
|
| 12 |
+
# VAL_FOLD = '1'
|
| 13 |
+
# TEST_FOLD = '2'
|
| 14 |
+
# SPLIT_FILE = 'fold_genre.csv'
|
| 15 |
+
|
| 16 |
+
SPLIT = 'cat'
|
| 17 |
+
TRAIN_FOLD = '0'
|
| 18 |
+
VAL_FOLD = '1'
|
| 19 |
+
TEST_FOLD = '2'
|
| 20 |
+
SPLIT_FILE = 'fold_cat.csv'
|
| 21 |
+
|
| 22 |
+
INPUT_PATH = './data'
|
| 23 |
+
OUTPUT_PATH = '../evaluation/gts'
|
| 24 |
+
TASKS = ['interaction', 'semantics', 'interactable']
|
| 25 |
+
FORMATS = ['det']
|
| 26 |
+
split_script_file = './split_coco.py'
|
| 27 |
+
# fold_file = './fold.csv' # split on image
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
for task in TASKS:
|
| 31 |
+
for format in FORMATS:
|
| 32 |
+
ann_file = os.path.join(INPUT_PATH, f'coco_{format}/annotations/{task}.json')
|
| 33 |
+
img_dir = os.path.join(INPUT_PATH, 'coco_merged/images', task)
|
| 34 |
+
split_path = os.path.join(INPUT_PATH, 'coco_split')
|
| 35 |
+
if os.path.exists(split_path):
|
| 36 |
+
shutil.rmtree(split_path)
|
| 37 |
+
# K fold
|
| 38 |
+
# for test_fold in range(FOLD_CNT):
|
| 39 |
+
# train_fold = ','.join([str(i) for i in range(FOLD_CNT) if i != test_fold])
|
| 40 |
+
# cli = f'python {split_script_file} ' + \
|
| 41 |
+
# f'--ann_file {ann_file} ' + \
|
| 42 |
+
# f'--img_dir {img_dir} ' + \
|
| 43 |
+
# f'--output_path {split_path} ' + \
|
| 44 |
+
# f'--fold_file {fold_file} ' + \
|
| 45 |
+
# f'--train_fold {train_fold} ' + \
|
| 46 |
+
# f'--val_fold {test_fold}'
|
| 47 |
+
# print(cli)
|
| 48 |
+
# os.system(cli)
|
| 49 |
+
# os.makedirs(os.path.join(OUTPUT_PATH, f'{format}/{task}'), exist_ok=True)
|
| 50 |
+
# shutil.copyfile(os.path.join(split_path, 'annotations', 'instances_val2017.json'), os.path.join(OUTPUT_PATH, f'{format}/{task}/fold{test_fold}.json'))
|
| 51 |
+
# shutil.rmtree(split_path)
|
| 52 |
+
|
| 53 |
+
# custom split
|
| 54 |
+
cli = f'python {split_script_file} ' + \
|
| 55 |
+
f'--ann_file {ann_file} ' + \
|
| 56 |
+
f'--img_dir {img_dir} ' + \
|
| 57 |
+
f'--output_path {split_path} ' + \
|
| 58 |
+
f'--fold_file {SPLIT_FILE} ' + \
|
| 59 |
+
f'--train_fold {TRAIN_FOLD} ' + \
|
| 60 |
+
f'--val_fold {VAL_FOLD} ' + \
|
| 61 |
+
f'--test_fold {TEST_FOLD} '
|
| 62 |
+
print(cli)
|
| 63 |
+
os.system(cli)
|
| 64 |
+
os.makedirs(os.path.join(OUTPUT_PATH, f'{format}/{task}'), exist_ok=True)
|
| 65 |
+
shutil.copy2(os.path.join(split_path, 'annotations', 'instances_test2017.json'), os.path.join(OUTPUT_PATH, f'{format}/{task}/{SPLIT}.json'))
|
| 66 |
+
shutil.rmtree(split_path)
|
dataset/interactable_coco.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# obtain interactable dataset from semantics dataset
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
import argparse
|
| 5 |
+
|
| 6 |
+
parser = argparse.ArgumentParser()
|
| 7 |
+
parser.add_argument('--input', type=str, required=True)
|
| 8 |
+
parser.add_argument('--output', type=str, required=True)
|
| 9 |
+
args = parser.parse_args()
|
| 10 |
+
|
| 11 |
+
with open(args.input, 'r') as fr:
|
| 12 |
+
data = json.load(fr)
|
| 13 |
+
|
| 14 |
+
data['categories'] = [{'id': 1, 'name': 'interactable'}]
|
| 15 |
+
for annotation in data['annotations']:
|
| 16 |
+
annotation['category_id'] = 1
|
| 17 |
+
|
| 18 |
+
with open(args.output, 'w') as fw:
|
| 19 |
+
json.dump(data, fw, indent=4)
|
dataset/list-union3.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
438100001, 438100002, 438100003, 438100004, 438100005, 438100006, 438100007, 438100008, 438100009, 438100010, 457380008, 457380010, 457380013, 1250210001, 1250210002, 1250210003, 1250210004, 1250210005, 1250210006, 1250210007, 1250210008, 1250210009, 1250210010, 1250210011, 1250210012, 1676620003, 1676620004, 1676620005, 1676620006, 1676620007, 1730290001, 1730290002, 1730290003, 1730290004, 1730290005, 497820001, 497820002, 497820003, 1178780015, 1486660012, 1456280001, 1456280002, 1456280003, 1456280004, 591680001, 591680002, 591680003, 591680004, 591680005, 591680006, 1707840007, 591680008, 591680009, 591680010, 591680011, 591680012, 591680013, 591680007, 1394410001, 1394410002, 1394410003, 1394410004, 1394410005, 622310001, 622310002, 716260002, 716260003, 716260005, 790750001, 790750002, 790750003, 790750004, 790750005, 790750006, 954160001, 954160002, 1561560001, 1561560002, 1561560003, 1561560004, 1561560005, 1561560006, 1561560007, 1561560008, 1561560009, 1561560010, 1561560011, 1561560012, 1561560013, 1561560014, 1561560015, 1561560016, 1561560017, 1561560018, 1561560019, 1561560020, 1561560021, 1561560022, 1561560023, 1561560024, 1561560025, 1561560026, 1561560027, 1561560028, 1561560029, 1561560030, 1561560031, 1561560032, 726910002, 726910003, 726910004, 726910005, 726910007, 726910010, 2057000001, 2057000002, 2057000003, 2057000004, 2057000005, 2057000006, 2057000007, 2057000008, 2057000009, 2057000010, 2057000011, 2057000012, 2057000013, 2057000014, 2057000015, 2057000016, 2057000017, 2057000018, 2057000019, 2057000020, 2057000021, 2057000022, 2057000023, 2057000024, 2057000025, 2057000026, 2057000027, 2057000028, 2057000029, 2057000030, 2057000031, 2057000032, 2057000033, 2057000034, 2057000035, 2057000036, 2057000037, 2057000038, 2057000039, 2057000040, 2057000041, 2057000042, 2057000043, 2057000044, 2057000045, 2057000046, 2057000047, 2057000048, 2057000049, 2057000050, 2057000051, 2057000052, 2057000053, 2057000054, 2057000055, 2057000056, 2057000057, 2057000058, 2057000059, 2057000060, 2057000061, 2057000062, 2057000063, 2057000064, 2057000065, 2057000066, 2057000067, 2057000068, 2057000069, 2057000070, 2057000071, 2057000072, 2057000073, 2057000074, 2057000075, 2057000076, 2057000077, 2057000078, 2057000079, 2057000080, 2057000081, 2057000082, 2057000083, 2057000084, 2057000085, 2057000086, 2057000087, 2057000088, 2057000089, 2057000090, 2057000091, 2057000092, 2057000093, 2057000094, 2057000095, 2057000096, 2057000097, 2057000098, 2057000099, 2057000100, 2057000101, 2057000102, 2057000103, 2057000104, 2057000105, 2057000106, 2057000107, 2057000108, 2057000109, 2057000110, 2057000111, 2057000112, 2057000113, 2057000114, 2057000115, 2057000116, 2057000117, 2057000118, 2057000119, 2057000120, 2057000121, 2057000122, 2057000123, 2057000124, 2057000125, 2057000126, 2057000127, 2057000128, 2057000129, 2057000130, 2057000131, 2057000132, 2057000133, 2057000134, 2057000135, 2057000136, 2057000137, 2057000138, 2057000139, 2057000140, 2057000141, 2057000142, 2057000143, 2057000144, 2057000145, 2057000146, 2057000147, 2057000148, 2057000149, 2057000150, 2057000151, 2057000152, 2057000153, 2057000154, 2057000155, 2057000156, 2057000157, 2057000158, 2057000159, 2057000160, 2057000161, 2057000162, 2057000163, 2057000164, 2057000165, 2057000166, 2057000167, 2057000168, 2057000169, 2057000170, 2057000171, 2057000172, 2057000173, 2057000174, 2057000175, 2057000176, 2057000177, 2057000178, 2193270001, 1334900003, 1334900004, 1334900005, 1334900006, 1550280001, 1404360004, 1245640005, 1245640006, 1404360005, 1404360006, 1245640009, 1404360007, 1404360009, 1404360010, 1550280010, 1245640014, 1245640015, 1245640016, 1404360016, 1404360018, 1550280018, 269170001, 1550280012, 1404360022, 1404360023, 269170002, 269170009, 269170008, 1550280027, 269170003, 1404360029, 1550280029, 1550280030, 1245640032, 1550280031, 1245640034, 1550280035, 1245640036, 1245640037, 1550280036, 1550280037, 1550280039, 1550280041, 1550280042, 1550280043, 1550280045, 1550280049, 1805510001, 1550280051, 1550280052, 1550280053, 1805510002, 1805510003, 1805510006, 1805510008, 1805510009, 1805510010, 1805510011, 1805510012, 1805510005, 1805510015, 1805510016, 1805510013, 1805510014, 1805510017, 1805510018, 1805510021, 1805510019, 1805510020, 1805510022, 1805510023, 1805510026, 1805510024, 1805510025, 1805510027, 1805510028, 1805510029, 1805510030, 1805510031, 1805510032, 1805510033, 2163140004, 2163140006, 2163140007, 2163140011, 1550280013, 2163140012, 2163140014, 2163140015, 2163140016, 2163140022, 2163140024, 2163140025, 2163140029, 2163140033, 2163140034, 2163140038, 2163140039, 2163140042, 2163140043, 2163140048, 2163140050, 2163140053, 2163140055, 2163140057, 2163140058, 1150310001, 1150310002, 1150310003, 1150310004, 1150310005, 1150310006, 1337530003, 1337530005, 1337530006, 1337530009, 1337530010, 1337530013, 1337530014, 1337530017, 1337530018, 1337530022, 1337530023, 1337530024, 1337530027, 1337530034, 1248270004, 1248270005, 1337530037, 1248270008, 1337530041, 1248270010, 1248270011, 1248270012, 1337530046, 1337530048, 1337530049, 1337530050, 1337530057, 1337530061, 1337530063, 1337530067, 1337530069, 1337530070, 1337530075, 1337530077, 1337530079, 982710005, 982710006, 982710008, 518580001, 518580002, 518580003, 518580004, 518580005, 518580006, 518580010, 518580011, 518580015, 518580016, 518580023, 518580024, 731790005, 731790010, 1488730001, 998660001, 998660002, 998660003, 998660004, 998660005, 2077870001, 2077870002, 2077870003, 2077870004, 2077870005, 720300003, 812460003, 812460004, 812460005, 812460006, 720300008, 812460007, 812460008, 812460009, 720300012, 812460010, 720300017, 1388030001, 269170004, 269170005, 269170006, 269170007, 457550001, 457550002, 457550003, 457550004, 457550005, 1480650002, 1453730001, 1453730002, 1453730003, 1453730004, 1453730005, 1453730006, 1453730007, 1453730008, 1453730009, 1453730010, 1453730011, 1453730012, 1453730013, 1453730014, 1480650007, 600140001, 600140002, 600140003, 600140004, 600140005, 600140006, 600140007, 600140008, 600140009, 600140010, 600140011, 600140012, 600140013, 600140014, 600140015, 600140016, 600140017, 600140018, 600140019, 600140020, 600140021, 600140022, 600140023, 600140024, 866540001, 866540002, 866540003, 866540004, 866540005, 866540006, 866540007, 866540008, 866540009, 866540010, 866540011, 1652800001, 1652800002, 1652800003, 1652800004, 1652800005, 1652800006, 1652800007, 1652800008, 1652800009, 1652800010, 1652800011, 1652800012, 1652800013, 2224020001, 2224020002, 2224020003, 2224020004, 2224020005, 2224020006, 2224020007, 2224020008, 2224020009, 2224020010, 2224020011, 2224020012, 2224020013, 2224020014, 2224020015, 2224020016, 2224020017, 2224020018, 2224020019, 2224020020, 2224020021, 2224020022, 2224020023, 2224020024, 2224020025, 2224020026, 2224020027, 2224020028, 2224020029, 2224020030, 2224020031, 2224020032, 2224020033, 2224020034, 2224020035, 2224020036, 2224020037, 2224020038, 2224020039, 2224020040, 2224020041, 2224020042, 2224020043, 2224020044, 2224020045, 2224020046, 2224020047, 2224020048, 2224020049, 2224020050, 2224020051, 2224020052, 2224020053, 2224020054, 2224020055, 2224020056, 2224020057, 2224020058, 1805510004, 1805510007, 1337060007, 1337060008, 1337060009, 1337060010, 1337060011, 1337060012, 1337060015, 731790001, 1337060018, 1337060019, 1337060020, 1337060021, 1337060022, 1337060023, 1337060024, 1337060025, 1337060026, 731790002, 731790003, 731790004, 1337060030, 731790006, 731790007, 731790008, 731790009, 1337060035, 891960001, 891960002, 891960003, 891960004, 891960005, 891960006, 891960007, 891960008, 891960009, 891960010, 451980001, 451980002, 451980003, 451980004, 451980005, 451980006, 451980007, 451980008, 451980009, 451980010, 451980011, 451980012, 451980013, 451980014, 451980015, 451980016, 451980017, 451980018, 1512840001, 1512840002, 1512840003, 1512840004, 1512840005, 1512840006, 1512840007, 2089520001, 2089520002, 2089520003, 2089520004, 1269890001, 1269890002, 1269890003, 1269890004, 1269890005, 1269890006, 1269890007, 1269890008, 1269890009, 1269890010, 1269890011, 1269890012, 1269890013, 1269890014, 1269890015, 997760001, 997760002, 997760003, 997760004, 997760005, 997760006, 997760007, 997760008, 997760009, 997760010, 997760011, 997760012, 1270910001, 1270910002, 1270910003, 1270910004, 1270910005, 2025000001, 2025000002, 2025000003, 2025000004, 2025000005, 2025000006, 660520005, 2025000007, 2025000009, 2025000010, 660520009, 2025000008, 1862180001, 1862180002, 1862180003, 1862180004, 1862180005, 1862180006, 1862180007, 1157070001, 1382350002, 1157070002, 1382350004, 1157070003, 1382350006, 1382350007, 1382350008, 1157070004, 1157070005, 1157070006, 1157070007, 1157070008, 1931980011, 1264160001, 1264160002, 1264160003, 1264160004, 1264160005, 1264160006, 1264160007, 1264160008, 1264160009, 1264160010, 1264160011, 1264160012, 1264160013, 1264160014, 1264160015, 1264160016, 1480650001, 898080018, 1480650003, 1298890004, 1480650004, 1298890006, 1480650005, 1298890008, 1298890009, 1298890010, 1480650006, 1298890012, 1298890013, 1298890014, 1480650008, 1298890016, 898080026, 898080027, 1298890019, 1298890020, 1298890021, 714100004, 714100006, 714100007, 1026760001, 1026760002, 1026760003, 1026760004, 1026760005, 1026760006, 1026760007, 1026760008, 1026760009, 1026760010, 1026760011, 1026760012, 1026760013, 1026760014, 1026760015, 1026760016, 1026760017, 1026760018, 1026760019, 1026760020, 1026760021, 1026760022, 1026760023, 1026760024, 1026760025, 1026760026, 1026760027, 1113370001, 1210650002, 1113370002, 1113370003, 1210650005, 1113370004, 1210650011, 1210650012, 1210650013, 1210650014, 528580003, 528580004, 1210650023, 1210650024, 1210650025, 1210650026, 1210650027, 1210650028, 1264160017, 758210001, 758210002, 758210003, 758210004, 758210005, 758210006, 758210007, 758210008, 758210009, 758210010, 758210011, 758210012, 758210013, 758210014, 1906880001, 1906880002, 1906880003, 1063530001, 1063530002, 1063530004, 1063530007, 1063530008, 1063530009, 1063530010, 1063530011, 1063530012, 1063530014, 1063530015, 1063530016, 1063530018, 1063530019, 1063530021, 1063530023, 1063530029, 1063530032, 1063530048, 1063530052, 898080019, 1063530057, 1063530059, 1063530065, 1063530067, 1063530069, 1063530070, 898080022, 1063530073, 1063530074, 1063530079, 1063530083, 898080028, 463290002, 463290003, 463290008, 1595490008, 490250001, 490250002, 490250003, 490250004, 490250005, 490250006, 490250007, 490250008, 490250009, 490250010, 490250011, 490250012, 490250013, 490250014, 490250015, 490250016, 490250017, 1825460002, 1825460003, 490250018, 490250019, 1825460001, 1825460004, 1825460005, 1825460009, 1825460006, 1825460007, 1825460008, 1825460010, 1825460011, 1825460012, 815280001, 815280002, 815280003, 815280004, 815280005, 815280006, 815280007, 815280008, 815280009, 815280010, 815280011, 815280012, 815280013, 815280014, 815280015, 815280016, 815280017, 815280018, 2020950001, 2020950002, 2020950003
|
dataset/list_union3.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
438100001, 438100002, 438100003, 438100004, 438100005, 438100006, 438100007, 438100008, 438100009, 438100010, 457380008, 457380010, 457380013, 1250210001, 1250210002, 1250210003, 1250210004, 1250210005, 1250210006, 1250210007, 1250210008, 1250210009, 1250210010, 1250210011, 1250210012, 1676620003, 1676620004, 1676620005, 1676620006, 1676620007, 1730290001, 1730290002, 1730290003, 1730290004, 1730290005, 497820001, 497820002, 497820003, 1178780015, 1486660012, 1456280001, 1456280002, 1456280003, 1456280004, 591680001, 591680002, 591680003, 591680004, 591680005, 591680006, 1707840007, 591680008, 591680009, 591680010, 591680011, 591680012, 591680013, 591680007, 1394410001, 1394410002, 1394410003, 1394410004, 1394410005, 622310001, 622310002, 716260002, 716260003, 716260005, 790750001, 790750002, 790750003, 790750004, 790750005, 790750006, 954160001, 954160002, 1561560001, 1561560002, 1561560003, 1561560004, 1561560005, 1561560006, 1561560007, 1561560008, 1561560009, 1561560010, 1561560011, 1561560012, 1561560013, 1561560014, 1561560015, 1561560016, 1561560017, 1561560018, 1561560019, 1561560020, 1561560021, 1561560022, 1561560023, 1561560024, 1561560025, 1561560026, 1561560027, 1561560028, 1561560029, 1561560030, 1561560031, 1561560032, 726910002, 726910003, 726910004, 726910005, 726910007, 726910010, 2057000001, 2057000002, 2057000003, 2057000004, 2057000005, 2057000006, 2057000007, 2057000008, 2057000009, 2057000010, 2057000011, 2057000012, 2057000013, 2057000014, 2057000015, 2057000016, 2057000017, 2057000018, 2057000019, 2057000020, 2057000021, 2057000022, 2057000023, 2057000024, 2057000025, 2057000026, 2057000027, 2057000028, 2057000029, 2057000030, 2057000031, 2057000032, 2057000033, 2057000034, 2057000035, 2057000036, 2057000037, 2057000038, 2057000039, 2057000040, 2057000041, 2057000042, 2057000043, 2057000044, 2057000045, 2057000046, 2057000047, 2057000048, 2057000049, 2057000050, 2057000051, 2057000052, 2057000053, 2057000054, 2057000055, 2057000056, 2057000057, 2057000058, 2057000059, 2057000060, 2057000061, 2057000062, 2057000063, 2057000064, 2057000065, 2057000066, 2057000067, 2057000068, 2057000069, 2057000070, 2057000071, 2057000072, 2057000073, 2057000074, 2057000075, 2057000076, 2057000077, 2057000078, 2057000079, 2057000080, 2057000081, 2057000082, 2057000083, 2057000084, 2057000085, 2057000086, 2057000087, 2057000088, 2057000089, 2057000090, 2057000091, 2057000092, 2057000093, 2057000094, 2057000095, 2057000096, 2057000097, 2057000098, 2057000099, 2057000100, 2057000101, 2057000102, 2057000103, 2057000104, 2057000105, 2057000106, 2057000107, 2057000108, 2057000109, 2057000110, 2057000111, 2057000112, 2057000113, 2057000114, 2057000115, 2057000116, 2057000117, 2057000118, 2057000119, 2057000120, 2057000121, 2057000122, 2057000123, 2057000124, 2057000125, 2057000126, 2057000127, 2057000128, 2057000129, 2057000130, 2057000131, 2057000132, 2057000133, 2057000134, 2057000135, 2057000136, 2057000137, 2057000138, 2057000139, 2057000140, 2057000141, 2057000142, 2057000143, 2057000144, 2057000145, 2057000146, 2057000147, 2057000148, 2057000149, 2057000150, 2057000151, 2057000152, 2057000153, 2057000154, 2057000155, 2057000156, 2057000157, 2057000158, 2057000159, 2057000160, 2057000161, 2057000162, 2057000163, 2057000164, 2057000165, 2057000166, 2057000167, 2057000168, 2057000169, 2057000170, 2057000171, 2057000172, 2057000173, 2057000174, 2057000175, 2057000176, 2057000177, 2057000178, 2193270001, 1334900003, 1334900004, 1334900005, 1334900006, 1550280001, 1404360004, 1245640005, 1245640006, 1404360005, 1404360006, 1245640009, 1404360007, 1404360009, 1404360010, 1550280010, 1245640014, 1245640015, 1245640016, 1404360016, 1404360018, 1550280018, 269170001, 1550280012, 1404360022, 1404360023, 269170002, 269170009, 269170008, 1550280027, 269170003, 1404360029, 1550280029, 1550280030, 1245640032, 1550280031, 1245640034, 1550280035, 1245640036, 1245640037, 1550280036, 1550280037, 1550280039, 1550280041, 1550280042, 1550280043, 1550280045, 1550280049, 1805510001, 1550280051, 1550280052, 1550280053, 1805510002, 1805510003, 1805510006, 1805510008, 1805510009, 1805510010, 1805510011, 1805510012, 1805510005, 1805510015, 1805510016, 1805510013, 1805510014, 1805510017, 1805510018, 1805510021, 1805510019, 1805510020, 1805510022, 1805510023, 1805510026, 1805510024, 1805510025, 1805510027, 1805510028, 1805510029, 1805510030, 1805510031, 1805510032, 1805510033, 2163140004, 2163140006, 2163140007, 2163140011, 1550280013, 2163140012, 2163140014, 2163140015, 2163140016, 2163140022, 2163140024, 2163140025, 2163140029, 2163140033, 2163140034, 2163140038, 2163140039, 2163140042, 2163140043, 2163140048, 2163140050, 2163140053, 2163140055, 2163140057, 2163140058, 1150310001, 1150310002, 1150310003, 1150310004, 1150310005, 1150310006, 1337530003, 1337530005, 1337530006, 1337530009, 1337530010, 1337530013, 1337530014, 1337530017, 1337530018, 1337530022, 1337530023, 1337530024, 1337530027, 1337530034, 1248270004, 1248270005, 1337530037, 1248270008, 1337530041, 1248270010, 1248270011, 1248270012, 1337530046, 1337530048, 1337530049, 1337530050, 1337530057, 1337530061, 1337530063, 1337530067, 1337530069, 1337530070, 1337530075, 1337530077, 1337530079, 982710005, 982710006, 982710008, 518580001, 518580002, 518580003, 518580004, 518580005, 518580006, 518580010, 518580011, 518580015, 518580016, 518580023, 518580024, 731790005, 731790010, 1488730001, 998660001, 998660002, 998660003, 998660004, 998660005, 2077870001, 2077870002, 2077870003, 2077870004, 2077870005, 720300003, 812460003, 812460004, 812460005, 812460006, 720300008, 812460007, 812460008, 812460009, 720300012, 812460010, 720300017, 1388030001, 269170004, 269170005, 269170006, 269170007, 457550001, 457550002, 457550003, 457550004, 457550005, 1480650002, 1453730001, 1453730002, 1453730003, 1453730004, 1453730005, 1453730006, 1453730007, 1453730008, 1453730009, 1453730010, 1453730011, 1453730012, 1453730013, 1453730014, 1480650007, 600140001, 600140002, 600140003, 600140004, 600140005, 600140006, 600140007, 600140008, 600140009, 600140010, 600140011, 600140012, 600140013, 600140014, 600140015, 600140016, 600140017, 600140018, 600140019, 600140020, 600140021, 600140022, 600140023, 600140024, 866540001, 866540002, 866540003, 866540004, 866540005, 866540006, 866540007, 866540008, 866540009, 866540010, 866540011, 1652800001, 1652800002, 1652800003, 1652800004, 1652800005, 1652800006, 1652800007, 1652800008, 1652800009, 1652800010, 1652800011, 1652800012, 1652800013, 2224020001, 2224020002, 2224020003, 2224020004, 2224020005, 2224020006, 2224020007, 2224020008, 2224020009, 2224020010, 2224020011, 2224020012, 2224020013, 2224020014, 2224020015, 2224020016, 2224020017, 2224020018, 2224020019, 2224020020, 2224020021, 2224020022, 2224020023, 2224020024, 2224020025, 2224020026, 2224020027, 2224020028, 2224020029, 2224020030, 2224020031, 2224020032, 2224020033, 2224020034, 2224020035, 2224020036, 2224020037, 2224020038, 2224020039, 2224020040, 2224020041, 2224020042, 2224020043, 2224020044, 2224020045, 2224020046, 2224020047, 2224020048, 2224020049, 2224020050, 2224020051, 2224020052, 2224020053, 2224020054, 2224020055, 2224020056, 2224020057, 2224020058, 1805510004, 1805510007, 1337060007, 1337060008, 1337060009, 1337060010, 1337060011, 1337060012, 1337060015, 731790001, 1337060018, 1337060019, 1337060020, 1337060021, 1337060022, 1337060023, 1337060024, 1337060025, 1337060026, 731790002, 731790003, 731790004, 1337060030, 731790006, 731790007, 731790008, 731790009, 1337060035, 891960001, 891960002, 891960003, 891960004, 891960005, 891960006, 891960007, 891960008, 891960009, 891960010, 451980001, 451980002, 451980003, 451980004, 451980005, 451980006, 451980007, 451980008, 451980009, 451980010, 451980011, 451980012, 451980013, 451980014, 451980015, 451980016, 451980017, 451980018, 1512840001, 1512840002, 1512840003, 1512840004, 1512840005, 1512840006, 1512840007, 2089520001, 2089520002, 2089520003, 2089520004, 1269890001, 1269890002, 1269890003, 1269890004, 1269890005, 1269890006, 1269890007, 1269890008, 1269890009, 1269890010, 1269890011, 1269890012, 1269890013, 1269890014, 1269890015, 997760001, 997760002, 997760003, 997760004, 997760005, 997760006, 997760007, 997760008, 997760009, 997760010, 997760011, 997760012, 1270910001, 1270910002, 1270910003, 1270910004, 1270910005, 2025000001, 2025000002, 2025000003, 2025000004, 2025000005, 2025000006, 660520005, 2025000007, 2025000009, 2025000010, 660520009, 2025000008, 1862180001, 1862180002, 1862180003, 1862180004, 1862180005, 1862180006, 1862180007, 1157070001, 1382350002, 1157070002, 1382350004, 1157070003, 1382350006, 1382350007, 1382350008, 1157070004, 1157070005, 1157070006, 1157070007, 1157070008, 1931980011, 1264160001, 1264160002, 1264160003, 1264160004, 1264160005, 1264160006, 1264160007, 1264160008, 1264160009, 1264160010, 1264160011, 1264160012, 1264160013, 1264160014, 1264160015, 1264160016, 1480650001, 898080018, 1480650003, 1298890004, 1480650004, 1298890006, 1480650005, 1298890008, 1298890009, 1298890010, 1480650006, 1298890012, 1298890013, 1298890014, 1480650008, 1298890016, 898080026, 898080027, 1298890019, 1298890020, 1298890021, 714100004, 714100006, 714100007, 1026760001, 1026760002, 1026760003, 1026760004, 1026760005, 1026760006, 1026760007, 1026760008, 1026760009, 1026760010, 1026760011, 1026760012, 1026760013, 1026760014, 1026760015, 1026760016, 1026760017, 1026760018, 1026760019, 1026760020, 1026760021, 1026760022, 1026760023, 1026760024, 1026760025, 1026760026, 1026760027, 1113370001, 1210650002, 1113370002, 1113370003, 1210650005, 1113370004, 1210650011, 1210650012, 1210650013, 1210650014, 528580003, 528580004, 1210650023, 1210650024, 1210650025, 1210650026, 1210650027, 1210650028, 1264160017, 758210001, 758210002, 758210003, 758210004, 758210005, 758210006, 758210007, 758210008, 758210009, 758210010, 758210011, 758210012, 758210013, 758210014, 1906880001, 1906880002, 1906880003, 1063530001, 1063530002, 1063530004, 1063530007, 1063530008, 1063530009, 1063530010, 1063530011, 1063530012, 1063530014, 1063530015, 1063530016, 1063530018, 1063530019, 1063530021, 1063530023, 1063530029, 1063530032, 1063530048, 1063530052, 898080019, 1063530057, 1063530059, 1063530065, 1063530067, 1063530069, 1063530070, 898080022, 1063530073, 1063530074, 1063530079, 1063530083, 898080028, 463290002, 463290003, 463290008, 1595490008, 490250001, 490250002, 490250003, 490250004, 490250005, 490250006, 490250007, 490250008, 490250009, 490250010, 490250011, 490250012, 490250013, 490250014, 490250015, 490250016, 490250017, 1825460002, 1825460003, 490250018, 490250019, 1825460001, 1825460004, 1825460005, 1825460009, 1825460006, 1825460007, 1825460008, 1825460010, 1825460011, 1825460012, 815280001, 815280002, 815280003, 815280004, 815280005, 815280006, 815280007, 815280008, 815280009, 815280010, 815280011, 815280012, 815280013, 815280014, 815280015, 815280016, 815280017, 815280018, 2020950001, 2020950002, 2020950003
|
dataset/split_coco.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import shutil
|
| 4 |
+
import argparse
|
| 5 |
+
import random
|
| 6 |
+
import pandas as pd
|
| 7 |
+
|
| 8 |
+
FOLD_CNT = 10
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def generate_fold(args):
|
| 12 |
+
df = pd.DataFrame(columns=['img_id', 'fold'])
|
| 13 |
+
json_file_path = args.ann_file
|
| 14 |
+
assert os.path.exists(json_file_path), f'json file not found: {json_file_path}'
|
| 15 |
+
with open(json_file_path, 'r') as f:
|
| 16 |
+
json_data = json.load(f)
|
| 17 |
+
|
| 18 |
+
# reproducable random shuffle
|
| 19 |
+
random.seed(1)
|
| 20 |
+
random.shuffle(json_data['images'])
|
| 21 |
+
fold_size = len(json_data['images']) // FOLD_CNT
|
| 22 |
+
for image in json_data['images']:
|
| 23 |
+
df.loc[len(df), df.columns] = [image['id'], min(len(df) // fold_size, FOLD_CNT - 1)]
|
| 24 |
+
df.to_csv(args.fold_file, index=False)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def generate_fold_app(args):
|
| 28 |
+
df = pd.DataFrame(columns=['img_id', 'fold'])
|
| 29 |
+
json_file_path = args.ann_file
|
| 30 |
+
assert os.path.exists(json_file_path), f'json file not found: {json_file_path}'
|
| 31 |
+
with open(json_file_path, 'r') as f:
|
| 32 |
+
json_data = json.load(f)
|
| 33 |
+
|
| 34 |
+
apps = set()
|
| 35 |
+
|
| 36 |
+
for image in json_data['images']:
|
| 37 |
+
appid = str(image['id'])[:-3]
|
| 38 |
+
apps.add(appid)
|
| 39 |
+
|
| 40 |
+
apps = list(apps)
|
| 41 |
+
bel_app = {}
|
| 42 |
+
|
| 43 |
+
# reproducable random shuffle
|
| 44 |
+
apps = sorted(apps)
|
| 45 |
+
random.seed(1)
|
| 46 |
+
random.shuffle(apps)
|
| 47 |
+
fold_size = len(apps) // FOLD_CNT
|
| 48 |
+
for app in apps:
|
| 49 |
+
bel_app[app] = min(len(bel_app) // fold_size, FOLD_CNT - 1)
|
| 50 |
+
for image in json_data['images']:
|
| 51 |
+
appid = str(image['id'])[:-3]
|
| 52 |
+
df.loc[len(df), df.columns] = [image['id'], bel_app[appid]]
|
| 53 |
+
|
| 54 |
+
df = df.sort_values(by='fold')
|
| 55 |
+
df.to_csv(args.fold_file, index=False)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def split(args):
|
| 59 |
+
json_file_path = args.ann_file
|
| 60 |
+
image_path = args.img_dir if args.img_dir and os.path.exists(args.img_dir) else None
|
| 61 |
+
fold_file = args.fold_file
|
| 62 |
+
output_path = args.output_path
|
| 63 |
+
|
| 64 |
+
assert os.path.exists(json_file_path), f'json file not found: {json_file_path}'
|
| 65 |
+
# assert os.path.exists(image_path), f'image path not found: {image_path}'
|
| 66 |
+
assert os.path.exists(fold_file), f'fold file not found: {fold_file}'
|
| 67 |
+
|
| 68 |
+
with open(json_file_path, 'r') as f:
|
| 69 |
+
json_data = json.load(f)
|
| 70 |
+
|
| 71 |
+
df = pd.read_csv(fold_file)
|
| 72 |
+
|
| 73 |
+
train_dataset = {'images': list(), 'categories': json_data['categories'], 'annotations': list()}
|
| 74 |
+
val_dataset = {'images': list(), 'categories': json_data['categories'], 'annotations': list()}
|
| 75 |
+
test_dataset = {'images': list(), 'categories': json_data['categories'], 'annotations': list()}
|
| 76 |
+
|
| 77 |
+
train_folds = [int(fold) for fold in args.train_folds.split(',')] if args.train_folds else []
|
| 78 |
+
val_folds = [int(fold) for fold in args.val_folds.split(',')] if args.val_folds else []
|
| 79 |
+
test_folds = [int(fold) for fold in args.test_folds.split(',')] if args.test_folds else []
|
| 80 |
+
|
| 81 |
+
print(f'train folds: {train_folds}')
|
| 82 |
+
print(f'val folds: {val_folds}')
|
| 83 |
+
print(f'test folds: {test_folds}')
|
| 84 |
+
|
| 85 |
+
train_imgid = set()
|
| 86 |
+
val_imgid = set()
|
| 87 |
+
test_imgid = set()
|
| 88 |
+
|
| 89 |
+
for _, row in df.iterrows():
|
| 90 |
+
if row['fold'] in train_folds:
|
| 91 |
+
train_imgid.add(row['img_id'])
|
| 92 |
+
elif row['fold'] in val_folds:
|
| 93 |
+
val_imgid.add(row['img_id'])
|
| 94 |
+
elif row['fold'] in test_folds:
|
| 95 |
+
test_imgid.add(row['img_id'])
|
| 96 |
+
else:
|
| 97 |
+
raise ValueError(f'fold not found: {row["fold"]}')
|
| 98 |
+
|
| 99 |
+
for image in json_data['images']:
|
| 100 |
+
if image['id'] in train_imgid:
|
| 101 |
+
train_dataset['images'].append(image)
|
| 102 |
+
elif image['id'] in val_imgid:
|
| 103 |
+
val_dataset['images'].append(image)
|
| 104 |
+
elif image['id'] in test_imgid:
|
| 105 |
+
test_dataset['images'].append(image)
|
| 106 |
+
else:
|
| 107 |
+
raise ValueError(f'image id not found: {image["id"]}')
|
| 108 |
+
|
| 109 |
+
for annotation in json_data['annotations']:
|
| 110 |
+
if annotation['image_id'] in train_imgid:
|
| 111 |
+
train_dataset['annotations'].append(annotation)
|
| 112 |
+
elif annotation['image_id'] in val_imgid:
|
| 113 |
+
val_dataset['annotations'].append(annotation)
|
| 114 |
+
elif annotation['image_id'] in test_imgid:
|
| 115 |
+
test_dataset['annotations'].append(annotation)
|
| 116 |
+
else:
|
| 117 |
+
raise ValueError(f'annotation image_id not found: {annotation["image_id"]}')
|
| 118 |
+
|
| 119 |
+
print(f'train dataset: {len(train_dataset["images"])} images, {len(train_dataset["annotations"])} annotations')
|
| 120 |
+
print(f'val dataset: {len(val_dataset["images"])} images, {len(val_dataset["annotations"])} annotations')
|
| 121 |
+
print(f'test dataset: {len(test_dataset["images"])} images, {len(test_dataset["annotations"])} annotations')
|
| 122 |
+
|
| 123 |
+
print(os.path.abspath(output_path))
|
| 124 |
+
train_image_path = os.path.join(output_path, 'images', 'instances_train2017')
|
| 125 |
+
val_image_path = os.path.join(output_path, 'images', 'instances_val2017')
|
| 126 |
+
test_image_path = os.path.join(output_path, 'images', 'instances_test2017')
|
| 127 |
+
os.makedirs(output_path, exist_ok=True)
|
| 128 |
+
os.makedirs(train_image_path, exist_ok=True)
|
| 129 |
+
os.makedirs(val_image_path, exist_ok=True)
|
| 130 |
+
os.makedirs(test_image_path, exist_ok=True)
|
| 131 |
+
os.makedirs(os.path.join(output_path, 'annotations'), exist_ok=True)
|
| 132 |
+
|
| 133 |
+
with open(os.path.join(output_path, 'annotations', 'instances_train2017.json'), 'w') as f:
|
| 134 |
+
json.dump(train_dataset, f, indent=4)
|
| 135 |
+
with open(os.path.join(output_path, 'annotations', 'instances_val2017.json'), 'w') as f:
|
| 136 |
+
json.dump(val_dataset, f, indent=4)
|
| 137 |
+
with open(os.path.join(output_path, 'annotations', 'instances_test2017.json'), 'w') as f:
|
| 138 |
+
json.dump(test_dataset, f, indent=4)
|
| 139 |
+
|
| 140 |
+
if image_path:
|
| 141 |
+
for image in train_dataset['images']:
|
| 142 |
+
shutil.copy(os.path.join(image_path, image['file_name']), train_image_path)
|
| 143 |
+
for image in val_dataset['images']:
|
| 144 |
+
shutil.copy(os.path.join(image_path, image['file_name']), val_image_path)
|
| 145 |
+
for image in test_dataset['images']:
|
| 146 |
+
shutil.copy(os.path.join(image_path, image['file_name']), test_image_path)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def main(args):
|
| 150 |
+
if args.gen:
|
| 151 |
+
generate_fold_app(args)
|
| 152 |
+
split(args)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
if __name__ == '__main__':
|
| 156 |
+
parser = argparse.ArgumentParser()
|
| 157 |
+
parser.add_argument('--ann_file', type=str, required=True)
|
| 158 |
+
parser.add_argument('--img_dir', type=str, required=True)
|
| 159 |
+
parser.add_argument('--output_path', type=str, default='./coco_split')
|
| 160 |
+
parser.add_argument('--train_folds', type=str, default=None)
|
| 161 |
+
parser.add_argument('--val_folds', type=str, default=None)
|
| 162 |
+
parser.add_argument('--test_folds', type=str, default=None)
|
| 163 |
+
parser.add_argument('--gen', action='store_true')
|
| 164 |
+
parser.add_argument('--fold_file', type=str, required=True)
|
| 165 |
+
args = parser.parse_args()
|
| 166 |
+
main(args)
|
environment.reference.yml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: orienter-ape-reference
|
| 2 |
+
channels:
|
| 3 |
+
- pytorch
|
| 4 |
+
- nvidia
|
| 5 |
+
- conda-forge
|
| 6 |
+
dependencies:
|
| 7 |
+
- python=3.10
|
| 8 |
+
- pip
|
| 9 |
+
- pytorch=2.2.0
|
| 10 |
+
- torchvision=0.17.0
|
| 11 |
+
- pytorch-cuda=12.1
|
| 12 |
+
- cuda-version=12.1
|
| 13 |
+
- numpy=1.22.4
|
| 14 |
+
- scipy=1.7.3
|
| 15 |
+
- pillow=12.3.0
|
| 16 |
+
- pip:
|
| 17 |
+
- beautifulsoup4==4.12.3
|
| 18 |
+
- cython==3.0.8
|
| 19 |
+
- einops==0.7.0
|
| 20 |
+
- fairscale==0.4.13
|
| 21 |
+
- fvcore==0.1.5.post20221221
|
| 22 |
+
- iopath==0.1.9
|
| 23 |
+
- lvis==0.5.3
|
| 24 |
+
- omegaconf==2.1.0
|
| 25 |
+
- opencv-python==4.9.0.80
|
| 26 |
+
- pycocotools==2.0.7
|
| 27 |
+
- pyyaml==6.0.1
|
| 28 |
+
- requests==2.31.0
|
| 29 |
+
- timm==0.9.12
|
| 30 |
+
- tqdm==4.66.1
|
| 31 |
+
- transformers==4.37.2
|
| 32 |
+
- xformers==0.0.24
|
| 33 |
+
- git+https://github.com/facebookresearch/detectron2.git@017abbfa5f2c2a2afa045200c2af9ccf2fc6227f
|
| 34 |
+
- git+https://github.com/IDEA-Research/detrex.git@776058ec229be37a5ff2a2b0bb54133bdd5da663
|
| 35 |
+
- git+https://github.com/openai/CLIP.git@d50d76daa670286dd6cacf3bcd80b5e4823fc8e1
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
beautifulsoup4>=4.12,<5
|
| 2 |
+
Pillow>=12.3,<13
|
| 3 |
+
PyYAML>=6,<7
|
| 4 |
+
requests>=2.31,<3
|
| 5 |
+
tqdm>=4.66,<5
|
tests/test_ape_stage.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import sys
|
| 3 |
+
import tempfile
|
| 4 |
+
import unittest
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
| 9 |
+
sys.path.insert(0, str(REPO_ROOT))
|
| 10 |
+
|
| 11 |
+
from approach.ape_stage import extract_image_id, run_ape_stage
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class ApeStageTests(unittest.TestCase):
|
| 15 |
+
def test_extract_image_id_requires_documented_basename_shape(self):
|
| 16 |
+
self.assertEqual(extract_image_id("123_4.jpg"), 123004)
|
| 17 |
+
with self.assertRaises(ValueError):
|
| 18 |
+
extract_image_id("123_4_view_a.png")
|
| 19 |
+
with self.assertRaises(ValueError):
|
| 20 |
+
extract_image_id("123_view_a.png")
|
| 21 |
+
|
| 22 |
+
def test_multiple_records_are_enriched_and_checkpointed_as_valid_json(self):
|
| 23 |
+
calls = []
|
| 24 |
+
|
| 25 |
+
def fake_inference(**kwargs):
|
| 26 |
+
calls.append(kwargs)
|
| 27 |
+
category = kwargs["text_prompt"].split(":", 1)[0]
|
| 28 |
+
return [
|
| 29 |
+
{
|
| 30 |
+
"category_name": category,
|
| 31 |
+
"bbox": [1, 2, 3, 4],
|
| 32 |
+
"score": 0.9,
|
| 33 |
+
}
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
records = [
|
| 37 |
+
{"question_id": 0, "text": {"objects": {"button": "round red"}}},
|
| 38 |
+
{"question_id": 1, "text": {"objects": {"lever": "long silver"}}},
|
| 39 |
+
]
|
| 40 |
+
questions = {
|
| 41 |
+
0: {"image": "123_4.jpg"},
|
| 42 |
+
1: {"image": "456_78.jpg"},
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 46 |
+
root = Path(tmpdir)
|
| 47 |
+
output_path = root / "predictions.json"
|
| 48 |
+
results, errors = run_ape_stage(
|
| 49 |
+
records=records,
|
| 50 |
+
questions=questions,
|
| 51 |
+
images_dir=root / "images",
|
| 52 |
+
output_path=output_path,
|
| 53 |
+
inference=fake_inference,
|
| 54 |
+
inference_kwargs={"confidence_threshold": 0.15},
|
| 55 |
+
resume=False,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
on_disk = json.loads(output_path.read_text())
|
| 59 |
+
|
| 60 |
+
self.assertEqual(errors, [])
|
| 61 |
+
self.assertEqual(results, on_disk)
|
| 62 |
+
self.assertEqual(len(results), 2)
|
| 63 |
+
self.assertEqual(results[0]["image_id"], 123004)
|
| 64 |
+
self.assertEqual(results[0]["category_id"], "button")
|
| 65 |
+
self.assertEqual(results[1]["image_id"], 456078)
|
| 66 |
+
self.assertEqual(results[1]["category_id"], "lever")
|
| 67 |
+
self.assertEqual(calls[0]["confidence_threshold"], 0.15)
|
| 68 |
+
|
| 69 |
+
def test_resume_does_not_duplicate_completed_images(self):
|
| 70 |
+
calls = []
|
| 71 |
+
|
| 72 |
+
def fake_inference(**kwargs):
|
| 73 |
+
calls.append(kwargs)
|
| 74 |
+
return [
|
| 75 |
+
{
|
| 76 |
+
"category_name": "button",
|
| 77 |
+
"bbox": [1, 2, 3, 4],
|
| 78 |
+
"score": 0.9,
|
| 79 |
+
}
|
| 80 |
+
]
|
| 81 |
+
|
| 82 |
+
records = [{"question_id": 0, "text": {"objects": {"button": "red"}}}]
|
| 83 |
+
questions = {0: {"image": "123_4.jpg"}}
|
| 84 |
+
|
| 85 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 86 |
+
root = Path(tmpdir)
|
| 87 |
+
output_path = root / "predictions.json"
|
| 88 |
+
first, _ = run_ape_stage(
|
| 89 |
+
records,
|
| 90 |
+
questions,
|
| 91 |
+
root,
|
| 92 |
+
output_path,
|
| 93 |
+
fake_inference,
|
| 94 |
+
resume=False,
|
| 95 |
+
)
|
| 96 |
+
second, _ = run_ape_stage(
|
| 97 |
+
records,
|
| 98 |
+
questions,
|
| 99 |
+
root,
|
| 100 |
+
output_path,
|
| 101 |
+
fake_inference,
|
| 102 |
+
resume=True,
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
self.assertEqual(first, second)
|
| 106 |
+
self.assertEqual(len(calls), 1)
|
| 107 |
+
|
| 108 |
+
def test_resume_tracks_completed_images_with_zero_detections(self):
|
| 109 |
+
calls = []
|
| 110 |
+
|
| 111 |
+
def empty_inference(**kwargs):
|
| 112 |
+
calls.append(kwargs)
|
| 113 |
+
return []
|
| 114 |
+
|
| 115 |
+
records = [{"question_id": "0", "text": {"objects": {"button": "red"}}}]
|
| 116 |
+
questions = {0: {"image": "123_4.jpg"}}
|
| 117 |
+
|
| 118 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 119 |
+
root = Path(tmpdir)
|
| 120 |
+
output_path = root / "predictions.json"
|
| 121 |
+
first, _ = run_ape_stage(
|
| 122 |
+
records,
|
| 123 |
+
questions,
|
| 124 |
+
root,
|
| 125 |
+
output_path,
|
| 126 |
+
empty_inference,
|
| 127 |
+
resume=False,
|
| 128 |
+
)
|
| 129 |
+
second, _ = run_ape_stage(
|
| 130 |
+
records,
|
| 131 |
+
questions,
|
| 132 |
+
root,
|
| 133 |
+
output_path,
|
| 134 |
+
empty_inference,
|
| 135 |
+
resume=True,
|
| 136 |
+
)
|
| 137 |
+
|
| 138 |
+
progress = json.loads((root / "predictions.progress.json").read_text())
|
| 139 |
+
|
| 140 |
+
self.assertEqual(first, [])
|
| 141 |
+
self.assertEqual(second, [])
|
| 142 |
+
self.assertEqual(calls, [calls[0]])
|
| 143 |
+
self.assertEqual(progress["completed_image_ids"], [123004])
|
| 144 |
+
|
| 145 |
+
def test_errors_are_recorded_without_corrupting_predictions(self):
|
| 146 |
+
def failing_inference(**kwargs):
|
| 147 |
+
raise RuntimeError("synthetic detector failure")
|
| 148 |
+
|
| 149 |
+
records = [{"question_id": 0, "text": {"objects": {"button": "red"}}}]
|
| 150 |
+
questions = {0: {"image": "123_4.jpg"}}
|
| 151 |
+
|
| 152 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 153 |
+
root = Path(tmpdir)
|
| 154 |
+
output_path = root / "predictions.json"
|
| 155 |
+
error_path = root / "errors.json"
|
| 156 |
+
results, errors = run_ape_stage(
|
| 157 |
+
records,
|
| 158 |
+
questions,
|
| 159 |
+
root,
|
| 160 |
+
output_path,
|
| 161 |
+
failing_inference,
|
| 162 |
+
error_path=error_path,
|
| 163 |
+
resume=False,
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
self.assertEqual(json.loads(output_path.read_text()), [])
|
| 167 |
+
self.assertEqual(json.loads(error_path.read_text()), errors)
|
| 168 |
+
|
| 169 |
+
self.assertEqual(results, [])
|
| 170 |
+
self.assertEqual(errors[0]["question_id"], 0)
|
| 171 |
+
self.assertEqual(errors[0]["error_type"], "RuntimeError")
|
| 172 |
+
self.assertNotIn("traceback", errors[0])
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
if __name__ == "__main__":
|
| 176 |
+
unittest.main()
|
tests/test_app_metadata_cache.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import tempfile
|
| 3 |
+
import unittest
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from approach.vlm.gpt4v.gpt4v import get_steam_app_data, load_app_metadata_cache
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class AppMetadataCacheTests(unittest.TestCase):
|
| 10 |
+
def test_json_cache_supplies_app_context(self):
|
| 11 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 12 |
+
cache_path = Path(tmpdir) / "app_metadata.json"
|
| 13 |
+
cache_path.write_text(
|
| 14 |
+
json.dumps(
|
| 15 |
+
{
|
| 16 |
+
"123": {
|
| 17 |
+
"app_name": "Pinned App",
|
| 18 |
+
"app_description": "Pinned description",
|
| 19 |
+
}
|
| 20 |
+
}
|
| 21 |
+
),
|
| 22 |
+
encoding="utf-8",
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
cache = load_app_metadata_cache(cache_path)
|
| 26 |
+
|
| 27 |
+
self.assertEqual(
|
| 28 |
+
get_steam_app_data("123", "123_4.jpg", metadata_cache=cache),
|
| 29 |
+
("Pinned App", "Pinned description"),
|
| 30 |
+
)
|
| 31 |
+
|
| 32 |
+
def test_cache_missing_app_id_fails_closed(self):
|
| 33 |
+
with self.assertRaises(KeyError):
|
| 34 |
+
get_steam_app_data("999", "999_1.jpg", metadata_cache={})
|
| 35 |
+
|
| 36 |
+
def test_jsonl_cache_is_keyed_by_app_id(self):
|
| 37 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 38 |
+
cache_path = Path(tmpdir) / "app_metadata.jsonl"
|
| 39 |
+
cache_path.write_text(
|
| 40 |
+
json.dumps(
|
| 41 |
+
{
|
| 42 |
+
"app_id": 456,
|
| 43 |
+
"name": "JSONL App",
|
| 44 |
+
"description": "JSONL description",
|
| 45 |
+
}
|
| 46 |
+
)
|
| 47 |
+
+ "\n",
|
| 48 |
+
encoding="utf-8",
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
cache = load_app_metadata_cache(cache_path)
|
| 52 |
+
|
| 53 |
+
self.assertEqual(
|
| 54 |
+
get_steam_app_data("456", "456_7.jpg", metadata_cache=cache),
|
| 55 |
+
("JSONL App", "JSONL description"),
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
def test_duplicate_jsonl_app_id_is_rejected(self):
|
| 59 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 60 |
+
cache_path = Path(tmpdir) / "app_metadata.jsonl"
|
| 61 |
+
cache_path.write_text(
|
| 62 |
+
"\n".join(
|
| 63 |
+
[
|
| 64 |
+
json.dumps(
|
| 65 |
+
{
|
| 66 |
+
"app_id": 123,
|
| 67 |
+
"app_name": "First",
|
| 68 |
+
"app_description": "Description",
|
| 69 |
+
}
|
| 70 |
+
),
|
| 71 |
+
json.dumps(
|
| 72 |
+
{
|
| 73 |
+
"app_id": "123",
|
| 74 |
+
"app_name": "Second",
|
| 75 |
+
"app_description": "Description",
|
| 76 |
+
}
|
| 77 |
+
),
|
| 78 |
+
]
|
| 79 |
+
)
|
| 80 |
+
+ "\n",
|
| 81 |
+
encoding="utf-8",
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
with self.assertRaises(ValueError):
|
| 85 |
+
load_app_metadata_cache(cache_path)
|
| 86 |
+
|
| 87 |
+
def test_duplicate_json_object_key_is_rejected(self):
|
| 88 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 89 |
+
cache_path = Path(tmpdir) / "app_metadata.json"
|
| 90 |
+
cache_path.write_text(
|
| 91 |
+
'{"123":{"app_name":"First","app_description":"One"},'
|
| 92 |
+
'"123":{"app_name":"Second","app_description":"Two"}}',
|
| 93 |
+
encoding="utf-8",
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
with self.assertRaises(ValueError):
|
| 97 |
+
load_app_metadata_cache(cache_path)
|
| 98 |
+
|
| 99 |
+
def test_mapping_key_and_record_app_id_must_match(self):
|
| 100 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 101 |
+
cache_path = Path(tmpdir) / "app_metadata.json"
|
| 102 |
+
cache_path.write_text(
|
| 103 |
+
json.dumps(
|
| 104 |
+
{
|
| 105 |
+
"123": {
|
| 106 |
+
"app_id": "456",
|
| 107 |
+
"app_name": "Mismatch",
|
| 108 |
+
"app_description": "Description",
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
),
|
| 112 |
+
encoding="utf-8",
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
with self.assertRaises(ValueError):
|
| 116 |
+
load_app_metadata_cache(cache_path)
|
| 117 |
+
|
| 118 |
+
def test_missing_cache_fields_fail_closed(self):
|
| 119 |
+
with self.assertRaises(ValueError):
|
| 120 |
+
get_steam_app_data("123", "123_4.jpg", metadata_cache={"123": {"app_name": "App"}})
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
if __name__ == "__main__":
|
| 124 |
+
unittest.main()
|
tests/test_cli_entrypoints.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import subprocess
|
| 3 |
+
import sys
|
| 4 |
+
import tempfile
|
| 5 |
+
import unittest
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
ROOT = Path(__file__).resolve().parents[1]
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class CliEntrypointTests(unittest.TestCase):
|
| 13 |
+
def test_run_vlm_script_path_supports_help(self):
|
| 14 |
+
result = subprocess.run(
|
| 15 |
+
[sys.executable, str(ROOT / "approach" / "run_vlm.py"), "--help"],
|
| 16 |
+
cwd=ROOT,
|
| 17 |
+
stdout=subprocess.PIPE,
|
| 18 |
+
stderr=subprocess.PIPE,
|
| 19 |
+
text=True,
|
| 20 |
+
check=False,
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 24 |
+
self.assertIn("--questions", result.stdout)
|
| 25 |
+
|
| 26 |
+
def test_run_vlm_returns_nonzero_when_a_record_fails(self):
|
| 27 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 28 |
+
root = Path(tmpdir)
|
| 29 |
+
questions = root / "questions.jsonl"
|
| 30 |
+
questions.write_text(
|
| 31 |
+
json.dumps(
|
| 32 |
+
{
|
| 33 |
+
"question_id": 0,
|
| 34 |
+
"image": "123_4.jpg",
|
| 35 |
+
"image_id": 123004,
|
| 36 |
+
"text": "mine",
|
| 37 |
+
}
|
| 38 |
+
)
|
| 39 |
+
+ "\n",
|
| 40 |
+
encoding="utf-8",
|
| 41 |
+
)
|
| 42 |
+
metadata = root / "metadata.json"
|
| 43 |
+
metadata.write_text(
|
| 44 |
+
json.dumps(
|
| 45 |
+
{
|
| 46 |
+
"456": {
|
| 47 |
+
"app_name": "Different app",
|
| 48 |
+
"app_description": "Does not cover the selected screenshot.",
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
),
|
| 52 |
+
encoding="utf-8",
|
| 53 |
+
)
|
| 54 |
+
result = subprocess.run(
|
| 55 |
+
[
|
| 56 |
+
sys.executable,
|
| 57 |
+
"-B",
|
| 58 |
+
str(ROOT / "approach" / "run_vlm.py"),
|
| 59 |
+
"--questions",
|
| 60 |
+
str(questions),
|
| 61 |
+
"--images-dir",
|
| 62 |
+
str(root / "images"),
|
| 63 |
+
"--output",
|
| 64 |
+
str(root / "answers.jsonl"),
|
| 65 |
+
"--app-metadata-cache",
|
| 66 |
+
str(metadata),
|
| 67 |
+
],
|
| 68 |
+
cwd=ROOT,
|
| 69 |
+
stdout=subprocess.PIPE,
|
| 70 |
+
stderr=subprocess.PIPE,
|
| 71 |
+
text=True,
|
| 72 |
+
check=False,
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
|
| 76 |
+
self.assertEqual(json.loads(result.stdout)["errors"], 1)
|
| 77 |
+
|
| 78 |
+
def test_run_ape_script_path_supports_help(self):
|
| 79 |
+
result = subprocess.run(
|
| 80 |
+
[sys.executable, str(ROOT / "approach" / "run_ape.py"), "--help"],
|
| 81 |
+
cwd=ROOT,
|
| 82 |
+
stdout=subprocess.PIPE,
|
| 83 |
+
stderr=subprocess.PIPE,
|
| 84 |
+
text=True,
|
| 85 |
+
check=False,
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 89 |
+
self.assertIn("--candidates", result.stdout)
|
| 90 |
+
|
| 91 |
+
def test_generate_questions_script_path_supports_help(self):
|
| 92 |
+
result = subprocess.run(
|
| 93 |
+
[sys.executable, str(ROOT / "scripts" / "generate_questions.py"), "--help"],
|
| 94 |
+
cwd=ROOT,
|
| 95 |
+
stdout=subprocess.PIPE,
|
| 96 |
+
stderr=subprocess.PIPE,
|
| 97 |
+
text=True,
|
| 98 |
+
check=False,
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 102 |
+
self.assertIn("--images-dir", result.stdout)
|
| 103 |
+
|
| 104 |
+
def test_environment_check_script_path_supports_help(self):
|
| 105 |
+
result = subprocess.run(
|
| 106 |
+
[sys.executable, str(ROOT / "scripts" / "check_environment.py"), "--help"],
|
| 107 |
+
cwd=ROOT,
|
| 108 |
+
stdout=subprocess.PIPE,
|
| 109 |
+
stderr=subprocess.PIPE,
|
| 110 |
+
text=True,
|
| 111 |
+
check=False,
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 115 |
+
self.assertIn("--allow-no-cuda", result.stdout)
|
| 116 |
+
|
| 117 |
+
def test_release_test_runner_is_present(self):
|
| 118 |
+
self.assertTrue((ROOT / "scripts" / "run_tests.py").is_file())
|
| 119 |
+
|
| 120 |
+
def test_offline_control_path_smoke(self):
|
| 121 |
+
result = subprocess.run(
|
| 122 |
+
[sys.executable, str(ROOT / "scripts" / "smoke_control_path.py")],
|
| 123 |
+
cwd=ROOT,
|
| 124 |
+
stdout=subprocess.PIPE,
|
| 125 |
+
stderr=subprocess.PIPE,
|
| 126 |
+
text=True,
|
| 127 |
+
check=False,
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 131 |
+
self.assertIn('"status": "ok"', result.stdout)
|
| 132 |
+
|
| 133 |
+
def test_ape_build_script_has_valid_shell_syntax(self):
|
| 134 |
+
result = subprocess.run(
|
| 135 |
+
["bash", "-n", str(ROOT / "scripts" / "build_ape_extension.sh")],
|
| 136 |
+
cwd=ROOT,
|
| 137 |
+
stdout=subprocess.PIPE,
|
| 138 |
+
stderr=subprocess.PIPE,
|
| 139 |
+
text=True,
|
| 140 |
+
check=False,
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
self.assertEqual(result.returncode, 0, result.stderr)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
if __name__ == "__main__":
|
| 147 |
+
unittest.main()
|
tests/test_config_providers.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import tempfile
|
| 4 |
+
import unittest
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
from unittest import mock
|
| 7 |
+
|
| 8 |
+
from approach.config import (
|
| 9 |
+
OPENROUTER_BASE_URL,
|
| 10 |
+
get_model_profile,
|
| 11 |
+
sanitize_manifest,
|
| 12 |
+
validate_model_available,
|
| 13 |
+
)
|
| 14 |
+
from approach.providers import EncodedImage, OpenAICompatibleChatClient, make_multimodal_content
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class ConfigProviderTests(unittest.TestCase):
|
| 18 |
+
def test_default_profile_uses_openrouter(self):
|
| 19 |
+
profile = get_model_profile("default")
|
| 20 |
+
self.assertEqual(profile.provider, "openrouter")
|
| 21 |
+
self.assertEqual(profile.model, "openai/gpt-5.6-sol")
|
| 22 |
+
self.assertEqual(profile.base_url, OPENROUTER_BASE_URL)
|
| 23 |
+
self.assertEqual(profile.api_key_env, "OPENROUTER_API_KEY")
|
| 24 |
+
|
| 25 |
+
def test_aliases_keep_paper_profiles_available(self):
|
| 26 |
+
self.assertEqual(get_model_profile("gpt4v").model, "openai/gpt-4o-2024-08-06")
|
| 27 |
+
self.assertEqual(get_model_profile("claude35sonnet").model, "anthropic/claude-3.5-sonnet")
|
| 28 |
+
self.assertEqual(get_model_profile("gemini15pro").model, "google/gemini-pro-1.5")
|
| 29 |
+
|
| 30 |
+
def test_json_config_override_without_yaml_dependency(self):
|
| 31 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 32 |
+
path = Path(tmpdir) / "profiles.json"
|
| 33 |
+
path.write_text(
|
| 34 |
+
json.dumps(
|
| 35 |
+
{
|
| 36 |
+
"profiles": {
|
| 37 |
+
"default": {
|
| 38 |
+
"provider": "openrouter",
|
| 39 |
+
"model": "custom/model",
|
| 40 |
+
"base_url": OPENROUTER_BASE_URL,
|
| 41 |
+
"api_key_env": "CUSTOM_KEY",
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
)
|
| 46 |
+
)
|
| 47 |
+
profile = get_model_profile("default", str(path))
|
| 48 |
+
self.assertEqual(profile.model, "custom/model")
|
| 49 |
+
self.assertEqual(profile.api_key_env, "CUSTOM_KEY")
|
| 50 |
+
|
| 51 |
+
def test_model_catalog_validation_is_dependency_injected(self):
|
| 52 |
+
profile = get_model_profile("default")
|
| 53 |
+
self.assertTrue(validate_model_available(profile, [{"id": "openai/gpt-5.6-sol"}]))
|
| 54 |
+
with self.assertRaises(ValueError):
|
| 55 |
+
validate_model_available(profile, [{"id": "other/model"}])
|
| 56 |
+
|
| 57 |
+
def test_multimodal_content_supports_multiple_images(self):
|
| 58 |
+
content = make_multimodal_content(
|
| 59 |
+
"check",
|
| 60 |
+
["aaa", EncodedImage(data="bbb", media_type="image/png")],
|
| 61 |
+
)
|
| 62 |
+
self.assertEqual(content[0], {"type": "text", "text": "check"})
|
| 63 |
+
self.assertEqual(len(content), 3)
|
| 64 |
+
self.assertEqual(content[1]["image_url"]["url"], "data:image/jpeg;base64,aaa")
|
| 65 |
+
self.assertEqual(content[2]["image_url"]["url"], "data:image/png;base64,bbb")
|
| 66 |
+
|
| 67 |
+
def test_client_posts_openai_compatible_payload(self):
|
| 68 |
+
profile = get_model_profile("default")
|
| 69 |
+
response = mock.Mock()
|
| 70 |
+
response.json.return_value = {
|
| 71 |
+
"model": profile.model,
|
| 72 |
+
"choices": [{"message": {"content": "{\"ok\": true}"}}],
|
| 73 |
+
}
|
| 74 |
+
post = mock.Mock(return_value=response)
|
| 75 |
+
|
| 76 |
+
with mock.patch.dict(os.environ, {"OPENROUTER_API_KEY": "test-token"}):
|
| 77 |
+
client = OpenAICompatibleChatClient(profile, post=post)
|
| 78 |
+
payload = client.build_payload(
|
| 79 |
+
"verify",
|
| 80 |
+
["image-a", "image-b"],
|
| 81 |
+
response_format={"type": "json_schema", "json_schema": {"name": "x"}},
|
| 82 |
+
)
|
| 83 |
+
result = client.complete_json(
|
| 84 |
+
"verify",
|
| 85 |
+
["image-a", "image-b"],
|
| 86 |
+
response_format={"type": "json_schema", "json_schema": {"name": "x"}},
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
self.assertEqual(payload["model"], "openai/gpt-5.6-sol")
|
| 90 |
+
self.assertEqual(result, {"ok": True})
|
| 91 |
+
self.assertEqual(
|
| 92 |
+
post.call_args.args[0],
|
| 93 |
+
"https://openrouter.ai/api/v1/chat/completions",
|
| 94 |
+
)
|
| 95 |
+
self.assertEqual(post.call_args.kwargs["headers"]["Authorization"], "Bearer test-token")
|
| 96 |
+
self.assertIn("response_format", post.call_args.kwargs["json"])
|
| 97 |
+
|
| 98 |
+
def test_client_supports_multi_turn_json_requests(self):
|
| 99 |
+
profile = get_model_profile("paper_claude")
|
| 100 |
+
response = mock.Mock()
|
| 101 |
+
response.json.return_value = {
|
| 102 |
+
"choices": [{"message": {"content": '{"objects": ["button"]}'}}]
|
| 103 |
+
}
|
| 104 |
+
post = mock.Mock(return_value=response)
|
| 105 |
+
messages = [
|
| 106 |
+
{"role": "user", "content": "list objects"},
|
| 107 |
+
{"role": "assistant", "content": '{"objects": ["button"]}'},
|
| 108 |
+
{"role": "user", "content": "filter interactable objects"},
|
| 109 |
+
]
|
| 110 |
+
|
| 111 |
+
with mock.patch.dict(os.environ, {"OPENROUTER_API_KEY": "test-token"}):
|
| 112 |
+
result = OpenAICompatibleChatClient(profile, post=post).complete_json_messages(
|
| 113 |
+
messages
|
| 114 |
+
)
|
| 115 |
+
|
| 116 |
+
self.assertEqual(result, {"objects": ["button"]})
|
| 117 |
+
self.assertEqual(post.call_args.kwargs["json"]["messages"], messages)
|
| 118 |
+
|
| 119 |
+
def test_manifest_never_records_api_key_value(self):
|
| 120 |
+
profile = get_model_profile("default")
|
| 121 |
+
manifest = sanitize_manifest(
|
| 122 |
+
profile,
|
| 123 |
+
{"model": "openai/gpt-5.6-sol"},
|
| 124 |
+
{"model": "openai/gpt-5.6-sol", "usage": {"total_tokens": 12}},
|
| 125 |
+
)
|
| 126 |
+
self.assertEqual(manifest["api_key_env"], "OPENROUTER_API_KEY")
|
| 127 |
+
self.assertNotIn("sk-", json.dumps(manifest))
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
if __name__ == "__main__":
|
| 131 |
+
unittest.main()
|
tests/test_evaluate_temp_paths.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import os
|
| 3 |
+
import subprocess
|
| 4 |
+
import sys
|
| 5 |
+
import tempfile
|
| 6 |
+
import unittest
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
| 11 |
+
EVAL_DIR = REPO_ROOT / "evaluation"
|
| 12 |
+
|
| 13 |
+
PYCOCOTOOLS_STUB = """
|
| 14 |
+
import sys
|
| 15 |
+
import types
|
| 16 |
+
pycocotools = types.ModuleType("pycocotools")
|
| 17 |
+
mask = types.ModuleType("pycocotools._mask")
|
| 18 |
+
def _not_used(*args, **kwargs):
|
| 19 |
+
raise AssertionError("pycocotools._mask stub should not be exercised in this test")
|
| 20 |
+
for _name in ["iou", "merge", "frPyObjects", "encode", "decode", "area", "toBbox"]:
|
| 21 |
+
setattr(mask, _name, _not_used)
|
| 22 |
+
pycocotools._mask = mask
|
| 23 |
+
sys.modules["pycocotools"] = pycocotools
|
| 24 |
+
sys.modules["pycocotools._mask"] = mask
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def run_eval_import(script: str, env_overrides: dict[str, str]) -> str:
|
| 29 |
+
env = os.environ.copy()
|
| 30 |
+
env.update(env_overrides)
|
| 31 |
+
env.pop("ORIENTER_TMP_ANN_PATH", None)
|
| 32 |
+
env.pop("ORIENTER_GT_CAT_MATCH_PATH", None)
|
| 33 |
+
pythonpath = [str(EVAL_DIR), str(REPO_ROOT)]
|
| 34 |
+
if env.get("PYTHONPATH"):
|
| 35 |
+
pythonpath.append(env["PYTHONPATH"])
|
| 36 |
+
env["PYTHONPATH"] = os.pathsep.join(pythonpath)
|
| 37 |
+
return subprocess.check_output(
|
| 38 |
+
[sys.executable, "-c", PYCOCOTOOLS_STUB + script],
|
| 39 |
+
cwd=EVAL_DIR,
|
| 40 |
+
env=env,
|
| 41 |
+
text=True,
|
| 42 |
+
).strip()
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class EvaluationTempPathTests(unittest.TestCase):
|
| 46 |
+
def test_default_temp_paths_are_process_isolated(self):
|
| 47 |
+
script = """
|
| 48 |
+
import json
|
| 49 |
+
import evaluate_coco
|
| 50 |
+
import pycocotools_ovod.semantic_matching as semantic_matching
|
| 51 |
+
print(json.dumps({
|
| 52 |
+
"tmp_ann": evaluate_coco.tmp_ann_path,
|
| 53 |
+
"gt_match": semantic_matching.gt_cat_match_path,
|
| 54 |
+
}))
|
| 55 |
+
"""
|
| 56 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 57 |
+
env = {"ORIENTER_EVALUATION_TMPDIR": tmpdir}
|
| 58 |
+
first = json.loads(run_eval_import(script, env))
|
| 59 |
+
second = json.loads(run_eval_import(script, env))
|
| 60 |
+
|
| 61 |
+
self.assertNotEqual(first["tmp_ann"], second["tmp_ann"])
|
| 62 |
+
self.assertNotEqual(first["gt_match"], second["gt_match"])
|
| 63 |
+
self.assertIn("tmp_ann.", Path(first["tmp_ann"]).name)
|
| 64 |
+
self.assertIn("tmp_gt_cat_match.", Path(first["gt_match"]).name)
|
| 65 |
+
|
| 66 |
+
def test_cleanup_removes_configured_temp_files_and_resets_match_cache(self):
|
| 67 |
+
script = """
|
| 68 |
+
import json
|
| 69 |
+
from pathlib import Path
|
| 70 |
+
import evaluate_coco
|
| 71 |
+
import pycocotools_ovod.semantic_matching as semantic_matching
|
| 72 |
+
|
| 73 |
+
semantic_matching.gt_cat_match_dict = {"Button": ["Button"]}
|
| 74 |
+
for path in [evaluate_coco.tmp_ann_path, semantic_matching.gt_cat_match_path]:
|
| 75 |
+
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
| 76 |
+
Path(path).write_text("{}")
|
| 77 |
+
|
| 78 |
+
evaluate_coco.cleanup_temp_outputs()
|
| 79 |
+
print(json.dumps({
|
| 80 |
+
"tmp_ann_exists": Path(evaluate_coco.tmp_ann_path).exists(),
|
| 81 |
+
"gt_match_exists": Path(semantic_matching.gt_cat_match_path).exists(),
|
| 82 |
+
"match_cache": semantic_matching.gt_cat_match_dict,
|
| 83 |
+
}))
|
| 84 |
+
"""
|
| 85 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 86 |
+
tmp = Path(tmpdir)
|
| 87 |
+
env = os.environ.copy()
|
| 88 |
+
env.update(
|
| 89 |
+
{
|
| 90 |
+
"ORIENTER_TMP_ANN_PATH": str(tmp / "custom_tmp_ann.json"),
|
| 91 |
+
"ORIENTER_GT_CAT_MATCH_PATH": str(tmp / "custom_gt_match.json"),
|
| 92 |
+
"PYTHONPATH": os.pathsep.join([str(EVAL_DIR), str(REPO_ROOT), env.get("PYTHONPATH", "")]),
|
| 93 |
+
}
|
| 94 |
+
)
|
| 95 |
+
result = subprocess.check_output(
|
| 96 |
+
[sys.executable, "-c", PYCOCOTOOLS_STUB + script],
|
| 97 |
+
cwd=EVAL_DIR,
|
| 98 |
+
env=env,
|
| 99 |
+
text=True,
|
| 100 |
+
).strip()
|
| 101 |
+
|
| 102 |
+
state = json.loads(result)
|
| 103 |
+
self.assertFalse(state["tmp_ann_exists"])
|
| 104 |
+
self.assertFalse(state["gt_match_exists"])
|
| 105 |
+
self.assertIsNone(state["match_cache"])
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
if __name__ == "__main__":
|
| 109 |
+
unittest.main()
|
tests/test_evaluation_config.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import importlib.util
|
| 2 |
+
import unittest
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def load_module(path):
|
| 7 |
+
spec = importlib.util.spec_from_file_location(path.stem, path)
|
| 8 |
+
module = importlib.util.module_from_spec(spec)
|
| 9 |
+
spec.loader.exec_module(module)
|
| 10 |
+
return module
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class EvaluationConfigTest(unittest.TestCase):
|
| 14 |
+
def test_context_baseline_lists_are_deduplicated(self):
|
| 15 |
+
module = load_module(Path("evaluation/context_eval_baselines.py"))
|
| 16 |
+
self.assertEqual(len(module.METHODS), len(set(module.METHODS)))
|
| 17 |
+
self.assertEqual(len(module.LLM_METHODS), len(set(module.LLM_METHODS)))
|
| 18 |
+
|
| 19 |
+
def test_to_pred_requires_explicit_task(self):
|
| 20 |
+
module = load_module(Path("evaluation/tools/to_pred.py"))
|
| 21 |
+
self.assertIn("interactable", module.TASKS)
|
| 22 |
+
self.assertIn("all", module.build_parser()._option_string_actions["--task"].choices)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
if __name__ == "__main__":
|
| 26 |
+
unittest.main()
|
tests/test_evaluation_tools.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import sys
|
| 3 |
+
import tempfile
|
| 4 |
+
import unittest
|
| 5 |
+
from itertools import chain, repeat
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from unittest import mock
|
| 8 |
+
|
| 9 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
| 10 |
+
|
| 11 |
+
from evaluation import context_eval_baselines
|
| 12 |
+
from evaluation.tools import to_pred
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class ToPredTests(unittest.TestCase):
|
| 16 |
+
def test_task_is_required(self):
|
| 17 |
+
parser = to_pred.build_parser()
|
| 18 |
+
|
| 19 |
+
with self.assertRaises(SystemExit):
|
| 20 |
+
parser.parse_args(["--input", "pred.json", "--output", "out.json"])
|
| 21 |
+
|
| 22 |
+
def test_convert_old_predictions_to_semantics_fields(self):
|
| 23 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 24 |
+
tmp = Path(tmpdir)
|
| 25 |
+
pred_path = tmp / "pred.json"
|
| 26 |
+
question_path = tmp / "questions.jsonl"
|
| 27 |
+
output_path = tmp / "out.json"
|
| 28 |
+
|
| 29 |
+
pred_path.write_text(
|
| 30 |
+
json.dumps(
|
| 31 |
+
{
|
| 32 |
+
"q1": {
|
| 33 |
+
"oovd_result": {
|
| 34 |
+
"button": [
|
| 35 |
+
{
|
| 36 |
+
"bbox_pixels": [1, 2, 3, 4],
|
| 37 |
+
"probability": 0.7,
|
| 38 |
+
}
|
| 39 |
+
]
|
| 40 |
+
}
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
)
|
| 44 |
+
)
|
| 45 |
+
question_path.write_text(
|
| 46 |
+
json.dumps({"question_id": "q1", "image": "1026760_11.jpg"}) + "\n"
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
to_pred.main(
|
| 50 |
+
[
|
| 51 |
+
"--task",
|
| 52 |
+
"semantics",
|
| 53 |
+
"--input",
|
| 54 |
+
str(pred_path),
|
| 55 |
+
"--questions",
|
| 56 |
+
str(question_path),
|
| 57 |
+
"--output",
|
| 58 |
+
str(output_path),
|
| 59 |
+
]
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
converted = json.loads(output_path.read_text())
|
| 63 |
+
self.assertEqual(
|
| 64 |
+
converted,
|
| 65 |
+
[
|
| 66 |
+
{
|
| 67 |
+
"image_id": 1026760011,
|
| 68 |
+
"category_id": "button",
|
| 69 |
+
"bbox": [1, 2, 3, 4],
|
| 70 |
+
"score": 0.7,
|
| 71 |
+
}
|
| 72 |
+
],
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
def test_all_outputs_do_not_overwrite(self):
|
| 76 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 77 |
+
tmp = Path(tmpdir)
|
| 78 |
+
pred_path = tmp / "pred.json"
|
| 79 |
+
output_path = tmp / "converted.json"
|
| 80 |
+
|
| 81 |
+
pred_path.write_text(
|
| 82 |
+
json.dumps(
|
| 83 |
+
[
|
| 84 |
+
{
|
| 85 |
+
"image_id": 1,
|
| 86 |
+
"category_id": "trigger",
|
| 87 |
+
"bbox": [1, 2, 3, 4],
|
| 88 |
+
"score": 0.5,
|
| 89 |
+
}
|
| 90 |
+
]
|
| 91 |
+
)
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
to_pred.main(
|
| 95 |
+
[
|
| 96 |
+
"--task",
|
| 97 |
+
"all",
|
| 98 |
+
"--input",
|
| 99 |
+
str(pred_path),
|
| 100 |
+
"--output",
|
| 101 |
+
str(output_path),
|
| 102 |
+
]
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
self.assertFalse(output_path.exists())
|
| 106 |
+
for task in to_pred.TASKS:
|
| 107 |
+
self.assertTrue((tmp / f"converted_{task}.json").exists())
|
| 108 |
+
|
| 109 |
+
interactable = json.loads((tmp / "converted_interactable.json").read_text())
|
| 110 |
+
interaction = json.loads((tmp / "converted_interaction.json").read_text())
|
| 111 |
+
semantics = json.loads((tmp / "converted_semantics.json").read_text())
|
| 112 |
+
self.assertEqual(interactable[0]["category_id"], 1)
|
| 113 |
+
self.assertEqual(interaction[0]["category_id"], "trigger")
|
| 114 |
+
self.assertEqual(semantics[0]["category_id"], "trigger")
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
class ContextEvalBaselineTests(unittest.TestCase):
|
| 118 |
+
def test_validate_unique_methods_rejects_duplicates(self):
|
| 119 |
+
with self.assertRaises(ValueError):
|
| 120 |
+
context_eval_baselines.validate_unique_methods(["Seed-E2E", "Seed-E2E"])
|
| 121 |
+
|
| 122 |
+
def test_main_uses_subprocess_check_true(self):
|
| 123 |
+
args = context_eval_baselines.build_parser().parse_args([])
|
| 124 |
+
methods = ["CenterNet2"]
|
| 125 |
+
|
| 126 |
+
with mock.patch.object(
|
| 127 |
+
context_eval_baselines, "METHODS", methods
|
| 128 |
+
), mock.patch.object(
|
| 129 |
+
context_eval_baselines, "LLM_METHODS", []
|
| 130 |
+
), mock.patch.object(
|
| 131 |
+
context_eval_baselines.os.path,
|
| 132 |
+
"exists",
|
| 133 |
+
side_effect=chain([True, False], repeat(False)),
|
| 134 |
+
), mock.patch.object(
|
| 135 |
+
context_eval_baselines.subprocess, "run"
|
| 136 |
+
) as run:
|
| 137 |
+
context_eval_baselines.main(args)
|
| 138 |
+
|
| 139 |
+
self.assertTrue(run.called)
|
| 140 |
+
self.assertTrue(run.call_args.kwargs["check"])
|
| 141 |
+
self.assertIsInstance(run.call_args.args[0], list)
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
if __name__ == "__main__":
|
| 145 |
+
unittest.main()
|
tests/test_merge_predictions.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import tempfile
|
| 3 |
+
import unittest
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from evaluation.tools.merge_predictions import merge_prediction_shards
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class MergePredictionTests(unittest.TestCase):
|
| 10 |
+
def test_merges_disjoint_shards_and_validates_question_coverage(self):
|
| 11 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 12 |
+
root = Path(tmpdir)
|
| 13 |
+
first = root / "pred.shard00-of-02.json"
|
| 14 |
+
second = root / "pred.shard01-of-02.json"
|
| 15 |
+
output = root / "merged.json"
|
| 16 |
+
first.write_text(json.dumps([{"image_id": 1001, "category_id": "button", "bbox": [1, 2, 3, 4], "score": 0.9}]))
|
| 17 |
+
second.write_text(json.dumps([{"image_id": 1002, "category_id": "lever", "bbox": [4, 3, 2, 1], "score": 0.8}]))
|
| 18 |
+
first.with_suffix(".progress.json").write_text(json.dumps({"completed_image_ids": [1001]}))
|
| 19 |
+
second.with_suffix(".progress.json").write_text(json.dumps({"completed_image_ids": [1002]}))
|
| 20 |
+
|
| 21 |
+
report = merge_prediction_shards(
|
| 22 |
+
[first, second],
|
| 23 |
+
output,
|
| 24 |
+
expected_image_ids={1001, 1002},
|
| 25 |
+
)
|
| 26 |
+
|
| 27 |
+
self.assertEqual(len(json.loads(output.read_text())), 2)
|
| 28 |
+
self.assertEqual(report["completed_image_ids"], 2)
|
| 29 |
+
self.assertEqual(report["missing_image_ids"], [])
|
| 30 |
+
|
| 31 |
+
def test_rejects_overlapping_shard_progress(self):
|
| 32 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 33 |
+
root = Path(tmpdir)
|
| 34 |
+
paths = [root / "a.json", root / "b.json"]
|
| 35 |
+
for path in paths:
|
| 36 |
+
path.write_text("[]")
|
| 37 |
+
path.with_suffix(".progress.json").write_text(
|
| 38 |
+
json.dumps({"completed_image_ids": [1001]})
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
with self.assertRaisesRegex(ValueError, "overlap"):
|
| 42 |
+
merge_prediction_shards(paths, root / "merged.json")
|
| 43 |
+
|
| 44 |
+
def test_rejects_missing_expected_images(self):
|
| 45 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 46 |
+
root = Path(tmpdir)
|
| 47 |
+
shard = root / "a.json"
|
| 48 |
+
shard.write_text("[]")
|
| 49 |
+
shard.with_suffix(".progress.json").write_text(
|
| 50 |
+
json.dumps({"completed_image_ids": [1001]})
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
with self.assertRaisesRegex(ValueError, "missing"):
|
| 54 |
+
merge_prediction_shards(
|
| 55 |
+
[shard],
|
| 56 |
+
root / "merged.json",
|
| 57 |
+
expected_image_ids={1001, 1002},
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
if __name__ == "__main__":
|
| 62 |
+
unittest.main()
|
tests/test_paired_bootstrap.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import sys
|
| 3 |
+
import tempfile
|
| 4 |
+
import unittest
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
| 8 |
+
|
| 9 |
+
from evaluation.tools import paired_bootstrap
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def _write(path, payload):
|
| 13 |
+
path.write_text(json.dumps(payload), encoding="utf-8")
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class PairedBootstrapTests(unittest.TestCase):
|
| 17 |
+
def _toy_gt(self):
|
| 18 |
+
return {
|
| 19 |
+
"images": [{"id": 1}, {"id": 2}, {"id": 3}],
|
| 20 |
+
"categories": [
|
| 21 |
+
{"id": 1, "name": "Button"},
|
| 22 |
+
{"id": 2, "name": "Sphere"},
|
| 23 |
+
],
|
| 24 |
+
"annotations": [
|
| 25 |
+
{"id": 1, "image_id": 1, "category_id": 1, "bbox": [0, 0, 10, 10]},
|
| 26 |
+
{"id": 2, "image_id": 2, "category_id": 2, "bbox": [0, 0, 10, 10]},
|
| 27 |
+
],
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
def test_greedy_matching_uses_score_order_and_complete_images(self):
|
| 31 |
+
gt = self._toy_gt()
|
| 32 |
+
preds = [
|
| 33 |
+
{"image_id": 1, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 0.9},
|
| 34 |
+
{"image_id": 1, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 0.2},
|
| 35 |
+
{"image_id": 2, "category_id": 2, "bbox": [0, 0, 10, 10], "score": 0.8},
|
| 36 |
+
]
|
| 37 |
+
|
| 38 |
+
prepared_gt = paired_bootstrap.prepare_ground_truth(gt)
|
| 39 |
+
evaluated = paired_bootstrap.evaluate_predictions(
|
| 40 |
+
prepared_gt,
|
| 41 |
+
preds,
|
| 42 |
+
dimension="interactable",
|
| 43 |
+
iou_threshold=0.75,
|
| 44 |
+
score_threshold=0.0,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
self.assertEqual(evaluated.counts["tp"], 2)
|
| 48 |
+
self.assertEqual(evaluated.counts["fp"], 1)
|
| 49 |
+
self.assertEqual(evaluated.counts["fn"], 0)
|
| 50 |
+
self.assertEqual(len(evaluated.per_image), 3)
|
| 51 |
+
self.assertEqual(evaluated.per_image[3].support, 0)
|
| 52 |
+
|
| 53 |
+
def test_auto_threshold_maximizes_micro_f1(self):
|
| 54 |
+
gt = self._toy_gt()
|
| 55 |
+
preds = [
|
| 56 |
+
{"image_id": 1, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 0.4},
|
| 57 |
+
{"image_id": 2, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 0.9},
|
| 58 |
+
{"image_id": 2, "category_id": 2, "bbox": [0, 0, 10, 10], "score": 0.8},
|
| 59 |
+
{"image_id": 3, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 0.4},
|
| 60 |
+
{"image_id": 3, "category_id": 1, "bbox": [1, 1, 10, 10], "score": 0.4},
|
| 61 |
+
{"image_id": 3, "category_id": 2, "bbox": [2, 2, 10, 10], "score": 0.4},
|
| 62 |
+
{"image_id": 3, "category_id": 2, "bbox": [3, 3, 10, 10], "score": 0.4},
|
| 63 |
+
]
|
| 64 |
+
|
| 65 |
+
prepared_gt = paired_bootstrap.prepare_ground_truth(gt)
|
| 66 |
+
threshold, evaluated = paired_bootstrap.select_best_threshold(
|
| 67 |
+
prepared_gt,
|
| 68 |
+
preds,
|
| 69 |
+
dimension="interactable",
|
| 70 |
+
iou_threshold=0.75,
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
self.assertEqual(threshold, 0.8)
|
| 74 |
+
self.assertAlmostEqual(evaluated.metrics["f1"], 0.5)
|
| 75 |
+
|
| 76 |
+
def test_fast_auto_threshold_matches_bruteforce_selection(self):
|
| 77 |
+
gt = self._toy_gt()
|
| 78 |
+
preds = [
|
| 79 |
+
{"image_id": 1, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 0.91},
|
| 80 |
+
{"image_id": 1, "category_id": 1, "bbox": [1, 1, 10, 10], "score": 0.42},
|
| 81 |
+
{"image_id": 2, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 0.88},
|
| 82 |
+
{"image_id": 2, "category_id": 2, "bbox": [0, 0, 10, 10], "score": 0.73},
|
| 83 |
+
{"image_id": 3, "category_id": 2, "bbox": [0, 0, 10, 10], "score": 0.11},
|
| 84 |
+
{"image_id": 99, "category_id": 2, "bbox": [0, 0, 10, 10], "score": 0.72},
|
| 85 |
+
]
|
| 86 |
+
prepared_gt = paired_bootstrap.prepare_ground_truth(gt)
|
| 87 |
+
|
| 88 |
+
fast_threshold = paired_bootstrap.select_best_threshold_fast(
|
| 89 |
+
prepared_gt,
|
| 90 |
+
preds,
|
| 91 |
+
dimension="interactable",
|
| 92 |
+
iou_threshold=0.75,
|
| 93 |
+
)
|
| 94 |
+
brute_threshold, brute_result = self._select_best_threshold_bruteforce(prepared_gt, preds)
|
| 95 |
+
|
| 96 |
+
self.assertEqual(fast_threshold, brute_threshold)
|
| 97 |
+
self.assertEqual(fast_threshold, 0.72)
|
| 98 |
+
fast_result = paired_bootstrap.evaluate_predictions(
|
| 99 |
+
prepared_gt,
|
| 100 |
+
preds,
|
| 101 |
+
dimension="interactable",
|
| 102 |
+
iou_threshold=0.75,
|
| 103 |
+
score_threshold=fast_threshold,
|
| 104 |
+
)
|
| 105 |
+
self.assertEqual(fast_result.metrics, brute_result.metrics)
|
| 106 |
+
|
| 107 |
+
def _select_best_threshold_bruteforce(self, prepared_gt, preds):
|
| 108 |
+
best_threshold = None
|
| 109 |
+
best_result = None
|
| 110 |
+
for threshold in sorted({float(pred["score"]) for pred in preds}, reverse=True):
|
| 111 |
+
result = paired_bootstrap.evaluate_predictions(
|
| 112 |
+
prepared_gt,
|
| 113 |
+
preds,
|
| 114 |
+
dimension="interactable",
|
| 115 |
+
iou_threshold=0.75,
|
| 116 |
+
score_threshold=threshold,
|
| 117 |
+
)
|
| 118 |
+
if best_result is None or (
|
| 119 |
+
result.metrics["f1"],
|
| 120 |
+
result.metrics["precision"],
|
| 121 |
+
result.metrics["recall"],
|
| 122 |
+
-threshold,
|
| 123 |
+
) > (
|
| 124 |
+
best_result.metrics["f1"],
|
| 125 |
+
best_result.metrics["precision"],
|
| 126 |
+
best_result.metrics["recall"],
|
| 127 |
+
-best_threshold,
|
| 128 |
+
):
|
| 129 |
+
best_threshold = threshold
|
| 130 |
+
best_result = result
|
| 131 |
+
return best_threshold, best_result
|
| 132 |
+
|
| 133 |
+
def test_semantic_cache_match_maps_numeric_categories(self):
|
| 134 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 135 |
+
tmp = Path(tmpdir)
|
| 136 |
+
cache = tmp / "embedding.json"
|
| 137 |
+
_write(cache, {"Button": [1.0, 0.0], "Control": [0.9, 0.1]})
|
| 138 |
+
matcher = paired_bootstrap.SemanticMatcher(cache)
|
| 139 |
+
|
| 140 |
+
gt = self._toy_gt()
|
| 141 |
+
preds = [
|
| 142 |
+
{"image_id": 1, "category_id": "control", "bbox": [0, 0, 10, 10], "score": 1.0}
|
| 143 |
+
]
|
| 144 |
+
prepared_gt = paired_bootstrap.prepare_ground_truth(gt)
|
| 145 |
+
evaluated = paired_bootstrap.evaluate_predictions(
|
| 146 |
+
prepared_gt,
|
| 147 |
+
preds,
|
| 148 |
+
dimension="semantics",
|
| 149 |
+
iou_threshold=0.75,
|
| 150 |
+
score_threshold=0.0,
|
| 151 |
+
semantic_matcher=matcher,
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
self.assertEqual(evaluated.counts["tp"], 1)
|
| 155 |
+
|
| 156 |
+
def test_semantic_cache_preserves_historical_camelcase_keys(self):
|
| 157 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 158 |
+
cache = Path(tmpdir) / "embedding.json"
|
| 159 |
+
_write(cache, {"QuitButton": [1.0, 0.0], "ConfirmButton": [0.9, 0.1]})
|
| 160 |
+
matcher = paired_bootstrap.SemanticMatcher(cache)
|
| 161 |
+
|
| 162 |
+
self.assertTrue(matcher.matches("quit_button", "confirm_button"))
|
| 163 |
+
|
| 164 |
+
def test_semantic_match_defaults_to_historical_raw_dot_product(self):
|
| 165 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 166 |
+
cache = Path(tmpdir) / "embedding.json"
|
| 167 |
+
_write(cache, {"A": [1.0, 0.0], "B": [0.84, 0.20]})
|
| 168 |
+
|
| 169 |
+
historical = paired_bootstrap.SemanticMatcher(cache)
|
| 170 |
+
cosine = paired_bootstrap.SemanticMatcher(cache, similarity_mode="cosine")
|
| 171 |
+
|
| 172 |
+
self.assertFalse(historical.matches("a", "b"))
|
| 173 |
+
self.assertTrue(cosine.matches("a", "b"))
|
| 174 |
+
|
| 175 |
+
def test_semantic_cache_miss_is_hard_error(self):
|
| 176 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 177 |
+
cache = Path(tmpdir) / "embedding.json"
|
| 178 |
+
_write(cache, {"Button": [1.0, 0.0]})
|
| 179 |
+
matcher = paired_bootstrap.SemanticMatcher(cache)
|
| 180 |
+
|
| 181 |
+
with self.assertRaisesRegex(KeyError, "Missing frozen embedding cache entries"):
|
| 182 |
+
matcher.matches("button", "unknown")
|
| 183 |
+
|
| 184 |
+
def test_cli_writes_json_report(self):
|
| 185 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 186 |
+
tmp = Path(tmpdir)
|
| 187 |
+
gt_path = tmp / "gt.json"
|
| 188 |
+
a_path = tmp / "a.json"
|
| 189 |
+
b_path = tmp / "b.json"
|
| 190 |
+
out_path = tmp / "report.json"
|
| 191 |
+
_write(gt_path, self._toy_gt())
|
| 192 |
+
_write(a_path, [{"image_id": 1, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 1.0}])
|
| 193 |
+
_write(
|
| 194 |
+
b_path,
|
| 195 |
+
[
|
| 196 |
+
{"image_id": 1, "category_id": 1, "bbox": [0, 0, 10, 10], "score": 1.0},
|
| 197 |
+
{"image_id": 2, "category_id": 2, "bbox": [0, 0, 10, 10], "score": 0.9},
|
| 198 |
+
],
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
paired_bootstrap.main(
|
| 202 |
+
[
|
| 203 |
+
"--gt",
|
| 204 |
+
str(gt_path),
|
| 205 |
+
"--method-a",
|
| 206 |
+
str(a_path),
|
| 207 |
+
"--method-b",
|
| 208 |
+
str(b_path),
|
| 209 |
+
"--dimension",
|
| 210 |
+
"interactable",
|
| 211 |
+
"--auto-threshold",
|
| 212 |
+
"--replicates",
|
| 213 |
+
"100",
|
| 214 |
+
"--seed",
|
| 215 |
+
"7",
|
| 216 |
+
"--output",
|
| 217 |
+
str(out_path),
|
| 218 |
+
]
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
report = json.loads(out_path.read_text(encoding="utf-8"))
|
| 222 |
+
self.assertEqual(report["protocol"]["dimension"], "interactable")
|
| 223 |
+
self.assertEqual(report["protocol"]["bootstrap_unit"], "image_id")
|
| 224 |
+
self.assertGreater(report["methods"]["method_b"]["point"]["micro"]["f1"], report["methods"]["method_a"]["point"]["micro"]["f1"])
|
| 225 |
+
self.assertIn("mean_per_all_images", report["methods"]["method_a"]["point"])
|
| 226 |
+
self.assertIn("mean_per_positive_support_images", report["methods"]["method_a"]["point"])
|
| 227 |
+
self.assertIn("mean_per_all_images_delta", report["bootstrap"])
|
| 228 |
+
self.assertIn("mean_per_positive_support_images_delta", report["bootstrap"])
|
| 229 |
+
self.assertEqual(report["bootstrap"]["delta_direction"], "method_b_minus_method_a")
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
if __name__ == "__main__":
|
| 233 |
+
unittest.main()
|
tests/test_pipeline_utils.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import tempfile
|
| 3 |
+
import unittest
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from PIL import Image
|
| 7 |
+
|
| 8 |
+
from approach.pipeline_utils import (
|
| 9 |
+
enrich_ape_results,
|
| 10 |
+
output_path_for_selection,
|
| 11 |
+
parse_orienter_image_name,
|
| 12 |
+
resolve_image_path,
|
| 13 |
+
run_optional_reflection,
|
| 14 |
+
select_jsonl_lines,
|
| 15 |
+
write_json_atomic,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class PipelineUtilsTest(unittest.TestCase):
|
| 20 |
+
def test_parse_orienter_image_name_is_strict(self):
|
| 21 |
+
self.assertEqual(parse_orienter_image_name("123_4.jpg"), ("123", "4", 123004))
|
| 22 |
+
with self.assertRaises(ValueError):
|
| 23 |
+
parse_orienter_image_name("123_4_extra.jpg")
|
| 24 |
+
with self.assertRaises(ValueError):
|
| 25 |
+
parse_orienter_image_name("nested/123_4.jpg")
|
| 26 |
+
with self.assertRaises(ValueError):
|
| 27 |
+
parse_orienter_image_name("123_1000.jpg")
|
| 28 |
+
|
| 29 |
+
def test_resolve_image_path_rejects_escape_from_images_directory(self):
|
| 30 |
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
| 31 |
+
images_dir = Path(tmp_dir) / "images"
|
| 32 |
+
images_dir.mkdir()
|
| 33 |
+
self.assertEqual(
|
| 34 |
+
resolve_image_path(images_dir, "nested/123_4.jpg"),
|
| 35 |
+
(images_dir / "nested" / "123_4.jpg").resolve(),
|
| 36 |
+
)
|
| 37 |
+
with self.assertRaises(ValueError):
|
| 38 |
+
resolve_image_path(images_dir, "../123_4.jpg")
|
| 39 |
+
with self.assertRaises(ValueError):
|
| 40 |
+
resolve_image_path(images_dir, str(Path(tmp_dir) / "123_4.jpg"))
|
| 41 |
+
|
| 42 |
+
def test_select_jsonl_lines_supports_ranges_or_shards(self):
|
| 43 |
+
lines = [f"{idx}\n" for idx in range(10)]
|
| 44 |
+
self.assertEqual(select_jsonl_lines(lines, start_index=2, end_index=8), lines[2:8])
|
| 45 |
+
self.assertEqual(
|
| 46 |
+
select_jsonl_lines(lines, shard_index=1, num_shards=2),
|
| 47 |
+
["1\n", "3\n", "5\n", "7\n", "9\n"],
|
| 48 |
+
)
|
| 49 |
+
with self.assertRaises(ValueError):
|
| 50 |
+
select_jsonl_lines(
|
| 51 |
+
lines,
|
| 52 |
+
start_index=2,
|
| 53 |
+
end_index=8,
|
| 54 |
+
shard_index=1,
|
| 55 |
+
num_shards=2,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
def test_output_path_for_selection_prevents_shard_overwrite(self):
|
| 59 |
+
self.assertEqual(
|
| 60 |
+
output_path_for_selection("predictions.json", shard_index=1, num_shards=4),
|
| 61 |
+
"predictions.shard01-of-04.json",
|
| 62 |
+
)
|
| 63 |
+
self.assertEqual(
|
| 64 |
+
output_path_for_selection("predictions.json", start_index=100, end_index=200),
|
| 65 |
+
"predictions.rows100-200.json",
|
| 66 |
+
)
|
| 67 |
+
self.assertEqual(output_path_for_selection("predictions.json"), "predictions.json")
|
| 68 |
+
|
| 69 |
+
def test_enrich_ape_results_keeps_added_metadata(self):
|
| 70 |
+
enriched = enrich_ape_results(
|
| 71 |
+
[{"bbox": [1, 2, 3, 4], "category_name": "button"}],
|
| 72 |
+
"123_4.jpg",
|
| 73 |
+
lambda image_name: 123004,
|
| 74 |
+
)
|
| 75 |
+
self.assertEqual(enriched[0]["image_id"], 123004)
|
| 76 |
+
self.assertEqual(enriched[0]["category_id"], "button")
|
| 77 |
+
self.assertEqual(enriched[0]["bbox"], [1, 2, 3, 4])
|
| 78 |
+
|
| 79 |
+
def test_write_json_atomic_writes_one_valid_json_document(self):
|
| 80 |
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
| 81 |
+
out_path = Path(tmp_dir) / "ape.json"
|
| 82 |
+
write_json_atomic(str(out_path), [{"image_id": 1}, {"image_id": 2}])
|
| 83 |
+
self.assertEqual(json.loads(out_path.read_text()), [{"image_id": 1}, {"image_id": 2}])
|
| 84 |
+
|
| 85 |
+
def test_optional_reflection_is_disabled_by_default(self):
|
| 86 |
+
self.assertIsNone(
|
| 87 |
+
run_optional_reflection(
|
| 88 |
+
"unused.png",
|
| 89 |
+
[],
|
| 90 |
+
detector=lambda candidates, previous: previous,
|
| 91 |
+
enabled=False,
|
| 92 |
+
)
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
def test_optional_reflection_feeds_back_to_detector(self):
|
| 96 |
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
| 97 |
+
image_path = Path(tmp_dir) / "image.png"
|
| 98 |
+
Image.new("RGB", (10, 10), "white").save(image_path)
|
| 99 |
+
detector_calls = []
|
| 100 |
+
|
| 101 |
+
def advisor(**kwargs):
|
| 102 |
+
if detector_calls:
|
| 103 |
+
return {"verified": [0], "needs_refinement": [], "feedback": []}
|
| 104 |
+
return {"verified": [], "needs_refinement": [0], "feedback": ["button label"]}
|
| 105 |
+
|
| 106 |
+
def detector(candidates, previous):
|
| 107 |
+
detector_calls.append(candidates)
|
| 108 |
+
return [{"bbox": [2, 2, 3, 3], "category_name": "button"}]
|
| 109 |
+
|
| 110 |
+
result = run_optional_reflection(
|
| 111 |
+
str(image_path),
|
| 112 |
+
[{"bbox": [1, 1, 3, 3], "category_name": "button"}],
|
| 113 |
+
detector=detector,
|
| 114 |
+
enabled=True,
|
| 115 |
+
advisor=advisor,
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
self.assertEqual(detector_calls, [["button label"]])
|
| 119 |
+
self.assertFalse(result["max_iterations_reached"])
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
if __name__ == "__main__":
|
| 123 |
+
unittest.main()
|
tests/test_reflection.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import unittest
|
| 2 |
+
|
| 3 |
+
from PIL import Image
|
| 4 |
+
|
| 5 |
+
from unittest import mock
|
| 6 |
+
|
| 7 |
+
from approach.reflection import (
|
| 8 |
+
build_reflection_prompt,
|
| 9 |
+
make_successful_detection_request,
|
| 10 |
+
openai_compatible_advisor,
|
| 11 |
+
run_reflection_loop,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class ReflectionTest(unittest.TestCase):
|
| 16 |
+
def test_pii5_crop_uses_bbox_without_resize_or_flip(self):
|
| 17 |
+
image = Image.new("RGB", (12, 8), "black")
|
| 18 |
+
for x in range(2, 7):
|
| 19 |
+
for y in range(1, 5):
|
| 20 |
+
image.putpixel((x, y), (x, y, 100))
|
| 21 |
+
|
| 22 |
+
request = make_successful_detection_request(
|
| 23 |
+
image, {"bbox": [2, 1, 5, 4], "category_name": "button"}
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
self.assertEqual(request.crop_image.size, (5, 4))
|
| 27 |
+
self.assertEqual(request.crop_image.getpixel((0, 0)), image.getpixel((2, 1)))
|
| 28 |
+
self.assertEqual(request.crop_image.getpixel((4, 0)), image.getpixel((6, 1)))
|
| 29 |
+
self.assertIs(request.original_image, image)
|
| 30 |
+
|
| 31 |
+
def test_fractional_bbox_crop_contains_the_full_detected_region(self):
|
| 32 |
+
request = make_successful_detection_request(
|
| 33 |
+
Image.new("RGB", (10, 10), "white"),
|
| 34 |
+
{"bbox": [1.2, 2.2, 2.2, 1.2], "category_name": "button"},
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
self.assertEqual(request.crop_image.size, (3, 2))
|
| 38 |
+
|
| 39 |
+
def test_reflection_loop_stops_when_advisor_has_no_concerns(self):
|
| 40 |
+
calls = []
|
| 41 |
+
|
| 42 |
+
def advisor(**kwargs):
|
| 43 |
+
calls.append(kwargs)
|
| 44 |
+
return {"verified": [0], "needs_refinement": [], "feedback": []}
|
| 45 |
+
|
| 46 |
+
result = run_reflection_loop(
|
| 47 |
+
Image.new("RGB", (10, 10), "white"),
|
| 48 |
+
[{"bbox": [1, 1, 3, 3], "category_name": "menu"}],
|
| 49 |
+
miner=lambda trace: ["menu"],
|
| 50 |
+
detector=lambda candidates, previous: previous,
|
| 51 |
+
advisor=advisor,
|
| 52 |
+
max_iterations=10,
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
self.assertEqual(len(calls), 1)
|
| 56 |
+
self.assertFalse(result["max_iterations_reached"])
|
| 57 |
+
self.assertEqual(result["trace"][0]["verified"], [0])
|
| 58 |
+
self.assertEqual(len(calls[0]["verification_requests"]), 1)
|
| 59 |
+
self.assertIn("PII.6", calls[0]["pii6_prompt"])
|
| 60 |
+
|
| 61 |
+
def test_reflection_loop_marks_max_iterations(self):
|
| 62 |
+
def advisor(**kwargs):
|
| 63 |
+
return {"verified": [], "needs_refinement": [0], "feedback": ["retry"]}
|
| 64 |
+
|
| 65 |
+
result = run_reflection_loop(
|
| 66 |
+
Image.new("RGB", (10, 10), "white"),
|
| 67 |
+
[{"bbox": [1, 1, 3, 3], "category_name": "menu"}],
|
| 68 |
+
miner=lambda trace: ["menu"],
|
| 69 |
+
detector=lambda candidates, previous: previous,
|
| 70 |
+
advisor=advisor,
|
| 71 |
+
max_iterations=3,
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
self.assertTrue(result["max_iterations_reached"])
|
| 75 |
+
self.assertEqual(len(result["trace"]), 3)
|
| 76 |
+
|
| 77 |
+
def test_reflection_removes_rejected_false_positive_before_redetection(self):
|
| 78 |
+
detector_calls = []
|
| 79 |
+
|
| 80 |
+
def detector(candidates, retained):
|
| 81 |
+
detector_calls.append((candidates, retained))
|
| 82 |
+
return retained
|
| 83 |
+
|
| 84 |
+
advice = [
|
| 85 |
+
{"verified": [1], "needs_refinement": [0], "feedback": ["missing lever"]},
|
| 86 |
+
{"verified": [0], "needs_refinement": [], "feedback": []},
|
| 87 |
+
]
|
| 88 |
+
detections = [
|
| 89 |
+
{"bbox": [1, 1, 2, 2], "category_name": "decoration"},
|
| 90 |
+
{"bbox": [5, 5, 2, 2], "category_name": "button"},
|
| 91 |
+
]
|
| 92 |
+
|
| 93 |
+
result = run_reflection_loop(
|
| 94 |
+
Image.new("RGB", (10, 10), "white"),
|
| 95 |
+
detections,
|
| 96 |
+
miner=lambda trace: trace[-1]["feedback"],
|
| 97 |
+
detector=detector,
|
| 98 |
+
advisor=lambda **kwargs: advice.pop(0),
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
self.assertEqual(detector_calls[0][0], ["missing lever"])
|
| 102 |
+
self.assertEqual(detector_calls[0][1], [detections[1]])
|
| 103 |
+
self.assertEqual(result["detections"], [detections[1]])
|
| 104 |
+
|
| 105 |
+
def test_openai_compatible_advisor_sends_annotated_scene_original_and_crop(self):
|
| 106 |
+
fake_client = mock.Mock()
|
| 107 |
+
fake_client.complete_json.return_value = {
|
| 108 |
+
"verified": [0],
|
| 109 |
+
"needs_refinement": [],
|
| 110 |
+
"feedback": [],
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
with mock.patch("approach.reflection.OpenAICompatibleChatClient", return_value=fake_client):
|
| 114 |
+
advisor = openai_compatible_advisor("default")
|
| 115 |
+
request = make_successful_detection_request(
|
| 116 |
+
Image.new("RGB", (10, 10), "white"),
|
| 117 |
+
{"bbox": [1, 1, 3, 3], "category_name": "button"},
|
| 118 |
+
)
|
| 119 |
+
result = advisor(
|
| 120 |
+
verification_requests=[request],
|
| 121 |
+
unsuccessful_detection_image=Image.new("RGB", (10, 10), "white"),
|
| 122 |
+
pii5_prompt="PII.5",
|
| 123 |
+
pii6_prompt="PII.6",
|
| 124 |
+
pii7_prompt="PII.7",
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
self.assertEqual(result["verified"], [0])
|
| 128 |
+
call = fake_client.complete_json.call_args
|
| 129 |
+
self.assertIn("PII.5", call.args[0])
|
| 130 |
+
self.assertEqual(len(call.args[1]), 3)
|
| 131 |
+
self.assertTrue(all(image.media_type == "image/png" for image in call.args[1]))
|
| 132 |
+
self.assertIn("response_format", call.kwargs)
|
| 133 |
+
|
| 134 |
+
def test_reflection_prompt_includes_detection_metadata(self):
|
| 135 |
+
request = make_successful_detection_request(
|
| 136 |
+
Image.new("RGB", (10, 10), "white"),
|
| 137 |
+
{"bbox": [1, 2, 3, 4], "category_name": "slider", "score": 0.9},
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
prompt = build_reflection_prompt([request], "PII.5", "PII.6", "PII.7")
|
| 141 |
+
|
| 142 |
+
self.assertIn('"category_name": "slider"', prompt)
|
| 143 |
+
self.assertIn("PII.6", prompt)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
if __name__ == "__main__":
|
| 147 |
+
unittest.main()
|
tests/test_release_hygiene.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
import unittest
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
ROOT = Path(__file__).resolve().parents[1]
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ReleaseHygieneTests(unittest.TestCase):
|
| 10 |
+
allowed_large_files = {
|
| 11 |
+
Path("approach/ovod/APE/ape_d_model_final.pth"),
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
def test_release_policies_exclude_generated_python_state(self):
|
| 15 |
+
gitignore = (ROOT / ".gitignore").read_text(encoding="utf-8")
|
| 16 |
+
release_excludes = (ROOT / "docs" / "RELEASE_EXCLUDES.txt").read_text(encoding="utf-8")
|
| 17 |
+
|
| 18 |
+
self.assertIn("__pycache__/", gitignore)
|
| 19 |
+
self.assertIn("*.py[cod]", gitignore)
|
| 20 |
+
self.assertIn(".pytest_cache/", gitignore)
|
| 21 |
+
self.assertIn("**/__pycache__/", release_excludes)
|
| 22 |
+
self.assertIn("**/*.pyc", release_excludes)
|
| 23 |
+
self.assertIn("**/.pytest_cache/", release_excludes)
|
| 24 |
+
|
| 25 |
+
def test_release_does_not_reference_private_relays_or_source_machines(self):
|
| 26 |
+
forbidden = (
|
| 27 |
+
"chat" + "anywhere",
|
| 28 |
+
"aigpt" + "x.top",
|
| 29 |
+
"/home/" + "sqli/",
|
| 30 |
+
"/research/d4/gds/" + "sqli21",
|
| 31 |
+
"10.249." + "190.53",
|
| 32 |
+
"/Users" + "/",
|
| 33 |
+
"/users/" + "prannay",
|
| 34 |
+
)
|
| 35 |
+
violations = []
|
| 36 |
+
for path in ROOT.rglob("*"):
|
| 37 |
+
if not path.is_file() or ".git" in path.parts:
|
| 38 |
+
continue
|
| 39 |
+
try:
|
| 40 |
+
text = path.read_text(encoding="utf-8")
|
| 41 |
+
except (UnicodeDecodeError, OSError):
|
| 42 |
+
continue
|
| 43 |
+
for needle in forbidden:
|
| 44 |
+
if needle in text:
|
| 45 |
+
violations.append(f"{path.relative_to(ROOT)}: {needle}")
|
| 46 |
+
self.assertEqual(violations, [])
|
| 47 |
+
|
| 48 |
+
def test_release_excludes_generated_results_and_local_git_state(self):
|
| 49 |
+
forbidden_parts = {
|
| 50 |
+
".git",
|
| 51 |
+
"__pycache__",
|
| 52 |
+
".pytest_cache",
|
| 53 |
+
"outputs",
|
| 54 |
+
"output",
|
| 55 |
+
"runs",
|
| 56 |
+
"wandb",
|
| 57 |
+
"results",
|
| 58 |
+
"eval_results",
|
| 59 |
+
"validation",
|
| 60 |
+
}
|
| 61 |
+
violations = []
|
| 62 |
+
for path in ROOT.rglob("*"):
|
| 63 |
+
rel = path.relative_to(ROOT)
|
| 64 |
+
if any(part in forbidden_parts for part in rel.parts):
|
| 65 |
+
violations.append(str(rel))
|
| 66 |
+
self.assertEqual(violations, [])
|
| 67 |
+
|
| 68 |
+
self.assertFalse(
|
| 69 |
+
(ROOT / "data").exists(),
|
| 70 |
+
"Top-level data/ must remain an external mount, not release content",
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
explicitly_forbidden = (
|
| 74 |
+
Path("docs/EVALUATION_VALIDATION.md"),
|
| 75 |
+
Path("evaluation/validation"),
|
| 76 |
+
Path("approach/ovod/APE/test_output.png"),
|
| 77 |
+
Path("baselines/claude-4.5-sonnet-e2e/claude-4.5-sonnet-tiny.json"),
|
| 78 |
+
)
|
| 79 |
+
self.assertEqual([str(path) for path in explicitly_forbidden if (ROOT / path).exists()], [])
|
| 80 |
+
|
| 81 |
+
forbidden_suffixes = (".log", ".pyc", ".pyo", ".nfs")
|
| 82 |
+
unexpected = []
|
| 83 |
+
for path in ROOT.rglob("*"):
|
| 84 |
+
if not path.is_file():
|
| 85 |
+
continue
|
| 86 |
+
if path.name.startswith(".nfs") or path.name.endswith(forbidden_suffixes):
|
| 87 |
+
unexpected.append(str(path.relative_to(ROOT)))
|
| 88 |
+
self.assertEqual(unexpected, [])
|
| 89 |
+
|
| 90 |
+
def test_release_has_no_credential_material(self):
|
| 91 |
+
forbidden_files = []
|
| 92 |
+
for path in ROOT.rglob("*"):
|
| 93 |
+
if not path.is_file():
|
| 94 |
+
continue
|
| 95 |
+
rel = path.relative_to(ROOT)
|
| 96 |
+
if path.name == ".env" or (
|
| 97 |
+
path.name.startswith(".env.") and path.name != ".env.example"
|
| 98 |
+
):
|
| 99 |
+
forbidden_files.append(str(rel))
|
| 100 |
+
if path.suffix.lower() in {".pem", ".key"}:
|
| 101 |
+
forbidden_files.append(str(rel))
|
| 102 |
+
self.assertEqual(forbidden_files, [])
|
| 103 |
+
|
| 104 |
+
secret_patterns = (
|
| 105 |
+
re.compile(r"sk-or-v1-[A-Za-z0-9_-]{20,}"),
|
| 106 |
+
re.compile(r"olp_[A-Za-z0-9_-]{12,}"),
|
| 107 |
+
re.compile(r"AIza[0-9A-Za-z_-]{20,}"),
|
| 108 |
+
re.compile(r"gh[pousr]_[0-9A-Za-z]{20,}"),
|
| 109 |
+
re.compile(r"hf_[0-9A-Za-z]{20,}"),
|
| 110 |
+
re.compile(r"AKIA[0-9A-Z]{16}"),
|
| 111 |
+
re.compile(r"BEGIN (?:RSA |OPENSSH |EC )?PRIVATE KEY"),
|
| 112 |
+
)
|
| 113 |
+
violations = []
|
| 114 |
+
for path in ROOT.rglob("*"):
|
| 115 |
+
if not path.is_file() or path.suffix.lower() == ".pth":
|
| 116 |
+
continue
|
| 117 |
+
try:
|
| 118 |
+
text = path.read_text(encoding="utf-8")
|
| 119 |
+
except (UnicodeDecodeError, OSError):
|
| 120 |
+
continue
|
| 121 |
+
if any(pattern.search(text) for pattern in secret_patterns):
|
| 122 |
+
violations.append(str(path.relative_to(ROOT)))
|
| 123 |
+
self.assertEqual(violations, [])
|
| 124 |
+
|
| 125 |
+
def test_large_binary_files_are_limited_to_bundled_main_checkpoint(self):
|
| 126 |
+
threshold = 20 * 1024 * 1024
|
| 127 |
+
violations = []
|
| 128 |
+
for path in ROOT.rglob("*"):
|
| 129 |
+
if not path.is_file():
|
| 130 |
+
continue
|
| 131 |
+
rel = path.relative_to(ROOT)
|
| 132 |
+
if path.stat().st_size > threshold and rel not in self.allowed_large_files:
|
| 133 |
+
violations.append(str(rel))
|
| 134 |
+
self.assertEqual(violations, [])
|
| 135 |
+
|
| 136 |
+
def test_model_manifest_matches_bundled_checkpoint_policy(self):
|
| 137 |
+
manifest = (ROOT / "docs" / "MODEL_MANIFEST.md").read_text(encoding="utf-8")
|
| 138 |
+
checkpoint = ROOT / "approach" / "ovod" / "APE" / "ape_d_model_final.pth"
|
| 139 |
+
self.assertIn("3548f41a3238148180e08fd4b16c71f4abc3ac3caf9c8434444462d1bdb7f965", manifest)
|
| 140 |
+
self.assertTrue(checkpoint.is_file())
|
| 141 |
+
self.assertEqual(checkpoint.stat().st_size, 5_956_547_279)
|
| 142 |
+
|
| 143 |
+
attributes = (ROOT / ".gitattributes").read_text(encoding="utf-8")
|
| 144 |
+
self.assertIn(
|
| 145 |
+
"approach/ovod/APE/ape_d_model_final.pth filter=lfs diff=lfs merge=lfs -text",
|
| 146 |
+
attributes,
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
if __name__ == "__main__":
|
| 151 |
+
unittest.main()
|
tests/test_release_scripts.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import tempfile
|
| 3 |
+
import unittest
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from scripts.generate_questions import build_questions, write_metadata_template
|
| 7 |
+
from scripts.verify_assets import (
|
| 8 |
+
check_embedding_cache,
|
| 9 |
+
check_metadata_cache,
|
| 10 |
+
check_questions,
|
| 11 |
+
sha256_file,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class ReleaseScriptTests(unittest.TestCase):
|
| 16 |
+
def test_generate_questions_from_image_directory(self):
|
| 17 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 18 |
+
root = Path(tmpdir)
|
| 19 |
+
images = root / "images"
|
| 20 |
+
images.mkdir()
|
| 21 |
+
(images / "123_4.jpg").write_bytes(b"fake")
|
| 22 |
+
(images / "123_5.png").write_bytes(b"fake")
|
| 23 |
+
(images / "123_10.png").write_bytes(b"fake")
|
| 24 |
+
(images / "notes.txt").write_text("ignored", encoding="utf-8")
|
| 25 |
+
|
| 26 |
+
questions = build_questions(images, "Prompt")
|
| 27 |
+
metadata_template = root / "metadata.json"
|
| 28 |
+
write_metadata_template(metadata_template, questions)
|
| 29 |
+
|
| 30 |
+
metadata = json.loads(metadata_template.read_text(encoding="utf-8"))
|
| 31 |
+
|
| 32 |
+
self.assertEqual([record["image_id"] for record in questions], [123004, 123005, 123010])
|
| 33 |
+
self.assertEqual(questions[0]["text"], "Prompt")
|
| 34 |
+
self.assertEqual(sorted(metadata), ["123"])
|
| 35 |
+
|
| 36 |
+
def test_recursive_question_generation_uses_basename_for_app_id(self):
|
| 37 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 38 |
+
root = Path(tmpdir)
|
| 39 |
+
images = root / "images"
|
| 40 |
+
nested = images / "nested"
|
| 41 |
+
nested.mkdir(parents=True)
|
| 42 |
+
(nested / "123_4.jpg").write_bytes(b"fake")
|
| 43 |
+
|
| 44 |
+
questions = build_questions(images, "Prompt", recursive=True)
|
| 45 |
+
metadata_template = root / "metadata.json"
|
| 46 |
+
write_metadata_template(metadata_template, questions)
|
| 47 |
+
metadata = json.loads(metadata_template.read_text(encoding="utf-8"))
|
| 48 |
+
|
| 49 |
+
self.assertEqual(questions[0]["image"], "nested/123_4.jpg")
|
| 50 |
+
self.assertEqual(sorted(metadata), ["123"])
|
| 51 |
+
|
| 52 |
+
def test_verify_questions_and_metadata_cache(self):
|
| 53 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 54 |
+
root = Path(tmpdir)
|
| 55 |
+
images = root / "images"
|
| 56 |
+
images.mkdir()
|
| 57 |
+
(images / "123_4.jpg").write_bytes(b"fake")
|
| 58 |
+
questions = root / "questions.jsonl"
|
| 59 |
+
questions.write_text(
|
| 60 |
+
json.dumps(
|
| 61 |
+
{
|
| 62 |
+
"question_id": 0,
|
| 63 |
+
"image": "123_4.jpg",
|
| 64 |
+
"image_id": 123004,
|
| 65 |
+
"text": "Prompt",
|
| 66 |
+
}
|
| 67 |
+
)
|
| 68 |
+
+ "\n",
|
| 69 |
+
encoding="utf-8",
|
| 70 |
+
)
|
| 71 |
+
metadata = root / "metadata.json"
|
| 72 |
+
metadata.write_text(
|
| 73 |
+
json.dumps(
|
| 74 |
+
{
|
| 75 |
+
"123": {
|
| 76 |
+
"app_name": "Test VR",
|
| 77 |
+
"app_description": "Description.",
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
),
|
| 81 |
+
encoding="utf-8",
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
report = check_questions(questions, images)
|
| 85 |
+
metadata_report = check_metadata_cache(metadata, report["app_ids"])
|
| 86 |
+
|
| 87 |
+
self.assertEqual(report["questions"], 1)
|
| 88 |
+
self.assertEqual(metadata_report["covered_app_ids"], 1)
|
| 89 |
+
|
| 90 |
+
def test_verify_metadata_cache_rejects_duplicates_and_missing_fields(self):
|
| 91 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 92 |
+
root = Path(tmpdir)
|
| 93 |
+
duplicate = root / "duplicate.jsonl"
|
| 94 |
+
duplicate.write_text(
|
| 95 |
+
"\n".join(
|
| 96 |
+
[
|
| 97 |
+
json.dumps(
|
| 98 |
+
{
|
| 99 |
+
"app_id": 123,
|
| 100 |
+
"app_name": "First",
|
| 101 |
+
"app_description": "Description.",
|
| 102 |
+
}
|
| 103 |
+
),
|
| 104 |
+
json.dumps(
|
| 105 |
+
{
|
| 106 |
+
"app_id": "123",
|
| 107 |
+
"app_name": "Second",
|
| 108 |
+
"app_description": "Description.",
|
| 109 |
+
}
|
| 110 |
+
),
|
| 111 |
+
]
|
| 112 |
+
)
|
| 113 |
+
+ "\n",
|
| 114 |
+
encoding="utf-8",
|
| 115 |
+
)
|
| 116 |
+
missing_field = root / "missing_field.json"
|
| 117 |
+
missing_field.write_text(
|
| 118 |
+
json.dumps({"123": {"app_name": "Test VR"}}),
|
| 119 |
+
encoding="utf-8",
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
with self.assertRaises(ValueError):
|
| 123 |
+
check_metadata_cache(duplicate, ["123"])
|
| 124 |
+
with self.assertRaises(ValueError):
|
| 125 |
+
check_metadata_cache(missing_field, ["123"])
|
| 126 |
+
|
| 127 |
+
def test_verify_external_embedding_cache_against_manifest(self):
|
| 128 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 129 |
+
root = Path(tmpdir)
|
| 130 |
+
cache = root / "embedding_dict.json"
|
| 131 |
+
cache.write_bytes(b"frozen-cache")
|
| 132 |
+
manifest = root / "cache_manifest.json"
|
| 133 |
+
manifest.write_text(
|
| 134 |
+
json.dumps(
|
| 135 |
+
{
|
| 136 |
+
"artifact": "embedding_dict.json",
|
| 137 |
+
"size_bytes": cache.stat().st_size,
|
| 138 |
+
"sha256": sha256_file(cache),
|
| 139 |
+
"entry_count": 1,
|
| 140 |
+
"embedding_dimension": 2,
|
| 141 |
+
}
|
| 142 |
+
),
|
| 143 |
+
encoding="utf-8",
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
report = check_embedding_cache(cache, manifest)
|
| 147 |
+
|
| 148 |
+
self.assertEqual(report["entry_count"], 1)
|
| 149 |
+
self.assertEqual(report["embedding_dimension"], 2)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
if __name__ == "__main__":
|
| 153 |
+
unittest.main()
|
tests/test_run_ape.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import tempfile
|
| 3 |
+
import unittest
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from approach.run_ape import (
|
| 7 |
+
_resolve_ape_checkpoint,
|
| 8 |
+
_verify_ape_checkpoint,
|
| 9 |
+
build_parser,
|
| 10 |
+
run,
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class RunApeTests(unittest.TestCase):
|
| 15 |
+
def test_custom_checkpoint_requires_explicit_trust(self):
|
| 16 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 17 |
+
checkpoint = Path(tmpdir) / "custom.pth"
|
| 18 |
+
checkpoint.write_bytes(b"not-the-released-checkpoint")
|
| 19 |
+
with self.assertRaises(ValueError):
|
| 20 |
+
_verify_ape_checkpoint(checkpoint)
|
| 21 |
+
_verify_ape_checkpoint(checkpoint, trust_custom_checkpoint=True)
|
| 22 |
+
|
| 23 |
+
def test_checkpoint_resolver_accepts_repo_relative_path(self):
|
| 24 |
+
repo_relative = Path("approach/ovod/APE/ape_d_model_final.pth")
|
| 25 |
+
resolved = _resolve_ape_checkpoint(Path("approach/ovod/APE"), repo_relative)
|
| 26 |
+
|
| 27 |
+
self.assertEqual(resolved, (Path.cwd() / repo_relative).resolve())
|
| 28 |
+
|
| 29 |
+
def test_configurable_runner_selects_shard_and_writes_isolated_output(self):
|
| 30 |
+
calls = []
|
| 31 |
+
|
| 32 |
+
def inference(**kwargs):
|
| 33 |
+
calls.append(kwargs)
|
| 34 |
+
return [
|
| 35 |
+
{
|
| 36 |
+
"category_name": kwargs["text_prompt"].split(":", 1)[0],
|
| 37 |
+
"bbox": [1, 2, 3, 4],
|
| 38 |
+
"score": 0.9,
|
| 39 |
+
}
|
| 40 |
+
]
|
| 41 |
+
|
| 42 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 43 |
+
root = Path(tmpdir)
|
| 44 |
+
questions = root / "questions.jsonl"
|
| 45 |
+
candidates = root / "candidates.jsonl"
|
| 46 |
+
output = root / "predictions.json"
|
| 47 |
+
questions.write_text(
|
| 48 |
+
"\n".join(
|
| 49 |
+
[
|
| 50 |
+
json.dumps({"question_id": 0, "image": "123_4.jpg"}),
|
| 51 |
+
json.dumps({"question_id": 1, "image": "456_7.jpg"}),
|
| 52 |
+
]
|
| 53 |
+
)
|
| 54 |
+
+ "\n"
|
| 55 |
+
)
|
| 56 |
+
candidates.write_text(
|
| 57 |
+
"\n".join(
|
| 58 |
+
[
|
| 59 |
+
json.dumps(
|
| 60 |
+
{
|
| 61 |
+
"question_id": 0,
|
| 62 |
+
"text": '{"objects": {"button": "round red"}}',
|
| 63 |
+
}
|
| 64 |
+
),
|
| 65 |
+
json.dumps(
|
| 66 |
+
{
|
| 67 |
+
"question_id": 1,
|
| 68 |
+
"text": {"objects": {"lever": "long silver"}},
|
| 69 |
+
}
|
| 70 |
+
),
|
| 71 |
+
]
|
| 72 |
+
)
|
| 73 |
+
+ "\n"
|
| 74 |
+
)
|
| 75 |
+
args = build_parser().parse_args(
|
| 76 |
+
[
|
| 77 |
+
"--questions",
|
| 78 |
+
str(questions),
|
| 79 |
+
"--candidates",
|
| 80 |
+
str(candidates),
|
| 81 |
+
"--images-dir",
|
| 82 |
+
str(root / "images"),
|
| 83 |
+
"--output",
|
| 84 |
+
str(output),
|
| 85 |
+
"--shard-index",
|
| 86 |
+
"0",
|
| 87 |
+
"--num-shards",
|
| 88 |
+
"2",
|
| 89 |
+
]
|
| 90 |
+
)
|
| 91 |
+
|
| 92 |
+
report = run(args, inference=inference)
|
| 93 |
+
shard_output = root / "predictions.shard00-of-02.json"
|
| 94 |
+
|
| 95 |
+
self.assertTrue(shard_output.exists())
|
| 96 |
+
self.assertTrue((root / "predictions.shard00-of-02.progress.json").exists())
|
| 97 |
+
self.assertEqual(json.loads(shard_output.read_text())[0]["image_id"], 123004)
|
| 98 |
+
|
| 99 |
+
self.assertEqual(report["records_selected"], 1)
|
| 100 |
+
self.assertEqual(len(calls), 1)
|
| 101 |
+
self.assertEqual(calls[0]["confidence_threshold"], 0.15)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
if __name__ == "__main__":
|
| 105 |
+
unittest.main()
|
tests/test_run_vlm.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import tempfile
|
| 3 |
+
import unittest
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from approach.run_vlm import build_parser, run
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class RunVlmTests(unittest.TestCase):
|
| 10 |
+
def test_configurable_vlm_runner_writes_selected_records(self):
|
| 11 |
+
calls = []
|
| 12 |
+
|
| 13 |
+
def processor(profile, question, image_path, ablation, key_index):
|
| 14 |
+
calls.append((profile, question, Path(image_path).name, ablation, key_index))
|
| 15 |
+
return {"objects": {"button": "round red"}}
|
| 16 |
+
|
| 17 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 18 |
+
root = Path(tmpdir)
|
| 19 |
+
questions = root / "questions.jsonl"
|
| 20 |
+
questions.write_text(
|
| 21 |
+
"\n".join(
|
| 22 |
+
[
|
| 23 |
+
json.dumps({"question_id": 0, "image": "123_4.jpg", "text": "mine"}),
|
| 24 |
+
json.dumps({"question_id": 1, "image": "456_7.jpg", "text": "mine"}),
|
| 25 |
+
]
|
| 26 |
+
)
|
| 27 |
+
+ "\n"
|
| 28 |
+
)
|
| 29 |
+
args = build_parser().parse_args(
|
| 30 |
+
[
|
| 31 |
+
"--questions",
|
| 32 |
+
str(questions),
|
| 33 |
+
"--images-dir",
|
| 34 |
+
str(root / "images"),
|
| 35 |
+
"--output",
|
| 36 |
+
str(root / "answers.jsonl"),
|
| 37 |
+
"--start-index",
|
| 38 |
+
"0",
|
| 39 |
+
"--end-index",
|
| 40 |
+
"1",
|
| 41 |
+
"--profile",
|
| 42 |
+
"paper_claude",
|
| 43 |
+
]
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
report = run(args, processor=processor)
|
| 47 |
+
output = root / "answers.rows0-1.jsonl"
|
| 48 |
+
answer = json.loads(output.read_text().strip())
|
| 49 |
+
|
| 50 |
+
self.assertEqual(report["completed"], 1)
|
| 51 |
+
self.assertEqual(answer["question_id"], 0)
|
| 52 |
+
self.assertEqual(answer["model_id"], "anthropic/claude-3.5-sonnet")
|
| 53 |
+
self.assertEqual(calls[0][:4], ("paper_claude", "mine", "123_4.jpg", False))
|
| 54 |
+
|
| 55 |
+
def test_default_processor_uses_app_metadata_cache_without_live_fallback(self):
|
| 56 |
+
from approach.vlm.gpt4v.gpt4v import get_steam_app_data, load_app_metadata_cache
|
| 57 |
+
|
| 58 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 59 |
+
root = Path(tmpdir)
|
| 60 |
+
cache_path = root / "metadata.json"
|
| 61 |
+
cache_path.write_text(
|
| 62 |
+
json.dumps(
|
| 63 |
+
{
|
| 64 |
+
"123": {
|
| 65 |
+
"app_name": "Test VR",
|
| 66 |
+
"app_description": "Synthetic cache-only description.",
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
),
|
| 70 |
+
encoding="utf-8",
|
| 71 |
+
)
|
| 72 |
+
cache = load_app_metadata_cache(cache_path)
|
| 73 |
+
|
| 74 |
+
self.assertEqual(
|
| 75 |
+
get_steam_app_data("123", "123_4.jpg", cache),
|
| 76 |
+
("Test VR", "Synthetic cache-only description."),
|
| 77 |
+
)
|
| 78 |
+
with self.assertRaises(KeyError):
|
| 79 |
+
get_steam_app_data("456", "456_7.jpg", cache)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
if __name__ == "__main__":
|
| 83 |
+
unittest.main()
|
tests/test_semantic_matching_cache.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import importlib.util
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
import sys
|
| 5 |
+
import tempfile
|
| 6 |
+
import types
|
| 7 |
+
import unittest
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
from unittest import mock
|
| 10 |
+
|
| 11 |
+
import numpy # noqa: F401 - preload C extension before dynamic module reloads.
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
MODULE_PATH = (
|
| 15 |
+
Path(__file__).resolve().parents[1]
|
| 16 |
+
/ "evaluation"
|
| 17 |
+
/ "pycocotools_ovod"
|
| 18 |
+
/ "semantic_matching.py"
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class _FakeEmbeddings:
|
| 23 |
+
def __init__(self):
|
| 24 |
+
self.calls = []
|
| 25 |
+
|
| 26 |
+
def create(self, input, model):
|
| 27 |
+
self.calls.append((list(input), model))
|
| 28 |
+
|
| 29 |
+
class Response:
|
| 30 |
+
def model_dump(self_inner):
|
| 31 |
+
return {
|
| 32 |
+
"data": [
|
| 33 |
+
{"embedding": [1.0, 0.0]},
|
| 34 |
+
{"embedding": [1.0, 0.0]},
|
| 35 |
+
]
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
return Response()
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def load_semantic_matching(cache_path, *, offline=False, readonly=False, api_key=None, fake_openai=None):
|
| 42 |
+
fake_openai = fake_openai or types.SimpleNamespace(embeddings=_FakeEmbeddings())
|
| 43 |
+
|
| 44 |
+
with mock.patch.dict(
|
| 45 |
+
os.environ,
|
| 46 |
+
{
|
| 47 |
+
"ORIENTER_EMBEDDING_CACHE": str(cache_path),
|
| 48 |
+
"ORIENTER_EMBEDDING_OFFLINE": "1" if offline else "",
|
| 49 |
+
"ORIENTER_EMBEDDING_READONLY": "1" if readonly else "",
|
| 50 |
+
"ZHIPU_API_KEY": api_key or "",
|
| 51 |
+
},
|
| 52 |
+
clear=False,
|
| 53 |
+
), mock.patch.dict(sys.modules, {"openai": fake_openai}):
|
| 54 |
+
name = f"semantic_matching_under_test_{id(cache_path)}_{offline}_{readonly}"
|
| 55 |
+
spec = importlib.util.spec_from_file_location(name, MODULE_PATH)
|
| 56 |
+
module = importlib.util.module_from_spec(spec)
|
| 57 |
+
spec.loader.exec_module(module)
|
| 58 |
+
return module, fake_openai
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class SemanticMatchingCacheTests(unittest.TestCase):
|
| 62 |
+
def test_offline_cache_hit_does_not_call_api(self):
|
| 63 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 64 |
+
cache_path = Path(tmpdir) / "embedding_dict.json"
|
| 65 |
+
cache_path.write_text(json.dumps({"Button": [1.0, 0.0]}))
|
| 66 |
+
|
| 67 |
+
module, fake_openai = load_semantic_matching(cache_path, offline=True)
|
| 68 |
+
|
| 69 |
+
self.assertTrue(module.is_semantic_match("button", "button", eval_dimension="s"))
|
| 70 |
+
self.assertEqual(fake_openai.embeddings.calls, [])
|
| 71 |
+
|
| 72 |
+
def test_offline_cache_miss_fails_before_api_call(self):
|
| 73 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 74 |
+
cache_path = Path(tmpdir) / "embedding_dict.json"
|
| 75 |
+
cache_path.write_text(json.dumps({"Known": [1.0, 0.0]}))
|
| 76 |
+
|
| 77 |
+
module, fake_openai = load_semantic_matching(cache_path, offline=True)
|
| 78 |
+
|
| 79 |
+
with self.assertRaisesRegex(RuntimeError, "ORIENTER_EMBEDDING_OFFLINE=1"):
|
| 80 |
+
module.is_semantic_match("known", "missing", eval_dimension="s")
|
| 81 |
+
|
| 82 |
+
self.assertEqual(fake_openai.embeddings.calls, [])
|
| 83 |
+
self.assertFalse((Path(tmpdir) / "embedding_dict.json.save").exists())
|
| 84 |
+
|
| 85 |
+
def test_invalid_cache_is_rejected_at_load_time(self):
|
| 86 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 87 |
+
cache_path = Path(tmpdir) / "embedding_dict.json"
|
| 88 |
+
cache_path.write_text(json.dumps({"A": [1.0, 0.0], "B": [1.0]}))
|
| 89 |
+
|
| 90 |
+
with self.assertRaisesRegex(ValueError, "dimension"):
|
| 91 |
+
load_semantic_matching(cache_path, offline=True)
|
| 92 |
+
|
| 93 |
+
def test_non_finite_cache_value_is_rejected_at_load_time(self):
|
| 94 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 95 |
+
cache_path = Path(tmpdir) / "embedding_dict.json"
|
| 96 |
+
cache_path.write_text(json.dumps({"A": [1.0, float("nan")]}))
|
| 97 |
+
|
| 98 |
+
with self.assertRaisesRegex(ValueError, "non-finite"):
|
| 99 |
+
load_semantic_matching(cache_path, offline=True)
|
| 100 |
+
|
| 101 |
+
def test_readonly_cache_does_not_write_after_api_fill(self):
|
| 102 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 103 |
+
cache_path = Path(tmpdir) / "embedding_dict.json"
|
| 104 |
+
cache_path.write_text(json.dumps({}))
|
| 105 |
+
original = cache_path.read_text()
|
| 106 |
+
|
| 107 |
+
module, fake_openai = load_semantic_matching(
|
| 108 |
+
cache_path,
|
| 109 |
+
readonly=True,
|
| 110 |
+
api_key="fake-key",
|
| 111 |
+
)
|
| 112 |
+
module.SAVE_INTERVAL = 1
|
| 113 |
+
|
| 114 |
+
self.assertTrue(module.is_semantic_match("alpha", "beta", eval_dimension="s"))
|
| 115 |
+
self.assertEqual(fake_openai.embeddings.calls, [(["Alpha", "Beta"], "embedding-3")])
|
| 116 |
+
self.assertEqual(cache_path.read_text(), original)
|
| 117 |
+
self.assertFalse((Path(tmpdir) / "embedding_dict.json.save").exists())
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
if __name__ == "__main__":
|
| 121 |
+
unittest.main()
|
tests/test_vlm_adapter.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tempfile
|
| 2 |
+
import unittest
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from unittest import mock
|
| 6 |
+
|
| 7 |
+
from approach.vlm.gpt4v import gpt4v
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class VlmAdapterTests(unittest.TestCase):
|
| 11 |
+
def test_app_metadata_cache_fails_on_missing_app_id(self):
|
| 12 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 13 |
+
cache_path = Path(tmpdir) / "apps.json"
|
| 14 |
+
cache_path.write_text(json.dumps({"123": {"app_name": "App", "app_description": "Desc"}}))
|
| 15 |
+
cache = gpt4v.load_app_metadata_cache(cache_path)
|
| 16 |
+
|
| 17 |
+
self.assertEqual(gpt4v.get_steam_app_data("123", "123_4.jpg", cache), ("App", "Desc"))
|
| 18 |
+
with self.assertRaises(KeyError):
|
| 19 |
+
gpt4v.get_steam_app_data("456", "456_1.jpg", cache)
|
| 20 |
+
|
| 21 |
+
def test_process_image_uses_selected_openrouter_profile_and_real_mime_type(self):
|
| 22 |
+
client = mock.Mock()
|
| 23 |
+
client.complete_json.return_value = {"objects": {"button": "round red"}}
|
| 24 |
+
|
| 25 |
+
with tempfile.TemporaryDirectory() as tmpdir:
|
| 26 |
+
image_path = Path(tmpdir) / "123_4.png"
|
| 27 |
+
image_path.write_bytes(b"not-decoded-by-the-adapter")
|
| 28 |
+
with mock.patch.object(
|
| 29 |
+
gpt4v,
|
| 30 |
+
"get_steam_app_data",
|
| 31 |
+
return_value=("Test App", "Test description"),
|
| 32 |
+
), mock.patch.object(
|
| 33 |
+
gpt4v,
|
| 34 |
+
"OpenAICompatibleChatClient",
|
| 35 |
+
return_value=client,
|
| 36 |
+
) as client_class:
|
| 37 |
+
result = gpt4v.process_image(
|
| 38 |
+
"default",
|
| 39 |
+
"describe",
|
| 40 |
+
str(image_path),
|
| 41 |
+
False,
|
| 42 |
+
0,
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
self.assertEqual(result["objects"]["button"], "round red")
|
| 46 |
+
self.assertEqual(client_class.call_args.args[0].provider, "openrouter")
|
| 47 |
+
encoded_image = client.complete_json.call_args.args[1][0]
|
| 48 |
+
self.assertEqual(encoded_image.media_type, "image/png")
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
if __name__ == "__main__":
|
| 52 |
+
unittest.main()
|
util/llm_gemini.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import google.generativeai as genai
|
| 3 |
+
|
| 4 |
+
API_KEY = os.environ.get("GOOGLE_API_KEY", "[GOOGLE_API_KEY]")
|
| 5 |
+
|
| 6 |
+
genai.configure(api_key=API_KEY)
|
| 7 |
+
model = genai.GenerativeModel('gemini-pro')
|
| 8 |
+
|
| 9 |
+
def generate_content(prompt, _place_holder = None):
|
| 10 |
+
response = model.generate_content(prompt)
|
| 11 |
+
return response.text
|
| 12 |
+
|
| 13 |
+
if __name__ == "__main__":
|
| 14 |
+
print(generate_content("What are you?"))
|
util/llm_third_party.py
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import requests
|
| 3 |
+
import json
|
| 4 |
+
import llm_gemini
|
| 5 |
+
|
| 6 |
+
AVAILABLE_MODELS = [
|
| 7 |
+
"claude-3-haiku",
|
| 8 |
+
"claude-3-sonnet",
|
| 9 |
+
"claude-3-opus",
|
| 10 |
+
"gemini-pro",
|
| 11 |
+
"gpt-3.5-turbo",
|
| 12 |
+
"gpt-4-turbo",
|
| 13 |
+
"gpt-4",
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
MODEL = "claude-3-haiku"
|
| 17 |
+
|
| 18 |
+
URL = "https://api.theb.ai/v1/chat/completions"
|
| 19 |
+
# URL = "https://api.baizhi.ai/v1/chat/completions"
|
| 20 |
+
API_KEY = os.environ.get("THEB_API_KEY", "[THEB_API_KEY]")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def generate_content(prompt, model=MODEL, api_key=API_KEY):
|
| 24 |
+
assert model in AVAILABLE_MODELS, f"Model {model} not available"
|
| 25 |
+
if model == "gemini-pro":
|
| 26 |
+
return llm_gemini.generate_content(prompt)
|
| 27 |
+
payload = json.dumps({
|
| 28 |
+
"model": model,
|
| 29 |
+
"messages": [
|
| 30 |
+
{
|
| 31 |
+
"role": "user",
|
| 32 |
+
"content": prompt
|
| 33 |
+
}
|
| 34 |
+
],
|
| 35 |
+
"stream": False
|
| 36 |
+
})
|
| 37 |
+
headers = {
|
| 38 |
+
'Authorization': f'Bearer {api_key}',
|
| 39 |
+
'Content-Type': 'application/json'
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
response = requests.request("POST", URL, headers=headers, data=payload, timeout=10)
|
| 43 |
+
if response.status_code != 200:
|
| 44 |
+
retry_count = 0
|
| 45 |
+
while retry_count < 5:
|
| 46 |
+
response = requests.request("POST", URL, headers=headers, data=payload, timeout=10)
|
| 47 |
+
if response.status_code == 200:
|
| 48 |
+
break
|
| 49 |
+
else:
|
| 50 |
+
retry_count += 1
|
| 51 |
+
if retry_count == 5:
|
| 52 |
+
raise Exception(f"Failed to get completion from LLM. Status code: {response.status_code}. Response: {response.text}")
|
| 53 |
+
|
| 54 |
+
# print(response)
|
| 55 |
+
|
| 56 |
+
return response.json()['choices'][0]['message']['content']
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
if __name__ == "__main__":
|
| 60 |
+
print(generate_content("What are you?", "claude-3-haiku"))
|