User Tools

Site Tools


wiki:ai:azure_function_app_to_perform_image_blur

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
wiki:ai:azure_function_app_to_perform_image_blur [2025/07/03 17:11] – created ymurugesanwiki:ai:azure_function_app_to_perform_image_blur [2025/07/03 18:20] (current) ymurugesan
Line 5: Line 5:
  
 Install Azure CLI Install Azure CLI
-bash# Windows+# Windows
 curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
  
Line 14: Line 14:
  
 Install Azure Functions Core Tools Install Azure Functions Core Tools
-bash# Windows (via npm)+<code> 
 +# Windows (via npm)
 npm install -g azure-functions-core-tools@4 --unsafe-perm true npm install -g azure-functions-core-tools@4 --unsafe-perm true
  
 +</code>
 +<code>
 # macOS # macOS
 brew tap azure/functions brew tap azure/functions
 brew install azure-functions-core-tools@4 brew install azure-functions-core-tools@4
 +</code> 
 +<code>
 # Linux # Linux
 curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
Line 28: Line 32:
 sudo apt-get install azure-functions-core-tools-4 sudo apt-get install azure-functions-core-tools-4
  
 +</code>
 +
 +Step 1: Login to Azure
 +<code>
 +az login
 +
 +
 +Step 2: Set Your Subscription (if you have multiple)
 +bash# List subscriptions
 +az account list --output table
 +
 +# Set active subscription
 +
 +az account set --subscription "Your-Subscription-Name-Or-ID"
 +</code>
 +
 +Step 3: Create Resource Group (if needed)
 +bash
 +
 +<code>
 +az group create --name myResourceGroup --location eastus
 +</code>
 +
 +Step 4: Create Storage Account (for Function App)
 +bash
 +<code>
 +az storage account create \
 +  --name myfunctionstorageacct \
 +  --location eastus \
 +  --resource-group myResourceGroup \
 +  --sku Standard_LRS
 +</code>
 +Step 5: Create Function App
 +bash
 +
 +<code>
 +az functionapp create \
 +  --resource-group myResourceGroup \
 +  --consumption-plan-location eastus \
 +  --runtime python \
 +  --runtime-version 3.9 \
 +  --functions-version 4 \
 +  --name myFaceBlurFunctionApp \
 +  --storage-account myfunctionstorageacct \
 +  --os-type linux
 +</code>
 +
 +Step 6: Prepare Your Project Structure
 +Create the following folder structure:
 +face-blur-function/
 +├── requirements.txt
 +├── host.json
 +├── local.settings.json
 +├── FaceBlurTimer/
 +│   ├── __init__.py
 +│   └── function.json
 +└── shared/
 +    └── face_blur_processor.py
 +    
 +    
 +Step 7: Create Project Files
 +requirements.txt
 +<code>
 +txtazure-functions
 +azure-storage-blob
 +requests
 +python-dotenv
 +Pillow
 +schedule
 +</code>
 +
 +host.json
 +
 +<code>
 +{
 +  "version": "2.0",
 +  "logging": {
 +    "applicationInsights": {
 +      "samplingSettings": {
 +        "isEnabled": true,
 +        "excludedTypes": "Request"
 +      }
 +    }
 +  },
 +  "extensionBundle": {
 +    "id": "Microsoft.Azure.Functions.ExtensionBundle",
 +    "version": "[2.*, 3.0.0)"
 +  },
 +  "functionTimeout": "00:10:00"
 +}
 +</code>
 +
 +local.settings.json
 +<code>
 +{
 +  "IsEncrypted": false,
 +  "Values": {
 +    "AzureWebJobsStorage": "your_function_storage_connection_string",
 +    "FUNCTIONS_WORKER_RUNTIME": "python",
 +    "AZURE_STORAGE_CONNECTION_STRING": "your_blob_storage_connection_string",
 +    "AZURE_CONTAINER_NAME": "your_source_container_name",
 +    "WEBAPP_URL": "https://your-webapp.com/blur",
 +    "WEBAPP_TIMEOUT": "60"
 +  }
 +}
 +<code>
 +
 +FaceBlurTimer/function.json
 +<code>
 +{
 +  "scriptFile": "__init__.py",
 +  "bindings": [
 +    {
 +      "name": "mytimer",
 +      "type": "timerTrigger",
 +      "direction": "in",
 +      "schedule": "0 0 * * * *"
 +    }
 +  ]
 +}
 +
 +</code>
 +Step 8: Set Application Settings
 +<code>
 +
 +# Set your environment variables
 +az functionapp config appsettings set \
 +  --name myFaceBlurFunctionApp \
 +  --resource-group myResourceGroup \
 +  --settings \
 +  "AZURE_STORAGE_CONNECTION_STRING=your_blob_storage_connection_string" \
 +  "AZURE_CONTAINER_NAME=your_source_container_name" \
 +  "WEBAPP_URL=https://your-webapp.com/blur" \
 +  "WEBAPP_TIMEOUT=60"
 +  
 +</code>
 +Step 9: Initialize Function Project Locally
 +
 +<code>
 +# Create project directory
 +mkdir face-blur-function
 +cd face-blur-function
 +# Initialize function project
 +func init --python
 +</code>
 +
 +Step 10: Create Timer Function
 +
 +<code>
 +
 +func new --name FaceBlurTimer --template "Timer trigger"
 +
 +<code>
 +
 +Step 11: Deploy to Azure
 +
 +<code>
 +# Deploy the function
 +func azure functionapp publish myFaceBlurFunctionApp
 +
 +</code>
 +
 +Alternative: Deploy using Azure CLI (Zip Deploy)
 +If you prefer to deploy using zip:
 +<code>
 +Create deployment package
 +zip -r deployment.zip .
 +</code>
 +
 +# Deploy using az cli
 +
 +<code>
 +az functionapp deployment source config-zip \
 +  --resource-group myResourceGroup \
 +  --name myFaceBlurFunctionApp \
 +  --src deployment.zip
 +
 +</code>
 +
 +function_app.py
 +
 +<code>
 +
 +import azure.functions as func
 +import datetime
 +import json
 +import logging
 +
 +app = func.FunctionApp()
 +</code>
 +
 +
 +face_blur_processor.py
 +
 +<code>
 +
 +
 +"""
 +Face Blur Processor Module for Azure Functions
 +"""
 +
 +import os
 +import requests
 +import logging
 +import random
 +from datetime import datetime
 +from azure.storage.blob import BlobServiceClient
 +from azure.core.exceptions import AzureError
 +
 +# Configure logging for Azure Functions
 +logger = logging.getLogger(__name__)
 +
 +class FaceBlurProcessor:
 +    def __init__(self):
 +        # Azure Blob Storage configuration
 +        self.connection_string = os.environ.get('AZURE_STORAGE_CONNECTION_STRING')
 +        self.container_name = os.environ.get('AZURE_CONTAINER_NAME', 'images')
 +        
 +        # Web app configuration
 +        webapp_url = os.environ.get('WEBAPP_URL', 'https://your-webapp.com/blur')
 +        # Auto-fix URLs missing scheme
 +        if not webapp_url.startswith(('http://', 'https://')):
 +            webapp_url = f'https://{webapp_url}'
 +        self.webapp_url = webapp_url
 +        self.webapp_timeout = int(os.environ.get('WEBAPP_TIMEOUT', '60'))
 +        
 +        # Initialize blob service client
 +        if not self.connection_string:
 +            raise ValueError("AZURE_STORAGE_CONNECTION_STRING environment variable is required")
 +        
 +        self.blob_service_client = BlobServiceClient.from_connection_string(self.connection_string)
 +
 +    def ensure_container_exists(self):
 +        """Ensure the container exists, create it if it does not exist."""
 +        try:
 +            container_client = self.blob_service_client.get_container_client(self.container_name)
 +            if not container_client.exists():
 +                logger.info(f"Container '{self.container_name}' does not exist. Creating it...")
 +                container_client.create_container()
 +                logger.info(f"Created container: {self.container_name}")
 +            return True
 +        except AzureError as e:
 +            logger.error(f"Error ensuring container exists: {e}")
 +            return False
 +        
 +    def get_random_image(self):
 +        """Get a random image from the blob container."""
 +        try:
 +            # Ensure container exists first
 +            if not self.ensure_container_exists():
 +                return None, None
 +                
 +            container_client = self.blob_service_client.get_container_client(self.container_name)
 +            
 +            # List all blobs
 +            blobs = list(container_client.list_blobs())
 +            if not blobs:
 +                logger.warning(f"No images found in container '{self.container_name}'")
 +                logger.info("Please upload some images to the container first")
 +                return None, None
 +            
 +            # Select a random blob
 +            random_blob = random.choice(blobs)
 +            logger.info(f"Randomly selected image: {random_blob.name}")
 +            logger.info(f"Total images available: {len(blobs)}")
 +            
 +            return random_blob.name, random_blob
 +            
 +        except AzureError as e:
 +            logger.error(f"Error accessing blob storage: {e}")
 +            return None, None
 +    
 +    def get_latest_image(self):
 +        """Get the most recent image from the blob container (kept for backward compatibility)."""
 +        try:
 +            # Ensure container exists first
 +            if not self.ensure_container_exists():
 +                return None, None
 +                
 +            container_client = self.blob_service_client.get_container_client(self.container_name)
 +            
 +            # List all blobs and find the most recent one
 +            blobs = list(container_client.list_blobs())
 +            if not blobs:
 +                logger.warning(f"No images found in container '{self.container_name}'")
 +                logger.info("Please upload some images to the container first")
 +                return None, None
 +            
 +            # Sort by last_modified to get the latest
 +            latest_blob = max(blobs, key=lambda x: x.last_modified)
 +            logger.info(f"Found latest image: {latest_blob.name}")
 +            
 +            return latest_blob.name, latest_blob
 +            
 +        except AzureError as e:
 +            logger.error(f"Error accessing blob storage: {e}")
 +            return None, None
 +    
 +    def call_blur_webapp(self, blob_name):
 +        """Call the web app to blur faces in the image by sending the blob name."""
 +        try:
 +            # Send the blob name as form data (matching your webapp's expectation)
 +            data = {'file': blob_name}
 +            
 +            logger.info(f"Making request to: {self.webapp_url} with blob name: {blob_name}")
 +            response = requests.post(
 +                self.webapp_url,
 +                data=data,
 +                timeout=self.webapp_timeout
 +            )
 +            
 +            logger.info(f"Response status code: {response.status_code}")
 +            logger.info(f"Response headers: {dict(response.headers)}")
 +            
 +            if response.status_code == 200:
 +                logger.info("Successfully called face blur web app")
 +                try:
 +                    response_data = response.json()
 +                    logger.info(f"Response JSON: {response_data}")
 +                    blob_url = response_data.get('blob_url')
 +                    if blob_url:
 +                        logger.info(f"Received blob URL: {blob_url}")
 +                        return blob_url
 +                    else:
 +                        logger.error("No blob_url in response")
 +                        return None
 +                except ValueError as e:
 +                    logger.error(f"Response is not valid JSON: {e}")
 +                    logger.error(f"Response text: {response.text}")
 +                    return None
 +            else:
 +                logger.error(f"Web app returned status code: {response.status_code}")
 +                logger.error(f"Response text: {response.text}")
 +                
 +                # Parse error response
 +                try:
 +                    error_data = response.json()
 +                    logger.error(f"Error details: {error_data}")
 +                    
 +                    # Check for specific Azure errors
 +                    error_msg = error_data.get('error', '')
 +                    if 'OutOfRangeInput' in error_msg:
 +                        logger.error("Azure Face API OutOfRangeInput error - image may not meet requirements")
 +                    elif 'InvalidImageFormat' in error_msg:
 +                        logger.error("Azure Face API InvalidImageFormat error - image format not supported")
 +                    elif 'InvalidImageSize' in error_msg:
 +                        logger.error("Azure Face API InvalidImageSize error - image size not supported")
 +                    elif 'NoFaceDetected' in error_msg:
 +                        logger.info("No faces detected in image - this might be normal")
 +                        return None  # This might not be an error
 +                        
 +                except ValueError:
 +                    logger.error("Error response is not JSON")
 +                
 +                return None
 +                    
 +        except Exception as e:
 +            logger.error(f"Error calling web app: {e}")
 +            return None
 +    
 +    def log_processed_image(self, blob_url, original_blob_name):
 +        """Log the processed image URL (since your web app already uploads to blob storage)."""
 +        try:
 +            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
 +            
 +            # Log the processing completion
 +            logger.info(f"Processed image available at: {blob_url}")
 +            logger.info(f"Original image: {original_blob_name}")
 +            logger.info(f"Processing completed at: {timestamp}")
 +            
 +            return blob_url
 +            
 +        except Exception as e:
 +            logger.error(f"Error logging processed image: {e}")
 +            return None
 +    
 +    def process_random_image(self):
 +        """Main processing function - pull random image, blur faces, and log result."""
 +        logger.info("Starting face blur processing (random image selection)...")
 +        
 +        # Get random image
 +        blob_name, blob_info = self.get_random_image()
 +        if not blob_name:
 +            return False
 +        
 +        try:
 +            # Call web app to blur faces directly with blob name (no need to download)
 +            blob_url = self.call_blur_webapp(blob_name)
 +            if not blob_url:
 +                return False
 +            
 +            # Log the processed image info (web app already uploaded it)
 +            result = self.log_processed_image(blob_url, blob_name)
 +            if result:
 +                logger.info(f"Successfully processed random image. Blurred version at: {blob_url}")
 +                return True
 +            else:
 +                return False
 +                
 +        except Exception as e:
 +            logger.error(f"Unexpected error during processing: {e}")
 +            return False
 +    
 +    def process_latest_image(self):
 +        """Main processing function - pull latest image, blur faces, and log result (kept for backward compatibility)."""
 +        logger.info("Starting face blur processing (latest image selection)...")
 +        
 +        # Get latest image
 +        blob_name, blob_info = self.get_latest_image()
 +        if not blob_name:
 +            return False
 +        
 +        try:
 +            # Call web app to blur faces directly with blob name (no need to download)
 +            blob_url = self.call_blur_webapp(blob_name)
 +            if not blob_url:
 +                return False
 +            
 +            # Log the processed image info (web app already uploaded it)
 +            result = self.log_processed_image(blob_url, blob_name)
 +            if result:
 +                logger.info(f"Successfully processed image. Blurred version at: {blob_url}")
 +                return True
 +            else:
 +                return False
 +                
 +        except Exception as e:
 +            logger.error(f"Unexpected error during processing: {e}")
 +            return False
 +
 +</code>
 +
 +
 +
 +  __init__.py 
 +
 +<code>
 +
 +import datetime
 +import logging
 +import azure.functions as func
 +import sys
 +import os
 +
 +# Add the parent directory to the path so we can import our modules
 +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 +
 +from shared.face_blur_processor import FaceBlurProcessor
 +
 +
 +def main(mytimer: func.TimerRequest) -> None:
 +    """
 +    Azure Function that runs on a timer to process random images for face blurring.
 +    """
 +    utc_timestamp = datetime.datetime.utcnow().replace(
 +        tzinfo=datetime.timezone.utc).isoformat()
  
 +    if mytimer.past_due:
 +        logging.info('The timer is past due!')
  
 +    logging.info('Face blur timer trigger function ran at %s', utc_timestamp)
 +    
 +    try:
 +        # Initialize the face blur processor
 +        processor = FaceBlurProcessor()
 +        
 +        # Process a random image
 +        success = processor.process_random_image()
 +        
 +        if success:
 +            logging.info("Face blur processing completed successfully")
 +        else:
 +            logging.error("Face blur processing failed")
 +            
 +    except Exception as e:
 +        logging.error(f"Error in face blur function: {str(e)}")
 +        raise e
  
 +    logging.info('Face blur timer trigger function completed at %s', 
 +                 datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat())
  
 +</code>
  
  
wiki/ai/azure_function_app_to_perform_image_blur.1751562706.txt.gz · Last modified: by ymurugesan