Learn How to Add Object Detection Capabilities to Your Appwrite Storage Using Cloud Functions
Tags: tutorial, serverless, python, machinelearning
Published: 2021-03-18 07:32:19

In this tutorial, we will cover how we can leverage Appwrite’s Cloud functions feature to execute certain tasks when certain events take place in the server. You can find a complete list of available system events [here](https://appwrite.io/docs/webhooks).

In this example, we will demonstrate how we can detect objects in an image that the user has uploaded. This could be useful if you’re building something like google photos where you would like to categorize photos based on objects in the image. 

For the sake of this example, we will be using [Cloudmersive's People and Object Detection API](https://cloudmersive.com/image-recognition-and-processing-api). A similar concept applies to other API providers like Google’s Vision API or Amazon’s Recognition API. So let’s get started. 

## Create your Cloud Function
The first step is to create a Cloudmersive account and obtain the free tier API Key. Now it's time to create your Cloud Function in the Appwrite Console. Head over to the **Functions** section of your console and select **Add Function**.  You can give your function a funky new name and select the preferred environment. We will be using Python for this example.

<p align="center">
    <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qjc5e5evw83feayrfl8i.png" alt="Add Function Dialog" width="600"  />
</p>


## Let's write some code
The next step is to write the code that will be executed and upload it. Create a directory to hold your Cloud Function. Then create your main code file and a `requirements.txt`.

```sh
$ mkdir cloud-functions-demo
$ cd cloud-functions-demo
$ touch main.py
$ touch requirements.txt
```

We will be using two dependencies for this example
1. cloudmersive-image-api-client
2. appwrite

Add these to your `requirements.txt`. We would typically perform a `pip install` at this stage but that would install the libraries in the shared libraries path.  We need the libraries to be installed in the same folder so that they can be packaged easily. 

Run the following command to install the libraries inside the local `.appwrite` directory. Appwrite’s Python environment will know how to autoload a file from that directory without any special configuration.

```sh
PIP_TARGET=./.appwrite pip install -r ./requirements.txt --upgrade --ignore-installed
```

Great. It’s time to start editing the `main.py` file. We start by importing the relevant libraries.

```py
from __future__ import print_function
import time
from pprint import pprint
import json 
import os

# For cloud vision API
import cloudmersive_image_api_client
from cloudmersive_image_api_client.rest import ApiException

# Appwrite SDK
from appwrite.client import Client
from appwrite.services.storage import Storage
```

We will use a temporary file to access the image during the function execution. Let’s give this file a name.  This is required because the Cloudmersive library expects a file path and not binary data as input.

```sh
FILENAME = "temp.jpg"
```

When a function is triggered by an event, we can obtain a lot of metadata about the event from some special environment variables that are set by Appwrite. A complete list is available [here](https://appwrite.io/docs/functions). In our case, we need the ID of the file that was uploaded in order to fetch it. Appwrite conveniently exposes this information as an environment variable named `APPWRITE_FUNCTION_EVENT_PAYLOAD`. Let’s parse this JSON string to retrieve the file ID.

```py
# Triggered by the storage.files.create event
payload = json.loads(os.environ["APPWRITE_FUNCTION_EVENT_PAYLOAD"])
fileID = payload["$id"]
```

Now it’s time to set up the Appwrite SDK 

```py
# Setup appwrite client
client = Client()
client.set_endpoint('http://192.168.1.6/v1') # PRIVATE IP OF YOUR APPWRITE CONTAINER
client.set_project('5fca866c65afc') # YOUR PROJECT ID
client.set_key(os.environ["APPWRITE_KEY"])
```

> Note: Within the Cloud Function, you cannot use localhost to refer to your Appwrite server, because localhost refers to your own runtime environment. You will have to find the private IP of your default network interface using ifconfig (usually eth0 in linux or en0 in macOS). 

Using the SDK, lets fetch the image and save it:
```py
# Get the image file 
storage = Storage(client)
result = storage.get_file_preview(fileID)

# Save the file to the container
with open(FILENAME, "wb") as newFile:
    newFile.write(result)
```

We’re almost done. Now we will set up the Cloudmersive SDK and make our API request

```py
# Configure API key authorization: Apikey
configuration = cloudmersive_image_api_client.Configuration()
configuration.api_key['Apikey'] = os.environ['API_KEY']

# create an instance of the API class
api_instance = cloudmersive_image_api_client.RecognizeApi(cloudmersive_image_api_client.ApiClient(configuration))
image_file = FILENAME # file | Image file to perform the operation on.  Common file formats such as PNG, JPEG are supported.

try:
    # Detect objects including types and locations in an image
    api_response = api_instance.recognize_detect_objects(image_file)
    pprint(api_response)
except ApiException as e:
    print("Exception when calling RecognizeApi->recognize_detect_objects: %s\n" % e)
```

## Packaging the Cloud function
Before we can package our cloud function, we need to ensure that our directory has the following structure.

```sh
.
├── .appwrite/
├── main.py
└── requirements.txt
```

We package the function by creating a tar file out of our folder. 
```sh
$ cd ..
$ tar -zcvf code.tar.gz cloud-functions-demo
```

We can now upload this tarfile to our function’s dashboard by selecting the **Deploy Tag** option. Our entry point command, in this case, would be:

```sh
$ python main.py
```

> Note: You can also automate the code upload process using the Appwrite server SDK latest version and your CI server. In future versions, Appwrite will also support this process using a dedicated CLI tool, directly from your terminal.


<p align="center">
    <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/geg1eagy5zj24f12onu3.png" alt="Deploy Tag" width="600"  />
</p>


Once created, we need to define a trigger for the function. In our case, we wish to trigger it whenever a new file is uploaded to the Appwrite server.  So we would be interested in the `storage.files.create` event. The trigger can be enabled under the **Settings tab** of the function. 

<p align="center">
    <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0o98ssrzhz6c99vhpfd0.png" alt="Function Settings" width="600"  />
</p>

Once the triggers are enabled, it’s time for our final step, **Function Variables**. Appwrite allows you to securely store secret keys using Appwrite Function Variables which will be available as environment variables to your program. The best part is that these keys are encrypted and stored securely on Appwrite’s internal DB. In this example, we have used two environment variables namely `API_KEY` (Cloudmersive API Key) and `APPWRITE_KEY` (Appwrite API Key) so let’s add them to the Function Variables. Don’t forget to click the **Update option** once you’re happy with your settings.

Great! We’re done with all the setup. All that’s left now is to test the Cloud Function.


# Testing
Now it’s time to test your shiny new Cloud Function! Head over to the **Storage** section of Appwrite and create a new file by clicking on the ‘+’ button at the bottom right. Choose an image of your choice and click **Create**. 
<p align="center">
    <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppkjngbcc8bup12xxspr.png" alt="Upload File" width="600"  />
</p>

Your Cloud Function would now have been triggered. You can check it out by heading over to `Functions > Your Function Name > Logs`

<p align="center">
    <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jx53noohl1fu458ji0gu.png" alt="Execution Logs" width="600"  />
</p>
Once the execution is complete, you can check the response from the API. 

<p align="center">
    <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qv4sv2k6offemn4etg1r.png" alt="API Response" width="600"  />
</p>

And in a few simple steps, we successfully deployed our first Cloud Function. The possibilities with Cloud Functions are endless! Stay tuned for more Cloud Function ideas from the Appwrite Team.

## Learn More
* You can find the complete code sample and lots of other demos in our [Cloud Functions Demo Repo](https://github.com/appwrite/demos-for-functions/tree/master/python/object-detection).  
* Check out [Appwrite's](https://github.com/appwrite/appwrite) Github Repo.
* [Our Discord Server](https://appwrite.io/discord) is the place to be if you ever get stuck.
* You can find all our Documentation [here](https://appwrite.io/docs). 

## Credits
Photo by <a href="https://unsplash.com/@tetrakiss?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Arseny Togulev</a> on <a href="/s/photos/machine-learning?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>