File size: 11,057 Bytes
15526fe
dbd2162
 
 
 
 
 
9814e5f
dbd2162
 
 
 
 
 
 
 
 
 
 
15526fe
dbd2162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60dab82
dbd2162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15526fe
dbd2162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f207d0
dbd2162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15526fe
9814e5f
dbd2162
 
 
 
 
4f207d0
dbd2162
 
 
 
 
 
15526fe
dbd2162
 
15526fe
dbd2162
 
60dab82
dbd2162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9814e5f
 
dbd2162
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
import streamlit as st
from transformers import (
    AutoModelForImageClassification, 
    AutoImageProcessor,
    ViTForImageClassification,
    ResNetForImageClassification
)
import torch
import numpy as np
from PIL import Image, ImageDraw
import cv2
from langchain import FAISS
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.chains import RetrievalQA
from langchain.llms import HuggingFacePipeline
import json
import os
from concurrent.futures import ThreadPoolExecutor
import pandas as pd

class DefectMeasurement:
    """Handle defect measurements and severity estimation"""
    
    @staticmethod
    def measure_defect(image, defect_type):
        """Measure defect dimensions using computer vision"""
        img_array = np.array(image)
        gray = cv2.cvtColor(img_array, cv2.COLOR_RGB2GRAY)
        
        if defect_type == "Crack":
            # Crack width measurement
            blur = cv2.GaussianBlur(gray, (3,3), 0)
            edges = cv2.Canny(blur, 100, 200)
            lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50, minLineLength=100, maxLineGap=10)
            
            if lines is not None:
                max_length = 0
                for line in lines:
                    x1, y1, x2, y2 = line[0]
                    length = np.sqrt((x2-x1)**2 + (y2-y1)**2)
                    max_length = max(max_length, length)
                return {"length": max_length, "unit": "pixels"}
            
        elif defect_type in ["Spalling", "Exposed_Bars"]:
            # Area measurement
            thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]
            contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            
            if contours:
                max_area = max(cv2.contourArea(cnt) for cnt in contours)
                return {"area": max_area, "unit": "square pixels"}
        
        return None

class MultiModelAnalyzer:
    """Handle multiple pre-trained models for defect detection"""
    
    def __init__(self):
        self.models = {
            "CODEBRIM-ViT": "chanwooong/codebrim-vit-base",
            "Concrete-Defect-ResNet": "nlp-waseda/concrete-defect-resnet",
            "Bridge-Damage-ViT": "microsoft/bridge-damage-vit-base"
        }
        self.loaded_models = {}
        self.loaded_processors = {}
        
    @st.cache_resource
    def load_model(self, model_name):
        """Load specific model and processor"""
        try:
            if "vit" in model_name.lower():
                model = ViTForImageClassification.from_pretrained(self.models[model_name])
            else:
                model = ResNetForImageClassification.from_pretrained(self.models[model_name])
            processor = AutoImageProcessor.from_pretrained(self.models[model_name])
            return model, processor
        except Exception as e:
            st.error(f"Error loading {model_name}: {str(e)}")
            return None, None
            
    def analyze_with_all_models(self, image):
        """Run analysis with all available models"""
        results = {}
        for model_name in self.models.keys():
            if model_name not in self.loaded_models:
                self.loaded_models[model_name], self.loaded_processors[model_name] = self.load_model(model_name)
            
            if self.loaded_models[model_name] is not None:
                try:
                    inputs = self.loaded_processors[model_name](images=image, return_tensors="pt")
                    outputs = self.loaded_models[model_name](**inputs)
                    probs = torch.nn.functional.softmax(outputs.logits, dim=1)[0]
                    results[model_name] = probs
                except Exception as e:
                    st.error(f"Error analyzing with {model_name}: {str(e)}")
        
        return results

class EnhancedRAGSystem:
    """Enhanced RAG system with comprehensive construction knowledge"""
    
    def __init__(self):
        self.knowledge_sources = {
            "ACI_318": "concrete_design_requirements.json",
            "ASTM": "testing_standards.json",
            "repair_guidelines": "repair_methods.json",
            "case_studies": "defect_cases.json"
        }
        self.embeddings = None
        self.vectorstore = None
        self.qa_chain = None
        
    def load_knowledge_base(self):
        """Load and combine multiple knowledge sources"""
        combined_knowledge = []
        for source, filename in self.knowledge_sources.items():
            try:
                with open(f"knowledge_base/{filename}", 'r') as f:
                    knowledge = json.load(f)
                    for item in knowledge:
                        item['source'] = source
                    combined_knowledge.extend(knowledge)
            except Exception as e:
                st.warning(f"Could not load {source}: {str(e)}")
        
        return combined_knowledge
        
    def init_rag(self):
        """Initialize enhanced RAG system"""
        try:
            self.embeddings = HuggingFaceEmbeddings(
                model_name="sentence-transformers/all-mpnet-base-v2"
            )
            
            knowledge_base = self.load_knowledge_base()
            texts = [
                f"{item['defect_type']} ({item['source']})\n" +
                f"Description: {item['description']}\n" +
                f"Repair: {item['repair_methods']}\n" +
                f"Standards: {item['applicable_standards']}\n" +
                f"Cases: {item['related_cases']}"
                for item in knowledge_base
            ]
            
            self.vectorstore = FAISS.from_texts(texts, self.embeddings)
            
            self.qa_chain = RetrievalQA.from_chain_type(
                llm=HuggingFacePipeline.from_model_id(
                    model_id="google/flan-t5-large",
                    task="text2text-generation",
                    model_kwargs={"temperature": 0.7}
                ),
                chain_type="stuff",
                retriever=self.vectorstore.as_retriever(
                    search_kwargs={"k": 5}
                )
            )
            
            return True
        except Exception as e:
            st.error(f"Error initializing RAG system: {str(e)}")
            return False

class ConstructionDefectAnalyzer:
    """Main application class"""
    
    def __init__(self):
        self.multi_model = MultiModelAnalyzer()
        self.rag_system = EnhancedRAGSystem()
        self.defect_measurement = DefectMeasurement()
        
    def analyze_multiple_images(self, images):
        """Analyze multiple images in parallel"""
        results = []
        with ThreadPoolExecutor() as executor:
            futures = []
            for img in images:
                future = executor.submit(self.analyze_single_image, img)
                futures.append(future)
            
            for future in futures:
                result = future.result()
                results.append(result)
        
        return results
    
    def analyze_single_image(self, image):
        """Analyze a single image with all features"""
        model_results = self.multi_model.analyze_with_all_models(image)
        measurements = {}
        recommendations = {}
        
        # Get measurements for detected defects
        for model_name, predictions in model_results.items():
            for idx, prob in enumerate(predictions):
                if prob > 0.15:  # Confidence threshold
                    defect_type = self.get_defect_type(model_name, idx)
                    measurements[defect_type] = self.defect_measurement.measure_defect(image, defect_type)
                    
                    # Get RAG recommendations
                    if self.rag_system.qa_chain:
                        query = self.generate_rag_query(defect_type, measurements.get(defect_type))
                        recommendations[defect_type] = self.rag_system.qa_chain.run(query)
        
        return {
            "model_results": model_results,
            "measurements": measurements,
            "recommendations": recommendations
        }
    
    @staticmethod
    def generate_rag_query(defect_type, measurement):
        """Generate detailed query for RAG system"""
        query = f"What are the recommended repairs, safety measures, and applicable standards for {defect_type}"
        if measurement:
            if "length" in measurement:
                query += f" with length {measurement['length']} {measurement['unit']}"
            elif "area" in measurement:
                query += f" with affected area {measurement['area']} {measurement['unit']}"
        return query + "?"

def main():
    st.set_page_config(page_title="Advanced Construction Defect Analyzer", layout="wide")
    
    analyzer = ConstructionDefectAnalyzer()
    
    st.title("🏗️ Advanced Construction Defect Analyzer")
    
    # Multiple image upload
    uploaded_files = st.file_uploader(
        "Upload construction images for analysis",
        type=['jpg', 'jpeg', 'png'],
        accept_multiple_files=True
    )
    
    if uploaded_files:
        images = [Image.open(file).convert('RGB') for file in uploaded_files]
        
        with st.spinner("Analyzing images..."):
            results = analyzer.analyze_multiple_images(images)
            
            for idx, (image, result) in enumerate(zip(images, results)):
                st.markdown(f"### Analysis Results - Image {idx + 1}")
                
                col1, col2 = st.columns([1, 2])
                
                with col1:
                    st.image(image, caption=f"Image {idx + 1}", use_column_width=True)
                
                with col2:
                    # Display model comparison
                    st.markdown("#### Model Predictions")
                    for model_name, predictions in result['model_results'].items():
                        st.markdown(f"**{model_name}:**")
                        for i, prob in enumerate(predictions):
                            if prob > 0.15:
                                defect_type = analyzer.get_defect_type(model_name, i)
                                st.progress(float(prob))
                                st.markdown(f"{defect_type}: {float(prob)*100:.1f}%")
                                
                                # Display measurements
                                if defect_type in result['measurements']:
                                    st.markdown("**Measurements:**")
                                    st.json(result['measurements'][defect_type])
                                
                                # Display recommendations
                                if defect_type in result['recommendations']:
                                    with st.expander("📋 Detailed Analysis"):
                                        st.markdown(result['recommendations'][defect_type])

if __name__ == "__main__":
    main()