team-8/image_extraction.py
francesco-bufalini 0ba7189bfc Commit last-minute
2025-08-02 13:46:28 +02:00

201 lines
No EOL
7.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from plantcv import plantcv as pcv
import cv2, json, copy
def plantcv_to_json(obs, plant="vine"):
# Morfologia
area = obs["plant_1"]["area"]["value"]
solidity = obs["plant_1"]["solidity"]["value"]
ecc = obs["plant_1"]["ellipse_eccentricity"]["value"]
height = obs["plant_1"]["height"]["value"]
if area < 100000: area_cat = "small"
elif area < 300000: area_cat = "medium"
else: area_cat = "large"
if solidity > 0.8: solidity_cat = "compact"
elif solidity > 0.5: solidity_cat = "normal"
else: solidity_cat = "loose"
if ecc < 0.4: shape_cat = "round"
elif ecc < 0.7: shape_cat = "elliptical"
else: shape_cat = "elongated"
if height < 50: height_cat = "low"
elif height < 150: height_cat = "medium"
else: height_cat = "high"
# Colore
h_mean = obs["plant_1"]["hue_circular_mean"]["value"]
h_std = obs["plant_1"]["hue_circular_std"]["value"]
if h_mean < 30: leaf_color = "yellowish"
elif h_mean < 90: leaf_color = "green-yellowish"
elif h_mean < 150: leaf_color = "green"
else: leaf_color = "bluish-green"
uniformity = "uniform" if h_std < 5 else "moderate" if h_std < 15 else "variegated"
# Health
gm_freq = obs["plant_1"]["green-magenta_frequencies"]["value"]
if sum(gm_freq[len(gm_freq)//2:]) > sum(gm_freq[:len(gm_freq)//2]):
health = "stressed"
else:
health = "good"
# Leaf count (se passato)
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
leaf_count = len(contours)
if leaf_count < 5: leaf_count_cat = "few"
elif leaf_count < 15: leaf_count_cat = "medium"
else: leaf_count_cat = "many"
result = {
"plant": plant,
"morphology": {
"area": area_cat,
"shape": shape_cat,
"solidity": solidity_cat,
"height": height_cat,
"leaf_count": leaf_count_cat
},
"color": {
"leaf_color": leaf_color,
"uniformity": uniformity
},
"health": {
"status": health
}
}
return result
def simplify_plantcv_results(filepath, plant_key="plant_1"):
"""
Carica un JSON generato da PlantCV e restituisce una versione semplificata
con i valori chiave (senza istogrammi lunghi).
"""
with open(filepath, "r") as f:
data = json.load(f)
obs = data.get("observations", {}).get(plant_key, {})
# Chiavi utili da conservare
keep_keys = [
"hue_circular_mean",
"hue_circular_std",
"hue_median",
"area",
"solidity",
"height",
"width",
"perimeter",
"longest_path",
"ellipse_major_axis",
"ellipse_minor_axis",
"ellipse_eccentricity",
"object_in_frame"
]
simplified = {}
for key in keep_keys:
if key in obs:
val = obs[key].get("value", None)
if isinstance(val, list) and len(val) == 1:
val = val[0]
simplified[key] = val
return simplified
def plantcv_to_hydroshoot(obs, base_config, scale_factor=0.025):
"""
Converte osservazioni PlantCV in parametri HydroShoot.
- obs: dizionario semplificato da PlantCV (hue, area, height, leaf_count, ecc.)
- base_config: JSON HydroShoot di partenza (dict caricato da file)
- scale_factor: conversione pixel → cm
"""
with open("hydroshoot_example.json", "r") as f:
base_config = json.load(f)
config = copy.deepcopy(base_config)
# Conversioni px → cm / cm²
height_cm = obs.get("height", 0) * scale_factor
area_cm2 = obs.get("area", 0) * (scale_factor**2)
leaf_count = obs.get("leaf_count", 0)
mean_leaf_area = area_cm2 / leaf_count if leaf_count > 0 else 0
# Colore foglie → stato salute
h_mean = obs.get("hue_circular_mean", 0)
if h_mean < 30:
leaf_color = "yellow"
photo_eff = 0.5 # fotosintesi molto ridotta
elif h_mean < 90:
leaf_color = "yellow-green"
photo_eff = 0.7
else:
leaf_color = "green"
photo_eff = 1.0
# Stato salute generale
uniformity = obs.get("hue_circular_std", 0)
health_index = max(0.0, 1.0 - uniformity/30.0)
# Aggiornamenti HydroShoot
config["simulation"]["unit_scene_length"] = "cm"
config["planting"]["spacing_on_row"] = max(0.5, 1 - (leaf_count/1000)) # spacing ridotto con piante più fitte
config["exchange"]["par_photo"]["alpha"] *= photo_eff * health_index
config["exchange"]["par_gs"]["g0"] *= health_index
config["exchange"]["par_gs"]["m0"] *= health_index
config["soil"]["rhyzo_coeff"] = min(1.0, 0.25 + (area_cm2/10000.0)) # più area = più radici attive
# Salviamo anche un blocco fenotipico extra
config["plantcv_update"] = {
"plant_height_cm": round(height_cm, 1),
"total_leaf_area_cm2": round(area_cm2, 1),
"mean_leaf_area_cm2": round(mean_leaf_area, 1),
"leaf_count": int(leaf_count),
"leaf_color": leaf_color,
"health_index": round(health_index, 2)
}
return config
def main():
# Prende il nome del file dell'immagine base
filename = "baseImg.jpeg"
# Legge immagine con PlantCV
img, path, filename = pcv.readimage(filename, mode='rgb')
# FIX: se viene restituito un tuple, prendo solo la prima immagine (RGB)
if isinstance(img, tuple):
img = img[0]
print("Formato immagine:", img.shape) # dovrebbe stampare (H, W, 3)
# ==============================================================
# Segmentazione verde con custom_range (HSV) - serve a estrarre immagine della pianta da sfondo
# ==============================================================
# Converte in Hue
hsv_hue = pcv.rgb2gray_hsv(img, channel='h')
# Soglia Hue (verde ~2585)
mask_low = pcv.threshold.binary(hsv_hue, threshold=25, object_type='light')
mask_high = pcv.threshold.binary(hsv_hue, threshold=85, object_type='dark')
# Combina le due maschere → range verde
mask = pcv.logical_and(mask_low, mask_high)
# Riempi piccoli buchi nella maschera
mask = pcv.fill(bin_img=mask, size=200)
# ==============================================================
# Analisi colore e forma
# ==============================================================
pcv.analyze.color(img, mask, colorspaces="hsv", label="plant")
pcv.analyze.color(img, mask, colorspaces="lab", label="plant")
pcv.analyze.size(img, mask, label="plant")
# ==============================================================
# Output RAW da PlantCV
# ==============================================================
#print("Output RAW da PlantCV:\n")
obs=pcv.outputs.observations
#controlla le chiavi presenti nel dizionario
print(pcv.outputs.observations.keys())
#controlla le sottochiavi dentro a plant_1 (la chiave giusta sotto cui salva tutti i dati al momento, le altre che leggi nella print prima sono di test vecchi)
print(pcv.outputs.observations["plant_1"].keys())
result=pcv.outputs.save_results("output.json")
finalJson=simplify_plantcv_results("output.json")
print("\nJSON risultante per Stable Diffusion:\n")
print(json.dumps(finalJson, indent=2))
# ==============================================================
# Conversione a json compatibile con HydroShoot
# ==============================================================
with open(r".\hydroshoot\example\vsp_ws_grapevine\params.json", "r") as f:
base_config = json.load(f)
#backup
with open(r".\hydroshoot\example\vsp_ws_grapevine\params_backup.json", "w") as f:
json.dump(base_config, f, indent=2) # indent=2 per leggibilità
new_config = plantcv_to_hydroshoot(obs, base_config)
# Salva su file (sovrascrivi params.json)
with open(r".\hydroshoot\example\vsp_ws_grapevine\params.json", "w") as f:
json.dump(new_config, f, indent=2) # indent=2 per leggibilità
if __name__=='__main__':
main()