adil9858 commited on
Commit
de6d908
Β·
verified Β·
1 Parent(s): 7c4a78c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +375 -0
app.py ADDED
@@ -0,0 +1,375 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import base64
4
+ import os
5
+ from pathlib import Path
6
+ import tempfile
7
+ import time
8
+
9
+ # --- Configuration ---
10
+ # NOTE: In a real production app, use environment variables for keys!
11
+ OPENROUTER_KEY = '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'
12
+ HYPERBOLIC_KEY = 'sk_live_vFjJESXnc96z6qdylQsaGAJUE1p6fgRosF0ULkaZXCoe5uO43H_8lqtCjJ_F79kuQ'
13
+
14
+ class MediClearBackend:
15
+ """Handles the API logic to keep the UI code clean."""
16
+
17
+ @staticmethod
18
+ def encode_image(image_path):
19
+ with open(image_path, "rb") as image_file:
20
+ return base64.b64encode(image_file.read()).decode('utf-8')
21
+
22
+ @staticmethod
23
+ def analyze_medical_image(image_path):
24
+ """Step 1: Extract technical info using OpenRouter (Nvidia)"""
25
+ try:
26
+ base64_image = MediClearBackend.encode_image(image_path)
27
+ url = "https://openrouter.ai/api/v1/chat/completions"
28
+ headers = {
29
+ "Authorization": f"Bearer {OPENROUTER_KEY}",
30
+ "Content-Type": "application/json"
31
+ }
32
+
33
+ messages = [
34
+ {
35
+ "role": "system",
36
+ "content": "You are a highly experienced medical doctor. Extract every piece of information from the image given. Provide detailed observations."
37
+ },
38
+ {
39
+ "role": "user",
40
+ "content": [
41
+ {"type": "text", "text": "Please extract every piece of information from the image given"},
42
+ {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}
43
+ ]
44
+ }
45
+ ]
46
+
47
+ payload = {
48
+ "model": "nvidia/nemotron-nano-12b-v2-vl:free",
49
+ "messages": messages
50
+ }
51
+
52
+ response = requests.post(url, headers=headers, json=payload)
53
+ response.raise_for_status()
54
+ return response.json()['choices'][0]['message']['content']
55
+ except Exception as e:
56
+ raise Exception(f"Image Analysis Error: {str(e)}")
57
+
58
+ @staticmethod
59
+ def summarize_for_patient(technical_text):
60
+ """Step 2: Convert technical info to patient-friendly text using Hyperbolic"""
61
+ try:
62
+ url = "https://api.hyperbolic.xyz/v1/chat/completions"
63
+ headers = {
64
+ "Content-Type": "application/json",
65
+ "Authorization": f"Bearer {HYPERBOLIC_KEY}"
66
+ }
67
+
68
+ system_prompt = """You are a senior medical doctor with 50 years of experience.
69
+ Your role is to explain medical reports to patients in simple, reassuring, everyday language.
70
+ Structure:
71
+ 1. Warm greeting.
72
+ 2. Overall assessment.
73
+ 3. Key findings (compared to normal).
74
+ 4. Next steps.
75
+ 5. Reassuring closing.
76
+ Speak directly to the patient."""
77
+
78
+ data = {
79
+ "messages": [
80
+ {"role": "system", "content": system_prompt},
81
+ {"role": "user", "content": technical_text}
82
+ ],
83
+ "model": "moonshotai/Kimi-K2-Instruct",
84
+ "max_tokens": 4096,
85
+ "temperature": 0.1,
86
+ "top_p": 0.9
87
+ }
88
+
89
+ response = requests.post(url, headers=headers, json=data)
90
+ response.raise_for_status()
91
+ return response.json()['choices'][0]['message']['content']
92
+ except Exception as e:
93
+ raise Exception(f"Summary Error: {str(e)}")
94
+
95
+ def process_single_image(image_file):
96
+ """Process a single medical image and return the analysis"""
97
+ backend = MediClearBackend()
98
+
99
+ if image_file is None:
100
+ return "❌ Please upload a medical image first."
101
+
102
+ try:
103
+ # Create progress updates
104
+ progress_updates = []
105
+
106
+ # Step 1: Technical analysis
107
+ progress_updates.append("🩺 Step 1/2: Scanning image for medical details...")
108
+ technical_data = backend.analyze_medical_image(image_file.name)
109
+
110
+ # Step 2: Patient-friendly summary
111
+ progress_updates.append("πŸ“ Step 2/2: Generating patient-friendly summary...")
112
+ final_report = backend.summarize_for_patient(technical_data)
113
+
114
+ # Format the final report
115
+ filename = Path(image_file.name).name
116
+ formatted_report = f"""
117
+ {'='*60}
118
+ 🩺 MEDICAL ANALYSIS REPORT: {filename}
119
+ {'='*60}
120
+
121
+ {final_report}
122
+
123
+ {'='*60}
124
+ βœ… Analysis Complete - This tool provides AI-powered insights and is not a substitute for professional medical diagnosis.
125
+ {'='*60}
126
+ """
127
+ return formatted_report
128
+
129
+ except Exception as e:
130
+ return f"❌ Error processing image: {str(e)}"
131
+
132
+ def process_multiple_images(image_files):
133
+ """Process multiple medical images and return combined analysis"""
134
+ backend = MediClearBackend()
135
+
136
+ if not image_files:
137
+ return "❌ Please upload at least one medical image."
138
+
139
+ full_report = f"🩺 SEHATSCAN - COMPREHENSIVE MEDICAL ANALYSIS\n{'='*70}\n\n"
140
+ total = len(image_files)
141
+
142
+ for index, image_file in enumerate(image_files, 1):
143
+ try:
144
+ filename = Path(image_file.name).name
145
+ full_report += f"\nπŸ“„ IMAGE {index}/{total}: {filename}\n{'-'*50}\n"
146
+ full_report += "πŸ” Analyzing medical image...\n"
147
+
148
+ # Process the image
149
+ technical_data = backend.analyze_medical_image(image_file.name)
150
+ patient_summary = backend.summarize_for_patient(technical_data)
151
+
152
+ full_report += f"{patient_summary}\n\n"
153
+ full_report += "βœ… Analysis complete for this image.\n\n"
154
+
155
+ except Exception as e:
156
+ full_report += f"❌ Failed to process {filename}: {str(e)}\n\n"
157
+
158
+ full_report += f"{'='*70}\n"
159
+ full_report += "🏁 All analyses complete. Remember: This tool provides AI-powered insights and is not a substitute for professional medical diagnosis.\n"
160
+ full_report += f"{'='*70}"
161
+
162
+ return full_report
163
+
164
+ def create_gradio_interface():
165
+ """Create the Gradio interface"""
166
+
167
+ # Custom CSS for better styling
168
+ custom_css = """
169
+ .gradio-container {
170
+ background: linear-gradient(135deg, #1A1A1A 0%, #2D2D2D 100%);
171
+ }
172
+ .dark {
173
+ background: transparent !important;
174
+ }
175
+ .panel {
176
+ background: #252525 !important;
177
+ border-radius: 12px !important;
178
+ border: 1px solid #404040 !important;
179
+ padding: 20px !important;
180
+ }
181
+ .header {
182
+ background: linear-gradient(135deg, #1A1A1A 0%, #252525 100%) !important;
183
+ border-radius: 12px !important;
184
+ padding: 20px !important;
185
+ margin-bottom: 20px !important;
186
+ border: 1px solid #404040 !important;
187
+ }
188
+ .warning {
189
+ background: #FF980020 !important;
190
+ border: 1px solid #FF9800 !important;
191
+ border-radius: 8px !important;
192
+ padding: 15px !important;
193
+ margin: 10px 0 !important;
194
+ }
195
+ .success {
196
+ background: #4CAF5020 !important;
197
+ border: 1px solid #4CAF50 !important;
198
+ border-radius: 8px !important;
199
+ padding: 15px !important;
200
+ margin: 10px 0 !important;
201
+ }
202
+ """
203
+
204
+ with gr.Blocks(
205
+ title="SehatScan - Koshur AI",
206
+ css=custom_css,
207
+ theme=gr.themes.Soft(
208
+ primary_hue="yellow",
209
+ neutral_hue="slate"
210
+ )
211
+ ) as demo:
212
+
213
+ # Header Section
214
+ with gr.Column(elem_classes="header"):
215
+ gr.Markdown(
216
+ """
217
+ # 🩺 SehatScan - Koshur AI
218
+ ### Professional Medical Image Analysis
219
+ """
220
+ )
221
+
222
+ # Main Content
223
+ with gr.Row():
224
+ # Left Panel - Image Input
225
+ with gr.Column(scale=1, min_width=400):
226
+ with gr.Group(elem_classes="panel"):
227
+ gr.Markdown("### πŸ“ Upload Medical Images")
228
+
229
+ # Single image upload
230
+ single_image = gr.File(
231
+ label="Single Image Analysis",
232
+ file_types=[".jpg", ".jpeg", ".png", ".bmp"],
233
+ file_count="single",
234
+ type="filepath"
235
+ )
236
+
237
+ # Multiple image upload
238
+ multiple_images = gr.File(
239
+ label="Batch Image Analysis (Multiple)",
240
+ file_types=[".jpg", ".jpeg", ".png", ".bmp"],
241
+ file_count="multiple",
242
+ type="filepath"
243
+ )
244
+
245
+ # Analysis buttons
246
+ with gr.Row():
247
+ analyze_single_btn = gr.Button(
248
+ "πŸš€ Analyze Single Image",
249
+ variant="primary",
250
+ size="lg"
251
+ )
252
+ analyze_batch_btn = gr.Button(
253
+ "πŸ“Š Analyze Multiple Images",
254
+ variant="secondary",
255
+ size="lg"
256
+ )
257
+
258
+ # Clear button
259
+ clear_btn = gr.Button("πŸ—‘οΈ Clear All", variant="stop")
260
+
261
+ # Disclaimer
262
+ with gr.Group(elem_classes="warning"):
263
+ gr.Markdown(
264
+ """
265
+ ⚠️ **Medical Disclaimer**
266
+ This tool provides AI-powered insights and is not a substitute for professional medical diagnosis,
267
+ treatment, or medical advice. Always consult with qualified healthcare providers for medical decisions.
268
+ """
269
+ )
270
+
271
+ # Right Panel - Output
272
+ with gr.Column(scale=2, min_width=600):
273
+ with gr.Group(elem_classes="panel"):
274
+ gr.Markdown("### πŸ“‹ Doctor's Analysis Report")
275
+
276
+ output_text = gr.Textbox(
277
+ label="Analysis Results",
278
+ lines=25,
279
+ max_lines=50,
280
+ show_copy_button=True,
281
+ container=False
282
+ )
283
+
284
+ # Output actions
285
+ with gr.Row():
286
+ copy_btn = gr.Button("πŸ“‹ Copy to Clipboard", size="sm")
287
+ export_btn = gr.Button("πŸ’Ύ Export Report", size="sm")
288
+
289
+ # Footer
290
+ with gr.Column(elem_classes="panel"):
291
+ with gr.Row():
292
+ gr.Markdown(
293
+ """
294
+ **❀️ Built by Koshur AI β€’ An initiative for Kashmir**
295
+ [Visit our website](https://koshur-ai.github.io)
296
+ """
297
+ )
298
+
299
+ # Event handlers for single image analysis
300
+ analyze_single_btn.click(
301
+ fn=process_single_image,
302
+ inputs=[single_image],
303
+ outputs=[output_text],
304
+ api_name="analyze_single"
305
+ )
306
+
307
+ # Event handlers for multiple image analysis
308
+ analyze_batch_btn.click(
309
+ fn=process_multiple_images,
310
+ inputs=[multiple_images],
311
+ outputs=[output_text],
312
+ api_name="analyze_batch"
313
+ )
314
+
315
+ # Clear functionality
316
+ def clear_all():
317
+ return None, None, ""
318
+
319
+ clear_btn.click(
320
+ fn=clear_all,
321
+ outputs=[single_image, multiple_images, output_text]
322
+ )
323
+
324
+ # Copy functionality
325
+ def copy_to_clipboard(text):
326
+ return gr.update(value=text)
327
+
328
+ copy_btn.click(
329
+ fn=copy_to_clipboard,
330
+ inputs=[output_text],
331
+ outputs=[output_text]
332
+ )
333
+
334
+ # Export functionality (simulated - in Gradio, users can copy from textbox)
335
+ def export_report(text):
336
+ if text:
337
+ timestamp = time.strftime("%Y%m%d-%H%M%S")
338
+ filename = f"sehatscan_report_{timestamp}.txt"
339
+ return f"βœ… Report ready! You can copy the text above and save it as '{filename}'"
340
+ return "❌ No analysis to export."
341
+
342
+ export_btn.click(
343
+ fn=export_report,
344
+ inputs=[output_text],
345
+ outputs=[output_text]
346
+ )
347
+
348
+ # Examples
349
+ gr.Markdown("### 🎯 Try it out!")
350
+ gr.Examples(
351
+ examples=[
352
+ # You can add example image paths here for demo purposes
353
+ # ["examples/xray1.jpg"],
354
+ # ["examples/mri1.jpg"],
355
+ ],
356
+ inputs=[single_image],
357
+ outputs=[output_text],
358
+ fn=process_single_image,
359
+ cache_examples=False,
360
+ label="Click on any example below to try:"
361
+ )
362
+
363
+ return demo
364
+
365
+ # Create and launch the interface
366
+ if __name__ == "__main__":
367
+ demo = create_gradio_interface()
368
+
369
+ # For Hugging Face Spaces, use this launch method
370
+ demo.launch(
371
+ server_name="0.0.0.0" if os.getenv("SPACE_ID") else None,
372
+ share=False,
373
+ show_error=True,
374
+ debug=False
375
+ )