|
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": |
|
|
|
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"]: |
|
|
|
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 = {} |
|
|
|
|
|
for model_name, predictions in model_results.items(): |
|
for idx, prob in enumerate(predictions): |
|
if prob > 0.15: |
|
defect_type = self.get_defect_type(model_name, idx) |
|
measurements[defect_type] = self.defect_measurement.measure_defect(image, defect_type) |
|
|
|
|
|
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") |
|
|
|
|
|
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: |
|
|
|
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}%") |
|
|
|
|
|
if defect_type in result['measurements']: |
|
st.markdown("**Measurements:**") |
|
st.json(result['measurements'][defect_type]) |
|
|
|
|
|
if defect_type in result['recommendations']: |
|
with st.expander("📋 Detailed Analysis"): |
|
st.markdown(result['recommendations'][defect_type]) |
|
|
|
if __name__ == "__main__": |
|
main() |
|
|
|
|