Introduction to AWS Step functions by Automated Image-Processing Using Amazon Rekognition
Tags: stepfunctions, lambda, rekognition, aws
Published: 2023-12-10 17:23:07


Hi everyone,

We are implementing an automated image-processing  workflow using state machines in step functions. It will cover the basics of step functions workflow 

Before jumping into the topic let me introduce the step functions and terminology we are going to use in this article

**Step functions: **It’s a managed service that helps to combine AWS services to create workflows. It will provide a visual workflow editor. We can just drag and drop the services and combine them to create a workflow

**Terminology:**

**State:** Each step in the workflow is defined as a state. A state will have input, outputs, and error-handling

**State Machine:** A workflow is defined as a state machine. It contains the states. By default, the editor will provide **start **and **end **states

![An empty visual state machine editor](https://cdn-images-1.medium.com/max/3810/1*cjhBl04K6F7GjscavTrBKA.png)

**Overview:** Here we will create 4 lambda functions and connect them using the step functions to automate image processing using the AWS Rekognition service

**API-receiver:** This lambda function receives the image through API-Gateway and triggers the state machine

**Image-uploader:** This function will upload the image to s3 bucket and return the image data

**Object-receiver:** This function will receive the image data pass that info to the AWS recognition service and return that data

**Store-to-dynamo-db:** This function will take the image info and store that data in the Dynamo Db

Let’s start 

## **Step 1: Create a lambda function to upload images to S3**

Here is the code for the lambda function

    import json
    import base64
    import boto3
    from datetime import datetime
    
    
    def lambda_handler(event, context):
        
        ##rececing base 64 image and data and decoding it
        image_data = base64.b64decode(event['base64Image'])
        
        s3 = boto3.client("s3")
        
        ##generating the name for the image to upload to s3
        now = datetime.now()
        date_time_string = now.strftime("%Y-%m-%d %H:%M:%S")
        
        bucket_name = "images-rek-test"
        object_key = f"{date_time_string}.png"
        region = "us-west-2"
        
         
        
        try:
            ##uploading image to s3
            s3.put_object(Bucket=bucket_name, Key=object_key, Body=image_data)
            
            response = {
                "bucket_name":bucket_name,
                "object_key":object_key,
                "region":region
            }
            
            return {
                'statusCode':200,
                'body': response
            }
        except Exception as e:
            print(e)
            return {
                'statusCode': 500,
                'body': json.dumps("Error in uploading image")
            }

## **Step 2: Create a lambda function for image processing:**

Here is the code

    import json
    import boto3
    
    def lambda_handler(event, context):
       
        s3_client = boto3.client('s3')
        rekognition_client = boto3.client('rekognition')
           
        bucket_name = event["body"]['bucket_name']
        object_key = event["body"]['object_key']
        
        
        print(f"keys {bucket_name} and {object_key}")
        
        ##sending uploaded image info to rekognition service
        response = rekognition_client.detect_labels(
            Image={'S3Object': {'Bucket': bucket_name, 'Name': object_key}},
            MaxLabels=10
            )
        
        print(response)
        
        return {
            'statusCode': 200,
            'body': response
        }

## **Step 3: Create a Lambda function to store data in DynamoDb**

* Create a table in the dynamo DB with id as the primary key

Here is the code

    import json
    import boto3
    from datetime import datetime
    from decimal import Decimal
    
    ##converting floats to decimal as dynamo db won't support
    def convert_floats_to_decimal(obj):
        if isinstance(obj, float):
            return Decimal(str(obj))
        elif isinstance(obj, dict):
            return {k: convert_floats_to_decimal(v) for k, v in obj.items()}
        elif isinstance(obj, list):
            return [convert_floats_to_decimal(v) for v in obj]
        else:
            return obj
    
    
    
    def lambda_handler(event, context):
        
        dynamodb = boto3.resource('dynamodb')
        table = dynamodb.Table('image_data')##table name in dynamo db
        
        
        rekognition_data = convert_floats_to_decimal(event['body'])
        
        ##generate an id with date and time to store in table
        now = datetime.now()
        date_time_string = now.strftime("%Y-%m-%d %H:%M:%S")
        
        item = {
            'id': date_time_string, 
            'RekognitionResponse': rekognition_data
        }
    
        table.put_item(Item=item)##stroing the data to db
    
        return {
            'statusCode': 200,
            'body': 'Data stored in DynamoDB successfully'
        }
    
        

* Now we have the Lambda functions ready, Let’s create the state machine in step function

## **Step 4: Create a state machine in step functions:**

* Visit the step functions service from the AWS search bar

* Click on create state machine and select a blank template

* From the side menu drag and drop the Lambda invoke

![](https://cdn-images-1.medium.com/max/2000/1*-8T2S8gK-ofQ45XHUTk7Hw.png)

* From the right side menu update the lambda configuration, input, and output

![](https://cdn-images-1.medium.com/max/2000/1*f6lUSHR-Htm5PY-yUEzkFA.png)

* Give a name for the state, From API Parameters select the function

* Leave the input empty

* Transform the output like this to pass info to the next state

![](https://cdn-images-1.medium.com/max/2000/1*0lymdLRQ1fQ2NJLzf44eXw.png)

. This is the state the machine looks like now

![](https://cdn-images-1.medium.com/max/2000/1*Eg6lBUCuUWZrKPZHeoWiHg.png)

* Drag another Lambda invoke state and keep it under the first function

![](https://cdn-images-1.medium.com/max/2000/1*3l-EmVxBqIcTfyST2U7s0A.png)

* Configure the second Lambda function configuration, Input and output like this

* Update the state name, select the lambda function, leave the input as it is, and change the output like the previous state

![](https://cdn-images-1.medium.com/max/2000/1*vHAvVA14pfYjBLscCWnBVQ.png)

![](https://cdn-images-1.medium.com/max/2000/1*vEXuUgVgLf44M_RG0IrSVg.png)

* Now drag and drop another Lambda invoke function below the second Lambda function

![](https://cdn-images-1.medium.com/max/2000/1*qSVL8wic75PceXjlEDFHjQ.png)

* Update the Lambda function configuration, leave the input as it is, and output like this

![](https://cdn-images-1.medium.com/max/2000/1*PGkYhozPR_ZU_5Jyz_OS0w.png)

![](https://cdn-images-1.medium.com/max/2000/1*AtmBuiCSqX7RdfjMaW3ghA.png)

* Save the state machine from the top right corner

* While creating the state machine you can test each state by selecting it. 

## **Step 5: Create a Lambda function to trigger this state machine**

* Copy the state machine ARN and paste it into the state_machine_arn variable

    import json
    import boto3
    
    def lambda_handler(event, context):
        
        client = boto3.client('stepfunctions') 
        
        state_machine_arn ="arn:aws:states:{region}:{accountid}:stateMachine:MyStateMachine-jz7b4j339"
        
        input_data = json.dumps(event)
        
        ##trigger the state machine
        response= client.start_execution(
            stateMachineArn=state_machine_arn,
            input=input_data
            )
            
            
        return{
            'statusCode':200,
            'body': json.dumps('Successfully started state machine')
        }

## **Step 6: Create an API-Gateway API to trigger the lambda function**

* Visit the API-Gateway service from the AWS Search bar and visit the API Gateway service

* Create a POST call click on the integration request and select the lambda function we created above in the mapping template enter the code like this

![](https://cdn-images-1.medium.com/max/2000/1*nosSvoJB6m62IfrreBK40Q.png)

![](https://cdn-images-1.medium.com/max/2000/1*Q-4gJe3BRAmv8J-hlpSZBQ.png)

* From the API Setting enable binary media types and enter image/png as the media type

![](https://cdn-images-1.medium.com/max/3704/1*WdJrMRhZTcI3EXvwgp1L8w.png)

* Deploy the API and copy the API URL and from the postman trigger that URL

* It will start the State machine and you can see the results stored in the dynamo DB

* You can see the execution flow from the state machine below and see the results in the dynamo db

![](https://cdn-images-1.medium.com/max/3704/1*piNb6TAtn3lwaYJOVYanmw.png)

![](https://cdn-images-1.medium.com/max/2476/1*9ieMvyCCHjZZisu0sLOe8g.png)

* That’s it. We successfully created a state machine to process the images 

* Go to the step function and visit all the features available, keep on trying other workflows

* There will be another article with more info about workflows

**Limitations: Here state machine input accepts only around 250kb of data, so if you want to input bigger images better to do them from S3 Bucket directly.**