File size: 7,565 Bytes
b4ed648
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
---
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