Spaces:
Sleeping
Sleeping
Delete Thermal_Image_Detection.py
Browse files- Thermal_Image_Detection.py +0 -253
Thermal_Image_Detection.py
DELETED
|
@@ -1,253 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import cv2
|
| 3 |
-
import numpy as np
|
| 4 |
-
from pathlib import Path
|
| 5 |
-
import tempfile
|
| 6 |
-
import os
|
| 7 |
-
import plotly.express as px
|
| 8 |
-
import pytesseract
|
| 9 |
-
import re
|
| 10 |
-
from ultralytics import YOLO
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
# Colors for labels
|
| 14 |
-
LABEL_COLORS = {
|
| 15 |
-
"Circuit": (37, 255, 76), # Green
|
| 16 |
-
"Fuse": (255, 76, 76), # Red
|
| 17 |
-
"Missing Fuse": (255, 204, 0), # Yellow
|
| 18 |
-
"Missing Circuit": (0, 128, 255) # Blue
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
def test_trained_model(model_path, test_image_path, conf_threshold=0.5):
|
| 22 |
-
model = YOLO(model_path)
|
| 23 |
-
test_image_path = Path(test_image_path)
|
| 24 |
-
|
| 25 |
-
if not test_image_path.exists():
|
| 26 |
-
st.error(f"No image found at {test_image_path}")
|
| 27 |
-
return None
|
| 28 |
-
|
| 29 |
-
results = model(str(test_image_path), conf=conf_threshold, verbose=False)
|
| 30 |
-
result = results[0]
|
| 31 |
-
image = cv2.imread(str(test_image_path))
|
| 32 |
-
|
| 33 |
-
if result.boxes is not None:
|
| 34 |
-
boxes = result.boxes.xyxy.cpu().numpy()
|
| 35 |
-
classes = result.boxes.cls.cpu().numpy().astype(int)
|
| 36 |
-
|
| 37 |
-
for box, cls_id in zip(boxes, classes):
|
| 38 |
-
x1, y1, x2, y2 = map(int, box)
|
| 39 |
-
label = model.names[cls_id]
|
| 40 |
-
color = LABEL_COLORS.get(label, (255, 255, 255)) # fallback white
|
| 41 |
-
cv2.rectangle(image, (x1, y1), (x2, y2), color, 2)
|
| 42 |
-
cv2.putText(image, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
|
| 43 |
-
|
| 44 |
-
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
| 45 |
-
st.image(image_rgb, caption=f"{test_image_path.name} - Detection with Labels", use_column_width=True)
|
| 46 |
-
|
| 47 |
-
# Legend
|
| 48 |
-
st.markdown("### 🏷 Legend (Bounding Box Colors)")
|
| 49 |
-
for label, color in LABEL_COLORS.items():
|
| 50 |
-
r, g, b = color
|
| 51 |
-
hex_color = f'#{r:02x}{g:02x}{b:02x}'
|
| 52 |
-
st.markdown(f"<span style='color:{hex_color}; font-weight:bold;'>■</span> {label}", unsafe_allow_html=True)
|
| 53 |
-
|
| 54 |
-
detections = [model.names[int(cls)] for cls in result.boxes.cls]
|
| 55 |
-
return detections
|
| 56 |
-
|
| 57 |
-
def count_circuit_components(detections):
|
| 58 |
-
target_labels = ['Circuit', 'Fuse', 'Missing Fuse', 'Missing Circuit']
|
| 59 |
-
component_counts = {}
|
| 60 |
-
|
| 61 |
-
for label in target_labels:
|
| 62 |
-
count = sum(1 for detection in detections if detection.lower().replace('_', ' ') == label.lower())
|
| 63 |
-
component_counts[label] = count
|
| 64 |
-
|
| 65 |
-
return component_counts
|
| 66 |
-
|
| 67 |
-
def display_component_summary(component_counts, image_name):
|
| 68 |
-
st.write(f"\n{'=' * 50}")
|
| 69 |
-
st.write(f"COMPONENT SUMMARY FOR: {image_name}")
|
| 70 |
-
st.write(f"{'=' * 50}")
|
| 71 |
-
|
| 72 |
-
for component, count in component_counts.items():
|
| 73 |
-
st.write(f"{component:15}: {count:3d}")
|
| 74 |
-
|
| 75 |
-
st.write(f"{'-' * 25}")
|
| 76 |
-
|
| 77 |
-
def extract_temperatures_from_image(image_path):
|
| 78 |
-
st.subheader("📋 Temperature Report")
|
| 79 |
-
|
| 80 |
-
image = cv2.imread(image_path)
|
| 81 |
-
if image is None:
|
| 82 |
-
st.error("Error loading image for OCR.")
|
| 83 |
-
return None
|
| 84 |
-
|
| 85 |
-
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
| 86 |
-
lower_white = np.array([0, 0, 200])
|
| 87 |
-
upper_white = np.array([180, 50, 255])
|
| 88 |
-
white_mask = cv2.inRange(hsv_image, lower_white, upper_white)
|
| 89 |
-
white_text_image = cv2.bitwise_and(image, image, mask=white_mask)
|
| 90 |
-
white_text_gray = cv2.cvtColor(white_text_image, cv2.COLOR_BGR2GRAY)
|
| 91 |
-
|
| 92 |
-
ocr_text = pytesseract.image_to_string(white_text_gray)
|
| 93 |
-
ocr_text = re.sub(r'(\d+)\s(\d)°C', r'\1.\2°C', ocr_text)
|
| 94 |
-
|
| 95 |
-
temps_found = re.findall(r'(\d{1,3}\.\d)\s*°C', ocr_text)
|
| 96 |
-
temps_found = sorted([float(t) for t in temps_found])
|
| 97 |
-
|
| 98 |
-
if len(temps_found) == 0:
|
| 99 |
-
st.warning("No temperature values detected.")
|
| 100 |
-
return None
|
| 101 |
-
|
| 102 |
-
highest_temp = max(temps_found)
|
| 103 |
-
lowest_temp = min(temps_found)
|
| 104 |
-
|
| 105 |
-
if len(temps_found) >= 3:
|
| 106 |
-
ambient_temp = temps_found[1]
|
| 107 |
-
elif len(temps_found) == 2:
|
| 108 |
-
ambient_temp = sum(temps_found) / 2
|
| 109 |
-
else:
|
| 110 |
-
ambient_temp = temps_found[0]
|
| 111 |
-
|
| 112 |
-
st.write("Detected Temperatures:")
|
| 113 |
-
st.write(f"- Highest Temp: {highest_temp:.2f} °C")
|
| 114 |
-
st.write(f"- Lowest Temp: {lowest_temp:.2f} °C")
|
| 115 |
-
st.write(f"- Ambient Temp: {ambient_temp:.2f} °C")
|
| 116 |
-
|
| 117 |
-
return {
|
| 118 |
-
"highest": highest_temp,
|
| 119 |
-
"lowest": lowest_temp,
|
| 120 |
-
"ambient": ambient_temp,
|
| 121 |
-
}
|
| 122 |
-
|
| 123 |
-
def plot_hover_temperature(image_path, temp_min=0.0, temp_max=100.0):
|
| 124 |
-
img = cv2.imread(image_path)
|
| 125 |
-
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
| 126 |
-
red_channel = img_rgb[:, :, 0]
|
| 127 |
-
|
| 128 |
-
def map_temp(val):
|
| 129 |
-
return round((val / 255.0) * (temp_max - temp_min) + temp_min, 2)
|
| 130 |
-
|
| 131 |
-
temp_map = np.vectorize(map_temp)(red_channel)
|
| 132 |
-
|
| 133 |
-
fig = px.imshow(
|
| 134 |
-
temp_map,
|
| 135 |
-
color_continuous_scale="Hot",
|
| 136 |
-
labels={'x': 'X', 'y': 'Y', 'color': 'Temperature (°C)'},
|
| 137 |
-
title=f"Temperature Map (Hover) - Range: {temp_min}°C to {temp_max}°C"
|
| 138 |
-
)
|
| 139 |
-
|
| 140 |
-
fig.update_layout(dragmode=False, margin=dict(l=20, r=20, t=40, b=20), height=600)
|
| 141 |
-
fig.update_traces(hovertemplate="X: %{x}<br>Y: %{y}<br>Temp: %{z:.2f} °C")
|
| 142 |
-
st.plotly_chart(fig, use_container_width=True)
|
| 143 |
-
|
| 144 |
-
def analyze_thermal_circuit_image(model_path, test_image_path, conf_threshold=0.5):
|
| 145 |
-
st.write("Starting thermal circuit analysis...")
|
| 146 |
-
detections = test_trained_model(model_path, test_image_path, conf_threshold)
|
| 147 |
-
|
| 148 |
-
if detections is None:
|
| 149 |
-
return
|
| 150 |
-
|
| 151 |
-
component_counts = count_circuit_components(detections)
|
| 152 |
-
image_name = Path(test_image_path).name
|
| 153 |
-
display_component_summary(component_counts, image_name)
|
| 154 |
-
|
| 155 |
-
return component_counts
|
| 156 |
-
|
| 157 |
-
def extract_ambient_temperature(image_path):
|
| 158 |
-
image = cv2.imread(image_path)
|
| 159 |
-
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
| 160 |
-
white_mask = cv2.inRange(hsv_image, np.array([0, 0, 200]), np.array([180, 50, 255]))
|
| 161 |
-
text_image = cv2.bitwise_and(image, image, mask=white_mask)
|
| 162 |
-
gray = cv2.cvtColor(text_image, cv2.COLOR_BGR2GRAY)
|
| 163 |
-
text = pytesseract.image_to_string(gray)
|
| 164 |
-
temps = re.findall(r'(\d{1,3}\.\d)\s*°C', text)
|
| 165 |
-
|
| 166 |
-
if not temps:
|
| 167 |
-
return None
|
| 168 |
-
|
| 169 |
-
temps = sorted([float(t) for t in temps])
|
| 170 |
-
return temps[1] if len(temps) >= 3 else sum(temps) / len(temps)
|
| 171 |
-
|
| 172 |
-
def detect_circuit_boxes(model_path, image_path, conf_threshold=0.5):
|
| 173 |
-
model = YOLO(model_path)
|
| 174 |
-
results = model(image_path, conf=conf_threshold)[0]
|
| 175 |
-
boxes = []
|
| 176 |
-
|
| 177 |
-
if results.boxes is not None:
|
| 178 |
-
for i, cls in enumerate(results.boxes.cls):
|
| 179 |
-
label = model.names[int(cls)]
|
| 180 |
-
if label.lower() == "circuit":
|
| 181 |
-
box = results.boxes.xyxy[i].cpu().numpy().astype(int)
|
| 182 |
-
boxes.append(box)
|
| 183 |
-
|
| 184 |
-
return boxes
|
| 185 |
-
|
| 186 |
-
def draw_hotspot_boxes(image_path, boxes, ambient_temp, min_temp=0, max_temp=100):
|
| 187 |
-
image = cv2.imread(image_path)
|
| 188 |
-
red_channel = image[:, :, 2]
|
| 189 |
-
temp_map = (red_channel / 255.0) * (max_temp - min_temp) + min_temp
|
| 190 |
-
threshold = ambient_temp + 5
|
| 191 |
-
|
| 192 |
-
mask = np.zeros_like(red_channel, dtype=np.uint8)
|
| 193 |
-
annotated = image.copy()
|
| 194 |
-
|
| 195 |
-
for box in boxes:
|
| 196 |
-
x1, y1, x2, y2 = box
|
| 197 |
-
region_temp = temp_map[y1:y2, x1:x2]
|
| 198 |
-
if np.mean(region_temp) > threshold:
|
| 199 |
-
cv2.rectangle(annotated, (x1, y1), (x2, y2), (0, 0, 255), 2)
|
| 200 |
-
cv2.putText(annotated, f"HOT > {threshold:.1f}°C", (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
|
| 201 |
-
mask[y1:y2, x1:x2] = 255
|
| 202 |
-
|
| 203 |
-
return annotated, mask, temp_map
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
def main():
|
| 207 |
-
st.set_page_config(layout="wide")
|
| 208 |
-
st.title("Thermal Circuit & Hotspot Analysis")
|
| 209 |
-
|
| 210 |
-
uploaded_file = st.file_uploader("Upload Thermal Image (.jpg/.png)", type=["jpg", "jpeg", "png"], key="upload")
|
| 211 |
-
if uploaded_file:
|
| 212 |
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as tmp_file:
|
| 213 |
-
tmp_file.write(uploaded_file.getvalue())
|
| 214 |
-
tmp_path = tmp_file.name
|
| 215 |
-
|
| 216 |
-
st.image(cv2.cvtColor(cv2.imread(tmp_path), cv2.COLOR_BGR2RGB), caption="Uploaded Thermal Image", use_column_width=True)
|
| 217 |
-
|
| 218 |
-
conf_threshold = st.slider("Detection Confidence Threshold", 0.0, 1.0, 0.5, 0.01, key="conf_thresh")
|
| 219 |
-
if st.button("Analyze Image", key="analyze"):
|
| 220 |
-
model_path = "best.pt"
|
| 221 |
-
analyze_thermal_circuit_image(model_path, tmp_path, conf_threshold)
|
| 222 |
-
temps = extract_temperatures_from_image(tmp_path)
|
| 223 |
-
if temps:
|
| 224 |
-
plot_hover_temperature(tmp_path, temp_min=temps["lowest"], temp_max=temps["highest"])
|
| 225 |
-
else:
|
| 226 |
-
st.info("Using default temperature range (0°C to 100°C) for hover map.")
|
| 227 |
-
plot_hover_temperature(tmp_path)
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
ambient = extract_ambient_temperature(tmp_path)
|
| 231 |
-
|
| 232 |
-
boxes = detect_circuit_boxes(model_path, tmp_path)
|
| 233 |
-
|
| 234 |
-
if not boxes:
|
| 235 |
-
st.warning("No circuit components detected.")
|
| 236 |
-
else:
|
| 237 |
-
st.success(f"Detected {len(boxes)} Circuit Regions")
|
| 238 |
-
|
| 239 |
-
st.subheader("Mapping Hotspots...")
|
| 240 |
-
annotated, mask, temp_map = draw_hotspot_boxes(tmp_path, boxes, ambient)
|
| 241 |
-
|
| 242 |
-
st.image(cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB), caption="Annotated Hot Circuits", use_column_width=True)
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as mask_file:
|
| 247 |
-
cv2.imwrite(mask_file.name, mask)
|
| 248 |
-
st.download_button("Download Hotspot Mask", data=open(mask_file.name, "rb"), file_name="hotspot_mask.png")
|
| 249 |
-
|
| 250 |
-
os.unlink(tmp_path)
|
| 251 |
-
|
| 252 |
-
if __name__ == "__main__":
|
| 253 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|