tvm-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tqc...@apache.org
Subject [incubator-tvm] branch master updated: TVM android camera demo (#5005)
Date Sat, 21 Mar 2020 00:29:46 GMT
This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-tvm.git


The following commit(s) were added to refs/heads/master by this push:
     new 1d83ece  TVM android camera demo (#5005)
1d83ece is described below

commit 1d83ece55dfa25cf810b97b665b3620d2322cbc1
Author: Yagna Srinath Reddy Battula <yagnasrinath@users.noreply.github.com>
AuthorDate: Fri Mar 20 17:29:39 2020 -0700

    TVM android camera demo (#5005)
---
 apps/android_camera/.gitignore                     |  15 +
 apps/android_camera/README.md                      |  28 +
 apps/android_camera/app/build.gradle               |  79 +++
 .../app/src/main/AndroidManifest.xml               |  50 ++
 .../androidcamerademo/Camera2BasicFragment.java    | 615 +++++++++++++++++++++
 .../android/androidcamerademo/MainActivity.java    |  88 +++
 apps/android_camera/app/src/main/jni/Android.mk    |  59 ++
 .../android_camera/app/src/main/jni/Application.mk |  46 ++
 apps/android_camera/app/src/main/jni/build.sh      |  26 +
 .../android_camera/app/src/main/jni/make/config.mk |  54 ++
 apps/android_camera/app/src/main/jni/tvm_runtime.h |  79 +++
 .../app/src/main/res/drawable/item_selector.xml    |  23 +
 .../app/src/main/res/layout/activity_main.xml      |  23 +
 .../src/main/res/layout/fragment_camera2_basic.xml |  85 +++
 .../app/src/main/res/layout/listview_row.xml       |  28 +
 .../app/src/main/res/values/colors.xml             |  20 +
 .../app/src/main/res/values/strings.xml            |  18 +
 .../app/src/main/res/values/styles.xml             |  31 ++
 .../app/src/main/res/xml/provider_paths.xml        |  20 +
 apps/android_camera/app/src/main/rs/yuv420888.rs   |  49 ++
 apps/android_camera/build.gradle                   |  49 ++
 apps/android_camera/dev_tools/gen_keystore.sh      |  20 +
 apps/android_camera/dev_tools/sign_apk.sh          |  24 +
 apps/android_camera/gradle.properties              |  21 +
 apps/android_camera/models/prepare_model.py        | 123 +++++
 apps/android_camera/settings.gradle                |  18 +
 tests/lint/add_asf_header.py                       |   1 -
 27 files changed, 1691 insertions(+), 1 deletion(-)

diff --git a/apps/android_camera/.gitignore b/apps/android_camera/.gitignore
new file mode 100644
index 0000000..6c2f76d
--- /dev/null
+++ b/apps/android_camera/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+/app/src/main/jni/jni_helper_func.h
+/app/src/main/jni/org_apache_tvm_native_c_api.cc
+/app/src/main/jni/org_apache_tvm_native_c_api.h
+/app/src/main/obj/
+gradle/
+app/src/main/assets/models
diff --git a/apps/android_camera/README.md b/apps/android_camera/README.md
new file mode 100644
index 0000000..c292ce4
--- /dev/null
+++ b/apps/android_camera/README.md
@@ -0,0 +1,28 @@
+[//]: # Licensed to the Apache Software Foundation (ASF) under one
+[//]: # or more contributor license agreements.  See the NOTICE file
+[//]: # distributed with this work for additional information
+[//]: # regarding copyright ownership.  The ASF licenses this file
+[//]: # to you under the Apache License, Version 2.0 (the
+[//]: # "License"); you may not use this file except in compliance
+[//]: # with the License.  You may obtain a copy of the License at
+[//]: #
+[//]: #   http://www.apache.org/licenses/LICENSE-2.0
+[//]: #
+[//]: # Unless required by applicable law or agreed to in writing,
+[//]: # software distributed under the License is distributed on an
+[//]: # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+[//]: # KIND, either express or implied.  See the License for the
+[//]: # specific language governing permissions and limitations
+[//]: # under the License.
+
+Android Camera Demo Sample App
+==============================
+
+The Android Camera Demo Sample App provides a basic implementation of an Android
+app that uses the tvm runtime to perform image classification in real time.
+
+Converting Models
+-----------------
+
+The `models/prepare_models.py` script provides a example flow for dumping model
+parameter files for use by the app.
diff --git a/apps/android_camera/app/build.gradle b/apps/android_camera/app/build.gradle
new file mode 100644
index 0000000..8a772a3
--- /dev/null
+++ b/apps/android_camera/app/build.gradle
@@ -0,0 +1,79 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+apply plugin: 'com.android.application'
+
+task buildJni(type: Exec, description: 'Build JNI libs') {
+    commandLine 'sh', 'src/main/jni/build.sh'
+}
+
+tasks.withType(JavaCompile) {
+    //compileTask -> compileTask.dependsOn buildJni
+}
+
+android {
+    compileSdkVersion 29
+    defaultConfig {
+        applicationId "ml.apache.tvm.android.androidcamerademo"
+        minSdkVersion 24
+        targetSdkVersion 29
+        renderscriptTargetApi 18
+        renderscriptSupportModeEnabled true
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    sourceSets {
+        main {
+            jni.srcDirs = []
+            jniLibs.srcDirs = ['src/main/libs']
+        }
+    }
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    buildToolsVersion = '29.0.3'
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    androidTestImplementation('androidx.test.espresso:espresso-core:3.2.0', {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    implementation 'androidx.appcompat:appcompat:1.1.0'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    implementation 'com.google.android.material:material:1.1.0'
+    implementation 'org.apache.tvm:tvm4j-core:0.0.1-SNAPSHOT'
+    testImplementation 'junit:junit:4.13'
+
+    implementation("androidx.concurrent:concurrent-futures:1.0.0")
+    implementation "androidx.camera:camera-core:1.0.0-beta01"
+    implementation "androidx.camera:camera-camera2:1.0.0-beta01"
+    // If you want to use the CameraX View class
+    implementation "androidx.camera:camera-view:1.0.0-alpha08"
+    // If you want to use the CameraX Extensions library
+    implementation "androidx.camera:camera-extensions:1.0.0-alpha08"
+    // If you want to use the CameraX Lifecycle library
+    implementation "androidx.camera:camera-lifecycle:1.0.0-beta01"
+}
diff --git a/apps/android_camera/app/src/main/AndroidManifest.xml b/apps/android_camera/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0821286
--- /dev/null
+++ b/apps/android_camera/app/src/main/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="org.apache.tvm.android.androidcamerademo">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.CAMERA" />
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        tools:ignore="AllowBackup,MissingApplicationIcon">
+        <activity
+            android:name="org.apache.tvm.android.androidcamerademo.MainActivity"
+            android:label="@string/app_name"
+            android:screenOrientation="portrait"
+            android:theme="@style/AppTheme.NoActionBar"
+            tools:ignore="LockedOrientationActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.provider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/provider_paths" />
+        </provider>
+    </application>
+</manifest>
diff --git a/apps/android_camera/app/src/main/java/org/apache/tvm/android/androidcamerademo/Camera2BasicFragment.java b/apps/android_camera/app/src/main/java/org/apache/tvm/android/androidcamerademo/Camera2BasicFragment.java
new file mode 100644
index 0000000..f598f8e
--- /dev/null
+++ b/apps/android_camera/app/src/main/java/org/apache/tvm/android/androidcamerademo/Camera2BasicFragment.java
@@ -0,0 +1,615 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tvm.android.androidcamerademo;
+
+import android.annotation.SuppressLint;
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.media.Image;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.Size;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.AppCompatTextView;
+import androidx.camera.core.Camera;
+import androidx.camera.core.CameraSelector;
+import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.ImageProxy;
+import androidx.camera.core.Preview;
+import androidx.camera.lifecycle.ProcessCameraProvider;
+import androidx.camera.view.PreviewView;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.renderscript.Allocation;
+import androidx.renderscript.Element;
+import androidx.renderscript.RenderScript;
+import androidx.renderscript.Script;
+import androidx.renderscript.Type;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.apache.tvm.Function;
+import org.apache.tvm.Module;
+import org.apache.tvm.NDArray;
+import org.apache.tvm.TVMContext;
+import org.apache.tvm.TVMType;
+import org.apache.tvm.TVMValue;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.PriorityQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+public class Camera2BasicFragment extends Fragment {
+    private static final String TAG = Camera2BasicFragment.class.getSimpleName();
+
+    // TVM constants
+    private static final int OUTPUT_INDEX = 0;
+    private static final int IMG_CHANNEL = 3;
+    private static final boolean EXE_GPU = false;
+    private static final int MODEL_INPUT_SIZE = 224;
+    private static final String MODEL_CL_LIB_FILE = "deploy_lib_opencl.so";
+    private static final String MODEL_CPU_LIB_FILE = "deploy_lib_cpu.so";
+    private static final String MODEL_GRAPH_FILE = "deploy_graph.json";
+    private static final String MODEL_PARAM_FILE = "deploy_param.params";
+    private static final String MODEL_LABEL_FILE = "image_net_labels.json";
+    private static final String MODELS = "models";
+    private static String INPUT_NAME = "input_1";
+    private static String[] models;
+    private static String mCurModel = "";
+    private final float[] mCHW = new float[MODEL_INPUT_SIZE * MODEL_INPUT_SIZE * IMG_CHANNEL];
+    private final float[] mCHW2 = new float[MODEL_INPUT_SIZE * MODEL_INPUT_SIZE * IMG_CHANNEL];
+    private final Semaphore isProcessingDone = new Semaphore(1);
+    private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
+            3,
+            3,
+            1,
+            TimeUnit.SECONDS,
+            new LinkedBlockingQueue<>()
+    );
+    // rs creation just for demo. Create rs just once in onCreate and use it again.
+    private RenderScript rs;
+    private ScriptC_yuv420888 mYuv420;
+    private boolean mRunClassifier = false;
+
+    private AppCompatTextView mResultView;
+    private AppCompatTextView mInfoView;
+    private ListView mModelView;
+    private AssetManager assetManager;
+    private Module graphRuntimeModule;
+    private JSONObject labels;
+    private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
+    private PreviewView previewView;
+    private ImageAnalysis imageAnalysis;
+
+    static Camera2BasicFragment newInstance() {
+        return new Camera2BasicFragment();
+    }
+
+    private static Matrix getTransformationMatrix(
+            final int srcWidth,
+            final int srcHeight,
+            final int dstWidth,
+            final int dstHeight,
+            final int applyRotation,
+            final boolean maintainAspectRatio) {
+        final Matrix matrix = new Matrix();
+
+        if (applyRotation != 0) {
+            if (applyRotation % 90 != 0) {
+                Log.w(TAG, "Rotation of %d % 90 != 0 " + applyRotation);
+            }
+
+            // Translate so center of image is at origin.
+            matrix.postTranslate(-srcWidth / 2.0f, -srcHeight / 2.0f);
+
+            // Rotate around origin.
+            matrix.postRotate(applyRotation);
+        }
+
+        // Account for the already applied rotation, if any, and then determine how
+        // much scaling is needed for each axis.
+        final boolean transpose = (Math.abs(applyRotation) + 90) % 180 == 0;
+
+        final int inWidth = transpose ? srcHeight : srcWidth;
+        final int inHeight = transpose ? srcWidth : srcHeight;
+
+        // Apply scaling if necessary.
+        if (inWidth != dstWidth || inHeight != dstHeight) {
+            final float scaleFactorX = dstWidth / (float) inWidth;
+            final float scaleFactorY = dstHeight / (float) inHeight;
+
+            if (maintainAspectRatio) {
+                // Scale by minimum factor so that dst is filled completely while
+                // maintaining the aspect ratio. Some image may fall off the edge.
+                final float scaleFactor = Math.max(scaleFactorX, scaleFactorY);
+                matrix.postScale(scaleFactor, scaleFactor);
+            } else {
+                // Scale exactly to fill dst from src.
+                matrix.postScale(scaleFactorX, scaleFactorY);
+            }
+        }
+
+        if (applyRotation != 0) {
+            // Translate back from origin centered reference to destination frame.
+            matrix.postTranslate(dstWidth / 2.0f, dstHeight / 2.0f);
+        }
+
+        return matrix;
+    }
+
+    private String[] getModels() {
+        String[] models;
+        try {
+            models = getActivity().getAssets().list(MODELS);
+        } catch (IOException e) {
+            return null;
+        }
+        return models;
+    }
+
+    @SuppressLint("DefaultLocale")
+    private String[] inference(float[] chw) {
+        NDArray inputNdArray = NDArray.empty(new long[]{1, IMG_CHANNEL, MODEL_INPUT_SIZE, MODEL_INPUT_SIZE}, new TVMType("float32"));
+        inputNdArray.copyFrom(chw);
+        Function setInputFunc = graphRuntimeModule.getFunction("set_input");
+        setInputFunc.pushArg(INPUT_NAME).pushArg(inputNdArray).invoke();
+        // release tvm local variables
+        inputNdArray.release();
+        setInputFunc.release();
+
+        // get the function from the module(run it)
+        Function runFunc = graphRuntimeModule.getFunction("run");
+        runFunc.invoke();
+        // release tvm local variables
+        runFunc.release();
+
+        // get the function from the module(get output data)
+        NDArray outputNdArray = NDArray.empty(new long[]{1, 1000}, new TVMType("float32"));
+        Function getOutputFunc = graphRuntimeModule.getFunction("get_output");
+        getOutputFunc.pushArg(OUTPUT_INDEX).pushArg(outputNdArray).invoke();
+        float[] output = outputNdArray.asFloatArray();
+        // release tvm local variables
+        outputNdArray.release();
+        getOutputFunc.release();
+
+        if (null != output) {
+            String[] results = new String[5];
+            // top-5
+            PriorityQueue<Integer> pq = new PriorityQueue<>(1000, (Integer idx1, Integer idx2) -> output[idx1] > output[idx2] ? -1 : 1);
+
+            // display the result from extracted output data
+            for (int j = 0; j < output.length; ++j) {
+                pq.add(j);
+            }
+            for (int l = 0; l < 5; l++) {
+                //noinspection ConstantConditions
+                int idx = pq.poll();
+                if (idx < labels.length()) {
+                    try {
+                        results[l] = String.format("%.2f", output[idx]) + " : " + labels.getString(Integer.toString(idx));
+                    } catch (JSONException e) {
+                        Log.e(TAG, "index out of range", e);
+                    }
+                } else {
+                    results[l] = "???: unknown";
+                }
+            }
+            return results;
+        }
+        return new String[5];
+    }
+
+    private void updateActiveModel() {
+        Log.i(TAG, "updating active model...");
+        new LoadModelAsyncTask().execute();
+    }
+
+    @Override
+    public void onViewCreated(final View view, Bundle savedInstanceState) {
+
+        mResultView = view.findViewById(R.id.resultTextView);
+        mInfoView = view.findViewById(R.id.infoTextView);
+        mModelView = view.findViewById(R.id.modelListView);
+        if (assetManager == null) {
+            assetManager = getActivity().getAssets();
+        }
+
+        mModelView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        models = getModels();
+
+        ArrayAdapter<String> modelAdapter =
+                new ArrayAdapter<>(
+                        getContext(), R.layout.listview_row, R.id.listview_row_text, models);
+        mModelView.setAdapter(modelAdapter);
+        mModelView.setItemChecked(0, true);
+        mModelView.setOnItemClickListener(
+                (parent, view1, position, id) -> updateActiveModel());
+
+        new LoadModelAsyncTask().execute();
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+    }
+
+    @Override
+    public void onDestroy() {
+        // release tvm local variables
+        if (null != graphRuntimeModule)
+            graphRuntimeModule.release();
+        super.onDestroy();
+    }
+
+    /**
+     * Read file from assets and return byte array.
+     *
+     * @param assets   The asset manager to be used to load assets.
+     * @param fileName The filepath of read file.
+     * @return byte[] file content
+     * @throws IOException
+     */
+    private byte[] getBytesFromFile(AssetManager assets, String fileName) throws IOException {
+        InputStream is = assets.open(fileName);
+        int length = is.available();
+        byte[] bytes = new byte[length];
+        // Read in the bytes
+        int offset = 0;
+        int numRead;
+        try {
+            while (offset < bytes.length
+                    && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
+                offset += numRead;
+            }
+        } finally {
+            is.close();
+        }
+        // Ensure all the bytes have been read in
+        if (offset < bytes.length) {
+            throw new IOException("Could not completely read file " + fileName);
+        }
+        return bytes;
+    }
+
+    /**
+     * Get application cache path where to place compiled functions.
+     *
+     * @param fileName library file name.
+     * @return String application cache folder path
+     * @throws IOException
+     */
+    private String getTempLibFilePath(String fileName) throws IOException {
+        File tempDir = File.createTempFile("tvm4j_demo_", "");
+        if (!tempDir.delete() || !tempDir.mkdir()) {
+            throw new IOException("Couldn't create directory " + tempDir.getAbsolutePath());
+        }
+        return (tempDir + File.separator + fileName);
+    }
+
+    private Bitmap YUV_420_888_toRGB(Image image, int width, int height) {
+        // Get the three image planes
+        Image.Plane[] planes = image.getPlanes();
+        ByteBuffer buffer = planes[0].getBuffer();
+        byte[] y = new byte[buffer.remaining()];
+        buffer.get(y);
+
+        buffer = planes[1].getBuffer();
+        byte[] u = new byte[buffer.remaining()];
+        buffer.get(u);
+
+        buffer = planes[2].getBuffer();
+        byte[] v = new byte[buffer.remaining()];
+        buffer.get(v);
+
+        int yRowStride = planes[0].getRowStride();
+        int uvRowStride = planes[1].getRowStride();
+        int uvPixelStride = planes[1].getPixelStride();
+
+
+        // Y,U,V are defined as global allocations, the out-Allocation is the Bitmap.
+        // Note also that uAlloc and vAlloc are 1-dimensional while yAlloc is 2-dimensional.
+        Type.Builder typeUcharY = new Type.Builder(rs, Element.U8(rs));
+        typeUcharY.setX(yRowStride).setY(height);
+        Allocation yAlloc = Allocation.createTyped(rs, typeUcharY.create());
+        yAlloc.copyFrom(y);
+        mYuv420.set_ypsIn(yAlloc);
+
+        Type.Builder typeUcharUV = new Type.Builder(rs, Element.U8(rs));
+        // note that the size of the u's and v's are as follows:
+        //      (  (width/2)*PixelStride + padding  ) * (height/2)
+        // =    (RowStride                          ) * (height/2)
+        typeUcharUV.setX(u.length);
+        Allocation uAlloc = Allocation.createTyped(rs, typeUcharUV.create());
+        uAlloc.copyFrom(u);
+        mYuv420.set_uIn(uAlloc);
+
+        Allocation vAlloc = Allocation.createTyped(rs, typeUcharUV.create());
+        vAlloc.copyFrom(v);
+        mYuv420.set_vIn(vAlloc);
+
+        // handover parameters
+        mYuv420.set_picWidth(width);
+        mYuv420.set_uvRowStride(uvRowStride);
+        mYuv420.set_uvPixelStride(uvPixelStride);
+
+        Bitmap outBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        Allocation outAlloc = Allocation.createFromBitmap(rs, outBitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+
+        Script.LaunchOptions lo = new Script.LaunchOptions();
+        lo.setX(0, width);  // by this we ignore the y’s padding zone, i.e. the right side of x between width and yRowStride
+        lo.setY(0, height);
+
+        mYuv420.forEach_doConvert(outAlloc, lo);
+        outAlloc.copyTo(outBitmap);
+
+        return outBitmap;
+    }
+
+    private float[] getFrame(ImageProxy imageProxy) {
+        @SuppressLint("UnsafeExperimentalUsageError")
+        Image image = imageProxy.getImage();
+        // extract the jpeg content
+        if (image == null) {
+            return null;
+        }
+        Bitmap imageBitmap = YUV_420_888_toRGB(image, image.getWidth(), image.getHeight());
+
+        imageProxy.close();
+        // crop input image at centre to model input size
+        Bitmap cropImageBitmap = Bitmap.createBitmap(MODEL_INPUT_SIZE, MODEL_INPUT_SIZE, Bitmap.Config.ARGB_8888);
+        Matrix frameToCropTransform = getTransformationMatrix(imageBitmap.getWidth(), imageBitmap.getHeight(),
+                MODEL_INPUT_SIZE, MODEL_INPUT_SIZE, 0, true);
+        Canvas canvas = new Canvas(cropImageBitmap);
+        canvas.drawBitmap(imageBitmap, frameToCropTransform, null);
+
+        // image pixel int values
+        int[] pixelValues = new int[MODEL_INPUT_SIZE * MODEL_INPUT_SIZE];
+        // image RGB float values
+
+        // pre-process the image data from 0-255 int to normalized float based on the
+        // provided parameters.
+        cropImageBitmap.getPixels(pixelValues, 0, MODEL_INPUT_SIZE, 0, 0, MODEL_INPUT_SIZE, MODEL_INPUT_SIZE);
+        for (int j = 0; j < pixelValues.length; ++j) {
+            mCHW2[j * 3 + 0] = ((pixelValues[j] >> 16) & 0xFF) / 255.0f;
+            mCHW2[j * 3 + 1] = ((pixelValues[j] >> 8) & 0xFF) / 255.0f;
+            mCHW2[j * 3 + 2] = (pixelValues[j] & 0xFF) / 255.0f;
+        }
+
+        // pre-process the image rgb data transpose based on the provided parameters.
+        for (int k = 0; k < IMG_CHANNEL; ++k) {
+            for (int l = 0; l < MODEL_INPUT_SIZE; ++l) {
+                for (int m = 0; m < MODEL_INPUT_SIZE; ++m) {
+                    int dst_index = m + MODEL_INPUT_SIZE * l + MODEL_INPUT_SIZE * MODEL_INPUT_SIZE * k;
+                    int src_index = k + IMG_CHANNEL * m + IMG_CHANNEL * MODEL_INPUT_SIZE * l;
+                    mCHW[dst_index] = mCHW2[src_index];
+                }
+            }
+        }
+
+        return mCHW;
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        cameraProviderFuture = ProcessCameraProvider.getInstance(getActivity());
+    }
+
+    @SuppressLint({"RestrictedApi", "UnsafeExperimentalUsageError"})
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        View v = inflater.inflate(R.layout.fragment_camera2_basic, container, false);
+        previewView = v.findViewById(R.id.textureView);
+        rs = RenderScript.create(getActivity());
+        mYuv420 = new ScriptC_yuv420888(rs);
+        cameraProviderFuture.addListener(() -> {
+            try {
+                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
+                bindPreview(cameraProvider);
+            } catch (ExecutionException | InterruptedException e) {
+                // No errors need to be handled for this Future. This should never be reached
+            }
+        }, ContextCompat.getMainExecutor(getActivity()));
+
+        imageAnalysis = new ImageAnalysis.Builder()
+                .setTargetResolution(new Size(224, 224))
+                .setMaxResolution(new Size(300, 300))
+                .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
+                .build();
+
+        imageAnalysis.setAnalyzer(threadPoolExecutor, image -> {
+            Log.e(TAG, "w: " + image.getWidth() + " h: " + image.getHeight());
+            if (mRunClassifier && isProcessingDone.tryAcquire()) {
+                long t1 = SystemClock.uptimeMillis();
+                //float[] chw = getFrame(image);
+                //float[] chw = YUV_420_888_toRGBPixels(image);
+                float[] chw = getFrame(image);
+                if (chw != null) {
+                    long t2 = SystemClock.uptimeMillis();
+                    String[] results = inference(chw);
+                    long t3 = SystemClock.uptimeMillis();
+                    StringBuilder msgBuilder = new StringBuilder();
+                    for (int l = 1; l < 5; l++) {
+                        msgBuilder.append(results[l]).append("\n");
+                    }
+                    String msg = msgBuilder.toString();
+                    msg += "getFrame(): " + (t2 - t1) + "ms" + "\n";
+                    msg += "inference(): " + (t3 - t2) + "ms" + "\n";
+                    String finalMsg = msg;
+                    this.getActivity().runOnUiThread(() -> {
+                        mResultView.setText(String.format("model: %s \n %s", mCurModel, results[0]));
+                        mInfoView.setText(finalMsg);
+                    });
+                }
+                isProcessingDone.release();
+            }
+            image.close();
+        });
+        return v;
+    }
+
+    private void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
+        @SuppressLint("RestrictedApi") Preview preview = new Preview.Builder()
+                .setMaxResolution(new Size(800, 800))
+                .setTargetName("Preview")
+                .build();
+
+        preview.setSurfaceProvider(previewView.getPreviewSurfaceProvider());
+        CameraSelector cameraSelector =
+                new CameraSelector.Builder()
+                        .requireLensFacing(CameraSelector.LENS_FACING_BACK)
+                        .build();
+        Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis);
+    }
+
+    @Override
+    public void onDestroyView() {
+        threadPoolExecutor.shutdownNow();
+        super.onDestroyView();
+    }
+
+    private void setInputName(String modelName) {
+        if (modelName.equals("mobilenet_v2")) {
+            INPUT_NAME = "input_1";
+        } else if (modelName.equals("resnet18_v1")) {
+            INPUT_NAME = "data";
+        } else {
+            throw new RuntimeException("Model input may not be right. Please set INPUT_NAME here explicitly.");
+        }
+    }
+
+    /*
+       Load precompiled model on TVM graph runtime and init the system.
+    */
+    private class LoadModelAsyncTask extends AsyncTask<Void, Void, Integer> {
+
+        @Override
+        protected Integer doInBackground(Void... args) {
+            mRunClassifier = false;
+            // load synset name
+            int modelIndex = mModelView.getCheckedItemPosition();
+            setInputName(models[modelIndex]);
+            String model = MODELS + "/" + models[modelIndex];
+            String labelFilename = MODEL_LABEL_FILE;
+            Log.i(TAG, "Reading labels from: " + model + "/" + labelFilename);
+            try {
+                labels = new JSONObject(new String(getBytesFromFile(assetManager, model + "/" + labelFilename)));
+            } catch (IOException | JSONException e) {
+                Log.e(TAG, "Problem reading labels name file!", e);
+                return -1;//failure
+            }
+
+            // load json graph
+            String modelGraph;
+            String graphFilename = MODEL_GRAPH_FILE;
+            Log.i(TAG, "Reading json graph from: " + model + "/" + graphFilename);
+            try {
+                modelGraph = new String(getBytesFromFile(assetManager, model + "/" + graphFilename));
+            } catch (IOException e) {
+                Log.e(TAG, "Problem reading json graph file!", e);
+                return -1;//failure
+            }
+
+            // upload tvm compiled function on application cache folder
+            String libCacheFilePath;
+            String libFilename = EXE_GPU ? MODEL_CL_LIB_FILE : MODEL_CPU_LIB_FILE;
+            Log.i(TAG, "Uploading compiled function to cache folder");
+            try {
+                libCacheFilePath = getTempLibFilePath(libFilename);
+                byte[] modelLibByte = getBytesFromFile(assetManager, model + "/" + libFilename);
+                FileOutputStream fos = new FileOutputStream(libCacheFilePath);
+                fos.write(modelLibByte);
+                fos.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Problem uploading compiled function!", e);
+                return -1;//failure
+            }
+
+            // load parameters
+            byte[] modelParams;
+            try {
+                modelParams = getBytesFromFile(assetManager, model + "/" + MODEL_PARAM_FILE);
+            } catch (IOException e) {
+                Log.e(TAG, "Problem reading params file!", e);
+                return -1;//failure
+            }
+
+            Log.i(TAG, "creating java tvm context...");
+            // create java tvm context
+            TVMContext tvmCtx = EXE_GPU ? TVMContext.opencl() : TVMContext.cpu();
+
+            Log.i(TAG, "loading compiled functions...");
+            Log.i(TAG, libCacheFilePath);
+            // tvm module for compiled functions
+            Module modelLib = Module.load(libCacheFilePath);
+
+
+            // get global function module for graph runtime
+            Log.i(TAG, "getting graph runtime create handle...");
+
+            Function runtimeCreFun = Function.getFunction("tvm.graph_runtime.create");
+            Log.i(TAG, "creating graph runtime...");
+
+            Log.i(TAG, "ctx type: " + tvmCtx.deviceType);
+            Log.i(TAG, "ctx id: " + tvmCtx.deviceId);
+
+            TVMValue runtimeCreFunRes = runtimeCreFun.pushArg(modelGraph)
+                    .pushArg(modelLib)
+                    .pushArg(tvmCtx.deviceType)
+                    .pushArg(tvmCtx.deviceId)
+                    .invoke();
+
+            Log.i(TAG, "as module...");
+            graphRuntimeModule = runtimeCreFunRes.asModule();
+            Log.i(TAG, "getting graph runtime load params handle...");
+            // get the function from the module(load parameters)
+            Function loadParamFunc = graphRuntimeModule.getFunction("load_params");
+            Log.i(TAG, "loading params...");
+            loadParamFunc.pushArg(modelParams).invoke();
+            // release tvm local variables
+            modelLib.release();
+            loadParamFunc.release();
+            runtimeCreFun.release();
+            mCurModel = model;
+            mRunClassifier = true;
+            return 0;//success
+        }
+    }
+}
diff --git a/apps/android_camera/app/src/main/java/org/apache/tvm/android/androidcamerademo/MainActivity.java b/apps/android_camera/app/src/main/java/org/apache/tvm/android/androidcamerademo/MainActivity.java
new file mode 100644
index 0000000..f9c573a
--- /dev/null
+++ b/apps/android_camera/app/src/main/java/org/apache/tvm/android/androidcamerademo/MainActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tvm.android.androidcamerademo;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+public class MainActivity extends AppCompatActivity implements
+        ActivityCompat.OnRequestPermissionsResultCallback {
+
+    private static final int PERMISSIONS_REQUEST_CODE = 1;
+
+    private String[] getRequiredPermissions() {
+        try {
+            PackageInfo info = getPackageManager()
+                    .getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS);
+            String[] ps = info.requestedPermissions;
+            if (ps != null && ps.length > 0) {
+                return ps;
+            } else {
+                return new String[0];
+            }
+        } catch (Exception e) {
+            return new String[0];
+        }
+    }
+
+    private boolean allPermissionsGranted() {
+        for (String permission : getRequiredPermissions()) {
+            if (ContextCompat.checkSelfPermission(this, permission)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        if (!allPermissionsGranted()) {
+            requestPermissions(getRequiredPermissions(), PERMISSIONS_REQUEST_CODE);
+            return;
+        }
+        startFragment();
+    }
+
+    private void startFragment() {
+        getSupportFragmentManager()
+                .beginTransaction()
+                .replace(R.id.container, Camera2BasicFragment.newInstance())
+                .commit();
+    }
+
+    @Override
+    public void onRequestPermissionsResult(
+            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        if (allPermissionsGranted()) {
+            startFragment();
+        } else {
+            Toast.makeText(this, "Required permissions are not granted. App may not run", Toast.LENGTH_SHORT).show();
+            finish();
+        }
+    }
+}
diff --git a/apps/android_camera/app/src/main/jni/Android.mk b/apps/android_camera/app/src/main/jni/Android.mk
new file mode 100644
index 0000000..f135c97
--- /dev/null
+++ b/apps/android_camera/app/src/main/jni/Android.mk
@@ -0,0 +1,59 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+LOCAL_PATH := $(call my-dir)
+MY_PATH := $(LOCAL_PATH)
+
+include $(CLEAR_VARS)
+
+LOCAL_PATH := $(MY_PATH)
+ROOT_PATH := $(MY_PATH)/../../../../../..
+
+ifndef config
+	ifneq ("$(wildcard ./config.mk)","")
+	  config ?= config.mk
+	else
+	  config ?= make/config.mk
+	endif
+endif
+
+include $(config)
+
+LOCAL_SRC_FILES := org_apache_tvm_native_c_api.cc
+LOCAL_LDFLAGS := -L$(SYSROOT)/usr/lib/ -llog
+
+LOCAL_C_INCLUDES := $(ROOT_PATH)/include \
+                    $(ROOT_PATH)/3rdparty/dlpack/include \
+                    $(ROOT_PATH)/3rdparty/dmlc-core/include \
+                    $(ROOT_PATH)/3rdparty/HalideIR/src \
+                    $(ROOT_PATH)/topi/include
+
+LOCAL_MODULE = tvm4j_runtime_packed
+
+LOCAL_CPP_FEATURES += exceptions
+LOCAL_LDLIBS += -latomic
+LOCAL_ARM_MODE := arm
+
+ifdef ADD_C_INCLUDES
+	LOCAL_C_INCLUDES += $(ADD_C_INCLUDES)
+endif
+
+ifdef ADD_LDLIBS
+	LOCAL_LDLIBS += $(ADD_LDLIBS)
+endif
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/apps/android_camera/app/src/main/jni/Application.mk b/apps/android_camera/app/src/main/jni/Application.mk
new file mode 100644
index 0000000..95a5a96
--- /dev/null
+++ b/apps/android_camera/app/src/main/jni/Application.mk
@@ -0,0 +1,46 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+ifndef config
+	ifneq ("$(wildcard ./config.mk)","")
+	  config ?= config.mk
+	else
+	  config ?= make/config.mk
+	endif
+endif
+
+include $(config)
+
+# We target every architecture except armeabi here, for two reasons:
+# 1) armeabi is deprecated in NDK r16 and removed in r17
+# 2) vulkan is not supported in armeabi
+APP_ABI ?= all
+APP_STL := c++_shared
+
+APP_CPPFLAGS += -DDMLC_LOG_STACK_TRACE=0 -DTVM4J_ANDROID=1 -std=c++11 -Oz -frtti
+ifeq ($(USE_OPENCL), 1)
+    APP_CPPFLAGS += -DTVM_OPENCL_RUNTIME=1
+endif
+
+ifeq ($(USE_VULKAN), 1)
+    APP_CPPFLAGS += -DTVM_VULKAN_RUNTIME=1
+    APP_LDFLAGS += -lvulkan
+endif
+
+ifeq ($(USE_SORT), 1)
+    APP_CPPFLAGS += -DUSE_SORT=1
+endif
\ No newline at end of file
diff --git a/apps/android_camera/app/src/main/jni/build.sh b/apps/android_camera/app/src/main/jni/build.sh
new file mode 100755
index 0000000..eb6aa2b
--- /dev/null
+++ b/apps/android_camera/app/src/main/jni/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+set -x
+PATH="$PATH:/usr/local/bin"
+CURR_DIR=$(cd `dirname $0`; pwd)
+ROOT_DIR="$CURR_DIR/../../../../../.."
+javah -o $CURR_DIR/org_apache_tvm_native_c_api.h -cp "$ROOT_DIR/jvm/core/target/*" org.apache.tvm.LibInfo || exit -1
+cp -f $ROOT_DIR/jvm/native/src/main/native/org_apache_tvm_native_c_api.cc $CURR_DIR/ || exit -1
+cp -f $ROOT_DIR/jvm/native/src/main/native/jni_helper_func.h $CURR_DIR/ || exit -1
+rm -rf $CURR_DIR/../libs
+ndk-build --directory=$CURR_DIR
diff --git a/apps/android_camera/app/src/main/jni/make/config.mk b/apps/android_camera/app/src/main/jni/make/config.mk
new file mode 100644
index 0000000..49e3326
--- /dev/null
+++ b/apps/android_camera/app/src/main/jni/make/config.mk
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#-------------------------------------------------------------------------------
+#  Template configuration for compiling
+#
+#  If you want to change the configuration, please use the following
+#  steps. Assume you are on the root directory. First copy the this
+#  file so that any local changes will be ignored by git
+#
+#  cp make/config.mk .
+#
+#  Next modify the according entries, and then compile by
+#
+#  ./build.sh
+#
+#-------------------------------------------------------------------------------
+APP_ABI = all
+
+APP_PLATFORM = android-24
+
+# whether enable OpenCL during compile
+USE_OPENCL = 0
+
+# whether to enable Vulkan during compile
+USE_VULKAN = 0
+
+# whether to enable contrib sort functions during compile
+USE_SORT = 1
+
+ifeq ($(USE_VULKAN), 1)
+  # Statically linking vulkan requires API Level 24 or higher
+  APP_PLATFORM = android-24
+endif
+
+# the additional include headers you want to add, e.g., SDK_PATH/adrenosdk/Development/Inc
+ADD_C_INCLUDES =
+
+# the additional link libs you want to add, e.g., ANDROID_LIB_PATH/libOpenCL.so
+ADD_LDLIBS =
diff --git a/apps/android_camera/app/src/main/jni/tvm_runtime.h b/apps/android_camera/app/src/main/jni/tvm_runtime.h
new file mode 100644
index 0000000..a58252e
--- /dev/null
+++ b/apps/android_camera/app/src/main/jni/tvm_runtime.h
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/*!
+ * \file tvm_runtime.h
+ * \brief Pack all tvm runtime source files
+ */
+#include <sys/stat.h>
+#include <fstream>
+
+/* Enable custom logging - this will cause TVM to pass every log message
+ * through CustomLogMessage instead of LogMessage. By enabling this, we must
+ * implement dmlc::CustomLogMessage::Log. We use this to pass TVM log
+ * messages to Android logcat.
+ */
+#define DMLC_LOG_CUSTOMIZE 1
+
+/* Ensure that fatal errors are passed to the logger before throwing
+ * in LogMessageFatal
+ */
+#define DMLC_LOG_BEFORE_THROW 1
+
+#include "../src/runtime/c_runtime_api.cc"
+#include "../src/runtime/cpu_device_api.cc"
+#include "../src/runtime/workspace_pool.cc"
+#include "../src/runtime/library_module.cc"
+#include "../src/runtime/system_library.cc"
+#include "../src/runtime/module.cc"
+#include "../src/runtime/registry.cc"
+#include "../src/runtime/file_util.cc"
+#include "../src/runtime/dso_library.cc"
+#include "../src/runtime/rpc/rpc_session.cc"
+#include "../src/runtime/rpc/rpc_event_impl.cc"
+#include "../src/runtime/rpc/rpc_server_env.cc"
+#include "../src/runtime/rpc/rpc_module.cc"
+#include "../src/runtime/rpc/rpc_socket_impl.cc"
+#include "../src/runtime/thread_pool.cc"
+#include "../src/runtime/threading_backend.cc"
+#include "../src/runtime/graph/graph_runtime.cc"
+#include "../src/runtime/ndarray.cc"
+#include "../src/runtime/object.cc"
+
+#ifdef TVM_OPENCL_RUNTIME
+#include "../src/runtime/opencl/opencl_device_api.cc"
+#include "../src/runtime/opencl/opencl_module.cc"
+#endif
+
+#ifdef TVM_VULKAN_RUNTIME
+#include "../src/runtime/vulkan/vulkan.cc"
+#endif
+
+#ifdef USE_SORT
+#include "../src/runtime/contrib/sort/sort.cc"
+#endif
+
+
+#include <android/log.h>
+
+void dmlc::CustomLogMessage::Log(const std::string& msg) {
+  // This is called for every message logged by TVM.
+  // We pass the message to logcat.
+  __android_log_write(ANDROID_LOG_DEBUG, "TVM_RUNTIME", msg.c_str());
+}
\ No newline at end of file
diff --git a/apps/android_camera/app/src/main/res/drawable/item_selector.xml b/apps/android_camera/app/src/main/res/drawable/item_selector.xml
new file mode 100644
index 0000000..bd87d8b
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/drawable/item_selector.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- pressed -->
+    <item android:drawable="@color/colorPrimaryDark" android:state_pressed="true" />
+    <!-- focused -->
+    <item android:drawable="@color/colorAccent" android:state_activated="true" />
+    <!-- default -->
+    <item android:drawable="@color/colorPrimary" />
+</selector>
diff --git a/apps/android_camera/app/src/main/res/layout/activity_main.xml b/apps/android_camera/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..80f9ac6
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#000"
+    tools:context="org.apache.tvm.android.androidcamerademo.MainActivity" />
\ No newline at end of file
diff --git a/apps/android_camera/app/src/main/res/layout/fragment_camera2_basic.xml b/apps/android_camera/app/src/main/res/layout/fragment_camera2_basic.xml
new file mode 100644
index 0000000..12dc53b
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/layout/fragment_camera2_basic.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <androidx.camera.view.PreviewView
+            android:id="@+id/textureView"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:gravity="center"
+            app:layout_constraintBottom_toTopOf="@id/resultTextView"
+            app:layout_constraintDimensionRatio="H,1:1"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/resultTextView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:shadowColor="#000000"
+            android:shadowDx="2"
+            android:shadowDy="2"
+            android:shadowRadius="2"
+            android:textColor="#ffffff"
+            android:textSize="24sp"
+            app:layout_constraintBottom_toTopOf="@id/infoTextView"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/textureView" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/infoTextView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:shadowColor="#000000"
+            android:shadowDx="1"
+            android:shadowDy="1"
+            android:shadowRadius="1"
+            android:textColor="#ffffff"
+            android:textSize="16sp"
+            app:layout_constraintBottom_toTopOf="@+id/modelListView"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/resultTextView" />
+
+        <ListView
+            android:id="@+id/modelListView"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/infoTextView"
+            app:layout_constraintVertical_bias="1.0" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/apps/android_camera/app/src/main/res/layout/listview_row.xml b/apps/android_camera/app/src/main/res/layout/listview_row.xml
new file mode 100644
index 0000000..4c233dc
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/layout/listview_row.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/listview_row_text"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginRight="2dp"
+        android:background="@drawable/item_selector"
+        android:padding="10dp"
+        android:textSize="18sp"
+        android:textStyle="bold" />
+</androidx.appcompat.widget.LinearLayoutCompat>
diff --git a/apps/android_camera/app/src/main/res/values/colors.xml b/apps/android_camera/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..8cb461a
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/values/colors.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+    <color name="colorPrimary">#DA6A84</color>
+    <color name="colorPrimaryDark">#F6F6F6</color>
+    <color name="colorAccent">#72BF3A</color>
+</resources>
diff --git a/apps/android_camera/app/src/main/res/values/strings.xml b/apps/android_camera/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..cc69189
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+    <string name="app_name">TVM Android Demo</string>
+</resources>
diff --git a/apps/android_camera/app/src/main/res/values/styles.xml b/apps/android_camera/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..545a6ee
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/values/styles.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+    </style>
+
+</resources>
diff --git a/apps/android_camera/app/src/main/res/xml/provider_paths.xml b/apps/android_camera/app/src/main/res/xml/provider_paths.xml
new file mode 100644
index 0000000..cef8fc0
--- /dev/null
+++ b/apps/android_camera/app/src/main/res/xml/provider_paths.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+    http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<paths>
+    <external-path
+        name="external_files"
+        path="." />
+</paths>
diff --git a/apps/android_camera/app/src/main/rs/yuv420888.rs b/apps/android_camera/app/src/main/rs/yuv420888.rs
new file mode 100644
index 0000000..05229a2
--- /dev/null
+++ b/apps/android_camera/app/src/main/rs/yuv420888.rs
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Source: https://stackoverflow.com/questions/36212904/yuv-420-888-interpretation-on-samsung-galaxy-s7-camera2
+#pragma version(1)
+#pragma rs java_package_name(org.apache.tvm.android.androidcamerademo);
+#pragma rs_fp_relaxed
+
+int32_t width;
+int32_t height;
+
+uint picWidth, uvPixelStride, uvRowStride ;
+rs_allocation ypsIn,uIn,vIn;
+
+// The LaunchOptions ensure that the Kernel does not enter the padding  zone of Y, so yRowStride can be ignored WITHIN the Kernel.
+uchar4 __attribute__((kernel)) doConvert(uint32_t x, uint32_t y) {
+
+    // index for accessing the uIn's and vIn's
+    uint uvIndex=  uvPixelStride * (x/2) + uvRowStride*(y/2);
+
+    // get the y,u,v values
+    uchar yps= rsGetElementAt_uchar(ypsIn, x, y);
+    uchar u= rsGetElementAt_uchar(uIn, uvIndex);
+    uchar v= rsGetElementAt_uchar(vIn, uvIndex);
+
+    // calc argb
+    int4 argb;
+    argb.r = yps + v * 1436 / 1024 - 179;
+    argb.g =  yps -u * 46549 / 131072 + 44 -v * 93604 / 131072 + 91;
+    argb.b = yps +u * 1814 / 1024 - 227;
+    argb.a = 255;
+
+    uchar4 out = convert_uchar4(clamp(argb, 0, 255));
+    return out;
+}
diff --git a/apps/android_camera/build.gradle b/apps/android_camera/build.gradle
new file mode 100644
index 0000000..a58bc63
--- /dev/null
+++ b/apps/android_camera/build.gradle
@@ -0,0 +1,49 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    repositories {
+        jcenter()
+        maven {
+            url 'https://maven.google.com'
+        }
+        google()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.6.1'
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        jcenter()
+        maven {
+            url 'https://maven.google.com'
+        }
+        mavenLocal()
+        mavenCentral()
+        google()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/apps/android_camera/dev_tools/gen_keystore.sh b/apps/android_camera/dev_tools/gen_keystore.sh
new file mode 100755
index 0000000..ff20597
--- /dev/null
+++ b/apps/android_camera/dev_tools/gen_keystore.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+CURR_DIR=$(cd `dirname $0`; pwd)
+keytool -genkey -keystore $CURR_DIR/tvmdemo.keystore -alias tvmdemo -keyalg RSA -validity 10000
diff --git a/apps/android_camera/dev_tools/sign_apk.sh b/apps/android_camera/dev_tools/sign_apk.sh
new file mode 100755
index 0000000..29e7777
--- /dev/null
+++ b/apps/android_camera/dev_tools/sign_apk.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+CURR_DIR=$(cd `dirname $0`; pwd)
+APK_DIR=$CURR_DIR/../app/build/outputs/apk/release
+UNSIGNED_APK=$APK_DIR/app-release-unsigned.apk
+SIGNED_APK=$APK_DIR/tv8mdemo-release.apk
+jarsigner -verbose -keystore $CURR_DIR/tv8mdemo.keystore -signedjar $SIGNED_APK $UNSIGNED_APK 'tv8mdemo'
+echo $SIGNED_APK
diff --git a/apps/android_camera/gradle.properties b/apps/android_camera/gradle.properties
new file mode 100644
index 0000000..f1328bf
--- /dev/null
+++ b/apps/android_camera/gradle.properties
@@ -0,0 +1,21 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+org.gradle.jvmargs=-Xmx4608M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/apps/android_camera/models/prepare_model.py b/apps/android_camera/models/prepare_model.py
new file mode 100644
index 0000000..36674d2
--- /dev/null
+++ b/apps/android_camera/models/prepare_model.py
@@ -0,0 +1,123 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import logging
+import pathlib
+from pathlib import Path
+from typing import Union
+import os
+from os import environ
+import json
+
+import tvm
+import tvm.relay as relay
+from tvm.contrib import util, ndk, graph_runtime as runtime
+from tvm.contrib.download import download_testdata, download
+
+target = 'llvm -target=arm64-linux-android'
+target_host = None
+
+def del_dir(target: Union[Path, str], only_if_empty: bool = False):
+    target = Path(target).expanduser()
+    assert target.is_dir()
+    for p in sorted(target.glob('**/*'), reverse=True):
+        if not p.exists():
+            continue
+        p.chmod(0o666)
+        if p.is_dir():
+            p.rmdir()
+        else:
+            if only_if_empty:
+                raise RuntimeError(f'{p.parent} is not empty!')
+            p.unlink()
+    target.rmdir()
+
+def get_model(model_name, batch_size=1):
+    if model_name == 'resnet18_v1':
+        import mxnet as mx
+        from mxnet import gluon
+        from mxnet.gluon.model_zoo import vision
+        gluon_model = vision.get_model(model_name, pretrained=True)
+        img_size = 224
+        data_shape = (batch_size, 3, img_size, img_size)
+        net, params = relay.frontend.from_mxnet(gluon_model, {"data": data_shape})
+        return (net, params)
+    elif model_name == 'mobilenet_v2':
+        import keras
+        from keras.applications.mobilenet_v2 import MobileNetV2
+        keras.backend.clear_session()  # Destroys the current TF graph and creates a new one.
+        weights_url = ''.join(['https://github.com/JonathanCMitchell/',
+                               'mobilenet_v2_keras/releases/download/v1.1/',
+                               'mobilenet_v2_weights_tf_dim_ordering_tf_kernels_0.5_224.h5'])
+        weights_file = 'mobilenet_v2_weights.h5'
+        weights_path = download_testdata(weights_url, weights_file, module='keras')
+        keras_mobilenet_v2 = MobileNetV2(alpha=0.5, include_top=True, weights=None,
+                                        input_shape=(224, 224, 3), classes=1000)
+        keras_mobilenet_v2.load_weights(weights_path)
+        
+        img_size = 224
+        data_shape = (batch_size, 3, img_size, img_size)
+        mod, params = relay.frontend.from_keras(keras_mobilenet_v2,  {'input_1': data_shape})
+        return (mod, params)
+
+def main(model_str, output_path):
+    if output_path.exists():
+        del_dir(output_path)
+    output_path.mkdir()
+    output_path_str = os.fspath(output_path)
+    print(model_str)
+    print("getting model...")
+    net, params = get_model(model_str)
+    try:
+        os.mkdir(model_str)
+    except FileExistsError:
+        pass
+    print("building...")
+    with relay.build_config(opt_level=3):
+        graph, lib, params = relay.build(net, target, target_host=target_host, params=params)
+    print("dumping lib...")
+    lib.export_library(output_path_str + '/' + 'deploy_lib_cpu.so', ndk.create_shared)
+    print("dumping graph...")
+    with open(output_path_str + '/' + 'deploy_graph.json', 'w') as f:
+        f.write(graph)
+    print("dumping params...")
+    with open(output_path_str + '/' + 'deploy_param.params', 'wb') as f:
+        f.write(relay.save_param_dict(params))
+    print("dumping labels...")
+    synset_url = ''.join(['https://gist.githubusercontent.com/zhreshold/',
+        '4d0b62f3d01426887599d4f7ede23ee5/raw/',
+        '596b27d23537e5a1b5751d2b0481ef172f58b539/',
+        'imagenet1000_clsid_to_human.txt'])
+    synset_path = output_path_str + '/image_net_labels'
+    download(synset_url, output_path_str + '/image_net_labels')
+    with open(synset_path) as fi:
+        synset = eval(fi.read())
+        with open(output_path_str + '/image_net_labels.json', "w") as fo:
+            json.dump(synset, fo, indent=4)
+    os.remove(synset_path)
+
+if __name__ == '__main__':
+    if environ.get('TVM_NDK_CC') is None:
+        raise RuntimeError("Require environment variable TVM_NDK_CC")
+    models_path = Path().absolute().parent.joinpath('app/src/main/assets/models/')
+    if not models_path.exists():
+        models_path.mkdir()
+    models = {'mobilenet_v2': models_path.joinpath('mobilenet_v2'),
+              'resnet18_v1': models_path.joinpath('resnet18_v1')
+            }
+    for model, output_path in models.items():
+        main(model, output_path)
diff --git a/apps/android_camera/settings.gradle b/apps/android_camera/settings.gradle
new file mode 100644
index 0000000..ee503c2
--- /dev/null
+++ b/apps/android_camera/settings.gradle
@@ -0,0 +1,18 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+include ':app'
diff --git a/tests/lint/add_asf_header.py b/tests/lint/add_asf_header.py
index cdfbb9a..a44fbd3 100644
--- a/tests/lint/add_asf_header.py
+++ b/tests/lint/add_asf_header.py
@@ -177,7 +177,6 @@ def add_header(fname, header):
         print("Skip file %s ..." % fname)
         return
 
-
     with open(fname, "w") as outfile:
         skipline = False
         ext = os.path.splitext(fname)[1][1:]


Mime
View raw message