Add versions info and project description
Browse files- README.md +71 -0
- app.py +19 -11
- modules/version_info.py +127 -0
README.md
CHANGED
|
@@ -16,4 +16,75 @@ thumbnail: >-
|
|
| 16 |
https://cdn-uploads.huggingface.co/production/uploads/6346595c9e5f0fe83fc60444/s0fQvcoiSBlH36AXpVwPi.png
|
| 17 |
---
|
| 18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 16 |
https://cdn-uploads.huggingface.co/production/uploads/6346595c9e5f0fe83fc60444/s0fQvcoiSBlH36AXpVwPi.png
|
| 17 |
---
|
| 18 |
|
| 19 |
+
# 3D Viewer
|
| 20 |
+
|
| 21 |
+
**3D Viewer** is a lightweight web application built with [Gradio](https://gradio.app) that allows users to view 3D models along with their corresponding height map and source images. The app dynamically loads these resources by parsing URL query string parameters, providing a seamless and interactive experience.
|
| 22 |
+
|
| 23 |
+
## Features
|
| 24 |
+
|
| 25 |
+
- **3D Model Display:**
|
| 26 |
+
Uses `gr.Model3D` to render 3D models dynamically from a URL parameter (`3d`).
|
| 27 |
+
|
| 28 |
+
- **Image Slider:**
|
| 29 |
+
Displays images (height map and source image) using `gr.ImageSlider`. The images are loaded via URL query parameters named `hm` (height map) and `image` (source image).
|
| 30 |
+
|
| 31 |
+
- **File Upload:**
|
| 32 |
+
Includes an `gr.UploadButton` for uploading new file types like `.glb`, `.gltf`, `.obj`, `.png`, `.jpg`, and `.ply`.
|
| 33 |
+
|
| 34 |
+
- **Custom Theming:**
|
| 35 |
+
The interface is styled with the `Surn/Beeuty` theme to ensure a modern and cohesive look.
|
| 36 |
+
|
| 37 |
+
## How It Works
|
| 38 |
+
|
| 39 |
+
- **Query String Parsing:**
|
| 40 |
+
While running in Gradio, since the Python callback does not operate within a conventional HTTP request context, the app retrieves query string values by using client-side JavaScript. This JavaScript snippet captures the URL parameters and passes them to the Python callback, which then updates the components accordingly.
|
| 41 |
+
|
| 42 |
+
- **Static Paths:**
|
| 43 |
+
The app sets static paths for directories such as `images/`, `models/`, and `assets/` for accessing local resources.
|
| 44 |
+
|
| 45 |
+
## Getting Started
|
| 46 |
+
|
| 47 |
+
1. **Install Dependencies:**
|
| 48 |
+
|
| 49 |
+
Ensure you have Python 3.12.8 installed and install Gradio (v5.29.0 or compatible):
|
| 50 |
+
|
| 51 |
+
```pip install gradio==5.29.0```
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
2. **Run the Application:**
|
| 55 |
+
|
| 56 |
+
Start the app by executing:
|
| 57 |
+
|
| 58 |
+
```python app.py```
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
3. **Use the Query String:**
|
| 62 |
+
|
| 63 |
+
After launching, the app can load specific resources if you pass the following query parameters in the URL:
|
| 64 |
+
|
| 65 |
+
- `3d`: URL for the 3D model (e.g., `?3d=https://example.com/model.glb`)
|
| 66 |
+
- `hm`: URL for the height map image (e.g., `?hm=https://example.com/heightmap.png`)
|
| 67 |
+
- `image`: URL for the source image (e.g., `?image=https://example.com/source.png`)
|
| 68 |
+
|
| 69 |
+
Example URL:
|
| 70 |
+
|
| 71 |
+
```http://localhost:7860/?3d=https://example.com/model.glb&hm=https://example.com/heightmap.png&image=https://example.com/source.png```
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
## Project Structure
|
| 75 |
+
|
| 76 |
+
- **app.py:**
|
| 77 |
+
The core application script defining the Gradio interface, callbacks, and dynamic query string processing.
|
| 78 |
+
|
| 79 |
+
- **README.md:**
|
| 80 |
+
This documentation file outlining the project details and configuration.
|
| 81 |
+
|
| 82 |
+
- **assets, images, models:**
|
| 83 |
+
Directories containing static resources such as images, 3D models, and other assets.
|
| 84 |
+
|
| 85 |
+
## License
|
| 86 |
+
|
| 87 |
+
This project is licensed under the Apache-2.0 license.
|
| 88 |
+
|
| 89 |
+
|
| 90 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
CHANGED
|
@@ -1,4 +1,9 @@
|
|
| 1 |
import gradio as gr
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
def load_data(query_params, model_3d, image_slider):
|
| 4 |
# set default values or pull from querystring
|
|
@@ -21,23 +26,26 @@ with gr.Blocks(css_paths="style_20250314.css", title="3D viewer", theme='Surn/Be
|
|
| 21 |
gr.Markdown("# 3D Model Viewer")
|
| 22 |
|
| 23 |
with gr.Row():
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
| 34 |
|
| 35 |
with gr.Row():
|
| 36 |
upload_btn = gr.UploadButton(
|
| 37 |
"Upload",
|
| 38 |
file_types=[".glb", ".gltf", ".obj", ".png", ".jpg", ".ply"]
|
| 39 |
)
|
| 40 |
-
|
|
|
|
|
|
|
| 41 |
# Use JavaScript to pass the query parameters to your callback.
|
| 42 |
viewer3d.load(
|
| 43 |
load_data,
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import modules.version_info as version_info
|
| 3 |
+
|
| 4 |
+
def getVersions():
|
| 5 |
+
#return html_versions
|
| 6 |
+
return version_info.versions_html()
|
| 7 |
|
| 8 |
def load_data(query_params, model_3d, image_slider):
|
| 9 |
# set default values or pull from querystring
|
|
|
|
| 26 |
gr.Markdown("# 3D Model Viewer")
|
| 27 |
|
| 28 |
with gr.Row():
|
| 29 |
+
with gr.Column():
|
| 30 |
+
model_3d = gr.Model3D(
|
| 31 |
+
label="3D Model",
|
| 32 |
+
value=None,
|
| 33 |
+
height=400
|
| 34 |
+
)
|
| 35 |
+
image_slider = gr.ImageSlider(
|
| 36 |
+
label="Images",
|
| 37 |
+
value=None,
|
| 38 |
+
height=400
|
| 39 |
+
)
|
| 40 |
|
| 41 |
with gr.Row():
|
| 42 |
upload_btn = gr.UploadButton(
|
| 43 |
"Upload",
|
| 44 |
file_types=[".glb", ".gltf", ".obj", ".png", ".jpg", ".ply"]
|
| 45 |
)
|
| 46 |
+
with gr.Row():
|
| 47 |
+
gr.HTML(value=getVersions(), visible=True, elem_id="versions")
|
| 48 |
+
|
| 49 |
# Use JavaScript to pass the query parameters to your callback.
|
| 50 |
viewer3d.load(
|
| 51 |
load_data,
|
modules/version_info.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# modules/version_info.py
|
| 2 |
+
|
| 3 |
+
import subprocess
|
| 4 |
+
import os
|
| 5 |
+
import sys
|
| 6 |
+
import gc
|
| 7 |
+
import gradio as gr
|
| 8 |
+
|
| 9 |
+
git = os.environ.get('GIT', "git")
|
| 10 |
+
|
| 11 |
+
def commit_hash():
|
| 12 |
+
try:
|
| 13 |
+
return subprocess.check_output([git, "rev-parse", "HEAD"], shell=False, encoding='utf8').strip()
|
| 14 |
+
except Exception:
|
| 15 |
+
return "<none>"
|
| 16 |
+
|
| 17 |
+
def get_xformers_version():
|
| 18 |
+
try:
|
| 19 |
+
import xformers
|
| 20 |
+
return xformers.__version__
|
| 21 |
+
except Exception:
|
| 22 |
+
return "<none>"
|
| 23 |
+
def get_transformers_version():
|
| 24 |
+
try:
|
| 25 |
+
import transformers
|
| 26 |
+
return transformers.__version__
|
| 27 |
+
except Exception:
|
| 28 |
+
return "<none>"
|
| 29 |
+
|
| 30 |
+
def get_accelerate_version():
|
| 31 |
+
try:
|
| 32 |
+
import accelerate
|
| 33 |
+
return accelerate.__version__
|
| 34 |
+
except Exception:
|
| 35 |
+
return "<none>"
|
| 36 |
+
def get_safetensors_version():
|
| 37 |
+
try:
|
| 38 |
+
import safetensors
|
| 39 |
+
return safetensors.__version__
|
| 40 |
+
except Exception:
|
| 41 |
+
return "<none>"
|
| 42 |
+
def get_diffusers_version():
|
| 43 |
+
try:
|
| 44 |
+
import diffusers
|
| 45 |
+
return diffusers.__version__
|
| 46 |
+
except Exception:
|
| 47 |
+
return "<none>"
|
| 48 |
+
def get_open3d_version():
|
| 49 |
+
try:
|
| 50 |
+
import open3d
|
| 51 |
+
return f"{open3d.__version__} cuda:{open3d.core.cuda.is_available()}"
|
| 52 |
+
except Exception:
|
| 53 |
+
return "<none>"
|
| 54 |
+
|
| 55 |
+
def get_torch_info():
|
| 56 |
+
from torch import __version__ as torch_version_, version, cuda, backends
|
| 57 |
+
initialize_cuda()
|
| 58 |
+
try:
|
| 59 |
+
info = [torch_version_, f"CUDA Version:{version.cuda}", f"Available:{cuda.is_available()}", f"flash attention enabled: {backends.cuda.flash_sdp_enabled()}", f"Capabilities: {cuda.get_device_capability(0)}", f"Device Name: {cuda.get_device_name(0)}", f"Device Count: {cuda.device_count()}",f"Devices: {os.environ['CUDA_VISIBLE_DEVICES']}", f"Zero :{os.environ['CUDA_MODULE_LOADING']}"]
|
| 60 |
+
del torch_version_, version, cuda, backends
|
| 61 |
+
return info
|
| 62 |
+
except Exception:
|
| 63 |
+
del torch_version_, version, cuda, backends
|
| 64 |
+
return "<none>"
|
| 65 |
+
|
| 66 |
+
def release_torch_resources():
|
| 67 |
+
from torch import cuda
|
| 68 |
+
if cuda.is_available():
|
| 69 |
+
# Clear the CUDA cache
|
| 70 |
+
cuda.empty_cache()
|
| 71 |
+
cuda.ipc_collect()
|
| 72 |
+
# Delete any objects that are using GPU memory
|
| 73 |
+
#for obj in gc.get_objects():
|
| 74 |
+
# if is_tensor(obj) or (hasattr(obj, 'data') and is_tensor(obj.data)):
|
| 75 |
+
# del obj
|
| 76 |
+
# Run garbage collection
|
| 77 |
+
del cuda
|
| 78 |
+
gc.collect()
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def initialize_cuda():
|
| 82 |
+
from torch import cuda, version
|
| 83 |
+
if cuda.is_available():
|
| 84 |
+
device = cuda.device("cuda")
|
| 85 |
+
print(f"CUDA is available. Using device: {cuda.get_device_name(0)} with CUDA version: {version.cuda}")
|
| 86 |
+
result = "cuda"
|
| 87 |
+
else:
|
| 88 |
+
#device = cuda.device("cpu")
|
| 89 |
+
print("CUDA is not available. Using CPU.")
|
| 90 |
+
result = "cpu"
|
| 91 |
+
return result
|
| 92 |
+
|
| 93 |
+
def versions_html():
|
| 94 |
+
from torch import __version__ as torch_version_
|
| 95 |
+
python_version = ".".join([str(x) for x in sys.version_info[0:3]])
|
| 96 |
+
commit = commit_hash()
|
| 97 |
+
|
| 98 |
+
# Define the Toggle Dark Mode link with JavaScript
|
| 99 |
+
toggle_dark_link = '''
|
| 100 |
+
<a href="#" onclick="document.body.classList.toggle('dark'); return false;" style="cursor: pointer; text-decoration: underline;">
|
| 101 |
+
Toggle Dark Mode
|
| 102 |
+
</a>
|
| 103 |
+
'''
|
| 104 |
+
|
| 105 |
+
v_html = f"""
|
| 106 |
+
version: <a href="https://huggingface.co/spaces/Surn/3D-Viewer/commit/{"huggingface" if commit == "<none>" else commit}" target="_blank">{"huggingface" if commit == "<none>" else commit}</a>
|
| 107 |
+
 • 
|
| 108 |
+
python: <span title="{sys.version}">{python_version}</span>
|
| 109 |
+
 • 
|
| 110 |
+
torch: {torch_version_}
|
| 111 |
+
 • 
|
| 112 |
+
diffusers: {get_diffusers_version()}
|
| 113 |
+
 • 
|
| 114 |
+
transformers: {get_transformers_version()}
|
| 115 |
+
 • 
|
| 116 |
+
safetensors: {get_safetensors_version()}
|
| 117 |
+
 • 
|
| 118 |
+
open3d: {get_open3d_version()}
|
| 119 |
+
 • 
|
| 120 |
+
gradio: {gr.__version__}
|
| 121 |
+
 • 
|
| 122 |
+
{toggle_dark_link}
|
| 123 |
+
<br>
|
| 124 |
+
Full GPU Info:{get_torch_info()}
|
| 125 |
+
"""
|
| 126 |
+
del torch_version_
|
| 127 |
+
return v_html
|