Image-Text-to-Text
Transformers
Safetensors
mage_vl
multimodal
vision-language-model
mage-vl
video-understanding
streaming
conversational
custom_code
Instructions to use Mage-Fans/Mage-VL with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Mage-Fans/Mage-VL with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="Mage-Fans/Mage-VL", trust_remote_code=True) messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoModelForImageTextToText model = AutoModelForImageTextToText.from_pretrained("Mage-Fans/Mage-VL", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Mage-Fans/Mage-VL with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Mage-Fans/Mage-VL" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Mage-Fans/Mage-VL", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/Mage-Fans/Mage-VL
- SGLang
How to use Mage-Fans/Mage-VL with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "Mage-Fans/Mage-VL" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Mage-Fans/Mage-VL", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "Mage-Fans/Mage-VL" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Mage-Fans/Mage-VL", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use Mage-Fans/Mage-VL with Docker Model Runner:
docker model run hf.co/Mage-Fans/Mage-VL
Duplicate from microsoft/Mage-VL
Browse filesCo-authored-by: Xinjie <Xinjie-Q@users.noreply.huggingface.co>
This view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +41 -0
- README.md +349 -0
- added_tokens.json +24 -0
- assets/mage-vl-cover.png +3 -0
- assets/mage-vl-framework.png +3 -0
- chat_template.jinja +7 -0
- codec_video_processing_mage_vl.py +591 -0
- config.json +118 -0
- configuration_mage_vl.py +110 -0
- examples/dog.jpg +3 -0
- examples/soccer-broadcast.mp4 +3 -0
- generation_config.json +6 -0
- inference.py +160 -0
- merges.txt +0 -0
- model-00001-of-00002.safetensors +3 -0
- model-00002-of-00002.safetensors +3 -0
- model.safetensors.index.json +703 -0
- modeling_mage_vl.py +1709 -0
- neural_codec/DCVC/LICENSE.txt +21 -0
- neural_codec/DCVC/NOTICE.txt +258 -0
- neural_codec/DCVC/src/cpp/py_rans/py_rans.cpp +393 -0
- neural_codec/DCVC/src/cpp/py_rans/py_rans.h +71 -0
- neural_codec/DCVC/src/cpp/py_rans/rans.cpp +534 -0
- neural_codec/DCVC/src/cpp/py_rans/rans.h +201 -0
- neural_codec/DCVC/src/cpp/py_rans/rans_byte.h +141 -0
- neural_codec/DCVC/src/cpp/setup.py +31 -0
- neural_codec/DCVC/src/layers/cuda_inference.py +208 -0
- neural_codec/DCVC/src/layers/extensions/inference/bind.cpp +36 -0
- neural_codec/DCVC/src/layers/extensions/inference/common.h +352 -0
- neural_codec/DCVC/src/layers/extensions/inference/def.h +107 -0
- neural_codec/DCVC/src/layers/extensions/inference/impl.cpp +167 -0
- neural_codec/DCVC/src/layers/extensions/inference/kernel.cu +1150 -0
- neural_codec/DCVC/src/layers/extensions/inference/setup.py +39 -0
- neural_codec/DCVC/src/layers/layers.py +156 -0
- neural_codec/DCVC/src/models/common_model.py +296 -0
- neural_codec/DCVC/src/models/entropy_models.py +341 -0
- neural_codec/DCVC/src/models/image_model.py +209 -0
- neural_codec/DCVC/src/models/video_model.py +379 -0
- neural_codec/DCVC/src/utils/common.py +177 -0
- neural_codec/DCVC/src/utils/metrics.py +96 -0
- neural_codec/DCVC/src/utils/stream_helper.py +217 -0
- neural_codec/DCVC/src/utils/transforms.py +63 -0
- neural_codec/DCVC/src/utils/video_reader.py +90 -0
- neural_codec/DCVC/src/utils/video_writer.py +52 -0
- neural_codec/README.md +40 -0
- neural_codec/canvas_assembler.py +217 -0
- neural_codec/codec_dcvc_config.py +65 -0
- neural_codec/codec_loader.py +109 -0
- neural_codec/codec_tools/__init__.py +0 -0
- neural_codec/codec_tools/codec_patch_gop/__init__.py +11 -0
.gitattributes
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 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
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
assets/mage-vl-framework.png
ADDED
|
Git LFS Details
|
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
|
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"
|