# Tablevision Code Documentation

This code documentation will walk you through the main processing logic, which is our Processer component. Please see the component breakdown for more information on the Tablevision components.

The Processer component is deployed on the Cloud on an AWS EC2 instance.

# Tablevision Processer

# Table object

Table objects have the following parameters:

class Table:
    table_id = None
    session_id = None
    session_start = None
    session_end = None
    states = None
    coords = []

coords refer to the coordinates provided by the tablevision_initialiser. For example:

[[0.03948170731707307, 0.09692911255411243], [0.4337906504065041, 0.09692911255411243], [0.03948170731707307, 0.4648944805194804], [0.4337906504065041, 0.4648944805194804]]

Four coordinates that refer to the 4 points of the square when initialising Tablevision in the GUI.


In Table objects, there are the following callable functions:

start_session(state) Starts a new patron session.

end_session() Ends the current running patron session.

reset_session() Resets a session. Called after end_session().

update_db(session_status="") Updates our MongoDB database.

did_change_state(new_state) Tracks whether the new_state variable is different from the current state. If it is, change state. If not, new_state is ignored.

is_valid_session() If the session has ended, track whether it is a "valid" session i.e. greater than 3 mins. If not, invalidate the session.

within_coordinates(x, y) Track if an object predicted is within the coordinates of the Table

# 1. Initialising tables

In app.py, we refer to the following API endpoint to initialise tables, initialise():

# initialise once pi has started
TABLES = {}

@app.route('/initialise', methods=['POST'])
def initialise():
    # to be able to access the TABLES variable and add objects in

    data = request.get_json()
    tables_json = json.loads(data['tables_json'])

    try:
        for table_number in tables_json:
            table_object = tables_json[table_number]
            table_number = int(table_number)
            TABLES[table_number] = Table(table_number, table_object)
        
        return jsonify({"type": "success", "message": "Tables successfully set up"}), 200
    except Exception as e:
        traceback.print_exc()
        return jsonify({"type": "error", "message": "unexpected error has occured", "debug": str(e)}), 500

# 2. Process image frame

We used the process() function to receive the images from our node (Rasperry Pi) and call the Google AutoML Vision API to receive results of objects predicted within the frame:

@app.route('/process', methods=['POST'])
def process():
    try:
        data = request.get_json()
        image64 = data['image64']

        # decode the image back to image
        image_data =  base64.b64decode(image64)
        filename = '/home/ubuntu/images/' + time.strftime("%Y%m%d-%H%M%S") + '.jpg'
        with open(filename, 'wb') as f:
            f.write(image_data) # save it to test.jpg

        with open('/home/ubuntu/processer/static/image.jpg', 'wb') as f:
            f.write(image_data) # save it to for rendering to image
        
        ########## Call the Google AutoML API ##########

        image = automl.Image(image_bytes=image_data)

        predictions = makeGoogleRequest(image)
        
        ########## Ended calling the Google AutoML API ##########

        # process the objects and update if got people or crockeries
        location_of_objects = processPrediction(predictions)

        # update table state liao
        updateTable(location_of_objects)

        return jsonify({"type": "success"}), 200
    except Exception as e:
        return jsonify({"type": "error", "debug": str(e)}), 500

# 3. Process prediction results

To process our prediction results returned by the Google AutoML Vision object detection API, we used the processPrediction(predictions) function:

def processPrediction(predictions):
    location_of_objects = {
        "people": [], 
        "crockeries": []
    }

    for prediction in predictions:
        formatted_object = resultFormatter(prediction)

        # boolean and table number
        item_within_table, table_number = itemWithinTable(formatted_object)

        # basically means if there is object within the table, then i shall update
        # either people or crockeries
        if item_within_table:
            if hasPeople(formatted_object):
                if table_number not in location_of_objects["people"]:
                    # means this prediction predicted succesfully a people within that table
                    location_of_objects["people"].append(table_number)

            if hasCrockeries(formatted_object):
                if table_number not in location_of_objects["crockeries"]:
                    location_of_objects["crockeries"].append(table_number)

    return location_of_objects

# 4. Update table state

Next, using the data of people and/or crockeries in each table, we will update the table status respectively:

def updateTable(location_of_objects):

    global TABLES

    for table_number in TABLES:
        if table_number in location_of_objects["people"]:
            # state 2: has people
            TABLES[table_number].did_change_state(2)
            # TABLES[table_number].print_states()
        elif table_number in location_of_objects["crockeries"] and table_number not in location_of_objects["people"]:
            # state 1: has crockeries, no people
            TABLES[table_number].did_change_state(1)
            # TABLES[table_number].print_states()
        else:
            # state 0: nothing
            TABLES[table_number].did_change_state(0)
            # TABLES[table_number].print_states()

# Summary

README

Check out our Github Repository to see our source codes.

Steps 2-4 will be in a constant loop to update table state changes so long as the Raspberry Pi node continues to send the data.