Check latest version: On-Device Activity Recognition

In this tutorial, we will learn how to deploy human activity recognition (HAR) model on Android device for real-time prediction. The majority of the code in this post is largely taken from Omid Alemi's simply elegant tutorial named "Build Your First Tensorflow Android App". This post could not have been possible without Omid's contribution. I am building upon his work in this post. If you have not seen the post on how to build a deep convolutional neural network for HAR, please follow this link. Here, details on how to freeze and export the model for use in Android app are discussed.

We need to have frozen Tensorflow graph with learned weights that will be imported into an Android app for making predictions using accelerometer data. Before proceeding further make sure, the model input and output nodes have given valid names. Another thing to notice is that the input shape of CNN model is 1 x 90 x 3 (i.e. height, width and channels). However, from Android, we will feed vector of size 270 (i.e. 90 x 3) that we can reshape in Tensorflow model. For doing this, the input node of shape (None, 270) will be added on top of earlier input node as follows:

X = tf.placeholder(tf.float32, shape=[None,input_width * num_channels], name="input")
X_reshaped = tf.reshape(X,[-1,1,90,3]) 

Now add following lines of code after model training steps to checkpoint and save the graph definition.

with tf.Session() as session:
		------- Model training code goes here ------
	tf.train.write_graph(session.graph_def, '.', '../har.pbtxt'),save_path = "../har.ckpt")

Next, the checkpointed model will be frozen and its optimized version can be saved as follows:

from import freeze_graph
from import optimize_for_inference_lib

freeze_graph.freeze_graph(input_graph = "../har.pbtxt",  input_saver = "",
             input_binary = False, input_checkpoint = "../har.ckpt", output_node_names = "y_",
             restore_op_name = "save/restore_all", filename_tensor_name = "save/Const:0",
             output_graph = "frozen_har.pb", clear_devices = True, initializer_nodes = "")

input_graph_def = tf.GraphDef()
with tf.gfile.Open(output_frozen_graph_name, "r") as f:
    data =

output_graph_def = optimize_for_inference_lib.optimize_for_inference(

f = tf.gfile.FastGFile("optimized_har.pb", "w")

Let's make a simple Android app to get accelerometer data and make predictions about user activities with the learned model. Before doing so, get Tensorflow libraries for Android from the following link. For the purpose of this tutorial, I used the libraries with artifact number 117.

We will now create an empty app and copy libandroid_tensorflow_inference_java.jar, arm64-v8a, armeabi-v7a, x86 and x86_64 files/folders in libs directory of the project. Also, copy the frozen optimized model into an asset directory of the project. The figure shows directory structure after copying all the required files.

Directory Structure

Afterwards, add following entry in build.gradle file to let build system know where files are located.

	main {
		jniLibs.srcDirs = ['libs']

Now we create a class, which will use TensorflowInferenceInterface for feeding data to a model, running inference and getting back results.

import android.content.Context;
import android.content.res.AssetManager;

public class ActivityInference {
    static {

    private static ActivityInference activityInferenceInstance;
    private TensorFlowInferenceInterface inferenceInterface;
    private static final String MODEL_FILE = "file:///android_asset/optimized_har.pb";
    private static final String INPUT_NODE = "input";
    private static final String[] OUTPUT_NODES = {"y_"};
    private static final String OUTPUT_NODE = "y_";
    private static final long[] INPUT_SIZE = {1,270};
    private static final int OUTPUT_SIZE = 6;
    private static AssetManager assetManager;

    public static ActivityInference getInstance(final Context context)
        if (activityInferenceInstance == null)
            activityInferenceInstance = new ActivityInference(context);
        return activityInferenceInstance;

    public ActivityInference(final Context context) {
        this.assetManager = context.getAssets();
        inferenceInterface = new TensorFlowInferenceInterface(assetManager, MODEL_FILE);

    public float[] getActivityProb(float[] input_signal)
        float[] result = new float[OUTPUT_SIZE];
        //Downstairs   Jogging      Sitting  Standing   Upstairs   Walking
        return result;

The rest of the code provided below is from MainActivity class. It is listening to accelerometer sensor and using getActivityProb method of ActivityInference class for getting a probability of each class to update the UI.

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.TextView;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements SensorEventListener {

    private final int N_SAMPLES = 90;
    private static List x;
    private static List y;
    private static List z;
    private static List input_signal;
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private ActivityInference activityInference;

    private TextView downstairsTextView;
    private TextView joggingTextView;
    private TextView sittingTextView;
    private TextView standingTextView;
    private TextView upstairsTextView;
    private TextView walkingTextView;

    protected void onCreate(Bundle savedInstanceState) {
        x = new ArrayList();
        y = new ArrayList();
        z = new ArrayList();
        input_signal = new ArrayList();

        downstairsTextView = (TextView)findViewById(;
        joggingTextView = (TextView)findViewById(;
        sittingTextView = (TextView)findViewById(;
        standingTextView = (TextView)findViewById(;
        upstairsTextView = (TextView)findViewById(;
        walkingTextView = (TextView)findViewById(;

        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, mAccelerometer , 
        activityInference = new ActivityInference(getApplicationContext());

    protected void onPause() {

    protected void onResume() {
        mSensorManager.registerListener(this, mAccelerometer, 

    public void onSensorChanged(SensorEvent event) {

    public void onAccuracyChanged(Sensor sensor, int i) {


    private void activityPrediction()
        if(x.size() == N_SAMPLES && y.size() == N_SAMPLES && z.size() == N_SAMPLES) {
            // Copy all x, y and z values to input_signal
            int i = 0;
            while (i < NUM_SAMPLES) {

            // Perform inference using Tensorflow
            float[] results = activityInference.getActivityProb(toFloatArray(input_signal));


            // Clear all the values
            x.clear(); y.clear(); z.clear(); input_signal.clear();

    private float[] toFloatArray(List list)
        int i = 0;
        float[] array = new float[list.size()];

        for (Float f : list) {
            array[i++] = (f != null ? f : Float.NaN);
        return array;

    public static float round(float d, int decimalPlace) {
        BigDecimal bd = new BigDecimal(Float.toString(d));
        bd = bd.setScale(decimalPlace, BigDecimal.ROUND_HALF_UP);
        return bd.floatValue();

This is all that we need to use Tensorflow on Android for making predictions about user activities. A simple app UI will look something as shown in the screenshot below.

HAR App Screenshot

The complete code of the app is available at the following link. If you have any question, please comment below.