SmartHeal commited on
Commit
bdd7e15
·
verified ·
1 Parent(s): 5b04b46

Update src/ai_processor.py

Browse files
Files changed (1) hide show
  1. src/ai_processor.py +73 -0
src/ai_processor.py CHANGED
@@ -845,6 +845,75 @@ class AIProcessor:
845
  box_pts = None
846
  segmentation_empty = True
847
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
848
  # --- Save visualizations ---
849
  original_path = os.path.join(out_dir, f"original_{ts}.png")
850
  cv2.imwrite(original_path, image_cv)
@@ -923,6 +992,10 @@ class AIProcessor:
923
  "segmentation_empty": segmentation_empty,
924
  "segmentation_debug": seg_debug,
925
  "original_image_path": original_path,
 
 
 
 
926
  }
927
  except Exception as e:
928
  logging.error(f"Visual analysis failed: {e}", exc_info=True)
 
845
  box_pts = None
846
  segmentation_empty = True
847
 
848
+ # --- Skin tone estimation using ITA (Individual Typology Angle) ---
849
+ try:
850
+ # Convert ROI to LAB for skin tone measurement
851
+ # cv2.cvtColor returns LAB with L in [0,255], a,b in [0,255]; we convert to L* and b*
852
+ lab_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2LAB)
853
+ L_chan = lab_roi[:, :, 0] / 2.55 # scale to 0–100
854
+ b_chan = lab_roi[:, :, 2].astype(np.float32) - 128.0
855
+ # Define skin pixels as non-wound region or entire ROI if mask is empty
856
+ if mask01.any():
857
+ skin_mask = (mask01 == 0)
858
+ else:
859
+ skin_mask = np.ones_like(mask01, dtype=bool)
860
+ L_vals = L_chan[skin_mask]
861
+ b_vals = b_chan[skin_mask]
862
+ # Safeguard against empty arrays
863
+ if L_vals.size == 0 or b_vals.size == 0:
864
+ mean_L = float(np.mean(L_chan))
865
+ mean_b = float(np.mean(b_chan))
866
+ else:
867
+ mean_L = float(np.mean(L_vals))
868
+ mean_b = float(np.mean(b_vals))
869
+ # Compute ITA in degrees; use arctan2 to handle mean_b=0 gracefully
870
+ ita_deg = float(np.degrees(np.arctan2((mean_L - 50.0), mean_b))) if mean_b != 0 else 0.0
871
+ # Map ITA to Fitzpatrick skin tone categories (Del Bino ranges)
872
+ if ita_deg > 55:
873
+ skin_tone_label = "Type I (Very Light)"
874
+ elif 41 < ita_deg <= 55:
875
+ skin_tone_label = "Type II (Light)"
876
+ elif 28 < ita_deg <= 41:
877
+ skin_tone_label = "Type III (Intermediate)"
878
+ elif 10 < ita_deg <= 28:
879
+ skin_tone_label = "Type IV (Tan)"
880
+ elif -30 < ita_deg <= 10:
881
+ skin_tone_label = "Type V (Brown)"
882
+ else:
883
+ skin_tone_label = "Type VI (Dark)"
884
+ except Exception as e:
885
+ logging.warning(f"Skin tone estimation failed: {e}")
886
+ ita_deg = 0.0
887
+ skin_tone_label = "Unknown"
888
+
889
+ # --- Tissue classification (granulation, slough, necrotic) ---
890
+ try:
891
+ tissue_type = "Unknown"
892
+ if mask01.any():
893
+ hsv_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)
894
+ mask_bool = mask01.astype(bool)
895
+ # Compute mean hue and value on wound region
896
+ h_vals = hsv_roi[:, :, 0][mask_bool]
897
+ s_vals = hsv_roi[:, :, 1][mask_bool]
898
+ v_vals = hsv_roi[:, :, 2][mask_bool]
899
+ # Safeguard against empty arrays
900
+ if h_vals.size > 0 and v_vals.size > 0:
901
+ mean_h = float(np.mean(h_vals))
902
+ mean_v = float(np.mean(v_vals))
903
+ # Necrotic (dark) if value is low
904
+ if mean_v < 50:
905
+ tissue_type = "Necrotic"
906
+ # Slough (yellow) if hue between ~10 and 30 on OpenCV scale (0–179)
907
+ elif 10 <= mean_h <= 30:
908
+ tissue_type = "Slough"
909
+ else:
910
+ tissue_type = "Granulation"
911
+ else:
912
+ tissue_type = "Unknown"
913
+ except Exception as e:
914
+ logging.warning(f"Tissue classification failed: {e}")
915
+ tissue_type = "Unknown"
916
+
917
  # --- Save visualizations ---
918
  original_path = os.path.join(out_dir, f"original_{ts}.png")
919
  cv2.imwrite(original_path, image_cv)
 
992
  "segmentation_empty": segmentation_empty,
993
  "segmentation_debug": seg_debug,
994
  "original_image_path": original_path,
995
+ # Additional AI insights
996
+ "skin_tone_label": skin_tone_label,
997
+ "ita_degrees": round(float(ita_deg), 2),
998
+ "tissue_type": tissue_type,
999
  }
1000
  except Exception as e:
1001
  logging.error(f"Visual analysis failed: {e}", exc_info=True)