|
import streamlit as st |
|
import torch |
|
from transformers import ViTForImageClassification, ViTImageProcessor, pipeline |
|
from PIL import Image |
|
import pandas as pd |
|
from langchain_community.embeddings import HuggingFaceEmbeddings |
|
from langchain_community.llms import HuggingFaceHub |
|
from langchain_community.vectorstores import FAISS |
|
from langchain.text_splitter import RecursiveCharacterTextSplitter |
|
from langchain.docstore.document import Document |
|
from langchain.prompts import PromptTemplate |
|
from langchain.chains import RetrievalQA |
|
import os |
|
|
|
|
|
st.set_page_config( |
|
page_title="Building Damage Analysis", |
|
page_icon="ποΈ", |
|
layout="wide" |
|
) |
|
|
|
|
|
@st.cache_resource |
|
def load_models(): |
|
|
|
damage_model = ViTForImageClassification.from_pretrained("microsoft/vit-base-patch16-224") |
|
processor = ViTImageProcessor.from_pretrained("microsoft/vit-base-patch16-224") |
|
|
|
|
|
llm = HuggingFaceHub( |
|
repo_id="google/flan-t5-large", |
|
model_kwargs={"temperature": 0.7, "max_length": 512} |
|
) |
|
|
|
embeddings = HuggingFaceEmbeddings( |
|
model_name='sentence-transformers/all-MiniLM-L6-v2' |
|
) |
|
|
|
return damage_model, processor, embeddings, llm |
|
|
|
|
|
SAMPLE_DATA = [ |
|
{ |
|
"repair_description": "Major wall crack requiring structural repair. Steel plate reinforcement needed.", |
|
"repair_cost": 5000, |
|
"damage_type": "Wall Crack" |
|
}, |
|
{ |
|
"repair_description": "Concrete beam damage with exposed rebar. Requires immediate attention.", |
|
"repair_cost": 7500, |
|
"damage_type": "Beam Damage" |
|
}, |
|
{ |
|
"repair_description": "Foundation settling causing structural issues. Need underpinning.", |
|
"repair_cost": 15000, |
|
"damage_type": "Foundation Issue" |
|
} |
|
] |
|
|
|
def setup_rag(embeddings, llm): |
|
|
|
documents = [ |
|
Document( |
|
page_content=f"{item['repair_description']} Cost: ${item['repair_cost']}", |
|
metadata={'cost': item['repair_cost'], 'damage_type': item['damage_type']} |
|
) |
|
for item in SAMPLE_DATA |
|
] |
|
|
|
|
|
vectorstore = FAISS.from_documents(documents, embeddings) |
|
|
|
|
|
template = """ |
|
Analyze building damage and provide repair recommendations based on this context: |
|
{context} |
|
|
|
For damage type: {question} |
|
|
|
Provide: |
|
1. Damage assessment |
|
2. Repair steps |
|
3. Safety considerations |
|
4. Estimated cost range |
|
""" |
|
|
|
prompt = PromptTemplate(template=template, input_variables=["context", "question"]) |
|
|
|
|
|
qa_chain = RetrievalQA.from_chain_type( |
|
llm=llm, |
|
chain_type="stuff", |
|
retriever=vectorstore.as_retriever(search_kwargs={'k': 2}), |
|
chain_type_kwargs={"prompt": prompt} |
|
) |
|
|
|
return qa_chain |
|
|
|
def process_image(image, model, processor): |
|
inputs = processor(images=image, return_tensors="pt") |
|
outputs = model(**inputs) |
|
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1) |
|
return predictions[0].tolist() |
|
|
|
def main(): |
|
st.title("ποΈ Building Damage Detection & Analysis") |
|
st.markdown(""" |
|
Upload a photo of building damage for AI analysis and repair recommendations. |
|
""") |
|
|
|
|
|
if 'models_loaded' not in st.session_state: |
|
with st.spinner('Loading AI models...'): |
|
damage_model, processor, embeddings, llm = load_models() |
|
qa_chain = setup_rag(embeddings, llm) |
|
st.session_state['models_loaded'] = True |
|
st.session_state['models'] = (damage_model, processor, qa_chain) |
|
|
|
damage_model, processor, qa_chain = st.session_state['models'] |
|
|
|
|
|
uploaded_file = st.file_uploader("Upload building damage photo", type=["jpg", "jpeg", "png"]) |
|
|
|
if uploaded_file: |
|
|
|
image = Image.open(uploaded_file) |
|
st.image(image, caption="Uploaded Image", use_column_width=True) |
|
|
|
with st.spinner('Analyzing damage...'): |
|
|
|
predictions = process_image(image, damage_model, processor) |
|
damage_types = ["Wall Crack", "Beam Damage", "Foundation Issue", |
|
"Roof Damage", "Structural Damage"] |
|
|
|
|
|
st.subheader("Detected Damage Types") |
|
for damage_type, prob in zip(damage_types, predictions): |
|
if prob > 0.2: |
|
st.metric(damage_type, f"{prob:.1%}") |
|
|
|
with st.spinner(f'Generating analysis for {damage_type}...'): |
|
analysis = qa_chain.invoke(damage_type) |
|
st.markdown(f"### Analysis for {damage_type}") |
|
st.markdown(analysis['result']) |
|
|
|
if __name__ == "__main__": |
|
main() |