Bonosa2 commited on
Commit
1048023
Β·
verified Β·
1 Parent(s): 4417072

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +327 -308
app.py CHANGED
@@ -1,309 +1,328 @@
1
- import gradio as gr
2
- import torch
3
- import numpy as np
4
- from PIL import Image
5
- import time
6
- import io
7
- import subprocess
8
- import sys
9
-
10
- # Install required packages
11
- def install_packages():
12
- packages = [
13
- "transformers",
14
- "accelerate",
15
- "timm",
16
- "easyocr"
17
- ]
18
- for package in packages:
19
- try:
20
- subprocess.check_call([sys.executable, "-m", "pip", "install", package])
21
- except:
22
- print(f"Warning: Could not install {package}")
23
-
24
- # Install packages at startup
25
- install_packages()
26
-
27
- from transformers import AutoProcessor, AutoModelForImageTextToText, AutoConfig
28
-
29
- # Global variables for model
30
- processor = None
31
- model = None
32
- config = None
33
- ocr_reader = None
34
-
35
- def load_model():
36
- """Load the Gemma 3n model"""
37
- global processor, model, config, ocr_reader
38
-
39
- try:
40
- print("πŸš€ Loading Gemma 3n model...")
41
- GEMMA_PATH = "google/gemma-3n-e2b-it"
42
-
43
- # Load configuration
44
- config = AutoConfig.from_pretrained(GEMMA_PATH, trust_remote_code=True)
45
- print("βœ… Config loaded")
46
-
47
- # Load processor
48
- processor = AutoProcessor.from_pretrained(GEMMA_PATH, trust_remote_code=True)
49
- print("βœ… Processor loaded")
50
-
51
- # Load model
52
- model = AutoModelForImageTextToText.from_pretrained(
53
- GEMMA_PATH,
54
- config=config,
55
- torch_dtype="auto",
56
- device_map="auto",
57
- trust_remote_code=True
58
- )
59
- print("βœ… Model loaded successfully!")
60
-
61
- # Set up compilation fix
62
- import torch._dynamo
63
- torch._dynamo.config.suppress_errors = True
64
-
65
- # Initialize OCR
66
- try:
67
- import easyocr
68
- ocr_reader = easyocr.Reader(['en'], gpu=False, verbose=False)
69
- print("βœ… EasyOCR initialized")
70
- except Exception as e:
71
- print(f"⚠️ EasyOCR not available: {e}")
72
- ocr_reader = None
73
-
74
- return True
75
-
76
- except Exception as e:
77
- print(f"❌ Model loading failed: {e}")
78
- return False
79
-
80
- def generate_soap_note(text):
81
- """Generate SOAP note using Gemma 3n"""
82
- if model is None or processor is None:
83
- return "❌ Model not loaded. Please wait for initialization."
84
-
85
- soap_prompt = f"""You are a medical AI assistant. Convert the following medical notes into a properly formatted SOAP note.
86
-
87
- Medical notes:
88
- {text}
89
-
90
- Please format as:
91
- S - SUBJECTIVE: (chief complaint, history of present illness, past medical history, medications, allergies)
92
- O - OBJECTIVE: (vital signs, physical examination findings)
93
- A - ASSESSMENT: (diagnosis/clinical impression)
94
- P - PLAN: (treatment plan, follow-up instructions)
95
-
96
- Generate a complete, professional SOAP note:"""
97
-
98
- messages = [{
99
- "role": "system",
100
- "content": [{"type": "text", "text": "You are an expert medical AI assistant specialized in creating SOAP notes from medical documentation."}]
101
- }, {
102
- "role": "user",
103
- "content": [{"type": "text", "text": soap_prompt}]
104
- }]
105
-
106
- try:
107
- inputs = processor.apply_chat_template(
108
- messages,
109
- add_generation_prompt=True,
110
- tokenize=True,
111
- return_dict=True,
112
- return_tensors="pt"
113
- ).to(model.device)
114
-
115
- input_len = inputs["input_ids"].shape[-1]
116
-
117
- with torch.no_grad():
118
- outputs = model.generate(
119
- **inputs,
120
- max_new_tokens=400,
121
- do_sample=True,
122
- temperature=0.1,
123
- top_p=0.95,
124
- pad_token_id=processor.tokenizer.eos_token_id,
125
- disable_compile=True
126
- )
127
-
128
- response = processor.batch_decode(
129
- outputs[:, input_len:],
130
- skip_special_tokens=True
131
- )[0].strip()
132
-
133
- return response
134
-
135
- except Exception as e:
136
- return f"❌ SOAP generation failed: {str(e)}"
137
-
138
- def extract_text_from_image(image):
139
- """Extract text using EasyOCR"""
140
- if ocr_reader is None:
141
- return "❌ OCR not available"
142
-
143
- try:
144
- if hasattr(image, 'convert'):
145
- image = image.convert('RGB')
146
- img_array = np.array(image)
147
-
148
- results = ocr_reader.readtext(img_array, detail=0, paragraph=True)
149
- if results:
150
- return ' '.join(results).strip()
151
- else:
152
- return "❌ No text detected in image"
153
-
154
- except Exception as e:
155
- return f"❌ OCR failed: {str(e)}"
156
-
157
- def process_medical_input(image, text):
158
- """Main processing function for the Gradio interface"""
159
-
160
- if image is not None and text.strip():
161
- return "⚠️ Please provide either an image OR text, not both.", ""
162
-
163
- if image is not None:
164
- # Process image
165
- print("πŸ” Extracting text from image...")
166
- extracted_text = extract_text_from_image(image)
167
-
168
- if extracted_text.startswith('❌'):
169
- return extracted_text, ""
170
-
171
- print("πŸ€– Generating SOAP note...")
172
- soap_note = generate_soap_note(extracted_text)
173
-
174
- return extracted_text, soap_note
175
-
176
- elif text.strip():
177
- # Process text directly
178
- print("πŸ€– Generating SOAP note from text...")
179
- soap_note = generate_soap_note(text.strip())
180
- return text.strip(), soap_note
181
-
182
- else:
183
- return "❌ Please provide either an image or text input.", ""
184
-
185
- def create_demo():
186
- """Create the Gradio demo interface"""
187
-
188
- # Sample text for demonstration
189
- sample_text = """Patient: John Smith, 45yo male
190
- CC: Chest pain
191
- Vitals: BP 140/90, HR 88, RR 16, O2 98%, Temp 98.6F
192
- HPI: Patient reports crushing chest pain x 2 hours, radiating to left arm
193
- PMH: HTN, DM Type 2
194
- Current Meds: Lisinopril 10mg daily, Metformin 500mg BID
195
- PE: Diaphoretic, anxious appearance
196
- EKG: ST elevation in leads II, III, aVF"""
197
-
198
- with gr.Blocks(title="Medical OCR SOAP Generator", theme=gr.themes.Soft()) as demo:
199
-
200
- gr.Markdown("""
201
- # πŸ₯ Medical OCR SOAP Generator
202
- ### Powered by Gemma 3n - Convert handwritten medical notes to professional SOAP format
203
-
204
- **Instructions:**
205
- - **Option 1:** Upload an image of handwritten medical notes
206
- - **Option 2:** Enter medical text directly
207
- - The system will generate a properly formatted SOAP note
208
-
209
- ⚠️ **Note:** First generation may take ~60-90 seconds as the model loads
210
- """)
211
-
212
- with gr.Row():
213
- with gr.Column():
214
- image_input = gr.Image(
215
- type="pil",
216
- label="πŸ“· Upload Medical Image",
217
- height=300
218
- )
219
-
220
- text_input = gr.Textbox(
221
- label="πŸ“ Or Enter Medical Text",
222
- placeholder=sample_text,
223
- lines=8,
224
- max_lines=15
225
- )
226
-
227
- submit_btn = gr.Button(
228
- "Generate SOAP Note",
229
- variant="primary",
230
- size="lg"
231
- )
232
-
233
- with gr.Column():
234
- extracted_output = gr.Textbox(
235
- label="πŸ“‹ Extracted/Input Text",
236
- lines=6,
237
- max_lines=10
238
- )
239
-
240
- soap_output = gr.Textbox(
241
- label="πŸ₯ Generated SOAP Note",
242
- lines=12,
243
- max_lines=20
244
- )
245
-
246
- # Example section
247
- gr.Markdown("### πŸ“‹ Quick Test Example")
248
- example_btn = gr.Button("Try Sample Medical Text", variant="secondary")
249
-
250
- def load_example():
251
- return sample_text, None
252
-
253
- example_btn.click(
254
- load_example,
255
- outputs=[text_input, image_input]
256
- )
257
-
258
- # Process function
259
- submit_btn.click(
260
- process_medical_input,
261
- inputs=[image_input, text_input],
262
- outputs=[extracted_output, soap_output]
263
- )
264
-
265
- gr.Markdown("""
266
- ---
267
- **About:** This application uses Google's Gemma 3n model for medical text understanding and EasyOCR for handwriting recognition.
268
- All processing is done locally for HIPAA compliance.
269
-
270
- **Competition Entry:** Medical AI Innovation Challenge 2024
271
- """)
272
-
273
- return demo
274
-
275
- # Initialize the application
276
- if __name__ == "__main__":
277
- print("πŸš€ Starting Medical OCR SOAP Generator...")
278
-
279
- # Load model
280
- model_loaded = load_model()
281
-
282
- if model_loaded:
283
- print("βœ… All systems ready!")
284
- demo = create_demo()
285
- demo.launch(
286
- share=True,
287
- server_name="0.0.0.0",
288
- server_port=7860
289
- )
290
- else:
291
- print("❌ Failed to load model. Creating fallback demo...")
292
-
293
- def fallback_demo():
294
- return "❌ Model loading failed. Please check the logs.", "❌ Model not available."
295
-
296
- demo = gr.Interface(
297
- fn=fallback_demo,
298
- inputs=[
299
- gr.Image(type="pil", label="Upload Medical Image"),
300
- gr.Textbox(label="Enter Medical Text", lines=5)
301
- ],
302
- outputs=[
303
- gr.Textbox(label="Status"),
304
- gr.Textbox(label="Error Message")
305
- ],
306
- title="❌ Medical OCR - Model Loading Failed"
307
- )
308
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  demo.launch(share=True)
 
1
+ import gradio as gr
2
+ import torch
3
+ import numpy as np
4
+ from PIL import Image
5
+ import time
6
+ import io
7
+ import subprocess
8
+ import sys
9
+
10
+ # Install required packages
11
+ def install_packages():
12
+ packages = [
13
+ "transformers",
14
+ "accelerate",
15
+ "timm",
16
+ "easyocr"
17
+ ]
18
+ for package in packages:
19
+ try:
20
+ subprocess.check_call([sys.executable, "-m", "pip", "install", package])
21
+ except:
22
+ print(f"Warning: Could not install {package}")
23
+
24
+ # Install packages at startup
25
+ install_packages()
26
+
27
+ from transformers import AutoProcessor, AutoModelForImageTextToText, AutoConfig
28
+
29
+ # Global variables for model
30
+ processor = None
31
+ model = None
32
+ config = None
33
+ ocr_reader = None
34
+
35
+ def load_model():
36
+ """Load the Gemma 3n model"""
37
+ global processor, model, config, ocr_reader
38
+
39
+ try:
40
+ print("πŸš€ Loading Gemma 3n model...")
41
+ GEMMA_PATH = "google/gemma-3n-e2b-it"
42
+
43
+ # Load configuration
44
+ config = AutoConfig.from_pretrained(GEMMA_PATH, trust_remote_code=True)
45
+ print("βœ… Config loaded")
46
+
47
+ # Load processor
48
+ processor = AutoProcessor.from_pretrained(GEMMA_PATH, trust_remote_code=True)
49
+ print("βœ… Processor loaded")
50
+
51
+ # Load model
52
+ model = AutoModelForImageTextToText.from_pretrained(
53
+ GEMMA_PATH,
54
+ config=config,
55
+ torch_dtype="auto",
56
+ device_map="auto",
57
+ trust_remote_code=True
58
+ )
59
+ print("βœ… Model loaded successfully!")
60
+
61
+ # Set up compilation fix
62
+ import torch._dynamo
63
+ torch._dynamo.config.suppress_errors = True
64
+
65
+ # Initialize OCR
66
+ try:
67
+ import easyocr
68
+ ocr_reader = easyocr.Reader(['en'], gpu=False, verbose=False)
69
+ print("βœ… EasyOCR initialized")
70
+ except Exception as e:
71
+ print(f"⚠️ EasyOCR not available: {e}")
72
+ ocr_reader = None
73
+
74
+ return True
75
+
76
+ except Exception as e:
77
+ print(f"❌ Model loading failed: {e}")
78
+ return False
79
+
80
+ def generate_soap_note(text):
81
+ """Generate SOAP note using Gemma 3n"""
82
+ if model is None or processor is None:
83
+ return "❌ Model not loaded. Please wait for initialization."
84
+
85
+ soap_prompt = f"""You are a medical AI assistant. Convert the following medical notes into a properly formatted SOAP note.
86
+
87
+ Medical notes:
88
+ {text}
89
+
90
+ Please format as:
91
+ S - SUBJECTIVE: (chief complaint, history of present illness, past medical history, medications, allergies)
92
+ O - OBJECTIVE: (vital signs, physical examination findings)
93
+ A - ASSESSMENT: (diagnosis/clinical impression)
94
+ P - PLAN: (treatment plan, follow-up instructions)
95
+
96
+ Generate a complete, professional SOAP note:"""
97
+
98
+ messages = [{
99
+ "role": "system",
100
+ "content": [{"type": "text", "text": "You are an expert medical AI assistant specialized in creating SOAP notes from medical documentation."}]
101
+ }, {
102
+ "role": "user",
103
+ "content": [{"type": "text", "text": soap_prompt}]
104
+ }]
105
+
106
+ try:
107
+ inputs = processor.apply_chat_template(
108
+ messages,
109
+ add_generation_prompt=True,
110
+ tokenize=True,
111
+ return_dict=True,
112
+ return_tensors="pt"
113
+ ).to(model.device)
114
+
115
+ input_len = inputs["input_ids"].shape[-1]
116
+
117
+ with torch.no_grad():
118
+ outputs = model.generate(
119
+ **inputs,
120
+ max_new_tokens=400,
121
+ do_sample=True,
122
+ temperature=0.1,
123
+ top_p=0.95,
124
+ pad_token_id=processor.tokenizer.eos_token_id,
125
+ disable_compile=True
126
+ )
127
+
128
+ response = processor.batch_decode(
129
+ outputs[:, input_len:],
130
+ skip_special_tokens=True
131
+ )[0].strip()
132
+
133
+ return response
134
+
135
+ except Exception as e:
136
+ return f"❌ SOAP generation failed: {str(e)}"
137
+
138
+ def extract_text_from_image(image):
139
+ """Extract text using EasyOCR"""
140
+ if ocr_reader is None:
141
+ return "❌ OCR not available"
142
+
143
+ try:
144
+ if hasattr(image, 'convert'):
145
+ image = image.convert('RGB')
146
+ img_array = np.array(image)
147
+
148
+ results = ocr_reader.readtext(img_array, detail=0, paragraph=True)
149
+ if results:
150
+ return ' '.join(results).strip()
151
+ else:
152
+ return "❌ No text detected in image"
153
+
154
+ except Exception as e:
155
+ return f"❌ OCR failed: {str(e)}"
156
+
157
+ def process_medical_input(image, text):
158
+ """Main processing function for the Gradio interface"""
159
+
160
+ if image is not None and text.strip():
161
+ return "⚠️ Please provide either an image OR text, not both.", ""
162
+
163
+ if image is not None:
164
+ # Process image
165
+ print("πŸ” Extracting text from image...")
166
+ extracted_text = extract_text_from_image(image)
167
+
168
+ if extracted_text.startswith('❌'):
169
+ return extracted_text, ""
170
+
171
+ print("πŸ€– Generating SOAP note...")
172
+ soap_note = generate_soap_note(extracted_text)
173
+
174
+ return extracted_text, soap_note
175
+
176
+ elif text.strip():
177
+ # Process text directly
178
+ print("πŸ€– Generating SOAP note from text...")
179
+ soap_note = generate_soap_note(text.strip())
180
+ return text.strip(), soap_note
181
+
182
+ else:
183
+ return "❌ Please provide either an image or text input.", ""
184
+
185
+ def create_demo():
186
+ """Create the Gradio demo interface"""
187
+
188
+ # Sample text for demonstration
189
+ sample_text = """Patient: John Smith, 45yo male
190
+ CC: Chest pain
191
+ Vitals: BP 140/90, HR 88, RR 16, O2 98%, Temp 98.6F
192
+ HPI: Patient reports crushing chest pain x 2 hours, radiating to left arm
193
+ PMH: HTN, DM Type 2
194
+ Current Meds: Lisinopril 10mg daily, Metformin 500mg BID
195
+ PE: Diaphoretic, anxious appearance
196
+ EKG: ST elevation in leads II, III, aVF"""
197
+
198
+ with gr.Blocks(title="Medical OCR SOAP Generator", theme=gr.themes.Soft()) as demo:
199
+
200
+
201
+ gr.HTML("""
202
+ <h1>πŸ₯ Medical OCR SOAP Generator - LIVE DEMO</h1>
203
+ <h2>🎯 For Competition Judges - Quick 2-Minute Demo:</h2>
204
+
205
+ <div style="background-color: #e6f3ff; padding: 15px; border-radius: 10px; margin: 10px 0;">
206
+ <h3>πŸ“‹ SAMPLE IMAGE PROVIDED:</h3>
207
+ <p><strong>πŸ‘† Download "docs-note-to-upload.jpg" from the Files tab above, then upload it below</strong></p>
208
+ <p><strong>OR</strong> click "Try Sample Medical Text" button for instant text demo</p>
209
+ </div>
210
+
211
+ <h3>Demo Steps:</h3>
212
+ <ol>
213
+ <li><strong>Upload the sample image</strong> (docs-note-to-upload.jpg from Files tab) <strong>OR</strong> click sample text button</li>
214
+ <li><strong>Click "Generate SOAP Note"</strong></li>
215
+ <li><strong>Wait ~2-3 minutes</strong> for AI processing (model loading + generation)</li>
216
+ <li><strong>See professional SOAP note</strong> generated by Gemma 3n</li>
217
+ </ol>
218
+
219
+ <h3>βœ… What This Demo Shows:</h3>
220
+ <ul>
221
+ <li><strong>Real OCR</strong> extraction from handwritten medical notes</li>
222
+ <li><strong>AI-powered medical reasoning</strong> with Gemma 3n</li>
223
+ <li><strong>Professional SOAP formatting</strong> (Subjective, Objective, Assessment, Plan)</li>
224
+ <li><strong>HIPAA-compliant</strong> local processing</li>
225
+ </ul>
226
+
227
+ <p><strong>⚠️ Note:</strong> First generation takes ~2-3 minutes as model loads. Subsequent ones are faster.</p>
228
+ <hr>
229
+ """)
230
+
231
+ with gr.Row():
232
+ with gr.Column():
233
+ image_input = gr.Image(
234
+ type="pil",
235
+ label="πŸ“· Upload Medical Image",
236
+ height=300
237
+ )
238
+
239
+ text_input = gr.Textbox(
240
+ label="πŸ“ Or Enter Medical Text",
241
+ placeholder=sample_text,
242
+ lines=8,
243
+ max_lines=15
244
+ )
245
+
246
+ submit_btn = gr.Button(
247
+ "Generate SOAP Note",
248
+ variant="primary",
249
+ size="lg"
250
+ )
251
+
252
+ with gr.Column():
253
+ extracted_output = gr.Textbox(
254
+ label="πŸ“‹ Extracted/Input Text",
255
+ lines=6,
256
+ max_lines=10
257
+ )
258
+
259
+ soap_output = gr.Textbox(
260
+ label="πŸ₯ Generated SOAP Note",
261
+ lines=12,
262
+ max_lines=20
263
+ )
264
+
265
+ # Example section
266
+ gr.Markdown("### πŸ“‹ Quick Test Example")
267
+ example_btn = gr.Button("Try Sample Medical Text", variant="secondary")
268
+
269
+ def load_example():
270
+ return sample_text, None
271
+
272
+ example_btn.click(
273
+ load_example,
274
+ outputs=[text_input, image_input]
275
+ )
276
+
277
+ # Process function
278
+ submit_btn.click(
279
+ process_medical_input,
280
+ inputs=[image_input, text_input],
281
+ outputs=[extracted_output, soap_output]
282
+ )
283
+
284
+ gr.Markdown("""
285
+ ---
286
+ **About:** This application uses Google's Gemma 3n model for medical text understanding and EasyOCR for handwriting recognition.
287
+ All processing is done locally for HIPAA compliance.
288
+
289
+ **Competition Entry:** Medical AI Innovation Challenge 2024
290
+ """)
291
+
292
+ return demo
293
+
294
+ # Initialize the application
295
+ if __name__ == "__main__":
296
+ print("πŸš€ Starting Medical OCR SOAP Generator...")
297
+
298
+ # Load model
299
+ model_loaded = load_model()
300
+
301
+ if model_loaded:
302
+ print("βœ… All systems ready!")
303
+ demo = create_demo()
304
+ demo.launch(
305
+ share=True,
306
+ server_name="0.0.0.0",
307
+ server_port=7860
308
+ )
309
+ else:
310
+ print("❌ Failed to load model. Creating fallback demo...")
311
+
312
+ def fallback_demo():
313
+ return "❌ Model loading failed. Please check the logs.", "❌ Model not available."
314
+
315
+ demo = gr.Interface(
316
+ fn=fallback_demo,
317
+ inputs=[
318
+ gr.Image(type="pil", label="Upload Medical Image"),
319
+ gr.Textbox(label="Enter Medical Text", lines=5)
320
+ ],
321
+ outputs=[
322
+ gr.Textbox(label="Status"),
323
+ gr.Textbox(label="Error Message")
324
+ ],
325
+ title="❌ Medical OCR - Model Loading Failed"
326
+ )
327
+
328
  demo.launch(share=True)