import gradio as gr import pandas as pd import os, shutil from PIL import Image from ultralyticsplus import YOLO # Gradio Theme theme = gr.themes.Soft( primary_hue="yellow", secondary_hue="blue", neutral_hue="gray", font=[gr.themes.GoogleFont('Inter'), 'ui-sans-serif', 'system-ui', 'sans-serif'], font_mono=[gr.themes.GoogleFont('Inter'), 'ui-monospace', 'Consolas', 'monospace'], ).set( background_fill_primary='*neutral_100', ) # Bread Prices bread_types = { "baguette": {"name": "Baguette", "price": 108}, "binangkal": {"name": "Binangkal", "price": 11}, "bonete": {"name": "Bonete", "price": 8}, "cornbread": {"name": "Cornbread", "price": 55}, "croissant": {"name": "Croissant", "price": 75}, "ensaymada": {"name": "Ensaymada", "price": 14}, "flatbread": {"name": "Flatbread", "price": 17}, "kalihim": {"name": "Kalihim", "price": 15}, "monay": {"name": "Monay", "price": 6}, "pandesal": {"name": "Pandesal", "price": 3}, "sourdough": {"name": "Sourdough", "price": 150}, "spanish-bread": {"name": "Spanish Bread", "price": 14}, "wheat-bread": {"name": "Wheat Bread", "price": 8}, "white-bread": {"name": "White Bread", "price": 4}, "whole-grain-bread": {"name": "Whole Grain Bread", "price": 10}, } # Instantiate the model model = YOLO("best.pt") # Converts image input into a list def preprocess_image(image): img_list = [] # Preprocess the image and add it to the list for im in image: image = Image.open(im.name) resize_img = image.resize((640, 640)) img_list.append(resize_img) return img_list # Gets all output images def get_predictions(directory): allowed_extensions = ('.png', '.jpg', '.jpeg', '.gif', '.bmp') return [ os.path.join(root, file) for root, _, files in os.walk(directory) for file in files if file.lower().endswith(allowed_extensions) ] # Clear output from previous detection def clear_output(): shutil.rmtree('output/', ignore_errors=True) # Bread Prediction function def detect_bread(image): clear_output() image_list = preprocess_image(image) results = model.predict(image_list, conf=0.4, save=True, hide_conf=True, project = "output", name = "results") detected_classes = [] for result in results: for cls in result.boxes.cls: # Stores all detected classes in the list detected_classes.append(result.names[int(cls)]) receipt = generate_receipt(detected_classes) return get_predictions(f'output/results'), receipt # Generate Receipt function def generate_receipt(detected_classes): detected_items = [] counts = {} # Dictionary to store bread type counts for item_class in detected_classes: # Counts the quantity of each class counts[item_class] = counts.get(item_class, 0) + 1 for item_class, count in counts.items(): # Gets the name and price of each class bread_info = bread_types.get(item_class, {}) item_name = bread_info.get("name", "Unknown Bread") price = bread_info.get("price", 0) detected_items.append({"item": item_name, "quantity": count, "price": price}) total_amount = sum(item["quantity"] * item["price"] for item in detected_items) # Creates the receipt dictionary data = {"Item": [], "Quantity": [], "Price": [], "Amount": []} for item_info in detected_items: item = item_info["item"] quantity = item_info["quantity"] price = item_info["price"] total_item_amount = quantity * price data["Item"].append(item) data["Quantity"].append(quantity) data["Price"].append(price) data["Amount"].append(total_item_amount) # Appends the last row of the dataframe for the total amount data["Item"].append("TOTAL") data["Quantity"].append("") data["Price"].append("") data["Amount"].append(total_amount) df = pd.DataFrame(data) return df # Export to CSV function def export_csv(df): df.to_csv("receipt.csv", index=False) return gr.File.update(value="receipt.csv", visible=True) # Export to JSON function def export_json(df): df.to_json("receipt.json") return gr.File.update(value="receipt.json", visible=True) # Select image from Files def preview(files, sd: gr.SelectData): prev = files[sd.index].name return gr.Image.update(value=prev, visible=True) # Gradio Interface with gr.Blocks(theme=theme) as demo: clear_output() gr.Markdown("# Bread Detector w/ POS") gr.Markdown("An application that detects different types of bread and calculates the total price.") gr.Markdown("**Bread types include:** baguette, binangkal, bonete, cornbread, croissant, ensaymada, flatbread, kalihim, monay, pandesal, sourdough, spanish bread, wheat bread, white bread, and whole grain bread.") with gr.Row(): with gr.Column(): fn = detect_bread img_input = gr.Files(file_types=["image"], label="Input Image") img_preview = gr.Image(label="Preview Image", interactive=False, visible=False) detect_btn = gr.Button(variant="primary", value="Detect") with gr.Column(): img_output = gr.Gallery(label='Output Image') receipt_output = gr.Dataframe( headers=["Item", "Quantity", "Price", "Amount"], datatype=["str", "number", "number", "number"], label="Receipt", interactive=False, ) with gr.Row(): clear_btn = gr.ClearButton() export_csv_btn = gr.Button(variant="primary", value="Export as CSV") export_json_btn = gr.Button(variant="primary", value="Export as JSON") with gr.Row(): csv = gr.File(interactive=False, visible=False) # Gradio Buttons img_input.select(preview, img_input, img_preview) detect_btn.click(detect_bread, inputs=img_input, outputs=[img_output, receipt_output]) export_csv_btn.click(export_csv, receipt_output, csv) export_json_btn.click(export_json, receipt_output, csv) clear_btn.click(lambda: [None, None, None, gr.File.update(visible=False), gr.Image.update(visible=False)], outputs=[img_input, img_output, receipt_output, csv, img_preview] ) demo.queue() demo.launch()