File size: 9,759 Bytes
757ed1c
9650ca8
 
 
 
 
 
 
 
757ed1c
 
6264a43
9650ca8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0d45a3b
9650ca8
0d45a3b
 
9650ca8
0d45a3b
 
9650ca8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5cfa18d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9650ca8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5cfa18d
9650ca8
 
 
 
 
 
 
5cfa18d
 
9650ca8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5cfa18d
9650ca8
757ed1c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
import streamlit as st
import os
import shutil
import zipfile
from space_carving import run_space_carving
from sfm import run_sfm
import open3d as o3d
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import plotly.graph_objects as go
from helpers import *

st.set_page_config(page_title="3D Reconstruction Web App", layout="wide")
st.markdown("<div style='text-align: center;'><h1>3D Scene Reconstruction</h1></div>", unsafe_allow_html=True)
st.markdown("<div style='text-align: center;'><h2>Computer Vision Course Project</h2></div>", unsafe_allow_html=True)
st.markdown("<div style='text-align: right; font-weight:none;color : gray;'><h5>Course Instructor - Dr. Pratik Mazumdar</h5></div>", unsafe_allow_html=True)
st.markdown(
    "<h4><a href='https://github.com/majisouvik26/3d-scene-reconstruction' target='_blank'>Github Link of Project</a></h4>",
    unsafe_allow_html=True
)
st.header("Team Member:")
st.markdown("<div> <ul><li>Aditya Sahani(B22CS003)</li><li>Veeraraju Elluru(B22CS080)</li><li>Souvik Maji(B22CS089)</li><li>Dishit Sharma(B22CS082)</li><li>Aditya Rathor(B22AI044)</li></ul></div>", unsafe_allow_html=True)


st.header("Introduction")
st.write("3D scene reconstruction is a fundamental problem in computer vision that involves recovering the three-dimensional structure of a scene from a set of two-dimensional images. " \
"The goal is to generate an accurate and detailed representation of the real world, typically in the form of point clouds, meshes, or volumetric models. This process plays a crucial role in various applications such as robotics, augmented and virtual reality (AR/VR), autonomous navigation, and cultural heritage preservation." \
" The reconstruction pipeline often incorporates techniques like multi-view stereo, Structure from Motion (SfM), and volumetric methods like voxel carving. By leveraging image geometry, camera calibration data, and feature correspondences across views, 3D reconstruction enables machines to perceive and interpret the spatial layout of the physical world.")


st.image("https://filelist.tudelft.nl/BK/Onderzoek/Research_stories/zhaiyu.png", caption="3D Reconstruction")


st.header("Methods Used and Results")
st.subheader("1. NeRF - Neural Radiance Fields (NeRF)")

col1, col2 = st.columns(2)

with col1:
    st.image("media/test_image_0.png", width=400, caption="Input Image")

with col2:
    st.image("media/truck_reconstruction.gif", width=400, caption="3D Reconstruction")

st.subheader("2. Space Carving")
col1, col2 = st.columns(2)

with col1:
    st.image("media/input_images.png", width=400, caption="Input Image")

with col2:
    st.image("media/shape_mesh.png", width=400, caption="3D Reconstruction")

st.subheader("3. Pix2Vox")
col1, col2 = st.columns(2)

with col1:
    st.image("media/pix.jpg", width=400, caption="Input Image")

with col2:
    st.image("media/pix_output.jpg", width=400, caption="3D Reconstruction")


st.subheader("4. SFM Method")
col1, col2 = st.columns(2)

with col1:
    st.image("media/DSC_0351.JPG", width=400, caption="Input Image")

with col2:
    st.image("media/image.png", width=400, caption="3D Reconstruction")

st.subheader("5. Incremental SFM Method")
col1, col2 = st.columns(2)

with col1:
    st.image("media/WhatsApp Image 2025-04-12 at 17.40.27_1137ddf7.jpg", width=400, caption="Input Image")

with col2:
    st.image("media/rotation_sfm_cam_1_2_3_4[1].gif", width=400, caption="3D Reconstruction")

st.subheader("6. Gaussian Splatting Method")
col1, col2 = st.columns(2)

with col1:
    st.image("media/gs.gif", width=400, caption="Input Image")

with col2:
    st.image("media/output_souvik.png", width=400, caption="3D Reconstruction")




st.header("DEMO OF MODELS")

def show_ply_interactive(ply_path):
    # Load point cloud
    pcd = o3d.io.read_point_cloud(ply_path)
    points = np.asarray(pcd.points)

    # Optional: use colors
    if pcd.has_colors():
        colors = np.asarray(pcd.colors)
    else:
        colors = np.full_like(points, fill_value=0.5)  # default gray

    # Create interactive plot
    fig = go.Figure(data=[go.Scatter3d(
        x=points[:, 0], y=points[:, 1], z=points[:, 2],
        mode='markers',
        marker=dict(
            size=1.5,
            color=colors,
            opacity=0.8
        )
    )])

    fig.update_layout(
        scene=dict(
            xaxis_title='X', yaxis_title='Y', zaxis_title='Z'
        ),
        width=800,
        height=600,
        margin=dict(r=10, l=10, b=10, t=10)
    )

    return fig

# Show PLY as image
# def show_ply_as_image(ply_path):
#     # Load point cloud
#     pcd = o3d.io.read_point_cloud(ply_path)
#     if pcd.is_empty():
#         raise ValueError("The .ply file is empty or could not be loaded.")

#     # Create visualization window (offscreen)
#     vis = o3d.visualization.Visualizer()
#     vis.create_window(visible=False)
#     vis.add_geometry(pcd)

#     # Set camera view
#     ctr = vis.get_view_control()
#     if ctr is None:
#         raise RuntimeError("Failed to get view control from the visualizer.")
#     ctr.set_zoom(0.7)

#     vis.poll_events()
#     vis.update_renderer()

#     # Screenshot to numpy
#     image = vis.capture_screen_float_buffer(do_render=True)
#     vis.destroy_window()

#     # Convert to displayable image
#     img = (np.asarray(image) * 255).astype(np.uint8)
#     return Image.fromarray(img)

# ---------- Function to extract zip ----------
def extract_zip(zip_file, extract_to):
    if os.path.exists(extract_to):
        shutil.rmtree(extract_to)
    os.makedirs(extract_to)

    with zipfile.ZipFile(zip_file, 'r') as zip_ref:
        zip_ref.extractall(extract_to)

    return extract_to

# ---------- SPACE CARVING ----------
st.header("πŸ“¦ Space Carving")
st.markdown("""
**Space Carving** is a volumetric method that uses silhouettes from multiple views to reconstruct a 3D object by carving away inconsistent voxels.

πŸ‘‰ Upload a `.zip` file containing images (JPG/PNG) from different calibrated views.
""")

sc_zip = st.file_uploader("Upload ZIP file for Space Carving", type=["zip"])

if sc_zip:
    sc_extract_path = "uploads_spacecarving"
    with open("temp_spacecarving.zip", "wb") as f:
        f.write(sc_zip.getbuffer())

    extract_zip("temp_spacecarving.zip", sc_extract_path)
    st.success("Extracted  images.")

    if st.button("Run Space Carving Model"):
        output = run_space_carving()  # This should generate the .vtr file
        st.success("Model ran successfully.")

        # Path to generated .vtr file
        vtr_path = "res_space/shape.vtr"  # Update if filename differs

        if os.path.exists(vtr_path):
            st.markdown("### πŸ“₯ Download Space Carved VTR File")
            with open(vtr_path, "rb") as f:
                st.download_button(
                    label="Download .vtr file",
                    data=f,
                    file_name=os.path.basename(vtr_path),
                    mime="application/octet-stream"
                )
        else:
            st.warning("No .vtr file found. Make sure the model ran successfully.")


# ---------- STRUCTURE FROM MOTION ----------
st.markdown("---")
st.header("πŸ“· Structure from Motion (SfM)")
st.markdown("""
**Structure from Motion (SfM)** reconstructs 3D geometry and camera poses from a series of images.

πŸ‘‰ Upload a `.zip` file containing your image dataset (JPG/PNG).
""")

sfm_zip_file = st.file_uploader("Upload ZIP file for SfM", type=["zip"])

if sfm_zip_file is not None:
    zip_name = os.path.splitext(sfm_zip_file.name)[0]  # πŸ‘‰ 'dataset.zip' β†’ 'dataset'
    sfm_extract_path = "uploads_sfm"

    extract_zip(sfm_zip_file, sfm_extract_path)
    st.success(f"Extracted {zip_name} dataset.")

    if st.button("Run SfM Model"):
        output = run_sfm(os.path.join(sfm_extract_path,zip_name))
        st.success("Model ran successfully.")

        # Construct PLY path based on zip filename
        ply_path = os.path.join("res", f"{zip_name}.ply")

        if os.path.exists(ply_path):
            st.markdown("### 🧩 Reconstructed Point Cloud Image")
            # image = show_ply_as_image(ply_path)
            # st.image(image, caption=f"{zip_name}.ply", use_column_width=True)

            # Optional download
            with open(ply_path, "rb") as f:
                st.download_button(
                    label="πŸ“₯ Download .ply file",
                    data=f,
                    file_name=f"{zip_name}.ply",
                    mime="application/octet-stream"
                )
        else:
            st.warning(f"No .ply file named {zip_name}.ply found in 'res/'.")

        if os.path.exists(ply_path):
            st.markdown("### 🧩 Reconstructed Point Cloud (Interactive)")
            fig = show_ply_interactive(ply_path)
            st.plotly_chart(fig, use_container_width=True)





st.header("🧠 Pix2Vox")

uploaded_images = st.file_uploader(f"Upload images", accept_multiple_files=True, type=["png", "jpg", "jpeg"])
# print(uploaded_images)


# --- DISPLAY ---
if uploaded_images:
    st.subheader("Uploaded Input Views")
    cols = st.columns(len(uploaded_images))
    rendering_images = []

    for i, uploaded_file in enumerate(uploaded_images):
        img = Image.open(uploaded_file)

        cols[i].image(img, caption=f"View {i+1}", use_container_width=True)

        img_np = np.array(img).astype(np.float32) / 255.0

        rendering_images.append(img_np)


    if st.button("Submit for Reconstruction"):
        gv=None
        with st.spinner("Reconstructing..."):
            gv = predict_voxel_from_images(rendering_images)

        fig = voxel_to_plotly(gv)
        st.plotly_chart(fig, use_container_width=True)

else:
    st.info(f"Upload images to continue.")