| --- |
| library_name: executorch |
| license: apache-2.0 |
| base_model: kadirnar/RRDB_PSNR_x4 |
| base_model_relation: quantized |
| tags: |
| - image-to-image |
| - esrgan |
| - int8 |
| - quantized |
| - xnnpack |
| - arm |
| - executorch |
| - edge-ai |
| - urban100 |
| - super-resolution |
| pipeline_tag: image-to-image |
| datasets: |
| - urban100 |
| metrics: |
| - psnr |
| model-index: |
| - name: esrgan-int8-xnnpack-executorch-graviton-g4 |
| results: |
| - task: |
| type: image-to-image |
| name: Super Resolution |
| dataset: |
| type: urban100 |
| name: Urban100 |
| split: validation |
| args: |
| evaluation_samples: 100 |
| metrics: |
| - type: psnr |
| value: 26.81 |
| name: PSNR (dB) |
| --- |
| |
| # ESRGAN x4 INT8 (ExecuTorch + XNNPACK) |
|
|
| This is an INT8-quantized version of [ESRGAN RRDBNet](https://github.com/xinntao/ESRGAN) optimized for edge deployment on ARM devices using [ExecuTorch](https://github.com/pytorch/executorch) with the XNNPACK backend. The model was quantized using PT2E static symmetric per-channel quantization and exported to the `.pte` format for efficient inference on ARM Cortex-A processors (AWS Graviton, mobile ARM, embedded). |
|
|
| The PSNR-oriented weights (`RRDB_PSNR_x4.pth`) from [kadirnar/RRDB_PSNR_x4](https://huggingface.co/kadirnar/RRDB_PSNR_x4) are used β the L1-trained high-PSNR variant whose metrics are directly comparable to standard SR benchmark figures. |
|
|
| ## Key Highlights |
|
|
| Compared to the FP32 baseline: |
|
|
| - **2.76x smaller** β 64.07 MB to 23.18 MB |
| - **2.44x faster** β 1763 ms to 724 ms on AWS Graviton (Neoverse-V2) |
| - **Minimal quality loss** β β0.22 dB PSNR on Urban100 |
|
|
| ## Model Details |
|
|
| ### Model Description |
|
|
| Quantized version of ESRGAN (Enhanced Super-Resolution Generative Adversarial Network) with a 23-block RRDBNet backbone. The model performs 4x bicubic super-resolution on urban scene images, optimized for efficient edge inference via INT8 quantization and the ExecuTorch runtime. |
|
|
| - **Developed by:** Xintao Wang et al. (ESRGAN), Marvik AI (quantization & optimization) |
| - **Model type:** Super Resolution (4x upscaling) |
| - **License:** Apache-2.0 |
| - **Base model:** [ESRGAN RRDBNet](https://github.com/xinntao/ESRGAN) β quantized, not finetuned |
|
|
| ### Model Sources |
|
|
| - **Repository:** https://github.com/xinntao/ESRGAN |
| - **Pretrained weights:** https://huggingface.co/kadirnar/RRDB_PSNR_x4 |
|
|
| ## How to Get Started with the Model |
|
|
| ### Install dependencies |
|
|
| ```bash |
| pip install executorch torch torchvision pillow numpy |
| ``` |
|
|
| ### Download the model |
|
|
| ```python |
| from huggingface_hub import hf_hub_download |
| |
| model_path = hf_hub_download( |
| repo_id="Arm/esrgan-int8-xnnpack-executorch-graviton-g4", |
| filename="esrgan-x4-int8-executorch.pte", |
| ) |
| ``` |
|
|
| ### Run inference |
|
|
| ```bash |
| python example.py |
| ``` |
|
|
| Or use the core inference loop directly: |
|
|
| ```python |
| from executorch.runtime import Runtime |
| from PIL import Image |
| from torchvision import transforms |
| |
| # Load model |
| runtime = Runtime.get() |
| program = runtime.load_program("esrgan-x4-int8-executorch.pte") |
| method = program.load_method("forward") |
| |
| # Preprocess (no normalization β model expects raw [0, 1] values) |
| image = Image.open("input.jpg").convert("RGB") |
| to_tensor = transforms.ToTensor() |
| input_tensor = to_tensor(image).unsqueeze(0) # [1, 3, H, W] |
| |
| # Run a single 128x128 tile |
| tile = input_tensor[:, :, :128, :128] |
| outputs = method.execute([tile]) |
| # outputs[0] shape: [1, 3, 512, 512] β the 4x super-resolved tile |
| # See example.py for full tiled inference on arbitrary-size images |
| ``` |
|
|
| ## Evaluation |
|
|
| ### Testing Data, Factors & Metrics |
|
|
| #### Testing Data |
|
|
| Evaluated on 100 images from Urban100 (validation split). Urban100 is a standard super-resolution benchmark consisting of urban scenes with repetitive structures, specifically chosen to challenge SR models on fine detail reconstruction. |
|
|
| #### Metrics |
|
|
| - **PSNR (Peak Signal-to-Noise Ratio, dB)** β measures pixel-level fidelity between the super-resolved output and the ground-truth high-resolution image; higher is better |
| - **SSIM (Structural Similarity Index)** β measures perceived structural similarity; higher is better |
|
|
| ### Results |
|
|
| #### Quality |
|
|
| | Metric | FP32 (Original) | INT8 (Optimized) | Delta | |
| |:---|:---:|:---:|:---:| |
| | PSNR (dB) | 27.03 | 26.81 | β0.22 dB | |
| | SSIM | 0.8176 | 0.8109 | β0.007 | |
|
|
| #### Efficiency |
|
|
| | Metric | FP32 (Original) | INT8 (Optimized) | Improvement | |
| |:---|:---:|:---:|:---:| |
| | Model Size (.pte) | 64.07 MB | 23.18 MB | **2.76x smaller** | |
| | Graviton Latency (mean) | 1763 ms | 724 ms | **2.44x faster** | |
| | Graviton Latency (p50) | 1763 ms | 704 ms | **2.50x faster** | |
| | Graviton Latency (p90) | 1765 ms | 780 ms | **2.26x faster** | |
| | Graviton Cold Start | 66.3 ms | 36.7 ms | **1.81x faster** | |
|
|
| *Latency measured on AWS Graviton G4 (Neoverse-V2, 16 cores) with ExecuTorch 1.1.0, XNNPACK + KleidiAI backend, batch size 1, 10 warmup runs + 100 measurement runs.* |
|
|
| ## Technical Specifications |
|
|
| ### Objective |
|
|
| 4x single-image super-resolution from bicubic-degraded low-resolution inputs, targeting urban scenes with repetitive structures. |
|
|
| ### Quantization |
|
|
| - **Method**: PT2E Static Quantization via XNNPACKQuantizer |
| - **Precision**: INT8 symmetric, per-channel |
| - **Backend**: XNNPACK |
| - **Layers kept in FP32**: upsampling layers (`upconv1`, `upconv2`, `HRconv`, `conv_last`) and the first 3 RRDB body blocks (`body.0`β`body.2`) to preserve output fidelity |
| - **Calibration**: 100 images from Urban100 (random subset, patch mode) |
|
|
| ### Export Pipeline |
|
|
| 1. Load pretrained FP32 RRDBNet with PSNR-oriented weights from HuggingFace (`kadirnar/RRDB_PSNR_x4`) |
| 2. Capture model graph via `torch.export` at fixed 128Γ128 tile size |
| 3. Insert quantization observers (XNNPACKQuantizer, skipping upsampling + first 3 body blocks) |
| 4. Calibrate with 100 Urban100 patches |
| 5. Convert observers to Q/DQ pairs |
| 6. Post-quantization graph surgery (remove spurious Q/DQ pairs from `cat` nodes in FP32 body blocks) |
| 7. Export to ExecuTorch `.pte` with XNNPACK backend |
|
|
| ### Preprocessing |
|
|
| | Property | Value | |
| |---|---| |
| | Input shape | `[1, 3, 128, 128]` (BCHW, single tile) | |
| | Data type | float32 | |
| | Value range | [0.0, 1.0] | |
| | Color space | RGB | |
|
|
| **Steps**: |
| 1. Convert PIL image to float32 tensor via `ToTensor()` (maps [0, 255] β [0, 1]) |
| 2. For images larger than 128Γ128: split into overlapping 128Γ128 tiles with 8-pixel overlap; each tile is processed independently |
|
|
| **Normalization**: None required β the model expects raw [0, 1] pixel values. |
|
|
| ### Postprocessing |
|
|
| | Property | Value | |
| |---|---| |
| | Output shape | `[1, 3, 512, 512]` per tile (4x upscaled) | |
| | Output format | Float32 RGB tensor, values may exceed [0, 1] before clamping | |
|
|
| **Steps**: |
| 1. Clamp output tensor to [0, 1] |
| 2. For tiled inputs: accumulate tile outputs into a full-resolution canvas, averaging overlapping regions |
| 3. Convert to uint8 for saving (`* 255`, round, cast) |
|
|
| ## Known Limitations |
|
|
| - Evaluated on 100 images from Urban100 β a benchmark focused on urban/architectural scenes; performance on natural landscapes or faces may differ |
| - Fixed tile size of 128Γ128: images must be at least 128Γ128; the tiling inference handles arbitrary sizes automatically |
| - Input images are not aspect-ratio padded β tiles are extracted at stride `tile_size - overlap` with edge tiles placed at the image boundary |
| - XNNPACK operator coverage is approximately 49%: upsampling, LeakyReLU, and some transpose operations fall back to non-XNNPACK kernels; latency figures reflect this mixed execution |
|
|