| import cv2 |
| import numpy as np |
| from PIL import Image |
| import torch |
| from torchvision import models, transforms |
| from ultralytics import YOLO |
| import gradio as gr |
| import torch.nn as nn |
| import os |
| from datetime import datetime |
| import json |
|
|
| |
| |
| |
| |
|
|
| |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
|
|
| |
| try: |
| detection_model = YOLO('best.pt') |
| classifier_network = models.resnet50(weights=None) |
| classifier_network.fc = nn.Linear(classifier_network.fc.in_features, 3) |
| classifier_network.load_state_dict(torch.load('rice_resnet_model.pth', map_location=device)) |
| classifier_network = classifier_network.to(device) |
| classifier_network.eval() |
| models_loaded = True |
| except Exception as e: |
| print(f" Model initialization failed: {e}") |
| detection_model = None |
| classifier_network = None |
| models_loaded = False |
|
|
| |
| VARIETY_MAP = { |
| 0: "C9 Premium", |
| 1: "Kant Special", |
| 2: "Superfine Grade" |
| } |
|
|
| VARIETY_COLORS = { |
| "C9 Premium": (255, 100, 100), |
| "Kant Special": (100, 100, 255), |
| "Superfine Grade": (100, 255, 100) |
| } |
|
|
| |
| image_preprocessor = transforms.Compose([ |
| transforms.Resize((224, 224)), |
| transforms.ToTensor(), |
| transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) |
| ]) |
|
|
| |
|
|
| def analyze_single_grain(grain_image): |
| """Perform classification on an individual grain""" |
| if not models_loaded: |
| return "System Error" |
| |
| tensor_input = image_preprocessor(grain_image).unsqueeze(0).to(device) |
| with torch.no_grad(): |
| prediction = classifier_network(tensor_input) |
| class_idx = torch.argmax(prediction, dim=1).item() |
| return VARIETY_MAP[class_idx] |
|
|
| def compute_distribution_stats(variety_counts): |
| """Generate statistical summary of grain distribution""" |
| total = sum(variety_counts.values()) |
| if total == 0: |
| return "No grains detected for analysis." |
| |
| stats = [" **Distribution Analysis**\n"] |
| stats.append(f" Total Grains Detected: **{total}**\n") |
| stats.append("\n**Breakdown by Variety:**\n") |
| |
| for variety, count in sorted(variety_counts.items(), key=lambda x: x[1], reverse=True): |
| percentage = (count / total) * 100 |
| bar_length = int(percentage / 5) |
| bar = "█" * bar_length + "░" * (20 - bar_length) |
| stats.append(f"- {variety}: {count} grains ({percentage:.1f}%) {bar}\n") |
| |
| |
| dominant = max(variety_counts.items(), key=lambda x: x[1]) |
| stats.append(f"\n **Dominant Variety:** {dominant[0]}") |
| |
| return "".join(stats) |
|
|
| def execute_batch_analysis(input_img): |
| """Main processing pipeline with analytics""" |
| if not models_loaded: |
| raise gr.Error(" Analysis engine unavailable. Please check model files.") |
| |
| if input_img is None: |
| raise gr.Error(" Please upload an image to begin analysis.") |
| |
| |
| img_array = np.array(input_img) |
| img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) |
| |
| |
| detection_results = detection_model(img_bgr, verbose=False)[0] |
| bounding_boxes = detection_results.boxes.xyxy.cpu().numpy() |
| |
| if len(bounding_boxes) == 0: |
| gr.Warning("⚠️ No rice grains found. Try a clearer image.") |
| return ( |
| Image.fromarray(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)), |
| "No grains detected in the provided image." |
| ) |
| |
| |
| variety_counter = {v: 0 for v in VARIETY_MAP.values()} |
| |
| for box in bounding_boxes: |
| x1, y1, x2, y2 = map(int, box[:4]) |
| grain_crop = img_bgr[y1:y2, x1:x2] |
| |
| if grain_crop.shape[0] > 0 and grain_crop.shape[1] > 0: |
| grain_pil = Image.fromarray(cv2.cvtColor(grain_crop, cv2.COLOR_BGR2RGB)) |
| variety_label = analyze_single_grain(grain_pil) |
| variety_counter[variety_label] += 1 |
| |
| |
| color = VARIETY_COLORS[variety_label] |
| cv2.rectangle(img_bgr, (x1, y1), (x2, y2), color, 3) |
| |
| |
| label_text = variety_label |
| (text_w, text_h), _ = cv2.getTextSize(label_text, cv2.FONT_HERSHEY_SIMPLEX, 0.7, 2) |
| cv2.rectangle(img_bgr, (x1, y1-text_h-10), (x1+text_w, y1), color, -1) |
| cv2.putText(img_bgr, label_text, (x1, y1-5), |
| cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2) |
| |
| |
| statistics_report = compute_distribution_stats(variety_counter) |
| |
| return ( |
| Image.fromarray(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)), |
| statistics_report |
| ) |
|
|
| |
|
|
| custom_css = """ |
| .gradio-container { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| } |
| .header-box { |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
| padding: 30px; |
| border-radius: 15px; |
| color: white; |
| text-align: center; |
| margin-bottom: 20px; |
| } |
| .stat-box { |
| background: #f8f9fa; |
| border-left: 4px solid #667eea; |
| padding: 15px; |
| border-radius: 8px; |
| margin: 10px 0; |
| } |
| """ |
|
|
| with gr.Blocks(css=custom_css, title="Rice Analyzer Pro") as app: |
| |
| gr.HTML(""" |
| <div class="header-box"> |
| <h1>🌾 Rice Analyzer Pro</h1> |
| <p style="font-size: 18px; margin-top: 10px;"> |
| Advanced Grain Analytics Platform | Professional Quality Assessment |
| </p> |
| </div> |
| """) |
| |
| with gr.Tabs(): |
| |
| with gr.Tab("Analysis app"): |
| gr.Markdown(""" |
| ### How It Works |
| 1. **Upload** your rice sample image (clear photos work best) |
| 2. **Analyze** Click on "Start Analysis" and let Our AI model detect and classify each grain |
| 3. **Review** detailed statistics and visual results |
| |
| **Color Coding:** 🔵 C9 Premium | 🔴 Kant Special | 🟢 Superfine Grade |
| """) |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| image_upload = gr.Image(type="pil", label="📸 Sample Image") |
| analyze_button = gr.Button(" Start Analysis", variant="primary", size="lg") |
| |
| with gr.Column(scale=1): |
| output_visualization = gr.Image(label=" Annotated Results") |
| |
| with gr.Row(): |
| statistics_output = gr.Markdown(label=" Statistical Report") |
| |
| analyze_button.click( |
| fn=execute_batch_analysis, |
| inputs=image_upload, |
| outputs=[output_visualization, statistics_output] |
| ) |
| |
| |
| with gr.Tab(" Documentation"): |
| gr.Markdown(""" |
| ## System Overview |
| |
| **Rice Analyzer Pro** uses a two-stage deep learning pipeline: |
| - **Stage 1:** YOLO-based grain detection |
| - **Stage 2:** ResNet50 variety classification |
| |
| ### Supported Varieties |
| | Variety | Description | Market Grade | |
| |---------|-------------|--------------| |
| | C9 Premium | High-quality long grain | Premium | |
| | Kant Special | Medium grain specialty | Standard | |
| | Superfine Grade | Ultra-refined grain | Super Fine | |
| |
| ### Best Practices |
| - Use well-lit images with minimal shadows |
| - Ensure grains are separated (not clumped) |
| - Plain backgrounds yield better results |
| - Recommended resolution: 1024x1024 or higher |
| |
| ### Technical Specifications |
| - Detection Model: YOLOv8 |
| - Classification: ResNet50 (Fine-tuned) |
| - Processing: GPU-accelerated (when available) |
| """) |
| |
| gr.Markdown("---") |
| gr.Markdown("### Sample Gallery") |
| |
| gr.Examples( |
| examples=[ |
| "samples/rice1.jpg", |
| "samples/rice2.jpg", |
| "samples/rice4.jpg", |
| "samples/rice5.jpg" |
| ], |
| inputs=image_upload, |
| outputs=[output_visualization, statistics_output], |
| fn=execute_batch_analysis, |
| label="Click any sample to run instant analysis" |
| ) |
|
|
| if __name__ == "__main__": |
| app.queue() |
| app.launch() |