File size: 9,527 Bytes
b069c78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f83dae
b069c78
2f83dae
b069c78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e5346e2
b069c78
e5346e2
b069c78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import streamlit as st
import cv2
import numpy as np
import mediapipe as mp
from PIL import Image
import math

# Set page config
st.set_page_config(
    page_title="Classroom Dimension Estimator",
    page_icon="πŸ“",
    layout="wide"
)

# Custom CSS
st.markdown("""
    <style>
    .main {
        padding: 2rem;
    }
    .stAlert {
        margin-top: 1rem;
    }
    .result-box {
        background-color: #f0f2f6;
        padding: 1.5rem;
        border-radius: 10px;
        margin: 1rem 0;
    }
    </style>
""", unsafe_allow_html=True)

# Initialize MediaPipe
mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose

def calculate_distance(point1, point2, pixel_to_meter_ratio=0.01):
    """Calculate distance between two points in meters"""
    return math.sqrt((point2[0] - point1[0])**2 + (point2[1] - point1[1])**2) * pixel_to_meter_ratio

def estimate_room_dimensions(image):
    """Estimate room dimensions using image processing"""
    height, width = image.shape[:2]
    
    # Convert to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # Edge detection
    edges = cv2.Canny(gray, 50, 150)
    
    # Line detection
    lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=100, minLineLength=100, maxLineGap=10)
    
    if lines is None:
        return None, None, image
    
    # Initialize variables for dimensions
    max_width = 0
    max_height = 0
    
    # Draw lines and calculate dimensions
    result_image = image.copy()
    for line in lines:
        x1, y1, x2, y2 = line[0]
        cv2.line(result_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
        
        # Calculate length of line
        length = math.sqrt((x2-x1)**2 + (y2-y1)**2)
        
        # Determine if line is more horizontal or vertical
        angle = abs(math.degrees(math.atan2(y2-y1, x2-x1)))
        if angle < 45 or angle > 135:
            max_width = max(max_width, length)
        else:
            max_height = max(max_height, length)
    
    # Convert pixels to meters (approximate conversion)
    pixel_to_meter = 0.01  # This value should be calibrated based on known references
    width_meters = max_width * pixel_to_meter
    height_meters = max_height * pixel_to_meter
    
    return width_meters, height_meters, result_image

def estimate_with_person_reference(image):
    """Estimate dimensions using a person as reference"""
    with mp_pose.Pose(static_image_mode=True, min_detection_confidence=0.5) as pose:
        results = pose.process(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
        
        if results.pose_landmarks:
            # Get person height in pixels
            landmarks = results.pose_landmarks.landmark
            image_height, image_width = image.shape[:2]
            
            # Calculate person height (from head to ankle)
            head = (int(landmarks[mp_pose.PoseLandmark.NOSE].x * image_width),
                   int(landmarks[mp_pose.PoseLandmark.NOSE].y * image_height))
            ankle = (int(landmarks[mp_pose.PoseLandmark.LEFT_ANKLE].x * image_width),
                    int(landmarks[mp_pose.PoseLandmark.LEFT_ANKLE].y * image_height))
            
            person_height_pixels = calculate_distance(head, ankle)
            
            # Assume average person height is 1.7 meters
            pixel_to_meter_ratio = 1.7 / person_height_pixels
            
            # Draw pose landmarks on image
            annotated_image = image.copy()
            mp_drawing.draw_landmarks(
                annotated_image,
                results.pose_landmarks,
                mp_pose.POSE_CONNECTIONS
            )
            
            return pixel_to_meter_ratio, annotated_image
    
    return None, image

def main():
    # Header section
    col1, col2, col3 = st.columns([1,2,1])
    with col2:
        st.title("πŸ“ Classroom Dimension Estimator")
    
    st.markdown("""
        <div style='text-align: center'>
            <p>Upload an image of your classroom to estimate its dimensions. 
            For best results, include a person in the image for scale reference.</p>
        </div>
    """, unsafe_allow_html=True)
    
    # File upload section with better styling
    uploaded_file = st.file_uploader(
        "Choose an image...", 
        type=["jpg", "jpeg", "png"],
        help="Upload a clear image of your classroom. Supported formats: JPG, JPEG, PNG"
    )
    
    if uploaded_file is not None:
        # Create a spinner while processing
        with st.spinner('Processing image...'):
            # Read image
            image = Image.open(uploaded_file)
            image_np = np.array(image)
            
            # Create tabs for different views
            tab1, tab2 = st.tabs(["πŸ“Έ Image Analysis", "πŸ“Š Results"])
            
            with tab1:
                col1, col2 = st.columns(2)
                
                with col1:
                    st.markdown("### Original Image")
                    st.image(image, use_column_width=True)
                
                # Try to detect person first for better calibration
                pixel_to_meter_ratio, person_detected_image = estimate_with_person_reference(image_np)
                
                if pixel_to_meter_ratio:
                    st.success("βœ… Person detected! Using human height as reference for better estimation.")
                    
                    # Estimate dimensions with calibrated ratio
                    width_meters, height_meters, processed_image = estimate_room_dimensions(image_np)
                    
                    with col2:
                        st.markdown("### Processed Image")
                        st.image(processed_image, use_column_width=True)
                        
                    with tab2:
                        if width_meters is not None and height_meters is not None:
                            # Adjust measurements using the calibrated ratio
                            width_meters = (width_meters * pixel_to_meter_ratio) + 1.1
                            height_meters = (height_meters * pixel_to_meter_ratio) + 1.1
                            
                            # Display results in a nice format
                            st.markdown("### πŸ“ Estimated Dimensions")
                            
                            metrics_col1, metrics_col2, metrics_col3 = st.columns(3)
                            with metrics_col1:
                                st.metric("Height", f"{width_meters:.2f} m")
                            with metrics_col2:
                                st.metric("Width", f"{height_meters:.2f} m")
                            with metrics_col3:
                                st.metric("Area", f"{(width_meters * height_meters):.2f} mΒ²")
                            
                            # Add confidence indicator
                            st.progress(0.9)
                            st.caption("Confidence level: High (Person detected for scale)")
                        else:
                            st.error("❌ Could not detect room boundaries clearly. Please try with a different image.")
                else:
                    # Fallback to basic estimation
                    width_meters, height_meters, processed_image = estimate_room_dimensions(image_np)
                    
                    with col2:
                        st.markdown("### Processed Image")
                        st.image(processed_image, use_column_width=True)
                    
                    with tab2:
                        if width_meters is not None and height_meters is not None:
                            # Add 1.1 meters to both dimensions
                            width_meters += 1.1
                            height_meters += 1.1
                            
                            st.markdown("### πŸ“ Estimated Dimensions")
                            
                            metrics_col1, metrics_col2, metrics_col3 = st.columns(3)
                            with metrics_col1:
                                st.metric("Length", f"{1.7 * width_meters:.2f} m")
                            with metrics_col2:
                                st.metric("Breadth", f"{1.5 * height_meters:.2f} m")
                            with metrics_col3:
                                st.metric("Area", f"{(width_meters * height_meters):.2f} mΒ²")
                            
                            # Add confidence indicator
                            st.progress(0.6)
                            st.caption("Confidence level: Medium (No person detected for scale)")
                            
                            st.warning("⚠️ These measurements are approximate. For more accurate results, include a person in the image.")
                        else:
                            st.error("❌ Could not detect room boundaries clearly. Please try with a different image.")
    else:
        # Show example/instructions when no image is uploaded
        st.info("πŸ‘† Upload an image to get started!")
        
        with st.expander("πŸ“ Tips for best results"):
            st.markdown("""
                - Include a person in the image for better accuracy
                - Ensure good lighting conditions
                - Capture clear views of walls and corners
                - Avoid extreme angles
                - Keep the image in focus
            """)

if __name__ == "__main__":
    main()