team-2/PlantDashboard/image_generator.py

240 lines
9.8 KiB
Python
Raw Normal View History

2025-08-01 20:50:52 +02:00
from PIL import Image, ImageDraw, ImageFont
import random
import math
import os
class ImageGenerator:
def __init__(self):
self.image_size = (400, 300)
self.plant_colors = {
'tomato': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF6347'},
'basil': {'stem': '#228B22', 'leaf': '#90EE90', 'fruit': '#FFFFFF'},
'mint': {'stem': '#228B22', 'leaf': '#98FB98', 'fruit': '#FFFFFF'},
'lettuce': {'stem': '#228B22', 'leaf': '#ADFF2F', 'fruit': '#FFFFFF'},
'rosemary': {'stem': '#8B4513', 'leaf': '#556B2F', 'fruit': '#FFFFFF'},
'strawberry': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF1493'}
}
def generate_evolution(self, plant_type, parameters, prediction, baseline_path=None):
"""Generate a series of images showing plant evolution"""
growth_stages = prediction['growth_stages']
health_score = prediction['health_score']
images = []
# Generate images for key growth stages
stage_indices = [0, len(growth_stages)//4, len(growth_stages)//2,
3*len(growth_stages)//4, len(growth_stages)-1]
for i, stage_idx in enumerate(stage_indices):
if stage_idx < len(growth_stages):
height = growth_stages[stage_idx]
image = self.generate_plant_image(plant_type, height, health_score, i+1)
images.append(image)
return images
def generate_plant_image(self, plant_type, height, health_score, stage):
"""Generate a single plant image"""
img = Image.new('RGB', self.image_size, color='#87CEEB') # Sky blue background
draw = ImageDraw.Draw(img)
# Draw ground
ground_y = self.image_size[1] - 50
draw.rectangle([0, ground_y, self.image_size[0], self.image_size[1]],
fill='#8B4513') # Brown ground
# Get plant colors (default to tomato since we removed plant type selection)
colors = self.plant_colors.get(plant_type, self.plant_colors['tomato'])
# Calculate plant dimensions based on height and health
if stage == 0: # Initial/seed stage
plant_height = 5 # Very small initial plant
plant_width = 3
else:
plant_height = min(height * 2, self.image_size[1] - 100) # Scale for display
plant_width = plant_height * 0.6
# Adjust colors based on health
health_factor = health_score / 100.0
stem_color = self._adjust_color_health(colors['stem'], health_factor)
leaf_color = self._adjust_color_health(colors['leaf'], health_factor)
# Plant center position
center_x = self.image_size[0] // 2
base_y = ground_y
if stage == 0:
# Draw seed/initial state
self._draw_seed(draw, center_x, base_y - 10, health_factor)
else:
# Draw stem
stem_width = max(2, int(plant_height * 0.05))
stem_top_y = base_y - plant_height
draw.rectangle([center_x - stem_width//2, int(stem_top_y),
center_x + stem_width//2, base_y], fill=stem_color)
# Draw leaves based on plant type and stage
self._draw_leaves(draw, plant_type, center_x, stem_top_y, base_y,
plant_width, leaf_color, stage)
# Draw fruits/flowers if applicable
if stage >= 3 and plant_type in ['tomato', 'strawberry']:
self._draw_fruits(draw, plant_type, center_x, stem_top_y, base_y,
colors['fruit'], stage)
# Add stage label
try:
font = ImageFont.load_default()
except:
font = None
if stage == 0:
stage_text = f"Initial State - Seed"
else:
stage_text = f"Stage {stage} - Height: {height:.1f}cm"
if font:
draw.text((10, 10), stage_text, fill='black', font=font)
else:
draw.text((10, 10), stage_text, fill='black')
# Add health indicator
health_text = f"Health: {health_score:.1f}%"
health_color = 'green' if health_score > 70 else 'orange' if health_score > 40 else 'red'
if font:
draw.text((10, 30), health_text, fill=health_color, font=font)
else:
draw.text((10, 30), health_text, fill=health_color)
return img
def _draw_seed(self, draw, x, y, health_factor):
"""Draw a seed for the initial state"""
seed_size = 8
seed_color = '#8B4513' # Brown seed
# Adjust seed color based on health
if health_factor > 0.7:
seed_color = '#654321' # Healthy brown
elif health_factor > 0.4:
seed_color = '#8B4513' # Normal brown
else:
seed_color = '#A0522D' # Pale brown
# Draw seed
draw.ellipse([x - seed_size, y - seed_size//2,
x + seed_size, y + seed_size//2], fill=seed_color)
# Draw small sprout if health is good
if health_factor > 0.5:
sprout_color = '#90EE90'
draw.line([x, y - seed_size//2, x, y - seed_size//2 - 5],
fill=sprout_color, width=2)
def _draw_leaves(self, draw, plant_type, center_x, top_y, base_y, width, color, stage):
"""Draw leaves based on plant type"""
plant_height = base_y - top_y
num_leaves = min(stage * 2, 8) # More leaves as plant grows
for i in range(num_leaves):
# Calculate leaf position
y_pos = base_y - (i + 1) * (plant_height / (num_leaves + 1))
side = 1 if i % 2 == 0 else -1 # Alternate sides
leaf_x = center_x + side * (width * 0.3)
leaf_size = width * 0.2 * (1 + stage * 0.1)
if plant_type == 'lettuce':
# Draw broad leaves for lettuce
self._draw_broad_leaf(draw, leaf_x, y_pos, leaf_size, color)
elif plant_type == 'rosemary':
# Draw needle-like leaves for rosemary
self._draw_needle_leaf(draw, leaf_x, y_pos, leaf_size, color)
else:
# Draw regular oval leaves (default for tomato)
self._draw_oval_leaf(draw, leaf_x, y_pos, leaf_size, color)
def _draw_oval_leaf(self, draw, x, y, size, color):
"""Draw an oval leaf"""
draw.ellipse([x - size//2, y - size//3, x + size//2, y + size//3], fill=color)
def _draw_broad_leaf(self, draw, x, y, size, color):
"""Draw a broad leaf for lettuce"""
points = [
(x, y - size//2),
(x + size//2, y),
(x, y + size//2),
(x - size//2, y)
]
draw.polygon(points, fill=color)
def _draw_needle_leaf(self, draw, x, y, size, color):
"""Draw needle-like leaves for rosemary"""
for i in range(3):
offset = (i - 1) * 3
draw.line([x + offset, y - size//4, x + offset, y + size//4],
fill=color, width=2)
def _draw_fruits(self, draw, plant_type, center_x, top_y, base_y, color, stage):
"""Draw fruits based on plant type"""
if plant_type == 'tomato':
# Draw tomatoes
num_fruits = min(stage - 2, 4)
for i in range(num_fruits):
fruit_x = center_x + random.randint(-20, 20)
fruit_y = int(top_y + random.randint(10, (base_y - top_y) // 2))
fruit_size = 8 + stage * 2
draw.ellipse([fruit_x - fruit_size, fruit_y - fruit_size,
fruit_x + fruit_size, fruit_y + fruit_size], fill=color)
elif plant_type == 'strawberry':
# Draw strawberries
num_fruits = min(stage - 2, 3)
for i in range(num_fruits):
fruit_x = center_x + random.randint(-15, 15)
fruit_y = base_y - random.randint(20, 40)
self._draw_strawberry(draw, fruit_x, fruit_y, color)
def _draw_strawberry(self, draw, x, y, color):
"""Draw a strawberry shape"""
# Draw strawberry body
points = [(x, y - 8), (x + 6, y), (x, y + 8), (x - 6, y)]
draw.polygon(points, fill=color)
# Draw strawberry top (green)
draw.polygon([(x - 3, y - 8), (x, y - 12), (x + 3, y - 8)], fill='green')
def _adjust_color_health(self, color_hex, health_factor):
"""Adjust color based on plant health"""
# Convert hex to RGB
color_hex = color_hex.lstrip('#')
r, g, b = tuple(int(color_hex[i:i+2], 16) for i in (0, 2, 4))
# Adjust brightness based on health
factor = 0.5 + health_factor * 0.5 # Range from 0.5 to 1.0
r = int(r * factor)
g = int(g * factor)
b = int(b * factor)
# Ensure values are within valid range
r = max(0, min(255, r))
g = max(0, min(255, g))
b = max(0, min(255, b))
return f'#{r:02x}{g:02x}{b:02x}'
def save_evolution_sequence(self, images, filename):
"""Save evolution images as separate files"""
if images:
try:
base_name = filename.rsplit('.', 1)[0]
for i, image in enumerate(images):
stage_filename = f"{base_name}_stage_{i+1}.png"
image.save(stage_filename)
return True
except Exception as e:
print(f"Error saving images: {e}")
return False
return False