In this lab, we will create a Cloud Function that will be triggered when an image is uploaded to a storage bucket. It will then analyze the image for violent, gory content and create a new image that blurs the content out.

Bring up a Cloud Shell session and clone the Python samples repository. Change into the directory containing the function.

git clone

cd python-docs-samples/functions/imagemagick

Create a storage bucket for uploading images to that has a globally unique bucket name.

gsutil mb gs://<UNIQUE_BUCKET_INPUT>

Create another storage bucket for the Cloud Function to store the blurred images to that also has a globally unique bucket name.

gsutil mb gs://<UNIQUE_BUCKET_OUTPUT>

We will be using a number of GCP APIs for the lab. One of them is GCP's Cloud Vision API so that we can analyze the content of images. This service contains machine learning models that have been trained using Google's massive collection of labeled images. Ensure that the Cloud Vision API has been enabled. Otherwise, this can be done via the web console by visiting "API & Services".

Then searching for "Cloud Vision"

Then enabling the API.

It can also be done with a single Cloud Shell command below:

gcloud services enable

Repeat the process of enabling APIs for the Cloud Functions API and the Cloud Build API using the UI as above or via command-line. These APIs allow us to build the function and deploy it on GCP.

gcloud services enable
gcloud services enable

The code for this Cloud Function is written in Python and resides in It first imports the packages we rely upon (e.g. the Google Cloud SDK's storage and vision packages) as well as ImageMagick, an open-source image manipulation toolkit. It then instantiates a storage client to interact with buckets as well as a vision client to interact with Google's VIsion API. The Cloud Vision service supports several different types of clients. Since we are only looking to determine the goriness of an image, we instantiate an image annotation client.

import os
import tempfile

from import storage, vision
from wand.image import Image

storage_client = storage.Client()
vision_client = vision.ImageAnnotatorClient() 

The main function is blur_offensive_images. When triggered, the function is passed information about an event that it can use to process it (data, context). The function first retrieves the file_name and the bucket_name that triggered the event. It pulls the file into the program as a blob, then begins the construction of a dictionary that includes the URI of the new object that it can then send to the Vision API as JSON. Note the use of Python 3.6 F-string (concise formatted string).

# Blurs uploaded images that are flagged as Adult or Violence.
def blur_offensive_images(data, context):
    file_data = data
    file_name = file_data['name']
    bucket_name = file_data['bucket']

    blob = storage_client.bucket(bucket_name).get_blob(file_name)
    blob_uri = f'gs://{bucket_name}/{file_name}'
    blob_source = {'source': {'image_uri': blob_uri}}

The function then calls the safe_search_detection functionality of the Vision annotation client to have the Vision API detect what's in the picture with the result being stored in detected. Safe search detects inappropriate image content. The ratings range from 0 to 5, with 5 being inappropriate. As the code shows, when Vision annotates an image with a 5 for either adult content or violent (bloody) content, a function to blur the image is then called with the blob that has been downloaded from the bucket earlier.

    result = vision_client.safe_search_detection(blob_source)
    detected = result.safe_search_annotation

    # Process image
    if == 5 or detected.violence == 5:
        print(f'The image {file_name} was detected as inappropriate.')
        return __blur_image(blob)
        print(f'The image {file_name} was detected as OK.') 

Visit the file and examine the code for __blur_image. Answer the following questions for your lab notebook:

Once blurred, the function uses the storage client to get a handle on the output storage bucket, creates a new blob in the bucket with the same file_name, then uploads the image to it. Finally, it removes the temporary file created previously.

    # Upload result to a second bucket, to avoid re-triggering the function.
    blur_bucket_name = os.getenv('BLURRED_BUCKET_NAME')
    blur_bucket = storage_client.bucket(blur_bucket_name)
    new_blob = blur_bucket.blob(file_name)
    print(f'Blurred image uploaded to: gs://{blur_bucket_name}/{file_name}')

    # Delete the temporary file.

We will now deploy the function. As part of the trigger, we need to specify the triggering event. In this case, it needs to be triggered when a new file appears in the input storage bucket as indicated in the command below with the --trigger-bucket flag. The function reads the output bucket name from environment variables so we need to set them in our deployment command.

gcloud functions deploy blur_offensive_images \
  --runtime python37 \
  --trigger-bucket <UNIQUE_BUCKET_INPUT> \

In Cloud Shell, use wget to download an image of a flesh-eating zombie at

Then, upload the image to the input bucket via the web console or command-line below:

gsutil cp zombie*.jpg gs://<UNIQUE_BUCKET_INPUT>

Once uploaded, the function should automatically execute via the bucket trigger and blur the image. Then, find and download two more gory images from the Internet. Upload them to the input bucket.

The log entries for the functions can be read via the web console or command-line via

gcloud functions logs read

Delete the function you created in the web console of Cloud Functions or from the command line in Cloud Shell.

gcloud functions delete blur_offensive_images

Delete the two storage buckets you created either in the web console or from the command line.

gsutil rm -r gs://<UNIQUE_BUCKET_INPUT>
gsutil rm -r gs://<UNIQUE_BUCKET_OUTPUT>