201 lines
7.9 KiB
Python
201 lines
7.9 KiB
Python
![]() |
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 ~25–85)
|
|||
|
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()
|