1# iOS quickstart
2
3This tutorial provides a simple iOS mobile application to classify images using
4the iOS device camera. In this tutorial, you will download the demo application
5from the Tensorflow repository, build it on your computer, and install it on
6your iOS Device. You will also learn how to customize the application to suit
7your requirements.
8
9## Prerequisites
10
11*   You must have [Xcode](https://developer.apple.com/xcode/) installed and have
12    a valid Apple Developer ID, and have an iOS device set up and linked to your
13    developer account with all of the appropriate certificates. For these
14    instructions, we assume that you have already been able to build and deploy
15    an app to an iOS device with your current developer environment.
16
17*   The demo app requires a camera and must be executed on a real iOS device.
18    You can build it and run with the iPhone Simulator but it won't have any
19    camera information to classify.
20
21*   You don't need to build the entire TensorFlow library to run the demo, but
22    you will need to clone the TensorFlow repository if you haven't already:
23
24        git clone https://github.com/tensorflow/tensorflow
25        cd tensorflow
26
27*   You'll also need the Xcode command-line tools:
28
29        xcode-select --install
30
31    If this is a new install, you will need to run the Xcode application once to
32    agree to the license before continuing.
33
34*   Install CocoaPods if you don't have it:
35
36        sudo gem install cocoapods
37
38### Step 1. Clone the TensorFlow source code
39
40First, we clone the GitHub repository on the computer in a folder to get the
41demo application.
42
43```
44git clone https://github.com/tensorflow/tensorflow
45```
46
47### Step 2. Download required dependencies
48
49Execute the shell script to download the model files used by the demo app (this
50is done from inside the cloned directory):
51
52```
53    tensorflow/lite/examples/ios/download_models.sh
54```
55
56Run the following command to install TensorFlow Lite pod:
57
58```
59    cd tensorflow/lite/examples/ios/camera
60    pod install
61```
62
63If you have installed this pod before and that command doesn't work, try
64
65```
66    pod repo update
67```
68
69### Step 3. Build the XCode project
70
71Open the `tflite_camera_example.xcworkspace` project file generated in the last
72step:
73
74```
75    open tflite_camera_example.xcworkspace
76```
77
78Under `Project navigator -> tflite_camera_example -> Targets ->
79tflite_camera_example -> General` change the bundle identifier by pre-pending
80your name:
81
82![pre-pend your name to the bundle identifier](../images/ios/bundle_identifier.png)
83
84Plug in your iOS device. Note the app must be executed with a real device with
85camera. Select the iOS device from the drop-down menu.
86
87![Device selection](../images/ios/device_selection.png)
88
89Click the "Run" button to build and run the app
90
91![Build and execute](../images/ios/build_and_execute.png)
92
93Note that as mentioned earlier, you must already have a device set up and linked
94to your Apple Developer account in order to deploy the app on a device.
95
96You'll have to grant permissions for the app to use the device's camera. Point
97the camera at various objects and enjoy seeing how the model classifies things!
98
99## Understanding iOS App Code
100
101### Get camera input
102
103The main logic of this app is in the Objective C++ source file
104`tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm`.
105
106The `setupAVCapture` method constructs a `AVCaptureSession` and set itself as a
107delegate. The `captureOutput:didOutputSampleBuffer:fromConnection:` method is
108called for every captured frame. It calls `runModelOnFrame` to run the model for
109every frame.
110
111### Create an interpreter
112
113To create the interpreter, we need to load the model file. The following code
114will load a model and create an interpreter.
115
116```
117model = tflite::FlatBufferModel::BuildFromFile([graph_path UTF8String]);
118```
119
120Behind the scenes, the model is loaded as a memory-mapped file. It offers faster
121load times and reduce the dirty pages in memory.
122
123Construct a `BuiltinOpResolver` to use the TensorFlow Lite buildin ops. Then,
124create the interpreter object using `InterpreterBuilder` that takes the model
125file as argument as shown below.
126
127```
128tflite::ops::builtin::BuiltinOpResolver resolver;
129tflite::InterpreterBuilder(*model, resolver)(&interpreter);
130```
131
132### Obtain the input buffer
133
134By default, the app uses quantized model since it's smaller and faster. The
135buffer is a raw pointer to an array of 8 bit unsigned integers (`uint8_t`). The
136following code obtains the input buffer from the interpreter:
137
138```
139// Get the index of first input tensor.
140int input_tensor_index = interpreter->inputs()[0];
141// Get the pointer to the input buffer.
142uint8_t* buffer = interpreter->typed_tensor<uint8_t>(input_tensor_index);
143```
144
145Throughout this document, it's assumed a quantized model is used.
146
147### Pre-process of bitmap image
148
149The MobileNet model we're using takes 224x224x3 inputs, where the dimensions are
150width, height, and colors (RGB). The images returned from `AVCaptureSession` is
151bigger, and has 4 color channels (RGBA).
152
153Many image classification models (like MobileNet) take fixe-sized inputs. It's
154required to scale or crop the image before feeding it into the model, and change
155the channels from RGBA to RGB.
156
157The code to pre-process the images is in `ProcessInputWithQuantizedModel`
158function in
159`tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm`. It's a
160simple implementation for nearest neighbor color sampling, and it only copies
161the first 3 bytes for each pixel.
162
163```
164void ProcessInputWithQuantizedModel(
165    uint8_t* input, uint8_t* output, int image_width, int image_height, int image_channels) {
166  for (int y = 0; y < wanted_input_height; ++y) {
167    uint8_t* out_row = output + (y * wanted_input_width * wanted_input_channels);
168    for (int x = 0; x < wanted_input_width; ++x) {
169      const int in_x = (y * image_width) / wanted_input_width;
170      const int in_y = (x * image_height) / wanted_input_height;
171      uint8_t* in_pixel = input + (in_y * image_width * image_channels) + (in_x * image_channels);
172      uint8_t* out_pixel = out_row + (x * wanted_input_channels);
173      for (int c = 0; c < wanted_input_channels; ++c) {
174        out_pixel[c] = in_pixel[c];
175      }
176    }
177  }
178}
179```
180
181Note the code is preprocessing and preparing the model input from the camera
182data. Therefore the first parameter `input` should be the camera buffer. The
183second parameter `output` should be the buffer of model input.
184
185### Run inference and obtain output buffer
186
187After preprocessing and filling the data into the input buffer of the
188interpreter, it's really easy to run the interpreter:
189
190```
191if (interpreter->Invoke() != kTfLiteOk) {
192  NSLog("Failed to invoke!");
193}
194```
195
196The result is stored in the output tensor buffer of the interpreter. The
197following code obtains the pointer to the buffer:
198
199```
200// Get the index of first output tensor.
201const int output_tensor_index = interpreter->outputs()[0];
202// Get the pointer to the output buffer.
203uint8_t* buffer = interpreter->typed_tensor<uint8_t>(output_tensor_index);
204```
205
206### Post-process values
207
208The output buffer contains an array of `uint8_t`, and the value range is 0-255.
209We need to convert the value to float to get the probabilities with value range
2100.0-1.0. The formula of the quantization value mapping is:
211
212    float_value = (quantized_value - zero_point) * scale
213
214The following code converts quantized values back to float values, using the
215quantizaiton parameters in tensors:
216
217```
218uint8_t* quantized_output = interpreter->typed_output_tensor<uint8_t>(0);
219int32_t zero_point = input_tensor->params.zero_point;
220float scale = input_tensor->params.scale;
221float output[output_size];
222for (int i = 0; i < output_size; ++i) {
223  output[i] = (quantized_output[i] - zero_point) * scale;
224}
225```
226
227Finally, we find the best set of classifications by storing them in a priority
228queue based on their confidence scores. See the `GetTopN` function in
229`tensorflow/lite/examples/ios/camera/CameraExampleViewController.mm`.
230