--- library_name: executorch display_name: ESRGAN x4 INT8 — ExecuTorch + XNNPACK 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