Shakir60 commited on
Commit
5ec4050
Β·
verified Β·
1 Parent(s): d774ee6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +165 -69
app.py CHANGED
@@ -1,13 +1,39 @@
 
1
  import streamlit as st
2
  from transformers import ViTForImageClassification, ViTImageProcessor
3
  from PIL import Image
4
  import torch
5
  import time
6
  import gc
 
 
 
 
 
 
 
 
 
 
7
 
8
  # Constants
9
  MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB
10
  MAX_IMAGE_SIZE = 1024 # Maximum dimension for images
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  def cleanup_memory():
13
  """Clean up memory and GPU cache"""
@@ -22,7 +48,7 @@ def init_session_state():
22
  if 'dark_mode' not in st.session_state:
23
  st.session_state.dark_mode = False
24
 
25
- @st.cache_resource(show_spinner="Loading AI model...")
26
  def load_model():
27
  """Load and cache the model and processor"""
28
  try:
@@ -35,43 +61,71 @@ def load_model():
35
  ignore_mismatched_sizes=True,
36
  ).to(device)
37
  model.eval()
 
38
  return model, processor
39
  except Exception as e:
40
- st.error(f"Error loading model: {str(e)}")
41
  return None, None
42
 
43
- def validate_image(image):
44
- """Validate image size and format"""
45
- if image.size[0] * image.size[1] > 1024 * 1024:
46
- st.warning("Large image detected. The image will be resized for better performance.")
47
- if image.format not in ['JPEG', 'PNG']:
48
- st.warning("Image format not optimal. Consider using JPEG or PNG for better performance.")
49
-
50
- def preprocess_image(uploaded_file):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  """Preprocess and validate uploaded image"""
52
- try:
53
- image = Image.open(uploaded_file)
54
- if max(image.size) > MAX_IMAGE_SIZE:
55
- ratio = MAX_IMAGE_SIZE / max(image.size)
56
- new_size = tuple([int(dim * ratio) for dim in image.size])
57
- image = image.resize(new_size, Image.Resampling.LANCZOS)
58
- return image
59
- except Exception as e:
60
- st.error(f"Error processing image: {str(e)}")
61
- return None
62
-
63
- def analyze_damage(image, model, processor):
64
  """Analyze structural damage in the image"""
 
 
 
65
  try:
66
  device = next(model.parameters()).device
67
- with torch.no_grad():
68
- image = image.convert('RGB')
69
- inputs = processor(images=image, return_tensors="pt")
70
- inputs = {k: v.to(device) for k, v in inputs.items()}
71
- outputs = model(**inputs)
72
- probs = torch.nn.functional.softmax(outputs.logits, dim=1)[0]
73
- cleanup_memory()
74
- return probs.cpu()
 
 
 
 
 
 
 
 
 
 
 
 
75
  except RuntimeError as e:
76
  if "out of memory" in str(e):
77
  cleanup_memory()
@@ -80,78 +134,120 @@ def analyze_damage(image, model, processor):
80
  st.error(f"Error analyzing image: {str(e)}")
81
  return None
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  def main():
 
84
  st.set_page_config(
85
  page_title="Structural Damage Analyzer Pro",
86
  page_icon="πŸ—οΈ",
87
  layout="wide",
88
  initial_sidebar_state="expanded"
89
  )
90
-
91
- # Initialize session state
92
  init_session_state()
93
-
94
- # Display header
95
- st.markdown(
96
- """
97
  <div style='text-align: center; padding: 1rem;'>
98
  <h1>πŸ—οΈ Structural Damage Analyzer Pro</h1>
99
  <p style='font-size: 1.2rem;'>Advanced AI-powered structural damage assessment tool</p>
100
  </div>
101
- """,
102
- unsafe_allow_html=True
103
- )
104
-
105
- # Load model
106
- model, processor = load_model()
107
- if model is None:
108
- st.error("Failed to load model. Please refresh the page.")
109
- return
110
-
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  # File upload
112
  uploaded_file = st.file_uploader(
113
  "Upload an image for analysis",
114
  type=['jpg', 'jpeg', 'png'],
115
  help="Supported formats: JPG, JPEG, PNG"
116
  )
117
-
118
- if uploaded_file:
119
  try:
120
- if uploaded_file.size > MAX_FILE_SIZE:
121
- st.error("File size too large. Please upload an image smaller than 5MB.")
122
- return
123
-
124
  image = preprocess_image(uploaded_file)
125
  if image is None:
126
  return
127
-
128
- validate_image(image)
129
 
130
- # Display image and analyze
131
- st.image(image, caption="Uploaded Structure", use_column_width=True)
 
 
132
 
133
- with st.spinner("πŸ” Analyzing damage..."):
134
- predictions = analyze_damage(image, model, processor)
 
 
135
  if predictions is not None:
136
- st.success("Analysis complete!")
137
- # Add analysis display logic here based on your DAMAGE_TYPES
138
-
 
139
  except Exception as e:
 
140
  cleanup_memory()
141
- st.error(f"Error processing image: {str(e)}")
142
- st.info("Please try uploading a different image.")
143
-
144
  # Footer
145
  st.markdown("---")
146
- st.markdown(
147
- """
148
  <div style='text-align: center'>
149
  <p>πŸ—οΈ Structural Damage Analyzer Pro | Built with Streamlit & Transformers</p>
150
  <p style='font-size: 0.8rem;'>For professional use only. Always consult with a structural engineer.</p>
151
  </div>
152
- """,
153
- unsafe_allow_html=True
154
- )
155
 
156
  if __name__ == "__main__":
157
  main()
 
1
+ # app.py
2
  import streamlit as st
3
  from transformers import ViTForImageClassification, ViTImageProcessor
4
  from PIL import Image
5
  import torch
6
  import time
7
  import gc
8
+ import logging
9
+ from knowledge_base import KNOWLEDGE_BASE, DAMAGE_TYPES
10
+ from rag_utils import RAGSystem
11
+ import structlog
12
+ from typing import Optional, Dict, Any
13
+ from functools import lru_cache
14
+
15
+ # Configure logging
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = structlog.get_logger()
18
 
19
  # Constants
20
  MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB
21
  MAX_IMAGE_SIZE = 1024 # Maximum dimension for images
22
+ MODEL = None
23
+ PROCESSOR = None
24
+ RAG_SYSTEM = None
25
+
26
+ def handle_exceptions(func):
27
+ """Decorator for exception handling"""
28
+ def wrapper(*args, **kwargs):
29
+ try:
30
+ return func(*args, **kwargs)
31
+ except Exception as e:
32
+ cleanup_memory()
33
+ st.error(f"Error in {func.__name__}: {str(e)}")
34
+ logger.error(f"Error in {func.__name__}: {str(e)}", exc_info=True)
35
+ return None
36
+ return wrapper
37
 
38
  def cleanup_memory():
39
  """Clean up memory and GPU cache"""
 
48
  if 'dark_mode' not in st.session_state:
49
  st.session_state.dark_mode = False
50
 
51
+ @st.cache_resource(show_spinner="Loading AI model...", ttl=3600*24)
52
  def load_model():
53
  """Load and cache the model and processor"""
54
  try:
 
61
  ignore_mismatched_sizes=True,
62
  ).to(device)
63
  model.eval()
64
+ logger.info("Model loaded successfully", device=device)
65
  return model, processor
66
  except Exception as e:
67
+ logger.error("Error loading model", error=str(e))
68
  return None, None
69
 
70
+ def validate_upload(file) -> bool:
71
+ """Validate uploaded file for security"""
72
+ if not file:
73
+ return False
74
+
75
+ allowed_extensions = {'jpg', 'jpeg', 'png'}
76
+ if not file.name.lower().endswith(tuple(allowed_extensions)):
77
+ st.error("Invalid file type. Please upload a JPG or PNG image.")
78
+ return False
79
+
80
+ if file.size > MAX_FILE_SIZE:
81
+ st.error("File too large. Maximum size is 5MB.")
82
+ return False
83
+
84
+ if file.type not in ['image/jpeg', 'image/png']:
85
+ st.error("Invalid file content type.")
86
+ return False
87
+
88
+ return True
89
+
90
+ @handle_exceptions
91
+ def preprocess_image(uploaded_file) -> Optional[Image.Image]:
92
  """Preprocess and validate uploaded image"""
93
+ image = Image.open(uploaded_file)
94
+ if max(image.size) > MAX_IMAGE_SIZE:
95
+ ratio = MAX_IMAGE_SIZE / max(image.size)
96
+ new_size = tuple([int(dim * ratio) for dim in image.size])
97
+ image = image.resize(new_size, Image.Resampling.LANCZOS)
98
+ return image
99
+
100
+ @handle_exceptions
101
+ def analyze_damage(image: Image.Image, model: ViTForImageClassification,
102
+ processor: ViTImageProcessor) -> Optional[torch.Tensor]:
 
 
103
  """Analyze structural damage in the image"""
104
+ progress_bar = st.progress(0)
105
+ stages = ['Preprocessing', 'Analysis', 'Results Generation']
106
+
107
  try:
108
  device = next(model.parameters()).device
109
+
110
+ for i, stage in enumerate(stages):
111
+ progress_bar.progress((i + 1) / len(stages))
112
+ st.write(f"Stage {i+1}/{len(stages)}: {stage}")
113
+
114
+ if i == 0: # Preprocessing
115
+ image = image.convert('RGB')
116
+ inputs = processor(images=image, return_tensors="pt")
117
+ inputs = {k: v.to(device) for k, v in inputs.items()}
118
+
119
+ elif i == 1: # Analysis
120
+ with torch.no_grad():
121
+ outputs = model(**inputs)
122
+ probs = torch.nn.functional.softmax(outputs.logits, dim=1)[0]
123
+
124
+ elif i == 2: # Results Generation
125
+ result = probs.cpu()
126
+
127
+ return result
128
+
129
  except RuntimeError as e:
130
  if "out of memory" in str(e):
131
  cleanup_memory()
 
134
  st.error(f"Error analyzing image: {str(e)}")
135
  return None
136
 
137
+ def display_analysis_results(predictions: torch.Tensor, analysis_time: float):
138
+ """Display analysis results with damage details"""
139
+ st.markdown("### πŸ“Š Analysis Results")
140
+ st.markdown(f"*Analysis completed in {analysis_time:.2f} seconds*")
141
+
142
+ detected = False
143
+ for idx, prob in enumerate(predictions):
144
+ confidence = float(prob) * 100
145
+ if confidence > 15:
146
+ detected = True
147
+ damage_type = DAMAGE_TYPES[idx]['name']
148
+
149
+ with st.expander(f"{damage_type.replace('_', ' ').title()} - {confidence:.1f}%", expanded=True):
150
+ st.progress(confidence / 100)
151
+
152
+ # Get enhanced analysis from RAG system
153
+ analysis = RAG_SYSTEM.get_enhanced_analysis(damage_type, confidence)
154
+
155
+ tabs = st.tabs(["πŸ“‹ Details", "πŸ”§ Repairs", "⚠️ Safety"])
156
+
157
+ with tabs[0]:
158
+ for detail in analysis['technical_details']:
159
+ st.markdown(detail)
160
+
161
+ with tabs[1]:
162
+ for rec in analysis['expert_recommendations']:
163
+ st.markdown(rec)
164
+
165
+ with tabs[2]:
166
+ for safety in analysis['safety_considerations']:
167
+ st.warning(safety)
168
+
169
+ if not detected:
170
+ st.info("No significant structural damage detected. Regular maintenance recommended.")
171
+
172
  def main():
173
+ """Main application function"""
174
  st.set_page_config(
175
  page_title="Structural Damage Analyzer Pro",
176
  page_icon="πŸ—οΈ",
177
  layout="wide",
178
  initial_sidebar_state="expanded"
179
  )
180
+
 
181
  init_session_state()
182
+
183
+ st.markdown("""
 
 
184
  <div style='text-align: center; padding: 1rem;'>
185
  <h1>πŸ—οΈ Structural Damage Analyzer Pro</h1>
186
  <p style='font-size: 1.2rem;'>Advanced AI-powered structural damage assessment tool</p>
187
  </div>
188
+ """, unsafe_allow_html=True)
189
+
190
+ # Sidebar
191
+ with st.sidebar:
192
+ st.markdown("### βš™οΈ Settings")
193
+ st.session_state.dark_mode = st.toggle("Dark Mode", st.session_state.dark_mode)
194
+ st.markdown("### πŸ“– Analysis History")
195
+ if st.session_state.history:
196
+ for item in st.session_state.history[-5:]:
197
+ st.markdown(f"- {item}")
198
+
199
+ # Load model and initialize RAG system
200
+ global MODEL, PROCESSOR, RAG_SYSTEM
201
+ if MODEL is None or PROCESSOR is None:
202
+ MODEL, PROCESSOR = load_model()
203
+ if MODEL is None:
204
+ st.error("Failed to load model. Please refresh the page.")
205
+ return
206
+
207
+ if RAG_SYSTEM is None:
208
+ RAG_SYSTEM = RAGSystem()
209
+ RAG_SYSTEM.initialize_knowledge_base(KNOWLEDGE_BASE)
210
+
211
  # File upload
212
  uploaded_file = st.file_uploader(
213
  "Upload an image for analysis",
214
  type=['jpg', 'jpeg', 'png'],
215
  help="Supported formats: JPG, JPEG, PNG"
216
  )
217
+
218
+ if uploaded_file and validate_upload(uploaded_file):
219
  try:
 
 
 
 
220
  image = preprocess_image(uploaded_file)
221
  if image is None:
222
  return
 
 
223
 
224
+ col1, col2 = st.columns([1, 1])
225
+
226
+ with col1:
227
+ st.image(image, caption="Uploaded Structure", use_column_width=True)
228
 
229
+ with col2:
230
+ start_time = time.time()
231
+ predictions = analyze_damage(image, MODEL, PROCESSOR)
232
+
233
  if predictions is not None:
234
+ analysis_time = time.time() - start_time
235
+ display_analysis_results(predictions, analysis_time)
236
+ st.session_state.history.append(f"Analyzed {uploaded_file.name}")
237
+
238
  except Exception as e:
239
+ logger.error("Error in main processing loop", error=str(e))
240
  cleanup_memory()
241
+ st.error("An error occurred during processing. Please try again.")
242
+
 
243
  # Footer
244
  st.markdown("---")
245
+ st.markdown("""
 
246
  <div style='text-align: center'>
247
  <p>πŸ—οΈ Structural Damage Analyzer Pro | Built with Streamlit & Transformers</p>
248
  <p style='font-size: 0.8rem;'>For professional use only. Always consult with a structural engineer.</p>
249
  </div>
250
+ """, unsafe_allow_html=True)
 
 
251
 
252
  if __name__ == "__main__":
253
  main()