Spaces:
Sleeping
Sleeping
Update src/ai_processor.py
Browse files- 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)
|