201 lines
No EOL
7.9 KiB
Python
201 lines
No EOL
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() |