Skip to content

Commit 3c90957

Browse files
brucejlin1Pat Mellon
authored andcommitted
Merge pull request #267 from IvanKobzarev/ik_android_tutorial_fixes_1007
[mobile][android] Tutorial corrections and aligning with the android api
2 parents c14ebb8 + 2e1bce4 commit 3c90957

File tree

1 file changed

+73
-72
lines changed

1 file changed

+73
-72
lines changed

_mobile/android.md

Lines changed: 73 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ published: true
1010

1111
# Android
1212

13-
## Quick start with a HelloWorld example
13+
## Quickstart with a HelloWorld Example
1414

15-
[HelloWorld](https://github.com/pytorch/android-demo-app/tree/master/HelloWorldApp) is a simple image classification application that demonstrates how to use PyTorch android api.
15+
[HelloWorld](https://github.com/pytorch/android-demo-app/tree/master/HelloWorldApp) is a simple image classification application that demonstrates how to use PyTorch Android API.
1616
This application runs TorchScript serialized TorchVision pretrained resnet18 model on static image which is packaged inside the app as android asset.
1717

18-
#### 1. Model preparation
18+
#### 1. Model Preparation
1919

2020
Let’s start with model preparation. If you are familiar with PyTorch, you probably should already know how to train and save your model. In case you don’t, we are going to use a pre-trained image classification model(Resnet18), which is packaged in [TorchVision](https://pytorch.org/docs/stable/torchvision/index.html).
2121
To install it, run the command below:
@@ -44,13 +44,13 @@ More details about TorchScript you can find in [tutorials on pytorch.org](https:
4444
git clone https://github.com/pytorch/android-demo-app.git
4545
cd HelloWorldApp
4646
```
47-
If [android sdk]() and [android ndk]() are already installed you can install this application to the connected android device or emulator with:
47+
If [Android SDK]() and [Android NDK]() are already installed you can install this application to the connected android device or emulator with:
4848
```
4949
./gradlew installDebug
5050
```
5151

5252
We recommend you to open this project in [Android Studio](https://developer.android.com/studio),
53-
in that case you will be able to install android ndk and android sdk using Android Studio UI.
53+
in that case you will be able to install Android NDK and Android SDK using Android Studio UI.
5454

5555
#### 3. Gradle dependencies
5656

@@ -66,15 +66,15 @@ dependencies {
6666
implementation 'org.pytorch:pytorch_android_torchvision:1.3.0'
6767
}
6868
```
69-
Where `org.pytorch:pytorch_android` is the main dependency with pytorch android api, including libtorch native library for all 4 android abis (armeabi-v7a, arm64-v8a, x86, x86_64).
69+
Where `org.pytorch:pytorch_android` is the main dependency with PyTorch Android API, including libtorch native library for all 4 android abis (armeabi-v7a, arm64-v8a, x86, x86_64).
7070
Further in this doc you can find how to rebuild it only for specific list of android abis.
7171

7272
`org.pytorch:pytorch_android_torchvision` - additional library with utility functions for converting `android.media.Image` and `android.graphics.Bitmap` to tensors.
7373

74-
#### 4. Reading static image from android asset
74+
#### 4. Reading image from Android Asset
7575

76-
All logic happens in [org.pytorch.helloworld.MainActivity](https://github.com/pytorch/android-demo-app/blob/master/HelloWorldApp/app/src/main/java/org/pytorch/helloworld/MainActivity.java#L31-L69).
77-
As a first step we read `image.jpg` to `android.graphics.Bitmap` using standard android api.
76+
All the logic happens in [`org.pytorch.helloworld.MainActivity`](https://github.com/pytorch/android-demo-app/blob/master/HelloWorldApp/app/src/main/java/org/pytorch/helloworld/MainActivity.java#L31-L69).
77+
As a first step we read `image.jpg` to `android.graphics.Bitmap` using the standard Android API.
7878
```
7979
Bitmap bitmap = BitmapFactory.decodeStream(getAssets().open("image.jpg"));
8080
```
@@ -85,30 +85,30 @@ Module module = Module.load(assetFilePath(this, "model.pt"));
8585
```
8686
`org.pytorch.Module` represents `torch::jit::script::Module` that can be loaded with `load` method specifying file path to the serialized to file model.
8787

88-
#### 6. Preparing input
88+
#### 6. Preparing Input
8989
```
9090
Tensor inputTensor = TensorImageUtils.bitmapToFloat32Tensor(bitmap,
9191
TensorImageUtils.TORCHVISION_NORM_MEAN_RGB, TensorImageUtils.TORCHVISION_NORM_STD_RGB);
9292
```
93-
`org.pytorch.torchvision.TensorImageUtils` is part of 'org.pytorch:pytorch_android_torchvision' library.
94-
`TensorImageUtils#bitmapToFloat32Tensor` method creates tensor in [torch vision format](https://pytorch.org/docs/stable/torchvision/models.html) using `android.graphics.Bitmap` as a source.
93+
`org.pytorch.torchvision.TensorImageUtils` is part of `org.pytorch:pytorch_android_torchvision` library.
94+
The `TensorImageUtils#bitmapToFloat32Tensor` method creates tensors in the [torchvision format](https://pytorch.org/docs/stable/torchvision/models.html) using `android.graphics.Bitmap` as a source.
9595

9696
> All pre-trained models expect input images normalized in the same way, i.e. mini-batches of 3-channel RGB images of shape (3 x H x W), where H and W are expected to be at least 224.
97-
> The images have to be loaded in to a range of [0, 1] and then normalized using mean = [0.485, 0.456, 0.406] and std = [0.229, 0.224, 0.225]
97+
> The images have to be loaded in to a range of `[0, 1]` and then normalized using `mean = [0.485, 0.456, 0.406]` and `std = [0.229, 0.224, 0.225]`
9898
99-
`inputTensor`'s shape is 1x3xHxW, where H and W are bitmap height and width appropriately.
99+
`inputTensor`'s shape is `1x3xHxW`, where `H` and `W` are bitmap height and width appropriately.
100100

101101
#### 7. Run Inference
102102

103103
```
104-
Tensor outputTensor = module.forward(IValue.tensor(inputTensor)).getTensor();
104+
Tensor outputTensor = module.forward(IValue.from(inputTensor)).toTensor();
105105
float[] scores = outputTensor.getDataAsFloatArray();
106106
```
107107

108108
`org.pytorch.Module.forward` method runs loaded module's `forward` method and gets result as `org.pytorch.Tensor` outputTensor with shape `1x1000`.
109109

110110
#### 8. Processing results
111-
It's content is retrieved using `org.pytorch.Tensor.getDataAsFloatArray()` method that returns java array of floats with scores for every image net class.
111+
Its content is retrieved using `org.pytorch.Tensor.getDataAsFloatArray()` method that returns java array of floats with scores for every image net class.
112112

113113
After that we just find index with maximum score and retrieve predicted class name from `ImageNetClasses.IMAGENET_CLASSES` array that contains all ImageNet classes.
114114

@@ -124,14 +124,16 @@ for (int i = 0; i < scores.length; i++) {
124124
String className = ImageNetClasses.IMAGENET_CLASSES[maxScoreIdx];
125125
```
126126

127-
In the following sections you can find detailed explanation of pytorch android api, code walk through for bigger [demo application](https://github.com/pytorch/android-demo-app/tree/master/PyTorchDemoApp), implementation details of api and how to customize and build it from the source.
127+
In the following sections you can find detailed explanations of PyTorch Android API, code walk through for a bigger [demo application](https://github.com/pytorch/android-demo-app/tree/master/PyTorchDemoApp),
128+
implementation details of the API, how to customize and build it from source.
128129

129-
## Pytorch demo app
130+
## PyTorch Demo Application
130131

131-
Bigger example of application that does image classification from android camera output and text classification you can find in the [same github repo](https://github.com/pytorch/android-demo-app/tree/master/PyTorchDemoApp).
132+
We have also created another more complex PyTorch Android demo application that does image classification from camera output and text classification in the [same github repo](https://github.com/pytorch/android-demo-app/tree/master/PyTorchDemoApp).
132133

133-
To get device camera output in it uses [android cameraX api](https://developer.android.com/training/camerax
134-
). All the logic that works with CameraX is separated to [`org.pytorch.demo.vision.AbstractCameraXActivity`](https://github.com/pytorch/android-demo-app/blob/master/PyTorchDemoApp/app/src/main/java/org/pytorch/demo/vision/AbstractCameraXActivity.java) class.
134+
To get device camera output it uses [Android CameraX API](https://developer.android.com/training/camerax
135+
).
136+
All the logic that works with CameraX is separated to [`org.pytorch.demo.vision.AbstractCameraXActivity`](https://github.com/pytorch/android-demo-app/blob/master/PyTorchDemoApp/app/src/main/java/org/pytorch/demo/vision/AbstractCameraXActivity.java) class.
135137

136138

137139
```
@@ -158,13 +160,13 @@ void setupCameraX() {
158160
void analyzeImage(android.media.Image, int rotationDegrees)
159161
```
160162

161-
Where `analyzeImage` method processes camera output, `android.media.Image`.
163+
Where the `analyzeImage` method process the camera output, `android.media.Image`.
162164

163-
It uses aforementioned [`TensorImageUtils.imageYUV420CenterCropToFloat32Tensor`](https://github.com/pytorch/pytorch/blob/master/android/pytorch_android_torchvision/src/main/java/org/pytorch/torchvision/TensorImageUtils.java#L90) method to convert `android.media.Image` in `YUV420` format to input tensor.
165+
It uses the aforementioned [`TensorImageUtils.imageYUV420CenterCropToFloat32Tensor`](https://github.com/pytorch/pytorch/blob/master/android/pytorch_android_torchvision/src/main/java/org/pytorch/torchvision/TensorImageUtils.java#L90) method to convert `android.media.Image` in `YUV420` format to input tensor.
164166

165-
After getting predicted scores from the model it [finds top K classes](https://github.com/pytorch/android-demo-app/blob/master/PyTorchDemoApp/app/src/main/java/org/pytorch/demo/vision/ImageClassificationActivity.java#L153-L161) with the highest scores and shows on the UI.
167+
After getting predicted scores from the model it finds top K classes with the highest scores and shows on the UI.
166168

167-
## Building pytorch android from source
169+
## Building PyTorch Android from Source
168170

169171
In some cases you might want to use a local build of pytorch android, for example you may build custom libtorch binary with another set of operators or to make local changes.
170172

@@ -175,19 +177,22 @@ cd pytorch
175177
sh ./scripts/build_pytorch_android.sh
176178
```
177179

178-
Its workflow contains several steps:
179-
1. Builds libtorch for android for all 4 android abis (armeabi-v7a, arm64-v8a, x86, x86_64)
180-
2. Creates symbolic links to the results of those builds:
180+
The workflow contains several steps:
181+
182+
1\. Build libtorch for android for all 4 android abis (armeabi-v7a, arm64-v8a, x86, x86_64)
183+
184+
2\. Create symbolic links to the results of those builds:
181185
`android/pytorch_android/src/main/jniLibs/${abi}` to the directory with output libraries
182-
`android/pytorch_android/src/main/cpp/libtorch_include/${abi}` to the directory with headers. These directories are used to build `libpytorch.so` library that will be loaded on android device.
183-
3. And finally runs `gradle` in `android/pytorch_android` directory with task `assembleRelease`
186+
`android/pytorch_android/src/main/cpp/libtorch_include/${abi}` to the directory with headers. These directories are used to build `libpytorch.so` library that will be loaded on android device.
187+
188+
3\. And finally run `gradle` in `android/pytorch_android` directory with task `assembleRelease`
184189

185-
Script requires that android sdk, android ndk and gradle are installed.
190+
Script requires that Android SDK, Android NDK and gradle are installed.
186191
They are specified as environment variables:
187192

188-
`ANDROID_HOME` - path to [android sdk](https://developer.android.com/studio/command-line/sdkmanager.html)
193+
`ANDROID_HOME` - path to [Android SDK](https://developer.android.com/studio/command-line/sdkmanager.html)
189194

190-
`ANDROID_NDK` - path to [android ndk](https://developer.android.com/studio/projects/install-ndk)
195+
`ANDROID_NDK` - path to [Android NDK](https://developer.android.com/studio/projects/install-ndk)
191196

192197
`GRADLE_HOME` - path to [gradle](https://gradle.org/releases/)
193198

@@ -226,7 +231,7 @@ dependencies {
226231
}
227232
```
228233

229-
At the moment for the case of using aar files directly we need additional configuration due to packaging specific (libfbjni.so is packaged in both pytorch_android_fbjni.aar and pytorch_android.aar).
234+
At the moment for the case of using aar files directly we need additional configuration due to packaging specific (`libfbjni.so` is packaged in both `pytorch_android_fbjni.aar` and `pytorch_android.aar`).
230235
```
231236
packagingOptions {
232237
pickFirst "**/libfbjni.so"
@@ -235,52 +240,49 @@ packagingOptions {
235240

236241
## API Details
237242

238-
Main part of java api includes 3 classes:
243+
Main part of java API includes 3 classes:
239244
```
240245
org.pytorch.Module
241246
org.pytorch.IValue
242247
org.pytorch.Tensor
243248
```
244249

245-
If the reader is familiar with pytorch python api, we can think that org.pytorch.Tensor represents torch.tensor, org.pytorch.Module torch.Module<?>, while org.pytorch.IValue represents value of TorchScript variable, supporting all its types. ( https://pytorch.org/docs/stable/jit.html#types )
250+
If the reader is familiar with PyTorch Python API, we can think of `org.pytorch.Tensor` representing `torch.tensor`, `org.pytorch.Module` representing `torch.Module`, and `org.pytorch.IValue` representing the value of the TorchScript variable, supporting all its [types](https://pytorch.org/docs/stable/jit.html#types).
246251

247-
### org.pytorch.Tensor (Tensor)
248-
[github](https://github.com/pytorch/pytorch/blob/master/android/pytorch_android/src/main/java/org/pytorch/Tensor.java)
252+
### [`org.pytorch.Tensor`]((https://github.com/pytorch/pytorch/blob/master/android/pytorch_android/src/main/java/org/pytorch/Tensor.java))
249253

250254
Tensor supports dtypes `uint8, int8, float32, int32, float64, int64`.
251255
Tensor holds data in DirectByteBuffer of proper type with native bit order.
252256

253257
To create a Tensor user can use one of the factory methods:
254258
```
255-
Tensor newUInt8Tensor(long[] shape, ByteBuffer data)
256-
Tensor newUInt8Tensor(long[] shape, byte[] data)
257-
258-
Tensor newInt8Tensor(long[] shape, ByteBuffer data)
259-
Tensor newInt8Tensor(long[] shape, byte[] data)
260-
261-
Tensor newFloat32Tensor(long[] shape, FloatBuffer data)
262-
Tensor newFloat32Tensor(long[] shape, float[] data)
259+
Tensor fromBlobUnsigned(ByteBuffer data, long[] shape)
260+
Tensor fromBlobUnsigned(byte[] data, long[] shape)
263261
262+
Tensor fromBlob(ByteBuffer data, long[] shape)
263+
Tensor fromBlob(byte[] data, long[] shape)
264264
265-
Tensor newInt32Tensor(long[] shape, IntBuffer data)
266-
Tensor newInt32Tensor(long[] shape, int[] data)
265+
Tensor fromBlob(FloatBuffer data, long[] shape)
266+
Tensor fromBlob(float[] data, long[] shape)
267267
268-
Tensor newFloat64Tensor(long[] shape, DoubleBuffer data)
269-
Tensor newFloat64Tensor(long[] shape, double[] data)
268+
Tensor fromBlob(IntBuffer data, long[] shape)
269+
Tensor fromBlob(int[] data, long[] shape)
270270
271+
Tensor fromBlob(DoubleBuffer data, long[] shape)
272+
Tensor fromBlob(double[] data, long[] shape)
271273
272-
Tensor newInt64Tensor(long[] shape, LongBuffer data)
273-
Tensor newInt64Tensor(long[] shape, long[] data)
274+
Tensor fromBlob(LongBuffer data, long[] shape)
275+
Tensor fromBlob(long[] data, long[] shape)
274276
```
275277
Where the first parameter `long[] shape` is shape of the Tensor as array of longs.
276278

277-
Content of the Tensor can be provided either as (a) java array or (b) as java.nio.DirectByteBuffer of proper type with native bit order.
279+
Content of the Tensor can be provided either as (a) java array or (b) as `java.nio.DirectByteBuffer` of proper type with native bit order.
278280

279-
In case of (a) proper DirectByteBuffer will be created internally. (b) case has an advantage that user can keep the reference to DirectByteBuffer and change its content in future for the next run, avoiding allocation of DirectByteBuffer for repeated runs.
281+
In case of (a) proper `DirectByteBuffer` will be created internally. (b) case has an advantage that user can keep the reference to DirectByteBuffer and change its content in future for the next run, avoiding allocation of DirectByteBuffer for repeated runs.
280282

281-
Java’s primitive type byte is signed and java does not have unsigned 8 bit type. For dtype=uint8 api uses byte that will be reinterpretted as uint8 on native side. On java side unsigned value of byte can be read as (byte & 0xFF).
283+
Java’s primitive type byte is signed and java does not have unsigned 8 bit type. For dtype=uint8 java API uses java primitive `byte` that will be reinterpreted as uint8 on native side. On java side unsigned value of byte can be read as `byte & 0xFF`.
282284

283-
#### Tensor content layout
285+
#### Tensor Content Layout
284286

285287
Tensor content is represented as a one dimensional array (buffer),
286288
where the first element has all zero indexes T\[0, ... 0\].
@@ -293,17 +295,17 @@ Tensor has methods to check its dtype:
293295
```
294296
int dtype()
295297
```
296-
That returns one of the dtype codes:
298+
That returns one of the `DType` enum element:
297299
```
298-
Tensor.DTYPE_UINT8
299-
Tensor.DTYPE_INT8
300-
Tensor.DTYPE_INT32
301-
Tensor.DTYPE_FLOAT32
302-
Tensor.DTYPE_INT64
303-
Tensor.DTYPE_FLOAT64
300+
DType.UINT8
301+
DType.INT8
302+
DType.INT32
303+
DType.FLOAT32
304+
DType.INT64
305+
DType.FLOAT64
304306
```
305307

306-
The data of Tensor can be read as java array:
308+
The data of Tensor can be read as a java array:
307309
```
308310
byte[] getDataAsUnsignedByteArray()
309311
byte[] getDataAsByteArray()
@@ -312,18 +314,17 @@ long[] getDataAsLongArray()
312314
float[] getDataAsFloatArray()
313315
double[] getDataAsDoubleArray()
314316
```
315-
These methods throw IllegalStateException if called for inappropriate dtype.
317+
These methods throw `IllegalStateException` if called for inappropriate dtype.
316318

317-
### org.pytorch.IValue (IValue)
318-
[github](https://github.com/pytorch/pytorch/blob/master/android/pytorch_android/src/main/java/org/pytorch/IValue.java)
319+
### [`org.pytorch.IValue`](https://github.com/pytorch/pytorch/blob/master/android/pytorch_android/src/main/java/org/pytorch/IValue.java)
319320

320-
IValue represents a TorchScript variable that can be one of the supported (by torchscript) types ( https://pytorch.org/docs/stable/jit.html#types ). IValue is a tagged union. For every supported type it has a factory method, method to check the type and a getter method to retrieve a value.
321-
Getters throw IllegalStateException if called for inappropriate type.
321+
IValue represents a TorchScript variable that can be one of the supported (by TorchScript) [types](https://pytorch.org/docs/stable/jit.html#types).
322+
`IValue` is a tagged union. For every supported type it has a factory method, method to check the type and a getter method to retrieve a value.
323+
Getters throw `IllegalStateException` if called for inappropriate type.
322324

323-
### org.pytorch.Module (Module)
324-
[github](https://github.com/pytorch/pytorch/blob/master/android/pytorch_android/src/main/java/org/pytorch/Module.java)
325+
### [`org.pytorch.Module`](https://github.com/pytorch/pytorch/blob/master/android/pytorch_android/src/main/java/org/pytorch/Module.java)
325326

326-
Module is a wrapper of torch.jit.ScriptModule (`torch::jit::script::Module` in pytorch c++ api) which can be constructed with factory method load providing absolute path to the file with serialized TorchScript.
327+
Module is a wrapper of torch.jit.ScriptModule (`torch::jit::script::Module` in PyTorch C++ API) which can be constructed with the factory method `load` providing absolute path to the file with serialized TorchScript.
327328
```
328329
IValue IValue.runMethod(String methodName, IValue... inputs)
329330
```

0 commit comments

Comments
 (0)