Xinjie-Q commited on
Commit
12acbba
·
verified ·
1 Parent(s): f3afe21

Upload Mage-VL: unified codec-native streaming VLM (image+video understanding + proactive gate)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +6 -0
  2. README.md +346 -0
  3. added_tokens.json +24 -0
  4. assets/mage-vl-cover.png +3 -0
  5. assets/mage-vl-framework.png +3 -0
  6. chat_template.jinja +7 -0
  7. codec_video_processing_mage_vl.py +591 -0
  8. config.json +118 -0
  9. configuration_mage_vl.py +110 -0
  10. examples/dog.jpg +3 -0
  11. examples/soccer-broadcast.mp4 +3 -0
  12. generation_config.json +6 -0
  13. inference.py +160 -0
  14. merges.txt +0 -0
  15. model-00001-of-00002.safetensors +3 -0
  16. model-00002-of-00002.safetensors +3 -0
  17. model.safetensors.index.json +703 -0
  18. modeling_mage_vl.py +1709 -0
  19. neural_codec/DCVC/LICENSE.txt +21 -0
  20. neural_codec/DCVC/NOTICE.txt +258 -0
  21. neural_codec/DCVC/src/cpp/py_rans/py_rans.cpp +393 -0
  22. neural_codec/DCVC/src/cpp/py_rans/py_rans.h +71 -0
  23. neural_codec/DCVC/src/cpp/py_rans/rans.cpp +534 -0
  24. neural_codec/DCVC/src/cpp/py_rans/rans.h +201 -0
  25. neural_codec/DCVC/src/cpp/py_rans/rans_byte.h +141 -0
  26. neural_codec/DCVC/src/cpp/setup.py +31 -0
  27. neural_codec/DCVC/src/layers/cuda_inference.py +208 -0
  28. neural_codec/DCVC/src/layers/extensions/inference/bind.cpp +36 -0
  29. neural_codec/DCVC/src/layers/extensions/inference/common.h +352 -0
  30. neural_codec/DCVC/src/layers/extensions/inference/def.h +107 -0
  31. neural_codec/DCVC/src/layers/extensions/inference/impl.cpp +167 -0
  32. neural_codec/DCVC/src/layers/extensions/inference/kernel.cu +1150 -0
  33. neural_codec/DCVC/src/layers/extensions/inference/setup.py +39 -0
  34. neural_codec/DCVC/src/layers/layers.py +156 -0
  35. neural_codec/DCVC/src/models/common_model.py +296 -0
  36. neural_codec/DCVC/src/models/entropy_models.py +341 -0
  37. neural_codec/DCVC/src/models/image_model.py +209 -0
  38. neural_codec/DCVC/src/models/video_model.py +379 -0
  39. neural_codec/DCVC/src/utils/common.py +177 -0
  40. neural_codec/DCVC/src/utils/metrics.py +96 -0
  41. neural_codec/DCVC/src/utils/stream_helper.py +217 -0
  42. neural_codec/DCVC/src/utils/transforms.py +63 -0
  43. neural_codec/DCVC/src/utils/video_reader.py +90 -0
  44. neural_codec/DCVC/src/utils/video_writer.py +52 -0
  45. neural_codec/README.md +40 -0
  46. neural_codec/canvas_assembler.py +217 -0
  47. neural_codec/codec_dcvc_config.py +65 -0
  48. neural_codec/codec_loader.py +109 -0
  49. neural_codec/codec_tools/__init__.py +0 -0
  50. neural_codec/codec_tools/codec_patch_gop/__init__.py +11 -0
.gitattributes CHANGED
@@ -33,3 +33,9 @@ saved_model/**/* 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
 
 
 
 
 
 
 
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
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
37
+ neural_codec/codec_tools/pipeline/__pycache__/generate_codec_patch_smart_resize.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
38
+ examples/dog.jpg filter=lfs diff=lfs merge=lfs -text
39
+ examples/soccer-broadcast.mp4 filter=lfs diff=lfs merge=lfs -text
40
+ assets/mage-vl-cover.png filter=lfs diff=lfs merge=lfs -text
41
+ assets/mage-vl-framework.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,3 +1,349 @@
1
  ---
2
  license: apache-2.0
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
+ library_name: transformers
4
+ pipeline_tag: image-text-to-text
5
+ tags:
6
+ - multimodal
7
+ - vision-language-model
8
+ - mage-vl
9
+ - video-understanding
10
+ - streaming
11
  ---
12
+
13
+ <h1 align="center">Mage-VL<br><span style="font-size: 0.55em; font-weight: normal;">An Efficient Codec-Native Streaming Multimodal Foundation Model</span></h1>
14
+
15
+ <p align="center">
16
+ <a href="https://microsoft.github.io/Mage"><img alt="Project Page" src="https://img.shields.io/badge/%F0%9F%8C%90-Project%20Page-blue" height="22" /></a>
17
+ <a href="https://github.com/microsoft/Mage/blob/main/assets/mage_vl_tech_report.pdf"><img alt="arXiv" src="https://img.shields.io/badge/arXiv-Mage--VL-b31b1b" height="22" /></a>
18
+ <a href="https://github.com/microsoft/Mage"><img src="https://img.shields.io/badge/Code-GitHub-181717?logo=github" alt="GitHub" height="22"></a>
19
+ <a href="https://huggingface.co/microsoft/Mage-VL"><img alt="Mage-VL" src="https://img.shields.io/badge/%F0%9F%A4%97-Mage--VL-yellow" height="22" /></a>
20
+ <a href="https://huggingface.co/microsoft/Mage-ViT"><img alt="Mage-ViT" src="https://img.shields.io/badge/%F0%9F%A4%97-Mage--ViT-yellow" height="22" /></a>
21
+ <a href="https://www.apache.org/licenses/LICENSE-2.0"><img src="https://img.shields.io/badge/License-Apache%202.0-green" alt="License: Apache 2.0" height="22"></a>
22
+ </p>
23
+
24
+ <div align="center">
25
+ <img src="assets/mage-vl-cover.png" width="100%" alt="Mage-VL">
26
+ </div>
27
+
28
+ ---
29
+
30
+ **Mage-VL** is a **codec-native, proactive-streaming multimodal foundation model** for image and video understanding, whose visual encoder is trained **entirely from scratch** at a compact **4B** scale. It targets a modern *Moravec's paradox* of VLMs — strong at complex offline reasoning, yet slow and compute-heavy on simple real-time streaming perception. Instead of decoding video into uniformly-sampled frames and pushing a dense grid of patch tokens through a frozen web-pretrained ViT, Mage-VL follows the structure of modern video codecs: it separates a stream into **anchor (I) frames** and **predicted (P) frames**, keeps every anchor patch, and retains only the predicted-frame patches where the codec spends bits — the regions carrying real motion and new detail. This codec-aligned sparsity cuts visual tokens by **over 75%** while preserving spatio-temporal context, yielding **up to 3.5× wall-clock inference speedup** over uniform frame sampling.
31
+
32
+ The system pairs **two components**:
33
+
34
+ - **Mage-ViT** — a from-scratch *Codec-ViT* visual encoder that allocates tokens by codec-derived spatio-temporal importance, on a shared `16×16` patch grid with 3D rotary position encoding. It is **codec-agnostic**: the same interface accepts a traditional codec (H.264/AVC, HEVC/H.265) via motion vectors + residual energy, or a neural codec (DCVC-RT) via its learned rate map — no architecture or retraining change.
35
+ - **Qwen3-4B causal decoder** — a Qwen3-4B-Instruct-2507 language backbone (the only pretrained component) that consumes Mage-ViT's variable-length token stream through a lightweight two-layer MLP projector, with a unified interface for images, short/long/ultra-long video, and streaming.
36
+
37
+ On top of this pair, a **System 1 & System 2 dual-process design** adds proactive streaming inside a single model: a lightweight **cognition gate** (System 1) watches each rolling codec window and stays silent on routine content, invoking the full VLM (System 2) only when a response-worthy event completes — no multi-agent pipeline required.
38
+
39
+ ## ✨ Highlights
40
+
41
+ - **Codec-native & from scratch.** The entire visual stack is trained from scratch — no billion-scale image-text ViT initialization. The bio-inspired predictive-patch mechanism (I/P frames at `16×16`) cuts visual-token consumption by **over 75%** (**~1/8 or less** of dense frame sampling), letting the model train on videos **8× longer** under the same budget.
42
+ - **Codec-native speedup.** Codec tokenization sets a superior accuracy–efficiency frontier — **up to 3.5× wall-clock inference speedup** over uniform frame sampling at matched accuracy, and the fastest of all compared models on most video benchmarks (single 8×B200 node).
43
+ - **Data-efficient tokenizer.** Trained on only **~100M unlabeled images/videos**, Mage-ViT matches or beats frontier encoders trained on billions of image-text pairs (SigLIP2 @ 10B, MoonViT @ 2B) — e.g. **99.33% on CIFAR-10** and **85.69% on ImageNet** with 256 tokens, showing web-scale pretraining is *not* essential for a strong VLM front-end.
44
+ - **Native-resolution scaling.** Variable-resolution pretraining lets Mage-ViT improve *monotonically* with the token budget (peaking **>96.1% Food-101 / >86.3% ImageNet** at 676 tokens) where fixed-resolution encoders saturate or degrade.
45
+ - **Matched-LLM video gains.** With the 4B Qwen3 backbone held fixed and only the ViT swapped, Mage-VL improves over Qwen3-VL-4B on **every** reported video and temporal-grounding benchmark — largest on localization-heavy tasks (**+22.5 QVHighlight**, +17.1 ActivityNet, +11.0 VSI-Bench, +24.5 VideoEval-Pro).
46
+ - **Strong for its size.** On par with Qwen3-VL-4B on static images, and clearly ahead on video understanding and spatial intelligence (**+11.0** VSI-Bench, **+53.1** CrossPoint, **+5.2** EmbSpatial, **+22.5** QVHighlight).
47
+ - **Proactive streaming, single model.** A frozen-backbone cognition gate delivers low-latency, event-gated commentary; it tops **TimVal / F1 / ROC-AUC / PR-AUC** on SoccerNet streaming and generalizes to real 2026 World Cup broadcasts.
48
+
49
+ ## 📥 Model
50
+
51
+ A **single checkpoint**, `microsoft/Mage-VL`, is one unified model that **simultaneously** provides image & video understanding **and** the proactive streaming gate — the same weights answer offline image/video questions and drive event-gated commentary. It covers every Mage-VL capability: image understanding, frame-sampled video, traditional H.264/HEVC codec video, neural DCVC-RT codec video, and event-gated streaming. The repository bundles the codec processor, the neural codec package, and the proactive gate weights — no separate understanding, NVC, or streaming checkpoint is required.
52
+
53
+ We additionally release **`microsoft/Mage-ViT`** — the standalone visual encoder from the two-stage, from-scratch ViT pre-training (cluster-discrimination on ~100M unlabeled image/video frames). This is the **ViT-pre-trained checkpoint only**: it has **not** gone through the joint VLM training with the language model. Use it as a data-efficient, codec-native visual encoder or as a drop-in ViT for your own multimodal training.
54
+
55
+ | Model | Task | Backbone | Hugging Face |
56
+ | :--- | :--- | :--- | :--- |
57
+ | `Mage-VL` | image & video understanding **+** proactive streaming gate | Mage-ViT + Qwen3-4B-Instruct-2507 | [🤗 microsoft/Mage-VL](https://huggingface.co/microsoft/Mage-VL) |
58
+ | `Mage-ViT` | codec-native visual encoder — ViT pre-training only, no VLM joint training | Codec-ViT (from scratch) | [🤗 microsoft/Mage-ViT](https://huggingface.co/microsoft/Mage-ViT) |
59
+
60
+ ## 🏗️ Architecture
61
+
62
+ <div align="center">
63
+ <img src="assets/mage-vl-framework.png" width="100%" alt="Mage-VL proactive streaming framework">
64
+ <em>Proactive streaming framework — Mage-ViT incrementally encodes the continuous stream into codec-native visual features shared by the event gate and the causal decoder. The gate scores each rolling window and stays silent on routine content; when it opens, the decoder emits an event-conditioned response.</em>
65
+ </div>
66
+
67
+ **Mage-ViT** — a from-scratch Codec-ViT visual encoder. On a `16×16` patch grid it keeps all anchor (I) frame patches and only the motion-salient predicted (P) frame patches, cutting visual tokens by over 75% while a shared 3D RoPE preserves spatio-temporal positions.
68
+
69
+ **Mage-VL** — a unified model where projected visual tokens and text tokens share one causal Qwen3 decoder. Still images become a single spatial block; videos become temporally-ordered codec windows. In **streaming** mode, a lightweight **cognition gate** predicts `p_speak = g(h_t)` per rolling window (over a recurrent streaming memory kept by an event-preserving feature extractor) and triggers generation when `p_speak ≥ τ`; the response is decoded by the frozen base model from a local sliding window of the most recent codec segments, and a text query can be injected at any time.
70
+
71
+ **Training** — a progressive **five-stage** supervised curriculum (no preference/RL post-training) that produces one unified model:
72
+
73
+ 1. **Multimodal alignment via captions** — ~350M dense image captions + 4.2M short-video captions.
74
+ 2. **Instruction tuning + short temporal grounding** — ~54M image-instruction samples + 3.4M 30–180s video captions.
75
+ 3. **Temporal-horizon expansion** — medium/long video (LLaVA-Video, TimeLens, VideoChat-Flash, Molmo2) with retained image SFT.
76
+ 4. **Codec-native long-context adaptation** — 350K long videos as rolling codec windows (up to 384/768 frames).
77
+ 5. **Proactive streaming alignment** — a cognition gate fine-tuned on ~3.3M streaming samples with the visual encoder and LLM kept frozen (only the gate is trained).
78
+
79
+ The five stages together produce a **single unified model**, `Mage-VL`, that handles image understanding, offline video reasoning, and proactive streaming — no separate variants are shipped.
80
+
81
+ Two parts of the pipeline apply an **AI4AI** (AI-for-AI) paradigm: (1) dense recaptioning runs through an agentic closed loop where a GPT-5 rubric scorer grades captions and a Copilot coding agent co-designs the prompt *and* harness code (e.g. rendering timestamp overlays) under a human validation gate — improving every downstream OCR/doc/chart/perception benchmark and inspiring SkillOpt-Lite; and (2) Stage-3 uses AI-based diagnostics to decide which video categories, resolutions, and frame counts to train on.
82
+
83
+ ## 📊 Performance
84
+
85
+ <details>
86
+ <summary><b>Image understanding & spatial intelligence — click to expand</b></summary>
87
+
88
+ Performance comparison across models. Mage-VL-4B and Qwen3-VL-4B use the same 4B Qwen3 LLM backbone; Phi-4-Multimodal-Instruct (Phi-4-MM, 5.6B) and Phi-4-Reasoning-Vision (Phi-4-R-V, 15B) are reported for reference. `–` = not run. **Bold** = best in row.
89
+
90
+ | Benchmark | Mage-VL-4B | Qwen3-VL-4B | Phi-4-MM-5.6B | Phi-4-R-V-15B |
91
+ | :--- | :---: | :---: | :---: | :---: |
92
+ | *Document understanding* | | | | |
93
+ | DocVQA-val | **95.14** | 94.69 | 92.79 | 76.20 |
94
+ | InfoVQA-val | **80.33** | 79.50 | 71.84 | 55.41 |
95
+ | AI2D w/ Mask | **83.16** | 81.54 | 81.83 | 82.87 |
96
+ | ChartQA | **84.88** | 83.96 | 83.76 | 83.40 |
97
+ | OCRBench | **81.80** | 81.60 | 81.70 | 73.90 |
98
+ | MultiDocVQA-val | **87.46** | 87.21 | 46.84 | 58.35 |
99
+ | ChartQAPro | **32.57** | 26.79 | 0.13 | 25.38 |
100
+ | TextVQA-val | 77.28 | **80.55** | 39.93 | 76.06 |
101
+ | CC-OCR Doc | 32.25 | **39.69** | 4.99 | 17.65 |
102
+ | *General VQA* | | | | |
103
+ | MMBench-EN-dev | 84.02 | 83.25 | 65.81 | **84.19** |
104
+ | MMBench-CN-dev | **82.04** | 80.58 | 75.17 | 79.47 |
105
+ | MMStar | **67.32** | 62.04 | 61.24 | 59.63 |
106
+ | MME-Perception | **1709.54** | 1703.50 | 1409.66 | 1590.21 |
107
+ | SeedBench (All) | **76.78** | 75.65 | 68.28 | 73.70 |
108
+ | CV-Bench | **87.79** | 85.37 | 57.09 | 81.31 |
109
+ | MME-RealWorld | **66.52** | 63.20 | 32.45 | 57.80 |
110
+ | *Spatial intelligence* | | | | |
111
+ | CV-Bench-2D | **82.13** | 81.00 | 56.12 | 80.11 |
112
+ | CV-Bench-3D | **94.75** | 92.30 | 56.92 | 82.50 |
113
+ | BLINK | **65.11** | 65.10 | 35.24 | 57.80 |
114
+ | EmbSpatial | **82.67** | 77.50 | 41.51 | 72.67 |
115
+ | CrossPoint | **80.00** | 26.90 | 12.20 | 47.73 |
116
+ | CRPE-Relation | 76.12 | **77.70** | 34.60 | 74.46 |
117
+ | SAT | 67.33 | **69.30** | 55.33 | 66.67 |
118
+
119
+ </details>
120
+
121
+ <details>
122
+ <summary><b>Video understanding & temporal grounding — click to expand</b></summary>
123
+
124
+ **Bold** = best in row.
125
+
126
+ | Benchmark | Mage-VL-4B | Qwen3-VL-4B | Phi-4-MM-5.6B | Phi-4-R-V-15B |
127
+ | :--- | :---: | :---: | :---: | :---: |
128
+ | *Video QA* | | | | |
129
+ | MV-Bench | 65.1 | **66.7** | 44.9 | 49.2 |
130
+ | NextQA | **83.1** | 79.8 | 54.1 | 69.0 |
131
+ | VideoMME | **64.0** | 59.7 | 44.7 | 55.3 |
132
+ | LongVideoBench | **61.3** | 57.7 | 41.14 | 51.2 |
133
+ | LVBench | **41.8** | 39.2 | 25.31 | 34.4 |
134
+ | MLVU-dev | **68.7** | 61.5 | 44.18 | 51.8 |
135
+ | VideoEval-Pro | **45.2** | 20.7 | 14.35 | 16.8 |
136
+ | *Temporal grounding* | | | | |
137
+ | Timelens-Charades | **50.7** | 43.1 | 4.09 | 20.6 |
138
+ | Timelens-ActivityNet | **45.4** | 28.4 | 2.03 | 23.0 |
139
+ | Timelens-QVHighlight | **57.4** | 34.9 | 2.47 | 11.6 |
140
+ | *Spatial reasoning* | | | | |
141
+ | VSI-Bench | **64.3** | 53.3 | 24.09 | 25.5 |
142
+ | *Tracking (J&F)* | | | | |
143
+ | Ref-DAVIS17 | **25.83** | 7.48 | 3.14 | 2.15 |
144
+ | MeViS-ValidU | **22.55** | 3.16 | 10.28 | 1.53 |
145
+ | ReasonVOS | **17.76** | 9.66 | 9.50 | 9.77 |
146
+ | Ref-YT-VOS | **25.57** | 5.28 | 8.64 | 3.85 |
147
+
148
+ </details>
149
+
150
+ <details>
151
+ <summary><b>Proactive streaming (SoccerNet) & online video (OVO-Bench) — click to expand</b></summary>
152
+
153
+ **SoccerNet — response timing** (StreamMind protocol, codec-native inputs, zero-tolerance canvas matching). **Bold** = best in column.
154
+
155
+ | Method | TriggerAcc | TimVal | F1 | ROC-AUC | PR-AUC |
156
+ | :--- | :---: | :---: | :---: | :---: | :---: |
157
+ | StreamMind | 52.18 | 47.36 | – | – | – |
158
+ | JoyAI-VL-Interaction-9B | **97.98** | 19.25 | 3.55 | 56.26 | 1.68 |
159
+ | **Mage-VL-4B** | 79.21 | **55.54** | **16.35** | **83.14** | **9.30** |
160
+
161
+ JoyAI's high TriggerAcc comes from predicting silence almost everywhere under SoccerNet's heavy class imbalance, so it collapses on the precision-sensitive metrics; StreamMind is trained *in-distribution* on SoccerNet, whereas Mage-VL is not.
162
+
163
+ **OVO-Bench** — online video understanding (SimpleStream recent-window protocol, 4 frames @ 1 fps; no streaming-specific fine-tuning). Mage-VL sets a new state-of-the-art overall score **among streaming architectures**. RT-Avg / BT-Avg are the Real-Time Visual Perception / Backward Tracing sub-task averages; Overall is their mean. **Bold** = best model per column (Human is the reference upper bound).
164
+
165
+ | Model | #Frames | RT-Avg | BT-Avg | Overall |
166
+ | :--- | :---: | :---: | :---: | :---: |
167
+ | Human | – | 93.2 | 92.3 | 92.77 |
168
+ | *Offline video LLMs* | | | | |
169
+ | Qwen2.5-VL-7B | 1 fps | 59.9 | 44.7 | 52.28 |
170
+ | LLaVA-Video-7B | 64 | 63.5 | 40.4 | 51.95 |
171
+ | Qwen3-VL-4B | 64 | 72.8 | **53.1** | 63.00 |
172
+ | *Online / streaming video LLMs* | | | | |
173
+ | VideoLLM-online-8B | 2 fps | 20.8 | 17.7 | 19.26 |
174
+ | Flash-VStream-7B | 1 fps | 28.4 | 27.4 | 27.90 |
175
+ | Dispider-7B | 1 fps | 54.6 | 36.1 | 45.35 |
176
+ | TimeChat-Online-7B | 1 fps | 61.9 | 41.7 | 51.80 |
177
+ | StreamForest-7B | 1 fps | 61.2 | 52.0 | 56.60 |
178
+ | Streamo-7B | 1 fps | 66.0 | 46.1 | 56.05 |
179
+ | HERMES-7B<sup>†</sup> | 1 fps | 69.0 | 49.4 | 59.20 |
180
+ | JoyAI-VL-Interaction-9B | 1 fps | 68.4 | 48.6 | 58.50 |
181
+ | **Mage-VL-4B** | 1 fps | **79.84** | 48.15 | **64.00** |
182
+
183
+ <sub><sup>†</sup> HERMES = Qwen2.5-VL-7B + HERMES (4K tokens). Baseline results and table structure follow SimpleStream.</sub>
184
+
185
+ </details>
186
+
187
+ ## 🔬 Key Findings
188
+
189
+ Beyond the model, the report distills **seven empirical findings** for efficient multimodal training:
190
+
191
+ 1. **Web-scale pretraining is not essential.** A from-scratch backbone on ~100M unlabeled frames matches encoders trained on billions of image-text pairs.
192
+ 2. **Variable-resolution pretraining scales monotonically.** Quality keeps improving with the visual-token budget instead of saturating/degrading like fixed-resolution encoders.
193
+ 3. **Codec-native tokenization sets a better accuracy–efficiency frontier** — up to **3.5× wall-clock inference speedup** over uniform frame sampling.
194
+ 4. **Explicit VideoQA SFT is redundant.** Dense video *captions* + standard image SFT are sufficient for strong zero-shot VideoQA.
195
+ 5. **Motion–spatial synergy.** Dynamic video training substantially improves static 2D/3D spatial reasoning.
196
+ 6. **AI4AI data pipeline.** Agentic closed-loop feedback + prompt/code co-design systematically lift caption quality and downstream scores (inspired SkillOpt-Lite).
197
+ 7. **Zero-Vision SFT for multimodal RL.** Bypassing visual SFT in favor of pure-text reasoning SFT unlocks stronger multimodal RL — a compute-efficient path.
198
+
199
+ ## 🚀 Quick Start
200
+
201
+ A single checkpoint, `microsoft/Mage-VL`, covers every capability below.
202
+
203
+ | Capability | Script | How to run |
204
+ |---|---|---|
205
+ | Image understanding | `inference.py` | `--mode offline --image` |
206
+ | Frame-sampled video | `inference.py` | `--mode offline --video --video-backend frames` |
207
+ | Traditional H.264/HEVC codec video | `inference.py` | `--mode offline --video --video-backend codec --codec-engine traditional` |
208
+ | Neural DCVC-RT codec video | `inference.py` | `--mode offline --video --video-backend codec --codec-engine neural` |
209
+ | Online image / video (SGLang) | `inference.py` | `--mode online … --base-url <server>` |
210
+ | Event-gated streaming commentary | `inference_streaming.py` | in the [GitHub repo](https://github.com/microsoft/Mage/tree/main/mage_vl) |
211
+
212
+ ### Installation
213
+
214
+ For offline Transformers inference:
215
+
216
+ ```bash
217
+ pip install "transformers>=5.7" accelerate pillow torch torchvision \
218
+ opencv-python codec-video-prep
219
+ ```
220
+
221
+ Codec-based video inference also requires `ffmpeg` and `ffprobe` on `PATH`.
222
+
223
+ ### Examples
224
+
225
+ Two sample inputs ship with this repository:
226
+
227
+ | Input | Question | Content |
228
+ |---|---|---|
229
+ | [`examples/dog.jpg`](examples/dog.jpg) | Describe this image in detail. | Photo of a dog sitting in front of a patterned rug |
230
+ | [`examples/soccer-broadcast.mp4`](examples/soccer-broadcast.mp4) | Describe this video. | 30s, 960×540 football broadcast clip |
231
+
232
+ ### Offline inference
233
+
234
+ Download [`inference.py`](inference.py). Offline mode loads the checkpoint with `AutoModelForCausalLM.from_pretrained` and supports images, frame sampling, and both codec engines:
235
+
236
+ ```bash
237
+ # image
238
+ python inference.py --mode offline --image examples/dog.jpg \
239
+ --question "Describe this image in detail."
240
+ ```
241
+
242
+ > The image depicts a dog sitting on a patterned rug. The dog appears to be a
243
+ > medium-sized breed with a thick, fluffy coat. Its fur is primarily white with
244
+ > patches of black and brown. The dog's ears are perked up, and it has a calm and
245
+ > attentive expression. [...]
246
+
247
+ ```bash
248
+ # video — uniform frame sampling
249
+ python inference.py --mode offline --video examples/soccer-broadcast.mp4 \
250
+ --video-backend frames --num-frames 32 \
251
+ --question "Describe this video."
252
+ ```
253
+
254
+ > The video opens with a man in a black polo shirt, sporting a short haircut,
255
+ > standing in a stadium. He is holding a yellow microphone with the BBC Sport
256
+ > logo on it. The background reveals a large crowd of spectators. [...]
257
+
258
+ ```bash
259
+ # video — traditional codec (HEVC/H.264)
260
+ python inference.py --mode offline --video examples/soccer-broadcast.mp4 \
261
+ --video-backend codec --codec-engine traditional --num-frames 32 \
262
+ --question "Describe this video."
263
+ ```
264
+
265
+ > The video opens with a BBC Sport broadcast, featuring a presenter in a black
266
+ > shirt holding a yellow microphone. The background reveals a packed stadium,
267
+ > with the scoreboard displaying "ENG 1 ARG 2 FT", indicating the final score of
268
+ > the match. [...]
269
+
270
+ ```bash
271
+ # video — neural codec (DCVC-RT)
272
+ python inference.py --mode offline --video examples/soccer-broadcast.mp4 \
273
+ --video-backend codec --codec-engine neural --num-frames 32 \
274
+ --question "Describe this video."
275
+ ```
276
+
277
+ > The video opens with a BBC Sport broadcast, featuring a presenter standing in a
278
+ > stadium filled with spectators. The presenter, dressed in a black shirt, holds
279
+ > a yellow BBC Sport microphone and wears a black earpiece. [...]
280
+
281
+ ### Online inference
282
+
283
+ Online mode talks to an OpenAI-compatible SGLang server. **First** build and launch the server with the Mage-VL SGLang branch (building it needs `protobuf-compiler` and a Rust toolchain):
284
+
285
+ ```bash
286
+ sudo apt-get update && sudo apt-get install -y protobuf-compiler
287
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
288
+ | sh -s -- -y --profile minimal --default-toolchain 1.90.0
289
+ source "$HOME/.cargo/env"
290
+
291
+ git clone -b feat/mage-vl https://github.com/kcz358/sglang
292
+ cd sglang
293
+ pip install -e 'python[all]'
294
+ python -m sglang.launch_server \
295
+ --model-path microsoft/Mage-VL \
296
+ --trust-remote-code
297
+ ```
298
+
299
+ **Then** send an image or sampled video frames to the running server:
300
+
301
+ ```bash
302
+ pip install openai
303
+
304
+ python inference.py --mode online --image examples/dog.jpg \
305
+ --question "Describe this image in detail." \
306
+ --base-url http://localhost:30000/v1
307
+
308
+ python inference.py --mode online --video examples/soccer-broadcast.mp4 \
309
+ --num-frames 32 \
310
+ --question "Describe this video." \
311
+ --base-url http://localhost:30000/v1
312
+ ```
313
+
314
+ Use `--model`, `--max-new-tokens`, and `--api-key` to override their defaults.
315
+
316
+ ### Streaming inference
317
+
318
+ `streammind_gate.safetensors` in this repository holds the event gate. Streaming inference splits a video into non-overlapping segments, stays silent on routine content, and generates a caption only when a response-worthy event is detected. Run it with `inference_streaming.py` from the [GitHub repository](https://github.com/microsoft/Mage/tree/main/mage_vl):
319
+
320
+ ```bash
321
+ python inference_streaming.py \
322
+ --video examples/soccer-broadcast.mp4 \
323
+ --video_backend codec \
324
+ --segment_sec 8
325
+ ```
326
+
327
+ ```text
328
+ [t=0.0-8.0s] gate=silence (p=0.19)
329
+ [t=8.0-16.0s] gate=response (p=0.55) -> The video features a live sports broadcast from BBC Sport, set in a large stadium filled with spectators. The broadcast focuses on a football match between England and Argentina, with the score displayed as England 1, Argentina 2. [...]
330
+ [t=16.0-24.0s] gate=response (p=0.73) -> The video features a sports broadcast set in a large stadium filled with spectators. Four commentators are gathered around a table with a 'BBC Sport' logo, each holding a yellow microphone. [...]
331
+ [t=24.0-30.0s] gate=silence (p=0.31)
332
+ ```
333
+
334
+ The gate is trained on codec inputs, so `--video_backend codec` is the intended setting. Use `--video_backend frames` for direct frame sampling. Additional controls include `--num_frames`, `--cur_fps`, `--max_segments`, `--max_new_tokens`, `--gate_threshold`, and `--attn_impl`.
335
+
336
+ ## 📝 Citation
337
+
338
+ ```bibtex
339
+ @article{mage2026magevl,
340
+ title={Mage-VL: An Efficient Codec-Native Streaming Multimodal Foundation Model},
341
+ author={Yang, Senqiao and Zhang, Kaichen and Jia, Zhaoyang and Guo, Jinghao and Shen, Yifei and Zhang, Xinjie and Zhang, Xiaoyi and Wang, Haoqing and Li, Xiao and An, Xiang and Xie, Yin and Liu, Zhening and Guo, Xun and Li, Jiahao and Zheng, Shicheng and Wang, Jinglu and Guo, Zongyu and Xie, Wenxuan and Zheng, Zihan and Luo, Yuxuan and Li, Bin and Lu, Yan},
342
+ journal={arXiv preprint},
343
+ year={2026}
344
+ }
345
+ ```
346
+
347
+ ## 📄 License
348
+
349
+ Mage-VL is released under the [Apache-2.0 License](https://www.apache.org/licenses/LICENSE-2.0).
added_tokens.json ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "</tool_call>": 151658,
3
+ "<tool_call>": 151657,
4
+ "<|box_end|>": 151649,
5
+ "<|box_start|>": 151648,
6
+ "<|endoftext|>": 151643,
7
+ "<|file_sep|>": 151664,
8
+ "<|fim_middle|>": 151660,
9
+ "<|fim_pad|>": 151662,
10
+ "<|fim_prefix|>": 151659,
11
+ "<|fim_suffix|>": 151661,
12
+ "<|im_end|>": 151645,
13
+ "<|im_start|>": 151644,
14
+ "<|image_pad|>": 151655,
15
+ "<|object_ref_end|>": 151647,
16
+ "<|object_ref_start|>": 151646,
17
+ "<|quad_end|>": 151651,
18
+ "<|quad_start|>": 151650,
19
+ "<|repo_name|>": 151663,
20
+ "<|video_pad|>": 151656,
21
+ "<|vision_end|>": 151653,
22
+ "<|vision_pad|>": 151654,
23
+ "<|vision_start|>": 151652
24
+ }
assets/mage-vl-cover.png ADDED

Git LFS Details

  • SHA256: f5a8f9a516d1d26b4798e9633fbca431b10d7065fa6f3c05df3b2a3bbbe3abff
  • Pointer size: 132 Bytes
  • Size of remote file: 2.71 MB
assets/mage-vl-framework.png ADDED

Git LFS Details

  • SHA256: 9a62ff5d41f67f420f3d32426358eee2fd56870eb07cd09a0e6f3a7fa96291f4
  • Pointer size: 132 Bytes
  • Size of remote file: 1.52 MB
chat_template.jinja ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {% set image_count = namespace(value=0) %}{% set video_count = namespace(value=0) %}{% for message in messages %}{% if loop.first and message['role'] != 'system' %}<|im_start|>system
2
+ You are a helpful assistant.<|im_end|>
3
+ {% endif %}<|im_start|>{{ message['role'] }}
4
+ {% if message['content'] is string %}{{ message['content'] }}<|im_end|>
5
+ {% else %}{% for content in message['content'] %}{% if content['type'] == 'image' or 'image' in content or 'image_url' in content %}{% set image_count.value = image_count.value + 1 %}{% if add_vision_id %}Picture {{ image_count.value }}: {% endif %}<|vision_start|><|image_pad|><|vision_end|>{% elif content['type'] == 'video' or 'video' in content %}{% set video_count.value = video_count.value + 1 %}{% if add_vision_id %}Video {{ video_count.value }}: {% endif %}<|vision_start|><|video_pad|><|vision_end|>{% elif 'text' in content %}{{ content['text'] }}{% endif %}{% endfor %}<|im_end|>
6
+ {% endif %}{% endfor %}{% if add_generation_prompt %}<|im_start|>assistant
7
+ {% endif %}
codec_video_processing_mage_vl.py ADDED
@@ -0,0 +1,591 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Codec-based video preprocessing for MageVL (trust_remote_code).
2
+
3
+ This module is the codec analogue of ``video_processing_magevl.py``.
4
+ It is invoked when a user calls::
5
+
6
+ processor(messages=..., video_backend="codec", max_pixels=...)
7
+
8
+ and is responsible for:
9
+
10
+ - Decoding the video and assembling canvas images via ``cv-preinfer``
11
+ (PyPI: ``codec-video-prep``, requires ``ffmpeg`` on PATH).
12
+ - Running the bundled ``Qwen2VLImageProcessor`` on those canvases with a
13
+ pixel budget that is *aligned* to the canvas dimensions (so the
14
+ smart_resize step never desynchronises ``image_grid_thw`` from the
15
+ codec-emitted ``src_patch_position`` array).
16
+ - Producing the per-patch ``patch_positions`` table that
17
+ ``modeling_magevl.py`` reads for the 2D-MRoPE block layout.
18
+
19
+ The result is a ``BatchFeature``-shaped dict containing the same keys that
20
+ the frame-sampling video path produces (``pixel_values`` /
21
+ ``image_grid_thw`` / ``patch_positions``), so downstream
22
+ ``modeling_magevl.py`` consumes it without changes.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import hashlib
28
+ import json
29
+ import os
30
+ import shutil
31
+ import subprocess
32
+ import sys
33
+ import tempfile
34
+ import warnings
35
+ from dataclasses import dataclass, field, fields as _dc_fields
36
+ from pathlib import Path
37
+ from typing import Optional
38
+
39
+ try:
40
+ import fcntl
41
+ except ImportError:
42
+ fcntl = None # type: ignore
43
+
44
+ import numpy as np
45
+ import torch
46
+ from PIL import Image
47
+
48
+
49
+ VISION_START = "<|vision_start|>"
50
+ VISION_END = "<|vision_end|>"
51
+ IMAGE_PAD = "<|image_pad|>"
52
+
53
+
54
+ # ----------------------------------------------------------------- config
55
+
56
+ @dataclass
57
+ class DcvcConfig:
58
+ """DCVC-RT neural-codec knobs (nested under ``CodecConfig.dcvc``).
59
+
60
+ Only used when ``CodecConfig.engine == "dcvc-rt"``. Checkpoints default to
61
+ the ``DCVC_INTRA_TAR`` / ``DCVC_INTER_TAR`` env vars (environment-specific,
62
+ so better left out of the model config).
63
+ """
64
+ qp: int = 42 # quantization (0-63); scoring only
65
+ reset_interval: int = 64 # feature-adaptor reset period (frames)
66
+ intra_period: int = -1 # -1 => single I-frame at t=0, rest P
67
+ max_side: int = 0 # >0 downscales frames before DCVC scoring;
68
+ # 0 => full-resolution scoring (matches the
69
+ # hevc baseline, which reads h264 bits at
70
+ # native stream resolution). Faithful but
71
+ # slower to encode.
72
+ seq_len_frames: int = 0 # 0 => CodecConfig.num_sampled_frames()
73
+ patch: int = 16 # MUST match image processor patch_size
74
+ canvas_token_side: Optional[int] = None # None => derive from max_pixels
75
+ # ---- readiness grouping knobs (used by the bundled process_video_bitcost_
76
+ # readiness pipeline). Defaults reproduce EXACTLY the offline/cluster DCVC
77
+ # sweep + hevc baseline generation params, so the release DCVC path yields
78
+ # the same assets we evaluated. (These intentionally differ from the release
79
+ # hevc _run_cv_preinfer knobs, e.g. max_group_frames=128 vs CodecConfig's 64.)
80
+ readiness_sum_threshold_mode: str = "auto"
81
+ readiness_coverage_bins: int = 3
82
+ readiness_delta_ratio: float = 0.05
83
+ bitcost_grid: str = "sub" # DCVC bitmap is at 16px granularity
84
+ bitcost_pct: int = 99
85
+ decode_backsearch_max: int = 16
86
+ max_group_frames: int = 128
87
+ intra_ckpt: str = field(default_factory=lambda: os.getenv("DCVC_INTRA_TAR", ""))
88
+ inter_ckpt: str = field(default_factory=lambda: os.getenv("DCVC_INTER_TAR", ""))
89
+ device: str = field(default_factory=lambda: os.getenv("DCVC_DEVICE", "cuda:0"))
90
+ pkg_dir: str = "" # dir with dcvc_rt_engine.py + dcvc_readiness_gen.py + codec_tools/
91
+
92
+
93
+ @dataclass
94
+ class CodecConfig:
95
+ """All knobs for the codec preprocessing pipeline.
96
+
97
+ ``max_pixels`` is shared with the image_processor / video_processor pixel
98
+ budget. The processor sets it from the user's ``max_pixels=`` kwarg, so
99
+ canvas size and HF smart_resize budget stay consistent.
100
+ """
101
+
102
+ target_canvas: int = 32
103
+ group_size: int = 32
104
+ images_per_group: int = 4
105
+ patch: int = 14
106
+ max_pixels: int = 150000
107
+ min_group_frames: int = 8
108
+ max_group_frames: int = 64
109
+ spatial_mask_mode: str = "off"
110
+ cache_root: Path = field(default_factory=lambda: Path(
111
+ os.getenv(
112
+ "ONLINE_CODEC_CACHE_DIR",
113
+ os.path.join(
114
+ os.getenv("HF_HOME", os.path.expanduser("~/.cache/huggingface")),
115
+ "online_codec",
116
+ ),
117
+ )
118
+ ))
119
+ timeout_seconds: int = int(os.getenv("ONLINE_CODEC_TIMEOUT", "7200"))
120
+
121
+ # ---- codec engine selection ------------------------------------------
122
+ # "hevc" : HEVC/h264 bit-cost readiness (external cv-preinfer binary; the
123
+ # default). "cv-preinfer" is accepted as a legacy alias.
124
+ # "dcvc-rt" : DCVC-RT neural-codec bit-cost readiness (self-contained, uses
125
+ # the bundled ``neural_codec/`` package + DCVC-RT checkpoints).
126
+ # Selectable per call via ``codec_config={"engine": "dcvc-rt", ...}``.
127
+ engine: str = field(default_factory=lambda: os.getenv("CODEC_ENGINE", "hevc"))
128
+
129
+ # DCVC-RT knobs, nested. In the model config / codec_config this is a plain
130
+ # dict (``"dcvc": {"qp": 21, ...}``); __post_init__ coerces it to DcvcConfig.
131
+ dcvc: "DcvcConfig" = field(default_factory=DcvcConfig)
132
+
133
+ def __post_init__(self):
134
+ if isinstance(self.dcvc, dict):
135
+ # Filter to known DcvcConfig fields — preprocessor_config.json's
136
+ # codec.dcvc also carries selection knobs (per_frame_cap_ratio,
137
+ # bottom_atten, threshold_scale, ...) read directly by the readiness
138
+ # pipeline via codec_dcvc_config, which are not DcvcConfig fields.
139
+ _known = {f.name for f in _dc_fields(DcvcConfig)}
140
+ self.dcvc = DcvcConfig(**{k: v for k, v in self.dcvc.items() if k in _known})
141
+
142
+ def validate(self) -> None:
143
+ if self.target_canvas <= 0:
144
+ raise ValueError("CodecConfig.target_canvas must be > 0")
145
+ if self.target_canvas % self.images_per_group != 0:
146
+ raise ValueError(
147
+ "CodecConfig.target_canvas must be divisible by images_per_group"
148
+ )
149
+ if self.group_size % self.images_per_group != 0:
150
+ raise ValueError(
151
+ "CodecConfig.group_size must be divisible by images_per_group"
152
+ )
153
+
154
+ def num_sampled_frames(self) -> int:
155
+ return (self.target_canvas // self.images_per_group) * self.group_size
156
+
157
+
158
+ # ---------------------------------------------------------- text/position
159
+
160
+ def _format_timestamp(seconds: float, decimals: int) -> str:
161
+ return f"<{seconds:.{decimals}f} seconds>"
162
+
163
+
164
+ def convert_positions_to_block_layout(
165
+ positions: torch.Tensor, t: int, h: int, w: int, spatial_merge_size: int = 2,
166
+ ) -> torch.Tensor:
167
+ """Reorder a (T*H*W, 3) patch position table into 2D-MRoPE block layout."""
168
+ sms = int(spatial_merge_size)
169
+ if sms == 1:
170
+ return positions
171
+ total = int(t) * int(h) * int(w)
172
+ indices = torch.arange(total, device=positions.device).view(t, h, w)
173
+ h_m, w_m = int(h) // sms, int(w) // sms
174
+ indices = (
175
+ indices.view(t, h_m, sms, w_m, sms)
176
+ .permute(0, 1, 3, 2, 4).contiguous().view(total)
177
+ )
178
+ return positions[indices]
179
+
180
+
181
+ def codec_positions_for_processor(
182
+ src_positions: np.ndarray, image_grid_thw: torch.Tensor, device: torch.device,
183
+ ) -> torch.Tensor:
184
+ positions = torch.from_numpy(src_positions).long().to(device)
185
+ expected_total = int(image_grid_thw.prod(dim=1).sum().item())
186
+ if expected_total != positions.shape[0]:
187
+ raise ValueError(
188
+ "codec patch position length mismatch: "
189
+ f"thw_total={expected_total}, positions={positions.shape[0]}"
190
+ )
191
+ chunks, offset = [], 0
192
+ for row in image_grid_thw:
193
+ t, h, w = int(row[0]), int(row[1]), int(row[2])
194
+ n = t * h * w
195
+ chunks.append(convert_positions_to_block_layout(positions[offset: offset + n], t, h, w))
196
+ offset += n
197
+ return torch.cat(chunks, dim=0)
198
+
199
+
200
+ def _timestamp_runs(
201
+ patch_positions: torch.Tensor, fps: float, decimals: int, spatial_merge_size: int = 2,
202
+ ) -> list[tuple[str, int]]:
203
+ t_values = patch_positions[:, 0]
204
+ unique_t, counts = torch.unique_consecutive(t_values, return_counts=True)
205
+ merge_factor = int(spatial_merge_size) ** 2
206
+ runs = []
207
+ for t_val, count in zip(unique_t.tolist(), counts.tolist()):
208
+ if int(t_val) < 0:
209
+ continue
210
+ token_count = int(count) // merge_factor
211
+ if token_count <= 0:
212
+ continue
213
+ runs.append((_format_timestamp(float(t_val) / float(fps), decimals), token_count))
214
+ return runs
215
+
216
+
217
+ def rewrite_text_with_codec_positions(
218
+ text: str, patch_positions: torch.Tensor, fps: float, decimals: int,
219
+ ) -> str:
220
+ """Replace the vision span in a chat-template string with codec-aware tokens."""
221
+ parts = []
222
+ for timestamp, token_count in _timestamp_runs(patch_positions, fps, decimals):
223
+ parts.extend([timestamp, VISION_START, IMAGE_PAD * token_count, VISION_END, "\n"])
224
+ vision_text = "".join(parts)
225
+ first_vs, last_ve = text.find(VISION_START), text.rfind(VISION_END)
226
+ if first_vs == -1 or last_ve == -1:
227
+ return text
228
+ tail_start = last_ve + len(VISION_END)
229
+ if tail_start < len(text) and text[tail_start] == "\n":
230
+ tail_start += 1
231
+ return text[:first_vs] + vision_text + text[tail_start:]
232
+
233
+
234
+ def drop_padding_canvases(
235
+ images: list[Image.Image], src_positions: np.ndarray,
236
+ ) -> tuple[list[Image.Image], np.ndarray, int]:
237
+ """Drop fully-padding canvases (all-negative timestamps) and their patches."""
238
+ n_canvas = len(images)
239
+ if n_canvas == 0:
240
+ return images, src_positions, 0
241
+ total_patches = src_positions.shape[0]
242
+ if total_patches % n_canvas != 0:
243
+ raise ValueError(
244
+ f"src_positions length {total_patches} not divisible by canvas count {n_canvas}"
245
+ )
246
+ ppc = total_patches // n_canvas
247
+ positions = src_positions.reshape(n_canvas, ppc, 3)
248
+ canvas_t = positions[..., 0]
249
+ keep_mask = (canvas_t >= 0).any(axis=1)
250
+ if bool((keep_mask & ~((canvas_t >= 0).all(axis=1))).any()):
251
+ raise ValueError("encountered half-padding canvas; padding is expected to be canvas-granular")
252
+ dropped = int(n_canvas - int(keep_mask.sum()))
253
+ if dropped == 0:
254
+ return images, src_positions, 0
255
+ kept_images = [img for img, keep in zip(images, keep_mask.tolist()) if keep]
256
+ kept_positions = positions[keep_mask].reshape(-1, 3)
257
+ return kept_images, kept_positions, dropped
258
+
259
+
260
+ # ------------------------------------------------------- cv-preinfer driver
261
+
262
+ def _get_video_total_frames(video_url: str) -> int:
263
+ import cv2
264
+ cap = cv2.VideoCapture(video_url)
265
+ try:
266
+ total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT) or 0)
267
+ finally:
268
+ cap.release()
269
+ return max(1, total)
270
+
271
+
272
+ def _cache_dir_for(video_url: str, cfg: CodecConfig) -> Path:
273
+ raw = (
274
+ f"{video_url}|eng={cfg.engine}|tc={cfg.target_canvas}|gs={cfg.group_size}"
275
+ f"|ipg={cfg.images_per_group}|patch={cfg.patch}"
276
+ f"|mp={cfg.max_pixels}|mask={cfg.spatial_mask_mode}"
277
+ )
278
+ if cfg.engine == "dcvc-rt":
279
+ d = cfg.dcvc
280
+ raw += (
281
+ f"|dqp={d.qp}|drst={d.reset_interval}|dip={d.intra_period}"
282
+ f"|dms={d.max_side}|dpatch={d.patch}|dseq={d.seq_len_frames}"
283
+ f"|dcts={d.canvas_token_side}"
284
+ )
285
+ # The DCVC subprocess reads the selection-tuning knobs (threshold_scale,
286
+ # bottom_atten / bottom_band, per_frame_cap_ratio, readiness_*, random_*, ...)
287
+ # straight from preprocessor_config.json's codec.dcvc — not via cfg.dcvc — so
288
+ # fold that whole block into the key. Otherwise editing a tuning knob leaves
289
+ # the key unchanged and process_codec_video returns stale cached canvases.
290
+ try:
291
+ _cfg_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),
292
+ "preprocessor_config.json")
293
+ with open(_cfg_path, "r", encoding="utf-8") as _f:
294
+ _dcvc_blk = (json.load(_f).get("codec", {}) or {}).get("dcvc", {}) or {}
295
+ raw += "|dcvcblk=" + json.dumps(_dcvc_blk, sort_keys=True, separators=(",", ":"))
296
+ except (OSError, ValueError):
297
+ pass
298
+ key = hashlib.md5(raw.encode()).hexdigest()
299
+ return cfg.cache_root / f"{Path(video_url).stem}_{key}"
300
+
301
+
302
+ def _load_codec_result(out_dir: Path) -> dict:
303
+ with open(out_dir / "meta.json", "r", encoding="utf-8") as f:
304
+ meta = json.load(f)
305
+ canvas_files = meta.get("canvas_files")
306
+ if not canvas_files:
307
+ for ext in ("npy", "jpg", "png"):
308
+ hits = sorted(p.name for p in out_dir.glob(f"canvas_*.{ext}"))
309
+ if hits:
310
+ canvas_files = hits
311
+ break
312
+ canvas_files = canvas_files or []
313
+ images = []
314
+ for name in canvas_files:
315
+ fp = out_dir / name
316
+ if name.endswith(".npy"):
317
+ images.append(Image.fromarray(np.load(fp)))
318
+ else:
319
+ images.append(Image.open(fp).convert("RGB"))
320
+ src_positions = np.load(out_dir / "src_patch_position.npy")
321
+ fps = float(meta.get("fps") or 30.0)
322
+ return {"images": images, "src_positions": src_positions, "fps": fps,
323
+ "out_dir": str(out_dir), "meta": meta}
324
+
325
+
326
+ def _run_cv_preinfer(video_url: str, out_dir: Path, cfg: CodecConfig) -> dict:
327
+ bin_name = os.environ.get("CV_PREINFER_BIN", "cv-preinfer")
328
+ if shutil.which(bin_name) is None and not os.path.isfile(bin_name):
329
+ raise RuntimeError(
330
+ f"engine='hevc' (traditional codec) needs the external '{bin_name}' binary on "
331
+ "PATH, which is not bundled with this repo. Install it and add it to PATH (or set "
332
+ "CV_PREINFER_BIN to its path), or use the neural codec (engine='dcvc-rt')."
333
+ )
334
+ tmp_dir = Path(tempfile.mkdtemp(dir=str(cfg.cache_root), prefix=f".tmp_{out_dir.name[:48]}_"))
335
+ num_sampled = min(cfg.num_sampled_frames(), _get_video_total_frames(video_url))
336
+ cmd = [
337
+ bin_name, "--video", video_url, "--out_dir", str(tmp_dir),
338
+ "--num_sampled_frames", str(num_sampled),
339
+ "--grouping_mode", "readiness",
340
+ "--group_size", str(cfg.group_size),
341
+ "--images_per_group", str(cfg.images_per_group),
342
+ "--patch", str(cfg.patch),
343
+ "--max_pixels", str(cfg.max_pixels),
344
+ "--readiness_sum_threshold", "0",
345
+ "--min_group_frames", str(cfg.min_group_frames),
346
+ "--max_group_frames", str(cfg.max_group_frames),
347
+ "--avoid_keyframes",
348
+ "--canvas_format", "jpg",
349
+ ]
350
+ try:
351
+ result = subprocess.run(cmd, text=True, capture_output=True, timeout=cfg.timeout_seconds)
352
+ if result.returncode != 0:
353
+ detail = (result.stderr or result.stdout)[-2000:]
354
+ raise RuntimeError(f"online codec failed rc={result.returncode}: {detail}")
355
+ if out_dir.exists():
356
+ shutil.rmtree(out_dir)
357
+ tmp_dir.rename(out_dir)
358
+ except Exception:
359
+ shutil.rmtree(tmp_dir, ignore_errors=True)
360
+ raise
361
+ return _load_codec_result(out_dir)
362
+
363
+
364
+ # ------------------------------------------------------- dcvc-rt driver
365
+ def _run_dcvc_rt(video_url: str, out_dir: Path, cfg: CodecConfig) -> dict:
366
+ """DCVC-RT analogue of ``_run_cv_preinfer``. Generates the codec asset dir
367
+ (canvas_*.jpg + src_patch_position.npy + meta.json) by running the SAME
368
+ readiness pipeline the hevc/offline path uses (``process_video_bitcost_
369
+ readiness``), swapping ONLY the per-frame score source from h264 block bits
370
+ to the DCVC-RT neural-codec bit-cost bitmap.
371
+
372
+ Faithful by construction: it shells out to the bundled
373
+ ``neural_codec/dcvc_readiness_gen.py`` — the exact script used for the offline /
374
+ cluster DCVC generation — with the same GEN_PARAMS, so the release
375
+ reproduces the evaluated assets. ffmpeg/ffprobe must be on PATH (same
376
+ requirement as the hevc path); the DCVC engine + checkpoints are loaded by
377
+ the subprocess.
378
+ """
379
+ d = cfg.dcvc
380
+ module_dir = os.path.dirname(os.path.abspath(__file__))
381
+ flat_pkg = os.path.join(module_dir, "neural_codec")
382
+ nested_pkg = os.path.join(os.path.dirname(module_dir), "neural_codec")
383
+ pkg = d.pkg_dir or (flat_pkg if os.path.isdir(flat_pkg) else nested_pkg)
384
+ gen = os.path.join(pkg, "dcvc_readiness_gen.py")
385
+ if not os.path.exists(gen):
386
+ raise RuntimeError(f"dcvc-rt: bundled generator not found at {gen}")
387
+ # Checkpoints default to the copies bundled in neural_codec/ (self-contained);
388
+ # DCVC_INTRA_TAR / DCVC_INTER_TAR still override.
389
+ if not d.intra_ckpt:
390
+ d.intra_ckpt = os.path.join(pkg, "dcvc_rt_intra.tar")
391
+ if not d.inter_ckpt:
392
+ d.inter_ckpt = os.path.join(pkg, "dcvc_rt_inter.tar")
393
+ if not os.path.exists(d.intra_ckpt) or not os.path.exists(d.inter_ckpt):
394
+ raise ValueError(
395
+ f"engine='dcvc-rt' checkpoints not found (intra={d.intra_ckpt!r}, "
396
+ f"inter={d.inter_ckpt!r}); they ship in neural_codec/ — or set "
397
+ "DCVC_INTRA_TAR / DCVC_INTER_TAR."
398
+ )
399
+ # The DCVC-RT source is bundled at neural_codec/DCVC and loaded by dcvc_rt_engine
400
+ # (no env var); sanity-check it so the subprocess fails fast with a clear message.
401
+ if not os.path.isdir(os.path.join(pkg, "DCVC", "src")):
402
+ raise ValueError(
403
+ f"engine='dcvc-rt' bundled DCVC source missing at "
404
+ f"{os.path.join(pkg, 'DCVC')!r} (expected a 'src/' dir); the "
405
+ "neural_codec/DCVC/ folder looks incomplete."
406
+ )
407
+
408
+ tmp_dir = Path(tempfile.mkdtemp(dir=str(cfg.cache_root), prefix=f".tmp_{out_dir.name[:48]}_"))
409
+ num_sampled = min(cfg.num_sampled_frames(), _get_video_total_frames(video_url))
410
+ # Readiness CLI args — mirror the offline/cluster GEN_PARAMS exactly so the
411
+ # generated assets match what we evaluated. Shared dims from cfg; DCVC +
412
+ # readiness knobs from cfg.dcvc (patch=16, max_group_frames=128, auto
413
+ # threshold, etc. — intentionally differing from the hevc _run_cv_preinfer).
414
+ cmd = [
415
+ sys.executable, gen,
416
+ "--video", video_url, "--out_dir", str(tmp_dir),
417
+ "--num_sampled_frames", str(num_sampled),
418
+ "--grouping_mode", "readiness",
419
+ "--readiness_sum_threshold_mode", str(d.readiness_sum_threshold_mode),
420
+ "--group_size", str(cfg.group_size),
421
+ "--images_per_group", str(cfg.images_per_group),
422
+ "--patch", str(d.patch),
423
+ "--max_pixels", str(cfg.max_pixels),
424
+ "--min_group_frames", str(cfg.min_group_frames),
425
+ "--max_group_frames", str(d.max_group_frames),
426
+ "--readiness_coverage_bins", str(d.readiness_coverage_bins),
427
+ "--readiness_delta_ratio", str(d.readiness_delta_ratio),
428
+ "--bitcost_grid", str(d.bitcost_grid),
429
+ "--bitcost_pct", str(d.bitcost_pct),
430
+ "--decode_backsearch_max", str(d.decode_backsearch_max),
431
+ "--canvas_format", "jpg",
432
+ ]
433
+ # Only DCVC engine *infra* travels via env (checkpoints / device / repo roots).
434
+ # Selection + scoring params (qp, reset_interval, intra_period, max_side, and
435
+ # the readiness knobs) are read by dcvc_readiness_gen from preprocessor_config.
436
+ # json's codec.dcvc via codec_dcvc_config — NOT from env.
437
+ env = dict(os.environ)
438
+ env.update({
439
+ "DCVC_INTRA_TAR": d.intra_ckpt, "DCVC_INTER_TAR": d.inter_ckpt,
440
+ "DCVC_DEVICE": d.device,
441
+ "DCVC_ENGINE_DIR": pkg, "DCVC_REPO_DIR": pkg,
442
+ })
443
+ try:
444
+ result = subprocess.run(cmd, text=True, capture_output=True,
445
+ timeout=cfg.timeout_seconds, env=env)
446
+ if result.returncode != 0:
447
+ detail = (result.stderr or result.stdout)[-2000:]
448
+ raise RuntimeError(f"dcvc-rt gen failed rc={result.returncode}: {detail}")
449
+ if not (tmp_dir / "meta.json").exists():
450
+ detail = (result.stderr or result.stdout)[-2000:]
451
+ raise RuntimeError(f"dcvc-rt gen produced no meta.json: {detail}")
452
+ # Tag the score source into meta so downstream can distinguish it.
453
+ try:
454
+ with open(tmp_dir / "meta.json", "r", encoding="utf-8") as f:
455
+ meta = json.load(f)
456
+ meta.update({"engine": "dcvc-rt", "score_source": "dcvc-rt",
457
+ "qp": d.qp, "reset_interval": d.reset_interval,
458
+ "dcvc_max_side": d.max_side})
459
+ with open(tmp_dir / "meta.json", "w", encoding="utf-8") as f:
460
+ json.dump(meta, f, ensure_ascii=False, indent=2)
461
+ except Exception:
462
+ pass
463
+ if out_dir.exists():
464
+ shutil.rmtree(out_dir)
465
+ tmp_dir.rename(out_dir)
466
+ except Exception:
467
+ shutil.rmtree(tmp_dir, ignore_errors=True)
468
+ raise
469
+ return _load_codec_result(out_dir)
470
+
471
+
472
+ def process_codec_video(video_url: str, cfg: CodecConfig) -> dict:
473
+ """Public entrypoint: video URL + config -> dict(images, src_positions, fps, ...).
474
+
475
+ Result is cached on disk under ``cfg.cache_root``; concurrent workers
476
+ coordinate via a flock-protected sentinel.
477
+
478
+ Soft-warning behaviour (B-mode):
479
+ - If the video has fewer frames than needed to fill ``target_canvas``,
480
+ we emit a one-time UserWarning describing the shortfall but proceed
481
+ normally (cv-preinfer will produce fewer canvases than requested).
482
+ - If the video is so short that cv-preinfer cannot form a single
483
+ group (``< min_group_frames``), we emit a clearer warning and let
484
+ cv-preinfer's own error propagate.
485
+ """
486
+ cfg.validate()
487
+ out_dir = _cache_dir_for(video_url, cfg)
488
+ if (out_dir / "meta.json").exists() and (out_dir / "src_patch_position.npy").exists():
489
+ return _load_codec_result(out_dir)
490
+
491
+ _maybe_warn_short_video(video_url, cfg)
492
+
493
+ cfg.cache_root.mkdir(parents=True, exist_ok=True)
494
+ lock_path = cfg.cache_root / f".{out_dir.name}.lock"
495
+ lock_fd = os.open(str(lock_path), os.O_CREAT | os.O_RDWR, 0o644)
496
+ try:
497
+ if fcntl is not None:
498
+ fcntl.flock(lock_fd, fcntl.LOCK_EX)
499
+ if (out_dir / "meta.json").exists() and (out_dir / "src_patch_position.npy").exists():
500
+ return _load_codec_result(out_dir)
501
+ if cfg.engine == "dcvc-rt":
502
+ return _run_dcvc_rt(video_url, out_dir, cfg)
503
+ if cfg.engine in ("hevc", "cv-preinfer"):
504
+ return _run_cv_preinfer(video_url, out_dir, cfg)
505
+ raise ValueError(
506
+ f"unknown codec engine: {cfg.engine!r} (use 'hevc' or 'dcvc-rt')"
507
+ )
508
+ finally:
509
+ try:
510
+ if fcntl is not None:
511
+ fcntl.flock(lock_fd, fcntl.LOCK_UN)
512
+ finally:
513
+ os.close(lock_fd)
514
+
515
+
516
+ def _maybe_warn_short_video(video_url: str, cfg: CodecConfig) -> None:
517
+ """Soft-warn (B-mode) when a video is too short to fill target_canvas.
518
+
519
+ Logic:
520
+ * needed_frames = num_sampled_frames() = (target_canvas/ipg)*group_size
521
+ * usable_frames = min(needed_frames, total_frames)
522
+ * expected_canv = (usable_frames // group_size) * images_per_group
523
+ If ``expected_canv < target_canvas`` we warn. If
524
+ ``total_frames < min_group_frames`` we warn more loudly (cv-preinfer
525
+ will fail downstream and that error is allowed to propagate).
526
+ """
527
+ try:
528
+ total_frames = _get_video_total_frames(video_url)
529
+ except Exception:
530
+ return # don't fail on probe errors; cv-preinfer will report its own
531
+
532
+ needed = cfg.num_sampled_frames()
533
+ usable = min(needed, total_frames)
534
+ expected_canv = (usable // cfg.group_size) * cfg.images_per_group
535
+
536
+ if total_frames < cfg.min_group_frames:
537
+ warnings.warn(
538
+ f"[codec] video {video_url!r} has only {total_frames} frames "
539
+ f"(< min_group_frames={cfg.min_group_frames}); cv-preinfer cannot "
540
+ f"form even a single group and will error out. Consider lowering "
541
+ f"min_group_frames or using video_backend='frames' for this clip.",
542
+ UserWarning,
543
+ stacklevel=2,
544
+ )
545
+ return
546
+
547
+ if expected_canv < cfg.target_canvas:
548
+ warnings.warn(
549
+ f"[codec] video {video_url!r} has {total_frames} frames; with "
550
+ f"group_size={cfg.group_size}, images_per_group={cfg.images_per_group} "
551
+ f"this yields ~{expected_canv} canvas(es) instead of the requested "
552
+ f"target_canvas={cfg.target_canvas}. Inference will proceed with the "
553
+ f"smaller canvas count.",
554
+ UserWarning,
555
+ stacklevel=2,
556
+ )
557
+
558
+
559
+ # ----------------------------------------------------- processor wiring
560
+
561
+ def codec_image_processor_outputs(
562
+ image_processor, images: list[Image.Image], max_pixels: int,
563
+ ) -> dict:
564
+ """Run ``Qwen2VLImageProcessor`` on codec canvases without smart_resize-ing.
565
+
566
+ The codec emits canvases already aligned to the patch grid. To keep
567
+ ``image_grid_thw`` consistent with ``src_patch_position``:
568
+ - ``max_pixels`` is clamped up to the largest canvas (never shrinks)
569
+ - ``min_pixels`` is clamped down to the smallest canvas (never upscales)
570
+
571
+ Without the ``min_pixels`` clamp, ``Qwen2VLImageProcessor``'s default
572
+ ``min_pixels=200704`` would grow any canvas below that threshold,
573
+ producing extra patches and a chunk/index mismatch downstream.
574
+ """
575
+ canvas_pixels = [im.width * im.height for im in images]
576
+ proc_max = max(int(max_pixels), max(canvas_pixels, default=int(max_pixels)))
577
+ proc_min = min(canvas_pixels) if canvas_pixels else 1
578
+ return image_processor(
579
+ images=images, min_pixels=proc_min, max_pixels=proc_max, return_tensors="pt",
580
+ )
581
+
582
+
583
+ __all__ = [
584
+ "CodecConfig",
585
+ "process_codec_video",
586
+ "drop_padding_canvases",
587
+ "codec_positions_for_processor",
588
+ "rewrite_text_with_codec_positions",
589
+ "codec_image_processor_outputs",
590
+ "VISION_START", "VISION_END", "IMAGE_PAD",
591
+ ]
config.json ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "auto_map": {
3
+ "AutoConfig": "configuration_mage_vl.MageVLConfig",
4
+ "AutoModel": "modeling_mage_vl.MageVLModel",
5
+ "AutoModelForImageTextToText": "modeling_mage_vl.MageVLForConditionalGeneration",
6
+ "AutoProcessor": "processing_mage_vl.MageVLProcessor",
7
+ "AutoVideoProcessor": "video_processing_mage_vl.MageVLVideoProcessor",
8
+ "AutoModelForCausalLM": "modeling_mage_vl.MageVLForConditionalGeneration"
9
+ },
10
+ "dtype": "bfloat16",
11
+ "image_token_id": 151655,
12
+ "model_type": "mage_vl",
13
+ "text_config": {
14
+ "_name_or_path": "/mage-vl/pretrain_models/Qwen3-4B-Instruct-2507",
15
+ "architectures": [
16
+ "MageVLForConditionalGeneration"
17
+ ],
18
+ "attention_bias": false,
19
+ "attention_dropout": 0.0,
20
+ "bos_token_id": 151643,
21
+ "dtype": "bfloat16",
22
+ "eos_token_id": 151645,
23
+ "head_dim": 128,
24
+ "hidden_act": "silu",
25
+ "hidden_size": 2560,
26
+ "initializer_range": 0.02,
27
+ "intermediate_size": 9728,
28
+ "layer_types": [
29
+ "full_attention",
30
+ "full_attention",
31
+ "full_attention",
32
+ "full_attention",
33
+ "full_attention",
34
+ "full_attention",
35
+ "full_attention",
36
+ "full_attention",
37
+ "full_attention",
38
+ "full_attention",
39
+ "full_attention",
40
+ "full_attention",
41
+ "full_attention",
42
+ "full_attention",
43
+ "full_attention",
44
+ "full_attention",
45
+ "full_attention",
46
+ "full_attention",
47
+ "full_attention",
48
+ "full_attention",
49
+ "full_attention",
50
+ "full_attention",
51
+ "full_attention",
52
+ "full_attention",
53
+ "full_attention",
54
+ "full_attention",
55
+ "full_attention",
56
+ "full_attention",
57
+ "full_attention",
58
+ "full_attention",
59
+ "full_attention",
60
+ "full_attention",
61
+ "full_attention",
62
+ "full_attention",
63
+ "full_attention",
64
+ "full_attention"
65
+ ],
66
+ "max_position_embeddings": 262144,
67
+ "max_window_layers": 36,
68
+ "model_type": "qwen3",
69
+ "num_attention_heads": 32,
70
+ "num_hidden_layers": 36,
71
+ "num_key_value_heads": 8,
72
+ "rms_norm_eps": 1e-06,
73
+ "rope_scaling": null,
74
+ "sliding_window": null,
75
+ "use_cache": true,
76
+ "use_sliding_window": false,
77
+ "vocab_size": 151936,
78
+ "rope_parameters": {
79
+ "rope_theta": 5000000,
80
+ "rope_type": "default"
81
+ },
82
+ "tie_word_embeddings": false
83
+ },
84
+ "transformers_version": "5.7.0",
85
+ "video_token_id": 151656,
86
+ "vision_config": {
87
+ "attention_dropout": 0.0,
88
+ "frame_windows_size": 4,
89
+ "hidden_act": "gelu",
90
+ "hidden_size": 1024,
91
+ "image_size": 448,
92
+ "initializer_range": 0.02,
93
+ "intermediate_size": 4096,
94
+ "layer_norm_eps": 1e-06,
95
+ "layer_norm_type": "layer_norm",
96
+ "model_type": "mage_vl_vision",
97
+ "num_attention_heads": 16,
98
+ "num_channels": 3,
99
+ "num_hidden_layers": 24,
100
+ "out_hidden_size": 2560,
101
+ "patch_size": 16,
102
+ "rope_theta": 10000.0,
103
+ "spatial_merge_size": 2,
104
+ "temporal_patch_size": 1,
105
+ "text_hidden_size": 2560,
106
+ "tokens_per_second": 1,
107
+ "use_head": false,
108
+ "max_position_embeddings": 8192
109
+ },
110
+ "vision_end_token_id": 151653,
111
+ "vision_start_token_id": 151652,
112
+ "architectures": [
113
+ "MageVLForConditionalGeneration"
114
+ ],
115
+ "bos_token_id": 151643,
116
+ "eos_token_id": 151645,
117
+ "tie_word_embeddings": false
118
+ }
configuration_mage_vl.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from huggingface_hub.dataclasses import strict
2
+
3
+ from transformers import CONFIG_MAPPING, AutoConfig
4
+ from transformers.configuration_utils import PreTrainedConfig
5
+
6
+
7
+ @strict
8
+ class MageVLVisionConfig(PreTrainedConfig):
9
+ model_type = "mage_vl_vision"
10
+ base_config_key = "vision_config"
11
+
12
+ hidden_size: int = 1024
13
+ intermediate_size: int = 4096
14
+ num_hidden_layers: int = 24
15
+ num_attention_heads: int = 16
16
+ num_channels: int = 3
17
+ image_size: int = 448
18
+ patch_size: int = 14
19
+ hidden_act: str = "gelu"
20
+ layer_norm_eps: float = 1e-6
21
+ layer_norm_type: str = "layer_norm"
22
+ attention_dropout: float = 0.0
23
+ initializer_range: float = 0.02
24
+ rope_theta: float = 10000.0
25
+ use_head: bool = False
26
+ out_hidden_size: int = 1024
27
+ spatial_merge_size: int = 2
28
+ tokens_per_second: int = 1
29
+ frame_windows_size: int = 4
30
+ use_patch_position_encoding: bool = False
31
+ patch_position_encoding_type: str = "absolute"
32
+ max_position_embeddings: int = 8192
33
+
34
+
35
+ @strict
36
+ class MageVLConfig(PreTrainedConfig):
37
+ r"""
38
+ This is the configuration class to store the configuration of a [`MageVLModel`]. It is used to instantiate a
39
+ MageVLModel model according to the specified arguments, defining the model architecture. Instantiating a configuration
40
+ with the defaults will yield a Mage-VL configuration.
41
+
42
+ Configuration objects inherit from [`PreTrainedConfig`] and can be used to control the model outputs. Read the
43
+ documentation from [`PreTrainedConfig`] for more information.
44
+
45
+ Args:
46
+ text_config (`Union[PreTrainedConfig, dict]`, *optional*, defaults to `Qwen3Config`):
47
+ The config object or dictionary of the text backbone.
48
+ vision_config (`Union[PreTrainedConfig, dict]`, *optional*, defaults to `MageVLVisionConfig`):
49
+ The config object or dictionary of the vision backbone.
50
+ image_token_id (`int`, *optional*, defaults to 151655):
51
+ The image token index to encode the image prompt.
52
+ video_token_id (`int`, *optional*, defaults to 151656):
53
+ The video token index to encode the image prompt.
54
+ vision_start_token_id (`int`, *optional*, defaults to 151652):
55
+ The token index to denote start of vision input.
56
+ vision_end_token_id (`int`, *optional*, defaults to 151653):
57
+ The token index to denote end of vision input.
58
+ """
59
+
60
+ model_type = "mage_vl"
61
+ # `text_config` is resolved dynamically based on its `model_type` (defaults to `qwen3`),
62
+ # so we use `AutoConfig` here as a placeholder; `__post_init__` swaps it for the
63
+ # concrete config class via `CONFIG_MAPPING`.
64
+ sub_configs = {"vision_config": MageVLVisionConfig, "text_config": AutoConfig}
65
+ keys_to_ignore_at_inference = ["past_key_values"]
66
+
67
+ text_config: dict | PreTrainedConfig | None = None
68
+ vision_config: dict | PreTrainedConfig | None = None
69
+ image_token_id: int = 151655
70
+ video_token_id: int = 151656
71
+ vision_start_token_id: int = 151652
72
+ vision_end_token_id: int = 151653
73
+ tie_word_embeddings: bool = False
74
+ # Generation-related token ids are mirrored from `text_config` in `__post_init__`
75
+ # so downstream tools (e.g. `generate`, vLLM) that read them at the top level keep working.
76
+ bos_token_id: int | None = None
77
+ eos_token_id: int | list[int] | None = None
78
+ pad_token_id: int | None = None
79
+
80
+ def __post_init__(self, **kwargs):
81
+ # Resolve vision_config
82
+ if isinstance(self.vision_config, dict):
83
+ self.vision_config = self.sub_configs["vision_config"](**self.vision_config)
84
+ elif self.vision_config is None:
85
+ self.vision_config = self.sub_configs["vision_config"]()
86
+
87
+ # Resolve text_config dynamically via CONFIG_MAPPING (defaults to qwen3)
88
+ if isinstance(self.text_config, dict):
89
+ text_model_type = self.text_config.get("model_type", "qwen3")
90
+ self.text_config["model_type"] = text_model_type
91
+ text_config_cls = CONFIG_MAPPING[text_model_type]
92
+ self.sub_configs["text_config"] = text_config_cls
93
+ self.text_config = text_config_cls(**self.text_config)
94
+ elif self.text_config is None:
95
+ text_config_cls = CONFIG_MAPPING["qwen3"]
96
+ self.sub_configs["text_config"] = text_config_cls
97
+ self.text_config = text_config_cls()
98
+
99
+ # Mirror generation-related token ids from text_config to the top level so
100
+ # downstream tools (e.g. `generate`, chat templates, vLLM) that read them
101
+ # from the top-level config keep working.
102
+ for tok_key in ("bos_token_id", "eos_token_id", "pad_token_id"):
103
+ text_val = getattr(self.text_config, tok_key, None)
104
+ if text_val is not None and getattr(self, tok_key, None) is None:
105
+ setattr(self, tok_key, text_val)
106
+
107
+ super().__post_init__(**kwargs)
108
+
109
+
110
+ __all__ = ["MageVLConfig", "MageVLVisionConfig"]
examples/dog.jpg ADDED

Git LFS Details

  • SHA256: 164d8dfe707fb854e288ad2eea65c2db87e90af11f689c85502860eeaf3f4794
  • Pointer size: 131 Bytes
  • Size of remote file: 525 kB
examples/soccer-broadcast.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9d840e86558fe5a59bb743092d161228c63c687ab53588ee476a1cd46064125a
3
+ size 3914911
generation_config.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 151643,
4
+ "eos_token_id": 151645,
5
+ "transformers_version": "4.57.3"
6
+ }
inference.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """Run Mage-VL-Base image or video inference offline or through SGLang."""
3
+
4
+ import argparse
5
+ import base64
6
+ import mimetypes
7
+ from pathlib import Path
8
+
9
+
10
+ def image_url(image: str) -> str:
11
+ if image.startswith(("http://", "https://", "data:")):
12
+ return image
13
+ path = Path(image)
14
+ mime = mimetypes.guess_type(path.name)[0] or "image/jpeg"
15
+ encoded = base64.b64encode(path.read_bytes()).decode("ascii")
16
+ return f"data:{mime};base64,{encoded}"
17
+
18
+
19
+ def sample_video(video: str, num_frames: int):
20
+ import cv2
21
+ import numpy as np
22
+ from PIL import Image
23
+
24
+ capture = cv2.VideoCapture(video)
25
+ frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
26
+ if frame_count <= 0:
27
+ capture.release()
28
+ raise ValueError(f"Could not read video: {video}")
29
+ indices = np.linspace(0, frame_count - 1, min(num_frames, frame_count), dtype=int)
30
+ frames = []
31
+ for index in indices:
32
+ capture.set(cv2.CAP_PROP_POS_FRAMES, int(index))
33
+ ok, frame = capture.read()
34
+ if not ok:
35
+ capture.release()
36
+ raise ValueError(f"Could not decode frame {index} from: {video}")
37
+ frames.append(Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)))
38
+ capture.release()
39
+ return frames
40
+
41
+
42
+ def run_offline(args):
43
+ import os
44
+ import torch
45
+ from PIL import Image
46
+ from transformers import AutoModelForCausalLM, AutoProcessor
47
+
48
+ model_path = args.model
49
+ if args.video and args.video_backend == "codec" and args.codec_engine == "neural":
50
+ if not os.path.isdir(model_path):
51
+ from huggingface_hub import snapshot_download
52
+
53
+ model_path = snapshot_download(args.model)
54
+ processor = AutoProcessor.from_pretrained(model_path, trust_remote_code=True)
55
+ model = AutoModelForCausalLM.from_pretrained(
56
+ model_path, trust_remote_code=True, torch_dtype="auto", device_map="auto"
57
+ ).eval()
58
+ media_type = "image" if args.image else "video"
59
+ messages = [{"role": "user", "content": [
60
+ {"type": media_type}, {"type": "text", "text": args.question},
61
+ ]}]
62
+ text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
63
+ if args.image:
64
+ inputs = processor(
65
+ text=[text], images=[Image.open(args.image).convert("RGB")], return_tensors="pt"
66
+ )
67
+ elif args.video_backend == "codec":
68
+ codec_config = {
69
+ "engine": "hevc" if args.codec_engine == "traditional" else "dcvc-rt",
70
+ "target_canvas": args.num_frames,
71
+ "patch": 16,
72
+ }
73
+ if args.codec_engine == "neural":
74
+ codec_config["dcvc"] = {
75
+ "pkg_dir": os.path.join(model_path, "neural_codec"),
76
+ "device": str(model.device),
77
+ }
78
+ inputs = processor(
79
+ text=[text],
80
+ videos=[args.video],
81
+ video_backend="codec",
82
+ max_pixels=args.max_pixels,
83
+ codec_config=codec_config,
84
+ return_tensors="pt",
85
+ padding=True,
86
+ )
87
+ else:
88
+ inputs = processor(
89
+ text=[text],
90
+ videos=[sample_video(args.video, args.num_frames)],
91
+ return_tensors="pt",
92
+ padding=True,
93
+ )
94
+ inputs = {k: (v.to(model.device) if hasattr(v, "to") else v) for k, v in inputs.items()}
95
+ if "pixel_values" in inputs:
96
+ inputs["pixel_values"] = inputs["pixel_values"].to(model.dtype)
97
+ with torch.inference_mode():
98
+ output = model.generate(**inputs, max_new_tokens=args.max_new_tokens, do_sample=False)
99
+ answer = processor.tokenizer.decode(
100
+ output[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True
101
+ )
102
+ print(answer.strip())
103
+
104
+
105
+ def run_online(args):
106
+ from openai import OpenAI
107
+
108
+ if args.image:
109
+ content = [{"type": "image_url", "image_url": {"url": image_url(args.image)}}]
110
+ else:
111
+ content = [
112
+ {"type": "image_url", "image_url": {"url": frame_url(frame)}}
113
+ for frame in sample_video(args.video, args.num_frames)
114
+ ]
115
+ content.append({"type": "text", "text": args.question})
116
+ client = OpenAI(base_url=args.base_url, api_key=args.api_key)
117
+ response = client.chat.completions.create(
118
+ model=args.model,
119
+ messages=[{"role": "user", "content": content}],
120
+ max_tokens=args.max_new_tokens,
121
+ )
122
+ print(response.choices[0].message.content)
123
+
124
+
125
+ def frame_url(frame) -> str:
126
+ import io
127
+
128
+ buffer = io.BytesIO()
129
+ frame.save(buffer, format="JPEG")
130
+ encoded = base64.b64encode(buffer.getvalue()).decode("ascii")
131
+ return f"data:image/jpeg;base64,{encoded}"
132
+
133
+
134
+ def main():
135
+ parser = argparse.ArgumentParser(description=__doc__)
136
+ parser.add_argument("--mode", choices=("offline", "online"), required=True)
137
+ media = parser.add_mutually_exclusive_group(required=True)
138
+ media.add_argument("--image", help="Local image path")
139
+ media.add_argument("--video", help="Local video path")
140
+ parser.add_argument("--video-backend", choices=("frames", "codec"), default="frames")
141
+ parser.add_argument(
142
+ "--codec-engine", choices=("traditional", "neural"), default="traditional"
143
+ )
144
+ parser.add_argument("--num-frames", type=int, default=32)
145
+ parser.add_argument("--max-pixels", type=int, default=150000)
146
+ parser.add_argument("--question", default="Describe this media.")
147
+ parser.add_argument("--model", default="microsoft/Mage-VL")
148
+ parser.add_argument("--max-new-tokens", type=int, default=256)
149
+ parser.add_argument("--base-url", default="http://localhost:30000/v1")
150
+ parser.add_argument("--api-key", default="EMPTY")
151
+ args = parser.parse_args()
152
+ if args.mode == "online" and args.video_backend == "codec":
153
+ parser.error("online video inference supports only --video-backend frames")
154
+ if args.num_frames <= 0:
155
+ parser.error("--num-frames must be positive")
156
+ (run_offline if args.mode == "offline" else run_online)(args)
157
+
158
+
159
+ if __name__ == "__main__":
160
+ main()
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
model-00001-of-00002.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:98fa203652a843650343732d2597f08c9b491cf4e310cd269eed34ca27ebce58
3
+ size 4967403560
model-00002-of-00002.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:719360069004f6b7b59303bd21bc1111bcd353bb0ccdebe1fe3971b87cd7b30f
3
+ size 4516272328
model.safetensors.index.json ADDED
@@ -0,0 +1,703 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "total_size": 9483587584
4
+ },
5
+ "weight_map": {
6
+ "model.language_model.embed_tokens.weight": "model-00001-of-00002.safetensors",
7
+ "model.language_model.layers.0.input_layernorm.weight": "model-00001-of-00002.safetensors",
8
+ "model.language_model.layers.0.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
9
+ "model.language_model.layers.0.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
10
+ "model.language_model.layers.0.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
11
+ "model.language_model.layers.0.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
12
+ "model.language_model.layers.0.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
13
+ "model.language_model.layers.0.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
14
+ "model.language_model.layers.0.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
15
+ "model.language_model.layers.0.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
16
+ "model.language_model.layers.0.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
17
+ "model.language_model.layers.0.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
18
+ "model.language_model.layers.1.input_layernorm.weight": "model-00001-of-00002.safetensors",
19
+ "model.language_model.layers.1.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
20
+ "model.language_model.layers.1.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
21
+ "model.language_model.layers.1.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
22
+ "model.language_model.layers.1.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
23
+ "model.language_model.layers.1.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
24
+ "model.language_model.layers.1.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
25
+ "model.language_model.layers.1.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
26
+ "model.language_model.layers.1.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
27
+ "model.language_model.layers.1.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
28
+ "model.language_model.layers.1.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
29
+ "model.language_model.layers.10.input_layernorm.weight": "model-00001-of-00002.safetensors",
30
+ "model.language_model.layers.10.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
31
+ "model.language_model.layers.10.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
32
+ "model.language_model.layers.10.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
33
+ "model.language_model.layers.10.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
34
+ "model.language_model.layers.10.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
35
+ "model.language_model.layers.10.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
36
+ "model.language_model.layers.10.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
37
+ "model.language_model.layers.10.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
38
+ "model.language_model.layers.10.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
39
+ "model.language_model.layers.10.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
40
+ "model.language_model.layers.11.input_layernorm.weight": "model-00001-of-00002.safetensors",
41
+ "model.language_model.layers.11.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
42
+ "model.language_model.layers.11.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
43
+ "model.language_model.layers.11.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
44
+ "model.language_model.layers.11.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
45
+ "model.language_model.layers.11.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
46
+ "model.language_model.layers.11.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
47
+ "model.language_model.layers.11.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
48
+ "model.language_model.layers.11.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
49
+ "model.language_model.layers.11.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
50
+ "model.language_model.layers.11.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
51
+ "model.language_model.layers.12.input_layernorm.weight": "model-00001-of-00002.safetensors",
52
+ "model.language_model.layers.12.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
53
+ "model.language_model.layers.12.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
54
+ "model.language_model.layers.12.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
55
+ "model.language_model.layers.12.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
56
+ "model.language_model.layers.12.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
57
+ "model.language_model.layers.12.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
58
+ "model.language_model.layers.12.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
59
+ "model.language_model.layers.12.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
60
+ "model.language_model.layers.12.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
61
+ "model.language_model.layers.12.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
62
+ "model.language_model.layers.13.input_layernorm.weight": "model-00001-of-00002.safetensors",
63
+ "model.language_model.layers.13.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
64
+ "model.language_model.layers.13.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
65
+ "model.language_model.layers.13.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
66
+ "model.language_model.layers.13.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
67
+ "model.language_model.layers.13.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
68
+ "model.language_model.layers.13.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
69
+ "model.language_model.layers.13.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
70
+ "model.language_model.layers.13.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
71
+ "model.language_model.layers.13.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
72
+ "model.language_model.layers.13.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
73
+ "model.language_model.layers.14.input_layernorm.weight": "model-00001-of-00002.safetensors",
74
+ "model.language_model.layers.14.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
75
+ "model.language_model.layers.14.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
76
+ "model.language_model.layers.14.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
77
+ "model.language_model.layers.14.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
78
+ "model.language_model.layers.14.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
79
+ "model.language_model.layers.14.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
80
+ "model.language_model.layers.14.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
81
+ "model.language_model.layers.14.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
82
+ "model.language_model.layers.14.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
83
+ "model.language_model.layers.14.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
84
+ "model.language_model.layers.15.input_layernorm.weight": "model-00001-of-00002.safetensors",
85
+ "model.language_model.layers.15.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
86
+ "model.language_model.layers.15.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
87
+ "model.language_model.layers.15.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
88
+ "model.language_model.layers.15.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
89
+ "model.language_model.layers.15.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
90
+ "model.language_model.layers.15.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
91
+ "model.language_model.layers.15.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
92
+ "model.language_model.layers.15.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
93
+ "model.language_model.layers.15.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
94
+ "model.language_model.layers.15.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
95
+ "model.language_model.layers.16.input_layernorm.weight": "model-00001-of-00002.safetensors",
96
+ "model.language_model.layers.16.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
97
+ "model.language_model.layers.16.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
98
+ "model.language_model.layers.16.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
99
+ "model.language_model.layers.16.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
100
+ "model.language_model.layers.16.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
101
+ "model.language_model.layers.16.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
102
+ "model.language_model.layers.16.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
103
+ "model.language_model.layers.16.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
104
+ "model.language_model.layers.16.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
105
+ "model.language_model.layers.16.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
106
+ "model.language_model.layers.17.input_layernorm.weight": "model-00001-of-00002.safetensors",
107
+ "model.language_model.layers.17.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
108
+ "model.language_model.layers.17.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
109
+ "model.language_model.layers.17.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
110
+ "model.language_model.layers.17.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
111
+ "model.language_model.layers.17.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
112
+ "model.language_model.layers.17.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
113
+ "model.language_model.layers.17.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
114
+ "model.language_model.layers.17.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
115
+ "model.language_model.layers.17.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
116
+ "model.language_model.layers.17.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
117
+ "model.language_model.layers.18.input_layernorm.weight": "model-00001-of-00002.safetensors",
118
+ "model.language_model.layers.18.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
119
+ "model.language_model.layers.18.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
120
+ "model.language_model.layers.18.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
121
+ "model.language_model.layers.18.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
122
+ "model.language_model.layers.18.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
123
+ "model.language_model.layers.18.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
124
+ "model.language_model.layers.18.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
125
+ "model.language_model.layers.18.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
126
+ "model.language_model.layers.18.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
127
+ "model.language_model.layers.18.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
128
+ "model.language_model.layers.19.input_layernorm.weight": "model-00001-of-00002.safetensors",
129
+ "model.language_model.layers.19.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
130
+ "model.language_model.layers.19.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
131
+ "model.language_model.layers.19.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
132
+ "model.language_model.layers.19.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
133
+ "model.language_model.layers.19.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
134
+ "model.language_model.layers.19.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
135
+ "model.language_model.layers.19.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
136
+ "model.language_model.layers.19.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
137
+ "model.language_model.layers.19.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
138
+ "model.language_model.layers.19.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
139
+ "model.language_model.layers.2.input_layernorm.weight": "model-00001-of-00002.safetensors",
140
+ "model.language_model.layers.2.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
141
+ "model.language_model.layers.2.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
142
+ "model.language_model.layers.2.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
143
+ "model.language_model.layers.2.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
144
+ "model.language_model.layers.2.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
145
+ "model.language_model.layers.2.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
146
+ "model.language_model.layers.2.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
147
+ "model.language_model.layers.2.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
148
+ "model.language_model.layers.2.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
149
+ "model.language_model.layers.2.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
150
+ "model.language_model.layers.20.input_layernorm.weight": "model-00001-of-00002.safetensors",
151
+ "model.language_model.layers.20.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
152
+ "model.language_model.layers.20.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
153
+ "model.language_model.layers.20.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
154
+ "model.language_model.layers.20.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
155
+ "model.language_model.layers.20.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
156
+ "model.language_model.layers.20.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
157
+ "model.language_model.layers.20.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
158
+ "model.language_model.layers.20.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
159
+ "model.language_model.layers.20.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
160
+ "model.language_model.layers.20.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
161
+ "model.language_model.layers.21.input_layernorm.weight": "model-00001-of-00002.safetensors",
162
+ "model.language_model.layers.21.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
163
+ "model.language_model.layers.21.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
164
+ "model.language_model.layers.21.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
165
+ "model.language_model.layers.22.input_layernorm.weight": "model-00001-of-00002.safetensors",
166
+ "model.language_model.layers.22.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
167
+ "model.language_model.layers.22.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
168
+ "model.language_model.layers.22.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
169
+ "model.language_model.layers.23.input_layernorm.weight": "model-00001-of-00002.safetensors",
170
+ "model.language_model.layers.23.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
171
+ "model.language_model.layers.23.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
172
+ "model.language_model.layers.23.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
173
+ "model.language_model.layers.24.input_layernorm.weight": "model-00001-of-00002.safetensors",
174
+ "model.language_model.layers.24.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
175
+ "model.language_model.layers.24.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
176
+ "model.language_model.layers.24.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
177
+ "model.language_model.layers.25.input_layernorm.weight": "model-00001-of-00002.safetensors",
178
+ "model.language_model.layers.25.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
179
+ "model.language_model.layers.25.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
180
+ "model.language_model.layers.25.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
181
+ "model.language_model.layers.26.input_layernorm.weight": "model-00001-of-00002.safetensors",
182
+ "model.language_model.layers.26.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
183
+ "model.language_model.layers.26.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
184
+ "model.language_model.layers.26.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
185
+ "model.language_model.layers.27.input_layernorm.weight": "model-00001-of-00002.safetensors",
186
+ "model.language_model.layers.27.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
187
+ "model.language_model.layers.27.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
188
+ "model.language_model.layers.27.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
189
+ "model.language_model.layers.28.input_layernorm.weight": "model-00001-of-00002.safetensors",
190
+ "model.language_model.layers.28.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
191
+ "model.language_model.layers.28.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
192
+ "model.language_model.layers.28.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
193
+ "model.language_model.layers.29.input_layernorm.weight": "model-00001-of-00002.safetensors",
194
+ "model.language_model.layers.29.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
195
+ "model.language_model.layers.29.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
196
+ "model.language_model.layers.29.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
197
+ "model.language_model.layers.3.input_layernorm.weight": "model-00001-of-00002.safetensors",
198
+ "model.language_model.layers.3.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
199
+ "model.language_model.layers.3.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
200
+ "model.language_model.layers.3.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
201
+ "model.language_model.layers.3.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
202
+ "model.language_model.layers.3.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
203
+ "model.language_model.layers.3.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
204
+ "model.language_model.layers.3.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
205
+ "model.language_model.layers.3.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
206
+ "model.language_model.layers.3.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
207
+ "model.language_model.layers.3.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
208
+ "model.language_model.layers.30.input_layernorm.weight": "model-00001-of-00002.safetensors",
209
+ "model.language_model.layers.30.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
210
+ "model.language_model.layers.30.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
211
+ "model.language_model.layers.30.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
212
+ "model.language_model.layers.31.input_layernorm.weight": "model-00001-of-00002.safetensors",
213
+ "model.language_model.layers.31.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
214
+ "model.language_model.layers.31.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
215
+ "model.language_model.layers.31.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
216
+ "model.language_model.layers.32.input_layernorm.weight": "model-00001-of-00002.safetensors",
217
+ "model.language_model.layers.32.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
218
+ "model.language_model.layers.32.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
219
+ "model.language_model.layers.32.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
220
+ "model.language_model.layers.33.input_layernorm.weight": "model-00001-of-00002.safetensors",
221
+ "model.language_model.layers.33.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
222
+ "model.language_model.layers.33.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
223
+ "model.language_model.layers.33.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
224
+ "model.language_model.layers.34.input_layernorm.weight": "model-00001-of-00002.safetensors",
225
+ "model.language_model.layers.34.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
226
+ "model.language_model.layers.34.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
227
+ "model.language_model.layers.34.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
228
+ "model.language_model.layers.35.input_layernorm.weight": "model-00001-of-00002.safetensors",
229
+ "model.language_model.layers.35.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
230
+ "model.language_model.layers.35.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
231
+ "model.language_model.layers.35.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
232
+ "model.language_model.layers.4.input_layernorm.weight": "model-00001-of-00002.safetensors",
233
+ "model.language_model.layers.4.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
234
+ "model.language_model.layers.4.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
235
+ "model.language_model.layers.4.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
236
+ "model.language_model.layers.4.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
237
+ "model.language_model.layers.4.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
238
+ "model.language_model.layers.4.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
239
+ "model.language_model.layers.4.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
240
+ "model.language_model.layers.4.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
241
+ "model.language_model.layers.4.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
242
+ "model.language_model.layers.4.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
243
+ "model.language_model.layers.5.input_layernorm.weight": "model-00001-of-00002.safetensors",
244
+ "model.language_model.layers.5.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
245
+ "model.language_model.layers.5.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
246
+ "model.language_model.layers.5.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
247
+ "model.language_model.layers.5.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
248
+ "model.language_model.layers.5.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
249
+ "model.language_model.layers.5.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
250
+ "model.language_model.layers.5.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
251
+ "model.language_model.layers.5.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
252
+ "model.language_model.layers.5.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
253
+ "model.language_model.layers.5.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
254
+ "model.language_model.layers.6.input_layernorm.weight": "model-00001-of-00002.safetensors",
255
+ "model.language_model.layers.6.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
256
+ "model.language_model.layers.6.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
257
+ "model.language_model.layers.6.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
258
+ "model.language_model.layers.6.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
259
+ "model.language_model.layers.6.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
260
+ "model.language_model.layers.6.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
261
+ "model.language_model.layers.6.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
262
+ "model.language_model.layers.6.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
263
+ "model.language_model.layers.6.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
264
+ "model.language_model.layers.6.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
265
+ "model.language_model.layers.7.input_layernorm.weight": "model-00001-of-00002.safetensors",
266
+ "model.language_model.layers.7.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
267
+ "model.language_model.layers.7.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
268
+ "model.language_model.layers.7.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
269
+ "model.language_model.layers.7.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
270
+ "model.language_model.layers.7.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
271
+ "model.language_model.layers.7.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
272
+ "model.language_model.layers.7.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
273
+ "model.language_model.layers.7.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
274
+ "model.language_model.layers.7.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
275
+ "model.language_model.layers.7.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
276
+ "model.language_model.layers.8.input_layernorm.weight": "model-00001-of-00002.safetensors",
277
+ "model.language_model.layers.8.mlp.down_proj.weight": "model-00001-of-00002.safetensors",
278
+ "model.language_model.layers.8.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
279
+ "model.language_model.layers.8.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
280
+ "model.language_model.layers.8.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
281
+ "model.language_model.layers.8.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
282
+ "model.language_model.layers.8.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
283
+ "model.language_model.layers.8.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
284
+ "model.language_model.layers.8.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
285
+ "model.language_model.layers.8.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
286
+ "model.language_model.layers.8.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
287
+ "model.language_model.layers.9.input_layernorm.weight": "model-00001-of-00002.safetensors",
288
+ "model.language_model.layers.9.mlp.gate_proj.weight": "model-00001-of-00002.safetensors",
289
+ "model.language_model.layers.9.mlp.up_proj.weight": "model-00001-of-00002.safetensors",
290
+ "model.language_model.layers.9.post_attention_layernorm.weight": "model-00001-of-00002.safetensors",
291
+ "model.language_model.layers.9.self_attn.k_norm.weight": "model-00001-of-00002.safetensors",
292
+ "model.language_model.layers.9.self_attn.k_proj.weight": "model-00001-of-00002.safetensors",
293
+ "model.language_model.layers.9.self_attn.o_proj.weight": "model-00001-of-00002.safetensors",
294
+ "model.language_model.layers.9.self_attn.q_norm.weight": "model-00001-of-00002.safetensors",
295
+ "model.language_model.layers.9.self_attn.q_proj.weight": "model-00001-of-00002.safetensors",
296
+ "model.language_model.layers.9.self_attn.v_proj.weight": "model-00001-of-00002.safetensors",
297
+ "model.language_model.norm.weight": "model-00001-of-00002.safetensors",
298
+ "lm_head.weight": "model-00002-of-00002.safetensors",
299
+ "model.language_model.layers.21.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
300
+ "model.language_model.layers.21.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
301
+ "model.language_model.layers.21.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
302
+ "model.language_model.layers.21.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
303
+ "model.language_model.layers.21.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
304
+ "model.language_model.layers.21.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
305
+ "model.language_model.layers.21.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
306
+ "model.language_model.layers.22.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
307
+ "model.language_model.layers.22.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
308
+ "model.language_model.layers.22.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
309
+ "model.language_model.layers.22.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
310
+ "model.language_model.layers.22.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
311
+ "model.language_model.layers.22.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
312
+ "model.language_model.layers.22.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
313
+ "model.language_model.layers.23.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
314
+ "model.language_model.layers.23.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
315
+ "model.language_model.layers.23.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
316
+ "model.language_model.layers.23.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
317
+ "model.language_model.layers.23.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
318
+ "model.language_model.layers.23.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
319
+ "model.language_model.layers.23.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
320
+ "model.language_model.layers.24.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
321
+ "model.language_model.layers.24.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
322
+ "model.language_model.layers.24.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
323
+ "model.language_model.layers.24.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
324
+ "model.language_model.layers.24.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
325
+ "model.language_model.layers.24.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
326
+ "model.language_model.layers.24.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
327
+ "model.language_model.layers.25.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
328
+ "model.language_model.layers.25.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
329
+ "model.language_model.layers.25.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
330
+ "model.language_model.layers.25.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
331
+ "model.language_model.layers.25.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
332
+ "model.language_model.layers.25.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
333
+ "model.language_model.layers.25.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
334
+ "model.language_model.layers.26.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
335
+ "model.language_model.layers.26.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
336
+ "model.language_model.layers.26.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
337
+ "model.language_model.layers.26.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
338
+ "model.language_model.layers.26.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
339
+ "model.language_model.layers.26.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
340
+ "model.language_model.layers.26.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
341
+ "model.language_model.layers.27.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
342
+ "model.language_model.layers.27.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
343
+ "model.language_model.layers.27.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
344
+ "model.language_model.layers.27.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
345
+ "model.language_model.layers.27.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
346
+ "model.language_model.layers.27.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
347
+ "model.language_model.layers.27.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
348
+ "model.language_model.layers.28.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
349
+ "model.language_model.layers.28.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
350
+ "model.language_model.layers.28.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
351
+ "model.language_model.layers.28.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
352
+ "model.language_model.layers.28.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
353
+ "model.language_model.layers.28.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
354
+ "model.language_model.layers.28.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
355
+ "model.language_model.layers.29.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
356
+ "model.language_model.layers.29.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
357
+ "model.language_model.layers.29.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
358
+ "model.language_model.layers.29.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
359
+ "model.language_model.layers.29.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
360
+ "model.language_model.layers.29.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
361
+ "model.language_model.layers.29.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
362
+ "model.language_model.layers.30.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
363
+ "model.language_model.layers.30.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
364
+ "model.language_model.layers.30.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
365
+ "model.language_model.layers.30.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
366
+ "model.language_model.layers.30.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
367
+ "model.language_model.layers.30.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
368
+ "model.language_model.layers.30.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
369
+ "model.language_model.layers.31.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
370
+ "model.language_model.layers.31.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
371
+ "model.language_model.layers.31.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
372
+ "model.language_model.layers.31.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
373
+ "model.language_model.layers.31.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
374
+ "model.language_model.layers.31.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
375
+ "model.language_model.layers.31.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
376
+ "model.language_model.layers.32.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
377
+ "model.language_model.layers.32.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
378
+ "model.language_model.layers.32.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
379
+ "model.language_model.layers.32.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
380
+ "model.language_model.layers.32.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
381
+ "model.language_model.layers.32.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
382
+ "model.language_model.layers.32.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
383
+ "model.language_model.layers.33.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
384
+ "model.language_model.layers.33.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
385
+ "model.language_model.layers.33.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
386
+ "model.language_model.layers.33.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
387
+ "model.language_model.layers.33.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
388
+ "model.language_model.layers.33.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
389
+ "model.language_model.layers.33.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
390
+ "model.language_model.layers.34.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
391
+ "model.language_model.layers.34.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
392
+ "model.language_model.layers.34.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
393
+ "model.language_model.layers.34.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
394
+ "model.language_model.layers.34.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
395
+ "model.language_model.layers.34.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
396
+ "model.language_model.layers.34.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
397
+ "model.language_model.layers.35.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
398
+ "model.language_model.layers.35.mlp.gate_proj.weight": "model-00002-of-00002.safetensors",
399
+ "model.language_model.layers.35.mlp.up_proj.weight": "model-00002-of-00002.safetensors",
400
+ "model.language_model.layers.35.self_attn.k_proj.weight": "model-00002-of-00002.safetensors",
401
+ "model.language_model.layers.35.self_attn.o_proj.weight": "model-00002-of-00002.safetensors",
402
+ "model.language_model.layers.35.self_attn.q_proj.weight": "model-00002-of-00002.safetensors",
403
+ "model.language_model.layers.35.self_attn.v_proj.weight": "model-00002-of-00002.safetensors",
404
+ "model.language_model.layers.9.mlp.down_proj.weight": "model-00002-of-00002.safetensors",
405
+ "model.visual.embeddings.patch_embedding.weight": "model-00002-of-00002.safetensors",
406
+ "model.visual.encoder.layers.0.layer_norm1.bias": "model-00002-of-00002.safetensors",
407
+ "model.visual.encoder.layers.0.layer_norm1.weight": "model-00002-of-00002.safetensors",
408
+ "model.visual.encoder.layers.0.layer_norm2.bias": "model-00002-of-00002.safetensors",
409
+ "model.visual.encoder.layers.0.layer_norm2.weight": "model-00002-of-00002.safetensors",
410
+ "model.visual.encoder.layers.0.mlp.fc1.bias": "model-00002-of-00002.safetensors",
411
+ "model.visual.encoder.layers.0.mlp.fc1.weight": "model-00002-of-00002.safetensors",
412
+ "model.visual.encoder.layers.0.mlp.fc2.bias": "model-00002-of-00002.safetensors",
413
+ "model.visual.encoder.layers.0.mlp.fc2.weight": "model-00002-of-00002.safetensors",
414
+ "model.visual.encoder.layers.0.self_attn.proj.bias": "model-00002-of-00002.safetensors",
415
+ "model.visual.encoder.layers.0.self_attn.proj.weight": "model-00002-of-00002.safetensors",
416
+ "model.visual.encoder.layers.0.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
417
+ "model.visual.encoder.layers.0.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
418
+ "model.visual.encoder.layers.1.layer_norm1.bias": "model-00002-of-00002.safetensors",
419
+ "model.visual.encoder.layers.1.layer_norm1.weight": "model-00002-of-00002.safetensors",
420
+ "model.visual.encoder.layers.1.layer_norm2.bias": "model-00002-of-00002.safetensors",
421
+ "model.visual.encoder.layers.1.layer_norm2.weight": "model-00002-of-00002.safetensors",
422
+ "model.visual.encoder.layers.1.mlp.fc1.bias": "model-00002-of-00002.safetensors",
423
+ "model.visual.encoder.layers.1.mlp.fc1.weight": "model-00002-of-00002.safetensors",
424
+ "model.visual.encoder.layers.1.mlp.fc2.bias": "model-00002-of-00002.safetensors",
425
+ "model.visual.encoder.layers.1.mlp.fc2.weight": "model-00002-of-00002.safetensors",
426
+ "model.visual.encoder.layers.1.self_attn.proj.bias": "model-00002-of-00002.safetensors",
427
+ "model.visual.encoder.layers.1.self_attn.proj.weight": "model-00002-of-00002.safetensors",
428
+ "model.visual.encoder.layers.1.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
429
+ "model.visual.encoder.layers.1.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
430
+ "model.visual.encoder.layers.10.layer_norm1.bias": "model-00002-of-00002.safetensors",
431
+ "model.visual.encoder.layers.10.layer_norm1.weight": "model-00002-of-00002.safetensors",
432
+ "model.visual.encoder.layers.10.layer_norm2.bias": "model-00002-of-00002.safetensors",
433
+ "model.visual.encoder.layers.10.layer_norm2.weight": "model-00002-of-00002.safetensors",
434
+ "model.visual.encoder.layers.10.mlp.fc1.bias": "model-00002-of-00002.safetensors",
435
+ "model.visual.encoder.layers.10.mlp.fc1.weight": "model-00002-of-00002.safetensors",
436
+ "model.visual.encoder.layers.10.mlp.fc2.bias": "model-00002-of-00002.safetensors",
437
+ "model.visual.encoder.layers.10.mlp.fc2.weight": "model-00002-of-00002.safetensors",
438
+ "model.visual.encoder.layers.10.self_attn.proj.bias": "model-00002-of-00002.safetensors",
439
+ "model.visual.encoder.layers.10.self_attn.proj.weight": "model-00002-of-00002.safetensors",
440
+ "model.visual.encoder.layers.10.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
441
+ "model.visual.encoder.layers.10.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
442
+ "model.visual.encoder.layers.11.layer_norm1.bias": "model-00002-of-00002.safetensors",
443
+ "model.visual.encoder.layers.11.layer_norm1.weight": "model-00002-of-00002.safetensors",
444
+ "model.visual.encoder.layers.11.layer_norm2.bias": "model-00002-of-00002.safetensors",
445
+ "model.visual.encoder.layers.11.layer_norm2.weight": "model-00002-of-00002.safetensors",
446
+ "model.visual.encoder.layers.11.mlp.fc1.bias": "model-00002-of-00002.safetensors",
447
+ "model.visual.encoder.layers.11.mlp.fc1.weight": "model-00002-of-00002.safetensors",
448
+ "model.visual.encoder.layers.11.mlp.fc2.bias": "model-00002-of-00002.safetensors",
449
+ "model.visual.encoder.layers.11.mlp.fc2.weight": "model-00002-of-00002.safetensors",
450
+ "model.visual.encoder.layers.11.self_attn.proj.bias": "model-00002-of-00002.safetensors",
451
+ "model.visual.encoder.layers.11.self_attn.proj.weight": "model-00002-of-00002.safetensors",
452
+ "model.visual.encoder.layers.11.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
453
+ "model.visual.encoder.layers.11.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
454
+ "model.visual.encoder.layers.12.layer_norm1.bias": "model-00002-of-00002.safetensors",
455
+ "model.visual.encoder.layers.12.layer_norm1.weight": "model-00002-of-00002.safetensors",
456
+ "model.visual.encoder.layers.12.layer_norm2.bias": "model-00002-of-00002.safetensors",
457
+ "model.visual.encoder.layers.12.layer_norm2.weight": "model-00002-of-00002.safetensors",
458
+ "model.visual.encoder.layers.12.mlp.fc1.bias": "model-00002-of-00002.safetensors",
459
+ "model.visual.encoder.layers.12.mlp.fc1.weight": "model-00002-of-00002.safetensors",
460
+ "model.visual.encoder.layers.12.mlp.fc2.bias": "model-00002-of-00002.safetensors",
461
+ "model.visual.encoder.layers.12.mlp.fc2.weight": "model-00002-of-00002.safetensors",
462
+ "model.visual.encoder.layers.12.self_attn.proj.bias": "model-00002-of-00002.safetensors",
463
+ "model.visual.encoder.layers.12.self_attn.proj.weight": "model-00002-of-00002.safetensors",
464
+ "model.visual.encoder.layers.12.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
465
+ "model.visual.encoder.layers.12.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
466
+ "model.visual.encoder.layers.13.layer_norm1.bias": "model-00002-of-00002.safetensors",
467
+ "model.visual.encoder.layers.13.layer_norm1.weight": "model-00002-of-00002.safetensors",
468
+ "model.visual.encoder.layers.13.layer_norm2.bias": "model-00002-of-00002.safetensors",
469
+ "model.visual.encoder.layers.13.layer_norm2.weight": "model-00002-of-00002.safetensors",
470
+ "model.visual.encoder.layers.13.mlp.fc1.bias": "model-00002-of-00002.safetensors",
471
+ "model.visual.encoder.layers.13.mlp.fc1.weight": "model-00002-of-00002.safetensors",
472
+ "model.visual.encoder.layers.13.mlp.fc2.bias": "model-00002-of-00002.safetensors",
473
+ "model.visual.encoder.layers.13.mlp.fc2.weight": "model-00002-of-00002.safetensors",
474
+ "model.visual.encoder.layers.13.self_attn.proj.bias": "model-00002-of-00002.safetensors",
475
+ "model.visual.encoder.layers.13.self_attn.proj.weight": "model-00002-of-00002.safetensors",
476
+ "model.visual.encoder.layers.13.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
477
+ "model.visual.encoder.layers.13.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
478
+ "model.visual.encoder.layers.14.layer_norm1.bias": "model-00002-of-00002.safetensors",
479
+ "model.visual.encoder.layers.14.layer_norm1.weight": "model-00002-of-00002.safetensors",
480
+ "model.visual.encoder.layers.14.layer_norm2.bias": "model-00002-of-00002.safetensors",
481
+ "model.visual.encoder.layers.14.layer_norm2.weight": "model-00002-of-00002.safetensors",
482
+ "model.visual.encoder.layers.14.mlp.fc1.bias": "model-00002-of-00002.safetensors",
483
+ "model.visual.encoder.layers.14.mlp.fc1.weight": "model-00002-of-00002.safetensors",
484
+ "model.visual.encoder.layers.14.mlp.fc2.bias": "model-00002-of-00002.safetensors",
485
+ "model.visual.encoder.layers.14.mlp.fc2.weight": "model-00002-of-00002.safetensors",
486
+ "model.visual.encoder.layers.14.self_attn.proj.bias": "model-00002-of-00002.safetensors",
487
+ "model.visual.encoder.layers.14.self_attn.proj.weight": "model-00002-of-00002.safetensors",
488
+ "model.visual.encoder.layers.14.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
489
+ "model.visual.encoder.layers.14.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
490
+ "model.visual.encoder.layers.15.layer_norm1.bias": "model-00002-of-00002.safetensors",
491
+ "model.visual.encoder.layers.15.layer_norm1.weight": "model-00002-of-00002.safetensors",
492
+ "model.visual.encoder.layers.15.layer_norm2.bias": "model-00002-of-00002.safetensors",
493
+ "model.visual.encoder.layers.15.layer_norm2.weight": "model-00002-of-00002.safetensors",
494
+ "model.visual.encoder.layers.15.mlp.fc1.bias": "model-00002-of-00002.safetensors",
495
+ "model.visual.encoder.layers.15.mlp.fc1.weight": "model-00002-of-00002.safetensors",
496
+ "model.visual.encoder.layers.15.mlp.fc2.bias": "model-00002-of-00002.safetensors",
497
+ "model.visual.encoder.layers.15.mlp.fc2.weight": "model-00002-of-00002.safetensors",
498
+ "model.visual.encoder.layers.15.self_attn.proj.bias": "model-00002-of-00002.safetensors",
499
+ "model.visual.encoder.layers.15.self_attn.proj.weight": "model-00002-of-00002.safetensors",
500
+ "model.visual.encoder.layers.15.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
501
+ "model.visual.encoder.layers.15.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
502
+ "model.visual.encoder.layers.16.layer_norm1.bias": "model-00002-of-00002.safetensors",
503
+ "model.visual.encoder.layers.16.layer_norm1.weight": "model-00002-of-00002.safetensors",
504
+ "model.visual.encoder.layers.16.layer_norm2.bias": "model-00002-of-00002.safetensors",
505
+ "model.visual.encoder.layers.16.layer_norm2.weight": "model-00002-of-00002.safetensors",
506
+ "model.visual.encoder.layers.16.mlp.fc1.bias": "model-00002-of-00002.safetensors",
507
+ "model.visual.encoder.layers.16.mlp.fc1.weight": "model-00002-of-00002.safetensors",
508
+ "model.visual.encoder.layers.16.mlp.fc2.bias": "model-00002-of-00002.safetensors",
509
+ "model.visual.encoder.layers.16.mlp.fc2.weight": "model-00002-of-00002.safetensors",
510
+ "model.visual.encoder.layers.16.self_attn.proj.bias": "model-00002-of-00002.safetensors",
511
+ "model.visual.encoder.layers.16.self_attn.proj.weight": "model-00002-of-00002.safetensors",
512
+ "model.visual.encoder.layers.16.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
513
+ "model.visual.encoder.layers.16.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
514
+ "model.visual.encoder.layers.17.layer_norm1.bias": "model-00002-of-00002.safetensors",
515
+ "model.visual.encoder.layers.17.layer_norm1.weight": "model-00002-of-00002.safetensors",
516
+ "model.visual.encoder.layers.17.layer_norm2.bias": "model-00002-of-00002.safetensors",
517
+ "model.visual.encoder.layers.17.layer_norm2.weight": "model-00002-of-00002.safetensors",
518
+ "model.visual.encoder.layers.17.mlp.fc1.bias": "model-00002-of-00002.safetensors",
519
+ "model.visual.encoder.layers.17.mlp.fc1.weight": "model-00002-of-00002.safetensors",
520
+ "model.visual.encoder.layers.17.mlp.fc2.bias": "model-00002-of-00002.safetensors",
521
+ "model.visual.encoder.layers.17.mlp.fc2.weight": "model-00002-of-00002.safetensors",
522
+ "model.visual.encoder.layers.17.self_attn.proj.bias": "model-00002-of-00002.safetensors",
523
+ "model.visual.encoder.layers.17.self_attn.proj.weight": "model-00002-of-00002.safetensors",
524
+ "model.visual.encoder.layers.17.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
525
+ "model.visual.encoder.layers.17.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
526
+ "model.visual.encoder.layers.18.layer_norm1.bias": "model-00002-of-00002.safetensors",
527
+ "model.visual.encoder.layers.18.layer_norm1.weight": "model-00002-of-00002.safetensors",
528
+ "model.visual.encoder.layers.18.layer_norm2.bias": "model-00002-of-00002.safetensors",
529
+ "model.visual.encoder.layers.18.layer_norm2.weight": "model-00002-of-00002.safetensors",
530
+ "model.visual.encoder.layers.18.mlp.fc1.bias": "model-00002-of-00002.safetensors",
531
+ "model.visual.encoder.layers.18.mlp.fc1.weight": "model-00002-of-00002.safetensors",
532
+ "model.visual.encoder.layers.18.mlp.fc2.bias": "model-00002-of-00002.safetensors",
533
+ "model.visual.encoder.layers.18.mlp.fc2.weight": "model-00002-of-00002.safetensors",
534
+ "model.visual.encoder.layers.18.self_attn.proj.bias": "model-00002-of-00002.safetensors",
535
+ "model.visual.encoder.layers.18.self_attn.proj.weight": "model-00002-of-00002.safetensors",
536
+ "model.visual.encoder.layers.18.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
537
+ "model.visual.encoder.layers.18.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
538
+ "model.visual.encoder.layers.19.layer_norm1.bias": "model-00002-of-00002.safetensors",
539
+ "model.visual.encoder.layers.19.layer_norm1.weight": "model-00002-of-00002.safetensors",
540
+ "model.visual.encoder.layers.19.layer_norm2.bias": "model-00002-of-00002.safetensors",
541
+ "model.visual.encoder.layers.19.layer_norm2.weight": "model-00002-of-00002.safetensors",
542
+ "model.visual.encoder.layers.19.mlp.fc1.bias": "model-00002-of-00002.safetensors",
543
+ "model.visual.encoder.layers.19.mlp.fc1.weight": "model-00002-of-00002.safetensors",
544
+ "model.visual.encoder.layers.19.mlp.fc2.bias": "model-00002-of-00002.safetensors",
545
+ "model.visual.encoder.layers.19.mlp.fc2.weight": "model-00002-of-00002.safetensors",
546
+ "model.visual.encoder.layers.19.self_attn.proj.bias": "model-00002-of-00002.safetensors",
547
+ "model.visual.encoder.layers.19.self_attn.proj.weight": "model-00002-of-00002.safetensors",
548
+ "model.visual.encoder.layers.19.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
549
+ "model.visual.encoder.layers.19.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
550
+ "model.visual.encoder.layers.2.layer_norm1.bias": "model-00002-of-00002.safetensors",
551
+ "model.visual.encoder.layers.2.layer_norm1.weight": "model-00002-of-00002.safetensors",
552
+ "model.visual.encoder.layers.2.layer_norm2.bias": "model-00002-of-00002.safetensors",
553
+ "model.visual.encoder.layers.2.layer_norm2.weight": "model-00002-of-00002.safetensors",
554
+ "model.visual.encoder.layers.2.mlp.fc1.bias": "model-00002-of-00002.safetensors",
555
+ "model.visual.encoder.layers.2.mlp.fc1.weight": "model-00002-of-00002.safetensors",
556
+ "model.visual.encoder.layers.2.mlp.fc2.bias": "model-00002-of-00002.safetensors",
557
+ "model.visual.encoder.layers.2.mlp.fc2.weight": "model-00002-of-00002.safetensors",
558
+ "model.visual.encoder.layers.2.self_attn.proj.bias": "model-00002-of-00002.safetensors",
559
+ "model.visual.encoder.layers.2.self_attn.proj.weight": "model-00002-of-00002.safetensors",
560
+ "model.visual.encoder.layers.2.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
561
+ "model.visual.encoder.layers.2.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
562
+ "model.visual.encoder.layers.20.layer_norm1.bias": "model-00002-of-00002.safetensors",
563
+ "model.visual.encoder.layers.20.layer_norm1.weight": "model-00002-of-00002.safetensors",
564
+ "model.visual.encoder.layers.20.layer_norm2.bias": "model-00002-of-00002.safetensors",
565
+ "model.visual.encoder.layers.20.layer_norm2.weight": "model-00002-of-00002.safetensors",
566
+ "model.visual.encoder.layers.20.mlp.fc1.bias": "model-00002-of-00002.safetensors",
567
+ "model.visual.encoder.layers.20.mlp.fc1.weight": "model-00002-of-00002.safetensors",
568
+ "model.visual.encoder.layers.20.mlp.fc2.bias": "model-00002-of-00002.safetensors",
569
+ "model.visual.encoder.layers.20.mlp.fc2.weight": "model-00002-of-00002.safetensors",
570
+ "model.visual.encoder.layers.20.self_attn.proj.bias": "model-00002-of-00002.safetensors",
571
+ "model.visual.encoder.layers.20.self_attn.proj.weight": "model-00002-of-00002.safetensors",
572
+ "model.visual.encoder.layers.20.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
573
+ "model.visual.encoder.layers.20.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
574
+ "model.visual.encoder.layers.21.layer_norm1.bias": "model-00002-of-00002.safetensors",
575
+ "model.visual.encoder.layers.21.layer_norm1.weight": "model-00002-of-00002.safetensors",
576
+ "model.visual.encoder.layers.21.layer_norm2.bias": "model-00002-of-00002.safetensors",
577
+ "model.visual.encoder.layers.21.layer_norm2.weight": "model-00002-of-00002.safetensors",
578
+ "model.visual.encoder.layers.21.mlp.fc1.bias": "model-00002-of-00002.safetensors",
579
+ "model.visual.encoder.layers.21.mlp.fc1.weight": "model-00002-of-00002.safetensors",
580
+ "model.visual.encoder.layers.21.mlp.fc2.bias": "model-00002-of-00002.safetensors",
581
+ "model.visual.encoder.layers.21.mlp.fc2.weight": "model-00002-of-00002.safetensors",
582
+ "model.visual.encoder.layers.21.self_attn.proj.bias": "model-00002-of-00002.safetensors",
583
+ "model.visual.encoder.layers.21.self_attn.proj.weight": "model-00002-of-00002.safetensors",
584
+ "model.visual.encoder.layers.21.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
585
+ "model.visual.encoder.layers.21.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
586
+ "model.visual.encoder.layers.22.layer_norm1.bias": "model-00002-of-00002.safetensors",
587
+ "model.visual.encoder.layers.22.layer_norm1.weight": "model-00002-of-00002.safetensors",
588
+ "model.visual.encoder.layers.22.layer_norm2.bias": "model-00002-of-00002.safetensors",
589
+ "model.visual.encoder.layers.22.layer_norm2.weight": "model-00002-of-00002.safetensors",
590
+ "model.visual.encoder.layers.22.mlp.fc1.bias": "model-00002-of-00002.safetensors",
591
+ "model.visual.encoder.layers.22.mlp.fc1.weight": "model-00002-of-00002.safetensors",
592
+ "model.visual.encoder.layers.22.mlp.fc2.bias": "model-00002-of-00002.safetensors",
593
+ "model.visual.encoder.layers.22.mlp.fc2.weight": "model-00002-of-00002.safetensors",
594
+ "model.visual.encoder.layers.22.self_attn.proj.bias": "model-00002-of-00002.safetensors",
595
+ "model.visual.encoder.layers.22.self_attn.proj.weight": "model-00002-of-00002.safetensors",
596
+ "model.visual.encoder.layers.22.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
597
+ "model.visual.encoder.layers.22.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
598
+ "model.visual.encoder.layers.23.layer_norm1.bias": "model-00002-of-00002.safetensors",
599
+ "model.visual.encoder.layers.23.layer_norm1.weight": "model-00002-of-00002.safetensors",
600
+ "model.visual.encoder.layers.23.layer_norm2.bias": "model-00002-of-00002.safetensors",
601
+ "model.visual.encoder.layers.23.layer_norm2.weight": "model-00002-of-00002.safetensors",
602
+ "model.visual.encoder.layers.23.mlp.fc1.bias": "model-00002-of-00002.safetensors",
603
+ "model.visual.encoder.layers.23.mlp.fc1.weight": "model-00002-of-00002.safetensors",
604
+ "model.visual.encoder.layers.23.mlp.fc2.bias": "model-00002-of-00002.safetensors",
605
+ "model.visual.encoder.layers.23.mlp.fc2.weight": "model-00002-of-00002.safetensors",
606
+ "model.visual.encoder.layers.23.self_attn.proj.bias": "model-00002-of-00002.safetensors",
607
+ "model.visual.encoder.layers.23.self_attn.proj.weight": "model-00002-of-00002.safetensors",
608
+ "model.visual.encoder.layers.23.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
609
+ "model.visual.encoder.layers.23.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
610
+ "model.visual.encoder.layers.3.layer_norm1.bias": "model-00002-of-00002.safetensors",
611
+ "model.visual.encoder.layers.3.layer_norm1.weight": "model-00002-of-00002.safetensors",
612
+ "model.visual.encoder.layers.3.layer_norm2.bias": "model-00002-of-00002.safetensors",
613
+ "model.visual.encoder.layers.3.layer_norm2.weight": "model-00002-of-00002.safetensors",
614
+ "model.visual.encoder.layers.3.mlp.fc1.bias": "model-00002-of-00002.safetensors",
615
+ "model.visual.encoder.layers.3.mlp.fc1.weight": "model-00002-of-00002.safetensors",
616
+ "model.visual.encoder.layers.3.mlp.fc2.bias": "model-00002-of-00002.safetensors",
617
+ "model.visual.encoder.layers.3.mlp.fc2.weight": "model-00002-of-00002.safetensors",
618
+ "model.visual.encoder.layers.3.self_attn.proj.bias": "model-00002-of-00002.safetensors",
619
+ "model.visual.encoder.layers.3.self_attn.proj.weight": "model-00002-of-00002.safetensors",
620
+ "model.visual.encoder.layers.3.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
621
+ "model.visual.encoder.layers.3.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
622
+ "model.visual.encoder.layers.4.layer_norm1.bias": "model-00002-of-00002.safetensors",
623
+ "model.visual.encoder.layers.4.layer_norm1.weight": "model-00002-of-00002.safetensors",
624
+ "model.visual.encoder.layers.4.layer_norm2.bias": "model-00002-of-00002.safetensors",
625
+ "model.visual.encoder.layers.4.layer_norm2.weight": "model-00002-of-00002.safetensors",
626
+ "model.visual.encoder.layers.4.mlp.fc1.bias": "model-00002-of-00002.safetensors",
627
+ "model.visual.encoder.layers.4.mlp.fc1.weight": "model-00002-of-00002.safetensors",
628
+ "model.visual.encoder.layers.4.mlp.fc2.bias": "model-00002-of-00002.safetensors",
629
+ "model.visual.encoder.layers.4.mlp.fc2.weight": "model-00002-of-00002.safetensors",
630
+ "model.visual.encoder.layers.4.self_attn.proj.bias": "model-00002-of-00002.safetensors",
631
+ "model.visual.encoder.layers.4.self_attn.proj.weight": "model-00002-of-00002.safetensors",
632
+ "model.visual.encoder.layers.4.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
633
+ "model.visual.encoder.layers.4.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
634
+ "model.visual.encoder.layers.5.layer_norm1.bias": "model-00002-of-00002.safetensors",
635
+ "model.visual.encoder.layers.5.layer_norm1.weight": "model-00002-of-00002.safetensors",
636
+ "model.visual.encoder.layers.5.layer_norm2.bias": "model-00002-of-00002.safetensors",
637
+ "model.visual.encoder.layers.5.layer_norm2.weight": "model-00002-of-00002.safetensors",
638
+ "model.visual.encoder.layers.5.mlp.fc1.bias": "model-00002-of-00002.safetensors",
639
+ "model.visual.encoder.layers.5.mlp.fc1.weight": "model-00002-of-00002.safetensors",
640
+ "model.visual.encoder.layers.5.mlp.fc2.bias": "model-00002-of-00002.safetensors",
641
+ "model.visual.encoder.layers.5.mlp.fc2.weight": "model-00002-of-00002.safetensors",
642
+ "model.visual.encoder.layers.5.self_attn.proj.bias": "model-00002-of-00002.safetensors",
643
+ "model.visual.encoder.layers.5.self_attn.proj.weight": "model-00002-of-00002.safetensors",
644
+ "model.visual.encoder.layers.5.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
645
+ "model.visual.encoder.layers.5.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
646
+ "model.visual.encoder.layers.6.layer_norm1.bias": "model-00002-of-00002.safetensors",
647
+ "model.visual.encoder.layers.6.layer_norm1.weight": "model-00002-of-00002.safetensors",
648
+ "model.visual.encoder.layers.6.layer_norm2.bias": "model-00002-of-00002.safetensors",
649
+ "model.visual.encoder.layers.6.layer_norm2.weight": "model-00002-of-00002.safetensors",
650
+ "model.visual.encoder.layers.6.mlp.fc1.bias": "model-00002-of-00002.safetensors",
651
+ "model.visual.encoder.layers.6.mlp.fc1.weight": "model-00002-of-00002.safetensors",
652
+ "model.visual.encoder.layers.6.mlp.fc2.bias": "model-00002-of-00002.safetensors",
653
+ "model.visual.encoder.layers.6.mlp.fc2.weight": "model-00002-of-00002.safetensors",
654
+ "model.visual.encoder.layers.6.self_attn.proj.bias": "model-00002-of-00002.safetensors",
655
+ "model.visual.encoder.layers.6.self_attn.proj.weight": "model-00002-of-00002.safetensors",
656
+ "model.visual.encoder.layers.6.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
657
+ "model.visual.encoder.layers.6.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
658
+ "model.visual.encoder.layers.7.layer_norm1.bias": "model-00002-of-00002.safetensors",
659
+ "model.visual.encoder.layers.7.layer_norm1.weight": "model-00002-of-00002.safetensors",
660
+ "model.visual.encoder.layers.7.layer_norm2.bias": "model-00002-of-00002.safetensors",
661
+ "model.visual.encoder.layers.7.layer_norm2.weight": "model-00002-of-00002.safetensors",
662
+ "model.visual.encoder.layers.7.mlp.fc1.bias": "model-00002-of-00002.safetensors",
663
+ "model.visual.encoder.layers.7.mlp.fc1.weight": "model-00002-of-00002.safetensors",
664
+ "model.visual.encoder.layers.7.mlp.fc2.bias": "model-00002-of-00002.safetensors",
665
+ "model.visual.encoder.layers.7.mlp.fc2.weight": "model-00002-of-00002.safetensors",
666
+ "model.visual.encoder.layers.7.self_attn.proj.bias": "model-00002-of-00002.safetensors",
667
+ "model.visual.encoder.layers.7.self_attn.proj.weight": "model-00002-of-00002.safetensors",
668
+ "model.visual.encoder.layers.7.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
669
+ "model.visual.encoder.layers.7.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
670
+ "model.visual.encoder.layers.8.layer_norm1.bias": "model-00002-of-00002.safetensors",
671
+ "model.visual.encoder.layers.8.layer_norm1.weight": "model-00002-of-00002.safetensors",
672
+ "model.visual.encoder.layers.8.layer_norm2.bias": "model-00002-of-00002.safetensors",
673
+ "model.visual.encoder.layers.8.layer_norm2.weight": "model-00002-of-00002.safetensors",
674
+ "model.visual.encoder.layers.8.mlp.fc1.bias": "model-00002-of-00002.safetensors",
675
+ "model.visual.encoder.layers.8.mlp.fc1.weight": "model-00002-of-00002.safetensors",
676
+ "model.visual.encoder.layers.8.mlp.fc2.bias": "model-00002-of-00002.safetensors",
677
+ "model.visual.encoder.layers.8.mlp.fc2.weight": "model-00002-of-00002.safetensors",
678
+ "model.visual.encoder.layers.8.self_attn.proj.bias": "model-00002-of-00002.safetensors",
679
+ "model.visual.encoder.layers.8.self_attn.proj.weight": "model-00002-of-00002.safetensors",
680
+ "model.visual.encoder.layers.8.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
681
+ "model.visual.encoder.layers.8.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
682
+ "model.visual.encoder.layers.9.layer_norm1.bias": "model-00002-of-00002.safetensors",
683
+ "model.visual.encoder.layers.9.layer_norm1.weight": "model-00002-of-00002.safetensors",
684
+ "model.visual.encoder.layers.9.layer_norm2.bias": "model-00002-of-00002.safetensors",
685
+ "model.visual.encoder.layers.9.layer_norm2.weight": "model-00002-of-00002.safetensors",
686
+ "model.visual.encoder.layers.9.mlp.fc1.bias": "model-00002-of-00002.safetensors",
687
+ "model.visual.encoder.layers.9.mlp.fc1.weight": "model-00002-of-00002.safetensors",
688
+ "model.visual.encoder.layers.9.mlp.fc2.bias": "model-00002-of-00002.safetensors",
689
+ "model.visual.encoder.layers.9.mlp.fc2.weight": "model-00002-of-00002.safetensors",
690
+ "model.visual.encoder.layers.9.self_attn.proj.bias": "model-00002-of-00002.safetensors",
691
+ "model.visual.encoder.layers.9.self_attn.proj.weight": "model-00002-of-00002.safetensors",
692
+ "model.visual.encoder.layers.9.self_attn.qkv.bias": "model-00002-of-00002.safetensors",
693
+ "model.visual.encoder.layers.9.self_attn.qkv.weight": "model-00002-of-00002.safetensors",
694
+ "model.visual.layernorm_pre.bias": "model-00002-of-00002.safetensors",
695
+ "model.visual.layernorm_pre.weight": "model-00002-of-00002.safetensors",
696
+ "model.visual.merger.ln_q.bias": "model-00002-of-00002.safetensors",
697
+ "model.visual.merger.ln_q.weight": "model-00002-of-00002.safetensors",
698
+ "model.visual.merger.mlp.0.bias": "model-00002-of-00002.safetensors",
699
+ "model.visual.merger.mlp.0.weight": "model-00002-of-00002.safetensors",
700
+ "model.visual.merger.mlp.2.bias": "model-00002-of-00002.safetensors",
701
+ "model.visual.merger.mlp.2.weight": "model-00002-of-00002.safetensors"
702
+ }
703
+ }
modeling_mage_vl.py ADDED
@@ -0,0 +1,1709 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Callable
2
+ from dataclasses import dataclass
3
+ from typing import Any, Optional, Union
4
+ from pathlib import Path
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ from torch.nn import LayerNorm
9
+
10
+ from transformers import AutoModel
11
+ from transformers.cache_utils import Cache
12
+ from transformers.generation import GenerationMixin
13
+ from transformers.modeling_outputs import BaseModelOutput, BaseModelOutputWithPooling, ModelOutput
14
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
15
+ from transformers.models.siglip.modeling_siglip import SiglipMLP
16
+ from transformers.processing_utils import Unpack
17
+ from transformers.utils import (
18
+ TransformersKwargs,
19
+ auto_docstring,
20
+ can_return_tuple,
21
+ replace_return_docstrings,
22
+ )
23
+ from transformers.utils.generic import is_flash_attention_requested
24
+
25
+ from .configuration_mage_vl import MageVLConfig, MageVLVisionConfig
26
+
27
+
28
+ @dataclass
29
+ @auto_docstring(
30
+ custom_intro="""
31
+ Base class for Mage-VL outputs, with hidden states and attentions.
32
+ """
33
+ )
34
+ class MageVLModelOutputWithPast(ModelOutput):
35
+ r"""
36
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
37
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
38
+
39
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
40
+ `past_key_values` input) to speed up sequential decoding.
41
+ """
42
+
43
+ last_hidden_state: Optional[torch.FloatTensor] = None
44
+ past_key_values: Optional[Cache] = None
45
+ hidden_states: Optional[tuple[torch.FloatTensor]] = None
46
+ attentions: Optional[tuple[torch.FloatTensor]] = None
47
+
48
+
49
+ @dataclass
50
+ @auto_docstring(
51
+ custom_intro="""
52
+ Base class for Mage-VL causal language model (or autoregressive) outputs.
53
+ """
54
+ )
55
+ class MageVLCausalLMOutputWithPast(ModelOutput):
56
+ r"""
57
+ loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided):
58
+ Language modeling loss (for next-token prediction).
59
+ logits (`torch.FloatTensor` of shape `(batch_size, sequence_length, config.vocab_size)`):
60
+ Prediction scores of the language modeling head (scores for each vocabulary token before SoftMax).
61
+ past_key_values (`Cache`, *optional*, returned when `use_cache=True` is passed or when `config.use_cache=True`):
62
+ It is a [`~cache_utils.Cache`] instance. For more details, see our [kv cache guide](https://huggingface.co/docs/transformers/en/kv_cache).
63
+
64
+ Contains pre-computed hidden-states (key and values in the self-attention blocks) that can be used (see
65
+ `past_key_values` input) to speed up sequential decoding.
66
+ """
67
+
68
+ loss: Optional[torch.FloatTensor] = None
69
+ logits: Optional[torch.FloatTensor] = None
70
+ past_key_values: Optional[Cache] = None
71
+ hidden_states: Optional[tuple[torch.FloatTensor]] = None
72
+ attentions: Optional[tuple[torch.FloatTensor]] = None
73
+
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Vision Rotary Embedding
77
+ # ---------------------------------------------------------------------------
78
+
79
+
80
+ class VisionRotaryEmbedding(nn.Module):
81
+ """
82
+ 3D (T,H,W) Rotary frequency constructor with 4:6:6 split.
83
+ Supports both grid_thw-based and explicit position-based RoPE computation.
84
+ """
85
+
86
+ def __init__(self, config: MageVLVisionConfig):
87
+ super().__init__()
88
+ head_dim = config.hidden_size // config.num_attention_heads
89
+ base = config.rope_theta
90
+
91
+ assert head_dim % 2 == 0, "head_dim must be even for rotary."
92
+ assert head_dim % 16 == 0, "head_dim must be divisible by 16."
93
+ half = head_dim // 2
94
+ assert half % 16 == 0, "head_dim//2 must also be divisible by 16 to split into 4:6:6."
95
+
96
+ self.head_dim = head_dim
97
+ self.half = half
98
+ self.base = base
99
+
100
+ # 4:6:6 split for T:H:W
101
+ unit = half // 16
102
+ self.t_size = 4 * unit
103
+ self.h_size = 6 * unit
104
+ self.w_size = 6 * unit
105
+
106
+ self.register_buffer(
107
+ "inv_freq_t",
108
+ 1.0 / (base ** (torch.arange(self.t_size, dtype=torch.float32) / self.t_size)),
109
+ persistent=False,
110
+ )
111
+ self.register_buffer(
112
+ "inv_freq_h",
113
+ 1.0 / (base ** (torch.arange(self.h_size, dtype=torch.float32) / self.h_size)),
114
+ persistent=False,
115
+ )
116
+ self.register_buffer(
117
+ "inv_freq_w",
118
+ 1.0 / (base ** (torch.arange(self.w_size, dtype=torch.float32) / self.w_size)),
119
+ persistent=False,
120
+ )
121
+
122
+ def forward(self, grid_thw: torch.Tensor) -> torch.Tensor:
123
+ """
124
+ Compute rotary position embeddings from grid_thw (Qwen2VL style).
125
+
126
+ Args:
127
+ grid_thw: [num_samples, 3] tensor with [t, h, w] for each sample
128
+
129
+ Returns:
130
+ freqs: [total_seq_len, half] tensor of position frequencies
131
+ """
132
+ device = grid_thw.device
133
+ inv_t = self.inv_freq_t.to(device=device)
134
+ inv_h = self.inv_freq_h.to(device=device)
135
+ inv_w = self.inv_freq_w.to(device=device)
136
+
137
+ all_freqs = []
138
+ for sample_thw in grid_thw:
139
+ t, h, w = sample_thw[0].item(), sample_thw[1].item(), sample_thw[2].item()
140
+
141
+ # Compute frequency tables
142
+ ft = torch.outer(torch.arange(t, device=device, dtype=torch.float32), inv_t)
143
+ fh = torch.outer(torch.arange(h, device=device, dtype=torch.float32), inv_h)
144
+ fw = torch.outer(torch.arange(w, device=device, dtype=torch.float32), inv_w)
145
+
146
+ # Build position indices for this sample
147
+ t_ids = torch.arange(t, device=device).repeat_interleave(h * w)
148
+ h_ids = torch.arange(h, device=device).repeat_interleave(w).repeat(t)
149
+ w_ids = torch.arange(w, device=device).repeat(h).repeat(t)
150
+
151
+ # Concatenate frequencies: [seq_len, half]
152
+ sample_freqs = torch.cat([ft[t_ids], fh[h_ids], fw[w_ids]], dim=-1)
153
+ all_freqs.append(sample_freqs)
154
+
155
+ return torch.cat(all_freqs, dim=0)
156
+
157
+ def forward_from_positions(self, patch_positions: torch.Tensor) -> torch.Tensor:
158
+ """
159
+ Compute rotary position embeddings from explicit patch positions.
160
+
161
+ Args:
162
+ patch_positions: [seq_len, 3] tensor with [t, h, w] positions for each patch
163
+
164
+ Returns:
165
+ freqs: [seq_len, half] tensor of position frequencies
166
+ """
167
+ device = patch_positions.device
168
+ inv_t = self.inv_freq_t.to(device=device)
169
+ inv_h = self.inv_freq_h.to(device=device)
170
+ inv_w = self.inv_freq_w.to(device=device)
171
+
172
+ t_pos = patch_positions[:, 0].float()
173
+ h_pos = patch_positions[:, 1].float()
174
+ w_pos = patch_positions[:, 2].float()
175
+
176
+ ft = torch.outer(t_pos, inv_t)
177
+ fh = torch.outer(h_pos, inv_h)
178
+ fw = torch.outer(w_pos, inv_w)
179
+
180
+ return torch.cat([ft, fh, fw], dim=-1)
181
+
182
+ def forward_with_thw(self, t: int, h: int, w: int, device=None) -> torch.Tensor:
183
+ """
184
+ Compute rotary position embeddings from explicit t, h, w dimensions.
185
+
186
+ Args:
187
+ t: Number of temporal frames
188
+ h: Number of height patches
189
+ w: Number of width patches
190
+ device: Target device
191
+
192
+ Returns:
193
+ freqs: [t*h*w, half] tensor of position frequencies
194
+ """
195
+ if device is None:
196
+ device = self.inv_freq_t.device
197
+
198
+ inv_t = self.inv_freq_t.to(device=device)
199
+ inv_h = self.inv_freq_h.to(device=device)
200
+ inv_w = self.inv_freq_w.to(device=device)
201
+
202
+ ft = torch.outer(torch.arange(t, device=device, dtype=torch.float32), inv_t)
203
+ fh = torch.outer(torch.arange(h, device=device, dtype=torch.float32), inv_h)
204
+ fw = torch.outer(torch.arange(w, device=device, dtype=torch.float32), inv_w)
205
+
206
+ t_ids = torch.arange(t, device=device).repeat_interleave(h * w)
207
+ h_ids = torch.arange(h, device=device).repeat_interleave(w).repeat(t)
208
+ w_ids = torch.arange(w, device=device).repeat(h).repeat(t)
209
+
210
+ freqs = torch.cat([ft[t_ids], fh[h_ids], fw[w_ids]], dim=-1)
211
+ return freqs
212
+
213
+
214
+ # ---------------------------------------------------------------------------
215
+ # Patch Embedding
216
+ # ---------------------------------------------------------------------------
217
+
218
+
219
+ class MageVLVisionEmbeddings(nn.Module):
220
+ """
221
+ Patch embedding layer that converts pre-processed patches to embeddings.
222
+
223
+ This module is designed to receive patches that have already been extracted
224
+ and arranged by the Qwen2VL image processor in 2x2 block spatial order.
225
+
226
+ Input format: [total_patches, num_channels, patch_size, patch_size]
227
+ Output format: [total_patches, embed_dim]
228
+ """
229
+
230
+ def __init__(self, config: MageVLVisionConfig):
231
+ super().__init__()
232
+ self.config = config
233
+ self.embed_dim = config.hidden_size
234
+ self.image_size = config.image_size
235
+ self.patch_size = config.patch_size
236
+ self.in_channels = config.num_channels
237
+
238
+ self.patch_embedding = nn.Conv2d(
239
+ in_channels=config.num_channels,
240
+ out_channels=self.embed_dim,
241
+ kernel_size=self.patch_size,
242
+ stride=self.patch_size,
243
+ bias=False,
244
+ )
245
+
246
+ def forward(self, hidden_states: torch.FloatTensor) -> torch.Tensor:
247
+ target_dtype = self.patch_embedding.weight.dtype
248
+ hidden_states = hidden_states.view(-1, self.in_channels, self.patch_size, self.patch_size)
249
+ hidden_states = self.patch_embedding(hidden_states.to(dtype=target_dtype)).view(-1, self.embed_dim)
250
+
251
+ return hidden_states
252
+
253
+
254
+ # ---------------------------------------------------------------------------
255
+ # Patch Merger
256
+ # ---------------------------------------------------------------------------
257
+
258
+
259
+ class MageVLVisionPatchMerger(nn.Module):
260
+ """
261
+ Patch merger that merges spatial_merge_size x spatial_merge_size patches into one.
262
+
263
+ This module is designed to work with Qwen2VL-style patch processing where patches
264
+ are already arranged in 2x2 block order by the image processor.
265
+ """
266
+
267
+ def __init__(
268
+ self,
269
+ dim: int,
270
+ context_dim: int,
271
+ spatial_merge_size: int = 2,
272
+ layer_norm_eps: float = 1e-05,
273
+ use_patch_position_encoding: bool = False,
274
+ patch_position_encoding_type: str = "absolute",
275
+ max_position_embeddings: int = 8192,
276
+ ) -> None:
277
+ super().__init__()
278
+ self.hidden_size = context_dim * (spatial_merge_size**2)
279
+ self.ln_q = LayerNorm(context_dim, eps=layer_norm_eps)
280
+ self.mlp = nn.Sequential(
281
+ nn.Linear(self.hidden_size, self.hidden_size),
282
+ nn.GELU(),
283
+ nn.Linear(self.hidden_size, dim),
284
+ )
285
+ self.spatial_merge_size = spatial_merge_size
286
+ self.use_patch_position_encoding = use_patch_position_encoding
287
+ self.patch_position_encoding_type = patch_position_encoding_type
288
+
289
+ if self.use_patch_position_encoding:
290
+ if self.patch_position_encoding_type != "absolute":
291
+ raise ValueError(
292
+ f"Unknown patch_position_encoding_type: {self.patch_position_encoding_type}. "
293
+ "Only 'absolute' is supported."
294
+ )
295
+ self.pos_emb_h = nn.Embedding(max_position_embeddings, dim)
296
+ self.pos_emb_w = nn.Embedding(max_position_embeddings, dim)
297
+
298
+ def forward(self, x: torch.Tensor, patch_positions: Optional[torch.Tensor] = None) -> torch.Tensor:
299
+ """
300
+ Merge patches from Qwen2VL-style input.
301
+
302
+ The input patches are already arranged in 2x2 block order by the image processor,
303
+ so we simply need to apply LayerNorm, reshape, and project through MLP.
304
+
305
+ Args:
306
+ x: Input tensor of shape [batch_size, seq_len, hidden_size] or [seq_len, hidden_size]
307
+ where seq_len = t * h * w (patches in 2x2 block order)
308
+
309
+ Returns:
310
+ Merged tensor of shape [batch_size, seq_len // spatial_merge_size^2, dim]
311
+ or [seq_len // spatial_merge_size^2, dim]
312
+ """
313
+ if patch_positions is not None and patch_positions.dim() == 3:
314
+ patch_positions = patch_positions.squeeze(0)
315
+
316
+ x = self.ln_q(x).view(-1, self.hidden_size)
317
+ x = self.mlp(x)
318
+
319
+ if self.use_patch_position_encoding and patch_positions is not None:
320
+ pp = patch_positions.view(-1, self.spatial_merge_size**2, 3)
321
+ pp = pp[:, 0, :]
322
+ pp = (pp // self.spatial_merge_size).long()
323
+
324
+ x = x + self.pos_emb_h(pp[:, 1]) + self.pos_emb_w(pp[:, 2])
325
+
326
+ return x
327
+
328
+
329
+ def rotate_half(x):
330
+ """
331
+ Interleaved rotation to match Source model's implementation.
332
+ (x1, x2, x3, x4) -> (-x2, x1, -x4, x3)
333
+ """
334
+ x_even = x[..., ::2]
335
+ x_odd = x[..., 1::2]
336
+ return torch.stack((-x_odd, x_even), dim=-1).flatten(-2)
337
+
338
+
339
+ def get_norm_layer(config):
340
+ if config.layer_norm_type == "rms_norm":
341
+ return nn.RMSNorm(config.hidden_size, eps=config.layer_norm_eps)
342
+ else:
343
+ return nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
344
+
345
+
346
+ def apply_rotary_pos_emb(q, k, freqs):
347
+ # q, k: (B, H, L, D)
348
+ # freqs: (B, L, D)
349
+ orig_q_dtype = q.dtype
350
+ orig_k_dtype = k.dtype
351
+ q, k = q.float(), k.float()
352
+ # We need to broadcast freqs to match heads
353
+ # (B, L, D) -> (B, 1, L, D)
354
+ # Keep the same dtype as q, k to avoid memory doubling from float32 promotion
355
+ cos = freqs.cos().unsqueeze(1).float()
356
+ sin = freqs.sin().unsqueeze(1).float()
357
+
358
+ q_embed = (q * cos) + (rotate_half(q) * sin)
359
+ k_embed = (k * cos) + (rotate_half(k) * sin)
360
+ q_embed = q_embed.to(orig_q_dtype)
361
+ k_embed = k_embed.to(orig_k_dtype)
362
+ return q_embed, k_embed
363
+
364
+
365
+ def eager_attention_forward(
366
+ module: nn.Module,
367
+ query: torch.Tensor,
368
+ key: torch.Tensor,
369
+ value: torch.Tensor,
370
+ attention_mask: Optional[torch.Tensor],
371
+ scaling: float,
372
+ dropout: float = 0.0,
373
+ **kwargs,
374
+ ):
375
+ """Eager attention; query/key/value are expected as ``(B, H, L, D)``."""
376
+ attn_weights = torch.matmul(query, key.transpose(2, 3)) * scaling
377
+ if attention_mask is not None:
378
+ attn_weights = attn_weights + attention_mask
379
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
380
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
381
+ attn_output = torch.matmul(attn_weights, value)
382
+ attn_output = attn_output.transpose(1, 2).contiguous() # (B, L, H, D)
383
+ return attn_output, attn_weights
384
+
385
+
386
+ class MageVLVisionAttention(nn.Module):
387
+ """
388
+ Multi-headed attention with RoPE support, dispatched through
389
+ :data:`ALL_ATTENTION_FUNCTIONS` (``eager`` / ``sdpa`` / ``flash_attention_2``)
390
+ based on ``config._attn_implementation``.
391
+ """
392
+
393
+ def __init__(self, config: MageVLVisionConfig):
394
+ super().__init__()
395
+ self.config = config
396
+ self.embed_dim = config.hidden_size
397
+ self.num_heads = config.num_attention_heads
398
+ self.head_dim = self.embed_dim // self.num_heads
399
+ if self.head_dim * self.num_heads != self.embed_dim:
400
+ raise ValueError(
401
+ f"embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`: {self.num_heads})."
402
+ )
403
+
404
+ self.num_key_value_groups = 1 # required by repeat_kv-aware eager paths
405
+ self.scale = self.head_dim**-0.5
406
+ self.scaling = self.scale # alias expected by some attention interfaces
407
+ self.attention_dropout = config.attention_dropout
408
+ self.is_causal = False
409
+ self.qkv = nn.Linear(self.embed_dim, self.embed_dim * 3)
410
+ self.proj = nn.Linear(self.embed_dim, self.embed_dim)
411
+
412
+ def forward(
413
+ self,
414
+ hidden_states: torch.Tensor,
415
+ attention_mask: Optional[torch.Tensor] = None,
416
+ rotary_pos_emb: Optional[torch.Tensor] = None,
417
+ output_attentions: bool = False,
418
+ cu_seqlens: Optional[torch.Tensor] = None,
419
+ max_seqlen: Optional[int] = None,
420
+ **kwargs,
421
+ ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
422
+ batch_size, q_len, _ = hidden_states.size()
423
+ # (B, L, 3*H*D) -> (B, L, 3, H, D) -> 3 x (B, L, H, D) -> 3 x (B, H, L, D)
424
+ q, k, v = (
425
+ self.qkv(hidden_states)
426
+ .reshape(batch_size, q_len, 3, self.num_heads, self.head_dim)
427
+ .permute(2, 0, 1, 3, 4)
428
+ .unbind(0)
429
+ )
430
+ query_states = q.transpose(1, 2)
431
+ key_states = k.transpose(1, 2)
432
+ value_states = v.transpose(1, 2)
433
+
434
+ if rotary_pos_emb is not None:
435
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, rotary_pos_emb)
436
+
437
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
438
+ self.config._attn_implementation, eager_attention_forward
439
+ )
440
+ dropout = 0.0 if not self.training else self.attention_dropout
441
+
442
+ if cu_seqlens is not None and is_flash_attention_requested(self.config):
443
+ # Flash Attention varlen path: pass cu_seq_lens / max_length kwargs.
444
+ if max_seqlen is None:
445
+ max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max()
446
+ attn_output, _ = attention_interface(
447
+ self,
448
+ query_states,
449
+ key_states,
450
+ value_states,
451
+ attention_mask=None,
452
+ scaling=self.scale,
453
+ dropout=dropout,
454
+ cu_seq_lens_q=cu_seqlens,
455
+ cu_seq_lens_k=cu_seqlens,
456
+ max_length_q=max_seqlen,
457
+ max_length_k=max_seqlen,
458
+ is_causal=False,
459
+ **kwargs,
460
+ )
461
+ elif cu_seqlens is not None:
462
+ # Non-FA implementations do not understand cu_seqlens directly; mirror
463
+ # Qwen3-VL by splitting the packed sequence into per-sample chunks
464
+ # along the L dim of (B, H, L, D) and running attention per chunk.
465
+ lengths = (cu_seqlens[1:] - cu_seqlens[:-1]).tolist()
466
+ splits = [torch.split(t, lengths, dim=2) for t in (query_states, key_states, value_states)]
467
+ attn_outputs = [
468
+ attention_interface(
469
+ self,
470
+ q_chunk,
471
+ k_chunk,
472
+ v_chunk,
473
+ attention_mask=None,
474
+ scaling=self.scale,
475
+ dropout=dropout,
476
+ is_causal=False,
477
+ **kwargs,
478
+ )[0]
479
+ for q_chunk, k_chunk, v_chunk in zip(*splits)
480
+ ]
481
+ # interface output is (B, l_i, H, D); concat along the L axis
482
+ attn_output = torch.cat(attn_outputs, dim=1)
483
+ else:
484
+ attn_mask = None
485
+ if attention_mask is not None:
486
+ attn_mask = attention_mask
487
+ if attn_mask.dim() == 2:
488
+ attn_mask = attn_mask.unsqueeze(0)
489
+ if attn_mask.shape[0] == 1 and batch_size > 1:
490
+ attn_mask = attn_mask.expand(batch_size, -1, -1)
491
+ attn_mask = attn_mask.unsqueeze(1) # (B, 1, L, L)
492
+ attn_output, _ = attention_interface(
493
+ self,
494
+ query_states,
495
+ key_states,
496
+ value_states,
497
+ attention_mask=attn_mask,
498
+ scaling=self.scale,
499
+ dropout=dropout,
500
+ is_causal=False,
501
+ **kwargs,
502
+ )
503
+
504
+ attn_output = attn_output.reshape(batch_size, q_len, self.embed_dim)
505
+ attn_output = self.proj(attn_output)
506
+
507
+ return attn_output, None
508
+
509
+
510
+ class MageVLVisionEncoderLayer(nn.Module):
511
+ """Vision encoder layer with pre-norm and Flash Attention 2."""
512
+
513
+ def __init__(self, config: MageVLVisionConfig):
514
+ super().__init__()
515
+ self.embed_dim = config.hidden_size
516
+ self.self_attn = MageVLVisionAttention(config)
517
+ self.layer_norm1 = get_norm_layer(config)
518
+ self.mlp = SiglipMLP(config)
519
+ self.layer_norm2 = get_norm_layer(config)
520
+
521
+ def forward(
522
+ self,
523
+ hidden_states: torch.Tensor,
524
+ attention_mask: Optional[torch.Tensor] = None,
525
+ rotary_pos_emb: Optional[torch.Tensor] = None,
526
+ output_attentions: bool = False,
527
+ cu_seqlens: Optional[torch.Tensor] = None,
528
+ max_seqlen: Optional[int] = None,
529
+ ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
530
+ residual = hidden_states
531
+ hidden_states = self.layer_norm1(hidden_states)
532
+
533
+ hidden_states, attn_weights = self.self_attn(
534
+ hidden_states=hidden_states,
535
+ attention_mask=attention_mask,
536
+ rotary_pos_emb=rotary_pos_emb,
537
+ output_attentions=output_attentions,
538
+ cu_seqlens=cu_seqlens,
539
+ max_seqlen=max_seqlen,
540
+ )
541
+ hidden_states = residual + hidden_states
542
+
543
+ residual = hidden_states
544
+ hidden_states = self.layer_norm2(hidden_states)
545
+ hidden_states = self.mlp(hidden_states)
546
+ hidden_states = residual + hidden_states
547
+
548
+ outputs = (hidden_states, attn_weights) if output_attentions else (hidden_states,)
549
+ return outputs
550
+
551
+
552
+ class MageVLVisionEncoder(nn.Module):
553
+ def __init__(self, config: MageVLVisionConfig):
554
+ super().__init__()
555
+ self.config = config
556
+ self.layers = nn.ModuleList([MageVLVisionEncoderLayer(config) for _ in range(config.num_hidden_layers)])
557
+ # Gradient checkpointing support
558
+ self.gradient_checkpointing = False
559
+
560
+ def forward(
561
+ self,
562
+ hidden_states: torch.Tensor,
563
+ attention_mask: Optional[torch.Tensor] = None,
564
+ rotary_pos_emb: Optional[torch.Tensor] = None,
565
+ output_attentions: bool = False,
566
+ output_hidden_states: bool = False,
567
+ return_dict: bool = True,
568
+ cu_seqlens: Optional[torch.Tensor] = None,
569
+ max_seqlen: Optional[int] = None,
570
+ ) -> Union[tuple, BaseModelOutput]:
571
+ all_hidden_states = () if output_hidden_states else None
572
+ all_self_attentions = () if output_attentions else None
573
+
574
+ for layer in self.layers:
575
+ if output_hidden_states:
576
+ all_hidden_states = all_hidden_states + (hidden_states,)
577
+
578
+ if self.gradient_checkpointing and self.training:
579
+ layer_outputs = self._gradient_checkpointing_func(
580
+ layer.__call__,
581
+ hidden_states,
582
+ attention_mask,
583
+ rotary_pos_emb,
584
+ output_attentions,
585
+ cu_seqlens,
586
+ max_seqlen,
587
+ )
588
+ else:
589
+ layer_outputs = layer(
590
+ hidden_states,
591
+ attention_mask=attention_mask,
592
+ rotary_pos_emb=rotary_pos_emb,
593
+ output_attentions=output_attentions,
594
+ cu_seqlens=cu_seqlens,
595
+ max_seqlen=max_seqlen,
596
+ )
597
+
598
+ hidden_states = layer_outputs[0]
599
+
600
+ if output_attentions:
601
+ all_self_attentions = all_self_attentions + (layer_outputs[1],)
602
+
603
+ if output_hidden_states:
604
+ all_hidden_states = all_hidden_states + (hidden_states,)
605
+
606
+ if not return_dict:
607
+ return tuple(v for v in [hidden_states, all_hidden_states, all_self_attentions] if v is not None)
608
+
609
+ return BaseModelOutput(
610
+ last_hidden_state=hidden_states,
611
+ hidden_states=all_hidden_states,
612
+ attentions=all_self_attentions,
613
+ )
614
+
615
+
616
+ class MageVLPreTrainedModel(PreTrainedModel):
617
+ config_class = MageVLConfig
618
+ base_model_prefix = "model"
619
+ input_modalities = ("image", "video", "text")
620
+ supports_gradient_checkpointing = True
621
+ _no_split_modules = ["MageVLVisionEncoderLayer", "Qwen3DecoderLayer"]
622
+ _skip_keys_device_placement = "past_key_values"
623
+ _supports_flash_attn = True
624
+ _supports_sdpa = True
625
+
626
+ def _init_weights(self, module):
627
+ super()._init_weights(module)
628
+ # Re-initialize VisionRotaryEmbedding inv_freq buffers.
629
+ # These are registered with persistent=False, so they are not in the checkpoint
630
+ # state_dict. When ``from_pretrained`` materializes the model from meta tensors,
631
+ # the values in these buffers end up uninitialized. Mirror Qwen3-VL by explicitly
632
+ # filling them here so RoPE produces the correct frequencies post-load.
633
+ if isinstance(module, VisionRotaryEmbedding):
634
+ base = module.base
635
+ with torch.no_grad():
636
+ inv_t = 1.0 / (base ** (torch.arange(module.t_size, dtype=torch.float32) / module.t_size))
637
+ inv_h = 1.0 / (base ** (torch.arange(module.h_size, dtype=torch.float32) / module.h_size))
638
+ inv_w = 1.0 / (base ** (torch.arange(module.w_size, dtype=torch.float32) / module.w_size))
639
+ module.inv_freq_t.copy_(inv_t.to(module.inv_freq_t.device))
640
+ module.inv_freq_h.copy_(inv_h.to(module.inv_freq_h.device))
641
+ module.inv_freq_w.copy_(inv_w.to(module.inv_freq_w.device))
642
+
643
+
644
+ class Siglip2MultiheadAttentionPoolingHead(nn.Module):
645
+ """
646
+ Multi-Head Attention Pooling with a learned probe (PMA-style).
647
+ """
648
+
649
+ def __init__(self, config: MageVLVisionConfig):
650
+ super().__init__()
651
+ self.embed_dim = config.hidden_size
652
+ self.probe = nn.Parameter(torch.randn(1, 1, config.hidden_size))
653
+ self.attention = nn.MultiheadAttention(config.hidden_size, config.num_attention_heads, batch_first=True)
654
+ self.norm = nn.RMSNorm(config.hidden_size, eps=config.layer_norm_eps)
655
+ self.mlp = SiglipMLP(config)
656
+
657
+ def forward(self, hidden_states):
658
+ batch_size = hidden_states.shape[0]
659
+ probe = self.probe.repeat(batch_size, 1, 1)
660
+
661
+ attn_output, _ = self.attention(probe, hidden_states, hidden_states)
662
+
663
+ residual = attn_output
664
+ attn_output = self.norm(attn_output)
665
+ attn_output = residual + self.mlp(attn_output)
666
+
667
+ return attn_output[:, 0]
668
+
669
+
670
+ # ---------------------------------------------------------------------------
671
+ # Vision Model
672
+ # ---------------------------------------------------------------------------
673
+
674
+
675
+ class MageVLVisionPretrainedModel(MageVLPreTrainedModel):
676
+ """
677
+ Mage-VL Vision Model.
678
+
679
+ This vision model is designed to work with Qwen2VL-style image processing:
680
+ - Receives pre-processed patches in 2x2 block spatial order
681
+ - Applies RoPE with matching 2x2 block layout conversion
682
+ - Accepts explicit patch_positions for RoPE computation
683
+
684
+ Input format:
685
+ hidden_state: [total_patches, num_channels, patch_size, patch_size]
686
+ grid_thw: [num_samples, 3] with [t, h, w] for each sample
687
+ """
688
+
689
+ def __init__(self, config: MageVLVisionConfig):
690
+ super().__init__(config)
691
+ self.config = config
692
+ self.spatial_merge_size = config.spatial_merge_size
693
+
694
+ # Vision components
695
+ self.embeddings = MageVLVisionEmbeddings(config)
696
+ self.layernorm_pre = get_norm_layer(config)
697
+ self.encoder = MageVLVisionEncoder(config)
698
+ self.video_rope = VisionRotaryEmbedding(config)
699
+
700
+ if config.use_head:
701
+ self.layernorm_post = get_norm_layer(config)
702
+ self.head = Siglip2MultiheadAttentionPoolingHead(config)
703
+ else:
704
+ self.layernorm_post = None
705
+ self.head = None
706
+
707
+ self.merger = MageVLVisionPatchMerger(
708
+ dim=config.out_hidden_size,
709
+ context_dim=config.hidden_size,
710
+ spatial_merge_size=config.spatial_merge_size,
711
+ layer_norm_eps=config.layer_norm_eps,
712
+ use_patch_position_encoding=getattr(config, "use_patch_position_encoding", False),
713
+ patch_position_encoding_type=getattr(config, "patch_position_encoding_type", "absolute"),
714
+ max_position_embeddings=getattr(config, "max_position_embeddings", 8192),
715
+ )
716
+
717
+ self.post_init()
718
+
719
+ def _build_cu_seqlens(
720
+ self,
721
+ grid_thw: torch.Tensor,
722
+ total_patches: int,
723
+ fixed_t: Optional[int] = 4,
724
+ device: Optional[torch.device] = None,
725
+ ) -> tuple[torch.Tensor, int]:
726
+ if grid_thw is None or grid_thw.numel() == 0:
727
+ # Fallback for no grid_thw: treat as single sequence
728
+ return torch.tensor([0, total_patches], dtype=torch.int32, device=device), total_patches
729
+
730
+ if device is None:
731
+ device = grid_thw.device
732
+
733
+ cu_seqlens = [0]
734
+ max_seqlen = 0
735
+ total_entries = grid_thw.shape[0]
736
+ current_len = 0
737
+
738
+ # Calculate cumulative lengths: split sequences based on fixed_t if provided
739
+ for idx in range(total_entries):
740
+ t_val = grid_thw[idx, 0].item()
741
+ h_val = grid_thw[idx, 1].item()
742
+ w_val = grid_thw[idx, 2].item()
743
+
744
+ if fixed_t is not None and fixed_t > 0 and t_val > fixed_t:
745
+ # Split large t into chunks of fixed_t
746
+ num_full_windows = t_val // fixed_t
747
+ remainder = t_val % fixed_t
748
+
749
+ # Add full windows
750
+ for _ in range(num_full_windows):
751
+ chunk_patches = fixed_t * int(h_val) * int(w_val)
752
+ current_len += chunk_patches
753
+ max_seqlen = max(max_seqlen, chunk_patches)
754
+ cu_seqlens.append(current_len)
755
+
756
+ # Add remainder if any
757
+ if remainder > 0:
758
+ chunk_patches = remainder * int(h_val) * int(w_val)
759
+ current_len += chunk_patches
760
+ max_seqlen = max(max_seqlen, chunk_patches)
761
+ cu_seqlens.append(current_len)
762
+ else:
763
+ # Standard case: add as one chunk
764
+ chunk_patches = t_val * int(h_val) * int(w_val)
765
+ current_len += chunk_patches
766
+ max_seqlen = max(max_seqlen, chunk_patches)
767
+ cu_seqlens.append(current_len)
768
+
769
+ last_len = cu_seqlens[-1]
770
+ if last_len != total_patches:
771
+ raise ValueError(
772
+ "cu_seqlens calculation mismatch:\n"
773
+ f"- total_patches: {total_patches}\n"
774
+ f"- calculated total: {last_len}\n"
775
+ f"- grid_thw: {grid_thw}"
776
+ )
777
+
778
+ return torch.tensor(cu_seqlens, dtype=torch.int32, device=device), max_seqlen
779
+
780
+ def _build_block_attention_mask(
781
+ self,
782
+ grid_thw: torch.Tensor,
783
+ total_patches: int,
784
+ fixed_t: Optional[int] = 4,
785
+ device: Optional[torch.device] = None,
786
+ ) -> Optional[torch.Tensor]:
787
+ if grid_thw is None or grid_thw.numel() == 0:
788
+ return None
789
+
790
+ if device is None:
791
+ device = grid_thw.device
792
+
793
+ lengths = []
794
+ total_entries = grid_thw.shape[0]
795
+
796
+ for idx in range(total_entries):
797
+ t_val = grid_thw[idx, 0].item()
798
+ h_val = grid_thw[idx, 1].item()
799
+ w_val = grid_thw[idx, 2].item()
800
+
801
+ if fixed_t is not None and fixed_t > 0 and t_val > fixed_t:
802
+ # Split large t into chunks of fixed_t
803
+ num_full_windows = t_val // fixed_t
804
+ remainder = t_val % fixed_t
805
+
806
+ # Add full windows
807
+ for _ in range(num_full_windows):
808
+ lengths.append(fixed_t * int(h_val) * int(w_val))
809
+
810
+ # Add remainder if any
811
+ if remainder > 0:
812
+ lengths.append(remainder * int(h_val) * int(w_val))
813
+ else:
814
+ lengths.append(t_val * int(h_val) * int(w_val))
815
+
816
+ total_len = sum(lengths)
817
+ if total_len != total_patches:
818
+ raise ValueError(
819
+ "Block attention mask length mismatch:\n"
820
+ f"- total_patches: {total_patches}\n"
821
+ f"- total_len: {total_len}\n"
822
+ f"- grid_thw: {grid_thw}"
823
+ )
824
+
825
+ attn_mask = torch.ones((total_len, total_len), dtype=torch.bool, device=device)
826
+ start = 0
827
+ for size in lengths:
828
+ end = start + size
829
+ attn_mask[start:end, start:end] = False
830
+ start = end
831
+
832
+ return attn_mask
833
+
834
+ @replace_return_docstrings(output_type=BaseModelOutputWithPooling, config_class=MageVLVisionConfig)
835
+ def forward(
836
+ self,
837
+ hidden_state: torch.Tensor,
838
+ grid_thw: Optional[torch.Tensor] = None,
839
+ patch_positions: Optional[torch.Tensor] = None,
840
+ output_attentions: Optional[bool] = None,
841
+ output_hidden_states: Optional[bool] = None,
842
+ return_dict: Optional[bool] = None,
843
+ skip_merger: Optional[bool] = False,
844
+ ) -> Union[tuple, BaseModelOutputWithPooling]:
845
+ r"""
846
+ Forward pass for vision model.
847
+
848
+ This method accepts pre-processed patches from Qwen2VL image processor and applies
849
+ RoPE (Rotary Position Embedding) in 2x2 block layout to match the spatial arrangement
850
+ of patches.
851
+
852
+ Args:
853
+ hidden_state: Pre-processed patches from Qwen2VL processor.
854
+ Shape: [total_patches, num_channels, patch_size, patch_size]
855
+ grid_thw: Grid sizes tensor of shape [num_samples, 3] with [t, h, w] for each sample.
856
+ Required for computing RoPE and handling visible indices.
857
+ patch_positions: Optional explicit patch positions for RoPE computation.
858
+ output_attentions: Whether to return attention weights.
859
+ output_hidden_states: Whether to return all hidden states.
860
+ return_dict: Whether to return a ModelOutput instead of tuple.
861
+ skip_merger: If True, skip patch merger (useful for consistency checking).
862
+
863
+ Returns:
864
+ BaseModelOutputWithPooling with last_hidden_state containing merged features.
865
+ """
866
+ output_attentions = (
867
+ output_attentions if output_attentions is not None else getattr(self.config, "output_attentions", False)
868
+ )
869
+ output_hidden_states = (
870
+ output_hidden_states
871
+ if output_hidden_states is not None
872
+ else getattr(self.config, "output_hidden_states", False)
873
+ )
874
+ return_dict = True if return_dict is None else return_dict
875
+
876
+ # 1. Embeddings
877
+ # Note: embeddings returns [total_patches, embed_dim], we need to add batch dimension
878
+ hidden_states = self.embeddings(hidden_state)
879
+ if hidden_states.dim() == 2:
880
+ hidden_states = hidden_states.unsqueeze(0) # [1, total_patches, embed_dim]
881
+ batch_size, total_patches, _ = hidden_states.shape
882
+
883
+ # 2. RoPE Construction
884
+ if patch_positions is not None and patch_positions.dim() == 3:
885
+ patch_positions = patch_positions.squeeze(0)
886
+ freqs_visible = self.video_rope.forward_from_positions(patch_positions)
887
+
888
+ # Concatenate D/2 + D/2 -> D for applying rope
889
+ freqs_visible = torch.cat([freqs_visible, freqs_visible], dim=-1)
890
+ if freqs_visible.dim() == 2:
891
+ freqs_visible = freqs_visible.unsqueeze(0)
892
+
893
+ # 3. Pre-Norm & Encoder
894
+ hidden_states = self.layernorm_pre(hidden_states)
895
+
896
+ cu_seqlens, max_seqlen = self._build_cu_seqlens(
897
+ grid_thw=grid_thw,
898
+ total_patches=total_patches,
899
+ fixed_t=getattr(self.config, "frame_windows_size", 4),
900
+ device=hidden_states.device,
901
+ )
902
+
903
+ encoder_outputs = self.encoder(
904
+ hidden_states,
905
+ attention_mask=None,
906
+ rotary_pos_emb=freqs_visible,
907
+ output_attentions=output_attentions,
908
+ output_hidden_states=True, # Always get hidden states to use -2 layer
909
+ return_dict=True,
910
+ cu_seqlens=cu_seqlens,
911
+ max_seqlen=max_seqlen,
912
+ )
913
+
914
+ # Use second-to-last layer output for better feature representation
915
+ if encoder_outputs.hidden_states is not None and len(encoder_outputs.hidden_states) >= 2 and not skip_merger:
916
+ sequence_output = encoder_outputs.hidden_states[-1]
917
+ else:
918
+ sequence_output = encoder_outputs[0]
919
+
920
+ # Post-Norm
921
+ if self.layernorm_post is not None:
922
+ sequence_output = self.layernorm_post(sequence_output)
923
+
924
+ # Skip merger for consistency check with original ViT
925
+ if skip_merger:
926
+ pooled_output = None
927
+ if self.head is not None:
928
+ pooled_output = self.head(sequence_output)
929
+
930
+ if not return_dict:
931
+ return (sequence_output, pooled_output) + (
932
+ encoder_outputs.hidden_states if output_hidden_states else None,
933
+ )
934
+ return BaseModelOutputWithPooling(
935
+ last_hidden_state=sequence_output,
936
+ pooler_output=pooled_output,
937
+ hidden_states=encoder_outputs.hidden_states if output_hidden_states else None,
938
+ attentions=encoder_outputs.attentions if output_attentions else None,
939
+ )
940
+
941
+ # Patch merger: input patches are already in 2x2 block order from Qwen2VL processor
942
+ merged_output = self.merger(sequence_output, patch_positions=patch_positions)
943
+
944
+ if not return_dict:
945
+ return (merged_output,) + (encoder_outputs.hidden_states if output_hidden_states else None,)
946
+
947
+ return BaseModelOutputWithPooling(
948
+ last_hidden_state=merged_output,
949
+ pooler_output=None,
950
+ hidden_states=encoder_outputs.hidden_states if output_hidden_states else None,
951
+ attentions=encoder_outputs.attentions if output_attentions else None,
952
+ )
953
+
954
+
955
+ @auto_docstring
956
+ class MageVLModel(MageVLPreTrainedModel):
957
+ base_model_prefix = ""
958
+ # Reference: fix gemma3 grad acc #37208
959
+ accepts_loss_kwargs = False
960
+ config: MageVLConfig
961
+ _no_split_modules = ["MageVLVisionEncoderLayer", "Qwen3DecoderLayer"]
962
+
963
+ def __init__(self, config: MageVLConfig):
964
+ super().__init__(config)
965
+ self.visual = MageVLVisionPretrainedModel._from_config(config.vision_config)
966
+ self.language_model = AutoModel.from_config(config.text_config)
967
+ self.streammind_gate = None
968
+ self._streammind_model_path = None
969
+
970
+ # Initialize weights and apply final processing
971
+ self.post_init()
972
+
973
+ def _load_streammind_gate(self):
974
+ if self.streammind_gate is not None:
975
+ return self.streammind_gate
976
+ from safetensors.torch import load_file
977
+ from .streammind_gate import StreamMindGate
978
+
979
+ if not self._streammind_model_path:
980
+ raise RuntimeError(
981
+ "StreamMind gate path is unavailable. Load the model with "
982
+ "MageVLStreamMindForConditionalGeneration.from_pretrained()."
983
+ )
984
+ gate = StreamMindGate(self.config.text_config.hidden_size)
985
+ gate_path = Path(self._streammind_model_path) / "streammind_gate.safetensors"
986
+ if not gate_path.is_file():
987
+ from huggingface_hub import hf_hub_download
988
+
989
+ gate_path = Path(
990
+ hf_hub_download(
991
+ repo_id=self._streammind_model_path,
992
+ filename="streammind_gate.safetensors",
993
+ )
994
+ )
995
+ state = load_file(str(gate_path))
996
+ gate.load_state_dict(state, strict=True)
997
+ gate.to(
998
+ device=next(self.visual.parameters()).device,
999
+ dtype=next(self.visual.parameters()).dtype,
1000
+ ).eval()
1001
+ self.streammind_gate = gate
1002
+ return gate
1003
+
1004
+ def _streammind_vision_tokens(self, pixel_values, image_grid_thw, patch_positions=None):
1005
+ pixel_values = pixel_values.type(self.visual.embeddings.patch_embedding.weight.dtype)
1006
+ rope = self.visual.video_rope
1007
+ saved_rope = (rope.inv_freq_t, rope.inv_freq_h, rope.inv_freq_w)
1008
+ try:
1009
+ # The StreamMind checkpoint was trained after the whole OV model,
1010
+ # including non-persistent RoPE buffers, was cast to BF16.
1011
+ rope.inv_freq_t = rope.inv_freq_t.to(pixel_values.dtype)
1012
+ rope.inv_freq_h = rope.inv_freq_h.to(pixel_values.dtype)
1013
+ rope.inv_freq_w = rope.inv_freq_w.to(pixel_values.dtype)
1014
+ vision_output = self.visual(
1015
+ pixel_values,
1016
+ grid_thw=image_grid_thw,
1017
+ patch_positions=patch_positions,
1018
+ )
1019
+ finally:
1020
+ rope.inv_freq_t, rope.inv_freq_h, rope.inv_freq_w = saved_rope
1021
+ merged = vision_output.last_hidden_state.reshape(-1, vision_output.last_hidden_state.shape[-1])
1022
+ merge = self.visual.spatial_merge_size
1023
+ time = int(image_grid_thw[:, 0].sum().item())
1024
+ patches_per_time = int(
1025
+ (image_grid_thw[0, 1].item() // merge)
1026
+ * (image_grid_thw[0, 2].item() // merge)
1027
+ )
1028
+ vision_tokens = merged.reshape(1, time, patches_per_time, -1)
1029
+ return vision_tokens
1030
+
1031
+ def streammind_gate_forward(self, pixel_values, image_grid_thw, patch_positions=None):
1032
+ """Run the gate on one segment without changing the base LLM visual path."""
1033
+ vision_tokens = self._streammind_vision_tokens(
1034
+ pixel_values, image_grid_thw, patch_positions=patch_positions
1035
+ )
1036
+ return self._load_streammind_gate()(vision_tokens)
1037
+
1038
+ def streammind_gate_forward_segments(self, segments):
1039
+ """Run EPFE continuously over a list of codec segments from one stream."""
1040
+ tokens = [
1041
+ self._streammind_vision_tokens(
1042
+ segment["pixel_values"],
1043
+ segment["image_grid_thw"],
1044
+ patch_positions=segment.get("patch_positions"),
1045
+ )
1046
+ for segment in segments
1047
+ ]
1048
+ lengths = [token.shape[1] for token in tokens]
1049
+ boundaries = torch.tensor(lengths).cumsum(0).tolist()
1050
+ return self._load_streammind_gate()(
1051
+ torch.cat(tokens, dim=1), response_positions=boundaries
1052
+ )
1053
+
1054
+ def get_input_embeddings(self):
1055
+ return self.language_model.get_input_embeddings()
1056
+
1057
+ def set_input_embeddings(self, value):
1058
+ self.language_model.set_input_embeddings(value)
1059
+
1060
+ def set_decoder(self, decoder):
1061
+ self.language_model = decoder
1062
+
1063
+ def get_decoder(self):
1064
+ return self.language_model
1065
+
1066
+ def get_video_features(
1067
+ self,
1068
+ pixel_values_videos: torch.FloatTensor,
1069
+ video_grid_thw: Optional[torch.LongTensor] = None,
1070
+ patch_positions=None,
1071
+ ):
1072
+ """
1073
+ Encodes videos into continuous embeddings that can be forwarded to the language model.
1074
+
1075
+ Args:
1076
+ pixel_values_videos: Pre-processed patches from Qwen2VL processor.
1077
+ `torch.FloatTensor` of shape `(total_patches, num_channels, patch_size, patch_size)`
1078
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
1079
+ The temporal, height and width of feature shape of each video in LLM.
1080
+ """
1081
+ # Convert to correct dtype
1082
+ pixel_values_videos = pixel_values_videos.type(self.visual.embeddings.patch_embedding.weight.dtype)
1083
+
1084
+ # Forward through vision model with grid_thw
1085
+ vision_output = self.visual(pixel_values_videos, grid_thw=video_grid_thw, patch_positions=patch_positions)
1086
+
1087
+ # Extract the actual tensor from BaseModelOutputWithPooling
1088
+ if hasattr(vision_output, "last_hidden_state"):
1089
+ video_embeds = vision_output.last_hidden_state
1090
+ else:
1091
+ video_embeds = vision_output[0] # Fallback for tuple output
1092
+
1093
+ # Compute split sizes from video_grid_thw or from input shape
1094
+ if video_grid_thw is not None:
1095
+ split_sizes = (video_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist()
1096
+ else:
1097
+ # Compute from input shape
1098
+ batch_size = pixel_values_videos.shape[0]
1099
+ split_sizes = [video_embeds.shape[1]] * batch_size
1100
+
1101
+ # Split embeddings per video
1102
+ if len(split_sizes) > 1:
1103
+ video_embeds = torch.split(video_embeds.view(-1, video_embeds.shape[-1]), split_sizes)
1104
+ else:
1105
+ video_embeds = [video_embeds.view(-1, video_embeds.shape[-1])]
1106
+
1107
+ return video_embeds
1108
+
1109
+ def get_image_features(
1110
+ self, pixel_values, image_grid_thw: Optional[torch.LongTensor] = None, patch_positions=None
1111
+ ):
1112
+ """
1113
+ Encodes images into continuous embeddings that can be forwarded to the language model.
1114
+
1115
+ Args:
1116
+ pixel_values: Pre-processed patches from Qwen2VL processor.
1117
+ - `torch.FloatTensor` of shape `(total_patches, num_channels, patch_size, patch_size)`
1118
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
1119
+ The temporal, height and width of feature shape of each image in LLM.
1120
+ """
1121
+ # Standard format from Qwen2VL processor
1122
+ if pixel_values.dim() == 2:
1123
+ # Convert to correct dtype
1124
+ pixel_values = pixel_values.type(self.visual.embeddings.patch_embedding.weight.dtype)
1125
+
1126
+ # Forward through vision model with grid_thw
1127
+ vision_output = self.visual(pixel_values, grid_thw=image_grid_thw, patch_positions=patch_positions)
1128
+
1129
+ # Extract the actual tensor from BaseModelOutputWithPooling
1130
+ if hasattr(vision_output, "last_hidden_state"):
1131
+ image_embeds = vision_output.last_hidden_state
1132
+ else:
1133
+ image_embeds = vision_output[0]
1134
+
1135
+ # Compute split sizes from grid_thw
1136
+ if image_grid_thw is not None:
1137
+ split_sizes = (image_grid_thw.prod(-1) // self.visual.spatial_merge_size**2).tolist()
1138
+ else:
1139
+ # Fallback: assume single image
1140
+ split_sizes = [image_embeds.shape[0] if image_embeds.dim() == 2 else image_embeds.shape[1]]
1141
+
1142
+ # Split embeddings per image
1143
+ image_embeds_flat = image_embeds.view(-1, image_embeds.shape[-1])
1144
+ if len(split_sizes) > 1:
1145
+ image_embeds = list(torch.split(image_embeds_flat, split_sizes))
1146
+ else:
1147
+ image_embeds = [image_embeds_flat]
1148
+
1149
+ return image_embeds
1150
+ else:
1151
+ raise ValueError(
1152
+ f"Unsupported pixel_values shape: expected 4D tensor [total_patches, C, H, W], "
1153
+ f"got {pixel_values.shape if hasattr(pixel_values, 'shape') else type(pixel_values)}"
1154
+ )
1155
+
1156
+ def get_placeholder_mask(
1157
+ self,
1158
+ input_ids: torch.LongTensor,
1159
+ inputs_embeds: torch.FloatTensor,
1160
+ image_features: Optional[torch.FloatTensor] = None,
1161
+ video_features: Optional[torch.FloatTensor] = None,
1162
+ ):
1163
+ """
1164
+ Obtains multimodal placeholder mask from `input_ids` or `inputs_embeds`, and checks that the placeholder token count is
1165
+ equal to the length of multimodal features. If the lengths are different, an error is raised.
1166
+ """
1167
+ if input_ids is None:
1168
+ special_image_mask = inputs_embeds == self.get_input_embeddings()(
1169
+ torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
1170
+ )
1171
+ special_image_mask = special_image_mask.all(-1)
1172
+ special_video_mask = inputs_embeds == self.get_input_embeddings()(
1173
+ torch.tensor(self.config.video_token_id, dtype=torch.long, device=inputs_embeds.device)
1174
+ )
1175
+ special_video_mask = special_video_mask.all(-1)
1176
+ else:
1177
+ special_image_mask = input_ids == self.config.image_token_id
1178
+ special_video_mask = input_ids == self.config.video_token_id
1179
+
1180
+ n_image_tokens = special_image_mask.sum()
1181
+ special_image_mask = special_image_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
1182
+ if image_features is not None and inputs_embeds[special_image_mask].numel() != image_features.numel():
1183
+ raise ValueError(
1184
+ f"Image features and image tokens do not match: tokens: {n_image_tokens}, features {image_features.shape[0]}"
1185
+ )
1186
+
1187
+ n_video_tokens = special_video_mask.sum()
1188
+ special_video_mask = special_video_mask.unsqueeze(-1).expand_as(inputs_embeds).to(inputs_embeds.device)
1189
+ if video_features is not None and inputs_embeds[special_video_mask].numel() != video_features.numel():
1190
+ raise ValueError(
1191
+ f"Videos features and video tokens do not match: tokens: {n_video_tokens}, features {video_features.shape[0]}"
1192
+ )
1193
+
1194
+ return special_image_mask, special_video_mask
1195
+
1196
+ @auto_docstring
1197
+ def forward(
1198
+ self,
1199
+ input_ids: Optional[torch.LongTensor] = None,
1200
+ attention_mask: Optional[torch.Tensor] = None,
1201
+ position_ids: Optional[torch.LongTensor] = None,
1202
+ past_key_values: Optional[Cache] = None,
1203
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1204
+ use_cache: Optional[bool] = None,
1205
+ output_attentions: Optional[bool] = None,
1206
+ output_hidden_states: Optional[bool] = None,
1207
+ return_dict: Optional[bool] = None,
1208
+ pixel_values: Optional[torch.Tensor] = None,
1209
+ pixel_values_videos: Optional[torch.FloatTensor] = None,
1210
+ image_grid_thw: Optional[torch.LongTensor] = None,
1211
+ patch_positions: Optional[torch.LongTensor] = None,
1212
+ video_grid_thw: Optional[torch.LongTensor] = None,
1213
+ cache_position: Optional[torch.LongTensor] = None,
1214
+ second_per_grid_ts: Optional[torch.Tensor] = None,
1215
+ **kwargs: Unpack[TransformersKwargs],
1216
+ ) -> Union[tuple, MageVLModelOutputWithPast]:
1217
+ r"""
1218
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
1219
+ The temporal, height and width of feature shape of each image in LLM.
1220
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
1221
+ The temporal, height and width of feature shape of each video in LLM.
1222
+ patch_positions (`torch.LongTensor` of shape `(total_patches, 3)` or `(1, total_patches, 3)`, *optional*):
1223
+ Explicit per-patch `(t, h, w)` position indices used by the vision tower to compute 3D rotary
1224
+ position embeddings (and the optional absolute position embedding inside the patch merger).
1225
+ `total_patches` is the sum of `t * h * w` across all images and videos in the batch, matching
1226
+ the layout produced by the Qwen2VL-style image processor.
1227
+ second_per_grid_ts (`torch.Tensor` of shape `(num_videos)`, *optional*):
1228
+ The time interval (in seconds) for each grid along the temporal dimension in the 3D position IDs.
1229
+ cache_position (`torch.LongTensor` of shape `(sequence_length)`, *optional*):
1230
+ Indices depicting the position of the input sequence tokens in the sequence. Contrarily to
1231
+ `position_ids`, this tensor is not affected by padding.
1232
+
1233
+ Note: see the top-level ``MageVLForConditionalGeneration.forward``
1234
+ docstring; currently video flows in via the ``image_grid_thw`` / ``pixel_values``
1235
+ alias, so ``pixel_values_videos`` / ``video_grid_thw`` /
1236
+ ``second_per_grid_ts`` are unused at this layer.
1237
+ """
1238
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
1239
+ output_hidden_states = (
1240
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
1241
+ )
1242
+ return_dict = True if return_dict is None else return_dict
1243
+
1244
+ if inputs_embeds is None:
1245
+ inputs_embeds = self.get_input_embeddings()(input_ids)
1246
+
1247
+ image_embeds = None
1248
+
1249
+ if pixel_values is not None:
1250
+ image_embeds = self.get_image_features(pixel_values, image_grid_thw, patch_positions=patch_positions)
1251
+
1252
+ if image_embeds is not None:
1253
+ image_embeds = torch.cat(image_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
1254
+ image_mask, _ = self.get_placeholder_mask(
1255
+ input_ids, inputs_embeds=inputs_embeds, image_features=image_embeds
1256
+ )
1257
+ inputs_embeds = inputs_embeds.masked_scatter(image_mask, image_embeds)
1258
+
1259
+ if pixel_values_videos is not None:
1260
+ video_embeds = self.get_video_features(
1261
+ pixel_values_videos, video_grid_thw, patch_positions=patch_positions
1262
+ )
1263
+ video_embeds = torch.cat(video_embeds, dim=0).to(inputs_embeds.device, inputs_embeds.dtype)
1264
+ _, video_mask = self.get_placeholder_mask(
1265
+ input_ids, inputs_embeds=inputs_embeds, video_features=video_embeds
1266
+ )
1267
+ inputs_embeds = inputs_embeds.masked_scatter(video_mask, video_embeds)
1268
+
1269
+ # Use simple 1D position_ids
1270
+ if position_ids is None:
1271
+ batch_size, seq_length, _ = inputs_embeds.shape
1272
+ if attention_mask is not None:
1273
+ position_ids = attention_mask.long().cumsum(-1) - 1
1274
+ position_ids.masked_fill_(attention_mask == 0, 1)
1275
+ else:
1276
+ position_ids = (
1277
+ torch.arange(seq_length, device=inputs_embeds.device).unsqueeze(0).expand(batch_size, -1)
1278
+ )
1279
+
1280
+ # Handle cache_position for generation
1281
+ if cache_position is not None and cache_position[0] != 0:
1282
+ position_ids = position_ids + cache_position[0]
1283
+
1284
+ outputs = self.language_model(
1285
+ input_ids=None,
1286
+ position_ids=position_ids,
1287
+ attention_mask=attention_mask,
1288
+ past_key_values=past_key_values,
1289
+ inputs_embeds=inputs_embeds,
1290
+ use_cache=use_cache,
1291
+ output_attentions=output_attentions,
1292
+ output_hidden_states=output_hidden_states,
1293
+ return_dict=True,
1294
+ cache_position=cache_position,
1295
+ **kwargs,
1296
+ )
1297
+
1298
+ output = MageVLModelOutputWithPast(
1299
+ last_hidden_state=outputs.last_hidden_state,
1300
+ past_key_values=outputs.past_key_values,
1301
+ hidden_states=outputs.hidden_states,
1302
+ attentions=outputs.attentions,
1303
+ )
1304
+ return output if return_dict else output.to_tuple()
1305
+
1306
+
1307
+ @auto_docstring
1308
+ class MageVLForConditionalGeneration(MageVLPreTrainedModel, GenerationMixin):
1309
+ _tied_weights_keys = {"lm_head.weight": "model.language_model.embed_tokens.weight"}
1310
+ # Reference: fix gemma3 grad acc #37208
1311
+ accepts_loss_kwargs = False
1312
+
1313
+ def __init__(self, config):
1314
+ super().__init__(config)
1315
+ self.model = MageVLModel(config)
1316
+ self.lm_head = nn.Linear(config.text_config.hidden_size, config.text_config.vocab_size, bias=False)
1317
+ self.post_init()
1318
+
1319
+ @classmethod
1320
+ def from_pretrained(cls, pretrained_model_name_or_path, *args, **kwargs):
1321
+ model = super().from_pretrained(pretrained_model_name_or_path, *args, **kwargs)
1322
+ model.model._streammind_model_path = str(pretrained_model_name_or_path)
1323
+ return model
1324
+
1325
+ def get_input_embeddings(self):
1326
+ return self.model.get_input_embeddings()
1327
+
1328
+ def set_input_embeddings(self, value):
1329
+ self.model.set_input_embeddings(value)
1330
+
1331
+ def set_decoder(self, decoder):
1332
+ self.model.set_decoder(decoder)
1333
+
1334
+ def get_decoder(self):
1335
+ return self.model.get_decoder()
1336
+
1337
+ def get_video_features(
1338
+ self,
1339
+ pixel_values_videos: torch.FloatTensor,
1340
+ video_grid_thw: Optional[torch.LongTensor] = None,
1341
+ patch_positions=None,
1342
+ ):
1343
+ return self.model.get_video_features(pixel_values_videos, video_grid_thw, patch_positions=patch_positions)
1344
+
1345
+ def get_image_features(self, pixel_values: torch.FloatTensor, image_grid_thw: Optional[torch.LongTensor] = None):
1346
+ return self.model.get_image_features(pixel_values, image_grid_thw)
1347
+
1348
+ # Make modules available through conditional class for BC
1349
+ @property
1350
+ def language_model(self):
1351
+ return self.model.language_model
1352
+
1353
+ @property
1354
+ def visual(self):
1355
+ return self.model.visual
1356
+
1357
+ def streammind_gate_forward(self, pixel_values, image_grid_thw, patch_positions=None):
1358
+ return self.model.streammind_gate_forward(
1359
+ pixel_values,
1360
+ image_grid_thw,
1361
+ patch_positions=patch_positions,
1362
+ )
1363
+
1364
+ def streammind_gate_forward_segments(self, segments):
1365
+ return self.model.streammind_gate_forward_segments(segments)
1366
+
1367
+ @can_return_tuple
1368
+ @auto_docstring
1369
+ def forward(
1370
+ self,
1371
+ input_ids: Optional[torch.LongTensor] = None,
1372
+ attention_mask: Optional[torch.Tensor] = None,
1373
+ position_ids: Optional[torch.LongTensor] = None,
1374
+ past_key_values: Optional[Cache] = None,
1375
+ inputs_embeds: Optional[torch.FloatTensor] = None,
1376
+ labels: Optional[torch.LongTensor] = None,
1377
+ use_cache: Optional[bool] = None,
1378
+ output_attentions: Optional[bool] = None,
1379
+ output_hidden_states: Optional[bool] = None,
1380
+ pixel_values: Optional[torch.Tensor] = None,
1381
+ pixel_values_videos: Optional[torch.FloatTensor] = None,
1382
+ image_grid_thw: Optional[torch.LongTensor] = None,
1383
+ patch_positions: Optional[torch.LongTensor] = None,
1384
+ video_grid_thw: Optional[torch.LongTensor] = None,
1385
+ cache_position: Optional[torch.LongTensor] = None,
1386
+ second_per_grid_ts: Optional[torch.Tensor] = None,
1387
+ logits_to_keep: Union[int, torch.Tensor] = 0,
1388
+ **kwargs: Unpack[TransformersKwargs],
1389
+ ) -> Union[tuple, MageVLCausalLMOutputWithPast]:
1390
+ r"""
1391
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
1392
+ Labels for computing the masked language modeling loss. Indices should either be in `[0, ...,
1393
+ config.vocab_size]` or -100 (see `input_ids` docstring). Tokens with indices set to `-100` are ignored
1394
+ (masked), the loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`.
1395
+ image_grid_thw (`torch.LongTensor` of shape `(num_images, 3)`, *optional*):
1396
+ The temporal, height and width of feature shape of each image in LLM.
1397
+ video_grid_thw (`torch.LongTensor` of shape `(num_videos, 3)`, *optional*):
1398
+ The temporal, height and width of feature shape of each video in LLM.
1399
+ patch_positions (`torch.LongTensor` of shape `(total_patches, 3)` or `(1, total_patches, 3)`, *optional*):
1400
+ Explicit per-patch `(t, h, w)` position indices used by the vision tower to compute 3D rotary
1401
+ position embeddings (and the optional absolute position embedding inside the patch merger).
1402
+ `total_patches` is the sum of `t * h * w` across all images and videos in the batch, matching
1403
+ the layout produced by the Qwen2VL-style image processor.
1404
+ second_per_grid_ts (`torch.Tensor` of shape `(num_videos)`, *optional*):
1405
+ The time interval (in seconds) for each grid along the temporal dimension in the 3D position IDs.
1406
+ cache_position (`torch.LongTensor` of shape `(sequence_length)`, *optional*):
1407
+ Indices depicting the position of the input sequence tokens in the sequence. Contrarily to
1408
+ `position_ids`, this tensor is not affected by padding.
1409
+
1410
+ Note (native-video alias):
1411
+ The companion ``MageVLProcessor.__call__(videos=...)`` does NOT
1412
+ pass ``pixel_values_videos`` / ``video_grid_thw`` / ``second_per_grid_ts``
1413
+ to this forward. Instead it aliases the video patch tensor as
1414
+ ``pixel_values=`` and ``image_grid_thw=``, so video inputs share the
1415
+ same code path as multi-image inputs (Mage-ViT is purely
1416
+ spatial; temporal information is carried by per-frame ``<X.X seconds>``
1417
+ text tags emitted by the processor). The ``*_videos`` and
1418
+ ``second_per_grid_ts`` kwargs are kept declared here only for API
1419
+ completeness and future use (e.g. 3D mRoPE / ``get_rope_index``); they
1420
+ are NOT consumed by the current Mage-ViT encoder.
1421
+
1422
+ Example:
1423
+
1424
+ ```python
1425
+ >>> from PIL import Image
1426
+ >>> import requests
1427
+ >>> from transformers import AutoProcessor, MageVLForConditionalGeneration
1428
+
1429
+ >>> model = MageVLForConditionalGeneration.from_pretrained("microsoft/Mage-VL", trust_remote_code=True)
1430
+ >>> processor = AutoProcessor.from_pretrained("microsoft/Mage-VL", trust_remote_code=True)
1431
+
1432
+ >>> messages = [
1433
+ {
1434
+ "role": "user",
1435
+ "content": [
1436
+ {"type": "image"},
1437
+ {"type": "text", "text": "What is shown in this image?"},
1438
+ ],
1439
+ },
1440
+ ]
1441
+ >>> url = "https://www.ilankelman.org/stopsigns/australia.jpg"
1442
+ >>> image = Image.open(requests.get(url, stream=True).raw)
1443
+
1444
+ >>> text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
1445
+ >>> inputs = processor(text=[text], images=[image], return_tensors="pt")
1446
+
1447
+ >>> # Generate
1448
+ >>> generate_ids = model.generate(inputs.input_ids, max_length=30)
1449
+ >>> tokenizer.batch_decode(generate_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False)[0]
1450
+ "The image shows a street scene with a red stop sign in the foreground. In the background, there is a large red gate with Chinese characters ..."
1451
+ ```"""
1452
+ output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions
1453
+ output_hidden_states = (
1454
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
1455
+ )
1456
+ outputs = self.model(
1457
+ input_ids=input_ids,
1458
+ pixel_values=pixel_values,
1459
+ pixel_values_videos=pixel_values_videos,
1460
+ image_grid_thw=image_grid_thw,
1461
+ patch_positions=patch_positions,
1462
+ video_grid_thw=video_grid_thw,
1463
+ second_per_grid_ts=second_per_grid_ts,
1464
+ position_ids=position_ids,
1465
+ attention_mask=attention_mask,
1466
+ past_key_values=past_key_values,
1467
+ inputs_embeds=inputs_embeds,
1468
+ use_cache=use_cache,
1469
+ output_attentions=output_attentions,
1470
+ output_hidden_states=output_hidden_states,
1471
+ return_dict=True,
1472
+ cache_position=cache_position,
1473
+ **kwargs,
1474
+ )
1475
+
1476
+ hidden_states = outputs[0]
1477
+
1478
+ # Only compute necessary logits, and do not upcast them to float if we are not computing the loss
1479
+ slice_indices = slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
1480
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
1481
+
1482
+ loss = None
1483
+ if labels is not None:
1484
+ loss = self.loss_function(
1485
+ logits=logits, labels=labels, vocab_size=self.config.text_config.vocab_size, **kwargs
1486
+ )
1487
+
1488
+ return MageVLCausalLMOutputWithPast(
1489
+ loss=loss,
1490
+ logits=logits,
1491
+ past_key_values=outputs.past_key_values,
1492
+ hidden_states=outputs.hidden_states,
1493
+ attentions=outputs.attentions,
1494
+ )
1495
+
1496
+
1497
+
1498
+ def prepare_inputs_for_generation(
1499
+ self,
1500
+ input_ids,
1501
+ past_key_values=None,
1502
+ attention_mask=None,
1503
+ inputs_embeds=None,
1504
+ cache_position=None,
1505
+ position_ids=None,
1506
+ use_cache=True,
1507
+ pixel_values=None,
1508
+ pixel_values_videos=None,
1509
+ image_grid_thw=None,
1510
+ patch_positions=None,
1511
+ video_grid_thw=None,
1512
+ second_per_grid_ts=None,
1513
+ is_first_iteration=False,
1514
+ **kwargs,
1515
+ ):
1516
+ # Overwritten -- in specific circumstances we don't want to forward image inputs to the model
1517
+ model_inputs = super().prepare_inputs_for_generation(
1518
+ input_ids,
1519
+ past_key_values=past_key_values,
1520
+ attention_mask=attention_mask,
1521
+ inputs_embeds=inputs_embeds,
1522
+ cache_position=cache_position,
1523
+ position_ids=position_ids,
1524
+ pixel_values=pixel_values,
1525
+ pixel_values_videos=pixel_values_videos,
1526
+ image_grid_thw=image_grid_thw,
1527
+ video_grid_thw=video_grid_thw,
1528
+ second_per_grid_ts=second_per_grid_ts,
1529
+ patch_positions=patch_positions,
1530
+ use_cache=use_cache,
1531
+ is_first_iteration=is_first_iteration,
1532
+ **kwargs,
1533
+ )
1534
+
1535
+ # After the prefill iteration, drop image inputs so the vision tower
1536
+ # isn't re-run on decode steps. Gating on `is_first_iteration` (the
1537
+ # Qwen3-VL convention) is the only reliable signal in transformers
1538
+ # 5.x: `past_key_values` is non-None even on the first call (an empty
1539
+ # DynamicCache is created up-front by `generate`), and `cache_position`
1540
+ # may be `None` for remote-code models.
1541
+ if not is_first_iteration and use_cache:
1542
+ model_inputs["pixel_values"] = None
1543
+ model_inputs["pixel_values_videos"] = None
1544
+
1545
+ return model_inputs
1546
+
1547
+ def _get_image_nums_and_video_nums(
1548
+ self,
1549
+ input_ids: Optional[torch.LongTensor],
1550
+ inputs_embeds: Optional[torch.Tensor] = None,
1551
+ ) -> tuple[torch.Tensor, torch.Tensor]:
1552
+ """
1553
+ Get the number of images and videos for each sample to calculate the separation length of the sample tensor.
1554
+ These parameters are not passed through the processor to avoid unpredictable impacts from interface modifications.
1555
+
1556
+ Args:
1557
+ input_ids (`torch.LongTensor` of shape `(batch_size, sequence_length)`):
1558
+ Indices of input sequence tokens in the vocabulary.
1559
+
1560
+ Returns:
1561
+ image_nums (`torch.LongTensor` of shape `(batch_size, num_images_sample)`)
1562
+ video_nums (`torch.LongTensor` of shape `(batch_size, num_videos_sample)`)
1563
+ """
1564
+ image_token_id = self.config.image_token_id
1565
+ video_token_id = self.config.video_token_id
1566
+ vision_start_token_id = self.config.vision_start_token_id
1567
+
1568
+ if inputs_embeds is not None:
1569
+ vision_start_mask = (
1570
+ inputs_embeds
1571
+ == self.get_input_embeddings()(
1572
+ torch.tensor(vision_start_token_id, dtype=torch.long, device=inputs_embeds.device)
1573
+ )
1574
+ )[..., 0]
1575
+ image_mask = (
1576
+ inputs_embeds
1577
+ == self.get_input_embeddings()(
1578
+ torch.tensor(image_token_id, dtype=torch.long, device=inputs_embeds.device)
1579
+ )
1580
+ )[..., 0]
1581
+ video_mask = (
1582
+ inputs_embeds
1583
+ == self.get_input_embeddings()(
1584
+ torch.tensor(video_token_id, dtype=torch.long, device=inputs_embeds.device)
1585
+ )
1586
+ )[..., 0]
1587
+ else:
1588
+ vision_start_mask = input_ids == vision_start_token_id
1589
+ image_mask = input_ids == image_token_id
1590
+ video_mask = input_ids == video_token_id
1591
+
1592
+ vision_first_mask = torch.roll(vision_start_mask, shifts=1, dims=1)
1593
+ image_nums = torch.sum(vision_first_mask & image_mask, dim=1)
1594
+ video_nums = torch.sum(vision_first_mask & video_mask, dim=1)
1595
+
1596
+ return image_nums, video_nums
1597
+
1598
+ def _expand_inputs_for_generation(
1599
+ self,
1600
+ expand_size: int = 1,
1601
+ is_encoder_decoder: bool = False,
1602
+ input_ids: Optional[torch.LongTensor] = None,
1603
+ **model_kwargs,
1604
+ ) -> tuple[torch.LongTensor, dict[str, Any]]:
1605
+ # Overwritten -- Support for expanding tensors without a batch size dimension
1606
+ # e.g., pixel_values, image_grid_thw, pixel_values_videos, video_grid_thw, second_per_grid_t
1607
+ # pixel_values.shape[0] is sum(seqlen_images for samples)
1608
+ # image_grid_thw.shape[0] is sum(num_images for samples)
1609
+
1610
+ if expand_size == 1:
1611
+ return input_ids, model_kwargs
1612
+
1613
+ visual_keys = [
1614
+ "pixel_values",
1615
+ "image_grid_thw",
1616
+ "pixel_values_videos",
1617
+ "video_grid_thw",
1618
+ "second_per_grid_ts",
1619
+ "patch_positions",
1620
+ ]
1621
+
1622
+ def _expand_dict_for_generation_visual(dict_to_expand):
1623
+ image_grid_thw = model_kwargs.get("image_grid_thw", None)
1624
+ video_grid_thw = model_kwargs.get("video_grid_thw", None)
1625
+ image_nums, video_nums = self._get_image_nums_and_video_nums(
1626
+ input_ids, inputs_embeds=model_kwargs.get("inputs_embeds", None)
1627
+ )
1628
+
1629
+ def _repeat_interleave_samples(x, lengths, repeat_times):
1630
+ samples = torch.split(x, lengths)
1631
+ repeat_args = [repeat_times] + [1] * (x.dim() - 1)
1632
+ result = torch.cat([sample.repeat(*repeat_args) for sample in samples], dim=0)
1633
+ return result
1634
+
1635
+ for key in dict_to_expand:
1636
+ if key == "pixel_values":
1637
+ # split images into samples
1638
+ samples = torch.split(image_grid_thw, list(image_nums))
1639
+ # compute the sequence length of images for each sample
1640
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
1641
+ dict_to_expand[key] = _repeat_interleave_samples(
1642
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
1643
+ )
1644
+ elif key == "image_grid_thw":
1645
+ # get the num of images for each sample
1646
+ lengths = list(image_nums)
1647
+ dict_to_expand[key] = _repeat_interleave_samples(
1648
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
1649
+ )
1650
+ elif key == "pixel_values_videos":
1651
+ samples = torch.split(video_grid_thw, list(video_nums))
1652
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
1653
+ dict_to_expand[key] = _repeat_interleave_samples(
1654
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
1655
+ )
1656
+ elif key == "video_grid_thw":
1657
+ lengths = list(video_nums)
1658
+ dict_to_expand[key] = _repeat_interleave_samples(
1659
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
1660
+ )
1661
+ elif key == "second_per_grid_ts":
1662
+ dict_to_expand[key] = _repeat_interleave_samples(
1663
+ dict_to_expand[key], lengths=list(video_nums), repeat_times=expand_size
1664
+ )
1665
+ elif key == "patch_positions":
1666
+ if image_grid_thw is not None and image_grid_thw.numel() > 0 and image_nums.sum() > 0:
1667
+ samples = torch.split(image_grid_thw, list(image_nums))
1668
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
1669
+ elif video_grid_thw is not None and video_grid_thw.numel() > 0 and video_nums.sum() > 0:
1670
+ samples = torch.split(video_grid_thw, list(video_nums))
1671
+ lengths = [torch.prod(sample, dim=1).sum() for sample in samples]
1672
+ else:
1673
+ continue
1674
+ dict_to_expand[key] = _repeat_interleave_samples(
1675
+ dict_to_expand[key], lengths=lengths, repeat_times=expand_size
1676
+ )
1677
+ return dict_to_expand
1678
+
1679
+ def _expand_dict_for_generation(dict_to_expand):
1680
+ for key in dict_to_expand:
1681
+ if (
1682
+ key != "cache_position"
1683
+ and dict_to_expand[key] is not None
1684
+ and isinstance(dict_to_expand[key], torch.Tensor)
1685
+ and key not in visual_keys
1686
+ ):
1687
+ dict_to_expand[key] = dict_to_expand[key].repeat_interleave(expand_size, dim=0)
1688
+ return dict_to_expand
1689
+
1690
+ model_kwargs = _expand_dict_for_generation_visual(model_kwargs)
1691
+
1692
+ if input_ids is not None:
1693
+ input_ids = input_ids.repeat_interleave(expand_size, dim=0)
1694
+
1695
+ model_kwargs = _expand_dict_for_generation(model_kwargs)
1696
+
1697
+ if is_encoder_decoder:
1698
+ if model_kwargs.get("encoder_outputs") is None:
1699
+ raise ValueError("If `is_encoder_decoder` is True, make sure that `encoder_outputs` is defined.")
1700
+ model_kwargs["encoder_outputs"] = _expand_dict_for_generation(model_kwargs["encoder_outputs"])
1701
+
1702
+ return input_ids, model_kwargs
1703
+
1704
+
1705
+ __all__ = [
1706
+ "MageVLForConditionalGeneration",
1707
+ "MageVLModel",
1708
+ "MageVLPreTrainedModel",
1709
+ ]
neural_codec/DCVC/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) Microsoft Corporation.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
neural_codec/DCVC/NOTICE.txt ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The implementation is based on [CompressAI](https://github.com/InterDigitalInc/CompressAI).
2
+ The license when we obtained the code was Apache License, Version 2.0.
3
+ Apache License
4
+ Version 2.0, January 2004
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction,
12
+ and distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "Licensor" shall mean the copyright owner or entity authorized by
15
+ the copyright owner that is granting the License.
16
+
17
+ "Legal Entity" shall mean the union of the acting entity and all
18
+ other entities that control, are controlled by, or are under common
19
+ control with that entity. For the purposes of this definition,
20
+ "control" means (i) the power, direct or indirect, to cause the
21
+ direction or management of such entity, whether by contract or
22
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
23
+ outstanding shares, or (iii) beneficial ownership of such entity.
24
+
25
+ "You" (or "Your") shall mean an individual or Legal Entity
26
+ exercising permissions granted by this License.
27
+
28
+ "Source" form shall mean the preferred form for making modifications,
29
+ including but not limited to software source code, documentation
30
+ source, and configuration files.
31
+
32
+ "Object" form shall mean any form resulting from mechanical
33
+ transformation or translation of a Source form, including but
34
+ not limited to compiled object code, generated documentation,
35
+ and conversions to other media types.
36
+
37
+ "Work" shall mean the work of authorship, whether in Source or
38
+ Object form, made available under the License, as indicated by a
39
+ copyright notice that is included in or attached to the work
40
+ (an example is provided in the Appendix below).
41
+
42
+ "Derivative Works" shall mean any work, whether in Source or Object
43
+ form, that is based on (or derived from) the Work and for which the
44
+ editorial revisions, annotations, elaborations, or other modifications
45
+ represent, as a whole, an original work of authorship. For the purposes
46
+ of this License, Derivative Works shall not include works that remain
47
+ separable from, or merely link (or bind by name) to the interfaces of,
48
+ the Work and Derivative Works thereof.
49
+
50
+ "Contribution" shall mean any work of authorship, including
51
+ the original version of the Work and any modifications or additions
52
+ to that Work or Derivative Works thereof, that is intentionally
53
+ submitted to Licensor for inclusion in the Work by the copyright owner
54
+ or by an individual or Legal Entity authorized to submit on behalf of
55
+ the copyright owner. For the purposes of this definition, "submitted"
56
+ means any form of electronic, verbal, or written communication sent
57
+ to the Licensor or its representatives, including but not limited to
58
+ communication on electronic mailing lists, source code control systems,
59
+ and issue tracking systems that are managed by, or on behalf of, the
60
+ Licensor for the purpose of discussing and improving the Work, but
61
+ excluding communication that is conspicuously marked or otherwise
62
+ designated in writing by the copyright owner as "Not a Contribution."
63
+
64
+ "Contributor" shall mean Licensor and any individual or Legal Entity
65
+ on behalf of whom a Contribution has been received by Licensor and
66
+ subsequently incorporated within the Work.
67
+
68
+ 2. Grant of Copyright License. Subject to the terms and conditions of
69
+ this License, each Contributor hereby grants to You a perpetual,
70
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71
+ copyright license to reproduce, prepare Derivative Works of,
72
+ publicly display, publicly perform, sublicense, and distribute the
73
+ Work and such Derivative Works in Source or Object form.
74
+
75
+ 3. Grant of Patent License. Subject to the terms and conditions of
76
+ this License, each Contributor hereby grants to You a perpetual,
77
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78
+ (except as stated in this section) patent license to make, have made,
79
+ use, offer to sell, sell, import, and otherwise transfer the Work,
80
+ where such license applies only to those patent claims licensable
81
+ by such Contributor that are necessarily infringed by their
82
+ Contribution(s) alone or by combination of their Contribution(s)
83
+ with the Work to which such Contribution(s) was submitted. If You
84
+ institute patent litigation against any entity (including a
85
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
86
+ or a Contribution incorporated within the Work constitutes direct
87
+ or contributory patent infringement, then any patent licenses
88
+ granted to You under this License for that Work shall terminate
89
+ as of the date such litigation is filed.
90
+
91
+ 4. Redistribution. You may reproduce and distribute copies of the
92
+ Work or Derivative Works thereof in any medium, with or without
93
+ modifications, and in Source or Object form, provided that You
94
+ meet the following conditions:
95
+
96
+ (a) You must give any other recipients of the Work or
97
+ Derivative Works a copy of this License; and
98
+
99
+ (b) You must cause any modified files to carry prominent notices
100
+ stating that You changed the files; and
101
+
102
+ (c) You must retain, in the Source form of any Derivative Works
103
+ that You distribute, all copyright, patent, trademark, and
104
+ attribution notices from the Source form of the Work,
105
+ excluding those notices that do not pertain to any part of
106
+ the Derivative Works; and
107
+
108
+ (d) If the Work includes a "NOTICE" text file as part of its
109
+ distribution, then any Derivative Works that You distribute must
110
+ include a readable copy of the attribution notices contained
111
+ within such NOTICE file, excluding those notices that do not
112
+ pertain to any part of the Derivative Works, in at least one
113
+ of the following places: within a NOTICE text file distributed
114
+ as part of the Derivative Works; within the Source form or
115
+ documentation, if provided along with the Derivative Works; or,
116
+ within a display generated by the Derivative Works, if and
117
+ wherever such third-party notices normally appear. The contents
118
+ of the NOTICE file are for informational purposes only and
119
+ do not modify the License. You may add Your own attribution
120
+ notices within Derivative Works that You distribute, alongside
121
+ or as an addendum to the NOTICE text from the Work, provided
122
+ that such additional attribution notices cannot be construed
123
+ as modifying the License.
124
+
125
+ You may add Your own copyright statement to Your modifications and
126
+ may provide additional or different license terms and conditions
127
+ for use, reproduction, or distribution of Your modifications, or
128
+ for any such Derivative Works as a whole, provided Your use,
129
+ reproduction, and distribution of the Work otherwise complies with
130
+ the conditions stated in this License.
131
+
132
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
133
+ any Contribution intentionally submitted for inclusion in the Work
134
+ by You to the Licensor shall be under the terms and conditions of
135
+ this License, without any additional terms or conditions.
136
+ Notwithstanding the above, nothing herein shall supersede or modify
137
+ the terms of any separate license agreement you may have executed
138
+ with Licensor regarding such Contributions.
139
+
140
+ 6. Trademarks. This License does not grant permission to use the trade
141
+ names, trademarks, service marks, or product names of the Licensor,
142
+ except as required for reasonable and customary use in describing the
143
+ origin of the Work and reproducing the content of the NOTICE file.
144
+
145
+ 7. Disclaimer of Warranty. Unless required by applicable law or
146
+ agreed to in writing, Licensor provides the Work (and each
147
+ Contributor provides its Contributions) on an "AS IS" BASIS,
148
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149
+ implied, including, without limitation, any warranties or conditions
150
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151
+ PARTICULAR PURPOSE. You are solely responsible for determining the
152
+ appropriateness of using or redistributing the Work and assume any
153
+ risks associated with Your exercise of permissions under this License.
154
+
155
+ 8. Limitation of Liability. In no event and under no legal theory,
156
+ whether in tort (including negligence), contract, or otherwise,
157
+ unless required by applicable law (such as deliberate and grossly
158
+ negligent acts) or agreed to in writing, shall any Contributor be
159
+ liable to You for damages, including any direct, indirect, special,
160
+ incidental, or consequential damages of any character arising as a
161
+ result of this License or out of the use or inability to use the
162
+ Work (including but not limited to damages for loss of goodwill,
163
+ work stoppage, computer failure or malfunction, or any and all
164
+ other commercial damages or losses), even if such Contributor
165
+ has been advised of the possibility of such damages.
166
+
167
+ 9. Accepting Warranty or Additional Liability. While redistributing
168
+ the Work or Derivative Works thereof, You may choose to offer,
169
+ and charge a fee for, acceptance of support, warranty, indemnity,
170
+ or other liability obligations and/or rights consistent with this
171
+ License. However, in accepting such obligations, You may act only
172
+ on Your own behalf and on Your sole responsibility, not on behalf
173
+ of any other Contributor, and only if You agree to indemnify,
174
+ defend, and hold each Contributor harmless for any liability
175
+ incurred by, or claims asserted against, such Contributor by reason
176
+ of your accepting any such warranty or additional liability.
177
+
178
+ END OF TERMS AND CONDITIONS
179
+
180
+ APPENDIX: How to apply the Apache License to your work.
181
+
182
+ To apply the Apache License to your work, attach the following
183
+ boilerplate notice, with the fields enclosed by brackets "[]"
184
+ replaced with your own identifying information. (Don't include
185
+ the brackets!) The text should be enclosed in the appropriate
186
+ comment syntax for the file format. We also recommend that a
187
+ file or class name and description of purpose be included on the
188
+ same "printed page" as the copyright notice for easier
189
+ identification within third-party archives.
190
+
191
+ Copyright [yyyy] [name of copyright owner]
192
+
193
+ Licensed under the Apache License, Version 2.0 (the "License");
194
+ you may not use this file except in compliance with the License.
195
+ You may obtain a copy of the License at
196
+
197
+ http://www.apache.org/licenses/LICENSE-2.0
198
+
199
+ Unless required by applicable law or agreed to in writing, software
200
+ distributed under the License is distributed on an "AS IS" BASIS,
201
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202
+ See the License for the specific language governing permissions and
203
+ limitations under the License.
204
+
205
+ The current license is BSD 3-Clause Clear License.
206
+
207
+ Copyright (c) 2021-2022, InterDigital Communications, Inc
208
+ All rights reserved.
209
+
210
+ Redistribution and use in source and binary forms, with or without
211
+ modification, are permitted (subject to the limitations in the disclaimer
212
+ below) provided that the following conditions are met:
213
+
214
+ * Redistributions of source code must retain the above copyright notice,
215
+ this list of conditions and the following disclaimer.
216
+ * Redistributions in binary form must reproduce the above copyright notice,
217
+ this list of conditions and the following disclaimer in the documentation
218
+ and/or other materials provided with the distribution.
219
+ * Neither the name of InterDigital Communications, Inc nor the names of its
220
+ contributors may be used to endorse or promote products derived from this
221
+ software without specific prior written permission.
222
+
223
+ NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
224
+ THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
225
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
226
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
227
+ PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
228
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
229
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
230
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
231
+ OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
232
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
233
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
234
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
235
+
236
+ [PyTorchVideoCompression](https://github.com/ZhihaoHu/PyTorchVideoCompression).
237
+ The current license is MIT License.
238
+ MIT License
239
+
240
+ Copyright (c) 2022 Zhihao Hu
241
+
242
+ Permission is hereby granted, free of charge, to any person obtaining a copy
243
+ of this software and associated documentation files (the "Software"), to deal
244
+ in the Software without restriction, including without limitation the rights
245
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
246
+ copies of the Software, and to permit persons to whom the Software is
247
+ furnished to do so, subject to the following conditions:
248
+
249
+ The above copyright notice and this permission notice shall be included in all
250
+ copies or substantial portions of the Software.
251
+
252
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
253
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
254
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
255
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
256
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
257
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
258
+ SOFTWARE.
neural_codec/DCVC/src/cpp/py_rans/py_rans.cpp ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "py_rans.h"
5
+
6
+ #include <algorithm>
7
+ #include <cmath>
8
+ #include <future>
9
+ #include <numeric>
10
+ #include <vector>
11
+
12
+ namespace py = pybind11;
13
+
14
+ RansEncoder::RansEncoder()
15
+ {
16
+ m_encoder0 = std::make_shared<RansEncoderLibMultiThread>();
17
+ m_encoder1 = std::make_shared<RansEncoderLibMultiThread>();
18
+ }
19
+
20
+ void RansEncoder::encode_y(const py::array_t<int16_t>& symbols, const int cdf_group_index)
21
+ {
22
+ py::buffer_info symbols_buf = symbols.request();
23
+ int16_t* symbols_ptr = static_cast<int16_t*>(symbols_buf.ptr);
24
+
25
+ int symbolSize = static_cast<int>(symbols.size());
26
+ if (m_use_two_encoders) {
27
+ int symbolSize0 = symbolSize / 2;
28
+ int symbolSize1 = symbolSize - symbolSize0;
29
+
30
+ auto vec_symbols0 = std::make_shared<std::vector<int16_t>>(symbolSize0);
31
+ memcpy(vec_symbols0->data(), symbols_ptr, symbolSize0 * sizeof(int16_t));
32
+ m_encoder0->encode_y(vec_symbols0, cdf_group_index);
33
+ auto vec_symbols1 = std::make_shared<std::vector<int16_t>>(symbolSize1);
34
+ memcpy(vec_symbols1->data(), symbols_ptr + symbolSize0, symbolSize1 * sizeof(int16_t));
35
+ m_encoder1->encode_y(vec_symbols1, cdf_group_index);
36
+ } else {
37
+ auto vec_symbols0 = std::make_shared<std::vector<int16_t>>(symbolSize);
38
+ memcpy(vec_symbols0->data(), symbols_ptr, symbolSize * sizeof(int16_t));
39
+ m_encoder0->encode_y(vec_symbols0, cdf_group_index);
40
+ }
41
+ }
42
+
43
+ void RansEncoder::encode_z(const py::array_t<int8_t>& symbols, const int cdf_group_index,
44
+ const int start_offset, const int per_channel_size)
45
+ {
46
+ py::buffer_info symbols_buf = symbols.request();
47
+ int8_t* symbols_ptr = static_cast<int8_t*>(symbols_buf.ptr);
48
+
49
+ int symbolSize = static_cast<int>(symbols.size());
50
+ if (m_use_two_encoders) {
51
+ int symbolSize0 = symbolSize / 2;
52
+ int symbolSize1 = symbolSize - symbolSize0;
53
+ int channel_half = symbolSize0 / per_channel_size;
54
+
55
+ auto vec_symbols0 = std::make_shared<std::vector<int8_t>>(symbolSize0);
56
+ memcpy(vec_symbols0->data(), symbols_ptr, symbolSize0 * sizeof(int8_t));
57
+ m_encoder0->encode_z(vec_symbols0, cdf_group_index, start_offset, per_channel_size);
58
+ auto vec_symbols1 = std::make_shared<std::vector<int8_t>>(symbolSize1);
59
+ memcpy(vec_symbols1->data(), symbols_ptr + symbolSize0, symbolSize1 * sizeof(int8_t));
60
+ m_encoder1->encode_z(vec_symbols1, cdf_group_index, start_offset + channel_half,
61
+ per_channel_size);
62
+ } else {
63
+ auto vec_symbols0 = std::make_shared<std::vector<int8_t>>(symbolSize);
64
+ memcpy(vec_symbols0->data(), symbols_ptr, symbolSize * sizeof(int8_t));
65
+ m_encoder0->encode_z(vec_symbols0, cdf_group_index, start_offset, per_channel_size);
66
+ }
67
+ }
68
+
69
+ int RansEncoder::add_cdf(const py::array_t<int32_t>& cdfs, const py::array_t<int32_t>& cdfs_sizes,
70
+ const py::array_t<int32_t>& offsets)
71
+ {
72
+ py::buffer_info cdfs_sizes_buf = cdfs_sizes.request();
73
+ py::buffer_info offsets_buf = offsets.request();
74
+ int32_t* cdfs_sizes_ptr = static_cast<int32_t*>(cdfs_sizes_buf.ptr);
75
+ int32_t* offsets_ptr = static_cast<int32_t*>(offsets_buf.ptr);
76
+
77
+ int cdf_num = static_cast<int>(cdfs_sizes.size());
78
+ auto vec_cdfs_sizes = std::make_shared<std::vector<int32_t>>(cdf_num);
79
+ memcpy(vec_cdfs_sizes->data(), cdfs_sizes_ptr, sizeof(int32_t) * cdf_num);
80
+ auto vec_offsets = std::make_shared<std::vector<int32_t>>(offsets.size());
81
+ memcpy(vec_offsets->data(), offsets_ptr, sizeof(int32_t) * offsets.size());
82
+
83
+ int per_vector_size = static_cast<int>(cdfs.size() / cdf_num);
84
+ auto vec_cdfs = std::make_shared<std::vector<std::vector<int32_t>>>(cdf_num);
85
+ auto cdfs_raw = cdfs.unchecked<2>();
86
+ for (int i = 0; i < cdf_num; i++) {
87
+ std::vector<int32_t> t(per_vector_size);
88
+ memcpy(t.data(), cdfs_raw.data(i, 0), sizeof(int32_t) * per_vector_size);
89
+ vec_cdfs->at(i) = t;
90
+ }
91
+
92
+ int cdf_idx = m_encoder0->add_cdf(vec_cdfs, vec_cdfs_sizes, vec_offsets);
93
+ m_encoder1->add_cdf(vec_cdfs, vec_cdfs_sizes, vec_offsets);
94
+ return cdf_idx;
95
+ }
96
+
97
+ void RansEncoder::empty_cdf_buffer()
98
+ {
99
+ m_encoder0->empty_cdf_buffer();
100
+ m_encoder1->empty_cdf_buffer();
101
+ }
102
+
103
+ void RansEncoder::flush()
104
+ {
105
+ m_encoder0->flush();
106
+ m_encoder1->flush();
107
+ }
108
+
109
+ py::array_t<uint8_t> RansEncoder::get_encoded_stream()
110
+ {
111
+ if (m_use_two_encoders) {
112
+ auto result0 = m_encoder0->get_encoded_stream();
113
+ int nbytes0 = static_cast<int>(result0->size());
114
+ auto result1 = m_encoder1->get_encoded_stream();
115
+ int nbytes1 = static_cast<int>(result1->size());
116
+
117
+ int identical_bytes = 0;
118
+ int check_bytes = std::min(nbytes0, nbytes1);
119
+ check_bytes = std::min(check_bytes, 8);
120
+ for (int i = 0; i < check_bytes; i++) {
121
+ if (result0->at(nbytes0 - 1 - i) != 0) {
122
+ break;
123
+ }
124
+ if (result1->at(nbytes1 - 1 - i) != 0) {
125
+ break;
126
+ }
127
+ identical_bytes++;
128
+ }
129
+ if (identical_bytes == 0 && result0->at(nbytes0 - 1) == result1->at(nbytes1 - 1)) {
130
+ identical_bytes = 1;
131
+ }
132
+
133
+ py::array_t<uint8_t> stream(nbytes0 + nbytes1 - identical_bytes);
134
+ py::buffer_info stream_buf = stream.request();
135
+ uint8_t* stream_ptr = static_cast<uint8_t*>(stream_buf.ptr);
136
+
137
+ std::copy(result0->begin(), result0->end(), stream_ptr);
138
+ std::reverse_copy(result1->begin(), result1->end() - identical_bytes, stream_ptr + nbytes0);
139
+ return stream;
140
+ }
141
+
142
+ auto result0 = m_encoder0->get_encoded_stream();
143
+ int nbytes0 = static_cast<int>(result0->size());
144
+
145
+ py::array_t<uint8_t> stream(nbytes0);
146
+ py::buffer_info stream_buf = stream.request();
147
+ uint8_t* stream_ptr = static_cast<uint8_t*>(stream_buf.ptr);
148
+
149
+ std::copy(result0->begin(), result0->end(), stream_ptr);
150
+ return stream;
151
+ }
152
+
153
+ void RansEncoder::reset()
154
+ {
155
+ m_encoder0->reset();
156
+ m_encoder1->reset();
157
+ }
158
+
159
+ void RansEncoder::set_use_two_encoders(bool b)
160
+ {
161
+ m_use_two_encoders = b;
162
+ }
163
+
164
+ bool RansEncoder::get_use_two_encoders()
165
+ {
166
+ return m_use_two_encoders;
167
+ }
168
+
169
+ RansDecoder::RansDecoder()
170
+ {
171
+ m_decoder0 = std::make_shared<RansDecoderLibMultiThread>();
172
+ m_decoder1 = std::make_shared<RansDecoderLibMultiThread>();
173
+ }
174
+
175
+ void RansDecoder::set_stream(const py::array_t<uint8_t>& encoded)
176
+ {
177
+ py::buffer_info encoded_buf = encoded.request();
178
+ const uint8_t* encoded_ptr = static_cast<uint8_t*>(encoded_buf.ptr);
179
+ const int encoded_size = static_cast<int>(encoded.size());
180
+ auto stream0 = std::make_shared<std::vector<uint8_t>>(encoded.size());
181
+ std::copy(encoded_ptr, encoded_ptr + encoded_size, stream0->data());
182
+ m_decoder0->set_stream(stream0);
183
+ if (m_use_two_decoders) {
184
+ auto stream1 = std::make_shared<std::vector<uint8_t>>(encoded.size());
185
+ std::reverse_copy(encoded_ptr, encoded_ptr + encoded_size, stream1->data());
186
+ m_decoder1->set_stream(stream1);
187
+ }
188
+ }
189
+
190
+ void RansDecoder::decode_y(const py::array_t<uint8_t>& indexes, const int cdf_group_index)
191
+ {
192
+ py::buffer_info indexes_buf = indexes.request();
193
+ uint8_t* indexes_ptr = static_cast<uint8_t*>(indexes_buf.ptr);
194
+
195
+ int indexSize = static_cast<int>(indexes.size());
196
+ if (m_use_two_decoders) {
197
+ int indexSize0 = indexSize / 2;
198
+ int indexSize1 = indexSize - indexSize0;
199
+
200
+ auto vec_indexes0 = std::make_shared<std::vector<uint8_t>>(indexSize0);
201
+ std::copy(indexes_ptr, indexes_ptr + indexSize0, vec_indexes0->data());
202
+ m_decoder0->decode_y(vec_indexes0, cdf_group_index);
203
+
204
+ auto vec_indexes1 = std::make_shared<std::vector<uint8_t>>(indexSize1);
205
+ std::copy(indexes_ptr + indexSize0, indexes_ptr + indexSize, vec_indexes1->data());
206
+ m_decoder1->decode_y(vec_indexes1, cdf_group_index);
207
+ } else {
208
+ auto vec_indexes0 = std::make_shared<std::vector<uint8_t>>(indexSize);
209
+ std::copy(indexes_ptr, indexes_ptr + indexSize, vec_indexes0->data());
210
+ m_decoder0->decode_y(vec_indexes0, cdf_group_index);
211
+ }
212
+ }
213
+
214
+ py::array_t<int8_t> RansDecoder::decode_and_get_y(const py::array_t<uint8_t>& indexes,
215
+ const int cdf_group_index)
216
+ {
217
+ decode_y(indexes, cdf_group_index);
218
+ return get_decoded_tensor();
219
+ }
220
+
221
+ void RansDecoder::decode_z(const int total_size, const int cdf_group_index, const int start_offset,
222
+ const int per_channel_size)
223
+ {
224
+ if (m_use_two_decoders) {
225
+ int symbolSize0 = total_size / 2;
226
+ int symbolSize1 = total_size - symbolSize0;
227
+ int channel_half = symbolSize0 / per_channel_size;
228
+ m_decoder0->decode_z(symbolSize0, cdf_group_index, start_offset, per_channel_size);
229
+ m_decoder1->decode_z(symbolSize1, cdf_group_index, start_offset + channel_half,
230
+ per_channel_size);
231
+ } else {
232
+ m_decoder0->decode_z(total_size, cdf_group_index, start_offset, per_channel_size);
233
+ }
234
+ }
235
+
236
+ py::array_t<int8_t> RansDecoder::get_decoded_tensor()
237
+ {
238
+ if (m_use_two_decoders) {
239
+ auto result0 = m_decoder0->get_decoded_tensor();
240
+ const int total_size0 = static_cast<int>(result0->size());
241
+
242
+ auto result1 = m_decoder1->get_decoded_tensor();
243
+ const int total_size1 = static_cast<int>(result1->size());
244
+ py::array_t<int8_t> output(total_size0 + total_size1);
245
+ py::buffer_info buf = output.request();
246
+ int8_t* buf_ptr = static_cast<int8_t*>(buf.ptr);
247
+ std::copy(result0->begin(), result0->end(), buf_ptr);
248
+ std::copy(result1->begin(), result1->end(), buf_ptr + total_size0);
249
+
250
+ return output;
251
+ }
252
+
253
+ auto result0 = m_decoder0->get_decoded_tensor();
254
+ const int total_size0 = static_cast<int>(result0->size());
255
+
256
+ py::array_t<int8_t> output(total_size0);
257
+ py::buffer_info buf = output.request();
258
+ int8_t* buf_ptr = static_cast<int8_t*>(buf.ptr);
259
+ std::copy(result0->begin(), result0->end(), buf_ptr);
260
+
261
+ return output;
262
+ }
263
+
264
+ int RansDecoder::add_cdf(const py::array_t<int32_t>& cdfs, const py::array_t<int32_t>& cdfs_sizes,
265
+ const py::array_t<int32_t>& offsets)
266
+ {
267
+ py::buffer_info cdfs_sizes_buf = cdfs_sizes.request();
268
+ py::buffer_info offsets_buf = offsets.request();
269
+ int32_t* cdfs_sizes_ptr = static_cast<int32_t*>(cdfs_sizes_buf.ptr);
270
+ int32_t* offsets_ptr = static_cast<int32_t*>(offsets_buf.ptr);
271
+
272
+ int cdf_num = static_cast<int>(cdfs_sizes.size());
273
+ auto vec_cdfs_sizes = std::make_shared<std::vector<int32_t>>(cdf_num);
274
+ memcpy(vec_cdfs_sizes->data(), cdfs_sizes_ptr, sizeof(int32_t) * cdf_num);
275
+ auto vec_offsets = std::make_shared<std::vector<int32_t>>(offsets.size());
276
+ memcpy(vec_offsets->data(), offsets_ptr, sizeof(int32_t) * offsets.size());
277
+
278
+ int per_vector_size = static_cast<int>(cdfs.size() / cdf_num);
279
+ auto vec_cdfs = std::make_shared<std::vector<std::vector<int32_t>>>(cdf_num);
280
+ auto cdfs_raw = cdfs.unchecked<2>();
281
+ for (int i = 0; i < cdf_num; i++) {
282
+ std::vector<int32_t> t(per_vector_size);
283
+ memcpy(t.data(), cdfs_raw.data(i, 0), sizeof(int32_t) * per_vector_size);
284
+ vec_cdfs->at(i) = t;
285
+ }
286
+ int cdf_idx = m_decoder0->add_cdf(vec_cdfs, vec_cdfs_sizes, vec_offsets);
287
+ m_decoder1->add_cdf(vec_cdfs, vec_cdfs_sizes, vec_offsets);
288
+ return cdf_idx;
289
+ }
290
+
291
+ void RansDecoder::empty_cdf_buffer()
292
+ {
293
+ m_decoder0->empty_cdf_buffer();
294
+ m_decoder1->empty_cdf_buffer();
295
+ }
296
+
297
+ void RansDecoder::set_use_two_decoders(bool b)
298
+ {
299
+ m_use_two_decoders = b;
300
+ }
301
+
302
+ bool RansDecoder::get_use_two_decoders()
303
+ {
304
+ return m_use_two_decoders;
305
+ }
306
+
307
+ std::vector<uint32_t> pmf_to_quantized_cdf(const std::vector<float>& pmf, int precision)
308
+ {
309
+ /* NOTE(begaintj): ported from `ryg_rans` public implementation. Not optimal
310
+ * although it's only run once per model after training. See TF/compression
311
+ * implementation for an optimized version. */
312
+
313
+ std::vector<uint32_t> cdf(pmf.size() + 1);
314
+ cdf[0] = 0; /* freq 0 */
315
+
316
+ std::transform(pmf.begin(), pmf.end(), cdf.begin() + 1, [=](float p) {
317
+ return static_cast<uint32_t>(std::round(p * (1 << precision)) + 0.5);
318
+ });
319
+
320
+ const uint32_t total = std::accumulate(cdf.begin(), cdf.end(), 0);
321
+
322
+ std::transform(cdf.begin(), cdf.end(), cdf.begin(), [precision, total](uint32_t p) {
323
+ return static_cast<uint32_t>((((1ull << precision) * p) / total));
324
+ });
325
+
326
+ std::partial_sum(cdf.begin(), cdf.end(), cdf.begin());
327
+ cdf.back() = 1 << precision;
328
+
329
+ for (int i = 0; i < static_cast<int>(cdf.size() - 1); ++i) {
330
+ if (cdf[i] == cdf[i + 1]) {
331
+ /* Try to steal frequency from low-frequency symbols */
332
+ uint32_t best_freq = ~0u;
333
+ int best_steal = -1;
334
+ for (int j = 0; j < static_cast<int>(cdf.size()) - 1; ++j) {
335
+ uint32_t freq = cdf[j + 1] - cdf[j];
336
+ if (freq > 1 && freq < best_freq) {
337
+ best_freq = freq;
338
+ best_steal = j;
339
+ }
340
+ }
341
+
342
+ assert(best_steal != -1);
343
+
344
+ if (best_steal < i) {
345
+ for (int j = best_steal + 1; j <= i; ++j) {
346
+ cdf[j]--;
347
+ }
348
+ } else {
349
+ assert(best_steal > i);
350
+ for (int j = i + 1; j <= best_steal; ++j) {
351
+ cdf[j]++;
352
+ }
353
+ }
354
+ }
355
+ }
356
+
357
+ assert(cdf[0] == 0);
358
+ assert(cdf.back() == (1u << precision));
359
+ for (int i = 0; i < static_cast<int>(cdf.size()) - 1; ++i) {
360
+ assert(cdf[i + 1] > cdf[i]);
361
+ }
362
+
363
+ return cdf;
364
+ }
365
+
366
+ PYBIND11_MODULE(MLCodec_extensions_cpp, m)
367
+ {
368
+ py::class_<RansEncoder>(m, "RansEncoder")
369
+ .def(py::init<>())
370
+ .def("encode_y", &RansEncoder::encode_y)
371
+ .def("encode_z", &RansEncoder::encode_z)
372
+ .def("flush", &RansEncoder::flush)
373
+ .def("get_encoded_stream", &RansEncoder::get_encoded_stream)
374
+ .def("reset", &RansEncoder::reset)
375
+ .def("add_cdf", &RansEncoder::add_cdf)
376
+ .def("empty_cdf_buffer", &RansEncoder::empty_cdf_buffer)
377
+ .def("set_use_two_encoders", &RansEncoder::set_use_two_encoders)
378
+ .def("get_use_two_encoders", &RansEncoder::get_use_two_encoders);
379
+
380
+ py::class_<RansDecoder>(m, "RansDecoder")
381
+ .def(py::init<>())
382
+ .def("set_stream", &RansDecoder::set_stream)
383
+ .def("decode_y", &RansDecoder::decode_y)
384
+ .def("decode_and_get_y", &RansDecoder::decode_and_get_y)
385
+ .def("decode_z", &RansDecoder::decode_z)
386
+ .def("get_decoded_tensor", &RansDecoder::get_decoded_tensor)
387
+ .def("add_cdf", &RansDecoder::add_cdf)
388
+ .def("empty_cdf_buffer", &RansDecoder::empty_cdf_buffer)
389
+ .def("set_use_two_decoders", &RansDecoder::set_use_two_decoders)
390
+ .def("get_use_two_decoders", &RansDecoder::get_use_two_decoders);
391
+
392
+ m.def("pmf_to_quantized_cdf", &pmf_to_quantized_cdf, "Return quantized CDF for a given PMF");
393
+ }
neural_codec/DCVC/src/cpp/py_rans/py_rans.h ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #pragma once
5
+ #include "rans.h"
6
+ #include <memory>
7
+
8
+ #include <pybind11/numpy.h>
9
+ #include <pybind11/pybind11.h>
10
+ #include <pybind11/stl.h>
11
+
12
+ namespace py = pybind11;
13
+
14
+ // the classes in this file only perform the type conversion
15
+ // from python type (numpy) to C++ type (vector)
16
+ class RansEncoder {
17
+ public:
18
+ RansEncoder();
19
+
20
+ RansEncoder(const RansEncoder&) = delete;
21
+ RansEncoder(RansEncoder&&) = delete;
22
+ RansEncoder& operator=(const RansEncoder&) = delete;
23
+ RansEncoder& operator=(RansEncoder&&) = delete;
24
+
25
+ void encode_y(const py::array_t<int16_t>& symbols, const int cdf_group_index);
26
+ void encode_z(const py::array_t<int8_t>& symbols, const int cdf_group_index,
27
+ const int start_offset, const int per_channel_size);
28
+ void flush();
29
+ py::array_t<uint8_t> get_encoded_stream();
30
+ void reset();
31
+ int add_cdf(const py::array_t<int32_t>& cdfs, const py::array_t<int32_t>& cdfs_sizes,
32
+ const py::array_t<int32_t>& offsets);
33
+ void empty_cdf_buffer();
34
+ void set_use_two_encoders(bool b);
35
+ bool get_use_two_encoders();
36
+
37
+ private:
38
+ std::shared_ptr<RansEncoderLib> m_encoder0;
39
+ std::shared_ptr<RansEncoderLib> m_encoder1;
40
+ bool m_use_two_encoders{ false };
41
+ };
42
+
43
+ class RansDecoder {
44
+ public:
45
+ RansDecoder();
46
+
47
+ RansDecoder(const RansDecoder&) = delete;
48
+ RansDecoder(RansDecoder&&) = delete;
49
+ RansDecoder& operator=(const RansDecoder&) = delete;
50
+ RansDecoder& operator=(RansDecoder&&) = delete;
51
+
52
+ void set_stream(const py::array_t<uint8_t>&);
53
+
54
+ void decode_y(const py::array_t<uint8_t>& indexes, const int cdf_group_index);
55
+ py::array_t<int8_t> decode_and_get_y(const py::array_t<uint8_t>& indexes, const int cdf_group_index);
56
+ void decode_z(const int total_size, const int cdf_group_index, const int start_offset,
57
+ const int per_channel_size);
58
+ py::array_t<int8_t> get_decoded_tensor();
59
+ int add_cdf(const py::array_t<int32_t>& cdfs, const py::array_t<int32_t>& cdfs_sizes,
60
+ const py::array_t<int32_t>& offsets);
61
+ void empty_cdf_buffer();
62
+ void set_use_two_decoders(bool b);
63
+ bool get_use_two_decoders();
64
+
65
+ private:
66
+ std::shared_ptr<RansDecoderLib> m_decoder0;
67
+ std::shared_ptr<RansDecoderLib> m_decoder1;
68
+ bool m_use_two_decoders{ false };
69
+ };
70
+
71
+ std::vector<uint32_t> pmf_to_quantized_cdf(const std::vector<float>& pmf, int precision);
neural_codec/DCVC/src/cpp/py_rans/rans.cpp ADDED
@@ -0,0 +1,534 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Copyright 2020 InterDigital Communications, Inc.
2
+ *
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ /* Rans64 extensions from:
17
+ * https://fgiesen.wordpress.com/2015/12/21/rans-in-practice/
18
+ * Unbounded range coding from:
19
+ * https://github.com/tensorflow/compression/blob/master/tensorflow_compression/cc/kernels/unbounded_index_range_coding_kernels.cc
20
+ **/
21
+
22
+ #include "rans.h"
23
+
24
+ #include <algorithm>
25
+ #include <cassert>
26
+ #include <cstring>
27
+
28
+ constexpr uint16_t bypass_precision = 2; /* number of bits in bypass mode */
29
+ constexpr uint16_t max_bypass_val = (1 << bypass_precision) - 1;
30
+
31
+ inline void RansEncPutBits(RansState& r, uint8_t*& ptr, uint32_t val)
32
+ {
33
+ RansAssert(bypass_precision <= 8);
34
+ RansAssert(val < (1u << bypass_precision));
35
+
36
+ constexpr uint32_t freq = 1 << (SCALE_BITS - bypass_precision);
37
+ constexpr uint32_t x_max = freq << ENC_RENORM_SHIFT_BITS;
38
+ while (r >= x_max) {
39
+ *(--ptr) = static_cast<uint8_t>(r & 0xff);
40
+ r >>= 8;
41
+ }
42
+
43
+ r = (r << bypass_precision) | val;
44
+ }
45
+
46
+ inline uint32_t RansDecGetBits(RansState& r, uint8_t*& ptr)
47
+ {
48
+ uint32_t val = r & ((1u << bypass_precision) - 1);
49
+
50
+ /* Re-normalize */
51
+ r = r >> bypass_precision;
52
+ if (r < RANS_BYTE_L) {
53
+ r = (r << 8) | *ptr++;
54
+ RansAssert(r >= RANS_BYTE_L);
55
+ }
56
+
57
+ return val;
58
+ }
59
+
60
+ RansEncoderLib::RansEncoderLib()
61
+ {
62
+ _stream = std::make_shared<std::vector<uint8_t>>();
63
+ }
64
+
65
+ int RansEncoderLib::add_cdf(const std::shared_ptr<std::vector<std::vector<int32_t>>> cdfs,
66
+ const std::shared_ptr<std::vector<int32_t>> cdfs_sizes,
67
+ const std::shared_ptr<std::vector<int32_t>> offsets)
68
+ {
69
+
70
+ auto ransSymbols = std::make_shared<std::vector<std::vector<RansSymbol>>>(cdfs->size());
71
+ for (int i = 0; i < static_cast<int>(cdfs->size()); i++) {
72
+ const int32_t* cdf = cdfs->at(i).data();
73
+ std::vector<RansSymbol> ransSym(cdfs->at(i).size());
74
+ const int ransSize = static_cast<int>(ransSym.size() - 1);
75
+ for (int j = 0; j < ransSize; j++) {
76
+ ransSym[j] = RansSymbol(
77
+ { static_cast<uint16_t>(cdf[j]), static_cast<uint16_t>(cdf[j + 1] - cdf[j]) });
78
+ }
79
+ ransSymbols->at(i) = ransSym;
80
+ }
81
+
82
+ _ransSymbols.push_back(ransSymbols);
83
+ _cdfs_sizes.push_back(cdfs_sizes);
84
+ _offsets.push_back(offsets);
85
+ return static_cast<int>(_ransSymbols.size()) - 1;
86
+ }
87
+
88
+ void RansEncoderLib::empty_cdf_buffer()
89
+ {
90
+ _ransSymbols.clear();
91
+ _cdfs_sizes.clear();
92
+ _offsets.clear();
93
+ }
94
+
95
+ FORCE_INLINE void RansEncoderLib::encode_one_symbol(uint8_t*& ptr, RansState& rans, const int32_t symbol,
96
+ const int32_t cdf_size, const int32_t offset,
97
+ const std::vector<RansSymbol>& ransSymbols)
98
+ {
99
+ const int32_t max_value = cdf_size - 2;
100
+ int32_t value = symbol - offset;
101
+
102
+ uint32_t raw_val = 0;
103
+ if (value < 0) {
104
+ raw_val = -2 * value - 1;
105
+ value = max_value;
106
+ } else if (value >= max_value) {
107
+ raw_val = 2 * (value - max_value);
108
+ value = max_value;
109
+ }
110
+
111
+ if (value == max_value) {
112
+ std::vector<uint16_t> bypassBins;
113
+ bypassBins.reserve(20);
114
+ /* Determine the number of bypasses (in bypass_precision size) needed to
115
+ * encode the raw value. */
116
+ int32_t n_bypass = 0;
117
+ while ((raw_val >> (n_bypass * bypass_precision)) != 0) {
118
+ ++n_bypass;
119
+ }
120
+
121
+ /* Encode number of bypasses */
122
+ int32_t val = n_bypass;
123
+ while (val >= max_bypass_val) {
124
+ bypassBins.push_back(max_bypass_val);
125
+ val -= max_bypass_val;
126
+ }
127
+ bypassBins.push_back(static_cast<uint16_t>(val));
128
+
129
+ /* Encode raw value */
130
+ for (int32_t j = 0; j < n_bypass; ++j) {
131
+ const int32_t val1 = (raw_val >> (j * bypass_precision)) & max_bypass_val;
132
+ bypassBins.push_back(static_cast<uint16_t>(val1));
133
+ }
134
+
135
+ for (auto it = bypassBins.rbegin(); it < bypassBins.rend(); it++) {
136
+ RansEncPutBits(rans, ptr, *it);
137
+ }
138
+ }
139
+ RansEncPut(rans, ptr, ransSymbols[value].start, ransSymbols[value].range);
140
+ }
141
+
142
+ void RansEncoderLib::encode_y(const std::shared_ptr<std::vector<int16_t>> symbols,
143
+ const int cdf_group_index)
144
+ {
145
+ PendingTask p;
146
+ p.workType = WorkType::EncodeDecodeY;
147
+ p.symbols_y = symbols;
148
+ p.cdf_group_index = cdf_group_index;
149
+ m_pendingEncodingList.push_back(p);
150
+ }
151
+
152
+ void RansEncoderLib::encode_z(const std::shared_ptr<std::vector<int8_t>> symbols,
153
+ const int cdf_group_index, const int start_offset,
154
+ const int per_channel_size)
155
+ {
156
+ PendingTask p;
157
+ p.workType = WorkType::EncodeDecodeZ;
158
+ p.symbols_z = symbols;
159
+ p.cdf_group_index = cdf_group_index;
160
+ p.start_offset = start_offset;
161
+ p.per_channel_size = per_channel_size;
162
+ m_pendingEncodingList.push_back(p);
163
+ }
164
+ #include <iostream>
165
+ FORCE_INLINE void RansEncoderLib::encode_y_internal(uint8_t*& ptr, RansState& rans,
166
+ const std::shared_ptr<std::vector<int16_t>> symbols,
167
+ const int cdf_group_index)
168
+ {
169
+ // backward loop on symbols from the end;
170
+ const int16_t* symbols_ptr = symbols->data();
171
+ const int32_t* cdfs_sizes_ptr = _cdfs_sizes[cdf_group_index]->data();
172
+ const int32_t* offsets_ptr = _offsets[cdf_group_index]->data();
173
+ const int symbol_size = static_cast<int>(symbols->size());
174
+
175
+ for (int i = symbol_size - 1; i >= 0; i--) {
176
+ const int32_t combined_symbol = symbols_ptr[i];
177
+ const int32_t cdf_idx = combined_symbol & 0xff;
178
+ const int32_t s = combined_symbol >> 8;
179
+ encode_one_symbol(ptr, rans, s, cdfs_sizes_ptr[cdf_idx], offsets_ptr[cdf_idx],
180
+ _ransSymbols[cdf_group_index]->at(cdf_idx));
181
+ }
182
+ }
183
+
184
+ FORCE_INLINE void RansEncoderLib::encode_z_internal(uint8_t*& ptr, RansState& rans,
185
+ const std::shared_ptr<std::vector<int8_t>> symbols,
186
+ const int cdf_group_index, const int start_offset,
187
+ const int per_channel_size)
188
+ {
189
+ // backward loop on symbols from the end;
190
+ const int8_t* symbols_ptr = symbols->data();
191
+ const int32_t* cdfs_sizes_ptr = _cdfs_sizes[cdf_group_index]->data();
192
+ const int32_t* offsets_ptr = _offsets[cdf_group_index]->data();
193
+ const int symbol_size = static_cast<int>(symbols->size());
194
+
195
+ for (int i = symbol_size - 1; i >= 0; i--) {
196
+ const int32_t cdf_idx = i / per_channel_size + start_offset;
197
+ encode_one_symbol(ptr, rans, symbols_ptr[i], cdfs_sizes_ptr[cdf_idx], offsets_ptr[cdf_idx],
198
+ _ransSymbols[cdf_group_index]->at(cdf_idx));
199
+ }
200
+ }
201
+
202
+ void RansEncoderLib::flush()
203
+ {
204
+ RansState rans;
205
+ RansEncInit(rans);
206
+
207
+ int32_t total_symbol_size = 0;
208
+ for (auto it = m_pendingEncodingList.begin(); it != m_pendingEncodingList.end(); it++) {
209
+ if (it->workType == WorkType::EncodeDecodeY) {
210
+ total_symbol_size += static_cast<int32_t>(it->symbols_y->size());
211
+ } else if (it->workType == WorkType::EncodeDecodeZ) {
212
+ total_symbol_size += static_cast<int32_t>(it->symbols_z->size());
213
+ }
214
+ }
215
+
216
+ if (total_symbol_size == 0) {
217
+ _stream->resize(0);
218
+ return;
219
+ }
220
+
221
+ uint8_t* output = new uint8_t[total_symbol_size]; // too much space ?
222
+ uint8_t* ptrEnd = output + total_symbol_size;
223
+ uint8_t* ptr = ptrEnd;
224
+ assert(ptr != nullptr);
225
+
226
+ for (auto it = m_pendingEncodingList.rbegin(); it != m_pendingEncodingList.rend(); it++) {
227
+ PendingTask p = *it;
228
+ if (p.workType == WorkType::EncodeDecodeY) {
229
+ encode_y_internal(ptr, rans, p.symbols_y, p.cdf_group_index);
230
+ } else if (p.workType == WorkType::EncodeDecodeZ) {
231
+ encode_z_internal(ptr, rans, p.symbols_z, p.cdf_group_index, p.start_offset,
232
+ p.per_channel_size);
233
+ }
234
+ }
235
+
236
+ RansEncFlush(rans, ptr);
237
+
238
+ const int nbytes = static_cast<int>(std::distance(ptr, ptrEnd));
239
+
240
+ _stream->resize(nbytes);
241
+ memcpy(_stream->data(), ptr, nbytes);
242
+ delete[] output;
243
+ }
244
+
245
+ std::shared_ptr<std::vector<uint8_t>> RansEncoderLib::get_encoded_stream()
246
+ {
247
+ return _stream;
248
+ }
249
+
250
+ void RansEncoderLib::reset()
251
+ {
252
+ m_pendingEncodingList.clear();
253
+ _stream->clear();
254
+ }
255
+
256
+ RansEncoderLibMultiThread::RansEncoderLibMultiThread()
257
+ : RansEncoderLib()
258
+ , m_finish(false)
259
+ , m_result_ready(false)
260
+ {
261
+ m_thread = std::thread(&RansEncoderLibMultiThread::worker, this);
262
+ }
263
+ RansEncoderLibMultiThread::~RansEncoderLibMultiThread()
264
+ {
265
+ {
266
+ std::lock_guard<std::mutex> lk(m_mutex_pending);
267
+ std::lock_guard<std::mutex> lk1(m_mutex_result);
268
+ m_finish = true;
269
+ }
270
+ m_cv_pending.notify_one();
271
+ m_cv_result.notify_one();
272
+ m_thread.join();
273
+ }
274
+
275
+ void RansEncoderLibMultiThread::flush()
276
+ {
277
+ PendingTask p;
278
+ p.workType = WorkType::Flush;
279
+ {
280
+ std::unique_lock<std::mutex> lk(m_mutex_pending);
281
+ m_pending.push_back(p);
282
+ }
283
+ m_cv_pending.notify_one();
284
+ }
285
+
286
+ std::shared_ptr<std::vector<uint8_t>> RansEncoderLibMultiThread::get_encoded_stream()
287
+ {
288
+ std::unique_lock<std::mutex> lk(m_mutex_result);
289
+ m_cv_result.wait(lk, [this] { return m_result_ready || m_finish; });
290
+ return RansEncoderLib::get_encoded_stream();
291
+ }
292
+
293
+ void RansEncoderLibMultiThread::reset()
294
+ {
295
+ RansEncoderLib::reset();
296
+ std::lock_guard<std::mutex> lk(m_mutex_result);
297
+ m_result_ready = false;
298
+ }
299
+
300
+ void RansEncoderLibMultiThread::worker()
301
+ {
302
+ while (!m_finish) {
303
+ std::unique_lock<std::mutex> lk(m_mutex_pending);
304
+ m_cv_pending.wait(lk, [this] { return m_pending.size() > 0 || m_finish; });
305
+ if (m_finish) {
306
+ lk.unlock();
307
+ break;
308
+ }
309
+ if (m_pending.size() == 0) {
310
+ lk.unlock();
311
+ // std::cout << "contine in worker" << std::endl;
312
+ continue;
313
+ }
314
+ while (m_pending.size() > 0) {
315
+ auto p = m_pending.front();
316
+ m_pending.pop_front();
317
+ lk.unlock();
318
+ if (p.workType == WorkType::Flush) {
319
+ RansEncoderLib::flush();
320
+ {
321
+ std::lock_guard<std::mutex> lk_result(m_mutex_result);
322
+ m_result_ready = true;
323
+ }
324
+ m_cv_result.notify_one();
325
+ }
326
+ lk.lock();
327
+ }
328
+ lk.unlock();
329
+ }
330
+ }
331
+
332
+ void RansDecoderLib::set_stream(const std::shared_ptr<std::vector<uint8_t>> encoded)
333
+ {
334
+ _stream = encoded;
335
+ _ptr8 = (uint8_t*)(_stream->data());
336
+ RansDecInit(_rans, _ptr8);
337
+ }
338
+
339
+ int RansDecoderLib::add_cdf(const std::shared_ptr<std::vector<std::vector<int32_t>>> cdfs,
340
+ const std::shared_ptr<std::vector<int32_t>> cdfs_sizes,
341
+ const std::shared_ptr<std::vector<int32_t>> offsets)
342
+ {
343
+ _cdfs.push_back(cdfs);
344
+ _cdfs_sizes.push_back(cdfs_sizes);
345
+ _offsets.push_back(offsets);
346
+ return static_cast<int>(_cdfs.size()) - 1;
347
+ }
348
+
349
+ void RansDecoderLib::empty_cdf_buffer()
350
+ {
351
+ _cdfs.clear();
352
+ _cdfs_sizes.clear();
353
+ _offsets.clear();
354
+ }
355
+
356
+ FORCE_INLINE int8_t RansDecoderLib::decode_one_symbol(const int32_t* cdf, const int32_t cdf_size,
357
+ const int32_t offset)
358
+ {
359
+ const int32_t max_value = cdf_size - 2;
360
+ const int32_t cum_freq = static_cast<int32_t>(RansDecGet(_rans));
361
+
362
+ int s = 1;
363
+ while (cdf[s++] <= cum_freq) {
364
+ }
365
+ s -= 2;
366
+
367
+ RansDecAdvance(_rans, _ptr8, cdf[s], cdf[s + 1] - cdf[s]);
368
+
369
+ int32_t value = static_cast<int32_t>(s);
370
+
371
+ if (value == max_value) {
372
+ /* Bypass decoding mode */
373
+ int32_t val = RansDecGetBits(_rans, _ptr8);
374
+ int32_t n_bypass = val;
375
+
376
+ while (val == max_bypass_val) {
377
+ val = RansDecGetBits(_rans, _ptr8);
378
+ n_bypass += val;
379
+ }
380
+
381
+ int32_t raw_val = 0;
382
+ for (int j = 0; j < n_bypass; ++j) {
383
+ val = RansDecGetBits(_rans, _ptr8);
384
+ raw_val |= val << (j * bypass_precision);
385
+ }
386
+ value = raw_val >> 1;
387
+ if (raw_val & 1) {
388
+ value = -value - 1;
389
+ } else {
390
+ value += max_value;
391
+ }
392
+ }
393
+
394
+ return static_cast<int8_t>(value + offset);
395
+ }
396
+
397
+ void RansDecoderLib::decode_y(const std::shared_ptr<std::vector<uint8_t>> indexes,
398
+ const int cdf_group_index)
399
+ {
400
+ int index_size = static_cast<int>(indexes->size());
401
+ m_decoded = std::make_shared<std::vector<int8_t>>(index_size);
402
+
403
+ int8_t* outout_ptr = m_decoded->data();
404
+ const uint8_t* indexes_ptr = indexes->data();
405
+ const int32_t* cdfs_sizes_ptr = _cdfs_sizes[cdf_group_index]->data();
406
+ const int32_t* offsets_ptr = _offsets[cdf_group_index]->data();
407
+ const auto& cdfs = _cdfs[cdf_group_index];
408
+ for (int i = 0; i < index_size; ++i) {
409
+ const int32_t cdf_idx = indexes_ptr[i];
410
+ outout_ptr[i] = decode_one_symbol(cdfs->at(cdf_idx).data(), cdfs_sizes_ptr[cdf_idx],
411
+ offsets_ptr[cdf_idx]);
412
+ }
413
+ }
414
+
415
+ void RansDecoderLib::decode_z(const int total_size, const int cdf_group_index,
416
+ const int start_offset, const int per_channel_size)
417
+ {
418
+ m_decoded = std::make_shared<std::vector<int8_t>>(total_size);
419
+
420
+ int8_t* outout_ptr = m_decoded->data();
421
+ const int32_t* cdfs_sizes_ptr = _cdfs_sizes[cdf_group_index]->data();
422
+ const int32_t* offsets_ptr = _offsets[cdf_group_index]->data();
423
+ const auto& cdfs = _cdfs[cdf_group_index];
424
+ for (int i = 0; i < total_size; ++i) {
425
+ const int32_t cdf_idx = i / per_channel_size + start_offset;
426
+ outout_ptr[i] = decode_one_symbol(cdfs->at(cdf_idx).data(), cdfs_sizes_ptr[cdf_idx],
427
+ offsets_ptr[cdf_idx]);
428
+ }
429
+ }
430
+
431
+ std::shared_ptr<std::vector<int8_t>> RansDecoderLib::get_decoded_tensor()
432
+ {
433
+ return m_decoded;
434
+ }
435
+
436
+ RansDecoderLibMultiThread::RansDecoderLibMultiThread()
437
+ : RansDecoderLib()
438
+ , m_finish(false)
439
+ , m_result_ready(false)
440
+ {
441
+ m_thread = std::thread(&RansDecoderLibMultiThread::worker, this);
442
+ }
443
+
444
+ RansDecoderLibMultiThread::~RansDecoderLibMultiThread()
445
+ {
446
+ {
447
+ std::lock_guard<std::mutex> lk(m_mutex_pending);
448
+ std::lock_guard<std::mutex> lk1(m_mutex_result);
449
+ m_finish = true;
450
+ }
451
+ m_cv_pending.notify_one();
452
+ m_cv_result.notify_one();
453
+ m_thread.join();
454
+ }
455
+
456
+ void RansDecoderLibMultiThread::decode_y(const std::shared_ptr<std::vector<uint8_t>> indexes,
457
+ const int cdf_group_index)
458
+ {
459
+ {
460
+ std::lock_guard<std::mutex> lk(m_mutex_result);
461
+ m_result_ready = false;
462
+ }
463
+ PendingTask p;
464
+ p.workType = WorkType::EncodeDecodeY;
465
+ p.indexes = indexes;
466
+ p.cdf_group_index = cdf_group_index;
467
+ {
468
+ std::unique_lock<std::mutex> lk(m_mutex_pending);
469
+ m_pending.push_back(p);
470
+ }
471
+ m_cv_pending.notify_one();
472
+ }
473
+
474
+ void RansDecoderLibMultiThread::decode_z(const int total_size, const int cdf_group_index,
475
+ const int start_offset, const int per_channel_size)
476
+ {
477
+ {
478
+ std::lock_guard<std::mutex> lk(m_mutex_result);
479
+ m_result_ready = false;
480
+ }
481
+ PendingTask p;
482
+ p.workType = WorkType::EncodeDecodeZ;
483
+ p.total_size = total_size;
484
+ p.cdf_group_index = cdf_group_index;
485
+ p.start_offset = start_offset;
486
+ p.per_channel_size = per_channel_size;
487
+ {
488
+ std::unique_lock<std::mutex> lk(m_mutex_pending);
489
+ m_pending.push_back(p);
490
+ }
491
+ m_cv_pending.notify_one();
492
+ }
493
+
494
+ std::shared_ptr<std::vector<int8_t>> RansDecoderLibMultiThread::get_decoded_tensor()
495
+ {
496
+ std::unique_lock<std::mutex> lk(m_mutex_result);
497
+ m_cv_result.wait(lk, [this] { return m_result_ready || m_finish; });
498
+ return RansDecoderLib::get_decoded_tensor();
499
+ }
500
+
501
+ void RansDecoderLibMultiThread::worker()
502
+ {
503
+ while (!m_finish) {
504
+ std::unique_lock<std::mutex> lk(m_mutex_pending);
505
+ m_cv_pending.wait(lk, [this] { return m_pending.size() > 0 || m_finish; });
506
+ if (m_finish) {
507
+ lk.unlock();
508
+ break;
509
+ }
510
+ if (m_pending.size() == 0) {
511
+ lk.unlock();
512
+ // std::cout << "contine in worker" << std::endl;
513
+ continue;
514
+ }
515
+ while (m_pending.size() > 0) {
516
+ auto p = m_pending.front();
517
+ m_pending.pop_front();
518
+ lk.unlock();
519
+ if (p.workType == WorkType::EncodeDecodeY) {
520
+ RansDecoderLib::decode_y(p.indexes, p.cdf_group_index);
521
+ } else if (p.workType == WorkType::EncodeDecodeZ) {
522
+ RansDecoderLib::decode_z(p.total_size, p.cdf_group_index, p.start_offset,
523
+ p.per_channel_size);
524
+ }
525
+ {
526
+ std::lock_guard<std::mutex> lk_result(m_mutex_result);
527
+ m_result_ready = true;
528
+ }
529
+ m_cv_result.notify_one();
530
+ lk.lock();
531
+ }
532
+ lk.unlock();
533
+ }
534
+ }
neural_codec/DCVC/src/cpp/py_rans/rans.h ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Copyright 2020 InterDigital Communications, Inc.
2
+ *
3
+ * Licensed under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License.
5
+ * You may obtain a copy of the License at
6
+ *
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software
10
+ * distributed under the License is distributed on an "AS IS" BASIS,
11
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ * See the License for the specific language governing permissions and
13
+ * limitations under the License.
14
+ */
15
+
16
+ #pragma once
17
+
18
+ #include <condition_variable>
19
+ #include <list>
20
+ #include <thread>
21
+ #include <vector>
22
+
23
+ #ifdef __GNUC__
24
+ #pragma GCC diagnostic push
25
+ #pragma GCC diagnostic ignored "-Wpedantic"
26
+ #pragma GCC diagnostic ignored "-Wsign-compare"
27
+ #endif
28
+
29
+ #ifdef _MSC_VER
30
+ #pragma warning(disable : 4244)
31
+ #endif
32
+
33
+ #include "rans_byte.h"
34
+
35
+ #ifdef _MSC_VER
36
+ #pragma warning(default : 4244)
37
+ #endif
38
+
39
+ #ifdef __GNUC__
40
+ #pragma GCC diagnostic pop
41
+ #endif
42
+
43
+ #ifdef _MSC_VER
44
+ #define FORCE_INLINE __forceinline
45
+ #endif
46
+
47
+ #ifdef __GNUC__
48
+ #define FORCE_INLINE __attribute__((always_inline)) inline
49
+ #endif
50
+
51
+ struct RansSymbol {
52
+ uint16_t start;
53
+ uint16_t range; // range for normal coding and 0 for bypass coding
54
+ };
55
+
56
+ enum class WorkType {
57
+ EncodeDecodeY,
58
+ EncodeDecodeZ,
59
+ Flush,
60
+ };
61
+
62
+ struct PendingTask {
63
+ WorkType workType;
64
+ std::shared_ptr<std::vector<int16_t>> symbols_y;
65
+ std::shared_ptr<std::vector<int8_t>> symbols_z;
66
+ std::shared_ptr<std::vector<uint8_t>> indexes;
67
+ int total_size{ 0 };
68
+ int cdf_group_index{ 0 };
69
+ int start_offset{ 0 };
70
+ int per_channel_size{ 0 };
71
+ };
72
+
73
+ /* NOTE: Warning, we buffer everything for now... In case of large files we
74
+ * should split the bitstream into chunks... Or for a memory-bounded encoder
75
+ **/
76
+ class RansEncoderLib {
77
+ public:
78
+ RansEncoderLib();
79
+ virtual ~RansEncoderLib() = default;
80
+
81
+ RansEncoderLib(const RansEncoderLib&) = delete;
82
+ RansEncoderLib(RansEncoderLib&&) = delete;
83
+ RansEncoderLib& operator=(const RansEncoderLib&) = delete;
84
+ RansEncoderLib& operator=(RansEncoderLib&&) = delete;
85
+
86
+ void encode_y(const std::shared_ptr<std::vector<int16_t>> symbols, const int cdf_group_index);
87
+ void encode_z(const std::shared_ptr<std::vector<int8_t>> symbols, const int cdf_group_index,
88
+ const int start_offset, const int per_channel_size);
89
+
90
+ FORCE_INLINE void encode_y_internal(uint8_t*& ptr, RansState& rans,
91
+ const std::shared_ptr<std::vector<int16_t>> symbols,
92
+ const int cdf_group_index);
93
+ FORCE_INLINE void encode_z_internal(uint8_t*& ptr, RansState& rans,
94
+ const std::shared_ptr<std::vector<int8_t>> symbols,
95
+ const int cdf_group_index, const int start_offset,
96
+ const int per_channel_size);
97
+ FORCE_INLINE void encode_one_symbol(uint8_t*& ptr, RansState& rans, const int32_t symbol,
98
+ const int32_t cdf_size, const int32_t offset,
99
+ const std::vector<RansSymbol>& ransSymbols);
100
+ virtual void flush();
101
+ virtual std::shared_ptr<std::vector<uint8_t>> get_encoded_stream();
102
+ virtual void reset();
103
+ virtual int add_cdf(const std::shared_ptr<std::vector<std::vector<int32_t>>> cdfs,
104
+ const std::shared_ptr<std::vector<int32_t>> cdfs_sizes,
105
+ const std::shared_ptr<std::vector<int32_t>> offsets);
106
+ virtual void empty_cdf_buffer();
107
+
108
+ private:
109
+ std::shared_ptr<std::vector<uint8_t>> _stream;
110
+
111
+ std::vector<std::shared_ptr<std::vector<std::vector<RansSymbol>>>> _ransSymbols;
112
+ std::vector<std::shared_ptr<std::vector<int32_t>>> _cdfs_sizes;
113
+ std::vector<std::shared_ptr<std::vector<int32_t>>> _offsets;
114
+
115
+ std::list<PendingTask> m_pendingEncodingList;
116
+ };
117
+
118
+ class RansEncoderLibMultiThread : public RansEncoderLib {
119
+ public:
120
+ RansEncoderLibMultiThread();
121
+ virtual ~RansEncoderLibMultiThread();
122
+ virtual void flush() override;
123
+ virtual std::shared_ptr<std::vector<uint8_t>> get_encoded_stream() override;
124
+ virtual void reset() override;
125
+
126
+ void worker();
127
+
128
+ private:
129
+ bool m_finish;
130
+ bool m_result_ready;
131
+ std::thread m_thread;
132
+ std::mutex m_mutex_result;
133
+ std::mutex m_mutex_pending;
134
+ std::condition_variable m_cv_pending;
135
+ std::condition_variable m_cv_result;
136
+ std::list<PendingTask> m_pending;
137
+ };
138
+
139
+ class RansDecoderLib {
140
+ public:
141
+ RansDecoderLib() {}
142
+ virtual ~RansDecoderLib() = default;
143
+
144
+ RansDecoderLib(const RansDecoderLib&) = delete;
145
+ RansDecoderLib(RansDecoderLib&&) = delete;
146
+ RansDecoderLib& operator=(const RansDecoderLib&) = delete;
147
+ RansDecoderLib& operator=(RansDecoderLib&&) = delete;
148
+
149
+ virtual void set_stream(const std::shared_ptr<std::vector<uint8_t>> encoded);
150
+
151
+ FORCE_INLINE int8_t decode_one_symbol(const int32_t* cdf, const int32_t cdf_size,
152
+ const int32_t offset);
153
+
154
+ virtual void decode_y(const std::shared_ptr<std::vector<uint8_t>> indexes,
155
+ const int cdf_group_index);
156
+ virtual void decode_z(const int total_size, const int cdf_group_index, const int start_offset,
157
+ const int per_channel_size);
158
+
159
+ virtual std::shared_ptr<std::vector<int8_t>> get_decoded_tensor();
160
+
161
+ virtual int add_cdf(const std::shared_ptr<std::vector<std::vector<int32_t>>> cdfs,
162
+ const std::shared_ptr<std::vector<int32_t>> cdfs_sizes,
163
+ const std::shared_ptr<std::vector<int32_t>> offsets);
164
+ virtual void empty_cdf_buffer();
165
+
166
+ private:
167
+ RansState _rans;
168
+ uint8_t* _ptr8;
169
+ std::shared_ptr<std::vector<uint8_t>> _stream;
170
+ std::shared_ptr<std::vector<int8_t>> m_decoded;
171
+
172
+ std::vector<std::shared_ptr<std::vector<std::vector<int32_t>>>> _cdfs;
173
+ std::vector<std::shared_ptr<std::vector<int32_t>>> _cdfs_sizes;
174
+ std::vector<std::shared_ptr<std::vector<int32_t>>> _offsets;
175
+ };
176
+
177
+ class RansDecoderLibMultiThread : public RansDecoderLib {
178
+ public:
179
+ RansDecoderLibMultiThread();
180
+ virtual ~RansDecoderLibMultiThread();
181
+
182
+ virtual void decode_y(const std::shared_ptr<std::vector<uint8_t>> indexes,
183
+ const int cdf_group_index) override;
184
+
185
+ virtual void decode_z(const int total_size, const int cdf_group_index, const int start_offset,
186
+ const int per_channel_size) override;
187
+
188
+ virtual std::shared_ptr<std::vector<int8_t>> get_decoded_tensor() override;
189
+
190
+ void worker();
191
+
192
+ private:
193
+ bool m_finish;
194
+ bool m_result_ready;
195
+ std::thread m_thread;
196
+ std::mutex m_mutex_result;
197
+ std::mutex m_mutex_pending;
198
+ std::condition_variable m_cv_pending;
199
+ std::condition_variable m_cv_result;
200
+ std::list<PendingTask> m_pending;
201
+ };
neural_codec/DCVC/src/cpp/py_rans/rans_byte.h ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // The code is from https://github.com/rygorous/ryg_rans
2
+ // The original lisence is below.
3
+
4
+ // To the extent possible under law, Fabian Giesen has waived all
5
+ // copyright and related or neighboring rights to ryg_rans, as
6
+ // per the terms of the CC0 license:
7
+
8
+ // https://creativecommons.org/publicdomain/zero/1.0
9
+
10
+ // This work is published from the United States.
11
+
12
+ // Simple byte-aligned rANS encoder/decoder - public domain - Fabian 'ryg'
13
+ // Giesen 2014
14
+ //
15
+ // Not intended to be "industrial strength"; just meant to illustrate the
16
+ // general idea.
17
+
18
+ #pragma once
19
+
20
+ #include <stdint.h>
21
+
22
+ #ifdef assert
23
+ #define RansAssert assert
24
+ #else
25
+ #define RansAssert(x)
26
+ #endif
27
+
28
+ // READ ME FIRST:
29
+ //
30
+ // This is designed like a typical arithmetic coder API, but there's three
31
+ // twists you absolutely should be aware of before you start hacking:
32
+ //
33
+ // 1. You need to encode data in *reverse* - last symbol first. rANS works
34
+ // like a stack: last in, first out.
35
+ // 2. Likewise, the encoder outputs bytes *in reverse* - that is, you give
36
+ // it a pointer to the *end* of your buffer (exclusive), and it will
37
+ // slowly move towards the beginning as more bytes are emitted.
38
+ // 3. Unlike basically any other entropy coder implementation you might
39
+ // have used, you can interleave data from multiple independent rANS
40
+ // encoders into the same bytestream without any extra signaling;
41
+ // you can also just write some bytes by yourself in the middle if
42
+ // you want to. This is in addition to the usual arithmetic encoder
43
+ // property of being able to switch models on the fly. Writing raw
44
+ // bytes can be useful when you have some data that you know is
45
+ // incompressible, and is cheaper than going through the rANS encode
46
+ // function. Using multiple rANS coders on the same byte stream wastes
47
+ // a few bytes compared to using just one, but execution of two
48
+ // independent encoders can happen in parallel on superscalar and
49
+ // Out-of-Order CPUs, so this can be *much* faster in tight decoding
50
+ // loops.
51
+ //
52
+ // This is why all the rANS functions take the write pointer as an
53
+ // argument instead of just storing it in some context struct.
54
+
55
+ // --------------------------------------------------------------------------
56
+
57
+ // L ('l' in the paper) is the lower bound of our normalization interval.
58
+ // Between this and our byte-aligned emission, we use 31 (not 32!) bits.
59
+ // This is done intentionally because exact reciprocals for 31-bit uints
60
+ // fit in 32-bit uints: this permits some optimizations during encoding.
61
+ constexpr int SCALE_BITS = 16;
62
+ constexpr int RANS_SHIFT_BITS = 23;
63
+ constexpr uint32_t RANS_BYTE_L = (1u << RANS_SHIFT_BITS);
64
+ constexpr int ENC_RENORM_SHIFT_BITS = RANS_SHIFT_BITS - SCALE_BITS + 8;
65
+ constexpr int DEC_MASK = ((1u << SCALE_BITS) - 1);
66
+
67
+ // State for a rANS encoder. Yep, that's all there is to it.
68
+ typedef uint32_t RansState;
69
+
70
+ // Initialize a rANS encoder.
71
+ static inline void RansEncInit(RansState& r)
72
+ {
73
+ r = RANS_BYTE_L;
74
+ }
75
+
76
+ // Renormalize the encoder. Internal function.
77
+ static inline void RansEncRenorm(RansState& x, uint8_t*& ptr, uint32_t freq)
78
+ {
79
+ const uint32_t x_max = freq << ENC_RENORM_SHIFT_BITS;
80
+ while (x >= x_max) {
81
+ *(--ptr) = static_cast<uint8_t>(x & 0xff);
82
+ x >>= 8;
83
+ }
84
+ }
85
+
86
+ // Encodes a single symbol with range start "start" and frequency "freq".
87
+ // All frequencies are assumed to sum to "1 << scale_bits", and the
88
+ // resulting bytes get written to ptr (which is updated).
89
+ //
90
+ // NOTE: With rANS, you need to encode symbols in *reverse order*, i.e. from
91
+ // beginning to end! Likewise, the output bytestream is written *backwards*:
92
+ // ptr starts pointing at the end of the output buffer and keeps decrementing.
93
+ static inline void RansEncPut(RansState& r, uint8_t*& ptr, uint32_t start, uint32_t freq)
94
+ {
95
+ // renormalize
96
+ RansEncRenorm(r, ptr, freq);
97
+
98
+ // x = C(s,x)
99
+ r = ((r / freq) << SCALE_BITS) + (r % freq) + start;
100
+ }
101
+
102
+ // Flushes the rANS encoder.
103
+ static inline void RansEncFlush(const RansState& r, uint8_t*& ptr)
104
+ {
105
+ ptr -= 4;
106
+ ptr[0] = (uint8_t)(r >> 0);
107
+ ptr[1] = (uint8_t)(r >> 8);
108
+ ptr[2] = (uint8_t)(r >> 16);
109
+ ptr[3] = (uint8_t)(r >> 24);
110
+ }
111
+
112
+ // Initializes a rANS decoder.
113
+ // Unlike the encoder, the decoder works forwards as you'd expect.
114
+ static inline void RansDecInit(RansState& r, uint8_t*& ptr)
115
+ {
116
+ r = (*ptr++) << 0;
117
+ r |= (*ptr++) << 8;
118
+ r |= (*ptr++) << 16;
119
+ r |= (*ptr++) << 24;
120
+ }
121
+
122
+ // Returns the current cumulative frequency (map it to a symbol yourself!)
123
+ static inline uint32_t RansDecGet(RansState& r)
124
+ {
125
+ return r & DEC_MASK;
126
+ }
127
+
128
+ // Advances in the bit stream by "popping" a single symbol with range start
129
+ // "start" and frequency "freq". All frequencies are assumed to sum to "1 <<
130
+ // scale_bits", and the resulting bytes get written to ptr (which is updated).
131
+ static inline void RansDecAdvance(RansState& r, uint8_t*& ptr, uint32_t start, uint32_t freq)
132
+ {
133
+
134
+ // s, x = D(x)
135
+ r = freq * (r >> SCALE_BITS) + (r & DEC_MASK) - start;
136
+
137
+ // renormalize
138
+ while (r < RANS_BYTE_L) {
139
+ r = (r << 8) | *ptr++;
140
+ }
141
+ }
neural_codec/DCVC/src/cpp/setup.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import glob
5
+ import sys
6
+ from setuptools import setup
7
+ from pybind11.setup_helpers import Pybind11Extension, build_ext
8
+
9
+
10
+ if sys.platform == "win32":
11
+ extra_compile_args = ['/std:c++17', '/O2', '/W4', '/WX', '/wd4100']
12
+ extra_link_args = []
13
+ else:
14
+ extra_compile_args = ['-std=c++17', '-O3', '-fPIC', '-Wall', '-Wextra', '-Werror']
15
+ extra_link_args = []
16
+
17
+
18
+ setup(
19
+ name="MLCodec_extensions_cpp",
20
+ ext_modules=[
21
+ Pybind11Extension(
22
+ name='MLCodec_extensions_cpp',
23
+ sources=glob.glob('py_rans/*.cpp'),
24
+ extra_compile_args=extra_compile_args,
25
+ extra_link_args=extra_link_args,
26
+ ),
27
+ ],
28
+ cmdclass={"build_ext": build_ext},
29
+ zip_safe=False,
30
+ python_requires=">=3.12",
31
+ )
neural_codec/DCVC/src/layers/cuda_inference.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import os
5
+
6
+ import torch
7
+ import torch.nn.functional as F
8
+
9
+
10
+ CUSTOMIZED_CUDA_INFERENCE = False
11
+ # Escape hatch: the native inference kernel makes vec4-aligned memory accesses
12
+ # that can crash (illegal/misaligned address) on some frame geometries under
13
+ # full-resolution scoring. Set DCVC_FORCE_PYTORCH=1 to force the pure-pytorch
14
+ # fallback (slower but always correct) for such inputs.
15
+ if os.environ.get("DCVC_FORCE_PYTORCH", "") not in ("1", "true", "True"):
16
+ try:
17
+ from inference_extensions_cuda import process_with_mask_cuda, combine_for_reading_2x_cuda, \
18
+ restore_y_2x_cuda, restore_y_4x_cuda, build_index_dec_cuda, \
19
+ round_and_to_int8_cuda, clamp_reciprocal_with_quant_cuda, bias_quant_cuda, \
20
+ add_and_multiply_cuda, bias_pixel_shuffle_8_cuda, replicate_pad_cuda, \
21
+ build_index_enc_cuda, DepthConvProxy, SubpelConv2xProxy # noqa: F401
22
+ CUSTOMIZED_CUDA_INFERENCE = True
23
+ except Exception: # pylint: disable=W0718
24
+ pass
25
+
26
+
27
+ if not CUSTOMIZED_CUDA_INFERENCE and 'SUPPRESS_CUSTOM_KERNEL_WARNING' not in os.environ:
28
+ print("cannot import cuda implementation for inference, fallback to pytorch.")
29
+
30
+
31
+ def round_and_to_int8(z):
32
+ if CUSTOMIZED_CUDA_INFERENCE and z.is_cuda:
33
+ z_int8 = round_and_to_int8_cuda(z)
34
+ return z, z_int8
35
+
36
+ z_hat = torch.clamp(torch.round(z), -128., 127.)
37
+ z_hat_write = z_hat.to(dtype=torch.int8)
38
+ return z_hat, z_hat_write
39
+
40
+
41
+ def clamp_reciprocal_with_quant(q_dec, y, min_val):
42
+ if CUSTOMIZED_CUDA_INFERENCE and q_dec.is_cuda:
43
+ # q_dec is not inplace modified at decoder side
44
+ q_dec = clamp_reciprocal_with_quant_cuda(q_dec, y, min_val)
45
+ return q_dec, y
46
+
47
+ q_dec = torch.clamp_min(q_dec, min_val)
48
+ q_enc = torch.reciprocal(q_dec)
49
+ y = y * q_enc
50
+ return q_dec, y
51
+
52
+
53
+ def add_and_multiply(y_hat_0, y_hat_1, q_dec):
54
+ if CUSTOMIZED_CUDA_INFERENCE and y_hat_0.is_cuda:
55
+ add_and_multiply_cuda(y_hat_0, y_hat_1, q_dec)
56
+ return y_hat_0
57
+
58
+ y_hat = y_hat_0 + y_hat_1
59
+ y_hat = y_hat * q_dec
60
+ return y_hat
61
+
62
+
63
+ def process_with_mask(y, scales, means, mask, force_zero_thres):
64
+ if CUSTOMIZED_CUDA_INFERENCE and y.is_cuda:
65
+ thres = force_zero_thres if force_zero_thres is not None else -1.
66
+ return process_with_mask_cuda(y, scales, means, mask, thres)
67
+
68
+ scales_hat = scales * mask
69
+ means_hat = means * mask
70
+
71
+ y_res = (y - means_hat) * mask
72
+ y_q = torch.round(y_res)
73
+ if force_zero_thres is not None:
74
+ cond = scales_hat > force_zero_thres
75
+ y_q = y_q * cond
76
+ y_q = torch.clamp(y_q, -128., 127.)
77
+ y_hat = y_q + means_hat
78
+
79
+ return y_res, y_q, y_hat, scales_hat
80
+
81
+
82
+ def combine_for_reading_2x(x, mask, inplace=False):
83
+ if CUSTOMIZED_CUDA_INFERENCE and x.is_cuda and x.is_contiguous():
84
+ B, C, H, W = x.shape
85
+ if inplace:
86
+ out = x[:, :C // 2, :, :]
87
+ else:
88
+ out = torch.empty((B, C // 2, H, W), dtype=x.dtype, layout=x.layout, device=x.device)
89
+ combine_for_reading_2x_cuda(out, x, mask)
90
+ return out
91
+
92
+ x = x * mask
93
+ x0, x1 = x.chunk(2, 1)
94
+ return x0 + x1
95
+
96
+
97
+ def restore_y_2x(y, means, mask):
98
+ if CUSTOMIZED_CUDA_INFERENCE and y.is_cuda and y.is_contiguous():
99
+ out = torch.empty_like(means)
100
+ restore_y_2x_cuda(out, y, means, mask)
101
+ return out
102
+
103
+ return (torch.cat((y, y), dim=1) + means) * mask
104
+
105
+
106
+ def restore_y_2x_with_cat_after(y, means, mask, to_cat):
107
+ if CUSTOMIZED_CUDA_INFERENCE and y.is_cuda and y.is_contiguous():
108
+ B, C1, H, W = means.shape
109
+ C2 = to_cat.shape[1]
110
+ out = torch.empty((B, C1 + C2, H, W), dtype=means.dtype, layout=means.layout,
111
+ device=means.device)
112
+ restore_y_2x_cuda(out[:, :C1, :, :], y, means, mask)
113
+ out[:, C1:, :, :] = to_cat
114
+ return out[:, :C1, :, :], out
115
+
116
+ out = (torch.cat((y, y), dim=1) + means) * mask
117
+ return out, torch.cat((out, to_cat), dim=1)
118
+
119
+
120
+ def restore_y_4x(y, means, mask):
121
+ if CUSTOMIZED_CUDA_INFERENCE and y.is_cuda and y.is_contiguous():
122
+ out = torch.empty_like(means)
123
+ restore_y_4x_cuda(out, y, means, mask)
124
+ return out
125
+
126
+ return (torch.cat((y, y, y, y), dim=1) + means) * mask
127
+
128
+
129
+ def build_index_dec(scales, scale_min, scale_max, log_scale_min, log_step_recip, skip_thres=None):
130
+ if CUSTOMIZED_CUDA_INFERENCE and scales.is_cuda:
131
+ out = torch.empty_like(scales, dtype=torch.uint8)
132
+ skip_cond = None
133
+ if skip_thres is not None:
134
+ skip_cond = torch.empty_like(scales, dtype=torch.bool)
135
+ else:
136
+ skip_thres = -1.
137
+
138
+ build_index_dec_cuda(out, skip_cond, scales, scale_min, scale_max, log_scale_min,
139
+ log_step_recip, skip_thres)
140
+ return out, skip_cond
141
+
142
+ skip_cond = None
143
+ scales = scales.clamp_(scale_min, scale_max)
144
+ indexes = (torch.log(scales) - log_scale_min) * log_step_recip
145
+ indexes = indexes.to(dtype=torch.uint8)
146
+ if skip_thres is not None:
147
+ skip_cond = scales > skip_thres
148
+ return indexes, skip_cond
149
+
150
+
151
+ def build_index_enc(symbols, scales, scale_min, scale_max, log_scale_min,
152
+ log_step_recip, skip_thres=None):
153
+ if CUSTOMIZED_CUDA_INFERENCE and scales.is_cuda:
154
+ out = torch.empty_like(scales, dtype=torch.int16)
155
+ skip_cond = None
156
+ if skip_thres is not None:
157
+ skip_cond = torch.empty_like(scales, dtype=torch.bool)
158
+ else:
159
+ skip_thres = -1.
160
+
161
+ build_index_enc_cuda(out, skip_cond, symbols, scales, scale_min, scale_max, log_scale_min,
162
+ log_step_recip, skip_thres)
163
+
164
+ out = out[skip_cond]
165
+ return out
166
+
167
+ scales = scales.clamp_(scale_min, scale_max)
168
+ indexes = (torch.log(scales) - log_scale_min) * log_step_recip
169
+ indexes = indexes.to(dtype=torch.uint8)
170
+ symbols = symbols.to(dtype=torch.int16)
171
+ out = (symbols << 8) + indexes
172
+ out = out.to(dtype=torch.int16)
173
+ if skip_thres is not None:
174
+ skip_cond = scales > skip_thres
175
+ out = out[skip_cond]
176
+ return out
177
+
178
+
179
+ def replicate_pad(x, pad_b, pad_r):
180
+ if pad_b == 0 and pad_r == 0:
181
+ return x
182
+ if CUSTOMIZED_CUDA_INFERENCE and x.is_cuda:
183
+ return replicate_pad_cuda(x, pad_b, pad_r)
184
+ return F.pad(x, (0, pad_r, 0, pad_b), mode="replicate")
185
+
186
+
187
+ def bias_pixel_shuffle_8(x, bias):
188
+ if CUSTOMIZED_CUDA_INFERENCE and x.is_cuda:
189
+ B, C, H, W = x.shape
190
+ assert B == 1
191
+ out = torch.empty((B, 3, H * 8, W * 8), dtype=x.dtype, device=x.device, layout=x.layout)
192
+ bias_pixel_shuffle_8_cuda(out, x, bias, C, H * W, W, True)
193
+ return out
194
+
195
+ out = x + bias[None, :, None, None]
196
+ out = F.pixel_shuffle(out, 8)
197
+ out = torch.clamp(out, 0., 1.)
198
+ return out
199
+
200
+
201
+ def bias_quant(x, bias, quant_step):
202
+ if CUSTOMIZED_CUDA_INFERENCE and x.is_cuda:
203
+ bias_quant_cuda(x, bias, quant_step)
204
+ return x
205
+
206
+ out = x + bias[None, :, None, None]
207
+ out = out * quant_step
208
+ return out
neural_codec/DCVC/src/layers/extensions/inference/bind.cpp ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "def.h"
5
+ #include <torch/extension.h>
6
+
7
+ PYBIND11_MODULE(TORCH_EXTENSION_NAME, m)
8
+ {
9
+ m.def("process_with_mask_cuda", &process_with_mask_cuda);
10
+ m.def("combine_for_reading_2x_cuda", &combine_for_reading_2x_cuda);
11
+ m.def("restore_y_2x_cuda", &restore_y_2x_cuda);
12
+ m.def("restore_y_4x_cuda", &restore_y_4x_cuda);
13
+ m.def("build_index_dec_cuda", &build_index_dec_cuda);
14
+ m.def("build_index_enc_cuda", &build_index_enc_cuda);
15
+ m.def("bias_quant_cuda", &bias_quant_cuda);
16
+ m.def("round_and_to_int8_cuda", &round_and_to_int8_cuda);
17
+ m.def("clamp_reciprocal_with_quant_cuda", &clamp_reciprocal_with_quant_cuda);
18
+ m.def("add_and_multiply_cuda", &add_and_multiply_cuda);
19
+ m.def("bias_pixel_shuffle_8_cuda", &bias_pixel_shuffle_8_cuda);
20
+ m.def("replicate_pad_cuda", &replicate_pad_cuda);
21
+ m.def("bias_wsilu_depthwise_conv2d_cuda", &bias_wsilu_depthwise_conv2d_cuda);
22
+
23
+ py::class_<DepthConvProxy>(m, "DepthConvProxy")
24
+ .def(py::init<>())
25
+ .def("set_param", &DepthConvProxy::set_param)
26
+ .def("set_param_with_adaptor", &DepthConvProxy::set_param_with_adaptor)
27
+ .def("forward", &DepthConvProxy::forward)
28
+ .def("forward_with_quant_step", &DepthConvProxy::forward_with_quant_step)
29
+ .def("forward_with_cat", &DepthConvProxy::forward_with_cat);
30
+
31
+ py::class_<SubpelConv2xProxy>(m, "SubpelConv2xProxy")
32
+ .def(py::init<>())
33
+ .def("set_param", &SubpelConv2xProxy::set_param)
34
+ .def("forward", &SubpelConv2xProxy::forward)
35
+ .def("forward_with_cat", &SubpelConv2xProxy::forward_with_cat);
36
+ }
neural_codec/DCVC/src/layers/extensions/inference/common.h ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #pragma once
5
+ #include <ATen/cuda/CUDAContext.h>
6
+ #include <cuda.h>
7
+ #include <cuda_fp16.h>
8
+ #include <cuda_runtime.h>
9
+ #include <torch/extension.h>
10
+ #include <type_traits>
11
+ #include <utility>
12
+
13
+ // Constrain the generic vec4 helper templates below to genuine 4-lane vector
14
+ // types (float4/Half4/short4/char4/uchar4/bool4). We detect them by the
15
+ // presence of a `.w` member: all four-component vectors have `.x/.y/.z/.w`,
16
+ // whereas scalar library types do NOT — crucially `c10::Half` (and
17
+ // `c10::BFloat16`) store their raw bits in a member literally named `x`, so a
18
+ // `.x`-based probe would wrongly classify Half as a vector and make these
19
+ // templates collide with the scalar Half operators (and with torch's own
20
+ // operators) under newer pybind11 / C++20 headers. `.w` is unique to the real
21
+ // vectors, so it cleanly separates them.
22
+ namespace dcvc_detail {
23
+ template <typename T, typename = void>
24
+ struct is_vec4 : ::std::false_type {};
25
+ template <typename T>
26
+ struct is_vec4<T, ::std::void_t<decltype(::std::declval<T&>().w)>> : ::std::true_type {};
27
+ } // namespace dcvc_detail
28
+ #define DCVC_VEC_ENABLE(TT) typename ::std::enable_if<dcvc_detail::is_vec4<TT>::value, int>::type = 0
29
+
30
+ // T maybe vector type, and may be different from t.dtype
31
+ template <typename T>
32
+ struct GPUTensor1D {
33
+ GPUTensor1D(torch::Tensor& t) : ptr(static_cast<T*>(t.data_ptr())) {}
34
+ GPUTensor1D(const torch::Tensor& t) : ptr(static_cast<T*>(t.data_ptr())) {}
35
+ GPUTensor1D(T* t) : ptr(static_cast<T*>(t)) { assert(t == nullptr); }
36
+
37
+ __device__ T& operator[](int idx) { return ptr[idx]; }
38
+ __device__ T& operator[](int idx) const { return ptr[idx]; }
39
+
40
+ T* __restrict__ const ptr;
41
+ };
42
+
43
+ template <typename scalar_t>
44
+ using Packed4DTensorAccessor32 = torch::PackedTensorAccessor32<scalar_t, 4, torch::RestrictPtrTraits>;
45
+ template <typename scalar_t>
46
+ using Packed1DTensorAccessor32 = torch::PackedTensorAccessor32<scalar_t, 1, torch::RestrictPtrTraits>;
47
+
48
+ struct __align__(8) Half4
49
+ {
50
+ c10::Half x;
51
+ c10::Half y;
52
+ c10::Half z;
53
+ c10::Half w;
54
+ };
55
+
56
+ struct __align__(4) bool4
57
+ {
58
+ bool x;
59
+ bool y;
60
+ bool z;
61
+ bool w;
62
+ };
63
+
64
+ __forceinline__ __device__ float4 make_vec4(const float& x, const float& y, const float& z,
65
+ const float& w)
66
+ {
67
+ return make_float4(x, y, z, w);
68
+ }
69
+
70
+ __forceinline__ __device__ Half4 make_vec4(const c10::Half& x, const c10::Half& y,
71
+ const c10::Half& z, const c10::Half& w)
72
+ {
73
+ Half4 t;
74
+ t.x = x;
75
+ t.y = y;
76
+ t.z = z;
77
+ t.w = w;
78
+ return t;
79
+ }
80
+
81
+ __forceinline__ __device__ Half4 make_Half4(const c10::Half& x, const c10::Half& y,
82
+ const c10::Half& z, const c10::Half& w)
83
+ {
84
+ Half4 t;
85
+ t.x = x;
86
+ t.y = y;
87
+ t.z = z;
88
+ t.w = w;
89
+ return t;
90
+ }
91
+
92
+ __forceinline__ __device__ bool4 make_vec4(const bool& x, const bool& y, const bool& z, const bool& w)
93
+ {
94
+ bool4 t;
95
+ t.x = x;
96
+ t.y = y;
97
+ t.z = z;
98
+ t.w = w;
99
+ return t;
100
+ }
101
+
102
+ __forceinline__ __device__ c10::Half round(const c10::Half& a)
103
+ {
104
+ return static_cast<c10::Half>(__half2int_rn(a));
105
+ }
106
+
107
+ template <typename T, DCVC_VEC_ENABLE(T)>
108
+ __forceinline__ __device__ T round(const T& a)
109
+ {
110
+ return make_vec4(round(a.x), round(a.y), round(a.z), round(a.w));
111
+ }
112
+
113
+ __forceinline__ __device__ int8_t to_int8(const float& a)
114
+ {
115
+ return static_cast<int8_t>(a);
116
+ }
117
+
118
+ __forceinline__ __device__ int8_t to_int8(const c10::Half& a)
119
+ {
120
+ return static_cast<int8_t>(a);
121
+ }
122
+
123
+ template <typename T, DCVC_VEC_ENABLE(T)>
124
+ __forceinline__ __device__ char4 to_int8(const T& a)
125
+ {
126
+ return make_char4(to_int8(a.x), to_int8(a.y), to_int8(a.z), to_int8(a.w));
127
+ }
128
+
129
+ __forceinline__ __device__ uint8_t to_uint8(const float& a)
130
+ {
131
+ return static_cast<uint8_t>(a);
132
+ }
133
+
134
+ __forceinline__ __device__ uint8_t to_uint8(const c10::Half& a)
135
+ {
136
+ return static_cast<uint8_t>(__half2uint_rd(a));
137
+ }
138
+
139
+ template <typename T, DCVC_VEC_ENABLE(T)>
140
+ __forceinline__ __device__ uchar4 to_uint8(const T& a)
141
+ {
142
+ return make_uchar4(to_uint8(a.x), to_uint8(a.y), to_uint8(a.z), to_uint8(a.w));
143
+ }
144
+
145
+ __forceinline__ __device__ int16_t to_int16(const float& a)
146
+ {
147
+ return static_cast<int16_t>(a);
148
+ }
149
+
150
+ __forceinline__ __device__ int16_t to_int16(const c10::Half& a)
151
+ {
152
+ return static_cast<int16_t>(__half2int_rd(a));
153
+ }
154
+
155
+ template <typename T, DCVC_VEC_ENABLE(T)>
156
+ __forceinline__ __device__ short4 to_int16(const T& a)
157
+ {
158
+ return make_short4(to_int16(a.x), to_int16(a.y), to_int16(a.z), to_int16(a.w));
159
+ }
160
+
161
+ __forceinline__ __device__ short4 operator<<(const short4& a, const int b)
162
+ {
163
+ return make_short4(a.x << b, a.y << b, a.z << b, a.w << b);
164
+ }
165
+
166
+ __forceinline__ __device__ c10::Half min(const c10::Half& a, const c10::Half& b)
167
+ {
168
+ return __hmin(a, b);
169
+ }
170
+ __forceinline__ __device__ c10::Half max(const c10::Half& a, const c10::Half& b)
171
+ {
172
+ return __hmax(a, b);
173
+ }
174
+
175
+ // Native-half compare. Constrained to EXACT c10::Half operands (via SFINAE) so
176
+ // it can never become a viable candidate for a *mixed* comparison such as
177
+ // `int64_t > c10::Half` inside torch's C++20 headers (TypeSafeSignMath.h). If
178
+ // it did, the implicit int64_t->c10::Half conversion would make it tie with the
179
+ // built-in `<=>`/`>` operators and the build fails with "more than one operator
180
+ // matches" (seen under torch 2.12 / CUDA 13.3, which compile with -std=c++20).
181
+ template <typename A, typename B,
182
+ typename ::std::enable_if<::std::is_same<A, c10::Half>::value &&
183
+ ::std::is_same<B, c10::Half>::value,
184
+ int>::type = 0>
185
+ __forceinline__ __device__ bool operator>(const A& a, const B& b)
186
+ {
187
+ return __hgt(a, b);
188
+ }
189
+
190
+ template <typename A, typename B,
191
+ typename ::std::enable_if<::std::is_same<A, c10::Half>::value &&
192
+ ::std::is_same<B, c10::Half>::value,
193
+ int>::type = 0>
194
+ __forceinline__ __device__ bool operator<(const A& a, const B& b)
195
+ {
196
+ return __hlt(a, b);
197
+ }
198
+
199
+ __forceinline__ __device__ c10::Half log(const c10::Half& a)
200
+ {
201
+ return hlog(a);
202
+ }
203
+
204
+ __forceinline__ __device__ short4 operator+(const short4& a, const short4& b)
205
+ {
206
+ return make_short4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
207
+ }
208
+
209
+ __forceinline__ __device__ float4 operator+(const float4& a, const float4& b)
210
+ {
211
+ return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
212
+ }
213
+
214
+ __forceinline__ __device__ Half4 operator+(const Half4& a, const Half4& b)
215
+ {
216
+ return make_Half4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
217
+ }
218
+
219
+ template <typename T, DCVC_VEC_ENABLE(T)>
220
+ __forceinline__ __device__ T operator-(const T& a, const T& b)
221
+ {
222
+ return make_vec4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
223
+ }
224
+
225
+ __forceinline__ __device__ float4 operator-(const float4& a, const float& b)
226
+ {
227
+ return make_vec4(a.x - b, a.y - b, a.z - b, a.w - b);
228
+ }
229
+
230
+ __forceinline__ __device__ Half4 operator-(const Half4& a, const c10::Half& b)
231
+ {
232
+ return make_vec4(a.x - b, a.y - b, a.z - b, a.w - b);
233
+ }
234
+
235
+ __forceinline__ __device__ c10::Half operator*(const c10::Half& a, const bool b)
236
+ {
237
+ return b ? a : static_cast<c10::Half>(0.f);
238
+ }
239
+
240
+ template <typename T, DCVC_VEC_ENABLE(T)>
241
+ __forceinline__ __device__ T operator*(const T& a, const T& b)
242
+ {
243
+ return make_vec4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
244
+ }
245
+
246
+ template <typename T, DCVC_VEC_ENABLE(T)>
247
+ __forceinline__ __device__ T operator*(const T& a, const bool4& b)
248
+ {
249
+ return make_vec4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
250
+ }
251
+
252
+ template <typename T1, typename T2, DCVC_VEC_ENABLE(T1)>
253
+ __forceinline__ __device__ T1 operator*(const T1& a, const T2& b)
254
+ {
255
+ return make_vec4(a.x * b, a.y * b, a.z * b, a.w * b);
256
+ }
257
+
258
+ template <typename T, DCVC_VEC_ENABLE(T)>
259
+ __forceinline__ __device__ T max(const T& a, const T& b)
260
+ {
261
+ return make_vec4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w));
262
+ }
263
+
264
+ template <typename T1, typename T2, DCVC_VEC_ENABLE(T1)>
265
+ __forceinline__ __device__ T1 max(const T1& a, const T2& b)
266
+ {
267
+ return make_vec4(max(a.x, b), max(a.y, b), max(a.z, b), max(a.w, b));
268
+ }
269
+
270
+ template <typename T, DCVC_VEC_ENABLE(T)>
271
+ __forceinline__ __device__ T min(const T& a, const T& b)
272
+ {
273
+ return make_vec4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w));
274
+ }
275
+
276
+ template <typename T1, typename T2, DCVC_VEC_ENABLE(T1)>
277
+ __forceinline__ __device__ T1 min(const T1& a, const T2& b)
278
+ {
279
+ return make_vec4(min(a.x, b), min(a.y, b), min(a.z, b), min(a.w, b));
280
+ }
281
+
282
+ template <typename T, DCVC_VEC_ENABLE(T)>
283
+ __forceinline__ __device__ T log(const T& a)
284
+ {
285
+ return make_vec4(log(a.x), log(a.y), log(a.z), log(a.w));
286
+ }
287
+
288
+ __forceinline__ __device__ float reciprocal(const float& a)
289
+ {
290
+ return __frcp_rd(a);
291
+ }
292
+
293
+ __forceinline__ __device__ c10::Half reciprocal(const c10::Half& a)
294
+ {
295
+ return hrcp(a);
296
+ }
297
+
298
+ template <typename T, DCVC_VEC_ENABLE(T)>
299
+ __forceinline__ __device__ T reciprocal(const T& a)
300
+ {
301
+ return make_vec4(reciprocal(a.x), reciprocal(a.y), reciprocal(a.z), reciprocal(a.w));
302
+ }
303
+
304
+ template <typename T1, typename T2, DCVC_VEC_ENABLE(T1)>
305
+ __forceinline__ __device__ bool4 operator>(const T1& a, const T2& b)
306
+ {
307
+ return make_vec4(a.x > b, a.y > b, a.z > b, a.w > b);
308
+ }
309
+
310
+ __forceinline__ __device__ float sigmoid(const float x)
311
+ {
312
+ return 1.0f / (1.0f + expf(-x));
313
+ }
314
+
315
+ __forceinline__ __device__ float wsilu(const float x)
316
+ {
317
+ return x * sigmoid(4.0f * x);
318
+ }
319
+
320
+ __forceinline__ __device__ c10::Half wsilu(const c10::Half x)
321
+ {
322
+ return __float2half_rn(wsilu(__half2float(x)));
323
+ }
324
+
325
+ __forceinline__ __device__ float4 wsilu(float4 data)
326
+ {
327
+ data.x = wsilu(data.x);
328
+ data.y = wsilu(data.y);
329
+ data.z = wsilu(data.z);
330
+ data.w = wsilu(data.w);
331
+ return data;
332
+ }
333
+
334
+ __forceinline__ __device__ Half4 wsilu(Half4 data)
335
+ {
336
+ data.x = wsilu(data.x);
337
+ data.y = wsilu(data.y);
338
+ data.z = wsilu(data.z);
339
+ data.w = wsilu(data.w);
340
+ return data;
341
+ }
342
+
343
+ __forceinline__ __device__ float multiply_add(const float a, const float b, const float c)
344
+ {
345
+ return __fmaf_rn(a, b, c);
346
+ }
347
+
348
+ __forceinline__ __device__ c10::Half multiply_add(const c10::Half a, const c10::Half b, const c10::Half c)
349
+ {
350
+
351
+ return __hfma(a, b, c);
352
+ }
neural_codec/DCVC/src/layers/extensions/inference/def.h ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include <torch/extension.h>
5
+
6
+ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor>
7
+ process_with_mask_cuda(const torch::Tensor& y, const torch::Tensor& scales, const torch::Tensor& means,
8
+ const torch::Tensor& mask, const float force_zero_thres);
9
+
10
+ void combine_for_reading_2x_cuda(torch::Tensor& out, const torch::Tensor& x, const torch::Tensor& mask);
11
+ void restore_y_2x_cuda(torch::Tensor& out, const torch::Tensor& y, const torch::Tensor& means,
12
+ const torch::Tensor& mask);
13
+ void restore_y_4x_cuda(torch::Tensor& out, const torch::Tensor& y, const torch::Tensor& means,
14
+ const torch::Tensor& mask);
15
+
16
+ void build_index_dec_cuda(torch::Tensor& out, torch::optional<torch::Tensor>& cond_out,
17
+ const torch::Tensor& scales, const float scale_min, const float scale_max,
18
+ const float log_scale_min, const float log_step_recip,
19
+ const float skip_thres);
20
+
21
+ void build_index_enc_cuda(torch::Tensor& out, torch::optional<torch::Tensor>& cond_out,
22
+ const torch::Tensor& symbols, const torch::Tensor& scales,
23
+ const float scale_min, const float scale_max, const float log_scale_min,
24
+ const float log_step_recip, const float skip_thres);
25
+
26
+ void bias_wsilu_cuda(torch::Tensor& x, const torch::Tensor& bias);
27
+
28
+ void bias_shortcut_cuda(torch::Tensor& x, const torch::Tensor& bias, const torch::Tensor& shortcut);
29
+ void bias_shortcut_no_inplace_cuda(torch::Tensor& out, const torch::Tensor& x,
30
+ const torch::Tensor& bias, const torch::Tensor& shortcut);
31
+ void bias_shortcut_2_cuda(torch::Tensor& x, const torch::Tensor& bias, torch::Tensor& shortcut);
32
+ void bias_shortcut_with_quant_step_cuda(torch::Tensor& x, const torch::Tensor& bias,
33
+ const torch::Tensor& quant_step, const torch::Tensor& shortcut);
34
+
35
+ void bias_quant_cuda(torch::Tensor& x, const torch::Tensor& bias, const torch::Tensor& quant_step);
36
+
37
+ void bias_wsilu_chunk_add_cuda(torch::Tensor& x, const torch::Tensor& bias);
38
+
39
+ void bias_pixel_shuffle_2_cuda(torch::Tensor& out, const torch::Tensor& x,
40
+ const torch::Tensor& bias, const int C, const int N, const int W);
41
+ void bias_pixel_shuffle_8_cuda(torch::Tensor& out, const torch::Tensor& x, const torch::Tensor& bias,
42
+ const int C, const int N, const int W, bool clamp);
43
+ torch::Tensor replicate_pad_cuda(const torch::Tensor& x, const int padB, const int padR);
44
+
45
+ torch::Tensor round_and_to_int8_cuda(torch::Tensor& z);
46
+ torch::Tensor clamp_reciprocal_with_quant_cuda(const torch::Tensor& q_dec, torch::Tensor& y,
47
+ const float min_val);
48
+ void add_and_multiply_cuda(torch::Tensor& x0, const torch::Tensor& x1, const torch::Tensor q);
49
+
50
+ torch::Tensor bias_wsilu_depthwise_conv2d_cuda(const torch::Tensor& x, const torch::Tensor& weight,
51
+ const torch::Tensor& bias);
52
+
53
+ class DepthConvProxy {
54
+ public:
55
+ DepthConvProxy() = default;
56
+ ~DepthConvProxy() = default;
57
+
58
+ void set_param(const torch::Tensor& dc_conv1_weight, const torch::Tensor& dc_conv1_bias,
59
+ const torch::Tensor& dc_depth_conv_weight,
60
+ const torch::Tensor& dc_depth_conv_bias, const torch::Tensor& dc_conv2_weight,
61
+ const torch::Tensor& dc_conv2_bias, const torch::Tensor& ffn_conv1_weight,
62
+ const torch::Tensor& ffn_conv1_bias, const torch::Tensor& ffn_conv2_weight,
63
+ const torch::Tensor& ffn_conv2_bias, const bool shortcut);
64
+ void set_param_with_adaptor(
65
+ const torch::Tensor& dc_conv1_weight, const torch::Tensor& dc_conv1_bias,
66
+ const torch::Tensor& dc_depth_conv_weight, const torch::Tensor& dc_depth_conv_bias,
67
+ const torch::Tensor& dc_conv2_weight, const torch::Tensor& dc_conv2_bias,
68
+ const torch::Tensor& ffn_conv1_weight, const torch::Tensor& ffn_conv1_bias,
69
+ const torch::Tensor& ffn_conv2_weight, const torch::Tensor& ffn_conv2_bias,
70
+ const torch::Tensor& adaptor_weight, const torch::Tensor& adaptor_bias, const bool shortcut);
71
+ torch::Tensor forward(const torch::Tensor& x);
72
+ torch::Tensor forward_with_quant_step(const torch::Tensor& x, const torch::Tensor& quant_step);
73
+ torch::Tensor forward_with_cat(const torch::Tensor& x, const torch::Tensor& to_cat,
74
+ const bool cat_at_front);
75
+
76
+ private:
77
+ std::tuple<torch::Tensor, torch::Tensor> forward_common(const torch::Tensor& x);
78
+
79
+ private:
80
+ torch::Tensor _dc_conv1_weight;
81
+ torch::Tensor _dc_conv1_bias;
82
+ torch::Tensor _dc_depth_conv_weight;
83
+ torch::Tensor _dc_conv2_weight;
84
+ torch::Tensor _dc_conv2_bias;
85
+ torch::Tensor _ffn_conv1_weight;
86
+ torch::Tensor _ffn_conv1_bias;
87
+ torch::Tensor _ffn_conv2_weight;
88
+ torch::Tensor _ffn_conv2_bias;
89
+ bool _adaptor{ false };
90
+ bool _shortcut{ false };
91
+ };
92
+
93
+ class SubpelConv2xProxy {
94
+ public:
95
+ SubpelConv2xProxy() = default;
96
+ ~SubpelConv2xProxy() = default;
97
+
98
+ void set_param(const torch::Tensor& weight, const torch::Tensor& bias, const int padding);
99
+ torch::Tensor forward(const torch::Tensor& x);
100
+ torch::Tensor forward_with_cat(const torch::Tensor& x, const torch::Tensor& to_cat,
101
+ const bool cat_at_front);
102
+
103
+ private:
104
+ torch::Tensor _weight;
105
+ torch::Tensor _bias;
106
+ int _padding{ 0 };
107
+ };
neural_codec/DCVC/src/layers/extensions/inference/impl.cpp ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "def.h"
5
+ namespace F = torch::nn::functional;
6
+
7
+ void DepthConvProxy::set_param(
8
+ const torch::Tensor& dc_conv1_weight, const torch::Tensor& dc_conv1_bias,
9
+ const torch::Tensor& dc_depth_conv_weight, const torch::Tensor& dc_depth_conv_bias,
10
+ const torch::Tensor& dc_conv2_weight, const torch::Tensor& dc_conv2_bias,
11
+ const torch::Tensor& ffn_conv1_weight, const torch::Tensor& ffn_conv1_bias,
12
+ const torch::Tensor& ffn_conv2_weight, const torch::Tensor& ffn_conv2_bias, const bool shortcut)
13
+ {
14
+ _dc_conv1_weight = dc_conv1_weight;
15
+ _dc_conv1_bias = dc_conv1_bias;
16
+ _dc_depth_conv_weight = dc_depth_conv_weight;
17
+ _dc_conv2_weight = dc_conv2_weight;
18
+ _dc_conv2_bias = F::conv2d(dc_depth_conv_bias.reshape({ 1, -1, 1, 1 }), dc_conv2_weight);
19
+ _dc_conv2_bias = _dc_conv2_bias.index({ 0, torch::indexing::Slice(), 0, 0 }) + dc_conv2_bias;
20
+ _ffn_conv1_weight = ffn_conv1_weight;
21
+ _ffn_conv1_bias = ffn_conv1_bias;
22
+ _ffn_conv2_weight = ffn_conv2_weight;
23
+ _ffn_conv2_bias = ffn_conv2_bias;
24
+ _shortcut = shortcut;
25
+ }
26
+
27
+ void DepthConvProxy::set_param_with_adaptor(
28
+ const torch::Tensor& dc_conv1_weight, const torch::Tensor& dc_conv1_bias,
29
+ const torch::Tensor& dc_depth_conv_weight, const torch::Tensor& dc_depth_conv_bias,
30
+ const torch::Tensor& dc_conv2_weight, const torch::Tensor& dc_conv2_bias,
31
+ const torch::Tensor& ffn_conv1_weight, const torch::Tensor& ffn_conv1_bias,
32
+ const torch::Tensor& ffn_conv2_weight, const torch::Tensor& ffn_conv2_bias,
33
+ const torch::Tensor& adaptor_weight, const torch::Tensor& adaptor_bias, const bool shortcut)
34
+ {
35
+ _dc_conv1_weight = F::conv2d(torch::transpose(adaptor_weight, 0, 1), dc_conv1_weight);
36
+ _dc_conv1_weight = torch::transpose(_dc_conv1_weight, 0, 1);
37
+ _dc_conv1_weight = torch::cat({ _dc_conv1_weight, adaptor_weight }, 0);
38
+ _dc_conv1_bias = F::conv2d(adaptor_bias.reshape({ 1, -1, 1, 1 }), dc_conv1_weight);
39
+ _dc_conv1_bias = _dc_conv1_bias.index({ 0, torch::indexing::Slice(), 0, 0 }) + dc_conv1_bias;
40
+ _dc_depth_conv_weight = dc_depth_conv_weight;
41
+ _dc_conv2_weight = dc_conv2_weight;
42
+ _dc_conv2_bias = F::conv2d(dc_depth_conv_bias.reshape({ 1, -1, 1, 1 }), dc_conv2_weight);
43
+ _dc_conv2_bias = _dc_conv2_bias.index({ 0, torch::indexing::Slice(), 0, 0 }) + dc_conv2_bias;
44
+ _dc_conv2_bias = _dc_conv2_bias + adaptor_bias;
45
+ _ffn_conv1_weight = ffn_conv1_weight;
46
+ _ffn_conv1_bias = ffn_conv1_bias;
47
+ _ffn_conv2_weight = ffn_conv2_weight;
48
+ _ffn_conv2_bias = ffn_conv2_bias;
49
+ _shortcut = shortcut;
50
+ _adaptor = true;
51
+ }
52
+
53
+ std::tuple<torch::Tensor, torch::Tensor> DepthConvProxy::forward_common(const torch::Tensor& x)
54
+ {
55
+ auto identity = x;
56
+ // depthconv
57
+ torch::Tensor out;
58
+ if (_adaptor) {
59
+ // NOTE: Here we always fuse adaptor with the first conv1x1 (when even in_ch > out_ch).
60
+ // It brings larger MACs, but it faster on A100 due to lower memory cost.
61
+ auto out_identity = F::conv2d(identity, _dc_conv1_weight);
62
+ auto chunks = torch::chunk(out_identity, 2, 1);
63
+ out = chunks[0];
64
+ identity = chunks[1];
65
+ } else {
66
+ out = F::conv2d(identity, _dc_conv1_weight);
67
+ }
68
+ out = bias_wsilu_depthwise_conv2d_cuda(out, _dc_depth_conv_weight, _dc_conv1_bias);
69
+ out = F::conv2d(out, _dc_conv2_weight);
70
+
71
+ if (_shortcut) {
72
+ bias_shortcut_2_cuda(out, _dc_conv2_bias, identity);
73
+ } else {
74
+ bias_shortcut_cuda(out, _dc_conv2_bias, identity);
75
+ identity = out;
76
+ }
77
+ // ffn
78
+ out = F::conv2d(out, _ffn_conv1_weight);
79
+ bias_wsilu_chunk_add_cuda(out, _ffn_conv1_bias);
80
+ out = F::conv2d(out, _ffn_conv2_weight);
81
+ return { out, identity };
82
+ }
83
+
84
+ torch::Tensor DepthConvProxy::forward(const torch::Tensor& x)
85
+ {
86
+ auto [out, identity] = forward_common(x);
87
+ bias_shortcut_cuda(out, _ffn_conv2_bias, identity);
88
+ return out;
89
+ }
90
+
91
+ torch::Tensor DepthConvProxy::forward_with_quant_step(const torch::Tensor& x,
92
+ const torch::Tensor& quant_step)
93
+ {
94
+ auto [out, identity] = forward_common(x);
95
+ bias_shortcut_with_quant_step_cuda(out, _ffn_conv2_bias, quant_step, identity);
96
+ return out;
97
+ }
98
+
99
+ torch::Tensor DepthConvProxy::forward_with_cat(const torch::Tensor& x, const torch::Tensor& to_cat,
100
+ const bool cat_at_front)
101
+ {
102
+ auto [t, identity] = forward_common(x);
103
+
104
+ auto t_shape = t.sizes();
105
+ auto B = t_shape[0];
106
+ auto C = t_shape[1];
107
+ auto H = t_shape[2];
108
+ auto W = t_shape[3];
109
+ auto add_ch = to_cat.sizes()[1];
110
+ auto out = torch::empty({ B, C + add_ch, H, W }, t.options());
111
+ if (cat_at_front) {
112
+ auto t_out = out.narrow(1, add_ch, C);
113
+ bias_shortcut_no_inplace_cuda(t_out, t, _ffn_conv2_bias, identity);
114
+ out.narrow(1, 0, add_ch) = to_cat;
115
+ } else {
116
+ auto t_out = out.narrow(1, 0, C);
117
+ bias_shortcut_no_inplace_cuda(t_out, t, _ffn_conv2_bias, identity);
118
+ out.narrow(1, C, add_ch) = to_cat;
119
+ }
120
+ return out;
121
+ }
122
+
123
+ void SubpelConv2xProxy::set_param(const torch::Tensor& weight, const torch::Tensor& bias,
124
+ const int padding)
125
+ {
126
+ _weight = weight;
127
+ _bias = bias;
128
+ _padding = padding;
129
+ }
130
+
131
+ torch::Tensor SubpelConv2xProxy::forward(const torch::Tensor& x)
132
+ {
133
+ auto t = F::conv2d(x, _weight, F::Conv2dFuncOptions().padding(_padding));
134
+ auto t_shape = t.sizes();
135
+ auto B = t_shape[0];
136
+ auto C = t_shape[1];
137
+ auto H = t_shape[2];
138
+ auto W = t_shape[3];
139
+ auto out = torch::empty({ B, C / 4, H * 2, W * 2 }, t.options());
140
+ assert(B == 1);
141
+ bias_pixel_shuffle_2_cuda(out, t, _bias, C, H * W, W);
142
+ return out;
143
+ }
144
+
145
+ torch::Tensor SubpelConv2xProxy::forward_with_cat(const torch::Tensor& x, const torch::Tensor& to_cat,
146
+ const bool cat_at_front)
147
+ {
148
+ auto t = F::conv2d(x, _weight, F::Conv2dFuncOptions().padding(_padding));
149
+ auto t_shape = t.sizes();
150
+ auto B = t_shape[0];
151
+ auto C = t_shape[1];
152
+ auto H = t_shape[2];
153
+ auto W = t_shape[3];
154
+ auto add_ch = to_cat.sizes()[1];
155
+ auto out = torch::empty({ B, add_ch + C / 4, H * 2, W * 2 }, t.options());
156
+ assert(B == 1);
157
+ if (cat_at_front) {
158
+ auto t_out = out.narrow(1, add_ch, C / 4);
159
+ bias_pixel_shuffle_2_cuda(t_out, t, _bias, C, H * W, W);
160
+ out.narrow(1, 0, add_ch) = to_cat;
161
+ } else {
162
+ auto t_out = out.narrow(1, 0, C / 4);
163
+ bias_pixel_shuffle_2_cuda(t_out, t, _bias, C, H * W, W);
164
+ out.narrow(1, C / 4, add_ch) = to_cat;
165
+ }
166
+ return out;
167
+ }
neural_codec/DCVC/src/layers/extensions/inference/kernel.cu ADDED
@@ -0,0 +1,1150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include <ATen/cuda/CUDAContext.h>
5
+ #include <cstdint>
6
+ #include <cuda.h>
7
+ #include <cuda_fp16.h>
8
+ #include <cuda_runtime.h>
9
+ #include <torch/extension.h>
10
+
11
+ #include "common.h"
12
+ #include "def.h"
13
+ #include <vector>
14
+
15
+ template <typename vec_t>
16
+ __forceinline__ __host__ bool can_vectorize(void* pointer)
17
+ {
18
+ uint64_t address = reinterpret_cast<uint64_t>(pointer);
19
+ constexpr int vec4_alignment = std::alignment_of<vec_t>::value;
20
+ return address % vec4_alignment == 0;
21
+ }
22
+
23
+ template <typename vec_t>
24
+ __forceinline__ std::tuple<dim3, dim3, at::cuda::CUDAStream, bool, bool, int, int>
25
+ get_kernel_launch_info(const torch::Tensor& x, const int cDiv = 1, const bool allow_useVec = true)
26
+ {
27
+ const torch::IntArrayRef x_shape = x.sizes();
28
+ const int B = x_shape[0];
29
+ assert(B == 1);
30
+ const int C = x_shape[1];
31
+ const int HW = x_shape[2] * x_shape[3];
32
+ const int N = C * HW / cDiv;
33
+ const int BLOCK_SIZE = 128;
34
+ const dim3 blockDim(BLOCK_SIZE);
35
+ const bool useVec = allow_useVec && N % 4 == 0 && can_vectorize<vec_t>(x.data_ptr());
36
+ const bool biasSafe = HW % 4 == 0;
37
+ const int factor = useVec ? 4 : 1;
38
+ const dim3 gridDim((N / factor + BLOCK_SIZE - 1) / BLOCK_SIZE);
39
+ return { blockDim, gridDim, at::cuda::getCurrentCUDAStream(), useVec, biasSafe, N / factor, HW };
40
+ }
41
+
42
+ template <typename vec_t>
43
+ __forceinline__ std::tuple<dim3, dim3, at::cuda::CUDAStream, bool, int>
44
+ get_kernel_launch_info_flatten(const torch::Tensor& x)
45
+ {
46
+ const int N = x.numel();
47
+ const int BLOCK_SIZE = 128;
48
+ const dim3 blockDim(BLOCK_SIZE);
49
+ const bool useVec = N % 4 == 0 && can_vectorize<vec_t>(x.data_ptr());
50
+ const int factor = useVec ? 4 : 1;
51
+ const dim3 gridDim((N / factor + BLOCK_SIZE - 1) / BLOCK_SIZE);
52
+ return { blockDim, gridDim, at::cuda::getCurrentCUDAStream(), useVec, N / factor };
53
+ }
54
+
55
+ template <typename scalar_t, typename T, bool forceZero = false>
56
+ __global__ void process_with_mask_kernel(GPUTensor1D<T> y_res, GPUTensor1D<T> y_q,
57
+ GPUTensor1D<T> y_hat, GPUTensor1D<T> s_hat,
58
+ const GPUTensor1D<T> y, const GPUTensor1D<T> scales,
59
+ const GPUTensor1D<T> means, const GPUTensor1D<T> mask,
60
+ const scalar_t force_zero_thres, const int N)
61
+ {
62
+ const scalar_t __min_val = static_cast<scalar_t>(-128.f);
63
+ const scalar_t __max_val = static_cast<scalar_t>(127.f);
64
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
65
+
66
+ if (chw < N) {
67
+ T _y = y[chw];
68
+ T _scale = scales[chw];
69
+ T _means = means[chw];
70
+ T _mask = mask[chw];
71
+
72
+ T _s_hat = _scale * _mask;
73
+ T _means_hat = _means * _mask;
74
+ T _y_res = (_y - _means_hat) * _mask;
75
+ T _y_q = round(_y_res);
76
+
77
+ if constexpr (forceZero) {
78
+ _y_q = _y_q * (_s_hat > force_zero_thres);
79
+ }
80
+ _y_q = max(min(_y_q, __max_val), __min_val);
81
+ T _y_hat = _y_q + _means_hat;
82
+
83
+ y_res[chw] = _y_res;
84
+ y_q[chw] = _y_q;
85
+ y_hat[chw] = _y_hat;
86
+ s_hat[chw] = _s_hat;
87
+ }
88
+ }
89
+
90
+ template <typename scalar_t, typename vec_t>
91
+ __forceinline__ void
92
+ process_with_mask_dispatcher(torch::Tensor& y_res, torch::Tensor& y_q, torch::Tensor& y_hat,
93
+ torch::Tensor& s_hat, const torch::Tensor& y,
94
+ const torch::Tensor& scales, const torch::Tensor& means,
95
+ const torch::Tensor& mask, const float force_zero_thres)
96
+ {
97
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(y);
98
+ const bool force_zero = force_zero_thres > 0.f;
99
+
100
+ auto launch_kernel = [&](auto in_v) {
101
+ using in_t = decltype(in_v);
102
+ if (force_zero) {
103
+ process_with_mask_kernel<scalar_t, in_t, true>
104
+ <<<gridDim, blockDim, 0, stream>>>(y_res, y_q, y_hat, s_hat, y, scales, means, mask,
105
+ static_cast<scalar_t>(force_zero_thres), N);
106
+ } else {
107
+ process_with_mask_kernel<scalar_t, in_t, false>
108
+ <<<gridDim, blockDim, 0, stream>>>(y_res, y_q, y_hat, s_hat, y, scales, means, mask,
109
+ static_cast<scalar_t>(force_zero_thres), N);
110
+ }
111
+ };
112
+
113
+ if (useVec) {
114
+ launch_kernel(vec_t{});
115
+ } else {
116
+ launch_kernel(scalar_t{});
117
+ }
118
+ }
119
+
120
+ std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor>
121
+ process_with_mask_cuda(const torch::Tensor& y, const torch::Tensor& scales, const torch::Tensor& means,
122
+ const torch::Tensor& mask, const float force_zero_thres)
123
+ {
124
+ auto y_res = torch::empty_like(y);
125
+ auto y_q = torch::empty_like(y);
126
+ auto y_hat = torch::empty_like(y);
127
+ auto s_hat = torch::empty_like(y);
128
+
129
+ if (y.dtype() == torch::kFloat32) {
130
+ process_with_mask_dispatcher<float, float4>(y_res, y_q, y_hat, s_hat, y, scales, means,
131
+ mask, force_zero_thres);
132
+ } else if (y.dtype() == torch::kFloat16) {
133
+ process_with_mask_dispatcher<c10::Half, Half4>(y_res, y_q, y_hat, s_hat, y, scales, means,
134
+ mask, force_zero_thres);
135
+ }
136
+
137
+ return { y_res, y_q, y_hat, s_hat };
138
+ }
139
+
140
+ template <typename T>
141
+ __global__ void combine_for_reading_2x_kernel(GPUTensor1D<T> out, const GPUTensor1D<T> x,
142
+ const GPUTensor1D<T> mask, const int N)
143
+ {
144
+ const int chw1 = blockIdx.x * blockDim.x + threadIdx.x;
145
+ const int chw2 = chw1 + N;
146
+
147
+ if (chw1 < N) {
148
+ T _s1 = x[chw1];
149
+ T _s2 = x[chw2];
150
+ T _m1 = mask[chw1];
151
+ T _m2 = mask[chw2];
152
+
153
+ _s1 = _s1 * _m1;
154
+ _s2 = _s2 * _m2;
155
+ out[chw1] = _s1 + _s2;
156
+ }
157
+ }
158
+
159
+ template <typename scalar_t, typename vec_t>
160
+ __forceinline__ void combine_for_reading_2x_dispatcher(torch::Tensor& out, const torch::Tensor& x,
161
+ const torch::Tensor& mask)
162
+ {
163
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(x, 2);
164
+ if (useVec) {
165
+ combine_for_reading_2x_kernel<vec_t><<<gridDim, blockDim, 0, stream>>>(out, x, mask, N);
166
+ } else {
167
+ combine_for_reading_2x_kernel<scalar_t><<<gridDim, blockDim, 0, stream>>>(out, x, mask, N);
168
+ }
169
+ }
170
+
171
+ void combine_for_reading_2x_cuda(torch::Tensor& out, const torch::Tensor& x, const torch::Tensor& mask)
172
+ {
173
+ if (x.dtype() == torch::kFloat32) {
174
+ combine_for_reading_2x_dispatcher<float, float4>(out, x, mask);
175
+ } else if (x.dtype() == torch::kFloat16) {
176
+ combine_for_reading_2x_dispatcher<c10::Half, Half4>(out, x, mask);
177
+ }
178
+ }
179
+
180
+ template <typename T>
181
+ __global__ void restore_y_2x_kernel(GPUTensor1D<T> out, const GPUTensor1D<T> y,
182
+ const GPUTensor1D<T> means, const GPUTensor1D<T> mask, const int N)
183
+ {
184
+ const int chw1 = blockIdx.x * blockDim.x + threadIdx.x;
185
+ const int chw2 = chw1 + N;
186
+
187
+ if (chw1 < N) {
188
+ T _y = y[chw1];
189
+ T _means1 = means[chw1];
190
+ T _means2 = means[chw2];
191
+ T _mask1 = mask[chw1];
192
+ T _mask2 = mask[chw2];
193
+
194
+ _means1 = (_y + _means1) * _mask1;
195
+ _means2 = (_y + _means2) * _mask2;
196
+ out[chw1] = _means1;
197
+ out[chw2] = _means2;
198
+ }
199
+ }
200
+
201
+ template <typename scalar_t, typename vec_t>
202
+ __forceinline__ void restore_y_2x_dispatcher(torch::Tensor& out, const torch::Tensor& y,
203
+ const torch::Tensor& means, const torch::Tensor& mask)
204
+ {
205
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(y);
206
+ if (useVec) {
207
+ restore_y_2x_kernel<vec_t><<<gridDim, blockDim, 0, stream>>>(out, y, means, mask, N);
208
+ } else {
209
+ restore_y_2x_kernel<scalar_t><<<gridDim, blockDim, 0, stream>>>(out, y, means, mask, N);
210
+ }
211
+ }
212
+
213
+ void restore_y_2x_cuda(torch::Tensor& out, const torch::Tensor& y, const torch::Tensor& means,
214
+ const torch::Tensor& mask)
215
+ {
216
+ if (y.dtype() == torch::kFloat32) {
217
+ restore_y_2x_dispatcher<float, float4>(out, y, means, mask);
218
+ } else if (y.dtype() == torch::kFloat16) {
219
+ restore_y_2x_dispatcher<c10::Half, Half4>(out, y, means, mask);
220
+ }
221
+ }
222
+
223
+ template <typename T>
224
+ __global__ void restore_y_4x_kernel(GPUTensor1D<T> out, const GPUTensor1D<T> y,
225
+ const GPUTensor1D<T> means, const GPUTensor1D<T> mask, const int N)
226
+ {
227
+ const int chw1 = blockIdx.x * blockDim.x + threadIdx.x;
228
+ const int chw2 = chw1 + N;
229
+ const int chw3 = chw2 + N;
230
+ const int chw4 = chw3 + N;
231
+
232
+ if (chw1 < N) {
233
+ T _y = y[chw1];
234
+ T _means1 = means[chw1];
235
+ T _means2 = means[chw2];
236
+ T _means3 = means[chw3];
237
+ T _means4 = means[chw4];
238
+ T _mask1 = mask[chw1];
239
+ T _mask2 = mask[chw2];
240
+ T _mask3 = mask[chw3];
241
+ T _mask4 = mask[chw4];
242
+
243
+ _means1 = (_y + _means1) * _mask1;
244
+ _means2 = (_y + _means2) * _mask2;
245
+ _means3 = (_y + _means3) * _mask3;
246
+ _means4 = (_y + _means4) * _mask4;
247
+ out[chw1] = _means1;
248
+ out[chw2] = _means2;
249
+ out[chw3] = _means3;
250
+ out[chw4] = _means4;
251
+ }
252
+ }
253
+
254
+ template <typename scalar_t, typename vec_t>
255
+ __forceinline__ void restore_y_4x_dispatcher(torch::Tensor& out, const torch::Tensor& y,
256
+ const torch::Tensor& means, const torch::Tensor& mask)
257
+ {
258
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(y);
259
+ if (useVec) {
260
+ restore_y_4x_kernel<vec_t><<<gridDim, blockDim, 0, stream>>>(out, y, means, mask, N);
261
+ } else {
262
+ restore_y_4x_kernel<scalar_t><<<gridDim, blockDim, 0, stream>>>(out, y, means, mask, N);
263
+ }
264
+ }
265
+
266
+ void restore_y_4x_cuda(torch::Tensor& out, const torch::Tensor& y, const torch::Tensor& means,
267
+ const torch::Tensor& mask)
268
+ {
269
+ if (y.dtype() == torch::kFloat32) {
270
+ restore_y_4x_dispatcher<float, float4>(out, y, means, mask);
271
+ } else if (y.dtype() == torch::kFloat16) {
272
+ restore_y_4x_dispatcher<c10::Half, Half4>(out, y, means, mask);
273
+ }
274
+ }
275
+
276
+ template <typename T, typename scalar_t>
277
+ __forceinline__ __device__ T scale_to_index(T scale, const scalar_t scale_min,
278
+ const scalar_t scale_max, const scalar_t log_scale_min,
279
+ const scalar_t log_step_recip)
280
+ {
281
+ scale = max(scale, scale_min);
282
+ scale = min(scale, scale_max);
283
+ scale = log(scale) - log_scale_min;
284
+ scale = scale * log_step_recip;
285
+ return scale;
286
+ }
287
+
288
+ template <typename scalar_t, typename in_t, typename out_t, typename cond_out_t, bool with_cond = false>
289
+ __global__ void build_index_dec_kernel(GPUTensor1D<out_t> out, GPUTensor1D<cond_out_t> cond_out,
290
+ const GPUTensor1D<in_t> scales, const scalar_t scale_min,
291
+ const scalar_t scale_max, const scalar_t log_scale_min,
292
+ const scalar_t log_step_recip, const scalar_t skip_thres,
293
+ const int N)
294
+ {
295
+ const int n = blockIdx.x * blockDim.x + threadIdx.x;
296
+
297
+ if (n < N) {
298
+ in_t _scale = scales[n];
299
+ in_t _index = scale_to_index(_scale, scale_min, scale_max, log_scale_min, log_step_recip);
300
+ out[n] = to_uint8(_index);
301
+ if constexpr (with_cond) {
302
+ cond_out_t _cond = _scale > skip_thres;
303
+ cond_out[n] = _cond;
304
+ }
305
+ }
306
+ }
307
+
308
+ template <typename scalar_t, typename vec_t>
309
+ __forceinline__ void
310
+ build_index_dec_dispatcher(torch::Tensor& out, torch::optional<torch::Tensor>& cond_out,
311
+ const torch::Tensor& scales, const scalar_t scale_min,
312
+ const scalar_t scale_max, const scalar_t log_scale_min,
313
+ const scalar_t log_step_recip, const scalar_t skip_thres)
314
+ {
315
+ auto [blockDim, gridDim, stream, useVec, N] = get_kernel_launch_info_flatten<vec_t>(scales);
316
+ const bool with_cond = static_cast<float>(skip_thres) > 0.f;
317
+
318
+ auto launch_kernel = [&](auto in_v, auto out_v, auto cond_out_v) {
319
+ using in_t = decltype(in_v);
320
+ using out_t = decltype(out_v);
321
+ using cond_out_t = decltype(cond_out_v);
322
+ if (with_cond) {
323
+ build_index_dec_kernel<scalar_t, in_t, out_t, cond_out_t, true>
324
+ <<<gridDim, blockDim, 0, stream>>>(out, cond_out.value(), scales, scale_min, scale_max,
325
+ log_scale_min, log_step_recip, skip_thres, N);
326
+ } else {
327
+ build_index_dec_kernel<scalar_t, in_t, out_t, cond_out_t, false>
328
+ <<<gridDim, blockDim, 0, stream>>>(out, nullptr, scales, scale_min, scale_max,
329
+ log_scale_min, log_step_recip, skip_thres, N);
330
+ }
331
+ };
332
+
333
+ if (useVec) {
334
+ launch_kernel(vec_t{}, uchar4{}, bool4{});
335
+ } else {
336
+ launch_kernel(scalar_t{}, uint8_t{}, bool{});
337
+ }
338
+ }
339
+
340
+ void build_index_dec_cuda(torch::Tensor& out, torch::optional<torch::Tensor>& cond_out,
341
+ const torch::Tensor& scales, const float scale_min, const float scale_max,
342
+ const float log_scale_min, const float log_step_recip, const float skip_thres)
343
+ {
344
+ if (scales.dtype() == torch::kFloat32) {
345
+ build_index_dec_dispatcher<float, float4>(out, cond_out, scales, scale_min, scale_max,
346
+ log_scale_min, log_step_recip, skip_thres);
347
+ } else if (scales.dtype() == torch::kFloat16) {
348
+ build_index_dec_dispatcher<c10::Half, Half4>(
349
+ out, cond_out, scales, static_cast<c10::Half>(scale_min),
350
+ static_cast<c10::Half>(scale_max), static_cast<c10::Half>(log_scale_min),
351
+ static_cast<c10::Half>(log_step_recip), static_cast<c10::Half>(skip_thres));
352
+ }
353
+ }
354
+
355
+ template <typename scalar_t, typename in_t, typename out_t, typename cond_out_t, bool with_cond = false>
356
+ __global__ void build_index_enc_kernel(GPUTensor1D<out_t> out, GPUTensor1D<cond_out_t> cond_out,
357
+ const GPUTensor1D<in_t> symbols, const GPUTensor1D<in_t> scales,
358
+ const scalar_t scale_min, const scalar_t scale_max,
359
+ const scalar_t log_scale_min, const scalar_t log_step_recip,
360
+ const scalar_t skip_thres, const int N)
361
+ {
362
+ const int n = blockIdx.x * blockDim.x + threadIdx.x;
363
+
364
+ if (n < N) {
365
+ in_t _scale = scales[n];
366
+ in_t _symbol = symbols[n];
367
+ in_t _index = scale_to_index(_scale, scale_min, scale_max, log_scale_min, log_step_recip);
368
+
369
+ out[n] = (to_int16(_symbol) << 8) + to_int16(_index);
370
+ if constexpr (with_cond) {
371
+ cond_out_t _cond = _scale > skip_thres;
372
+ cond_out[n] = _cond;
373
+ }
374
+ }
375
+ }
376
+
377
+ template <typename scalar_t, typename vec_t>
378
+ __forceinline__ void build_index_enc_dispatcher(
379
+ torch::Tensor& out, torch::optional<torch::Tensor>& cond_out, const torch::Tensor& symbols,
380
+ const torch::Tensor& scales, const scalar_t scale_min, const scalar_t scale_max,
381
+ const scalar_t log_scale_min, const scalar_t log_step_recip, const scalar_t skip_thres)
382
+ {
383
+ auto [blockDim, gridDim, stream, useVec, N] = get_kernel_launch_info_flatten<vec_t>(scales);
384
+ const bool with_cond = static_cast<float>(skip_thres) > 0.f;
385
+
386
+ auto launch_kernel = [&](auto in_v, auto out_v, auto cond_out_v) {
387
+ using in_t = decltype(in_v);
388
+ using out_t = decltype(out_v);
389
+ using cond_out_t = decltype(cond_out_v);
390
+ if (with_cond) {
391
+ build_index_enc_kernel<scalar_t, in_t, out_t, cond_out_t, true>
392
+ <<<gridDim, blockDim, 0, stream>>>(out, cond_out.value(), symbols, scales,
393
+ scale_min, scale_max, log_scale_min,
394
+ log_step_recip, skip_thres, N);
395
+ } else {
396
+ build_index_enc_kernel<scalar_t, in_t, out_t, cond_out_t, false>
397
+ <<<gridDim, blockDim, 0, stream>>>(out, nullptr, symbols, scales, scale_min, scale_max,
398
+ log_scale_min, log_step_recip, skip_thres, N);
399
+ }
400
+ };
401
+
402
+ if (useVec) {
403
+ launch_kernel(vec_t{}, short4{}, bool4{});
404
+ } else {
405
+ launch_kernel(scalar_t{}, int16_t{}, bool{});
406
+ }
407
+ }
408
+
409
+ void build_index_enc_cuda(torch::Tensor& out, torch::optional<torch::Tensor>& cond_out,
410
+ const torch::Tensor& symbols, const torch::Tensor& scales,
411
+ const float scale_min, const float scale_max, const float log_scale_min,
412
+ const float log_step_recip, const float skip_thres)
413
+ {
414
+ if (scales.dtype() == torch::kFloat32) {
415
+ build_index_enc_dispatcher<float, float4>(out, cond_out, symbols, scales, scale_min, scale_max,
416
+ log_scale_min, log_step_recip, skip_thres);
417
+ } else if (scales.dtype() == torch::kFloat16) {
418
+ build_index_enc_dispatcher<c10::Half, Half4>(
419
+ out, cond_out, symbols, scales, static_cast<c10::Half>(scale_min),
420
+ static_cast<c10::Half>(scale_max), static_cast<c10::Half>(log_scale_min),
421
+ static_cast<c10::Half>(log_step_recip), static_cast<c10::Half>(skip_thres));
422
+ }
423
+ }
424
+
425
+ template <typename vec_t, typename scalar_t, bool biasSafe = false>
426
+ __forceinline__ __device__ vec_t get_bias(const GPUTensor1D<scalar_t> bias, const int HW, const int chw)
427
+ {
428
+ vec_t _bias;
429
+ if constexpr (sizeof(vec_t) / sizeof(scalar_t) == 4) {
430
+ if constexpr (biasSafe) {
431
+ scalar_t b = bias[(chw * 4 + 0) / HW];
432
+ _bias = make_vec4(b, b, b, b);
433
+ } else {
434
+ _bias = make_vec4(bias[(chw * 4 + 0) / HW], bias[(chw * 4 + 1) / HW],
435
+ bias[(chw * 4 + 2) / HW], bias[(chw * 4 + 3) / HW]);
436
+ }
437
+ } else {
438
+ _bias = bias[(chw) / HW];
439
+ }
440
+ return _bias;
441
+ }
442
+
443
+ template <typename scalar_t, typename vec_t, bool biasSafe>
444
+ __global__ void bias_wsilu_kernel(GPUTensor1D<vec_t> x, const GPUTensor1D<scalar_t> bias,
445
+ const int N, const int HW)
446
+ {
447
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
448
+
449
+ if (chw < N) {
450
+ vec_t _bias = get_bias<vec_t, scalar_t, biasSafe>(bias, HW, chw);
451
+ vec_t _x = x[chw];
452
+ _x = _x + _bias;
453
+ _x = wsilu(_x);
454
+ x[chw] = _x;
455
+ }
456
+ }
457
+
458
+ template <typename scalar_t, typename vec_t>
459
+ __forceinline__ void bias_wsilu_dispatcher(torch::Tensor& x, const torch::Tensor& bias)
460
+ {
461
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(x);
462
+ if (useVec) {
463
+ if (biasSafe) {
464
+ bias_wsilu_kernel<scalar_t, vec_t, true><<<gridDim, blockDim, 0, stream>>>(x, bias, N, HW);
465
+ } else {
466
+ bias_wsilu_kernel<scalar_t, vec_t, false><<<gridDim, blockDim, 0, stream>>>(x, bias, N, HW);
467
+ }
468
+ } else {
469
+ bias_wsilu_kernel<scalar_t, scalar_t, true><<<gridDim, blockDim, 0, stream>>>(x, bias, N, HW);
470
+ }
471
+ }
472
+
473
+ void bias_wsilu_cuda(torch::Tensor& x, const torch::Tensor& bias)
474
+ {
475
+ if (x.dtype() == torch::kFloat32) {
476
+ bias_wsilu_dispatcher<float, float4>(x, bias);
477
+ } else if (x.dtype() == torch::kFloat16) {
478
+ bias_wsilu_dispatcher<c10::Half, Half4>(x, bias);
479
+ }
480
+ }
481
+
482
+ template <typename scalar_t, typename vec_t, bool biasSafe, bool with_shortcut = true, bool with_quant = false>
483
+ __global__ void bias_shortcut_kernel(GPUTensor1D<vec_t> x, const GPUTensor1D<scalar_t> bias,
484
+ const GPUTensor1D<scalar_t> quant_step,
485
+ const GPUTensor1D<vec_t> shortcut, const int N, const int HW)
486
+ {
487
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
488
+
489
+ if (chw < N) {
490
+ vec_t _x = x[chw];
491
+ vec_t _bias = get_bias<vec_t, scalar_t, biasSafe>(bias, HW, chw);
492
+ _x = _x + _bias;
493
+ if constexpr (with_shortcut) {
494
+ vec_t _s = shortcut[chw];
495
+ _x = _x + _s;
496
+ }
497
+ if constexpr (with_quant) {
498
+ vec_t _q = get_bias<vec_t, scalar_t, biasSafe>(quant_step, HW, chw);
499
+ _x = _x * _q;
500
+ }
501
+ x[chw] = _x;
502
+ }
503
+ }
504
+
505
+ template <typename scalar_t, typename vec_t, bool with_shortcut = true, bool with_quant = false>
506
+ __forceinline__ void bias_shortcut_dispatcher(torch::Tensor& x, const torch::Tensor& bias,
507
+ const torch::Tensor& quant_step,
508
+ const torch::Tensor& shortcut)
509
+ {
510
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(x);
511
+ if (useVec) {
512
+ if (biasSafe) {
513
+ bias_shortcut_kernel<scalar_t, vec_t, true, with_shortcut, with_quant>
514
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, quant_step, shortcut, N, HW);
515
+ } else {
516
+ bias_shortcut_kernel<scalar_t, vec_t, false, with_shortcut, with_quant>
517
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, quant_step, shortcut, N, HW);
518
+ }
519
+ } else {
520
+ bias_shortcut_kernel<scalar_t, scalar_t, true, with_shortcut, with_quant>
521
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, quant_step, shortcut, N, HW);
522
+ }
523
+ }
524
+
525
+ void bias_shortcut_cuda(torch::Tensor& x, const torch::Tensor& bias, const torch::Tensor& shortcut)
526
+ {
527
+ if (x.dtype() == torch::kFloat32) {
528
+ bias_shortcut_dispatcher<float, float4, true, false>(x, bias, bias, shortcut);
529
+ } else if (x.dtype() == torch::kFloat16) {
530
+ bias_shortcut_dispatcher<c10::Half, Half4, true, false>(x, bias, bias, shortcut);
531
+ }
532
+ }
533
+
534
+ void bias_quant_cuda(torch::Tensor& x, const torch::Tensor& bias, const torch::Tensor& quant_step)
535
+ {
536
+ if (x.dtype() == torch::kFloat32) {
537
+ bias_shortcut_dispatcher<float, float4, false, true>(x, bias, quant_step, bias);
538
+ } else if (x.dtype() == torch::kFloat16) {
539
+ bias_shortcut_dispatcher<c10::Half, Half4, false, true>(x, bias, quant_step, bias);
540
+ }
541
+ }
542
+
543
+ template <typename scalar_t, typename vec_t, bool biasSafe>
544
+ __global__ void bias_shortcut_no_inplace_kernel(GPUTensor1D<vec_t> out, const GPUTensor1D<vec_t> x,
545
+ const GPUTensor1D<scalar_t> bias,
546
+ const GPUTensor1D<vec_t> shortcut, const int N,
547
+ const int HW)
548
+ {
549
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
550
+
551
+ if (chw < N) {
552
+ vec_t _x = x[chw];
553
+ vec_t _bias = get_bias<vec_t, scalar_t, biasSafe>(bias, HW, chw);
554
+ _x = _x + _bias;
555
+ vec_t _s = shortcut[chw];
556
+ _x = _x + _s;
557
+ out[chw] = _x;
558
+ }
559
+ }
560
+
561
+ template <typename scalar_t, typename vec_t>
562
+ __forceinline__ void bias_shortcut_no_inplace_dispatcher(torch::Tensor& out, const torch::Tensor& x,
563
+ const torch::Tensor& bias,
564
+ const torch::Tensor& shortcut)
565
+ {
566
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(x);
567
+ if (useVec) {
568
+ if (biasSafe) {
569
+ bias_shortcut_no_inplace_kernel<scalar_t, vec_t, true>
570
+ <<<gridDim, blockDim, 0, stream>>>(out, x, bias, shortcut, N, HW);
571
+ } else {
572
+ bias_shortcut_no_inplace_kernel<scalar_t, vec_t, false>
573
+ <<<gridDim, blockDim, 0, stream>>>(out, x, bias, shortcut, N, HW);
574
+ }
575
+ } else {
576
+ bias_shortcut_no_inplace_kernel<scalar_t, scalar_t, true>
577
+ <<<gridDim, blockDim, 0, stream>>>(out, x, bias, shortcut, N, HW);
578
+ }
579
+ }
580
+ void bias_shortcut_no_inplace_cuda(torch::Tensor& out, const torch::Tensor& x,
581
+ const torch::Tensor& bias, const torch::Tensor& shortcut)
582
+ {
583
+ if (x.dtype() == torch::kFloat32) {
584
+ bias_shortcut_no_inplace_dispatcher<float, float4>(out, x, bias, shortcut);
585
+ } else if (x.dtype() == torch::kFloat16) {
586
+ bias_shortcut_no_inplace_dispatcher<c10::Half, Half4>(out, x, bias, shortcut);
587
+ }
588
+ }
589
+
590
+ template <typename scalar_t, typename vec_t, bool biasSafe>
591
+ __global__ void bias_shortcut_2_kernel(GPUTensor1D<vec_t> x, const GPUTensor1D<scalar_t> bias,
592
+ GPUTensor1D<vec_t> shortcut, const int N, const int HW)
593
+ {
594
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
595
+
596
+ if (chw < N) {
597
+ vec_t _x = x[chw];
598
+ vec_t _bias = get_bias<vec_t, scalar_t, biasSafe>(bias, HW, chw);
599
+ _x = _x + _bias;
600
+ vec_t _s = shortcut[chw];
601
+ _x = _x + _s;
602
+ x[chw] = _x;
603
+ shortcut[chw] = _x + _s;
604
+ }
605
+ }
606
+
607
+ template <typename scalar_t, typename vec_t>
608
+ __forceinline__ void bias_shortcut_2_dispatcher(torch::Tensor& x, const torch::Tensor& bias,
609
+ torch::Tensor& shortcut)
610
+ {
611
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(x);
612
+ if (useVec) {
613
+ if (biasSafe) {
614
+ bias_shortcut_2_kernel<scalar_t, vec_t, true>
615
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, shortcut, N, HW);
616
+ } else {
617
+ bias_shortcut_2_kernel<scalar_t, vec_t, false>
618
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, shortcut, N, HW);
619
+ }
620
+ } else {
621
+ bias_shortcut_2_kernel<scalar_t, scalar_t, true>
622
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, shortcut, N, HW);
623
+ }
624
+ }
625
+
626
+ void bias_shortcut_2_cuda(torch::Tensor& x, const torch::Tensor& bias, torch::Tensor& shortcut)
627
+ {
628
+ if (x.dtype() == torch::kFloat32) {
629
+ bias_shortcut_2_dispatcher<float, float4>(x, bias, shortcut);
630
+ } else if (x.dtype() == torch::kFloat16) {
631
+ bias_shortcut_2_dispatcher<c10::Half, Half4>(x, bias, shortcut);
632
+ }
633
+ }
634
+
635
+ void bias_shortcut_with_quant_step_cuda(torch::Tensor& x, const torch::Tensor& bias,
636
+ const torch::Tensor& quant_step, const torch::Tensor& shortcut)
637
+ {
638
+ if (x.dtype() == torch::kFloat32) {
639
+ bias_shortcut_dispatcher<float, float4, true, true>(x, bias, quant_step, shortcut);
640
+ } else if (x.dtype() == torch::kFloat16) {
641
+ bias_shortcut_dispatcher<c10::Half, Half4, true, true>(x, bias, quant_step, shortcut);
642
+ }
643
+ }
644
+
645
+ template <typename scalar_t, typename vec_t, bool biasSafe>
646
+ __global__ void bias_wsilu_chunk_add_kernel(GPUTensor1D<vec_t> x, const GPUTensor1D<scalar_t> bias,
647
+ const int N, const int HW)
648
+ {
649
+ const int chw1 = blockIdx.x * blockDim.x + threadIdx.x;
650
+ const int chw2 = chw1 + N;
651
+
652
+ if (chw1 < N) {
653
+ vec_t _x1 = x[chw1];
654
+ vec_t _bias1 = get_bias<vec_t, scalar_t, biasSafe>(bias, HW, chw1);
655
+ _x1 = _x1 + _bias1;
656
+ _x1 = wsilu(_x1);
657
+
658
+ vec_t _x2 = x[chw2];
659
+ vec_t _bias2 = get_bias<vec_t, scalar_t, biasSafe>(bias, HW, chw2);
660
+ _x2 = _x2 + _bias2;
661
+ _x2 = wsilu(_x2);
662
+
663
+ x[chw1] = _x1 + _x2;
664
+ }
665
+ }
666
+
667
+ template <typename scalar_t, typename vec_t>
668
+ __forceinline__ void bias_wsilu_chunk_add_dispatcher(torch::Tensor& x, const torch::Tensor& bias)
669
+ {
670
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(x, 2);
671
+ if (useVec) {
672
+ if (biasSafe) {
673
+ bias_wsilu_chunk_add_kernel<scalar_t, vec_t, true>
674
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, N, HW);
675
+ } else {
676
+ bias_wsilu_chunk_add_kernel<scalar_t, vec_t, false>
677
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, N, HW);
678
+ }
679
+ } else {
680
+ bias_wsilu_chunk_add_kernel<scalar_t, scalar_t, true>
681
+ <<<gridDim, blockDim, 0, stream>>>(x, bias, N, HW);
682
+ }
683
+ }
684
+
685
+ void bias_wsilu_chunk_add_cuda(torch::Tensor& x, const torch::Tensor& bias)
686
+ {
687
+ if (x.dtype() == torch::kFloat32) {
688
+ bias_wsilu_chunk_add_dispatcher<float, float4>(x, bias);
689
+ } else if (x.dtype() == torch::kFloat16) {
690
+ bias_wsilu_chunk_add_dispatcher<c10::Half, Half4>(x, bias);
691
+ }
692
+ const torch::IntArrayRef x_shape = x.sizes();
693
+ x = x.narrow(1, 0, x_shape[1] / 2);
694
+ }
695
+
696
+ template <typename scalar_t>
697
+ __global__ void bias_pixel_shuffle_2_kernel(Packed4DTensorAccessor32<scalar_t> out,
698
+ const Packed4DTensorAccessor32<scalar_t> x,
699
+ const Packed1DTensorAccessor32<scalar_t> bias,
700
+ const int N, const int W)
701
+ {
702
+ const int c = blockIdx.y * 4;
703
+ const int c1 = c / 4;
704
+ const int hw = blockIdx.x * blockDim.x + threadIdx.x;
705
+ const int h = (hw / W) * 2;
706
+ const int w = (hw % W) * 2;
707
+
708
+ __shared__ scalar_t _bias[4];
709
+ if (threadIdx.x < 4) {
710
+ _bias[threadIdx.x] = bias[c + threadIdx.x];
711
+ }
712
+ __syncthreads();
713
+
714
+ if (hw < N) {
715
+ scalar_t _bias_0 = _bias[0];
716
+ scalar_t _bias_1 = _bias[1];
717
+ scalar_t _bias_2 = _bias[2];
718
+ scalar_t _bias_3 = _bias[3];
719
+
720
+ scalar_t _x0 = x[0][c + 0][0][hw];
721
+ scalar_t _x1 = x[0][c + 1][0][hw];
722
+ scalar_t _x2 = x[0][c + 2][0][hw];
723
+ scalar_t _x3 = x[0][c + 3][0][hw];
724
+
725
+ _x0 = _x0 + _bias_0;
726
+ _x1 = _x1 + _bias_1;
727
+ _x2 = _x2 + _bias_2;
728
+ _x3 = _x3 + _bias_3;
729
+
730
+ out[0][c1][h + 0][w + 0] = _x0;
731
+ out[0][c1][h + 0][w + 1] = _x1;
732
+ out[0][c1][h + 1][w + 0] = _x2;
733
+ out[0][c1][h + 1][w + 1] = _x3;
734
+ }
735
+ }
736
+
737
+ template <typename scalar_t>
738
+ __forceinline__ void bias_pixel_shuffle_2_dispatcher(torch::Tensor& out, const torch::Tensor& x,
739
+ const torch::Tensor& bias, const int C,
740
+ const int N, const int W)
741
+ {
742
+ const int BLOCK_SIZE = 128;
743
+ const dim3 gridDim((N + BLOCK_SIZE - 1) / BLOCK_SIZE, C / 4);
744
+ const dim3 blockDim(BLOCK_SIZE);
745
+ auto stream = at::cuda::getCurrentCUDAStream();
746
+ bias_pixel_shuffle_2_kernel<scalar_t><<<gridDim, blockDim, 0, stream>>>(
747
+ out.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(),
748
+ x.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(),
749
+ bias.packed_accessor32<scalar_t, 1, torch::RestrictPtrTraits>(), N, W);
750
+ }
751
+
752
+ void bias_pixel_shuffle_2_cuda(torch::Tensor& out, const torch::Tensor& x,
753
+ const torch::Tensor& bias, const int C, const int N, const int W)
754
+ {
755
+ if (x.dtype() == torch::kFloat32) {
756
+ bias_pixel_shuffle_2_dispatcher<float>(out, x, bias, C, N, W);
757
+ } else if (x.dtype() == torch::kFloat16) {
758
+ bias_pixel_shuffle_2_dispatcher<at::Half>(out, x, bias, C, N, W);
759
+ }
760
+ }
761
+
762
+ template <typename scalar_t, bool clamp = false>
763
+ __global__ void bias_pixel_shuffle_8_kernel(Packed4DTensorAccessor32<scalar_t> out,
764
+ const Packed4DTensorAccessor32<scalar_t> x,
765
+ const Packed1DTensorAccessor32<scalar_t> bias,
766
+ const int N, const int W)
767
+ {
768
+ const int c = blockIdx.y * 64;
769
+ const int c1 = c / 64;
770
+ const int hw = blockIdx.x * blockDim.x + threadIdx.x;
771
+ const int h = (hw / W) * 8;
772
+ const int w = (hw % W) * 8;
773
+
774
+ __shared__ scalar_t _bias[64];
775
+ if (threadIdx.x < 64) {
776
+ _bias[threadIdx.x] = bias[c + threadIdx.x];
777
+ }
778
+ __syncthreads();
779
+
780
+ if (hw < N) {
781
+ for (int i = 0; i < 64; i++) {
782
+ scalar_t _x = x[0][c + i][0][hw];
783
+ _x = _x + _bias[i];
784
+ const int out_y_offset = i >> 3;
785
+ const int out_x_offset = i & 7;
786
+ if constexpr (clamp) {
787
+ _x = max(_x, static_cast<scalar_t>(0.f));
788
+ _x = min(_x, static_cast<scalar_t>(1.f));
789
+ }
790
+ out[0][c1][h + out_y_offset][w + out_x_offset] = _x;
791
+ }
792
+ }
793
+ }
794
+
795
+ template <typename scalar_t>
796
+ __forceinline__ void bias_pixel_shuffle_8_dispatcher(torch::Tensor& out, const torch::Tensor& x,
797
+ const torch::Tensor& bias, const int C,
798
+ const int N, const int W, bool clamp)
799
+ {
800
+ const int BLOCK_SIZE = 128;
801
+ const dim3 gridDim((N + BLOCK_SIZE - 1) / BLOCK_SIZE, C / 64);
802
+ const dim3 blockDim(BLOCK_SIZE);
803
+ auto stream = at::cuda::getCurrentCUDAStream();
804
+ if (clamp) {
805
+ bias_pixel_shuffle_8_kernel<scalar_t, true><<<gridDim, blockDim, 0, stream>>>(
806
+ out.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(),
807
+ x.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(),
808
+ bias.packed_accessor32<scalar_t, 1, torch::RestrictPtrTraits>(), N, W);
809
+ } else {
810
+ bias_pixel_shuffle_8_kernel<scalar_t, false><<<gridDim, blockDim, 0, stream>>>(
811
+ out.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(),
812
+ x.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(),
813
+ bias.packed_accessor32<scalar_t, 1, torch::RestrictPtrTraits>(), N, W);
814
+ }
815
+ }
816
+
817
+ void bias_pixel_shuffle_8_cuda(torch::Tensor& out, const torch::Tensor& x, const torch::Tensor& bias,
818
+ const int C, const int N, const int W, bool clamp)
819
+ {
820
+ if (x.dtype() == torch::kFloat32) {
821
+ bias_pixel_shuffle_8_dispatcher<float>(out, x, bias, C, N, W, clamp);
822
+ } else if (x.dtype() == torch::kFloat16) {
823
+ bias_pixel_shuffle_8_dispatcher<at::Half>(out, x, bias, C, N, W, clamp);
824
+ }
825
+ }
826
+
827
+ template <typename scalar_t, typename vec_t1, typename vec_t2>
828
+ __global__ void round_and_to_int8_kernel(GPUTensor1D<vec_t1> z, GPUTensor1D<vec_t2> z_int8, const int N)
829
+ {
830
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
831
+
832
+ if (chw < N) {
833
+ vec_t1 _z = z[chw];
834
+ _z = round(_z);
835
+ _z = max(_z, static_cast<scalar_t>(-128.f));
836
+ _z = min(_z, static_cast<scalar_t>(127.f));
837
+ z[chw] = _z;
838
+ vec_t2 _z_int8 = to_int8(_z);
839
+ z_int8[chw] = _z_int8;
840
+ }
841
+ }
842
+
843
+ template <typename scalar_t, typename vec_t>
844
+ __forceinline__ void round_and_to_int8_dispatcher(torch::Tensor& z, torch::Tensor& z_int8)
845
+ {
846
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(z);
847
+ if (useVec) {
848
+ round_and_to_int8_kernel<scalar_t, vec_t, char4>
849
+ <<<gridDim, blockDim, 0, stream>>>(z, z_int8, N);
850
+ } else {
851
+ round_and_to_int8_kernel<scalar_t, scalar_t, int8_t>
852
+ <<<gridDim, blockDim, 0, stream>>>(z, z_int8, N);
853
+ }
854
+ }
855
+
856
+ torch::Tensor round_and_to_int8_cuda(torch::Tensor& z)
857
+ {
858
+ auto z_int8 = torch::empty_like(z, at::TensorOptions().dtype(torch::kInt8));
859
+ if (z.dtype() == torch::kFloat32) {
860
+ round_and_to_int8_dispatcher<float, float4>(z, z_int8);
861
+ } else if (z.dtype() == torch::kFloat16) {
862
+ round_and_to_int8_dispatcher<c10::Half, Half4>(z, z_int8);
863
+ }
864
+ return z_int8;
865
+ }
866
+
867
+ template <typename scalar_t, typename vec_t>
868
+ __global__ void clamp_reciprocal_with_quant_kernel(GPUTensor1D<vec_t> q_dec_clamp,
869
+ const GPUTensor1D<vec_t> q_dec, GPUTensor1D<vec_t> y,
870
+ const scalar_t min_val, const int N)
871
+ {
872
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
873
+
874
+ if (chw < N) {
875
+ vec_t _q_dec = q_dec[chw];
876
+ vec_t _y = y[chw];
877
+ _q_dec = max(_q_dec, min_val);
878
+ q_dec_clamp[chw] = _q_dec;
879
+ vec_t _q_enc = reciprocal(_q_dec);
880
+ _y = _y * _q_enc;
881
+ y[chw] = _y;
882
+ }
883
+ }
884
+
885
+ template <typename scalar_t, typename vec_t>
886
+ __forceinline__ void clamp_reciprocal_with_quant_dispatcher(torch::Tensor& q_dec_clamp,
887
+ const torch::Tensor& q_dec,
888
+ torch::Tensor& y, const float min_val)
889
+ {
890
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(q_dec);
891
+ if (useVec) {
892
+ clamp_reciprocal_with_quant_kernel<scalar_t, vec_t><<<gridDim, blockDim, 0, stream>>>(
893
+ q_dec_clamp, q_dec, y, static_cast<scalar_t>(min_val), N);
894
+ } else {
895
+ clamp_reciprocal_with_quant_kernel<scalar_t, scalar_t><<<gridDim, blockDim, 0, stream>>>(
896
+ q_dec_clamp, q_dec, y, static_cast<scalar_t>(min_val), N);
897
+ }
898
+ }
899
+
900
+ torch::Tensor clamp_reciprocal_with_quant_cuda(const torch::Tensor& q_dec, torch::Tensor& y,
901
+ const float min_val)
902
+ {
903
+ auto q_dec_clamp = torch::empty_like(q_dec);
904
+ if (q_dec.dtype() == torch::kFloat32) {
905
+ clamp_reciprocal_with_quant_dispatcher<float, float4>(q_dec_clamp, q_dec, y, min_val);
906
+ } else if (q_dec.dtype() == torch::kFloat16) {
907
+ clamp_reciprocal_with_quant_dispatcher<c10::Half, Half4>(q_dec_clamp, q_dec, y, min_val);
908
+ }
909
+ return q_dec_clamp;
910
+ }
911
+
912
+ template <typename T>
913
+ __global__ void add_and_multiply_kernel(GPUTensor1D<T> x0, const GPUTensor1D<T> x1,
914
+ const GPUTensor1D<T> q, const int N)
915
+ {
916
+ const int chw = blockIdx.x * blockDim.x + threadIdx.x;
917
+
918
+ if (chw < N) {
919
+ T _x0 = x0[chw];
920
+ T _x1 = x1[chw];
921
+ T _q = q[chw];
922
+ _x0 = _x0 + _x1;
923
+ _x0 = _x0 * _q;
924
+ x0[chw] = _x0;
925
+ }
926
+ }
927
+
928
+ template <typename scalar_t, typename vec_t>
929
+ __forceinline__ void add_and_multiply_dispatcher(torch::Tensor& x0, const torch::Tensor& x1,
930
+ const torch::Tensor& q)
931
+ {
932
+ auto [blockDim, gridDim, stream, useVec, biasSafe, N, HW] = get_kernel_launch_info<vec_t>(x0);
933
+ if (useVec) {
934
+ add_and_multiply_kernel<vec_t><<<gridDim, blockDim, 0, stream>>>(x0, x1, q, N);
935
+ } else {
936
+ add_and_multiply_kernel<scalar_t><<<gridDim, blockDim, 0, stream>>>(x0, x1, q, N);
937
+ }
938
+ }
939
+
940
+ void add_and_multiply_cuda(torch::Tensor& x0, const torch::Tensor& x1, const torch::Tensor q)
941
+ {
942
+ if (x0.dtype() == torch::kFloat32) {
943
+ add_and_multiply_dispatcher<float, float4>(x0, x1, q);
944
+ } else if (x0.dtype() == torch::kFloat16) {
945
+ add_and_multiply_dispatcher<c10::Half, Half4>(x0, x1, q);
946
+ }
947
+ }
948
+
949
+ template <typename scalar_t>
950
+ __global__ void replicate_pad_kernel(Packed4DTensorAccessor32<scalar_t> out,
951
+ const Packed4DTensorAccessor32<scalar_t> x, const int C,
952
+ const int H, const int W, const int H_padded, const int W_padded)
953
+ {
954
+ const int b = blockIdx.y;
955
+ const int n = blockIdx.x * blockDim.x + threadIdx.x;
956
+
957
+ if (n < H_padded * W_padded) {
958
+ const int dst_y = n / W_padded;
959
+ const int dst_x = n % W_padded;
960
+ const int src_y = min(dst_y, H - 1);
961
+ const int src_x = min(dst_x, W - 1);
962
+ for (int i = 0; i < C; i++) {
963
+ scalar_t _x = x[b][i][src_y][src_x];
964
+ out[b][i][dst_y][dst_x] = _x;
965
+ }
966
+ }
967
+ }
968
+
969
+ template <typename scalar_t>
970
+ __forceinline__ void replicate_pad_dispatcher(torch::Tensor& out, const torch::Tensor& x,
971
+ const int B, const int C, const int H, const int W,
972
+ const int padB, const int padR)
973
+ {
974
+ const int totalOutPixel = (H + padB) * (W + padR);
975
+ const int BLOCK_SIZE = 128;
976
+ const dim3 blockDim(BLOCK_SIZE);
977
+ const dim3 gridDim((totalOutPixel + BLOCK_SIZE - 1) / BLOCK_SIZE, B);
978
+ auto stream = at::cuda::getCurrentCUDAStream();
979
+
980
+ replicate_pad_kernel<scalar_t><<<gridDim, blockDim, 0, stream>>>(
981
+ out.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(),
982
+ x.packed_accessor32<scalar_t, 4, torch::RestrictPtrTraits>(), C, H, W, H + padB, W + padR);
983
+ }
984
+
985
+ torch::Tensor replicate_pad_cuda(const torch::Tensor& x, const int padB, const int padR)
986
+ {
987
+ const torch::IntArrayRef x_shape = x.sizes();
988
+ const int B = x_shape[0];
989
+ const int C = x_shape[1];
990
+ const int H = x_shape[2];
991
+ const int W = x_shape[3];
992
+ auto out = torch::empty({ B, C, H + padB, W + padR }, x.options());
993
+ if (x.dtype() == torch::kFloat32) {
994
+ replicate_pad_dispatcher<float>(out, x, B, C, H, W, padB, padR);
995
+ } else if (x.dtype() == torch::kFloat16) {
996
+ replicate_pad_dispatcher<c10::Half>(out, x, B, C, H, W, padB, padR);
997
+ } else if (x.dtype() == torch::kInt8) {
998
+ replicate_pad_dispatcher<int8_t>(out, x, B, C, H, W, padB, padR);
999
+ } else if (x.dtype() == torch::kInt16) {
1000
+ replicate_pad_dispatcher<int16_t>(out, x, B, C, H, W, padB, padR);
1001
+ }
1002
+ return out;
1003
+ }
1004
+
1005
+ template <typename T, typename T1, int BLOCK_SIZE, int THREAD_NUM_X, int THREAD_NUM_Y>
1006
+ __global__ void bias_wsilu_depthwise_conv2d_kernel(Packed4DTensorAccessor32<T> out,
1007
+ const Packed4DTensorAccessor32<T> x,
1008
+ const Packed4DTensorAccessor32<T> weight,
1009
+ const Packed1DTensorAccessor32<T> bias,
1010
+ const int B, const int C, const int H, const int W)
1011
+ {
1012
+ const int b = blockIdx.z / C;
1013
+ const int c = blockIdx.z % C;
1014
+ const int h = blockIdx.y * BLOCK_SIZE; // start of the block
1015
+ const int w = blockIdx.x * BLOCK_SIZE;
1016
+ const int THREAD_NUM = THREAD_NUM_Y * THREAD_NUM_X;
1017
+ const int t_idx = threadIdx.y * THREAD_NUM_X + threadIdx.x;
1018
+
1019
+ __shared__ T1 x_shared[BLOCK_SIZE + 2][BLOCK_SIZE + 2];
1020
+ const T1 __bias = static_cast<T1>(bias[c]);
1021
+ T1 __weight[3][3];
1022
+ #pragma unroll
1023
+ for (int i = 0; i < 3; i++) {
1024
+ #pragma unroll
1025
+ for (int j = 0; j < 3; j++) {
1026
+ __weight[i][j] = static_cast<T1>(weight[c][0][i][j]);
1027
+ }
1028
+ }
1029
+
1030
+ // load boundary padded pixels
1031
+ const int read_times = (BLOCK_SIZE * 4 + THREAD_NUM - 1) / THREAD_NUM;
1032
+ const int boundary_pos = BLOCK_SIZE + 1;
1033
+
1034
+ for (int i = 0; i < read_times; i++) {
1035
+ int pixel_idx = i * THREAD_NUM + t_idx;
1036
+ if (pixel_idx < BLOCK_SIZE * 2) {
1037
+ const int y_offset = pixel_idx / 2 + 1;
1038
+ const int x_offset = (pixel_idx & 1) * boundary_pos;
1039
+ const int curr_y = h + y_offset - 1;
1040
+ const int curr_x = w + x_offset - 1;
1041
+ if (curr_y < 0 || curr_x < 0 || curr_y >= H || curr_x >= W) {
1042
+ x_shared[y_offset][x_offset] = static_cast<T1>(0.f);
1043
+ } else {
1044
+ T1 x_tmp = static_cast<T1>(x[b][c][curr_y][curr_x]);
1045
+ x_shared[y_offset][x_offset] = wsilu(x_tmp + __bias);
1046
+ }
1047
+ } else if (pixel_idx < BLOCK_SIZE * 4) {
1048
+ pixel_idx -= BLOCK_SIZE * 2;
1049
+ const int y_offset = (pixel_idx & 1) * boundary_pos;
1050
+ const int x_offset = pixel_idx / 2 + 1;
1051
+ const int curr_y = h + y_offset - 1;
1052
+ const int curr_x = w + x_offset - 1;
1053
+ if (curr_y < 0 || curr_x < 0 || curr_y >= H || curr_x >= W) {
1054
+ x_shared[y_offset][x_offset] = static_cast<T1>(0.f);
1055
+ } else {
1056
+ T1 x_tmp = static_cast<T1>(x[b][c][curr_y][curr_x]);
1057
+ x_shared[y_offset][x_offset] = wsilu(x_tmp + __bias);
1058
+ }
1059
+ }
1060
+ }
1061
+
1062
+ // load corner 4 pixels
1063
+ if (t_idx < 4) {
1064
+ const int y_offset = (t_idx / 2) * boundary_pos;
1065
+ const int x_offset = (t_idx & 1) * boundary_pos;
1066
+ const int curr_y = h + y_offset - 1;
1067
+ const int curr_x = w + x_offset - 1;
1068
+ if (curr_y < 0 || curr_x < 0 || curr_y >= H || curr_x >= W) {
1069
+ x_shared[y_offset][x_offset] = static_cast<T1>(0.f);
1070
+ } else {
1071
+ T1 x_tmp = static_cast<T1>(x[b][c][curr_y][curr_x]);
1072
+ x_shared[y_offset][x_offset] = wsilu(x_tmp + __bias);
1073
+ }
1074
+ }
1075
+
1076
+ const int per_y_thread_pix_num = BLOCK_SIZE / THREAD_NUM_Y;
1077
+ const int per_x_thread_pix_num = BLOCK_SIZE / THREAD_NUM_X;
1078
+
1079
+ for (int t_y = 0; t_y < per_y_thread_pix_num; t_y++) {
1080
+ for (int t_x = 0; t_x < per_x_thread_pix_num; t_x++) {
1081
+ const int h_offset = threadIdx.y * per_y_thread_pix_num + t_y + 1;
1082
+ const int w_offset = threadIdx.x * per_x_thread_pix_num + t_x + 1;
1083
+ const int curr_y = h + h_offset - 1;
1084
+ const int curr_x = w + w_offset - 1;
1085
+ // curr_x and curr_y cannot < 0
1086
+ if (curr_y >= H || curr_x >= W) {
1087
+ x_shared[h_offset][w_offset] = static_cast<T1>(0.f);
1088
+ } else {
1089
+ T1 x_tmp = static_cast<T1>(x[b][c][curr_y][curr_x]);
1090
+ x_shared[h_offset][w_offset] = wsilu(x_tmp + __bias);
1091
+ }
1092
+ }
1093
+ }
1094
+ __syncthreads();
1095
+
1096
+ // calculation
1097
+ for (int t_y = 0; t_y < per_y_thread_pix_num; t_y++) {
1098
+ for (int t_x = 0; t_x < per_x_thread_pix_num; t_x++) {
1099
+ const int h_offset = threadIdx.y * per_y_thread_pix_num + t_y;
1100
+ const int w_offset = threadIdx.x * per_x_thread_pix_num + t_x;
1101
+ if (h + h_offset < H && w + w_offset < W) {
1102
+ T1 r = static_cast<T1>(0.f);
1103
+ #pragma unroll
1104
+ for (int i = 0; i < 3; i++) {
1105
+ #pragma unroll
1106
+ for (int j = 0; j < 3; j++) {
1107
+ r = multiply_add(__weight[i][j], x_shared[h_offset + i][w_offset + j], r);
1108
+ }
1109
+ }
1110
+
1111
+ out[b][c][h + h_offset][w + w_offset] = static_cast<T>(r);
1112
+ }
1113
+ }
1114
+ }
1115
+ }
1116
+
1117
+ torch::Tensor bias_wsilu_depthwise_conv2d_cuda(const torch::Tensor& x, const torch::Tensor& weight,
1118
+ const torch::Tensor& bias)
1119
+ {
1120
+ const torch::IntArrayRef x_shape = x.sizes();
1121
+ const int B = x_shape[0];
1122
+ const int C = x_shape[1];
1123
+ const int H = x_shape[2];
1124
+ const int W = x_shape[3];
1125
+
1126
+ auto out = torch::empty_like(x);
1127
+
1128
+ const int BLOCK_SIZE = 32;
1129
+ const int THREAD_NUM_X = 16;
1130
+ const int THREAD_NUM_Y = 8;
1131
+ const dim3 gridDim((W + BLOCK_SIZE - 1) / BLOCK_SIZE, (H + BLOCK_SIZE - 1) / BLOCK_SIZE, B * C);
1132
+ const dim3 blockDim(THREAD_NUM_X, THREAD_NUM_Y);
1133
+ auto stream = at::cuda::getCurrentCUDAStream();
1134
+ if (x.dtype() == torch::kFloat32) {
1135
+ bias_wsilu_depthwise_conv2d_kernel<float, float, BLOCK_SIZE, THREAD_NUM_X, THREAD_NUM_Y>
1136
+ <<<gridDim, blockDim, 0, stream>>>(
1137
+ out.packed_accessor32<float, 4, torch::RestrictPtrTraits>(),
1138
+ x.packed_accessor32<float, 4, torch::RestrictPtrTraits>(),
1139
+ weight.packed_accessor32<float, 4, torch::RestrictPtrTraits>(),
1140
+ bias.packed_accessor32<float, 1, torch::RestrictPtrTraits>(), B, C, H, W);
1141
+ } else if (x.dtype() == torch::kFloat16) {
1142
+ bias_wsilu_depthwise_conv2d_kernel<c10::Half, float, BLOCK_SIZE, THREAD_NUM_X, THREAD_NUM_Y>
1143
+ <<<gridDim, blockDim, 0, stream>>>(
1144
+ out.packed_accessor32<c10::Half, 4, torch::RestrictPtrTraits>(),
1145
+ x.packed_accessor32<c10::Half, 4, torch::RestrictPtrTraits>(),
1146
+ weight.packed_accessor32<c10::Half, 4, torch::RestrictPtrTraits>(),
1147
+ bias.packed_accessor32<c10::Half, 1, torch::RestrictPtrTraits>(), B, C, H, W);
1148
+ }
1149
+ return out;
1150
+ }
neural_codec/DCVC/src/layers/extensions/inference/setup.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import os
5
+ import glob
6
+ import sys
7
+ from setuptools import setup
8
+ from torch.utils.cpp_extension import BuildExtension, CUDAExtension
9
+
10
+
11
+ cxx_flags = ["-O3"]
12
+ nvcc_flags = ["-O3", "--use_fast_math", "--extra-device-vectorization"]
13
+ # `-arch=native` detects the building machine's GPU at compile time, which is
14
+ # convenient interactively but (a) fails when no GPU is visible during the build
15
+ # and (b) can conflict with the `-gencode` flags torch appends from
16
+ # TORCH_CUDA_ARCH_LIST. So only use `native` when the caller has NOT pinned the
17
+ # arch via TORCH_CUDA_ARCH_LIST (e.g. cluster builds set it to "8.0").
18
+ if not os.environ.get("TORCH_CUDA_ARCH_LIST"):
19
+ nvcc_flags.append("-arch=native")
20
+ if sys.platform == 'win32':
21
+ cxx_flags = ["/O2"]
22
+
23
+
24
+ setup(
25
+ name='inference_extensions_cuda',
26
+ ext_modules=[
27
+ CUDAExtension(
28
+ name='inference_extensions_cuda',
29
+ sources=glob.glob('*.cpp') + glob.glob('*.cu'),
30
+ extra_compile_args={
31
+ "cxx": cxx_flags,
32
+ "nvcc": nvcc_flags,
33
+ },
34
+ ),
35
+ ],
36
+ cmdclass={
37
+ 'build_ext': BuildExtension
38
+ }
39
+ )
neural_codec/DCVC/src/layers/layers.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import torch
5
+ from torch import nn
6
+ from .cuda_inference import CUSTOMIZED_CUDA_INFERENCE
7
+ if CUSTOMIZED_CUDA_INFERENCE:
8
+ from .cuda_inference import DepthConvProxy, SubpelConv2xProxy
9
+
10
+
11
+ class WSiLU(nn.Module):
12
+ def __init__(self):
13
+ super().__init__()
14
+
15
+ def forward(self, x):
16
+ return torch.sigmoid(4.0 * x) * x
17
+
18
+
19
+ class WSiLUChunkAdd(nn.Module):
20
+ def __init__(self):
21
+ super().__init__()
22
+ self.silu = WSiLU()
23
+
24
+ def forward(self, x):
25
+ x1, x2 = self.silu(x).chunk(2, 1)
26
+ return x1 + x2
27
+
28
+
29
+ class SubpelConv2x(nn.Module):
30
+ def __init__(self, in_ch, out_ch, kernel_size, padding=0):
31
+ super().__init__()
32
+ self.conv = nn.Sequential(
33
+ nn.Conv2d(in_ch, out_ch * 4, kernel_size=kernel_size, padding=padding),
34
+ nn.PixelShuffle(2),
35
+ )
36
+ self.padding = padding
37
+
38
+ self.proxy = None
39
+
40
+ def forward(self, x, to_cat=None, cat_at_front=True):
41
+ if not CUSTOMIZED_CUDA_INFERENCE or not x.is_cuda:
42
+ return self.forward_torch(x, to_cat, cat_at_front)
43
+
44
+ return self.forward_cuda(x, to_cat, cat_at_front)
45
+
46
+ def forward_torch(self, x, to_cat=None, cat_at_front=True):
47
+ out = self.conv(x)
48
+ if to_cat is None:
49
+ return out
50
+ if cat_at_front:
51
+ return torch.cat((to_cat, out), dim=1)
52
+ return torch.cat((out, to_cat), dim=1)
53
+
54
+ def forward_cuda(self, x, to_cat=None, cat_at_front=True):
55
+ if self.proxy is None:
56
+ self.proxy = SubpelConv2xProxy()
57
+ self.proxy.set_param(self.conv[0].weight, self.conv[0].bias, self.padding)
58
+
59
+ if to_cat is None:
60
+ return self.proxy.forward(x)
61
+
62
+ return self.proxy.forward_with_cat(x, to_cat, cat_at_front)
63
+
64
+
65
+ class DepthConvBlock(nn.Module):
66
+ def __init__(self, in_ch, out_ch, shortcut=False, force_adaptor=False):
67
+ super().__init__()
68
+ self.adaptor = None
69
+ if in_ch != out_ch or force_adaptor:
70
+ self.adaptor = nn.Conv2d(in_ch, out_ch, 1)
71
+ self.shortcut = shortcut
72
+ self.dc = nn.Sequential(
73
+ nn.Conv2d(out_ch, out_ch, 1),
74
+ WSiLU(),
75
+ nn.Conv2d(out_ch, out_ch, 3, padding=1, groups=out_ch),
76
+ nn.Conv2d(out_ch, out_ch, 1),
77
+ )
78
+ self.ffn = nn.Sequential(
79
+ nn.Conv2d(out_ch, out_ch * 4, 1),
80
+ WSiLUChunkAdd(),
81
+ nn.Conv2d(out_ch * 2, out_ch, 1),
82
+ )
83
+
84
+ self.proxy = None
85
+
86
+ def forward(self, x, quant_step=None, to_cat=None, cat_at_front=True):
87
+ if not CUSTOMIZED_CUDA_INFERENCE or not x.is_cuda:
88
+ return self.forward_torch(x, quant_step, to_cat, cat_at_front)
89
+
90
+ return self.forward_cuda(x, quant_step, to_cat, cat_at_front)
91
+
92
+ def forward_torch(self, x, quant_step=None, to_cat=None, cat_at_front=True):
93
+ if self.adaptor is not None:
94
+ x = self.adaptor(x)
95
+ out = self.dc(x) + x
96
+ out = self.ffn(out) + out
97
+ if self.shortcut:
98
+ out = out + x
99
+ if quant_step is not None:
100
+ out = out * quant_step
101
+ if to_cat is not None:
102
+ if cat_at_front:
103
+ out = torch.cat((to_cat, out), dim=1)
104
+ else:
105
+ out = torch.cat((out, to_cat), dim=1)
106
+ return out
107
+
108
+ def forward_cuda(self, x, quant_step=None, to_cat=None, cat_at_front=True):
109
+ if self.proxy is None:
110
+ self.proxy = DepthConvProxy()
111
+ if self.adaptor is not None:
112
+ self.proxy.set_param_with_adaptor(self.dc[0].weight, self.dc[0].bias,
113
+ self.dc[2].weight, self.dc[2].bias,
114
+ self.dc[3].weight, self.dc[3].bias,
115
+ self.ffn[0].weight, self.ffn[0].bias,
116
+ self.ffn[2].weight, self.ffn[2].bias,
117
+ self.adaptor.weight, self.adaptor.bias,
118
+ self.shortcut)
119
+ else:
120
+ self.proxy.set_param(self.dc[0].weight, self.dc[0].bias,
121
+ self.dc[2].weight, self.dc[2].bias,
122
+ self.dc[3].weight, self.dc[3].bias,
123
+ self.ffn[0].weight, self.ffn[0].bias,
124
+ self.ffn[2].weight, self.ffn[2].bias,
125
+ self.shortcut)
126
+
127
+ if quant_step is not None:
128
+ return self.proxy.forward_with_quant_step(x, quant_step)
129
+ if to_cat is not None:
130
+ return self.proxy.forward_with_cat(x, to_cat, cat_at_front)
131
+
132
+ return self.proxy.forward(x)
133
+
134
+
135
+ class ResidualBlockWithStride2(nn.Module):
136
+ def __init__(self, in_ch, out_ch):
137
+ super().__init__()
138
+ self.down = nn.Conv2d(in_ch, out_ch, 2, stride=2)
139
+ self.conv = DepthConvBlock(out_ch, out_ch, shortcut=True)
140
+
141
+ def forward(self, x):
142
+ x = self.down(x)
143
+ out = self.conv(x)
144
+ return out
145
+
146
+
147
+ class ResidualBlockUpsample(nn.Module):
148
+ def __init__(self, in_ch, out_ch):
149
+ super().__init__()
150
+ self.up = SubpelConv2x(in_ch, out_ch, 1)
151
+ self.conv = DepthConvBlock(out_ch, out_ch, shortcut=True)
152
+
153
+ def forward(self, x):
154
+ out = self.up(x)
155
+ out = self.conv(out)
156
+ return out
neural_codec/DCVC/src/models/common_model.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import torch
5
+ from torch import nn
6
+
7
+ from ..layers.cuda_inference import combine_for_reading_2x, \
8
+ restore_y_2x, restore_y_2x_with_cat_after, add_and_multiply, \
9
+ replicate_pad, restore_y_4x, clamp_reciprocal_with_quant
10
+ from .entropy_models import BitEstimator, GaussianEncoder, EntropyCoder
11
+
12
+
13
+ class CompressionModel(nn.Module):
14
+ def __init__(self, z_channel, extra_qp=0):
15
+ super().__init__()
16
+
17
+ self.z_channel = z_channel
18
+ self.entropy_coder = None
19
+ self.bit_estimator_z = BitEstimator(64 + extra_qp, z_channel)
20
+ self.gaussian_encoder = GaussianEncoder()
21
+
22
+ self.masks = {}
23
+ self.cuda_streams = {}
24
+
25
+ def get_cuda_stream(self, device, idx=0, priority=0):
26
+ key = f"{device}_{priority}_{idx}"
27
+ if key not in self.cuda_streams:
28
+ self.cuda_streams[key] = torch.cuda.Stream(device, priority=priority)
29
+ return self.cuda_streams[key]
30
+
31
+ @staticmethod
32
+ def get_qp_num():
33
+ return 64
34
+
35
+ @staticmethod
36
+ def get_padding_size(height, width, p=64):
37
+ new_h = (height + p - 1) // p * p
38
+ new_w = (width + p - 1) // p * p
39
+ padding_right = new_w - width
40
+ padding_bottom = new_h - height
41
+ return padding_right, padding_bottom
42
+
43
+ @staticmethod
44
+ def get_downsampled_shape(height, width, p):
45
+ new_h = (height + p - 1) // p * p
46
+ new_w = (width + p - 1) // p * p
47
+ return int(new_h / p + 0.5), int(new_w / p + 0.5)
48
+
49
+ def update(self, force_zero_thres=None):
50
+ self.entropy_coder = EntropyCoder()
51
+ self.gaussian_encoder.update(self.entropy_coder, force_zero_thres=force_zero_thres)
52
+ self.bit_estimator_z.update(self.entropy_coder)
53
+
54
+ def set_use_two_entropy_coders(self, use_two_entropy_coders):
55
+ self.entropy_coder.set_use_two_entropy_coders(use_two_entropy_coders)
56
+
57
+ def pad_for_y(self, y):
58
+ _, _, H, W = y.size()
59
+ padding_r, padding_b = self.get_padding_size(H, W, 4)
60
+ y_pad = replicate_pad(y, padding_b, padding_r)
61
+ return y_pad
62
+
63
+ def separate_prior(self, params, is_video=False):
64
+ if is_video:
65
+ quant_step, scales, means = params.chunk(3, 1)
66
+ quant_step = torch.clamp_min(quant_step, 0.5)
67
+ q_enc = 1. / quant_step
68
+ q_dec = quant_step
69
+ else:
70
+ q = params[:, :2, :, :]
71
+ q_enc, q_dec = (torch.sigmoid(q) * 1.5 + 0.5).chunk(2, 1)
72
+ scales, means = params[:, 2:, :, :].chunk(2, 1)
73
+ return q_enc, q_dec, scales, means
74
+
75
+ @staticmethod
76
+ def separate_prior_for_video_encoding(params, y):
77
+ q_dec, scales, means = params.chunk(3, 1)
78
+ q_dec, y = clamp_reciprocal_with_quant(q_dec, y, 0.5)
79
+ return y, q_dec, scales, means
80
+
81
+ @staticmethod
82
+ def separate_prior_for_video_decoding(params):
83
+ quant_step, scales, means = params.chunk(3, 1)
84
+ quant_step = torch.clamp_min(quant_step, 0.5)
85
+ return quant_step, scales, means
86
+
87
+ def process_with_mask(self, y, scales, means, mask):
88
+ return self.gaussian_encoder.process_with_mask(y, scales, means, mask)
89
+
90
+ @staticmethod
91
+ def get_one_mask(micro_mask, height, width, dtype, device):
92
+ mask = torch.tensor(micro_mask, dtype=dtype, device=device)
93
+ mask = mask.repeat((height + 1) // 2, (width + 1) // 2)
94
+ mask = mask[:height, :width]
95
+ mask = torch.unsqueeze(mask, 0)
96
+ mask = torch.unsqueeze(mask, 0)
97
+ return mask
98
+
99
+ def get_mask_4x(self, batch, channel, height, width, dtype, device):
100
+ curr_mask_str = f"{batch}_{channel}_{width}_{height}_4x"
101
+ with torch.no_grad():
102
+ if curr_mask_str not in self.masks:
103
+ assert channel % 4 == 0
104
+ m = torch.ones((batch, channel // 4, height, width), dtype=dtype, device=device)
105
+ m0 = self.get_one_mask(((1, 0), (0, 0)), height, width, dtype, device)
106
+ m1 = self.get_one_mask(((0, 1), (0, 0)), height, width, dtype, device)
107
+ m2 = self.get_one_mask(((0, 0), (1, 0)), height, width, dtype, device)
108
+ m3 = self.get_one_mask(((0, 0), (0, 1)), height, width, dtype, device)
109
+
110
+ mask_0 = torch.cat((m * m0, m * m1, m * m2, m * m3), dim=1)
111
+ mask_1 = torch.cat((m * m3, m * m2, m * m1, m * m0), dim=1)
112
+ mask_2 = torch.cat((m * m2, m * m3, m * m0, m * m1), dim=1)
113
+ mask_3 = torch.cat((m * m1, m * m0, m * m3, m * m2), dim=1)
114
+
115
+ self.masks[curr_mask_str] = [mask_0, mask_1, mask_2, mask_3]
116
+ return self.masks[curr_mask_str]
117
+
118
+ def get_mask_2x(self, batch, channel, height, width, dtype, device):
119
+ curr_mask_str = f"{batch}_{channel}_{width}_{height}_2x"
120
+ with torch.no_grad():
121
+ if curr_mask_str not in self.masks:
122
+ assert channel % 2 == 0
123
+ m = torch.ones((batch, channel // 2, height, width), dtype=dtype, device=device)
124
+ m0 = self.get_one_mask(((1, 0), (0, 1)), height, width, dtype, device)
125
+ m1 = self.get_one_mask(((0, 1), (1, 0)), height, width, dtype, device)
126
+
127
+ mask_0 = torch.cat((m * m0, m * m1), dim=1)
128
+ mask_1 = torch.cat((m * m1, m * m0), dim=1)
129
+
130
+ self.masks[curr_mask_str] = [mask_0, mask_1]
131
+ return self.masks[curr_mask_str]
132
+
133
+ @staticmethod
134
+ def single_part_for_writing_4x(x):
135
+ x0, x1, x2, x3 = x.chunk(4, 1)
136
+ return (x0 + x1) + (x2 + x3)
137
+
138
+ @staticmethod
139
+ def single_part_for_writing_2x(x):
140
+ x0, x1 = x.chunk(2, 1)
141
+ return x0 + x1
142
+
143
+ def compress_prior_2x(self, y, common_params, y_spatial_prior):
144
+ y, q_dec, scales, means = self.separate_prior_for_video_encoding(common_params, y)
145
+ dtype = y.dtype
146
+ device = y.device
147
+ B, C, H, W = y.size()
148
+ mask_0, mask_1 = self.get_mask_2x(B, C, H, W, dtype, device)
149
+
150
+ _, y_q_0, y_hat_0, s_hat_0 = self.process_with_mask(y, scales, means, mask_0)
151
+ cat_params = torch.cat((y_hat_0, common_params), dim=1)
152
+ scales, means = y_spatial_prior(cat_params).chunk(2, 1)
153
+ _, y_q_1, y_hat_1, s_hat_1 = self.process_with_mask(y, scales, means, mask_1)
154
+
155
+ y_hat = add_and_multiply(y_hat_0, y_hat_1, q_dec)
156
+
157
+ y_q_w_0 = self.single_part_for_writing_2x(y_q_0)
158
+ y_q_w_1 = self.single_part_for_writing_2x(y_q_1)
159
+ s_w_0 = self.single_part_for_writing_2x(s_hat_0)
160
+ s_w_1 = self.single_part_for_writing_2x(s_hat_1)
161
+ return y_q_w_0, y_q_w_1, s_w_0, s_w_1, y_hat
162
+
163
+ def decompress_prior_2x(self, common_params, y_spatial_prior):
164
+ infos = self.decompress_prior_2x_part1(common_params)
165
+ y_hat = self.decompress_prior_2x_part2(common_params, y_spatial_prior, infos)
166
+ return y_hat
167
+
168
+ def decompress_prior_2x_part1(self, common_params):
169
+ q_dec, scales, means = self.separate_prior_for_video_decoding(common_params)
170
+ dtype = means.dtype
171
+ device = means.device
172
+ B, C, H, W = means.size()
173
+ mask_0, mask_1 = self.get_mask_2x(B, C, H, W, dtype, device)
174
+
175
+ scales_r = combine_for_reading_2x(scales, mask_0, inplace=False)
176
+ indexes, skip_cond = self.gaussian_encoder.build_indexes_decoder(scales_r)
177
+ self.gaussian_encoder.decode_y(indexes)
178
+ infos = {
179
+ "q_dec": q_dec,
180
+ "mask_0": mask_0,
181
+ "mask_1": mask_1,
182
+ "means": means,
183
+ "scales_r": scales_r,
184
+ "skip_cond": skip_cond,
185
+ "indexes": indexes,
186
+ }
187
+ return infos
188
+
189
+ def decompress_prior_2x_part2(self, common_params, y_spatial_prior, infos):
190
+ dtype = common_params.dtype
191
+ device = common_params.device
192
+ y_q_r = self.gaussian_encoder.get_y(infos["scales_r"].shape,
193
+ infos["scales_r"].numel(),
194
+ dtype, device,
195
+ infos["skip_cond"], infos["indexes"])
196
+ y_hat_0, cat_params = restore_y_2x_with_cat_after(y_q_r, infos["means"], infos["mask_0"],
197
+ common_params)
198
+ scales, means = y_spatial_prior(cat_params).chunk(2, 1)
199
+ scales_r = combine_for_reading_2x(scales, infos["mask_1"], inplace=True)
200
+ y_q_r = self.gaussian_encoder.decode_and_get_y(scales_r, dtype, device)
201
+ y_hat_1 = restore_y_2x(y_q_r, means, infos["mask_1"])
202
+
203
+ y_hat = add_and_multiply(y_hat_0, y_hat_1, infos["q_dec"])
204
+ return y_hat
205
+
206
+ def compress_prior_4x(self, y, common_params, y_spatial_prior_reduction,
207
+ y_spatial_prior_adaptor_1, y_spatial_prior_adaptor_2,
208
+ y_spatial_prior_adaptor_3, y_spatial_prior):
209
+ '''
210
+ y_0 means split in channel, the 0/4 quater
211
+ y_1 means split in channel, the 1/4 quater
212
+ y_2 means split in channel, the 2/4 quater
213
+ y_3 means split in channel, the 3/4 quater
214
+ y_?_0, means multiply with mask_0
215
+ y_?_1, means multiply with mask_1
216
+ y_?_2, means multiply with mask_2
217
+ y_?_3, means multiply with mask_3
218
+ '''
219
+ q_enc, q_dec, scales, means = self.separate_prior(common_params, False)
220
+ common_params = y_spatial_prior_reduction(common_params)
221
+ dtype = y.dtype
222
+ device = y.device
223
+ B, C, H, W = y.size()
224
+ mask_0, mask_1, mask_2, mask_3 = self.get_mask_4x(B, C, H, W, dtype, device)
225
+
226
+ y = y * q_enc
227
+
228
+ _, y_q_0, y_hat_0, s_hat_0 = self.process_with_mask(y, scales, means, mask_0)
229
+
230
+ y_hat_so_far = y_hat_0
231
+ params = torch.cat((y_hat_so_far, common_params), dim=1)
232
+ scales, means = y_spatial_prior(y_spatial_prior_adaptor_1(params)).chunk(2, 1)
233
+ _, y_q_1, y_hat_1, s_hat_1 = self.process_with_mask(y, scales, means, mask_1)
234
+
235
+ y_hat_so_far = y_hat_so_far + y_hat_1
236
+ params = torch.cat((y_hat_so_far, common_params), dim=1)
237
+ scales, means = y_spatial_prior(y_spatial_prior_adaptor_2(params)).chunk(2, 1)
238
+ _, y_q_2, y_hat_2, s_hat_2 = self.process_with_mask(y, scales, means, mask_2)
239
+
240
+ y_hat_so_far = y_hat_so_far + y_hat_2
241
+ params = torch.cat((y_hat_so_far, common_params), dim=1)
242
+ scales, means = y_spatial_prior(y_spatial_prior_adaptor_3(params)).chunk(2, 1)
243
+ _, y_q_3, y_hat_3, s_hat_3 = self.process_with_mask(y, scales, means, mask_3)
244
+
245
+ y_hat = y_hat_so_far + y_hat_3
246
+ y_hat = y_hat * q_dec
247
+
248
+ y_q_w_0 = self.single_part_for_writing_4x(y_q_0)
249
+ y_q_w_1 = self.single_part_for_writing_4x(y_q_1)
250
+ y_q_w_2 = self.single_part_for_writing_4x(y_q_2)
251
+ y_q_w_3 = self.single_part_for_writing_4x(y_q_3)
252
+ s_w_0 = self.single_part_for_writing_4x(s_hat_0)
253
+ s_w_1 = self.single_part_for_writing_4x(s_hat_1)
254
+ s_w_2 = self.single_part_for_writing_4x(s_hat_2)
255
+ s_w_3 = self.single_part_for_writing_4x(s_hat_3)
256
+ return y_q_w_0, y_q_w_1, y_q_w_2, y_q_w_3, s_w_0, s_w_1, s_w_2, s_w_3, y_hat
257
+
258
+ def decompress_prior_4x(self, common_params, y_spatial_prior_reduction,
259
+ y_spatial_prior_adaptor_1, y_spatial_prior_adaptor_2,
260
+ y_spatial_prior_adaptor_3, y_spatial_prior):
261
+ _, quant_step, scales, means = self.separate_prior(common_params, False)
262
+ common_params = y_spatial_prior_reduction(common_params)
263
+ dtype = means.dtype
264
+ device = means.device
265
+ B, C, H, W = means.size()
266
+ mask_0, mask_1, mask_2, mask_3 = self.get_mask_4x(B, C, H, W, dtype, device)
267
+
268
+ scales_r = self.single_part_for_writing_4x(scales * mask_0)
269
+ y_q_r = self.gaussian_encoder.decode_and_get_y(scales_r, dtype, device)
270
+ y_hat_curr_step = restore_y_4x(y_q_r, means, mask_0)
271
+ y_hat_so_far = y_hat_curr_step
272
+
273
+ params = torch.cat((y_hat_so_far, common_params), dim=1)
274
+ scales, means = y_spatial_prior(y_spatial_prior_adaptor_1(params)).chunk(2, 1)
275
+ scales_r = self.single_part_for_writing_4x(scales * mask_1)
276
+ y_q_r = self.gaussian_encoder.decode_and_get_y(scales_r, dtype, device)
277
+ y_hat_curr_step = restore_y_4x(y_q_r, means, mask_1)
278
+ y_hat_so_far = y_hat_so_far + y_hat_curr_step
279
+
280
+ params = torch.cat((y_hat_so_far, common_params), dim=1)
281
+ scales, means = y_spatial_prior(y_spatial_prior_adaptor_2(params)).chunk(2, 1)
282
+ scales_r = self.single_part_for_writing_4x(scales * mask_2)
283
+ y_q_r = self.gaussian_encoder.decode_and_get_y(scales_r, dtype, device)
284
+ y_hat_curr_step = restore_y_4x(y_q_r, means, mask_2)
285
+ y_hat_so_far = y_hat_so_far + y_hat_curr_step
286
+
287
+ params = torch.cat((y_hat_so_far, common_params), dim=1)
288
+ scales, means = y_spatial_prior(y_spatial_prior_adaptor_3(params)).chunk(2, 1)
289
+ scales_r = self.single_part_for_writing_4x(scales * mask_3)
290
+ y_q_r = self.gaussian_encoder.decode_and_get_y(scales_r, dtype, device)
291
+ y_hat_curr_step = restore_y_4x(y_q_r, means, mask_3)
292
+ y_hat_so_far = y_hat_so_far + y_hat_curr_step
293
+
294
+ y_hat = y_hat_so_far * quant_step
295
+
296
+ return y_hat
neural_codec/DCVC/src/models/entropy_models.py ADDED
@@ -0,0 +1,341 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+
3
+ import torch
4
+ import numpy as np
5
+ from torch import nn
6
+ import torch.nn.functional as F
7
+
8
+ from ..layers.cuda_inference import build_index_dec, build_index_enc, process_with_mask
9
+
10
+
11
+ class EntropyCoder():
12
+ def __init__(self):
13
+ super().__init__()
14
+
15
+ from MLCodec_extensions_cpp import RansEncoder, RansDecoder
16
+ self.encoder = RansEncoder()
17
+ self.decoder = RansDecoder()
18
+
19
+ @staticmethod
20
+ def pmf_to_quantized_cdf(pmf, precision=16):
21
+ from MLCodec_extensions_cpp import pmf_to_quantized_cdf as _pmf_to_cdf
22
+ cdf = _pmf_to_cdf(pmf.tolist(), precision)
23
+ cdf = torch.IntTensor(cdf)
24
+ return cdf
25
+
26
+ @staticmethod
27
+ def pmf_to_cdf(pmf, tail_mass, pmf_length, max_length):
28
+ entropy_coder_precision = 16
29
+ cdf = torch.zeros((len(pmf_length), max_length + 2), dtype=torch.int32)
30
+ for i, p in enumerate(pmf):
31
+ prob = torch.cat((p[: pmf_length[i]], tail_mass[i]), dim=0)
32
+ _cdf = EntropyCoder.pmf_to_quantized_cdf(prob, entropy_coder_precision)
33
+ cdf[i, : _cdf.size(0)] = _cdf
34
+ return cdf
35
+
36
+ def reset(self):
37
+ self.encoder.reset()
38
+
39
+ def add_cdf(self, cdf, cdf_length, offset):
40
+ enc_cdf_idx = self.encoder.add_cdf(cdf, cdf_length, offset)
41
+ dec_cdf_idx = self.decoder.add_cdf(cdf, cdf_length, offset)
42
+ assert enc_cdf_idx == dec_cdf_idx
43
+ return enc_cdf_idx
44
+
45
+ def encode_y(self, symbols, cdf_group_index):
46
+ # symbols: int16, high 8 bits: int8 symbol to be encoded; low 8 bits: uint8 index to use
47
+ assert symbols.dtype == torch.int16
48
+ self.encoder.encode_y(symbols.cpu().numpy(), cdf_group_index)
49
+
50
+ def encode_z(self, symbols, cdf_group_index, start_offset, per_channel_size):
51
+ self.encoder.encode_z(symbols.to(torch.int8).cpu().numpy(),
52
+ cdf_group_index, start_offset, per_channel_size)
53
+
54
+ def flush(self):
55
+ self.encoder.flush()
56
+
57
+ def get_encoded_stream(self):
58
+ return self.encoder.get_encoded_stream().tobytes()
59
+
60
+ def set_stream(self, stream):
61
+ self.decoder.set_stream((np.frombuffer(stream, dtype=np.uint8)))
62
+
63
+ def decode_y(self, indexes, cdf_group_index):
64
+ self.decoder.decode_y(indexes.to(torch.uint8).cpu().numpy(), cdf_group_index)
65
+
66
+ def decode_and_get_y(self, indexes, cdf_group_index, device, dtype):
67
+ rv = self.decoder.decode_and_get_y(indexes.to(torch.uint8).cpu().numpy(), cdf_group_index)
68
+ rv = torch.as_tensor(rv)
69
+ return rv.to(device).to(dtype)
70
+
71
+ def decode_z(self, total_size, cdf_group_index, start_offset, per_channel_size):
72
+ self.decoder.decode_z(total_size, cdf_group_index, start_offset, per_channel_size)
73
+
74
+ def get_decoded_tensor(self, device, dtype, non_blocking=False):
75
+ rv = self.decoder.get_decoded_tensor()
76
+ rv = torch.as_tensor(rv)
77
+ return rv.to(device, non_blocking=non_blocking).to(dtype)
78
+
79
+ def set_use_two_entropy_coders(self, use_two_entropy_coders):
80
+ self.encoder.set_use_two_encoders(use_two_entropy_coders)
81
+ self.decoder.set_use_two_decoders(use_two_entropy_coders)
82
+
83
+
84
+ class Bitparm(nn.Module):
85
+ def __init__(self, qp_num, channel, final=False):
86
+ super().__init__()
87
+ self.final = final
88
+ self.h = nn.Parameter(torch.nn.init.normal_(
89
+ torch.empty([qp_num, channel, 1, 1]), 0, 0.01))
90
+ self.b = nn.Parameter(torch.nn.init.normal_(
91
+ torch.empty([qp_num, channel, 1, 1]), 0, 0.01))
92
+ if not final:
93
+ self.a = nn.Parameter(torch.nn.init.normal_(
94
+ torch.empty([qp_num, channel, 1, 1]), 0, 0.01))
95
+ else:
96
+ self.a = None
97
+
98
+ def forward(self, x, index):
99
+ h = torch.index_select(self.h, 0, index)
100
+ b = torch.index_select(self.b, 0, index)
101
+ x = x * F.softplus(h) + b
102
+ if self.final:
103
+ return x
104
+
105
+ a = torch.index_select(self.a, 0, index)
106
+ return x + torch.tanh(x) * torch.tanh(a)
107
+
108
+
109
+ class AEHelper():
110
+ def __init__(self):
111
+ super().__init__()
112
+ self.entropy_coder = None
113
+ self.cdf_group_index = None
114
+ self._offset = None
115
+ self._quantized_cdf = None
116
+ self._cdf_length = None
117
+
118
+ def set_cdf_info(self, quantized_cdf, cdf_length, offset):
119
+ self._quantized_cdf = quantized_cdf.cpu().numpy()
120
+ self._cdf_length = cdf_length.reshape(-1).int().cpu().numpy()
121
+ self._offset = offset.reshape(-1).int().cpu().numpy()
122
+
123
+ def get_cdf_info(self):
124
+ return self._quantized_cdf, \
125
+ self._cdf_length, \
126
+ self._offset
127
+
128
+
129
+ class BitEstimator(AEHelper, nn.Module):
130
+ def __init__(self, qp_num, channel):
131
+ super().__init__()
132
+ self.f1 = Bitparm(qp_num, channel)
133
+ self.f2 = Bitparm(qp_num, channel)
134
+ self.f3 = Bitparm(qp_num, channel)
135
+ self.f4 = Bitparm(qp_num, channel, True)
136
+ self.qp_num = qp_num
137
+ self.channel = channel
138
+
139
+ def forward(self, x, index):
140
+ return self.get_cdf(x, index)
141
+
142
+ def get_logits_cdf(self, x, index):
143
+ x = self.f1(x, index)
144
+ x = self.f2(x, index)
145
+ x = self.f3(x, index)
146
+ x = self.f4(x, index)
147
+ return x
148
+
149
+ def get_cdf(self, x, index):
150
+ return torch.sigmoid(self.get_logits_cdf(x, index))
151
+
152
+ def update(self, entropy_coder):
153
+ self.entropy_coder = entropy_coder
154
+
155
+ with torch.no_grad():
156
+ device = next(self.parameters()).device
157
+ medians = torch.zeros((self.qp_num, self.channel, 1, 1), device=device)
158
+ index = torch.arange(self.qp_num, device=device, dtype=torch.int32)
159
+
160
+ minima = medians + 8
161
+ for i in range(8, 1, -1):
162
+ samples = torch.zeros_like(medians) - i
163
+ probs = self.forward(samples, index)
164
+ minima = torch.where(probs < torch.zeros_like(medians) + 0.0001,
165
+ torch.zeros_like(medians) + i, minima)
166
+
167
+ maxima = medians + 8
168
+ for i in range(8, 1, -1):
169
+ samples = torch.zeros_like(medians) + i
170
+ probs = self.forward(samples, index)
171
+ maxima = torch.where(probs > torch.zeros_like(medians) + 0.9999,
172
+ torch.zeros_like(medians) + i, maxima)
173
+
174
+ minima = minima.int()
175
+ maxima = maxima.int()
176
+
177
+ offset = -minima
178
+
179
+ pmf_start = medians - minima
180
+ pmf_length = maxima + minima + 1
181
+
182
+ max_length = pmf_length.max()
183
+ device = pmf_start.device
184
+ samples = torch.arange(max_length, device=device)
185
+
186
+ samples = samples[None, None, None, :] + pmf_start
187
+
188
+ half = float(0.5)
189
+
190
+ lower = self.forward(samples - half, index)
191
+ upper = self.forward(samples + half, index)
192
+ pmf = upper - lower
193
+
194
+ pmf = pmf[:, :, 0, :]
195
+ upper = self.forward(maxima.to(torch.float32), index)
196
+ tail_mass = lower[:, :, 0, :1] + (1.0 - upper[:, :, 0, -1:])
197
+
198
+ pmf = pmf.reshape([-1, max_length])
199
+ tail_mass = tail_mass.reshape([-1, 1])
200
+ pmf_length = pmf_length.reshape([-1])
201
+ offset = offset.reshape([-1])
202
+ quantized_cdf = EntropyCoder.pmf_to_cdf(pmf, tail_mass, pmf_length, max_length)
203
+ cdf_length = pmf_length + 2
204
+ self.set_cdf_info(quantized_cdf, cdf_length, offset)
205
+ self.cdf_group_index = self.entropy_coder.add_cdf(*self.get_cdf_info())
206
+
207
+ def build_indexes(self, size, qp):
208
+ B, C, H, W = size
209
+ indexes = torch.arange(C, dtype=torch.int).view(1, -1, 1, 1) + qp * self.channel
210
+ return indexes.repeat(B, 1, H, W)
211
+
212
+ def encode_z(self, x, qp):
213
+ _, _, H, W = x.size()
214
+ return self.entropy_coder.encode_z(x.reshape(-1), self.cdf_group_index, qp * self.channel,
215
+ H * W)
216
+
217
+ def decode_z(self, size, qp):
218
+ self.entropy_coder.decode_z(self.channel * size[0] * size[1], self.cdf_group_index,
219
+ qp * self.channel, size[0] * size[1])
220
+
221
+ def get_z(self, size, device, dtype):
222
+ output_size = (1, self.channel, size[0], size[1])
223
+ val = self.entropy_coder.get_decoded_tensor(device, dtype, non_blocking=True)
224
+ return val.reshape(output_size)
225
+
226
+
227
+ class GaussianEncoder(AEHelper):
228
+ def __init__(self):
229
+ super().__init__()
230
+ self.scale_min = 0.11
231
+ self.scale_max = 16.0
232
+ self.scale_level = 128 # <= 256
233
+ self.scale_table = self.get_scale_table(self.scale_min, self.scale_max, self.scale_level)
234
+
235
+ self.log_scale_min = math.log(self.scale_min)
236
+ self.log_scale_max = math.log(self.scale_max)
237
+ self.log_scale_step = (self.log_scale_max - self.log_scale_min) / (self.scale_level - 1)
238
+ self.log_step_recip = 1. / self.log_scale_step
239
+
240
+ self.force_zero_thres = None
241
+ self.decode_index_cache = {}
242
+ self.decode_zeros_cache = {}
243
+
244
+ @staticmethod
245
+ def get_scale_table(min_val, max_val, levels):
246
+ return torch.exp(torch.linspace(math.log(min_val), math.log(max_val), levels))
247
+
248
+ def update(self, entropy_coder, force_zero_thres=None):
249
+ self.entropy_coder = entropy_coder
250
+ self.force_zero_thres = force_zero_thres
251
+
252
+ pmf_center = torch.zeros_like(self.scale_table) + 8
253
+ scales = torch.zeros_like(pmf_center) + self.scale_table
254
+ cdf_distribution = torch.distributions.normal.Normal(0., scales)
255
+ for i in range(8, 1, -1):
256
+ samples = torch.zeros_like(pmf_center) + i
257
+ probs = cdf_distribution.cdf(samples)
258
+ probs = torch.squeeze(probs)
259
+ pmf_center = torch.where(probs > torch.zeros_like(pmf_center) + 0.9999,
260
+ torch.zeros_like(pmf_center) + i, pmf_center)
261
+
262
+ pmf_center = pmf_center.int()
263
+ pmf_length = 2 * pmf_center + 1
264
+ max_length = torch.max(pmf_length).item()
265
+
266
+ device = pmf_center.device
267
+ samples = torch.arange(max_length, device=device) - pmf_center[:, None]
268
+ samples = samples.float()
269
+
270
+ scales = torch.zeros_like(samples) + self.scale_table[:, None]
271
+ cdf_distribution = torch.distributions.normal.Normal(0., scales)
272
+
273
+ upper = cdf_distribution.cdf(samples + 0.5)
274
+ lower = cdf_distribution.cdf(samples - 0.5)
275
+ pmf = upper - lower
276
+
277
+ tail_mass = 2 * lower[:, :1]
278
+
279
+ quantized_cdf = torch.Tensor(len(pmf_length), max_length + 2)
280
+ quantized_cdf = EntropyCoder.pmf_to_cdf(pmf, tail_mass, pmf_length, max_length)
281
+
282
+ self.set_cdf_info(quantized_cdf, pmf_length+2, -pmf_center)
283
+ self.cdf_group_index = self.entropy_coder.add_cdf(*self.get_cdf_info())
284
+
285
+ def process_with_mask(self, y, scales, means, mask):
286
+ return process_with_mask(y, scales, means, mask, self.force_zero_thres)
287
+
288
+ def build_indexes_decoder(self, scales):
289
+ scales = scales.reshape(-1)
290
+ indexes, skip_cond = build_index_dec(scales, self.scale_min, self.scale_max,
291
+ self.log_scale_min, self.log_step_recip,
292
+ self.force_zero_thres)
293
+ if self.force_zero_thres is not None:
294
+ indexes = indexes[skip_cond]
295
+ return indexes, skip_cond
296
+
297
+ def build_indexes_encoder(self, symbols, scales):
298
+ symbols = symbols.reshape(-1)
299
+ scales = scales.reshape(-1)
300
+ symbols = build_index_enc(symbols, scales, self.scale_min, self.scale_max,
301
+ self.log_scale_min, self.log_step_recip, self.force_zero_thres)
302
+ return symbols
303
+
304
+ def encode_y(self, x, scales):
305
+ symbols = self.build_indexes_encoder(x, scales)
306
+ return self.entropy_coder.encode_y(symbols, self.cdf_group_index)
307
+
308
+ def get_decode_index_cache(self, num, device):
309
+ if num not in self.decode_index_cache:
310
+ c = torch.arange(0, num, dtype=torch.int32, device=device)
311
+ self.decode_index_cache[num] = c
312
+
313
+ return self.decode_index_cache[num]
314
+
315
+ def get_decode_zeros_cache(self, num, device):
316
+ if num not in self.decode_zeros_cache:
317
+ c = torch.zeros(num, dtype=torch.int32, device=device)
318
+ self.decode_zeros_cache[num] = c
319
+
320
+ return self.decode_zeros_cache[num].clone()
321
+
322
+ def decode_and_get_y(self, scales, dtype, device):
323
+ indexes, skip_cond = self.build_indexes_decoder(scales)
324
+ self.decode_y(indexes)
325
+ return self.get_y(scales.shape, scales.numel(), dtype, device, skip_cond, indexes)
326
+
327
+ def decode_y(self, indexes):
328
+ self.entropy_coder.decode_y(indexes, self.cdf_group_index)
329
+
330
+ def get_y(self, shape, numel, dtype, device, skip_cond, indexes):
331
+ if len(indexes) == 0:
332
+ return torch.zeros(shape, dtype=dtype, device=device)
333
+ if skip_cond is not None:
334
+ curr_index = self.get_decode_index_cache(numel, device)
335
+ back_index = self.get_decode_zeros_cache(numel, device)
336
+ back_index.masked_scatter_(skip_cond, curr_index)
337
+ val = self.entropy_coder.get_decoded_tensor(device, dtype, non_blocking=True)
338
+ if skip_cond is not None:
339
+ y = torch.index_select(val, 0, back_index) * skip_cond
340
+ return y.reshape(shape)
341
+ return val.reshape(shape)
neural_codec/DCVC/src/models/image_model.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import torch
5
+ from torch import nn
6
+ import torch.nn.functional as F
7
+
8
+
9
+ from .common_model import CompressionModel
10
+ from ..layers.layers import DepthConvBlock, ResidualBlockUpsample, ResidualBlockWithStride2
11
+ from ..layers.cuda_inference import CUSTOMIZED_CUDA_INFERENCE, round_and_to_int8
12
+
13
+ g_ch_src = 3 * 8 * 8
14
+ g_ch_enc_dec = 368
15
+
16
+
17
+ class IntraEncoder(nn.Module):
18
+ def __init__(self, N):
19
+ super().__init__()
20
+
21
+ self.enc_1 = DepthConvBlock(g_ch_src, g_ch_enc_dec)
22
+ self.enc_2 = nn.Sequential(
23
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
24
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
25
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
26
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
27
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
28
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
29
+ nn.Conv2d(g_ch_enc_dec, N, 3, stride=2, padding=1),
30
+ )
31
+
32
+ def forward(self, x, quant_step):
33
+ out = F.pixel_unshuffle(x, 8)
34
+ if not CUSTOMIZED_CUDA_INFERENCE or not x.is_cuda:
35
+ return self.forward_torch(out, quant_step)
36
+
37
+ return self.forward_cuda(out, quant_step)
38
+
39
+ def forward_torch(self, out, quant_step):
40
+ out = self.enc_1(out)
41
+ out = out * quant_step
42
+ return self.enc_2(out)
43
+
44
+ def forward_cuda(self, out, quant_step):
45
+ out = self.enc_1(out, quant_step=quant_step)
46
+ return self.enc_2(out)
47
+
48
+
49
+ class IntraDecoder(nn.Module):
50
+ def __init__(self, N):
51
+ super().__init__()
52
+
53
+ self.dec_1 = nn.Sequential(
54
+ ResidualBlockUpsample(N, g_ch_enc_dec),
55
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
56
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
57
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
58
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
59
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
60
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
61
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
62
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
63
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
64
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
65
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
66
+ DepthConvBlock(g_ch_enc_dec, g_ch_enc_dec),
67
+ )
68
+ self.dec_2 = DepthConvBlock(g_ch_enc_dec, g_ch_src)
69
+
70
+ def forward(self, x, quant_step):
71
+ if not CUSTOMIZED_CUDA_INFERENCE or not x.is_cuda:
72
+ return self.forward_torch(x, quant_step)
73
+
74
+ return self.forward_cuda(x, quant_step)
75
+
76
+ def forward_torch(self, x, quant_step):
77
+ out = self.dec_1(x)
78
+ out = out * quant_step
79
+ out = self.dec_2(out)
80
+ out = F.pixel_shuffle(out, 8)
81
+ return out
82
+
83
+ def forward_cuda(self, x, quant_step):
84
+ out = self.dec_1[0](x)
85
+ out = self.dec_1[1](out)
86
+ out = self.dec_1[2](out)
87
+ out = self.dec_1[3](out)
88
+ out = self.dec_1[4](out)
89
+ out = self.dec_1[5](out)
90
+ out = self.dec_1[6](out)
91
+ out = self.dec_1[7](out)
92
+ out = self.dec_1[8](out)
93
+ out = self.dec_1[9](out)
94
+ out = self.dec_1[10](out)
95
+ out = self.dec_1[11](out)
96
+ out = self.dec_1[12](out, quant_step=quant_step)
97
+ out = self.dec_2(out)
98
+ out = F.pixel_shuffle(out, 8)
99
+ return out
100
+
101
+
102
+ class DMCI(CompressionModel):
103
+ def __init__(self, N=256, z_channel=128):
104
+ super().__init__(z_channel=z_channel)
105
+
106
+ self.enc = IntraEncoder(N)
107
+
108
+ self.hyper_enc = nn.Sequential(
109
+ DepthConvBlock(N, z_channel),
110
+ ResidualBlockWithStride2(z_channel, z_channel),
111
+ ResidualBlockWithStride2(z_channel, z_channel),
112
+ )
113
+
114
+ self.hyper_dec = nn.Sequential(
115
+ ResidualBlockUpsample(z_channel, z_channel),
116
+ ResidualBlockUpsample(z_channel, z_channel),
117
+ DepthConvBlock(z_channel, N),
118
+ )
119
+
120
+ self.y_prior_fusion = nn.Sequential(
121
+ DepthConvBlock(N, N * 2),
122
+ DepthConvBlock(N * 2, N * 2),
123
+ DepthConvBlock(N * 2, N * 2),
124
+ nn.Conv2d(N * 2, N * 2 + 2, 1),
125
+ )
126
+
127
+ self.y_spatial_prior_reduction = nn.Conv2d(N * 2 + 2, N * 1, 1)
128
+ self.y_spatial_prior_adaptor_1 = DepthConvBlock(N * 2, N * 2, force_adaptor=True)
129
+ self.y_spatial_prior_adaptor_2 = DepthConvBlock(N * 2, N * 2, force_adaptor=True)
130
+ self.y_spatial_prior_adaptor_3 = DepthConvBlock(N * 2, N * 2, force_adaptor=True)
131
+ self.y_spatial_prior = nn.Sequential(
132
+ DepthConvBlock(N * 2, N * 2),
133
+ DepthConvBlock(N * 2, N * 2),
134
+ DepthConvBlock(N * 2, N * 2),
135
+ nn.Conv2d(N * 2, N * 2, 1),
136
+ )
137
+
138
+ self.dec = IntraDecoder(N)
139
+
140
+ self.q_scale_enc = nn.Parameter(torch.ones((self.get_qp_num(), g_ch_enc_dec, 1, 1)))
141
+ self.q_scale_dec = nn.Parameter(torch.ones((self.get_qp_num(), g_ch_enc_dec, 1, 1)))
142
+
143
+ def compress(self, x, qp):
144
+ device = x.device
145
+ curr_q_enc = self.q_scale_enc[qp:qp+1, :, :, :]
146
+ curr_q_dec = self.q_scale_dec[qp:qp+1, :, :, :]
147
+
148
+ y = self.enc(x, curr_q_enc)
149
+ y_pad = self.pad_for_y(y)
150
+ z = self.hyper_enc(y_pad)
151
+ z_hat, z_hat_write = round_and_to_int8(z)
152
+
153
+ params = self.hyper_dec(z_hat)
154
+ params = self.y_prior_fusion(params)
155
+ _, _, yH, yW = y.shape
156
+ params = params[:, :, :yH, :yW].contiguous()
157
+ y_q_w_0, y_q_w_1, y_q_w_2, y_q_w_3, s_w_0, s_w_1, s_w_2, s_w_3, y_hat = \
158
+ self.compress_prior_4x(
159
+ y, params, self.y_spatial_prior_reduction,
160
+ self.y_spatial_prior_adaptor_1, self.y_spatial_prior_adaptor_2,
161
+ self.y_spatial_prior_adaptor_3, self.y_spatial_prior)
162
+
163
+ cuda_event = torch.cuda.Event()
164
+ cuda_event.record()
165
+ x_hat = self.dec(y_hat, curr_q_dec).clamp_(0, 1)
166
+
167
+ cuda_stream = self.get_cuda_stream(device=device, priority=-1)
168
+ with torch.cuda.stream(cuda_stream):
169
+ cuda_event.wait()
170
+ self.entropy_coder.reset()
171
+ self.bit_estimator_z.encode_z(z_hat_write, qp)
172
+ self.gaussian_encoder.encode_y(y_q_w_0, s_w_0)
173
+ self.gaussian_encoder.encode_y(y_q_w_1, s_w_1)
174
+ self.gaussian_encoder.encode_y(y_q_w_2, s_w_2)
175
+ self.gaussian_encoder.encode_y(y_q_w_3, s_w_3)
176
+ self.entropy_coder.flush()
177
+
178
+ bit_stream = self.entropy_coder.get_encoded_stream()
179
+
180
+ torch.cuda.synchronize(device=device)
181
+ result = {
182
+ "bit_stream": bit_stream,
183
+ "x_hat": x_hat,
184
+ }
185
+ return result
186
+
187
+ def decompress(self, bit_stream, sps, qp):
188
+ dtype = next(self.parameters()).dtype
189
+ device = next(self.parameters()).device
190
+ curr_q_dec = self.q_scale_dec[qp:qp+1, :, :, :]
191
+
192
+ self.entropy_coder.set_use_two_entropy_coders(sps['ec_part'] == 1)
193
+ self.entropy_coder.set_stream(bit_stream)
194
+ z_size = self.get_downsampled_shape(sps['height'], sps['width'], 64)
195
+ y_height, y_width = self.get_downsampled_shape(sps['height'], sps['width'], 16)
196
+ self.bit_estimator_z.decode_z(z_size, qp)
197
+ z_q = self.bit_estimator_z.get_z(z_size, device, dtype)
198
+ z_hat = z_q
199
+
200
+ params = self.hyper_dec(z_hat)
201
+ params = self.y_prior_fusion(params)
202
+ params = params[:, :, :y_height, :y_width].contiguous()
203
+ y_hat = self.decompress_prior_4x(params, self.y_spatial_prior_reduction,
204
+ self.y_spatial_prior_adaptor_1,
205
+ self.y_spatial_prior_adaptor_2,
206
+ self.y_spatial_prior_adaptor_3, self.y_spatial_prior)
207
+
208
+ x_hat = self.dec(y_hat, curr_q_dec).clamp_(0, 1)
209
+ return {"x_hat": x_hat}
neural_codec/DCVC/src/models/video_model.py ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import torch
5
+ import torch.nn.functional as F
6
+ from torch import nn
7
+
8
+ from .common_model import CompressionModel
9
+ from ..layers.layers import SubpelConv2x, DepthConvBlock, \
10
+ ResidualBlockUpsample, ResidualBlockWithStride2
11
+ from ..layers.cuda_inference import CUSTOMIZED_CUDA_INFERENCE, round_and_to_int8, \
12
+ bias_pixel_shuffle_8, bias_quant
13
+
14
+
15
+ qp_shift = [0, 8, 4]
16
+ extra_qp = max(qp_shift)
17
+
18
+ g_ch_src_d = 3 * 8 * 8
19
+ g_ch_recon = 320
20
+ g_ch_y = 128
21
+ g_ch_z = 128
22
+ g_ch_d = 256
23
+
24
+
25
+ class FeatureExtractor(nn.Module):
26
+ def __init__(self):
27
+ super().__init__()
28
+ self.conv1 = nn.Sequential(
29
+ DepthConvBlock(g_ch_d, g_ch_d),
30
+ DepthConvBlock(g_ch_d, g_ch_d),
31
+ )
32
+ self.conv2 = nn.Sequential(
33
+ DepthConvBlock(g_ch_d, g_ch_d),
34
+ DepthConvBlock(g_ch_d, g_ch_d),
35
+ DepthConvBlock(g_ch_d, g_ch_d),
36
+ DepthConvBlock(g_ch_d, g_ch_d),
37
+ )
38
+
39
+ def forward(self, x, quant):
40
+ x1, ctx_t = self.forward_part1(x, quant)
41
+ ctx = self.forward_part2(x1)
42
+ return ctx, ctx_t
43
+
44
+ def forward_part1(self, x, quant):
45
+ x1 = self.conv1(x)
46
+ ctx_t = x1 * quant
47
+ return x1, ctx_t
48
+
49
+ def forward_part2(self, x1):
50
+ ctx = self.conv2(x1)
51
+ return ctx
52
+
53
+
54
+ class Encoder(nn.Module):
55
+ def __init__(self):
56
+ super().__init__()
57
+ self.conv1 = nn.Conv2d(g_ch_src_d, g_ch_d, 1)
58
+ self.conv2 = nn.Sequential(
59
+ DepthConvBlock(g_ch_d * 2, g_ch_d),
60
+ DepthConvBlock(g_ch_d, g_ch_d),
61
+ )
62
+ self.conv3 = DepthConvBlock(g_ch_d, g_ch_d)
63
+ self.down = nn.Conv2d(g_ch_d, g_ch_y, 3, stride=2, padding=1)
64
+
65
+ self.fuse_conv1_flag = False
66
+
67
+ def forward(self, x, ctx, quant_step):
68
+ feature = F.pixel_unshuffle(x, 8)
69
+ if not CUSTOMIZED_CUDA_INFERENCE or not x.is_cuda:
70
+ return self.forward_torch(feature, ctx, quant_step)
71
+ return self.forward_cuda(feature, ctx, quant_step)
72
+
73
+ def forward_torch(self, feature, ctx, quant_step):
74
+ feature = self.conv1(feature)
75
+ feature = self.conv2(torch.cat((feature, ctx), dim=1))
76
+ feature = self.conv3(feature)
77
+ feature = feature * quant_step
78
+ feature = self.down(feature)
79
+ return feature
80
+
81
+ def forward_cuda(self, feature, ctx, quant_step):
82
+ if not self.fuse_conv1_flag:
83
+ fuse_weight1 = torch.matmul(
84
+ self.conv2[0].adaptor.weight[:, :g_ch_d, 0, 0],
85
+ self.conv1.weight[:, :, 0, 0]
86
+ )[:, :, None, None]
87
+ fuse_weight2 = self.conv2[0].adaptor.weight[:, g_ch_d:]
88
+ self.conv2[0].adaptor.bias.data = self.conv2[0].adaptor.bias + \
89
+ torch.matmul(self.conv2[0].adaptor.weight[:, :g_ch_d, 0, 0],
90
+ self.conv1.bias[:, None])[:, 0]
91
+ self.conv2[0].adaptor.weight.data = torch.cat([fuse_weight1, fuse_weight2], dim=1)
92
+ self.fuse_conv1_flag = True
93
+
94
+ feature = self.conv2(torch.cat((feature, ctx), dim=1))
95
+ feature = self.conv3(feature, quant_step=quant_step)
96
+ feature = self.down(feature)
97
+ return feature
98
+
99
+
100
+ class Decoder(nn.Module):
101
+ def __init__(self):
102
+ super().__init__()
103
+ self.up = SubpelConv2x(g_ch_y, g_ch_d, 3, padding=1)
104
+ self.conv1 = nn.Sequential(
105
+ DepthConvBlock(g_ch_d * 2, g_ch_d),
106
+ DepthConvBlock(g_ch_d, g_ch_d),
107
+ DepthConvBlock(g_ch_d, g_ch_d),
108
+ )
109
+ self.conv2 = nn.Conv2d(g_ch_d, g_ch_d, 1)
110
+
111
+ def forward(self, x, ctx, quant_step,):
112
+ if not CUSTOMIZED_CUDA_INFERENCE or not x.is_cuda:
113
+ return self.forward_torch(x, ctx, quant_step)
114
+
115
+ return self.forward_cuda(x, ctx, quant_step)
116
+
117
+ def forward_torch(self, x, ctx, quant_step):
118
+ feature = self.up(x)
119
+ feature = self.conv1(torch.cat((feature, ctx), dim=1))
120
+ feature = self.conv2(feature)
121
+ feature = feature * quant_step
122
+ return feature
123
+
124
+ def forward_cuda(self, x, ctx, quant_step):
125
+ feature = self.up(x, to_cat=ctx, cat_at_front=False)
126
+ feature = self.conv1(feature)
127
+ feature = F.conv2d(feature, self.conv2.weight)
128
+ feature = bias_quant(feature, self.conv2.bias, quant_step)
129
+ return feature
130
+
131
+
132
+ class ReconGeneration(nn.Module):
133
+ def __init__(self):
134
+ super().__init__()
135
+ self.conv = nn.Sequential(
136
+ DepthConvBlock(g_ch_d, g_ch_recon),
137
+ DepthConvBlock(g_ch_recon, g_ch_recon),
138
+ DepthConvBlock(g_ch_recon, g_ch_recon),
139
+ DepthConvBlock(g_ch_recon, g_ch_recon),
140
+ )
141
+ self.head = nn.Conv2d(g_ch_recon, g_ch_src_d, 1)
142
+
143
+ def forward(self, x, quant_step):
144
+ if not CUSTOMIZED_CUDA_INFERENCE or not x.is_cuda:
145
+ return self.forward_torch(x, quant_step)
146
+
147
+ return self.forward_cuda(x, quant_step)
148
+
149
+ def forward_torch(self, x, quant_step):
150
+ out = self.conv(x)
151
+ out = out * quant_step
152
+ out = self.head(out)
153
+ out = F.pixel_shuffle(out, 8)
154
+ out = torch.clamp(out, 0., 1.)
155
+ return out
156
+
157
+ def forward_cuda(self, x, quant_step):
158
+ out = self.conv[0](x)
159
+ out = self.conv[1](out)
160
+ out = self.conv[2](out)
161
+ out = self.conv[3](out, quant_step=quant_step)
162
+ out = F.conv2d(out, self.head.weight)
163
+ return bias_pixel_shuffle_8(out, self.head.bias)
164
+
165
+
166
+ class HyperEncoder(nn.Module):
167
+ def __init__(self):
168
+ super().__init__()
169
+ self.conv = nn.Sequential(
170
+ DepthConvBlock(g_ch_y, g_ch_z),
171
+ ResidualBlockWithStride2(g_ch_z, g_ch_z),
172
+ ResidualBlockWithStride2(g_ch_z, g_ch_z),
173
+ )
174
+
175
+ def forward(self, x):
176
+ return self.conv(x)
177
+
178
+
179
+ class HyperDecoder(nn.Module):
180
+ def __init__(self):
181
+ super().__init__()
182
+ self.conv = nn.Sequential(
183
+ ResidualBlockUpsample(g_ch_z, g_ch_z),
184
+ ResidualBlockUpsample(g_ch_z, g_ch_z),
185
+ DepthConvBlock(g_ch_z, g_ch_y),
186
+ )
187
+
188
+ def forward(self, x):
189
+ return self.conv(x)
190
+
191
+
192
+ class PriorFusion(nn.Module):
193
+ def __init__(self):
194
+ super().__init__()
195
+ self.conv = nn.Sequential(
196
+ DepthConvBlock(g_ch_y * 3, g_ch_y * 3),
197
+ DepthConvBlock(g_ch_y * 3, g_ch_y * 3),
198
+ DepthConvBlock(g_ch_y * 3, g_ch_y * 3),
199
+ nn.Conv2d(g_ch_y * 3, g_ch_y * 3, 1),
200
+ )
201
+
202
+ def forward(self, x):
203
+ return self.conv(x)
204
+
205
+
206
+ class SpatialPrior(nn.Module):
207
+ def __init__(self):
208
+ super().__init__()
209
+ self.conv = nn.Sequential(
210
+ DepthConvBlock(g_ch_y * 4, g_ch_y * 3),
211
+ DepthConvBlock(g_ch_y * 3, g_ch_y * 3),
212
+ nn.Conv2d(g_ch_y * 3, g_ch_y * 2, 1),
213
+ )
214
+
215
+ def forward(self, x):
216
+ return self.conv(x)
217
+
218
+
219
+ class RefFrame():
220
+ def __init__(self):
221
+ self.frame = None
222
+ self.feature = None
223
+ self.poc = None
224
+
225
+
226
+ class DMC(CompressionModel):
227
+ def __init__(self):
228
+ super().__init__(z_channel=g_ch_z, extra_qp=extra_qp)
229
+ self.qp_shift = qp_shift
230
+
231
+ self.feature_adaptor_i = DepthConvBlock(g_ch_src_d, g_ch_d)
232
+ self.feature_adaptor_p = nn.Conv2d(g_ch_d, g_ch_d, 1)
233
+ self.feature_extractor = FeatureExtractor()
234
+
235
+ self.encoder = Encoder()
236
+ self.hyper_encoder = HyperEncoder()
237
+ self.hyper_decoder = HyperDecoder()
238
+ self.temporal_prior_encoder = ResidualBlockWithStride2(g_ch_d, g_ch_y * 2)
239
+ self.y_prior_fusion = PriorFusion()
240
+ self.y_spatial_prior = SpatialPrior()
241
+ self.decoder = Decoder()
242
+ self.recon_generation_net = ReconGeneration()
243
+
244
+ self.q_encoder = nn.Parameter(torch.ones((self.get_qp_num() + extra_qp, g_ch_d, 1, 1)))
245
+ self.q_decoder = nn.Parameter(torch.ones((self.get_qp_num() + extra_qp, g_ch_d, 1, 1)))
246
+ self.q_feature = nn.Parameter(torch.ones((self.get_qp_num() + extra_qp, g_ch_d, 1, 1)))
247
+ self.q_recon = nn.Parameter(torch.ones((self.get_qp_num() + extra_qp, g_ch_recon, 1, 1)))
248
+
249
+ self.dpb = []
250
+ self.max_dpb_size = 1
251
+ self.curr_poc = 0
252
+
253
+ def reset_ref_feature(self):
254
+ if len(self.dpb) > 0:
255
+ self.dpb[0].feature = None
256
+
257
+ def add_ref_frame(self, feature=None, frame=None, increase_poc=True):
258
+ ref_frame = RefFrame()
259
+ ref_frame.poc = self.curr_poc
260
+ ref_frame.frame = frame
261
+ ref_frame.feature = feature
262
+ if len(self.dpb) >= self.max_dpb_size:
263
+ self.dpb.pop(-1)
264
+ self.dpb.insert(0, ref_frame)
265
+ if increase_poc:
266
+ self.curr_poc += 1
267
+
268
+ def clear_dpb(self):
269
+ self.dpb.clear()
270
+
271
+ def set_curr_poc(self, poc):
272
+ self.curr_poc = poc
273
+
274
+ def apply_feature_adaptor(self):
275
+ if self.dpb[0].feature is None:
276
+ return self.feature_adaptor_i(F.pixel_unshuffle(self.dpb[0].frame, 8))
277
+ return self.feature_adaptor_p(self.dpb[0].feature)
278
+
279
+ def res_prior_param_decoder(self, z_hat, ctx_t):
280
+ hierarchical_params = self.hyper_decoder(z_hat)
281
+ temporal_params = self.temporal_prior_encoder(ctx_t)
282
+ _, _, H, W = temporal_params.shape
283
+ hierarchical_params = hierarchical_params[:, :, :H, :W].contiguous()
284
+ params = self.y_prior_fusion(
285
+ torch.cat((hierarchical_params, temporal_params), dim=1))
286
+ return params
287
+
288
+ def get_recon_and_feature(self, y_hat, ctx, q_decoder, q_recon):
289
+ feature = self.decoder(y_hat, ctx, q_decoder)
290
+ x_hat = self.recon_generation_net(feature, q_recon)
291
+ return x_hat, feature
292
+
293
+ def prepare_feature_adaptor_i(self, last_qp):
294
+ if self.dpb[0].frame is None:
295
+ q_recon = self.q_recon[last_qp:last_qp+1, :, :, :]
296
+ self.dpb[0].frame = self.recon_generation_net(self.dpb[0].feature, q_recon).clamp_(0, 1)
297
+ self.reset_ref_feature()
298
+
299
+ def compress(self, x, qp):
300
+ # pic_width and pic_height may be different from x's size. x here is after padding
301
+ # x_hat has the same size with x
302
+ device = x.device
303
+ q_encoder = self.q_encoder[qp:qp+1, :, :, :]
304
+ q_decoder = self.q_decoder[qp:qp+1, :, :, :]
305
+ q_feature = self.q_feature[qp:qp+1, :, :, :]
306
+
307
+ feature = self.apply_feature_adaptor()
308
+ ctx, ctx_t = self.feature_extractor(feature, q_feature)
309
+ y = self.encoder(x, ctx, q_encoder)
310
+
311
+ hyper_inp = self.pad_for_y(y)
312
+
313
+ z = self.hyper_encoder(hyper_inp)
314
+ z_hat, z_hat_write = round_and_to_int8(z)
315
+ cuda_event_z_ready = torch.cuda.Event()
316
+ cuda_event_z_ready.record()
317
+ params = self.res_prior_param_decoder(z_hat, ctx_t)
318
+ y_q_w_0, y_q_w_1, s_w_0, s_w_1, y_hat = \
319
+ self.compress_prior_2x(y, params, self.y_spatial_prior)
320
+
321
+ cuda_event_y_ready = torch.cuda.Event()
322
+ cuda_event_y_ready.record()
323
+ feature = self.decoder(y_hat, ctx, q_decoder)
324
+
325
+ cuda_stream = self.get_cuda_stream(device=device, priority=-1)
326
+ with torch.cuda.stream(cuda_stream):
327
+ self.entropy_coder.reset()
328
+ cuda_event_z_ready.wait()
329
+ self.bit_estimator_z.encode_z(z_hat_write, qp)
330
+ cuda_event_y_ready.wait()
331
+ self.gaussian_encoder.encode_y(y_q_w_0, s_w_0)
332
+ self.gaussian_encoder.encode_y(y_q_w_1, s_w_1)
333
+ self.entropy_coder.flush()
334
+
335
+ bit_stream = self.entropy_coder.get_encoded_stream()
336
+
337
+ torch.cuda.synchronize(device=device)
338
+ self.add_ref_frame(feature, None)
339
+ return {
340
+ 'bit_stream': bit_stream,
341
+ }
342
+
343
+ def decompress(self, bit_stream, sps, qp):
344
+ dtype = next(self.parameters()).dtype
345
+ device = next(self.parameters()).device
346
+ q_decoder = self.q_decoder[qp:qp+1, :, :, :]
347
+ q_feature = self.q_feature[qp:qp+1, :, :, :]
348
+ q_recon = self.q_recon[qp:qp+1, :, :, :]
349
+
350
+ self.entropy_coder.set_use_two_entropy_coders(sps['ec_part'] == 1)
351
+ self.entropy_coder.set_stream(bit_stream)
352
+ z_size = self.get_downsampled_shape(sps['height'], sps['width'], 64)
353
+ self.bit_estimator_z.decode_z(z_size, qp)
354
+
355
+ feature = self.apply_feature_adaptor()
356
+ c1, ctx_t = self.feature_extractor.forward_part1(feature, q_feature)
357
+
358
+ z_hat = self.bit_estimator_z.get_z(z_size, device, dtype)
359
+ params = self.res_prior_param_decoder(z_hat, ctx_t)
360
+ infos = self.decompress_prior_2x_part1(params)
361
+
362
+ ctx = self.feature_extractor.forward_part2(c1)
363
+
364
+ cuda_stream = self.get_cuda_stream(device=device, priority=-1)
365
+ with torch.cuda.stream(cuda_stream):
366
+ y_hat = self.decompress_prior_2x_part2(params, self.y_spatial_prior, infos)
367
+ cuda_event = torch.cuda.Event()
368
+ cuda_event.record()
369
+
370
+ cuda_event.wait()
371
+ x_hat, feature = self.get_recon_and_feature(y_hat, ctx, q_decoder, q_recon)
372
+
373
+ self.add_ref_frame(feature, x_hat)
374
+ return {
375
+ 'x_hat': x_hat,
376
+ }
377
+
378
+ def shift_qp(self, qp, fa_idx):
379
+ return qp + self.qp_shift[fa_idx]
neural_codec/DCVC/src/utils/common.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import json
5
+ import os
6
+ from unittest.mock import patch
7
+
8
+ import torch
9
+ from torch.nn.modules.utils import consume_prefix_in_state_dict_if_present
10
+ import numpy as np
11
+
12
+
13
+ def str2bool(v):
14
+ return str(v).lower() in ("yes", "y", "true", "t", "1")
15
+
16
+
17
+ def set_torch_env():
18
+ os.environ['CUBLAS_WORKSPACE_CONFIG'] = ":4096:8"
19
+ torch.backends.cudnn.enabled = True
20
+ torch.backends.cudnn.benchmark = False
21
+ torch.use_deterministic_algorithms(True)
22
+ torch.manual_seed(0)
23
+ torch.set_num_threads(1)
24
+ np.random.seed(seed=0)
25
+ try:
26
+ # require pytorch >= 2.2.0
27
+ torch.utils.deterministic.fill_uninitialized_memory = False
28
+ except Exception: # pylint: disable=W0718
29
+ pass
30
+
31
+
32
+ def create_folder(path, print_if_create=False):
33
+ if not os.path.exists(path):
34
+ os.makedirs(path)
35
+ if print_if_create:
36
+ print(f"created folder: {path}")
37
+
38
+
39
+ def get_state_dict(ckpt_path):
40
+ ckpt = torch.load(ckpt_path, map_location=torch.device('cpu'), weights_only=True)
41
+ if "state_dict" in ckpt:
42
+ ckpt = ckpt['state_dict']
43
+ if "net" in ckpt:
44
+ ckpt = ckpt["net"]
45
+ consume_prefix_in_state_dict_if_present(ckpt, prefix="module.")
46
+ return ckpt
47
+
48
+
49
+ @patch('json.encoder.c_make_encoder', None)
50
+ def dump_json(obj, fid, float_digits=-1, **kwargs):
51
+ of = json.encoder._make_iterencode # pylint: disable=W0212
52
+
53
+ def inner(*args, **kwargs):
54
+ args = list(args)
55
+ # fifth argument is float formater which we will replace
56
+ args[4] = lambda o: format(o, '.%df' % float_digits)
57
+ return of(*args, **kwargs)
58
+
59
+ with patch('json.encoder._make_iterencode', wraps=inner):
60
+ json.dump(obj, fid, **kwargs)
61
+
62
+
63
+ def generate_log_json(frame_num, frame_pixel_num, test_time, frame_types, bits, psnrs, ssims,
64
+ verbose=False, avg_encoding_time=None, avg_decoding_time=None):
65
+ include_yuv = len(psnrs[0]) > 1
66
+ assert not include_yuv or (len(psnrs[0]) == 4 and len(ssims[0]) == 4)
67
+ i_bits = 0
68
+ i_psnr = 0
69
+ i_psnr_y = 0
70
+ i_psnr_u = 0
71
+ i_psnr_v = 0
72
+ i_ssim = 0
73
+ i_ssim_y = 0
74
+ i_ssim_u = 0
75
+ i_ssim_v = 0
76
+ p_bits = 0
77
+ p_psnr = 0
78
+ p_psnr_y = 0
79
+ p_psnr_u = 0
80
+ p_psnr_v = 0
81
+ p_ssim = 0
82
+ p_ssim_y = 0
83
+ p_ssim_u = 0
84
+ p_ssim_v = 0
85
+ i_num = 0
86
+ p_num = 0
87
+ for idx in range(frame_num):
88
+ if frame_types[idx] == 0:
89
+ i_bits += bits[idx]
90
+ i_psnr += psnrs[idx][0]
91
+ i_ssim += ssims[idx][0]
92
+ i_num += 1
93
+ if include_yuv:
94
+ i_psnr_y += psnrs[idx][1]
95
+ i_psnr_u += psnrs[idx][2]
96
+ i_psnr_v += psnrs[idx][3]
97
+ i_ssim_y += ssims[idx][1]
98
+ i_ssim_u += ssims[idx][2]
99
+ i_ssim_v += ssims[idx][3]
100
+ else:
101
+ p_bits += bits[idx]
102
+ p_psnr += psnrs[idx][0]
103
+ p_ssim += ssims[idx][0]
104
+ p_num += 1
105
+ if include_yuv:
106
+ p_psnr_y += psnrs[idx][1]
107
+ p_psnr_u += psnrs[idx][2]
108
+ p_psnr_v += psnrs[idx][3]
109
+ p_ssim_y += ssims[idx][1]
110
+ p_ssim_u += ssims[idx][2]
111
+ p_ssim_v += ssims[idx][3]
112
+
113
+ log_result = {}
114
+ log_result['frame_pixel_num'] = frame_pixel_num
115
+ log_result['i_frame_num'] = i_num
116
+ log_result['p_frame_num'] = p_num
117
+ log_result['ave_i_frame_bpp'] = i_bits / i_num / frame_pixel_num
118
+ log_result['ave_i_frame_psnr'] = i_psnr / i_num
119
+ log_result['ave_i_frame_msssim'] = i_ssim / i_num
120
+ if include_yuv:
121
+ log_result['ave_i_frame_psnr_y'] = i_psnr_y / i_num
122
+ log_result['ave_i_frame_psnr_u'] = i_psnr_u / i_num
123
+ log_result['ave_i_frame_psnr_v'] = i_psnr_v / i_num
124
+ log_result['ave_i_frame_msssim_y'] = i_ssim_y / i_num
125
+ log_result['ave_i_frame_msssim_u'] = i_ssim_u / i_num
126
+ log_result['ave_i_frame_msssim_v'] = i_ssim_v / i_num
127
+ if verbose:
128
+ log_result['frame_bpp'] = list(np.array(bits) / frame_pixel_num)
129
+ log_result['frame_psnr'] = [v[0] for v in psnrs]
130
+ log_result['frame_msssim'] = [v[0] for v in ssims]
131
+ log_result['frame_type'] = frame_types
132
+ if include_yuv:
133
+ log_result['frame_psnr_y'] = [v[1] for v in psnrs]
134
+ log_result['frame_psnr_u'] = [v[2] for v in psnrs]
135
+ log_result['frame_psnr_v'] = [v[3] for v in psnrs]
136
+ log_result['frame_msssim_y'] = [v[1] for v in ssims]
137
+ log_result['frame_msssim_u'] = [v[2] for v in ssims]
138
+ log_result['frame_msssim_v'] = [v[3] for v in ssims]
139
+ log_result['test_time'] = test_time
140
+ if p_num > 0:
141
+ total_p_pixel_num = p_num * frame_pixel_num
142
+ log_result['ave_p_frame_bpp'] = p_bits / total_p_pixel_num
143
+ log_result['ave_p_frame_psnr'] = p_psnr / p_num
144
+ log_result['ave_p_frame_msssim'] = p_ssim / p_num
145
+ if include_yuv:
146
+ log_result['ave_p_frame_psnr_y'] = p_psnr_y / p_num
147
+ log_result['ave_p_frame_psnr_u'] = p_psnr_u / p_num
148
+ log_result['ave_p_frame_psnr_v'] = p_psnr_v / p_num
149
+ log_result['ave_p_frame_msssim_y'] = p_ssim_y / p_num
150
+ log_result['ave_p_frame_msssim_u'] = p_ssim_u / p_num
151
+ log_result['ave_p_frame_msssim_v'] = p_ssim_v / p_num
152
+ else:
153
+ log_result['ave_p_frame_bpp'] = 0
154
+ log_result['ave_p_frame_psnr'] = 0
155
+ log_result['ave_p_frame_msssim'] = 0
156
+ if include_yuv:
157
+ log_result['ave_p_frame_psnr_y'] = 0
158
+ log_result['ave_p_frame_psnr_u'] = 0
159
+ log_result['ave_p_frame_psnr_v'] = 0
160
+ log_result['ave_p_frame_msssim_y'] = 0
161
+ log_result['ave_p_frame_msssim_u'] = 0
162
+ log_result['ave_p_frame_msssim_v'] = 0
163
+ log_result['ave_all_frame_bpp'] = (i_bits + p_bits) / (frame_num * frame_pixel_num)
164
+ log_result['ave_all_frame_psnr'] = (i_psnr + p_psnr) / frame_num
165
+ log_result['ave_all_frame_msssim'] = (i_ssim + p_ssim) / frame_num
166
+ if avg_encoding_time is not None and avg_decoding_time is not None:
167
+ log_result['avg_frame_encoding_time'] = avg_encoding_time
168
+ log_result['avg_frame_decoding_time'] = avg_decoding_time
169
+ if include_yuv:
170
+ log_result['ave_all_frame_psnr_y'] = (i_psnr_y + p_psnr_y) / frame_num
171
+ log_result['ave_all_frame_psnr_u'] = (i_psnr_u + p_psnr_u) / frame_num
172
+ log_result['ave_all_frame_psnr_v'] = (i_psnr_v + p_psnr_v) / frame_num
173
+ log_result['ave_all_frame_msssim_y'] = (i_ssim_y + p_ssim_y) / frame_num
174
+ log_result['ave_all_frame_msssim_u'] = (i_ssim_u + p_ssim_u) / frame_num
175
+ log_result['ave_all_frame_msssim_v'] = (i_ssim_v + p_ssim_v) / frame_num
176
+
177
+ return log_result
neural_codec/DCVC/src/utils/metrics.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import numpy as np
5
+ from scipy import signal
6
+ from scipy import ndimage
7
+
8
+
9
+ def fspecial_gauss(size, sigma):
10
+ x, y = np.mgrid[-size // 2 + 1:size // 2 + 1, -size // 2 + 1:size // 2 + 1]
11
+ g = np.exp(-((x**2 + y**2) / (2.0 * sigma**2)))
12
+ return g / g.sum()
13
+
14
+
15
+ def calc_ssim(img1, img2, data_range=255):
16
+ img1 = img1.astype(np.float64)
17
+ img2 = img2.astype(np.float64)
18
+ size = 11
19
+ sigma = 1.5
20
+ window = fspecial_gauss(size, sigma)
21
+ K1 = 0.01
22
+ K2 = 0.03
23
+ C1 = (K1 * data_range)**2
24
+ C2 = (K2 * data_range)**2
25
+ mu1 = signal.fftconvolve(window, img1, mode='valid')
26
+ mu2 = signal.fftconvolve(window, img2, mode='valid')
27
+ mu1_sq = mu1 * mu1
28
+ mu2_sq = mu2 * mu2
29
+ mu1_mu2 = mu1 * mu2
30
+ sigma1_sq = signal.fftconvolve(window, img1 * img1, mode='valid') - mu1_sq
31
+ sigma2_sq = signal.fftconvolve(window, img2 * img2, mode='valid') - mu2_sq
32
+ sigma12 = signal.fftconvolve(window, img1 * img2, mode='valid') - mu1_mu2
33
+
34
+ return (((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) *
35
+ (sigma1_sq + sigma2_sq + C2)),
36
+ (2.0 * sigma12 + C2) / (sigma1_sq + sigma2_sq + C2))
37
+
38
+
39
+ def calc_msssim(img1, img2, data_range=255):
40
+ '''
41
+ img1 and img2 are 2D arrays
42
+ '''
43
+ level = 5
44
+ weight = np.array([0.0448, 0.2856, 0.3001, 0.2363, 0.1333])
45
+ height, width = img1.shape
46
+ if height < 176 or width < 176:
47
+ # according to HM implementation
48
+ level = 4
49
+ weight = np.array([0.0517, 0.3295, 0.3462, 0.2726])
50
+ if height < 88 or width < 88:
51
+ assert False
52
+ downsample_filter = np.ones((2, 2)) / 4.0
53
+ im1 = img1.astype(np.float64)
54
+ im2 = img2.astype(np.float64)
55
+ mssim = np.array([])
56
+ mcs = np.array([])
57
+ for _ in range(level):
58
+ ssim_map, cs_map = calc_ssim(im1, im2, data_range=data_range)
59
+ mssim = np.append(mssim, ssim_map.mean())
60
+ mcs = np.append(mcs, cs_map.mean())
61
+ filtered_im1 = ndimage.filters.convolve(im1, downsample_filter,
62
+ mode='reflect')
63
+ filtered_im2 = ndimage.filters.convolve(im2, downsample_filter,
64
+ mode='reflect')
65
+ im1 = filtered_im1[::2, ::2]
66
+ im2 = filtered_im2[::2, ::2]
67
+ return (np.prod(mcs[0:level - 1]**weight[0:level - 1]) *
68
+ (mssim[level - 1]**weight[level - 1]))
69
+
70
+
71
+ def calc_msssim_rgb(img1, img2, data_range=255):
72
+ '''
73
+ img1 and img2 are arrays with 3xHxW
74
+ '''
75
+ msssim = 0
76
+ for i in range(3):
77
+ msssim += calc_msssim(img1[i, :, :], img2[i, :, :], data_range)
78
+ return msssim / 3
79
+
80
+
81
+ def calc_psnr(img1, img2, data_range=255):
82
+ '''
83
+ img1 and img2 are arrays with same shape
84
+ '''
85
+ img1 = img1.astype(np.float64)
86
+ img2 = img2.astype(np.float64)
87
+ mse = np.mean(np.square(img1 - img2))
88
+ if np.isnan(mse) or np.isinf(mse):
89
+ return -999.9
90
+ if mse > 1e-10:
91
+ psnr = 10 * np.log10(data_range * data_range / mse)
92
+ else:
93
+ psnr = 999.9
94
+ if psnr > 99.9:
95
+ psnr = 99.9
96
+ return psnr
neural_codec/DCVC/src/utils/stream_helper.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2020 InterDigital Communications, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import enum
16
+ import struct
17
+ from pathlib import Path
18
+
19
+
20
+ def filesize(filepath: str) -> int:
21
+ if not Path(filepath).is_file():
22
+ raise ValueError(f'Invalid file "{filepath}".')
23
+ return Path(filepath).stat().st_size
24
+
25
+
26
+ def write_uints(fd, values, fmt=">{:d}I"):
27
+ fd.write(struct.pack(fmt.format(len(values)), *values))
28
+ return len(values) * 4
29
+
30
+
31
+ def write_uchars(fd, values, fmt=">{:d}B"):
32
+ fd.write(struct.pack(fmt.format(len(values)), *values))
33
+ return len(values)
34
+
35
+
36
+ def read_uints(fd, n, fmt=">{:d}I"):
37
+ sz = struct.calcsize("I")
38
+ return struct.unpack(fmt.format(n), fd.read(n * sz))
39
+
40
+
41
+ def read_uchars(fd, n, fmt=">{:d}B"):
42
+ sz = struct.calcsize("B")
43
+ return struct.unpack(fmt.format(n), fd.read(n * sz))
44
+
45
+
46
+ def write_bytes(fd, values, fmt=">{:d}s"):
47
+ if len(values) == 0:
48
+ return 0
49
+ fd.write(struct.pack(fmt.format(len(values)), values))
50
+ return len(values)
51
+
52
+
53
+ def read_bytes(fd, n, fmt=">{:d}s"):
54
+ sz = struct.calcsize("s")
55
+ return struct.unpack(fmt.format(n), fd.read(n * sz))[0]
56
+
57
+
58
+ def write_ushorts(fd, values, fmt=">{:d}H"):
59
+ fd.write(struct.pack(fmt.format(len(values)), *values))
60
+ return len(values) * 2
61
+
62
+
63
+ def read_ushorts(fd, n, fmt=">{:d}H"):
64
+ sz = struct.calcsize("H")
65
+ return struct.unpack(fmt.format(n), fd.read(n * sz))
66
+
67
+
68
+ def write_uint_adaptive(f, a):
69
+ if a < (1 << 7):
70
+ a0 = (a >> 0) & 0xff
71
+ a0 = a0 | (0x00 << 7)
72
+ write_uchars(f, (a0,))
73
+ return 1
74
+
75
+ if a < (1 << 14):
76
+ a0 = (a >> 0) & 0xff
77
+ a1 = (a >> 8) & 0xff
78
+ a1 = a1 | (0x02 << 6)
79
+ write_uchars(f, (a1, a0))
80
+ return 2
81
+
82
+ assert a < (1 << 30)
83
+ a0 = (a >> 0) & 0xff
84
+ a1 = (a >> 8) & 0xff
85
+ a2 = (a >> 16) & 0xff
86
+ a3 = (a >> 24) & 0xff
87
+ a3 = a3 | (0x03 << 6)
88
+ write_uchars(f, (a3, a2, a1, a0))
89
+ return 4
90
+
91
+
92
+ def read_uint_adaptive(f):
93
+ a3 = read_uchars(f, 1)[0]
94
+ if (a3 >> 7) == 0:
95
+ return a3
96
+
97
+ a2 = read_uchars(f, 1)[0]
98
+
99
+ if (a3 >> 6) == 0x02:
100
+ a3 = a3 & 0x3f
101
+ return (a3 << 8) + a2
102
+ a3 = a3 & 0x3f
103
+ a1 = read_uchars(f, 1)[0]
104
+ a0 = read_uchars(f, 1)[0]
105
+ return (a3 << 24) + (a2 << 16) + (a1 << 8) + a0
106
+
107
+
108
+ class NalType(enum.IntEnum):
109
+ NAL_SPS = 0
110
+ NAL_I = 1
111
+ NAL_P = 2
112
+
113
+
114
+ class SPSHelper():
115
+ def __init__(self):
116
+ super().__init__()
117
+ self.spss = []
118
+
119
+ def get_sps_id(self, target_sps):
120
+ min_id = -1
121
+ for sps in self.spss:
122
+ if sps['height'] == target_sps['height'] and sps['width'] == target_sps['width'] and \
123
+ sps['use_ada_i'] == target_sps['use_ada_i'] and \
124
+ sps['ec_part'] == target_sps['ec_part']:
125
+ return sps['sps_id'], False
126
+ if sps['sps_id'] > min_id:
127
+ min_id = sps['sps_id']
128
+ assert min_id < 15
129
+ sps = target_sps.copy()
130
+ sps['sps_id'] = min_id + 1
131
+ self.spss.append(sps)
132
+ return sps['sps_id'], True
133
+
134
+ def add_sps_by_id(self, sps):
135
+ for i in range(len(self.spss)):
136
+ if self.spss[i]['sps_id'] == sps['sps_id']:
137
+ self.spss[i] = sps.copy()
138
+ return
139
+ self.spss.append(sps.copy())
140
+
141
+ def get_sps_by_id(self, sps_id):
142
+ for sps in self.spss:
143
+ if sps['sps_id'] == sps_id:
144
+ return sps
145
+ return None
146
+
147
+
148
+ def write_sps(f, sps):
149
+ # nal_type(4), sps_id(4)
150
+ # height (variable)
151
+ # width (vairable)
152
+ # 0(6), ec_part(1) use_ada_i(1)
153
+ assert sps['sps_id'] < 16
154
+ assert sps['use_ada_i'] < 2
155
+ written = 0
156
+ flag = int((NalType.NAL_SPS << 4) + sps['sps_id'])
157
+ written += write_uchars(f, (flag,))
158
+ written += write_uint_adaptive(f, sps['height'])
159
+ written += write_uint_adaptive(f, sps['width'])
160
+ flag = (sps['ec_part'] << 2) + sps['use_ada_i']
161
+ written += write_uchars(f, (flag,))
162
+ return written
163
+
164
+
165
+ def read_header(f):
166
+ header = {}
167
+ flag = read_uchars(f, 1)[0]
168
+ nal_type = flag >> 4
169
+ header['nal_type'] = NalType(nal_type)
170
+ if nal_type < 3:
171
+ header['sps_id'] = flag & 0x0f
172
+ return header
173
+
174
+ frame_num_minus1 = flag & 0x0f
175
+ frame_num = frame_num_minus1 + 1
176
+ header['frame_num'] = frame_num
177
+ sps_ids = []
178
+ for _ in range(0, frame_num, 2):
179
+ flag = read_uchars(f, 1)[0]
180
+ sps_ids.append(flag >> 4)
181
+ sps_ids.append(flag & 0x0f)
182
+ sps_ids = sps_ids[:frame_num]
183
+ header['sps_ids'] = sps_ids
184
+ return header
185
+
186
+
187
+ def read_sps_remaining(f, sps_id):
188
+ sps = {}
189
+ sps['sps_id'] = sps_id
190
+ sps['height'] = read_uint_adaptive(f)
191
+ sps['width'] = read_uint_adaptive(f)
192
+ flag = read_uchars(f, 1)[0]
193
+ sps['ec_part'] = (flag >> 2) & 0x01
194
+ sps['use_ada_i'] = flag & 0x01
195
+ return sps
196
+
197
+
198
+ def write_ip(f, is_i_frame, sps_id, qp, bit_stream):
199
+ written = 0
200
+ flag = (int(NalType.NAL_I if is_i_frame else NalType.NAL_P) << 4) + sps_id
201
+ written += write_uchars(f, (flag,))
202
+ assert qp < 256 and qp >= 0
203
+ flag = qp
204
+ written += write_uchars(f, (flag,))
205
+ # we write all the streams in the same file, thus, we need to write the per-frame length
206
+ # if packed independently, we do not need to write it
207
+ written += write_uint_adaptive(f, len(bit_stream))
208
+ written += write_bytes(f, bit_stream)
209
+ return written
210
+
211
+
212
+ def read_ip_remaining(f):
213
+ flag = read_uchars(f, 1)[0]
214
+ qp = flag
215
+ stream_length = read_uint_adaptive(f)
216
+ bit_stream = read_bytes(f, stream_length)
217
+ return qp, bit_stream
neural_codec/DCVC/src/utils/transforms.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import scipy.ndimage
3
+ import torch
4
+ import torch.nn.functional as F
5
+
6
+
7
+ YCBCR_WEIGHTS = {
8
+ # Spec: (K_r, K_g, K_b) with K_g = 1 - K_r - K_b
9
+ "ITU-R_BT.709": (0.2126, 0.7152, 0.0722)
10
+ }
11
+
12
+
13
+ def ycbcr420_to_444_np(y, uv, order=0, separate=False):
14
+ '''
15
+ y is 1xhxw Y float numpy array
16
+ uv is 2x(h/2)x(w/2) UV float numpy array
17
+ order: 0 nearest neighbor (default), 1: binear
18
+ return value is 3xhxw YCbCr float numpy array
19
+ '''
20
+ uv = scipy.ndimage.zoom(uv, (1, 2, 2), order=order)
21
+ if separate:
22
+ return y, uv
23
+ yuv = np.concatenate((y, uv), axis=0)
24
+ return yuv
25
+
26
+
27
+ def rgb2ycbcr(rgb, is_bgr=False):
28
+ if is_bgr:
29
+ b, g, r = rgb.chunk(3, -3)
30
+ else:
31
+ r, g, b = rgb.chunk(3, -3)
32
+ Kr, Kg, Kb = YCBCR_WEIGHTS["ITU-R_BT.709"]
33
+ y = Kr * r + Kg * g + Kb * b
34
+ cb = 0.5 * (b - y) / (1 - Kb) + 0.5
35
+ cr = 0.5 * (r - y) / (1 - Kr) + 0.5
36
+ ycbcr = torch.cat((y, cb, cr), dim=-3)
37
+ ycbcr = torch.clamp(ycbcr, 0., 1.)
38
+ return ycbcr
39
+
40
+
41
+ def ycbcr2rgb(ycbcr, is_bgr=False, clamp=True):
42
+ y, cb, cr = ycbcr.chunk(3, -3)
43
+ Kr, Kg, Kb = YCBCR_WEIGHTS["ITU-R_BT.709"]
44
+ r = y + (2 - 2 * Kr) * (cr - 0.5)
45
+ b = y + (2 - 2 * Kb) * (cb - 0.5)
46
+ g = (y - Kr * r - Kb * b) / Kg
47
+ if is_bgr:
48
+ rgb = torch.cat((b, g, r), dim=-3)
49
+ else:
50
+ rgb = torch.cat((r, g, b), dim=-3)
51
+ if clamp:
52
+ rgb = torch.clamp(rgb, 0., 1.)
53
+ return rgb
54
+
55
+
56
+ def yuv_444_to_420(yuv):
57
+ def _downsample(tensor):
58
+ return F.avg_pool2d(tensor, kernel_size=2, stride=2)
59
+
60
+ y = yuv[:, :1, :, :]
61
+ uv = yuv[:, 1:, :, :]
62
+
63
+ return y, _downsample(uv)
neural_codec/DCVC/src/utils/video_reader.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import os
5
+
6
+ import numpy as np
7
+ from PIL import Image
8
+
9
+
10
+ class PNGReader():
11
+ def __init__(self, src_path, width, height, start_num=1):
12
+ self.eof = False
13
+ self.src_path = src_path
14
+ self.width = width
15
+ self.height = height
16
+ pngs = os.listdir(self.src_path)
17
+ if 'im1.png' in pngs:
18
+ self.padding = 1
19
+ elif 'im00001.png' in pngs:
20
+ self.padding = 5
21
+ else:
22
+ raise ValueError('unknown image naming convention; please specify')
23
+ self.current_frame_index = start_num
24
+
25
+ def read_one_frame(self):
26
+ # rgb: 3xhxw uint8 numpy array
27
+ if self.eof:
28
+ return None
29
+
30
+ png_path = os.path.join(self.src_path,
31
+ f"im{str(self.current_frame_index).zfill(self.padding)}.png"
32
+ )
33
+ if not os.path.exists(png_path):
34
+ self.eof = True
35
+ return None
36
+
37
+ rgb = Image.open(png_path).convert('RGB')
38
+ rgb = np.asarray(rgb).astype(np.uint8).transpose(2, 0, 1)
39
+ _, height, width = rgb.shape
40
+ assert height == self.height
41
+ assert width == self.width
42
+
43
+ self.current_frame_index += 1
44
+ return rgb
45
+
46
+ def close(self):
47
+ self.current_frame_index = 1
48
+
49
+
50
+ class YUV420Reader():
51
+ def __init__(self, src_path, width, height, skip_frame=0):
52
+ self.eof = False
53
+ if not src_path.endswith('.yuv'):
54
+ src_path = src_path + '.yuv'
55
+ self.src_path = src_path
56
+
57
+ self.y_size = width * height
58
+ self.y_width = width
59
+ self.y_height = height
60
+ self.uv_size = width * height // 2
61
+ self.uv_width = width // 2
62
+ self.uv_height = height // 2
63
+ # pylint: disable=R1732
64
+ self.file = open(src_path, "rb")
65
+ # pylint: enable=R1732
66
+ skipped_frame = 0
67
+ while not self.eof and skipped_frame < skip_frame:
68
+ y = self.file.read(self.y_size)
69
+ uv = self.file.read(self.uv_size)
70
+ if not y or not uv:
71
+ self.eof = True
72
+ skipped_frame += 1
73
+
74
+ def read_one_frame(self):
75
+ # y: 1xhxw uint8 numpy array
76
+ # uv: 2x(h/2)x(w/2) uint8 numpy array
77
+ if self.eof:
78
+ return None, None
79
+ y = self.file.read(self.y_size)
80
+ uv = self.file.read(self.uv_size)
81
+ if not y or not uv:
82
+ self.eof = True
83
+ return None, None
84
+ y = np.frombuffer(y, dtype=np.uint8).copy().reshape(1, self.y_height, self.y_width)
85
+ uv = np.frombuffer(uv, dtype=np.uint8).copy().reshape(2, self.uv_height, self.uv_width)
86
+
87
+ return y, uv
88
+
89
+ def close(self):
90
+ self.file.close()
neural_codec/DCVC/src/utils/video_writer.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Microsoft Corporation.
2
+ # Licensed under the MIT License.
3
+
4
+ import os
5
+
6
+ from PIL import Image
7
+
8
+
9
+ class PNGWriter():
10
+ def __init__(self, dst_path, width, height):
11
+ self.dst_path = dst_path
12
+ self.width = width
13
+ self.height = height
14
+ self.padding = 5
15
+ self.current_frame_index = 1
16
+ os.makedirs(dst_path, exist_ok=True)
17
+
18
+ def write_one_frame(self, rgb):
19
+ # rgb: 3xhxw uint8 numpy array
20
+ rgb = rgb.transpose(1, 2, 0)
21
+
22
+ png_path = os.path.join(self.dst_path,
23
+ f"im{str(self.current_frame_index).zfill(self.padding)}.png"
24
+ )
25
+ Image.fromarray(rgb).save(png_path)
26
+
27
+ self.current_frame_index += 1
28
+
29
+ def close(self):
30
+ self.current_frame_index = 1
31
+
32
+
33
+ class YUV420Writer():
34
+ def __init__(self, dst_path, width, height):
35
+ if not dst_path.endswith('.yuv'):
36
+ dst_path = dst_path + '/out.yuv'
37
+ self.dst_path = dst_path
38
+ self.width = width
39
+ self.height = height
40
+
41
+ # pylint: disable=R1732
42
+ self.file = open(dst_path, "wb")
43
+ # pylint: enable=R1732
44
+
45
+ def write_one_frame(self, y, uv):
46
+ # y: 1xhxw uint8 numpy array
47
+ # uv: 2x(h/2)x(w/2) uint8 numpy array
48
+ self.file.write(y.tobytes())
49
+ self.file.write(uv.tobytes())
50
+
51
+ def close(self):
52
+ self.file.close()
neural_codec/README.md ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # neural_codec — DCVC-RT patch selection
2
+
3
+ Internal package for **DCVC-RT** neural-codec patch selection: the codec's per-frame
4
+ **bit-cost map** decides which video patches to feed the VLM (regions the codec spends more
5
+ bits on — motion / new detail — are kept; predictable background is dropped), as the neural
6
+ alternative to the traditional HEVC (`cv-preinfer`) path.
7
+
8
+ > **Usage, environment setup, and the controllable `codec.dcvc` parameters are documented in
9
+ > the top-level [`../README.md`](../README.md).** This file is an internal file reference.
10
+
11
+ ## Files
12
+
13
+ | File | Role |
14
+ |------|------|
15
+ | `dcvc_rt_engine.py` | Loads DCVC-RT intra/inter nets; `DMCIBitmap` / `DMCBitmap` add `compute_bitmap` (per-frame `(H/16, W/16)` bit-cost map) via a streaming `reset_sequence` / `step` API. Loads the bundled DCVC-RT source from `DCVC/` (no env var); checkpoints default to `dcvc_rt_intra.tar` / `dcvc_rt_inter.tar` in this dir (`DCVC_INTRA_TAR` / `DCVC_INTER_TAR` to override). |
16
+ | `codec_dcvc_config.py` | Single source of truth — reads the `codec.dcvc` block of `../processor/preprocessor_config.json`. |
17
+ | `dcvc_readiness_gen.py` | Config-driven generator: runs the readiness pipeline (`codec_tools/`) with DCVC bit-cost as the score source. Invoked by the model's codec path (`../processor/codec_video_processing_magevl.py::_run_dcvc_rt`). |
18
+ | `reproduce_bench.py` | Reproduce the evaluated selection for one video (config-driven; presets `cap12` / `b50` / `s95_b50`). |
19
+ | `codec_loader.py` | Load precomputed assets → model inputs via the release codec helpers. |
20
+ | `infer_dcvc_rt.py` | Standalone end-to-end demo (`--asset_dir` or `--video`). |
21
+ | `precompute_dcvc_rt.py` | Standalone, CLI-flag-driven batch precompute (video(s) → assets). |
22
+ | `canvas_assembler.py` | Top-k patch selection + canvas packing used by the standalone precompute path. |
23
+ | `codec_tools/` | Vendored readiness pipeline (frame sampling, grouping, 2×2-block selection, canvas packing). |
24
+ | `DCVC/` | Bundled DCVC-RT source (MIT, [microsoft/DCVC](https://github.com/microsoft/DCVC)): the `src/` package the engine imports + the CUDA-kernel source under `src/layers/extensions/inference/`. No external checkout / `DCVC_RT_ROOT` needed. |
25
+ | `dcvc_rt_intra.tar` / `dcvc_rt_inter.tar` | DCVC-RT checkpoints. |
26
+
27
+ ## Notes / limitations
28
+
29
+ - The bit-cost map is the summed **y-bits** estimate from DCVC-RT's Gaussian entropy model
30
+ (the dominant, spatially-resolved term); the small hyperprior `z` term is omitted (it is a
31
+ ranking signal). It is **not** run through the RANS arithmetic coder.
32
+ - `patch=16` is mandatory — it must match the image processor
33
+ (`preprocessor_config.json: patch_size=16, merge_size=2`). The `codec.patch=14` field is a
34
+ separate cv-preinfer internal and does not apply here.
35
+ - Canvases are square; non-16:9 videos are letterboxed, so wide videos waste some budget on
36
+ padding. Tune `codec.dcvc.max_pixels` if needed.
37
+ - DCVC-RT decodes every frame `0..max(sampled)` to keep temporal references valid, so long
38
+ videos are slow — use `codec.dcvc.max_side` and multiple GPUs.
39
+ - The DCVC CUDA kernels fall back to pytorch when the compiled extension is unavailable
40
+ (slower but numerically fine, and deterministic on the fallback path).
neural_codec/canvas_assembler.py ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Patch selection + canvas assembly for the DCVC-RT release backend.
2
+
3
+ Turns per-frame DCVC-RT bit-cost bitmaps into the exact on-disk contract the
4
+ release codec path (``codec_video_processing_magevl.py``) consumes:
5
+
6
+ <out_dir>/canvas_000.jpg ... RGB canvases, all identical size
7
+ <out_dir>/src_patch_position.npy int32 [total_patches, 3] = (t, h, w)
8
+ <out_dir>/meta.json {"fps": ..., "canvas_files": [...]}
9
+
10
+ Layout invariants required by the downstream release code
11
+ ---------------------------------------------------------
12
+ * All canvases share one size, so ``total_patches`` is divisible by the canvas
13
+ count (``drop_padding_canvases``).
14
+ * Canvas patch grid ``(Gh, Gw)`` is even in both dims (``spatial_merge_size=2``
15
+ block reorder in ``codec_positions_for_processor``).
16
+ * We select at **2x2-patch (28px) block granularity** and pack blocks in
17
+ time-sorted order, so after the block reorder the ``t`` column forms
18
+ consecutive runs whose lengths are multiples of 4 -- required for
19
+ ``rewrite_text_with_codec_positions`` (``count // merge**2`` token counts).
20
+ * ``t`` is the *original video frame index* so ``t / fps`` is the real
21
+ timestamp (same convention the HEVC/cv-preinfer contract uses).
22
+ * We never emit fully-padding canvases; a short final canvas is repeat-filled
23
+ with real blocks (so ``drop_padding_canvases`` is a no-op).
24
+
25
+ The importance signal (which blocks to keep) is DCVC-RT bits; everything else
26
+ mirrors the dev codec's ``pack_topk`` selection (mandatory full first frame,
27
+ then global top-k over the rest).
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import math
33
+ from dataclasses import dataclass
34
+ from typing import Dict, List, Optional, Tuple
35
+
36
+ import cv2
37
+ import numpy as np
38
+ from PIL import Image
39
+
40
+
41
+ @dataclass
42
+ class AssembleConfig:
43
+ # Must match the release image processor (preprocessor_config.json):
44
+ # patch_size=16, merge_size=2 -> selectable unit is 32px. (The codec.patch=14
45
+ # field in that config is a cv-preinfer internal; the Qwen2VLImageProcessor
46
+ # grid that codec_positions_for_processor reads uses 16.)
47
+ patch: int = 16
48
+ spatial_merge_size: int = 2
49
+ target_canvas: int = 32
50
+ seq_len_frames: int = 64
51
+ max_pixels: int = 150000
52
+ # Canvas is square with ``canvas_token_side`` tokens per side (a token = one
53
+ # 2x2 patch block = ``patch*merge`` px). If None, derived from max_pixels.
54
+ canvas_token_side: Optional[int] = None
55
+ mandatory_first_frame: bool = True
56
+
57
+ @property
58
+ def unit(self) -> int:
59
+ return int(self.patch) * int(self.spatial_merge_size)
60
+
61
+ def token_side(self) -> int:
62
+ if self.canvas_token_side is not None:
63
+ return int(self.canvas_token_side)
64
+ side_px = math.sqrt(float(self.max_pixels))
65
+ return max(2, int(round(side_px / self.unit)))
66
+
67
+
68
+ def _resize_longer_pad_square(img: np.ndarray, side_px: int, is_map: bool = False):
69
+ """Resize longer side to ``side_px`` (keep aspect) and center-pad to a
70
+ square. Returns (square_array, pad_info). ``is_map`` uses float + linear.
71
+ """
72
+ H, W = img.shape[:2]
73
+ scale = float(side_px) / float(max(H, W))
74
+ Hn = max(1, int(round(H * scale)))
75
+ Wn = max(1, int(round(W * scale)))
76
+ interp = cv2.INTER_LINEAR
77
+ resized = cv2.resize(img.astype(np.float32) if is_map else img, (Wn, Hn), interpolation=interp)
78
+ pad_top = (side_px - Hn) // 2
79
+ pad_left = (side_px - Wn) // 2
80
+ if is_map:
81
+ out = np.zeros((side_px, side_px), dtype=np.float32)
82
+ out[pad_top:pad_top + Hn, pad_left:pad_left + Wn] = resized
83
+ else:
84
+ out = np.zeros((side_px, side_px, 3), dtype=np.uint8)
85
+ out[pad_top:pad_top + Hn, pad_left:pad_left + Wn] = resized
86
+ info = dict(scale=scale, Hn=Hn, Wn=Wn, pad_top=pad_top, pad_left=pad_left)
87
+ return out, info
88
+
89
+
90
+ def _token_valid_mask(side_px: int, unit: int, info: dict) -> np.ndarray:
91
+ """Boolean (Ts, Ts): a token is valid if fully inside the non-padded region."""
92
+ ts = side_px // unit
93
+ top, left = info["pad_top"], info["pad_left"]
94
+ bottom, right = top + info["Hn"], left + info["Wn"]
95
+ mask = np.zeros((ts, ts), dtype=bool)
96
+ for r in range(ts):
97
+ y0, y1 = r * unit, (r + 1) * unit
98
+ if y0 < top or y1 > bottom:
99
+ continue
100
+ for c in range(ts):
101
+ x0, x1 = c * unit, (c + 1) * unit
102
+ if x0 < left or x1 > right:
103
+ continue
104
+ mask[r, c] = True
105
+ return mask
106
+
107
+
108
+ def _pool_map_to_tokens(square_map: np.ndarray, unit: int) -> np.ndarray:
109
+ """Sum a (side, side) map into (Ts, Ts) token scores."""
110
+ side = square_map.shape[0]
111
+ ts = side // unit
112
+ m = square_map[: ts * unit, : ts * unit]
113
+ return m.reshape(ts, unit, ts, unit).sum(axis=(1, 3))
114
+
115
+
116
+ def assemble_canvases(
117
+ sampled_frames_rgb: List[np.ndarray],
118
+ frame_ids: List[int],
119
+ bitmaps: Dict[int, np.ndarray],
120
+ cfg: AssembleConfig,
121
+ ) -> Tuple[List[Image.Image], np.ndarray]:
122
+ """Select high-bit blocks and pack them into release-contract canvases.
123
+
124
+ ``sampled_frames_rgb[i]`` is the decoded RGB frame for ``frame_ids[i]``;
125
+ ``bitmaps[frame_ids[i]]`` is its DCVC-RT bit map (H/16, W/16). Returns
126
+ (list of PIL RGB canvases, src_positions int32 [total_patches, 3]).
127
+ """
128
+ unit = cfg.unit
129
+ ts = cfg.token_side()
130
+ side_px = ts * unit
131
+ seq_len = len(sampled_frames_rgb)
132
+
133
+ # Per-frame square frames + token score maps + validity.
134
+ frames_sq: List[np.ndarray] = []
135
+ scores = np.full((seq_len, ts, ts), -np.inf, dtype=np.float32)
136
+ for i in range(seq_len):
137
+ fsq, info = _resize_longer_pad_square(sampled_frames_rgb[i], side_px, is_map=False)
138
+ frames_sq.append(fsq)
139
+ bm = bitmaps.get(int(frame_ids[i]))
140
+ if bm is None:
141
+ bm = np.zeros((max(1, side_px // 16), max(1, side_px // 16)), dtype=np.float32)
142
+ bm_sq, _ = _resize_longer_pad_square(bm, side_px, is_map=True)
143
+ vmask = _token_valid_mask(side_px, unit, info)
144
+ tok = _pool_map_to_tokens(bm_sq, unit)
145
+ scores[i][vmask] = tok[vmask]
146
+
147
+ tokens_per_canvas = ts * ts
148
+ target_tokens = int(cfg.target_canvas) * tokens_per_canvas
149
+
150
+ # --- selection: mandatory full first frame, then global top-k over rest ---
151
+ selected: List[Tuple[int, int, int]] = [] # (t_orig, th, tw)
152
+ if cfg.mandatory_first_frame and seq_len > 0:
153
+ for r in range(ts):
154
+ for c in range(ts):
155
+ selected.append((int(frame_ids[0]), r, c))
156
+
157
+ remaining = target_tokens - len(selected)
158
+ if remaining > 0 and seq_len > 1:
159
+ rest = scores[1:].copy() # (seq_len-1, ts, ts)
160
+ flat = rest.reshape(-1)
161
+ finite = np.isfinite(flat)
162
+ n_avail = int(finite.sum())
163
+ k = min(remaining, n_avail)
164
+ if k > 0:
165
+ order = np.argsort(-flat, kind="stable")[:k]
166
+ for idx in order:
167
+ fi = idx // (ts * ts) + 1
168
+ rem = idx % (ts * ts)
169
+ selected.append((int(frame_ids[fi]), rem // ts, rem % ts))
170
+
171
+ if not selected:
172
+ selected.append((int(frame_ids[0]) if frame_ids else 0, 0, 0))
173
+
174
+ # Time-sorted packing so per-canvas ``t`` runs are contiguous.
175
+ selected.sort(key=lambda x: (x[0], x[1], x[2]))
176
+
177
+ # Repeat-fill to a whole number of canvases (>=1), never half-pad.
178
+ if len(selected) < tokens_per_canvas:
179
+ n_canvas = 1
180
+ else:
181
+ n_canvas = min(int(cfg.target_canvas), len(selected) // tokens_per_canvas)
182
+ n_canvas = max(1, n_canvas)
183
+ target = n_canvas * tokens_per_canvas
184
+ if len(selected) < target:
185
+ selected = selected + [selected[-1]] * (target - len(selected))
186
+ else:
187
+ selected = selected[:target]
188
+
189
+ # index sampled frame_id -> its square frame (dup ids collapse fine)
190
+ fid_to_sq = {int(frame_ids[i]): frames_sq[i] for i in range(seq_len)}
191
+
192
+ Gh = Gw = ts * cfg.spatial_merge_size # canvas patch grid
193
+ sms = int(cfg.spatial_merge_size)
194
+ images: List[Image.Image] = []
195
+ all_positions: List[np.ndarray] = []
196
+
197
+ for ci in range(n_canvas):
198
+ canvas = np.zeros((side_px, side_px, 3), dtype=np.uint8)
199
+ positions = np.zeros((Gh * Gw, 3), dtype=np.int32)
200
+ chunk = selected[ci * tokens_per_canvas:(ci + 1) * tokens_per_canvas]
201
+ for p, (t_orig, th, tw) in enumerate(chunk):
202
+ rc, cc = p // ts, p % ts # canvas token cell
203
+ src = fid_to_sq.get(int(t_orig))
204
+ if src is not None:
205
+ canvas[rc * unit:(rc + 1) * unit, cc * unit:(cc + 1) * unit] = \
206
+ src[th * unit:(th + 1) * unit, tw * unit:(tw + 1) * unit]
207
+ pr, pc = sms * rc, sms * cc # canvas patch coords (top-left)
208
+ sph, spw = sms * th, sms * tw # source patch coords (top-left)
209
+ for dy in range(sms):
210
+ for dx in range(sms):
211
+ row = (pr + dy) * Gw + (pc + dx)
212
+ positions[row] = (int(t_orig), sph + dy, spw + dx)
213
+ images.append(Image.fromarray(canvas))
214
+ all_positions.append(positions)
215
+
216
+ src_positions = np.concatenate(all_positions, axis=0).astype(np.int32)
217
+ return images, src_positions
neural_codec/codec_dcvc_config.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Single source of truth for DCVC canvas-selection parameters.
2
+
3
+ Reads the ``codec.dcvc`` block of ``preprocessor_config.json`` (found by walking
4
+ up from this file to the model directory). Used by ``dcvc_readiness_gen.py`` and
5
+ the readiness pipeline (``process_video_bitcost_readiness.py`` /
6
+ ``process_video_bitcost_mv_mask_collage.py``) so ALL selection knobs are
7
+ controlled from the config file — NOT environment variables.
8
+
9
+ Full schema (defaults = the ``b50`` benchmark config) lives in
10
+ ``preprocessor_config.json`` under ``codec.dcvc``.
11
+ """
12
+ import json
13
+ import os
14
+ import functools
15
+
16
+ # Baseline defaults (the b50 benchmark config). Used only when a key is absent
17
+ # from preprocessor_config.json's codec.dcvc, so the file stays authoritative.
18
+ _DEFAULTS = {
19
+ # DCVC engine
20
+ "qp": 42, "reset_interval": 64, "intra_period": -1, "max_side": 0,
21
+ # readiness sampling / grouping
22
+ "num_sampled_frames": 256, "grouping_mode": "readiness",
23
+ "readiness_sum_threshold_mode": "auto", "group_size": 32,
24
+ "images_per_group": 4, "patch": 16, "max_pixels": 150000,
25
+ "min_group_frames": 8, "max_group_frames": 128,
26
+ "readiness_coverage_bins": 3, "readiness_delta_ratio": 0.05,
27
+ "bitcost_grid": "sub", "bitcost_pct": 99, "decode_backsearch_max": 16,
28
+ "canvas_format": "jpg",
29
+ # selection tuning knobs (our additions)
30
+ "per_frame_cap_ratio": 1.2, # spread block budget over more time frames
31
+ "bottom_atten": 0.5, # attenuate bottom-edge bit-cost (de-bias overlays)
32
+ "bottom_band": 0.10, # fraction of frame height treated as bottom band
33
+ "threshold_scale": 1.0, # scale readiness threshold (<1 -> more canvases)
34
+ "random_select": False, # random patch baseline (control)
35
+ "random_seed": 0,
36
+ }
37
+
38
+
39
+ @functools.lru_cache(maxsize=1)
40
+ def _load_file():
41
+ d = os.path.dirname(os.path.abspath(__file__))
42
+ for _ in range(4):
43
+ # preprocessor_config.json may sit next to this file (bundled layout) or in
44
+ # a sibling ``processor/`` subdir (multi-component model repo layout).
45
+ for p in (os.path.join(d, "preprocessor_config.json"),
46
+ os.path.join(d, "processor", "preprocessor_config.json")):
47
+ if os.path.exists(p):
48
+ try:
49
+ with open(p, encoding="utf-8") as f:
50
+ return json.load(f).get("codec", {}).get("dcvc", {}) or {}
51
+ except Exception:
52
+ return {}
53
+ d = os.path.dirname(d)
54
+ return {}
55
+
56
+
57
+ def get(key, cast=None):
58
+ """Return codec.dcvc[key] from preprocessor_config.json, else the b50 default.
59
+ ``cast`` optionally coerces (e.g. float, int, bool)."""
60
+ v = _load_file().get(key, _DEFAULTS.get(key))
61
+ if cast is not None and v is not None:
62
+ if cast is bool and isinstance(v, str):
63
+ return v.strip().lower() in ("1", "true", "yes")
64
+ return cast(v)
65
+ return v
neural_codec/codec_loader.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Load precomputed DCVC-RT canvases and turn them into model inputs.
2
+
3
+ This is the inference-side counterpart of ``precompute_dcvc_rt.py``. It reuses
4
+ the release codec helpers verbatim, so precomputed DCVC-RT assets go through the
5
+ *exact* same downstream the HEVC ``video_backend="codec"`` path uses.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import importlib
11
+ import importlib.util
12
+ import json
13
+ import os
14
+ import sys
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ import numpy as np
19
+ import torch
20
+ from PIL import Image
21
+
22
+ _REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Mage-VL-Ported/
23
+
24
+
25
+ def _load_release_codec_module():
26
+ """Import ``codec_video_processing_magevl`` from the release dir."""
27
+ try:
28
+ return importlib.import_module("codec_video_processing_magevl")
29
+ except Exception:
30
+ path = os.path.join(_REPO, "processor", "codec_video_processing_magevl.py")
31
+ spec = importlib.util.spec_from_file_location(
32
+ "codec_video_processing_magevl", path
33
+ )
34
+ mod = importlib.util.module_from_spec(spec)
35
+ sys.modules[spec.name] = mod # required for dataclass + future annotations
36
+ spec.loader.exec_module(mod)
37
+ return mod
38
+
39
+
40
+ def load_precomputed(asset_dir: str) -> dict:
41
+ """Read ``<asset_dir>`` written by precompute -> {images, src_positions, fps}.
42
+
43
+ Mirrors ``codec_video_processing_magevl._load_codec_result``.
44
+ """
45
+ asset_dir = Path(asset_dir)
46
+ with open(asset_dir / "meta.json", "r", encoding="utf-8") as f:
47
+ meta = json.load(f)
48
+ canvas_files = meta.get("canvas_files")
49
+ if not canvas_files:
50
+ canvas_files = sorted(p.name for p in asset_dir.glob("canvas_*.jpg"))
51
+ images = [Image.open(asset_dir / n).convert("RGB") for n in canvas_files]
52
+ src_positions = np.load(asset_dir / "src_patch_position.npy")
53
+ return {
54
+ "images": images,
55
+ "src_positions": src_positions,
56
+ "fps": float(meta.get("fps") or 30.0),
57
+ "meta": meta,
58
+ }
59
+
60
+
61
+ def build_inputs_from_assets(
62
+ processor,
63
+ asset_dir: str,
64
+ text: str,
65
+ max_pixels: Optional[int] = None,
66
+ device: Optional[torch.device] = None,
67
+ ) -> dict:
68
+ """Build a model-ready input dict from precomputed DCVC-RT assets + a
69
+ chat-templated ``text`` string (containing a ``<|vision_start|>...<|vision_end|>``
70
+ video span). Returns tensors ready for ``model.generate``.
71
+ """
72
+ cm = _load_release_codec_module()
73
+ payload = load_precomputed(asset_dir)
74
+
75
+ if max_pixels is None:
76
+ # Canvas budget lives in preprocessor_config.json's codec.dcvc (150000),
77
+ # NOT the image_processor's global max_pixels (the full-frame budget, 4M).
78
+ try:
79
+ import codec_dcvc_config as _dc
80
+ max_pixels = int(_dc.get("max_pixels"))
81
+ except Exception:
82
+ max_pixels = 150000
83
+
84
+ imgs, src_positions, _ = cm.drop_padding_canvases(payload["images"], payload["src_positions"])
85
+ if not imgs:
86
+ raise RuntimeError(f"no usable canvases in {asset_dir}")
87
+
88
+ image_data = cm.codec_image_processor_outputs(processor.image_processor, imgs, max_pixels=max_pixels)
89
+ image_grid_thw = image_data["image_grid_thw"]
90
+ patch_positions = cm.codec_positions_for_processor(
91
+ src_positions, image_grid_thw, device=image_grid_thw.device
92
+ )
93
+ rewritten = cm.rewrite_text_with_codec_positions(
94
+ text, patch_positions, fps=float(payload["fps"]), decimals=1
95
+ )
96
+
97
+ enc = processor.tokenizer(rewritten, return_tensors="pt")
98
+ out = {
99
+ "input_ids": enc["input_ids"],
100
+ "attention_mask": enc["attention_mask"],
101
+ "pixel_values": image_data["pixel_values"],
102
+ "image_grid_thw": image_grid_thw,
103
+ "patch_positions": patch_positions,
104
+ }
105
+ if device is not None:
106
+ for k, v in out.items():
107
+ if isinstance(v, torch.Tensor):
108
+ out[k] = v.to(device)
109
+ return out
neural_codec/codec_tools/__init__.py ADDED
File without changes
neural_codec/codec_tools/codec_patch_gop/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Codec-aware patch dataset generator with GOP-based sampling.
6
+
7
+ This package provides tools for extracting patches from videos using
8
+ motion vector and residual-based scoring.
9
+ """
10
+
11
+ __version__ = "1.0.0"