# Improve image resolution with machine learning super resolution on mobile
Learn how to build an application to improve image resolution using ONNX Runtime Mobile, with a model that includes pre and post processing.
You can use this tutorial to build the application for Android or iOS.
The application takes an image input, performs the super resolution operation when the button is clicked and displays the image with improved resolution below, as in the following screenshot.

## Contents
{: .no_toc }
* TOC placeholder
{:toc}
## Prepare the model
The machine learning model used in this tutorial is based on the one used in the PyTorch tutorial referenced at the bottom of this page.
We provide a convenient Python script that exports the PyTorch model into ONNX format and adds pre and post processing.
1. Before running this script, install the following python packages:
A note on versions: the best super resolution results are achieved with ONNX opset 18 (with its support for the Resize operator with anti-aliasing), which is supported by onnx 1.13.0 and onnxruntime 1.14.0 and later. The onnxruntime-extensions package is a pre-release version. The release version will be available soon.
If you load the two models into [netron](https://netron.app/) you can see the difference in inputs and outputs between the two. The first two images below show the original model with its inputs being batches of channel data, and the second two show the inputs and outputs being the image bytes.




Now it's time to write the application code.
## Android app
### Pre-requisites
* Android Studio Dolphin 2021.3.1 Patch + (installed on Mac/Windows/Linux)
* Android SDK 29+
* Android NDK r22+
* An Android device or an Android Emulator
### Sample code
You can find full [source code for the Android super resolution app](https://github.com/microsoft/onnxruntime-inference-examples/tree/main/mobile/examples/super_resolution/android) in GitHub.
To run the app from source code, clone the above repo and load the `build.gradle` file into Android studio, build and run!
To build the app, step by step, follow the following sections.
### Code from scratch
#### Setup project
Create a new project for Phone and Tablet in Android studio and select the blank template. Call the application `super_resolution` or similar.
#### Dependencies
Add the following dependencies to the app `build.gradle`:
Create a folder called `raw` in the `src/main/res` folder and move or copy the ONNX model into the raw folder.
2. Add the test image as an asset
Create a folder called `assets` in the main project folder and copy the image that you want to run super resolution on into that folder with the filename of `test_superresolution.png`
#### Main application class code
Create a file called MainActivity.kt and add the following pieces of code to it.
2. Create the main activity class and add the class variables
```kotlin
class MainActivity : AppCompatActivity() {
private var ortEnv: OrtEnvironment = OrtEnvironment.getEnvironment()
private lateinit var ortSession: OrtSession
private var inputImage: ImageView? = null
private var outputImage: ImageView? = null
private var superResolutionButton: Button? = null
...
}
```
3. Add the `onCreate()` method
This is where we initialize the [ONNX Runtime session](https://onnxruntime.ai/docs/api/java/ai/onnxruntime/OrtSession.html). A session holds a reference to the model used to perform inference in the application. It also takes a session options parameter, which is where you can specify different execution providers (hardware accelerators such as NNAPI). In this case, we default to running on CPU. We do however register the custom op library where the image encoding and decoding operators at the input and output of the model are found.
```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
This method reads a test image from the assets folder. Currently it reads a fixed image built into the application. The sample will soon be extended to read the image directly from the camera or the camera roll.
```kotlin
private fun readInputImage(): InputStream {
return assets.open("test_superresolution.png")
}
```
8. Add the method to perform inference
This method calls the method that is at the heart of the application: `SuperResPerformer.upscale()`, which is the method that runs inference on the model. The code for this is shown in the next section.
```kotlin
private fun performSuperResolution(ortSession: OrtSession) {
var superResPerformer = SuperResPerformer()
var result = superResPerformer.upscale(readInputImage(), ortEnv, ortSession)
updateUI(result);
}
```
9. Add the TAG object
```kotlin
companion object {
const val TAG = "ORTSuperResolution"
}
```
#### Model inference class code
Create a file called `SuperResPerformer.kt` and add the following snippets of code to it.
1. Add imports
```kotlin
import ai.onnxruntime.OnnxJavaType
import ai.onnxruntime.OrtSession
import ai.onnxruntime.OnnxTensor
import ai.onnxruntime.OrtEnvironment
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import java.io.InputStream
import java.nio.ByteBuffer
import java.util.*
```
2. Create a result class
```kotlin
internal data class Result(
var outputBitmap: Bitmap? = null
) {}
```
3. Create the super resolution performer class
This class and its main function `upscale` are where most of the calls to ONNX Runtime live.
* The [OrtEnvironment](https://onnxruntime.ai/docs/api/java/ai/onnxruntime/OrtEnvironment.html) singleton maintains properties of the environment and configured logging levels
* [OnnxTensor.createTensor()](https://onnxruntime.ai/docs/api/java/ai/onnxruntime/OnnxTensor.html#createTensor(ai.onnxruntime.OrtEnvironment,java.nio.ByteBuffer,long%5B%5D,ai.onnxruntime.OnnxJavaType)) is used to create a tensor made up of the input image bytes, suitable as input to the model
* [OnnxJavaType.UINT8](https://onnxruntime.ai/docs/api/java/ai/onnxruntime/OnnxJavaType.html) is the data type of the ByteBuffer of the input tensor
* [OrtSession.run()](https://onnxruntime.ai/docs/api/java/ai/onnxruntime/OrtSession.html#run(java.util.Map)) run the inference (prediction) on the model to get the output upscaled image
```kotlin
internal class SuperResPerformer(
) {
fun upscale(inputStream: InputStream, ortEnv: OrtEnvironment, ortSession: OrtSession): Result {
// Step 2: get the shape of the byte array and make ort tensor
val shape = longArrayOf(rawImageBytes.size.toLong())
val inputTensor = OnnxTensor.createTensor(
ortEnv,
ByteBuffer.wrap(rawImageBytes),
shape,
OnnxJavaType.UINT8
)
inputTensor.use {
// Step 3: call ort inferenceSession run
val output = ortSession.run(Collections.singletonMap("image", inputTensor))
// Step 4: output analysis
output.use {
val rawOutput = (output?.get(0)?.value) as ByteArray
val outputImageBitmap =
byteArrayToBitmap(rawOutput)
// Step 5: set output result
result.outputBitmap = outputImageBitmap
}
}
return result
}
```
### Build and run the app
Within Android studio:
* Select Build -> Make Project
* Run -> app
The app runs in the device emulator. Connect to your Android device to run the app on device.
## iOS app
### Pre-requisites
* Install Xcode 13.0 and above (preferably latest version)
* An iOS device or iOS simulator
* Xcode command line tools `xcode-select --install`
* CocoaPods `sudo gem install cocoapods`
* A valid Apple Developer ID (if you are planning to run on device)
### Sample code
You can find full [source code for the iOS super resolution app](https://github.com/microsoft/onnxruntime-inference-examples/tree/main/mobile/examples/super_resolution/ios) in GitHub.