""" Netra AI - Public Interface Calls protected backend space for classification """ import gradio as gr from gradio_client import Client from huggingface_hub import login import os # Global backend client backend = None def get_backend(): """Lazy initialization of backend client""" global backend if backend is None: HF_TOKEN = os.getenv("HF_TOKEN") if HF_TOKEN: login(token=HF_TOKEN) backend = Client("rg321/Construction_Material_Classification") return backend def classify_material(image): """Proxy function that calls protected backend""" if image is None: return "Please upload an image", "", "" try: import tempfile from PIL import Image as PILImage from gradio_client import handle_file print(f"📤 Calling backend API with image type: {type(image)}") # Convert PIL Image to temp file for gradio_client if isinstance(image, PILImage.Image): with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg', mode='wb') as tmp: image.save(tmp, 'JPEG') image_path = tmp.name else: image_path = image print(f"📤 Sending file: {image_path}") # Call protected space API with properly formatted file client = get_backend() result = client.predict( image=handle_file(image_path), api_name="/classify_material" ) print(f"📥 Backend response: {result}") # result is tuple: (classification, confidence, reasoning) return result[0], result[1], result[2] except Exception as e: import traceback error_msg = str(e) full_error = traceback.format_exc() print(f"❌ Error: {full_error}") return f"❌ Classification Error", "", f"Error: {error_msg}" # Custom CSS (same as original) custom_css = """ #header { text-align: center; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); padding: 20px; border-radius: 10px; color: white; } #result { font-size: 72px; font-weight: 900; text-align: center; padding: 50px 30px; border-radius: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; margin-bottom: 25px; box-shadow: 0 8px 16px rgba(0,0,0,0.2); text-transform: uppercase; letter-spacing: 2px; line-height: 1.2; } #confidence { font-size: 24px; font-weight: 600; text-align: center; padding: 15px; background: #f0f9ff; border-radius: 10px; margin-bottom: 20px; } #reasoning { font-size: 14px; text-align: left; padding: 15px; background: #f9fafb; border-radius: 8px; margin-bottom: 20px; color: #4b5563; } #about { font-size: 13px; color: #6b7280; padding-top: 20px; border-top: 1px solid #e5e7eb; } """ # Build Gradio interface with gr.Blocks(css=custom_css, title="Netra AI - Material Classifier") as demo: gr.Markdown( """ """, elem_id="header" ) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📸 Upload Material Image") image_input = gr.Image( type="pil", label="Construction Material Photo", height=400 ) classify_btn = gr.Button("🚀 Classify Material", variant="primary", size="lg") gr.Markdown( """ **Supported Materials:** - 🏖️ Reet (Sand) - 🔷 12mm VSI (Uniform Aggregate) - 🪨 Stone (Large Boulders) - 🏗️ GSB (Mixed Graded Base) """ ) gr.Markdown("### 📷 Try Sample Images") gr.Examples( examples=[ ["sanity_test_dataset/12mm_VSI_01.jpeg"], ["sanity_test_dataset/Reet_01_fine.jpeg"], ["sanity_test_dataset/Stone_01.jpeg"], ["sanity_test_dataset/GSB_01.jpeg"], ], inputs=image_input, label="" ) with gr.Column(scale=1): result_output = gr.Markdown("", elem_id="result") confidence_output = gr.Markdown("", elem_id="confidence") reasoning_output = gr.Markdown("", elem_id="reasoning") gr.Markdown( """
### 📊 About This Demo This is a proof-of-concept for **Netra AI's** automated material classification system. **Technology:** - Vision AI for real-time material identification - Prevents grade fraud at construction sites - Automates dispatch logging **Use Cases:** - Gate monitoring at crusher plants - Dispatch verification - Quality control --- **Netra AI** - *Making every tonne count.* For commercial inquiries: [Contact Us](mailto:guptaraghu321@gmail.com)
""" ) # Connect button classify_btn.click( fn=classify_material, inputs=[image_input], outputs=[result_output, confidence_output, reasoning_output] ) # Launch if __name__ == "__main__": demo.launch(show_error=True)