danieljstuart/offshore-hazard-ai
0
1import streamlit as st2import openai3import os4import base645from io import BytesIO6from PIL import Image7 8# Load OpenAI API key9OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")10openai.api_key = OPENAI_API_KEY11 12st.title("⚙️ AI Offshore Hazard Detection")13st.write("📝 **Upload a worksite image and (optionally) describe the job. The AI will detect hazards and recommend controls.**")14 15# User input16job_description = st.text_area("📋 **Job Description (optional):**")17uploaded_image = st.file_uploader("📷 **Upload an Image for Analysis:**", type=["jpg", "png", "jpeg"])18 19# Function to encode image to base6420def encode_image(image):21 image_bytes = image.read()22 return base64.b64encode(image_bytes).decode("utf-8")23 24# Function to determine task category25def infer_task_type(text):26 task = text.lower()27 if any(keyword in task for keyword in ["scaffold", "scaffolding"]):28 return "scaffold"29 elif any(keyword in task for keyword in ["valve", "flange", "relief valve"]):30 return "valve"31 elif any(keyword in task for keyword in ["tank", "vessel", "confined space"]):32 return "confined_space"33 else:34 return "general"35 36# Function to generate smart prompt chain37def generate_prompt(job_description, task_type):38 context = {39 "scaffold": "You're a safety advisor guiding a technician dismantling a scaffold offshore. Focus on structural, dropped object, access, and lifting hazards.",40 "valve": "You're a safety advisor guiding a technician replacing a valve in a pressurised system. Focus on pressure, gas, tool handling, access and dropped object risks.",41 "confined_space": "You're a safety advisor guiding a technician entering a confined space. Focus on ventilation, atmosphere, access and communication hazards.",42 "general": "You're a safety advisor guiding a technician offshore. Focus only on hazards visible in the image and described in the task."43 }[task_type]44 45 intro = f"""46 🚧 **Task Overview**47 {job_description if job_description else "No specific job description provided. Use the image as primary context."}48 49 📸 **Visual Scene Analysis**50 Carefully examine the uploaded image. Identify what kind of work area this is, any visible equipment, height, obstructions, trip hazards, pressure components, etc.51 52 ### 🔍 Step 1: Hazards Detected53 List only specific hazards that can be inferred from the image or the job description. Avoid generic advice. Skip anything that doesn’t clearly apply.54 55 ### 🛠 Step 2: Control Measures56 Provide task-specific, technician-focused controls for each hazard. Mention PPE, tooling, isolation, safe access, and other relevant controls.57 58 ### ✅ Step 3: Final Checklist59 Provide a short summary list of practical checks before starting work.60 """61 62 return f"{context}\n\n{intro}"63 64# Function to analyze hazards65def analyze_hazards(job_description, image):66 task_type = infer_task_type(job_description)67 prompt = generate_prompt(job_description, task_type)68 69 messages = [70 {"role": "system", "content": "You are an expert offshore safety advisor helping a technician complete their job safely. Use the uploaded photo and description to provide highly specific guidance — no generic policy language."},71 {"role": "user", "content": prompt}72 ]73 74 if image:75 encoded_image = encode_image(image)76 messages.append({77 "role": "user",78 "content": [79 {"type": "text", "text": "Use this image to detect specific real-world hazards, equipment, access issues, or risks. Don't give generic safety advice."},80 {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{encoded_image}"}}81 ]82 })83 84 try:85 with st.spinner("🔍 Analyzing photo and job info for hazards..."):86 response = openai.chat.completions.create(87 model="gpt-4o",88 messages=messages,89 max_tokens=900 # Reduced to reduce cost90 )91 return response.choices[0].message.content92 except Exception as e:93 return f"❌ Error: {str(e)}"94 95if st.button("🔍 Analyze Hazards"):96 if job_description or uploaded_image:97 hazard_report = analyze_hazards(job_description, uploaded_image)98 st.markdown("## 📊 **Hazard Analysis Report:**")99 st.markdown("---")100 st.markdown(hazard_report)101 st.markdown("---")102 else:103 st.warning("⚠️ Please enter a job description or upload an image before analyzing.")104 105if uploaded_image:106 st.image(uploaded_image, caption="📷 Uploaded Image", use_container_width=True)107 