first round of development

This commit is contained in:
giusber2005 2025-08-01 23:55:50 +02:00
parent a014734a9d
commit 8df9fa61f3
8 changed files with 82 additions and 151 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View file

@ -1,17 +1,11 @@
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import pandas as pd
import numpy as np
from PIL import Image, ImageTk
import json
import os
from datetime import datetime, timedelta
import joblib
from datetime import datetime
from plant_model import PlantGrowthModel
from data_handler import DataHandler
from image_generator import ImageGenerator
class PlantGrowthDashboard:
def __init__(self, root):
@ -29,7 +23,6 @@ class PlantGrowthDashboard:
# Initialize components
self.plant_model = PlantGrowthModel()
self.data_handler = DataHandler()
self.image_generator = ImageGenerator()
# Variables - fixed plant type
self.current_plant = "tomato" # Fixed plant type
@ -110,10 +103,9 @@ class PlantGrowthDashboard:
ttk.Button(control_frame, text="📷 Load Plant Image",
command=self.load_baseline_image).grid(row=2, column=0, columnspan=2, pady=(0, 10), sticky=(tk.W, tk.E))
# Environmental parameters
ttk.Label(control_frame, text="Parameters:",
font=('Arial', 9, 'bold')).grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=(0, 4))
font=('Arial', 9, 'bold')).grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=(0, 4))
param_labels = {
'temperature': '🌡️ Temp (°C)',
'humidity': '💧 Humidity (%)',
@ -124,7 +116,19 @@ class PlantGrowthDashboard:
'water': '💦 Water (%)',
'co2': '🫧 CO2'
}
# Define bounds for each parameter (min, max)
param_bounds = {
'temperature': (10, 40),
'humidity': (20, 90),
'soil_acidity': (4.0, 9.0),
'pressure': (950, 1100),
'brightness': (100, 100000),
'nutrients': (0, 100),
'water': (10, 100),
'co2': (300, 1000)
}
row = 4
for param, label in param_labels.items():
# Compact parameter layout
@ -133,21 +137,12 @@ class PlantGrowthDashboard:
ttk.Label(param_frame, text=label, width=11, font=('Arial', 8)).pack(side=tk.LEFT)
# Scale ranges based on parameter type
if param == 'co2':
scale_max = 1000
elif param == 'brightness':
scale_max = 100000
elif param == 'pressure':
scale_max = 1100
elif param == 'soil_acidity':
scale_max = 14
else:
scale_max = 100
scale = ttk.Scale(param_frame, from_=0, to=scale_max,
variable=self.env_params[param], orient=tk.HORIZONTAL,
command=lambda x, p=param: self.on_param_change(p))
# Get bounds for this parameter
scale_min, scale_max = param_bounds.get(param, (0, 100))
scale = ttk.Scale(param_frame, from_=scale_min, to=scale_max,
variable=self.env_params[param], orient=tk.HORIZONTAL,
command=lambda x, p=param: self.on_param_change(p))
scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(4, 4))
# Value label
@ -212,7 +207,7 @@ class PlantGrowthDashboard:
info_frame.pack(fill=tk.X, pady=(8, 0))
self.plant_info_text = tk.Text(info_frame, height=8, width=35, wrap=tk.WORD,
font=('Arial', 8), bg='#f8f9fa')
font=('Arial', 8), bg="#000000")
self.plant_info_text.pack(fill=tk.BOTH, expand=True)
# Submit button
@ -283,10 +278,31 @@ class PlantGrowthDashboard:
for param_name, default_value in self.default_params.items():
self.env_params[param_name].set(default_value)
self.update_parameter_label(param_name, default_value)
self.ambient_mode.set("controlled")
self.update_prediction()
def update_parameter_label(self, param, value):
"""Update the value label for a specific parameter"""
try:
# Get the value label for this parameter
value_label = getattr(self, f"{param}_value_label")
# Format the value based on parameter type
if param == 'soil_acidity':
value_label.config(text=f"{value:.1f}")
elif param in ['brightness', 'pressure', 'co2']:
value_label.config(text=f"{value:.0f}")
else:
value_label.config(text=f"{value:.1f}")
except AttributeError:
# Handle case where label doesn't exist
print(f"Warning: No label found for parameter {param}")
def load_baseline_image(self):
self.results_text.delete(1.0, tk.END)
file_path = filedialog.askopenfilename(
title="Select baseline plant image",
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif")]
@ -308,15 +324,27 @@ class PlantGrowthDashboard:
'timestamp': datetime.now().isoformat(),
'parameters': params,
'baseline_image_path': self.baseline_image_path,
'plant_info': self.plant_info_text.get(1.0, tk.END),
'results': self.results_text.get(1.0, tk.END)
'plant_info': self.plant_info_text.get(1.0, tk.END)
}
# Show confirmation dialog
messagebox.showinfo("Submission Successful",
"Plant information and photo have been submitted successfully!\n\n"
f"Submission ID: {datetime.now().strftime('%Y%m%d_%H%M%S')}")
#Remove plant_info_text
self.plant_info_text.delete(1.0, tk.END)
data_dir = "../data"
os.makedirs(data_dir, exist_ok=True)
current_date = datetime.now().strftime('%Y%m%d')
filename = f"{current_date}-{current_date}.txt"
filepath = os.path.join(data_dir, filename)
with open(filepath, 'w') as f:
json.dump(submission_data, f, indent=4)
# Here call the bot pipeline to store results on files in plant_data
# Here update the informations in the last box from plant_data/texts
# Here update the informations in growth evolution from plant_data/images
print(f"Submission data saved to: {filepath}")
except Exception as e:
messagebox.showerror("Submission Error", f"Error submitting data: {str(e)}")
@ -331,10 +359,7 @@ class PlantGrowthDashboard:
prediction = self.plant_model.predict_growth(params)
# Update initial plant display
self.update_initial_plant_display(params, prediction)
# Generate plant evolution images
self.generate_plant_evolution(params, prediction)
self.update_initial_plant_display()
# Update results text
self.update_results_display(prediction)
@ -342,125 +367,35 @@ class PlantGrowthDashboard:
except Exception as e:
messagebox.showerror("Prediction Error", f"Error generating prediction: {str(e)}")
def update_initial_plant_display(self, params, prediction):
def update_initial_plant_display(self):
"""Update the initial plant state display"""
try:
initial_image = None
# Generate initial plant image (stage 0)
initial_image = self.image_generator.generate_plant_image(
self.current_plant, 0.5, prediction['health_score'], 0
)
try:
if self.baseline_image_path != None and os.path.exists(self.baseline_image_path):
initial_image = Image.open(self.baseline_image_path)
except Exception as e:
print(f"Error loading image from {self.baseline_image_path}: {e}")
# Resize image to fit better in square layout
initial_image = initial_image.resize((280, 210), Image.Resampling.LANCZOS)
# Convert to PhotoImage and display
photo = ImageTk.PhotoImage(initial_image)
self.initial_plant_label.configure(image=photo, text="")
self.initial_plant_label.image = photo # Keep reference
if initial_image != None:
initial_image = initial_image.resize((280, 210), Image.Resampling.LANCZOS)
# Update plant information
self.update_plant_info(params, prediction)
# Convert to PhotoImage and display
photo = ImageTk.PhotoImage(initial_image)
self.initial_plant_label.configure(image=photo, text="")
self.initial_plant_label.image = photo # Keep reference
except Exception as e:
messagebox.showerror("Image Error", f"Could not generate initial plant image: {str(e)}")
def update_plant_info(self, params, prediction):
"""Update plant information display"""
self.plant_info_text.delete(1.0, tk.END)
info_text = f"""🌱 PLANT STATUS
{'='*22}
Plant Type: Tomato
Health Score: {prediction['health_score']:.1f}%
Growth Rate: {prediction['growth_rate']:.2f} cm/day
🌡 CONDITIONS:
Temperature: {params['temperature']:.1f}°C
Humidity: {params['humidity']:.1f}%
Soil pH: {params['soil_acidity']:.1f}
Light: {params['brightness']:.0f} lux
Water: {params['water']:.1f}%
Nutrients: {params['nutrients']:.1f}%
📊 FORECAST:
Final Height: {prediction['final_height']:.1f} cm
Expected Yield: {prediction.get('yield', 'N/A')}
Optimal Conditions: {prediction['optimal_conditions']:.1f}%
"""
self.plant_info_text.insert(1.0, info_text)
def generate_plant_evolution(self, params, prediction):
"""Generate and display plant evolution images"""
try:
# Generate plant evolution images
images = self.image_generator.generate_evolution(
self.current_plant, params, prediction, self.baseline_image_path
)
if images:
# Clear previous images
for widget in self.image_display_frame.winfo_children():
widget.destroy()
# Display evolution stages in a square grid
stages_per_row = 2 # Square layout
for i, image in enumerate(images):
row = i // stages_per_row
col = i % stages_per_row
stage_frame = ttk.Frame(self.image_display_frame)
stage_frame.grid(row=row, column=col, padx=4, pady=4, sticky=(tk.W, tk.E))
# Stage label
ttk.Label(stage_frame, text=f"Stage {i+1}",
font=('Arial', 9, 'bold')).pack()
# Resize image for compact display
resized_image = image.resize((180, 135), Image.Resampling.LANCZOS)
# Convert PIL image to PhotoImage
photo = ImageTk.PhotoImage(resized_image)
image_label = tk.Label(stage_frame, image=photo)
image_label.image = photo # Keep a reference
image_label.pack()
# Configure grid weights for even distribution
for col in range(stages_per_row):
self.image_display_frame.columnconfigure(col, weight=1)
except Exception as e:
messagebox.showerror("Evolution Error", f"Could not generate evolution images: {str(e)}")
def update_results_display(self, prediction):
self.results_text.delete(1.0, tk.END)
results = f"""🌱 GROWTH FORECAST
{'='*18}
Final Height: {prediction['final_height']:.1f} cm
Growth Rate: {prediction['growth_rate']:.2f} cm/day
Health Score: {prediction['health_score']:.1f}/100
📅 Growth Phases:
"""
for phase in prediction.get('phases', []):
results += f"{phase['name']}: Days {phase['start']}-{phase['end']}\n"
results += f"\n✅ Optimal Conditions: {prediction['optimal_conditions']:.1f}%"
results += f"\n🍅 Expected Yield: {prediction.get('yield', 'N/A')}"
# Add environmental summary
results += f"\n\n🌡️ ENVIRONMENT SUMMARY:"
results += f"\nTemperature: {self.env_params['temperature'].get():.1f}°C"
results += f"\nHumidity: {self.env_params['humidity'].get():.1f}%"
results += f"\nSoil pH: {self.env_params['soil_acidity'].get():.1f}"
results += f"\nLight Level: {self.env_params['brightness'].get():.0f} lux"
results += f"\nWater Level: {self.env_params['water'].get():.1f}%"
results += f"\nNutrients: {self.env_params['nutrients'].get():.1f}%"
results += f"\nCO2 Level: {self.env_params['co2'].get():.0f} ppm"
#Here update the results display after submitting the photo and the message with the parameters and receveing the output
results = ""
self.results_text.insert(1.0, results)

View file

@ -28,6 +28,8 @@ colorlog>=6.6.0
psutil>=5.8.0
memory-profiler>=0.60.0
tkcalendar
# Note: tkinter comes pre-installed with most Python distributions
# If tkinter is not available, install it using your system package manager:
# Ubuntu/Debian: sudo apt-get install python3-tk

View file

@ -49,7 +49,7 @@ def main():
print("\n🚀 Starting graphics demo...")
try:
from graphics_demo import PlantGrowthGraphicsDemo
from PlantDashboard.utils.graphics_demo import PlantGrowthGraphicsDemo
import tkinter as tk
root = tk.Tk()

View file

@ -4,7 +4,6 @@ ROADMAP
-add the possibility to retrieve all the data when the user click the submit button
-format the data in an usable format
-resolve numpy.float64 bug
-differences between the three modes:
Open mode:
-the temp, humidity, light, water parameters are setted by the meteo api, the rest is setted by the user
@ -23,8 +22,6 @@ ROADMAP
-The growth evolution will have as input the photo/s obtained by the model
(the output will be in the text folder inside data/images folder, format "date1-date2-enum.jpg)
-make the plant information as a text field, remove all the connections with other functions
-set the value of light as Daily Light Integral (DLI)
-forecast for a period of time:
@ -36,6 +33,3 @@ ROADMAP
-final updates of README.md, hackathon page, forgejo, github page, small design adjustments.
DURING DINNER
-update all on forgejo, hackathon page, forgejo