prakman's picture
Update app.py
3dc96fa verified
# app.py - backward-compatible SmartPack launcher (auto-detects Gradio editor support)
import os, joblib, inspect
import gradio as gr
import numpy as np, pandas as pd, cv2
from PIL import Image
# (keep your feature columns / helper functions)
FEATURE_COLS = [
'mean_R','mean_G','mean_B',
'std_R','std_G','std_B',
'mean_H','mean_S','mean_V',
'std_H','std_S','std_V',
'brightness','ratio_R_G','ratio_R_B'
]
# load models (example)
clf = None
rfr = None
for fname, varname in (("final_rfc.joblib","clf"), ("final_rfr.joblib","rfr")):
if os.path.exists(fname):
try:
loaded = joblib.load(fname)
if fname.endswith("rfc.joblib"): clf = loaded
else: rfr = loaded
print("[MODEL] Loaded:", fname)
except Exception as e:
print("[MODEL] Error loading", fname, ":", e)
# small helper to detect gr.Image params
def gr_image_supports_editor():
try:
sig = inspect.signature(gr.Image.__init__)
params = sig.parameters
return ('tool' in params) and ('source' in params)
except Exception:
return False
SUPPORTS_EDITOR = gr_image_supports_editor()
print("Gradio version:", getattr(gr, "__version__", "unknown"), "Editor support:", SUPPORTS_EDITOR)
# --- reuse your feature extraction & tvb helper (kept concise here) ---
def extract_features(pil_img):
arr = np.array(pil_img.convert("RGB"))
mean_rgb = arr.mean(axis=(0,1))
std_rgb = arr.std(axis=(0,1))
hsv = cv2.cvtColor(arr, cv2.COLOR_RGB2HSV)
mean_hsv = hsv.mean(axis=(0,1))
std_hsv = hsv.std(axis=(0,1))
R,G,B = mean_rgb
brightness = 0.299*R + 0.587*G + 0.114*B
feats = {
'mean_R':float(R),'mean_G':float(G),'mean_B':float(B),
'std_R':float(std_rgb[0]),'std_G':float(std_rgb[1]),'std_B':float(std_rgb[2]),
'mean_H':float(mean_hsv[0]),'mean_S':float(mean_hsv[1]),'mean_V':float(mean_hsv[2]),
'std_H':float(std_hsv[0]),'std_S':float(std_hsv[1]),'std_V':float(std_hsv[2]),
'brightness':float(brightness),
'ratio_R_G':float(R/(G+1e-6)),
'ratio_R_B':float(R/(B+1e-6))
}
return feats
def literature_tvb_estimate(time_hr, temp_C):
days = float(time_hr) / 24.0
if temp_C >= 25:
if days <= 0: return 13.0
if days <= 5: return 13.0 + (days/5.0)*(35-13)
return 70.0 + (days-5.0)*3.0
elif temp_C >= 10:
if days <= 0: return 13.0
if days <= 3: return 13.0 + (days/3.0)*(30-13)
return 30.0 + (days-3.0)*1.5
elif temp_C >= 4:
if days <= 0: return 13.0
if days <= 7: return 13.0 + (days/7.0)*(30-13)
return 30.0 + (days-7.0)*0.5
else:
if days <= 0: return 13.0
return 13.0 + days * 0.1
def tvb_to_class_label(tvb_val):
if tvb_val is None: return None
if tvb_val < 20: return "Fresh"
elif tvb_val < 30: return "Mildly Spoiled"
else: return "Spoiled"
# main analyze function (same outputs as your earlier UI)
def analyze(image, mass_g, time_hr, temperature_C, use_meta_for_tvb):
if image is None:
return "No image uploaded", None, None, "No image"
# If editor supported, the image is expected to be already cropped by user.
# Otherwise we attempt naive autocrop center-crop fallback (you can replace with your autocrop).
img_pil = image if isinstance(image, Image.Image) else Image.fromarray(image)
feats = extract_features(img_pil)
X = pd.DataFrame([feats])
# classification
cls_pred = "Classifier not available"
if clf is not None:
try:
cls_pred = str(clf.predict(X[FEATURE_COLS])[0])
except Exception as e:
cls_pred = f"Classifier error: {e}"
# tvb
tvb = None
if rfr is not None:
try:
tvb = float(rfr.predict(X[FEATURE_COLS])[0])
except Exception:
tvb = None
if (tvb is None) and use_meta_for_tvb and (time_hr is not None) and (temperature_C is not None):
try:
tvb = literature_tvb_estimate(time_hr, temperature_C)
except Exception:
tvb = None
meta_msg = f"mass_g={mass_g} time_hr={time_hr} temp_C={temperature_C}"
return cls_pred, (None if tvb is None else float(tvb)), img_pil, meta_msg
# Build UI depending on support
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# SmartPack — Mackerel Freshness Detector")
if SUPPORTS_EDITOR:
gr.Markdown("Editor available: use the crop tool (pencil icon) to crop the indicator.")
else:
gr.Markdown("**Editor unavailable in this Gradio version.**\n- Either upgrade Gradio (pip install -U gradio) or crop images before upload.\n- App will proceed with provided image (no in-browser crop).")
with gr.Row():
with gr.Column(scale=2):
if SUPPORTS_EDITOR:
img_in = gr.Image(type="pil", label="Upload image (use crop tool)", tool="editor", source="upload")
else:
img_in = gr.Image(type="pil", label="Upload pre-cropped indicator image (no editor available)")
mass_in = gr.Number(label="Mass (g)", value=None, precision=0)
time_in = gr.Number(label="Time (hr)", value=None, precision=1)
temp_in = gr.Number(label="Temperature (°C)", value=None, precision=1)
use_meta = gr.Checkbox(label="Use metadata (time/temp) for TVB-N estimate if regressor missing", value=True)
submit = gr.Button("Analyze")
with gr.Column(scale=1):
out_cls = gr.Textbox(label="Freshness class")
out_tvb = gr.Number(label="Estimated TVB-N (mg/100g)")
out_crop = gr.Image(label="Cropped indicator (echo)")
out_meta = gr.Textbox(label="Provided metadata (echo)")
submit.click(fn=analyze, inputs=[img_in, mass_in, time_in, temp_in, use_meta],
outputs=[out_cls, out_tvb, out_crop, out_meta])
if __name__ == "__main__":
demo.launch(server_name="0.0.0.0", server_port=7860)