TestProto / app.py
DeveshThakran's picture
New changes to accomodate image processing
146963b
Raw
History Blame Contribute Delete
9.66 kB
import gradio as gr
import tensorflow as tf
from transformers import AutoImageProcessor, AutoModelForImageClassification
import numpy as np
import pymongo
import random
import base64 # Import base64 module
import io # Import io module
from PIL import Image # Import PIL
print(gr.__version__)
# --- Loading models and DB connection (Keep as is) ---
processor = AutoImageProcessor.from_pretrained("SowmyaKannan/fine_tuned_vit_model")
model = AutoModelForImageClassification.from_pretrained("SowmyaKannan/Vit-FER")
labels = ['angry', 'disgust', 'fear', 'happy', 'sad', 'neutral', 'surprise']
mongo_db_uri = "mongodb+srv://sowmyakannan10:O2BkajONKtfZsNqm@cluster0.zehazwd.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0"
client = None
db = None
quotes_collection = None
author_list = []
try:
mongodb_uri = mongo_db_uri
if not mongodb_uri: print("MONGODB_URI not found!!")
else:
print("Connecting to MongoDB...")
client = pymongo.MongoClient(mongodb_uri, serverSelectionTimeoutMS=5000)
client.admin.command('ping')
print("Successfully connected to MongoDB!")
db = client['AhumDB']
quotes_collection = db['AhumQuotes']
author_list = sorted(filter(None, quotes_collection.distinct("author")))
print(f"Fetched {len(author_list)} authors.")
except Exception as e:
print(f"Error during MongoDB setup: {e}")
client = None
# --- End loading models and DB connection ---
# --- Quote fetching function (Keep as is) ---
def get_quotes(index=None, authors=None):
if client is None or quotes_collection is None:
print("MongoDB connection not available. Cannot fetch quote.")
return "Quote data unavailable (DB connection issue).", "Unknown"
pipeline = []
emotion_label = None
emotion_match = {}
if index is not None and 0 <= index < len(labels):
emotion_label = labels[index]
emotion_match = {"emotions": {"$regex": f"^{emotion_label}$", "$options": "i"}}
author_match = {}
if authors:
author_match = {"$or": [{"author": {"$regex": f"^{a}$", "$options": "i"}} for a in authors]}
combined_match = {}
if emotion_match and author_match: combined_match = {"$and": [emotion_match, author_match]}
elif emotion_match: combined_match = emotion_match
elif author_match: combined_match = author_match
if combined_match: pipeline.append({"$match": combined_match})
pipeline.append({"$sample": {"size": 1}})
try:
result = list(quotes_collection.aggregate(pipeline))
if result: return result[0].get('quote', "Quote not found."), result[0].get('author', "Unknown")
else: # Fallback logic
if emotion_label:
fallback_pipeline = [{"$match": {"emotions": {"$regex": f"^{emotion_label}$", "$options": "i"}}}, {"$sample": {"size": 1}}]
fallback_result = list(quotes_collection.aggregate(fallback_pipeline))
if fallback_result: return fallback_result[0].get('quote', "Quote not found."), fallback_result[0].get('author', "Unknown")
return f"Finding strength in '{emotion_label}' comes from within.", "Unknown"
else: return "Could not find a suitable quote at this time.", "Unknown"
except Exception as e:
print(f"Error in get_quotes: {e}")
return "Quote data unavailable (DB query error).", "Unknown"
# --- End quote fetching ---
# --- CORE LOGIC: Process PIL image and get prediction/quote ---
def process_pil_image_and_get_quote(img_pil, selected_authors):
"""Takes a PIL image and author list, returns formatted prediction string."""
if img_pil is None:
return "Error: Process function received an empty image."
try:
print(f"Processing PIL Image - Mode: {img_pil.mode}, Size: {img_pil.size}") # Debug
# Ensure image is RGB if required by processor/model
if img_pil.mode != 'RGB':
img_pil = img_pil.convert('RGB')
print("Converted image to RGB.") # Debug
# Process image with Hugging Face processor
inputs = processor(images=img_pil, return_tensors="pt")
# Model prediction
outputs = model(**inputs)
logits = outputs.logits
predicted_class_index = np.argmax(logits.detach().numpy(), axis=1)[0]
predicted_emotion = labels[predicted_class_index]
print(f"Predicted Emotion Index: {predicted_class_index}, Emotion: {predicted_emotion}") # Debug
# Get quote
quote, author = get_quotes(predicted_class_index, authors=selected_authors)
return f"Predicted Emotion: {predicted_emotion.capitalize()}\n\n\"{quote}\"\n\n— {author}"
except Exception as e:
import traceback
print(f"Error in process_pil_image_and_get_quote: {e}\n{traceback.format_exc()}")
return f"An error occurred during image processing: {e}"
# --- API FUNCTION: Handles Base64 input ---
def main_fn_api(img_base64_string, selected_authors):
"""
API endpoint function. Expects a base64 data URI string for the image.
Decodes it and calls the core processing function.
"""
print(">>> API endpoint main_fn_api called.") # Debug
if not isinstance(img_base64_string, str) or not img_base64_string.startswith('data:image'):
print(f"Error: Invalid base64 string received: {str(img_base64_string)[:100]}...") # Debug
return "Error: Invalid image data received via API."
try:
# 1. Split header (e.g., "data:image/png;base64,") and encoded data
try:
header, encoded_data = img_base64_string.split(',', 1)
except ValueError:
print("Error: Base64 string does not contain expected comma separator.")
return "Error: Malformed Base64 image data."
# 2. Decode Base64 data
print(f"Attempting to decode base64 data (starts with {encoded_data[:10]}...).") # Debug
decoded_bytes = base64.b64decode(encoded_data)
print(f"Decoded {len(decoded_bytes)} bytes.") # Debug
# 3. Create PIL Image from decoded bytes
img_pil = Image.open(io.BytesIO(decoded_bytes))
print("Successfully created PIL Image from decoded bytes.") # Debug
# 4. Call the core processing function with the PIL image
return process_pil_image_and_get_quote(img_pil, selected_authors)
except base64.binascii.Error as e:
print(f"Error decoding Base64: {e}") # Debug
return "Error: Invalid Base64 encoding."
except Exception as e:
import traceback
print(f"Error in main_fn_api: {e}\n{traceback.format_exc()}")
return f"An error occurred during API image processing: {e}"
# --- Other helper functions (Keep as is) ---
def get_random_quote(authors):
quote, author = get_quotes(index=None, authors=authors)
return f"\"{quote}\"\n\n— {author}"
def select_all_authors():
return author_list
# --- End other helper functions ---
# --- Build Gradio Interface ---
with gr.Blocks() as demo:
gr.Markdown("## Emotion Detection from Image & Quote")
with gr.Row():
# This Image component is primarily for the Web UI interaction
image_input_ui = gr.Image(type="pil", label="Upload Image (for Web UI)")
output_text = gr.Textbox(label="Prediction & Quote", lines=5) # Increased lines
with gr.Row():
author_selector = gr.Dropdown(
choices=author_list,
multiselect=True,
label="Select Author(s)",
info="Optional: filter quotes by author"
)
with gr.Row():
select_all_button = gr.Button("Select All Authors")
with gr.Row():
# This button triggers the processing for images uploaded via the UI
submit_button_ui = gr.Button("Detect Emotion & Get Quote (from UI Upload)")
random_quote_button = gr.Button("Get a Random Quote")
# --- Event Handlers ---
# 1. UI Submit Button: Calls the core logic function directly with the PIL image from gr.Image
submit_button_ui.click(
process_pil_image_and_get_quote, # Target function expects PIL image
inputs=[image_input_ui, author_selector], # Inputs from UI components
outputs=output_text
# No api_name here means this specific button click isn't easily callable via API
)
# 2. API Endpoint Registration:
# We need to expose `main_fn_api` so the Android app can call it.
# We can link it to a component's event and give it the correct `api_name`.
# Using a "hidden" Textbox's `change` event is a way to register it without a visible trigger.
api_trigger_input = gr.Textbox(visible=False) # Hidden component to hook the API call
api_trigger_input.change( # Event doesn't really matter, just need to link the function
fn=main_fn_api, # The function that expects BASE64 STRING
inputs=[api_trigger_input, author_selector], # Inputs match main_fn_api signature
outputs=output_text, # API calls need an output defined
api_name="main_fn" # <--- CRITICAL: This name matches the Android API URL target
# (.../gradio_api/call/main_fn)
)
# 3. Random Quote Button (Keep as is)
random_quote_button.click(
get_random_quote,
inputs=author_selector,
outputs=output_text,
api_name="get_random_quote" # Keep this if Android uses it too
)
# 4. Select All Authors Button (Keep as is)
select_all_button.click(select_all_authors, inputs=None, outputs=author_selector)
print("Launching Gradio Interface...")
demo.launch() # share=True if you need external access temporarily for testing