Image Processing using Google Cloud Functions
Tags: googlecloud, microservices, serverless, python
Published: 2020-08-02 12:07:05

In this blog I am going to share how to create a microservice using Google Cloud Function. The objective is to reformat image when it gets uploaded to Google Cloud Storage bucket.


## Setup
I am using Google cloud shell to run all the command. For details about the Google cloud shell please follow [official documentation](https://cloud.google.com/shell/docs/).

## Solution
According to [Google](https://cloud.google.com/storage),
> Cloud Storage provides worldwide, highly durable object storage that scales to exabytes of data. You can access data instantly from any storage class, integrate storage into your applications with a single unified API, and easily optimize price and performance.


**Step 1**: Create two GCS buckets using gcloud commands. One for source image sequence and another for processed image sequence.

```bash
export SOURCE_BUCKET_NAME=$DEVSHELL_PROJECT_ID-source
export PROCESSED_BUCKET_NAME=$DEVSHELL_PROJECT_ID-processed

echo "Creating source bucket: gs://$SOURCE_BUCKET_NAME"
gsutil mb gs://$SOURCE_BUCKET_NAME

echo "Creating processed bucket: gs://$PROCESSED_BUCKET_NAME"
gsutil mb gs://$PROCESSED_BUCKET_NAME
```

**Step 2**: Create a Google Cloud Function using gcloud.
> Cloud Functions is Google Cloud’s event-driven serverless compute platform. Run your code locally or in the cloud without having to provision servers. Go from code to deploy with continuous delivery and monitoring tools. Cloud Functions scales up or down, so you pay only for compute resources you use. Easily create end-to-end complex development scenarios by connecting with existing Google Cloud or third-party services.

Now, we will create a [google cloud function](https://cloud.google.com/functions/docs/) which will get [triggered](https://cloud.google.com/functions/docs/concepts/events-triggers) when the image gets uploaded to the storage.

Cloud function’s entry point must be defined in a Python source file called `main.py` Details on Python runtime can be found [here](https://cloud.google.com/functions/docs/concepts/python-runtime#source_code_structure). Add the following function to `main.py`.

```python
import os
import cv2
import numpy as np
from google.cloud import storage
from tempfile import NamedTemporaryFile


def reformat_image(event, context):
    """Triggered by a change to a Cloud Storage bucket.
    Args:
         event (dict): Event payload.
         context (google.cloud.functions.Context): Metadata for the event.
    """
    file = event

    client = storage.Client()
    source_bucket = client.get_bucket(file['bucket'])
    source_blob = source_bucket.get_blob(file['name'])

    image = np.asarray(bytearray(source_blob.download_as_string()), dtype="uint8")
    image = cv2.imdecode(image, cv2.IMREAD_UNCHANGED)

    scale_percent = 50  # percent of original size
    width = int(image.shape[1] * scale_percent / 100)
    height = int(image.shape[0] * scale_percent / 100)
    dim = (width, height)

    # resize image
    resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)

    with NamedTemporaryFile() as temp:
        # Extract name to the temp file
        temp_file = "".join([str(temp.name), file['name']])

        # Save image to temp file
        cv2.imwrite(temp_file, resized)

        # Uploading the temp image file to the bucket
        dest_filename = file['name']
        dest_bucket_name = os.environ.get('PROCESSED_BUCKET_NAME', 'Specified environment variable is not set.')
        dest_bucket = client.get_bucket(dest_bucket_name)
        dest_blob = dest_bucket.blob(dest_filename)
        dest_blob.upload_from_filename(temp_file)
```

Add `requirements.txt` with the following content:

```
opencv-python==4.2.0.34
numpy==1.18.2
google-cloud-storage==1.27.0
```

**Step 3**: Deploy function
```
gcloud functions deploy reformat_image --set-env-vars PROCESSED_BUCKET_NAME=$PROCESSED_BUCKET_NAME --runtime python37 --trigger-resource $SOURCE_BUCKET_NAME --trigger-event google.storage.object.finalize
```
The above-mentioned command will deploy the function. This function will get triggered in the event of storage object creation. For further details on cloud function triggers please see the [official documentation](https://cloud.google.com/functions/docs/calling/storage).

Now, we will copy an image to the bucket to test our recently deployed function. To copy the image, please run the command as mentioned below:

```bash
gsutil cp /path/to/images gs://$SOURCE_BUCKET_NAME
```

The command mentioned above should create a new object in the storage bucket. This event should trigger the cloud function. To verify this, we will run the following command and see the logs:

```bash
gcloud functions logs read reformat_image --limit 50
```

Now, let’s list the output images from destination bucket

```bash
gsutil ls gs://$PROCESSED_BUCKET_NAME
```

## Summary

In this blog I used Google Cloud Platform managed services to resize images without deploying any server. To avoid any charges on GCP, make sure to delete the GCS buckets and Cloud Functions.

Hope this blog helps you to understand Cloud Function and it’s usefulness.

Get in touch on [LinkedIn](https://uk.linkedin.com/in/dheerajbhadani) or [Twitter](https://twitter.com/dheerajbhadani).