This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revision | |||
| wiki:ai:azure_function_app_to_perform_image_blur [2025/07/03 18:00] – ymurugesan | wiki:ai:azure_function_app_to_perform_image_blur [2025/07/03 18:20] (current) – ymurugesan | ||
|---|---|---|---|
| Line 211: | Line 211: | ||
| </ | </ | ||
| + | function_app.py | ||
| + | < | ||
| + | |||
| + | import azure.functions as func | ||
| + | import datetime | ||
| + | import json | ||
| + | import logging | ||
| + | |||
| + | app = func.FunctionApp() | ||
| + | </ | ||
| + | |||
| + | |||
| + | face_blur_processor.py | ||
| + | |||
| + | < | ||
| + | |||
| + | |||
| + | """ | ||
| + | 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(' | ||
| + | self.container_name = os.environ.get(' | ||
| + | | ||
| + | # Web app configuration | ||
| + | webapp_url = os.environ.get(' | ||
| + | # Auto-fix URLs missing scheme | ||
| + | if not webapp_url.startswith((' | ||
| + | webapp_url = f' | ||
| + | self.webapp_url = webapp_url | ||
| + | self.webapp_timeout = int(os.environ.get(' | ||
| + | | ||
| + | # Initialize blob service client | ||
| + | if not self.connection_string: | ||
| + | raise ValueError(" | ||
| + | | ||
| + | self.blob_service_client = BlobServiceClient.from_connection_string(self.connection_string) | ||
| + | |||
| + | def ensure_container_exists(self): | ||
| + | """ | ||
| + | try: | ||
| + | container_client = self.blob_service_client.get_container_client(self.container_name) | ||
| + | if not container_client.exists(): | ||
| + | logger.info(f" | ||
| + | container_client.create_container() | ||
| + | logger.info(f" | ||
| + | return True | ||
| + | except AzureError as e: | ||
| + | logger.error(f" | ||
| + | return False | ||
| + | | ||
| + | def get_random_image(self): | ||
| + | """ | ||
| + | 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" | ||
| + | logger.info(" | ||
| + | return None, None | ||
| + | | ||
| + | # Select a random blob | ||
| + | random_blob = random.choice(blobs) | ||
| + | logger.info(f" | ||
| + | logger.info(f" | ||
| + | | ||
| + | return random_blob.name, | ||
| + | | ||
| + | except AzureError as e: | ||
| + | logger.error(f" | ||
| + | return None, None | ||
| + | | ||
| + | def get_latest_image(self): | ||
| + | """ | ||
| + | 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" | ||
| + | logger.info(" | ||
| + | return None, None | ||
| + | | ||
| + | # Sort by last_modified to get the latest | ||
| + | latest_blob = max(blobs, key=lambda x: x.last_modified) | ||
| + | logger.info(f" | ||
| + | | ||
| + | return latest_blob.name, | ||
| + | | ||
| + | except AzureError as e: | ||
| + | logger.error(f" | ||
| + | return None, None | ||
| + | | ||
| + | def call_blur_webapp(self, | ||
| + | """ | ||
| + | try: | ||
| + | # Send the blob name as form data (matching your webapp' | ||
| + | data = {' | ||
| + | | ||
| + | logger.info(f" | ||
| + | response = requests.post( | ||
| + | self.webapp_url, | ||
| + | data=data, | ||
| + | timeout=self.webapp_timeout | ||
| + | ) | ||
| + | | ||
| + | logger.info(f" | ||
| + | logger.info(f" | ||
| + | | ||
| + | if response.status_code == 200: | ||
| + | logger.info(" | ||
| + | try: | ||
| + | response_data = response.json() | ||
| + | logger.info(f" | ||
| + | blob_url = response_data.get(' | ||
| + | if blob_url: | ||
| + | logger.info(f" | ||
| + | return blob_url | ||
| + | else: | ||
| + | logger.error(" | ||
| + | return None | ||
| + | except ValueError as e: | ||
| + | logger.error(f" | ||
| + | logger.error(f" | ||
| + | return None | ||
| + | else: | ||
| + | logger.error(f" | ||
| + | logger.error(f" | ||
| + | | ||
| + | # Parse error response | ||
| + | try: | ||
| + | error_data = response.json() | ||
| + | logger.error(f" | ||
| + | | ||
| + | # Check for specific Azure errors | ||
| + | error_msg = error_data.get(' | ||
| + | if ' | ||
| + | logger.error(" | ||
| + | elif ' | ||
| + | logger.error(" | ||
| + | elif ' | ||
| + | logger.error(" | ||
| + | elif ' | ||
| + | logger.info(" | ||
| + | return None # This might not be an error | ||
| + | | ||
| + | except ValueError: | ||
| + | logger.error(" | ||
| + | | ||
| + | return None | ||
| + | | ||
| + | except Exception as e: | ||
| + | logger.error(f" | ||
| + | return None | ||
| + | | ||
| + | def log_processed_image(self, | ||
| + | """ | ||
| + | try: | ||
| + | timestamp = datetime.now().strftime(" | ||
| + | | ||
| + | # Log the processing completion | ||
| + | logger.info(f" | ||
| + | logger.info(f" | ||
| + | logger.info(f" | ||
| + | | ||
| + | return blob_url | ||
| + | | ||
| + | except Exception as e: | ||
| + | logger.error(f" | ||
| + | return None | ||
| + | | ||
| + | def process_random_image(self): | ||
| + | """ | ||
| + | logger.info(" | ||
| + | | ||
| + | # 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, | ||
| + | if result: | ||
| + | logger.info(f" | ||
| + | return True | ||
| + | else: | ||
| + | return False | ||
| + | | ||
| + | except Exception as e: | ||
| + | logger.error(f" | ||
| + | return False | ||
| + | | ||
| + | def process_latest_image(self): | ||
| + | """ | ||
| + | logger.info(" | ||
| + | | ||
| + | # 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, | ||
| + | if result: | ||
| + | logger.info(f" | ||
| + | return True | ||
| + | else: | ||
| + | return False | ||
| + | | ||
| + | except Exception as e: | ||
| + | logger.error(f" | ||
| + | return False | ||
| + | |||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| + | __init__.py | ||
| + | |||
| + | < | ||
| + | |||
| + | 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: | ||
| + | """ | ||
| + | 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(' | ||
| + | |||
| + | logging.info(' | ||
| + | | ||
| + | try: | ||
| + | # Initialize the face blur processor | ||
| + | processor = FaceBlurProcessor() | ||
| + | | ||
| + | # Process a random image | ||
| + | success = processor.process_random_image() | ||
| + | | ||
| + | if success: | ||
| + | logging.info(" | ||
| + | else: | ||
| + | logging.error(" | ||
| + | | ||
| + | except Exception as e: | ||
| + | logging.error(f" | ||
| + | raise e | ||
| + | |||
| + | logging.info(' | ||
| + | | ||
| + | |||
| + | </ | ||