amasood commited on
Commit
4434fcc
·
verified ·
1 Parent(s): 7ec9059

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -154
app.py CHANGED
@@ -1,187 +1,147 @@
1
- import os
2
- import io
3
- import base64
4
- import requests
5
- from PIL import Image
6
  import gradio as gr
 
7
  from transformers import pipeline
 
 
 
 
 
8
 
9
- # ----------------------------
10
- # Configuration
11
- # ----------------------------
12
- MODEL_ID = os.environ.get("HF_MODEL_ID", "imfarzanansari/skintelligent-acne")
13
- MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY")
14
- MISTRAL_MODEL = os.environ.get("MISTRAL_MODEL", "mistral-large-latest")
15
 
16
- # Load local acne classifier pipeline
17
  classifier = pipeline("image-classification", model=MODEL_ID)
18
 
19
- # ----------------------------
20
- # Helper functions
21
- # ----------------------------
22
- def fetch_image_from_url(url: str) -> Image.Image:
23
- """Download and return image from URL."""
24
- resp = requests.get(url, timeout=15)
25
- resp.raise_for_status()
26
- return Image.open(io.BytesIO(resp.content)).convert("RGB")
27
-
28
- def classify_acne(image_url: str):
29
- """Run acne classification and explanation."""
30
- try:
31
- img = fetch_image_from_url(image_url)
32
- except Exception as e:
33
- return {"error": f"Failed to fetch image: {e}"}
34
 
 
 
 
 
35
  try:
36
- preds = classifier(img)
 
37
  except Exception as e:
38
- return {"error": f"Model inference failed: {e}"}
39
 
40
- # Keep top 5 predictions
41
- top_preds = preds[:5]
42
- explanation = generate_explanation_with_mistral(top_preds)
 
43
 
44
- return {"image": img, "predictions": top_preds, "explanation": explanation}
 
45
 
46
- def generate_explanation_with_mistral(preds):
47
- """Generate explanation using Mistral API."""
48
- if not MISTRAL_API_KEY:
49
- lines = [f"{p['label']}: {p['score']*100:.1f}%" for p in preds]
50
- return (
51
- "\n".join(lines)
52
- + "\n\n(Note: Mistral API key not set — enable MISTRAL_API_KEY to get full explanations.)"
53
- )
54
 
55
- system_prompt = (
56
- "You are a helpful dermatology assistant. Explain each acne type simply: "
57
- "1) what it means, 2) likely causes, 3) basic care, 4) when to see a dermatologist. "
58
- "End with a disclaimer that this is not medical advice."
59
- )
60
 
61
- user_message = "Detected acne predictions:\n"
62
- for p in preds:
63
- user_message += f"- {p['label']}: {p['score']*100:.1f}%\n"
64
 
65
- user_message += "\nExplain these findings clearly and briefly."
66
-
67
- headers = {
68
- "Authorization": f"Bearer {MISTRAL_API_KEY}",
69
- "Content-Type": "application/json",
 
 
 
70
  }
 
71
 
72
- body = {
73
- "model": MISTRAL_MODEL,
74
- "messages": [
75
- {"role": "system", "content": system_prompt},
76
- {"role": "user", "content": user_message},
77
- ],
78
- "max_tokens": 400,
79
- "temperature": 0.2,
80
- }
81
 
 
 
 
82
  try:
83
- r = requests.post(
84
- "https://api.mistral.ai/v1/chat/completions",
85
- json=body,
86
- headers=headers,
87
- timeout=30,
88
  )
89
- r.raise_for_status()
90
- j = r.json()
91
- if "choices" in j and len(j["choices"]) > 0:
92
- return j["choices"][0]["message"]["content"]
93
- return str(j)
94
  except Exception as e:
95
- return f"(LLM explanation unavailable: {e})"
96
-
97
- # ----------------------------
98
- # Chatbot
99
- # ----------------------------
100
- def mistral_chat(messages):
101
- """Simple chat interface using Mistral API."""
102
- if not MISTRAL_API_KEY:
103
- return "Mistral API key not set."
104
-
105
- body = {
106
- "model": MISTRAL_MODEL,
107
- "messages": [{"role": r, "content": c} for r, c in messages],
108
- "max_tokens": 400,
109
- "temperature": 0.3,
110
- }
111
- headers = {
112
- "Authorization": f"Bearer {MISTRAL_API_KEY}",
113
- "Content-Type": "application/json",
114
- }
115
-
116
- try:
117
- r = requests.post(
118
- "https://api.mistral.ai/v1/chat/completions",
119
- json=body,
120
- headers=headers,
121
- timeout=30,
122
- )
123
- r.raise_for_status()
124
- j = r.json()
125
- if "choices" in j and len(j["choices"]) > 0:
126
- return j["choices"][0]["message"]["content"]
127
- return str(j)
128
- except Exception as e:
129
- return f"(Chat unavailable: {e})"
130
-
131
- # ----------------------------
132
- # Gradio UI
133
- # ----------------------------
134
- def run_analysis(image_url: str):
135
- result = classify_acne(image_url)
136
- if "error" in result:
137
- return result["error"], None, "", None
138
-
139
- img = result["image"]
140
- preds_str = "\n".join(
141
- [f"{p['label']} — {p['score']*100:.1f}%" for p in result["predictions"]]
142
  )
143
- return None, img, preds_str, result["explanation"]
144
-
145
- with gr.Blocks(title="Acne Type Classifier & Chat") as demo:
146
- gr.Markdown("# Acne Type Classifier — Enter Image URL and Get Insights")
147
 
148
  with gr.Row():
149
- with gr.Column(scale=1):
150
- url_in = gr.Textbox(label="Image URL", placeholder="https://...jpg")
151
- analyze_btn = gr.Button("Analyze Image")
152
- error_out = gr.Textbox(label="Error", interactive=False)
153
 
 
154
  with gr.Column(scale=1):
155
- image_out = gr.Image(type="pil", label="Input Image")
156
- preds_out = gr.Textbox(label="Predictions", interactive=False)
157
- explanation_out = gr.Textbox(label="Explanation", interactive=False)
158
-
159
- analyze_btn.click(
160
- run_analysis, inputs=[url_in], outputs=[error_out, image_out, preds_out, explanation_out]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  )
162
 
163
- gr.Markdown("---\n## Ask a Question About Acne")
164
-
165
- chatbot = gr.Chatbot()
166
- user_msg = gr.Textbox(label="Your Question")
167
- send_btn = gr.Button("Send")
168
-
169
- history = gr.State([])
170
-
171
- def on_send(user_text, history):
172
- history = history or []
173
- history.append(("user", user_text))
174
- reply = mistral_chat(history)
175
- history.append(("assistant", reply))
176
 
177
- pairs = []
178
- for i in range(0, len(history), 2):
179
- u = history[i][1] if i < len(history) else ""
180
- a = history[i + 1][1] if i + 1 < len(history) else ""
181
- pairs.append([u, a])
182
- return pairs, history
183
 
184
- send_btn.click(on_send, inputs=[user_msg, history], outputs=[chatbot, history])
 
 
 
 
 
 
185
 
 
186
  if __name__ == "__main__":
187
  demo.launch()
 
 
 
 
 
 
1
  import gradio as gr
2
+ from huggingface_hub import InferenceApi
3
  from transformers import pipeline
4
+ import requests
5
+ from PIL import Image
6
+ import io
7
+ import os
8
+ import openai
9
 
10
+ # -----------------------------
11
+ # CONFIGURATION
12
+ # -----------------------------
13
+ # Hugging Face model for acne classification
14
+ MODEL_ID = "imfarzanansari/skintelligent-acne"
 
15
 
16
+ # Use local pipeline for image classification (faster and more stable)
17
  classifier = pipeline("image-classification", model=MODEL_ID)
18
 
19
+ # Set your Mistral API key (via environment variable)
20
+ openai.api_key = os.getenv("MISTRAL_API_KEY")
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ # -----------------------------
23
+ # HELPER FUNCTIONS
24
+ # -----------------------------
25
+ def classify_acne(image_url):
26
  try:
27
+ response = requests.get(image_url)
28
+ img = Image.open(io.BytesIO(response.content)).convert("RGB")
29
  except Exception as e:
30
+ return " Could not load image. Please check the URL.", "", None
31
 
32
+ # Run the acne classification
33
+ preds = classifier(img)
34
+ if not preds:
35
+ return "No prediction.", "", img
36
 
37
+ top_pred = preds[0]["label"]
38
+ score = preds[0]["score"]
39
 
40
+ # Explanation text
41
+ explanation = explain_acne_type(top_pred)
 
 
 
 
 
 
42
 
43
+ result_text = f"**Detected Acne Type:** {top_pred}\n\n**Confidence:** {score:.2f}"
44
+ return result_text, explanation, img
 
 
 
45
 
 
 
 
46
 
47
+ def explain_acne_type(acne_type):
48
+ explanations = {
49
+ "Blackheads": "Blackheads are open comedones caused by clogged hair follicles with sebum and dead skin. They appear black due to oxidation.",
50
+ "Whiteheads": "Whiteheads are closed comedones formed when pores are clogged with oil and dead skin but remain closed at the surface.",
51
+ "Papules": "Papules are small, red, inflamed bumps without visible pus. They often result from irritated or infected clogged pores.",
52
+ "Pustules": "Pustules are pus-filled pimples with a white or yellow center. They can be tender and are often caused by bacterial infection.",
53
+ "Nodules": "Nodules are large, painful lumps deep under the skin caused by severe inflammation and infection in clogged pores.",
54
+ "Cysts": "Cysts are severe acne lesions filled with pus and can cause scarring if not treated properly.",
55
  }
56
+ return explanations.get(acne_type, "This acne type is uncommon or not specifically defined in the dataset.")
57
 
 
 
 
 
 
 
 
 
 
58
 
59
+ def query_acne_info(acne_type, user_query):
60
+ if not user_query.strip():
61
+ return "Please enter a question."
62
  try:
63
+ prompt = f"You are an expert dermatologist. The user has acne type '{acne_type}'. Answer this query:\n{user_query}"
64
+ completion = openai.ChatCompletion.create(
65
+ model="mistral-tiny",
66
+ messages=[{"role": "user", "content": prompt}],
67
+ temperature=0.6,
68
  )
69
+ return completion.choices[0].message["content"]
 
 
 
 
70
  except Exception as e:
71
+ return f"Error: {str(e)}"
72
+
73
+
74
+ # -----------------------------
75
+ # GRADIO INTERFACE
76
+ # -----------------------------
77
+ with gr.Blocks(theme=gr.themes.Soft(), title="Acne Type Classifier & Chatbot") as demo:
78
+ gr.Markdown(
79
+ """
80
+ # 🧴 Acne Type Classifier & Dermatology Assistant
81
+ Upload or paste the URL of an acne image to detect its type.
82
+ Then ask any query about the detected acne type using the chatbot below.
83
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  )
 
 
 
 
85
 
86
  with gr.Row():
87
+ image_url = gr.Textbox(label="🔗 Enter Image URL", placeholder="Paste image URL here...")
88
+ submit_btn = gr.Button("Classify", variant="primary")
 
 
89
 
90
+ with gr.Row():
91
  with gr.Column(scale=1):
92
+ image_output = gr.Image(label="Uploaded Image", type="pil")
93
+
94
+ with gr.Column(scale=2):
95
+ result_box = gr.Markdown(label="Prediction Result", elem_classes="big-box")
96
+ explanation_box = gr.Textbox(
97
+ label="Acne Explanation",
98
+ lines=6,
99
+ interactive=False,
100
+ elem_classes="big-box"
101
+ )
102
+
103
+ # Chatbot section
104
+ with gr.Accordion("💬 Ask Dermatology Chatbot", open=True):
105
+ with gr.Row():
106
+ user_query = gr.Textbox(
107
+ label="Enter your query about the detected acne",
108
+ placeholder="e.g., What is the best treatment for cystic acne?",
109
+ lines=2,
110
+ )
111
+ with gr.Row():
112
+ chat_response = gr.Textbox(
113
+ label="Chatbot Response",
114
+ lines=6,
115
+ interactive=False,
116
+ elem_classes="big-box"
117
+ )
118
+ chat_btn = gr.Button("Ask Chatbot", variant="secondary")
119
+
120
+ # Button functionality
121
+ submit_btn.click(
122
+ classify_acne,
123
+ inputs=[image_url],
124
+ outputs=[result_box, explanation_box, image_output],
125
  )
126
 
127
+ chat_btn.click(
128
+ query_acne_info,
129
+ inputs=[result_box, user_query],
130
+ outputs=[chat_response],
131
+ )
 
 
 
 
 
 
 
 
132
 
133
+ gr.Markdown(
134
+ "#### ⚕️ Disclaimer: This app provides general information and should not replace professional medical advice."
135
+ )
 
 
 
136
 
137
+ # Custom CSS to enlarge boxes
138
+ demo.css = """
139
+ .big-box textarea, .big-box pre, .big-box .wrap {
140
+ height: 220px !important;
141
+ font-size: 16px;
142
+ }
143
+ """
144
 
145
+ # Launch app
146
  if __name__ == "__main__":
147
  demo.launch()