Upload 2 files
Browse files- app.py +129 -0
- requirements.txt +6 -0
app.py
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
st.set_page_config(page_title="Object Volume Estimator", layout="wide")
|
3 |
+
|
4 |
+
import cv2
|
5 |
+
import torch
|
6 |
+
import numpy as np
|
7 |
+
from PIL import Image
|
8 |
+
import pandas as pd
|
9 |
+
from ultralytics import YOLO
|
10 |
+
from torchvision.transforms import Compose, Resize, ToTensor, Normalize
|
11 |
+
|
12 |
+
# Load models
|
13 |
+
@st.cache_resource
|
14 |
+
def load_models():
|
15 |
+
yolo = YOLO("yolov8n.pt")
|
16 |
+
midas = torch.hub.load("intel-isl/MiDaS", "DPT_Large")
|
17 |
+
midas.eval()
|
18 |
+
transform = Compose([
|
19 |
+
Resize(384),
|
20 |
+
ToTensor(),
|
21 |
+
Normalize(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
|
22 |
+
])
|
23 |
+
return yolo, midas, transform
|
24 |
+
|
25 |
+
yolo_model, midas_model, midas_transform = load_models()
|
26 |
+
|
27 |
+
# --- Streamlit App ---
|
28 |
+
st.title("Object Dimension & Volume Estimator")
|
29 |
+
|
30 |
+
# ๐ธ Image Source Selection
|
31 |
+
option = st.radio("Choose Image Source:", ("๐ค Upload Image", "๐ธ Use Camera"))
|
32 |
+
|
33 |
+
# Mode Selection
|
34 |
+
mode = st.selectbox("Select Image Type:", ["2D RGB Image", "RGB + Depth Image"])
|
35 |
+
|
36 |
+
image_pil = None
|
37 |
+
|
38 |
+
if option == "๐ค Upload Image":
|
39 |
+
uploaded_file = st.file_uploader("Upload an image", type=["jpg", "jpeg", "png"])
|
40 |
+
if uploaded_file:
|
41 |
+
image_pil = Image.open(uploaded_file).convert("RGB")
|
42 |
+
|
43 |
+
elif option == "๐ธ Use Camera":
|
44 |
+
camera_input = st.camera_input("Take a picture")
|
45 |
+
if camera_input:
|
46 |
+
image_pil = Image.open(camera_input).convert("RGB")
|
47 |
+
|
48 |
+
# Upload depth image if RGB + Depth selected
|
49 |
+
depth_map = None
|
50 |
+
if mode == "RGB + Depth Image" and image_pil is not None:
|
51 |
+
depth_file = st.file_uploader("Upload corresponding Depth Map (grayscale image)", type=["png", "jpg", "jpeg", "tiff"])
|
52 |
+
if depth_file:
|
53 |
+
depth_map = np.array(Image.open(depth_file).convert("L"))
|
54 |
+
|
55 |
+
# Proceed if we have an image
|
56 |
+
if image_pil is not None:
|
57 |
+
# Convert to OpenCV format
|
58 |
+
image_cv = np.array(image_pil)
|
59 |
+
image_cv = cv2.cvtColor(image_cv, cv2.COLOR_RGB2BGR)
|
60 |
+
img_h, img_w = image_cv.shape[:2]
|
61 |
+
st.image(image_pil, caption="Input Image", use_container_width=True)
|
62 |
+
|
63 |
+
# Store detected object data
|
64 |
+
object_data = []
|
65 |
+
|
66 |
+
# ๐ YOLO Object Detection
|
67 |
+
results = yolo_model(image_cv)
|
68 |
+
|
69 |
+
# ๐ Depth Estimation
|
70 |
+
if mode == "2D RGB Image":
|
71 |
+
input_tensor = midas_transform(image_pil).unsqueeze(0)
|
72 |
+
with torch.no_grad():
|
73 |
+
estimated_depth = midas_model(input_tensor).squeeze().cpu().numpy()
|
74 |
+
depth_map_resized = cv2.resize(estimated_depth, (img_w, img_h))
|
75 |
+
else:
|
76 |
+
if depth_map is not None:
|
77 |
+
depth_map_resized = cv2.resize(depth_map, (img_w, img_h))
|
78 |
+
else:
|
79 |
+
st.error("Please upload a corresponding depth map for 3D images.")
|
80 |
+
st.stop()
|
81 |
+
|
82 |
+
# ๐ฏ Process each detection
|
83 |
+
for r in results:
|
84 |
+
for box in r.boxes:
|
85 |
+
x1, y1, x2, y2 = map(int, box.xyxy[0])
|
86 |
+
width = x2 - x1
|
87 |
+
height = y2 - y1
|
88 |
+
region = depth_map_resized[y1:y2, x1:x2]
|
89 |
+
if region.size == 0:
|
90 |
+
continue
|
91 |
+
depth = np.mean(region)
|
92 |
+
volume = round(depth * width * height, 2)
|
93 |
+
|
94 |
+
# Draw bounding box & label
|
95 |
+
cv2.rectangle(image_cv, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
96 |
+
label = f"LรBรH: {depth:.2f}ร{width}ร{height} | V: {volume}"
|
97 |
+
cv2.putText(image_cv, label, (x1, y1 - 10),
|
98 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 0), 2)
|
99 |
+
|
100 |
+
# Store data for CSV
|
101 |
+
object_data.append({
|
102 |
+
"Length": round(depth, 2),
|
103 |
+
"Breadth": int(width),
|
104 |
+
"Height": int(height),
|
105 |
+
"Volume": volume
|
106 |
+
})
|
107 |
+
|
108 |
+
# Show object details
|
109 |
+
st.markdown(f"**๐ง Object Detected:**")
|
110 |
+
st.write(f"๐ Length ร Breadth ร Height: {depth:.2f} ร {width} ร {height}")
|
111 |
+
st.write(f"๐ฆ Estimated Volume: {volume} (relative unitsยณ)")
|
112 |
+
|
113 |
+
# Show annotated image
|
114 |
+
result_img = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB)
|
115 |
+
st.image(result_img, caption="Detected Dimensions", use_container_width=True)
|
116 |
+
|
117 |
+
# Show table & CSV download if data exists
|
118 |
+
if object_data:
|
119 |
+
df = pd.DataFrame(object_data)
|
120 |
+
st.markdown("### ๐ Detected Objects Table")
|
121 |
+
st.dataframe(df)
|
122 |
+
|
123 |
+
csv = df.to_csv(index=False).encode('utf-8')
|
124 |
+
st.download_button(
|
125 |
+
label="๐ฅ Download Results as CSV",
|
126 |
+
data=csv,
|
127 |
+
file_name='object_dimensions_volume.csv',
|
128 |
+
mime='text/csv',
|
129 |
+
)
|
requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit
|
2 |
+
torch
|
3 |
+
torchvision
|
4 |
+
ultralytics
|
5 |
+
opencv-python
|
6 |
+
timm
|