mie035 commited on
Commit
fba2ed6
·
1 Parent(s): d045149
Files changed (9) hide show
  1. Dockerfile +31 -0
  2. README.md +23 -4
  3. api.py +30 -0
  4. app.py +31 -31
  5. public/bed.png +0 -0
  6. requirements.txt +3 -1
  7. view/index.css +11 -0
  8. view/index.html +25 -0
  9. view/index.js +75 -0
Dockerfile ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the official Python 3.9 image
2
+ FROM python:3.9
3
+
4
+ RUN apt-get update && apt-get upgrade -y
5
+
6
+ RUN apt-get install -y libgl1-mesa-dev
7
+
8
+ # Set the working directory to /code
9
+ WORKDIR /code
10
+
11
+ # Copy the current directory contents into the container at /code
12
+ COPY ./requirements.txt /code/requirements.txt
13
+
14
+ # Install requirements.txt
15
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
16
+
17
+ # Set up a new user named "user" with user ID 1000
18
+ RUN useradd -m -u 1000 user
19
+ # Switch to the "user" user
20
+ USER user
21
+ # Set home to the user's home directory
22
+ ENV HOME=/home/user \
23
+ PATH=/home/user/.local/bin:$PATH
24
+
25
+ # Set the working directory to the user's home directory
26
+ WORKDIR $HOME/app
27
+
28
+ # Copy the current directory contents into the container at $HOME/app setting the owner to the user
29
+ COPY --chown=user . $HOME/app
30
+
31
+ CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -3,11 +3,30 @@ title: Image2mesh
3
  emoji: 👁
4
  colorFrom: green
5
  colorTo: blue
6
- sdk: gradio
7
- sdk_version: 3.19.1
8
- app_file: app.py
9
  pinned: false
10
- duplicated_from: mattiagatti/image2mesh
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  emoji: 👁
4
  colorFrom: green
5
  colorTo: blue
6
+ sdk: docker
7
+ app_file: api.py
 
8
  pinned: false
 
9
  ---
10
 
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
12
+
13
+ addded by nagauta below
14
+ I modified the app below to use it as an API.(nagauta)
15
+ https://huggingface.co/spaces/mattiagatti/image2mesh
16
+
17
+ ## here is my note to startup enviroment construction
18
+ # env.
19
+ ## local
20
+ Machine: MacBook Air (M1, 2020)
21
+ OS: Monterey 12.6.3
22
+ RAM: 16GB
23
+
24
+ # anaconda
25
+ conda create -n i2m python=3.9
26
+ conda activate i2m
27
+ pip install --no-cache-dir --upgrade -r /code/requirements.txt
28
+ uvicorn api:app --reload
29
+
30
+ # Docker
31
+ docker build -t i2m .
32
+ docker run -it -p 7860:7860 i2m
api.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI,UploadFile
2
+ import shutil
3
+ from fastapi.responses import HTMLResponse
4
+ from fastapi.responses import FileResponse
5
+ from fastapi.staticfiles import StaticFiles
6
+ import app as predictor
7
+ from PIL import Image, ImageFilter
8
+
9
+ app = FastAPI()
10
+
11
+ app.mount("/view", StaticFiles(directory="view", html=True), name="view")
12
+ app.mount("/public", StaticFiles(directory="public", html=True), name="public")
13
+
14
+ @app.get("/")
15
+ def index() -> FileResponse:
16
+ return FileResponse(path="./view/index.html", media_type="text/html")
17
+
18
+ @app.post("/prediction/")
19
+ async def predict(targetImage: UploadFile):
20
+ path = f'public/{targetImage.filename}'# api/filesディレクトリを作成しておく
21
+ with open(path, 'wb+') as buffer:
22
+ shutil.copyfileobj(targetImage.file, buffer)
23
+ im = Image.open(path)
24
+ # todo quality指定できるようにする
25
+ depth_image, mesh_path = predictor.predict(im, 3)
26
+ print(mesh_path)
27
+ return {
28
+ "path":"public",
29
+ "name":mesh_path
30
+ }
app.py CHANGED
@@ -76,7 +76,7 @@ def generate_mesh(image, depth_image, quality):
76
 
77
  # save the mesh
78
  temp_name = next(tempfile._get_candidate_names()) + '.obj'
79
- o3d.io.write_triangle_mesh(temp_name, mesh)
80
 
81
  return temp_name
82
 
@@ -91,33 +91,33 @@ def predict(image, quality):
91
 
92
  return depth_image, mesh_path
93
 
94
-
95
- # GUI
96
- title = 'Image2Mesh'
97
- description = 'Demo based on my <a href="https://towardsdatascience.com/generate-a-3d-mesh-from-an-image-with-python' \
98
- '-12210c73e5cc">article</a>. This demo predicts the depth of an image and then generates the 3D mesh. ' \
99
- 'Choosing a higher quality increases the time to generate the mesh. You can download the mesh by ' \
100
- 'clicking the top-right button on the 3D viewer. '
101
- examples = [[f'examples/{name}', 3] for name in sorted(os.listdir('examples'))]
102
-
103
- # example image source:
104
- # N. Silberman, D. Hoiem, P. Kohli, and Rob Fergus,
105
- # Indoor Segmentation and Support Inference from RGBD Images (2012)
106
-
107
- iface = gr.Interface(
108
- fn=predict,
109
- inputs=[
110
- gr.Image(type='pil', label='Input Image'),
111
- gr.Slider(1, 5, step=1, value=3, label='Mesh quality')
112
- ],
113
- outputs=[
114
- gr.Image(label='Depth'),
115
- gr.Model3D(label='3D Model', clear_color=[0.0, 0.0, 0.0, 0.0])
116
- ],
117
- examples=examples,
118
- allow_flagging='never',
119
- cache_examples=False,
120
- title=title,
121
- description=description
122
- )
123
- iface.launch()
 
76
 
77
  # save the mesh
78
  temp_name = next(tempfile._get_candidate_names()) + '.obj'
79
+ o3d.io.write_triangle_mesh("public/" + temp_name, mesh)
80
 
81
  return temp_name
82
 
 
91
 
92
  return depth_image, mesh_path
93
 
94
+ if __name__ == '__main__':
95
+ # GUI
96
+ title = 'Image2Mesh'
97
+ description = 'Demo based on my <a href="https://towardsdatascience.com/generate-a-3d-mesh-from-an-image-with-python' \
98
+ '-12210c73e5cc">article</a>. This demo predicts the depth of an image and then generates the 3D mesh. ' \
99
+ 'Choosing a higher quality increases the time to generate the mesh. You can download the mesh by ' \
100
+ 'clicking the top-right button on the 3D viewer. '
101
+ examples = [[f'examples/{name}', 3] for name in sorted(os.listdir('examples'))]
102
+
103
+ # example image source:
104
+ # N. Silberman, D. Hoiem, P. Kohli, and Rob Fergus,
105
+ # Indoor Segmentation and Support Inference from RGBD Images (2012)
106
+
107
+ iface = gr.Interface(
108
+ fn=predict,
109
+ inputs=[
110
+ gr.Image(type='pil', label='Input Image'),
111
+ gr.Slider(1, 5, step=1, value=3, label='Mesh quality')
112
+ ],
113
+ outputs=[
114
+ gr.Image(label='Depth'),
115
+ gr.Model3D(label='3D Model', clear_color=[0.0, 0.0, 0.0, 0.0])
116
+ ],
117
+ examples=examples,
118
+ allow_flagging='never',
119
+ cache_examples=False,
120
+ title=title,
121
+ description=description
122
+ )
123
+ iface.launch()
public/bed.png ADDED
requirements.txt CHANGED
@@ -4,4 +4,6 @@ numpy
4
  open3d
5
  Pillow
6
  torch
7
- transformers
 
 
 
4
  open3d
5
  Pillow
6
  torch
7
+ transformers
8
+ fastapi
9
+ uvicorn[standard]
view/index.css ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html, body {
2
+ width: 100%;
3
+ height: 100%;
4
+ overflow: hidden;
5
+ }
6
+
7
+ #renderCanvas {
8
+ width : 100%;
9
+ height : 100%;
10
+ touch-action: none;
11
+ }
view/index.html ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <link rel="stylesheet" type="text/css" href="/view/index.css" />
8
+ <title>Image to 3D</title>
9
+ </head>
10
+ <body>
11
+ <form id="upload">
12
+ <input type="file" id="targetImage">
13
+ <button>変換する</button>
14
+ </form>
15
+ <canvas id="renderCanvas"></canvas>
16
+
17
+ <script src="https://preview.babylonjs.com/babylon.js"></script>
18
+ <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
19
+ <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
20
+ <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
21
+ <script src="https://cdn.babylonjs.com/loaders/babylon.objFileLoader.js"></script>
22
+ <script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
23
+ <script src="/view/index.js"></script>
24
+ </body>
25
+ </html>
view/index.js ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function main() {
2
+
3
+ prepareToUploadFile();
4
+ // load3dModel();
5
+
6
+ }
7
+ function prepareToUploadFile(){
8
+ /*
9
+ * 送信イベントが発生したら実行
10
+ */
11
+ document.querySelector("#upload").addEventListener("submit", (e)=>{
12
+ const targetImage = document.querySelector("#targetImage");
13
+ // 規定の送信処理をキャンセル(画面遷移などしない)
14
+ e.preventDefault();
15
+
16
+ // 送信データの準備
17
+ const formData = new FormData();
18
+ formData.append("targetImage", targetImage.files[0]); // ファイル内容を詰める
19
+
20
+ const param = {
21
+ method: "POST",
22
+ body: formData
23
+ }
24
+
25
+ // アップロードする
26
+ fetch(`${window.origin}/prediction`, param)
27
+ .then((res)=>{
28
+ return( res.json() );
29
+ })
30
+ .then((json)=>{
31
+ // 通信が成功した際の処理
32
+ load3dModel(json["path"], json["name"]);
33
+ })
34
+ .catch((error)=>{
35
+ // エラー処理
36
+ alert("残念、なんかエラー!")
37
+ });
38
+ });
39
+ }
40
+ function load3dModel(rcPath, rcName){
41
+
42
+ const canvas = document.getElementById('renderCanvas');
43
+ const engine = new BABYLON.Engine(canvas);
44
+ // ここから
45
+ function createScene() {
46
+ // シーンを作成
47
+ const scene = new BABYLON.Scene(engine);
48
+ // カメラを作成
49
+ const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 3, new BABYLON.Vector3(0, 0, 0), scene);
50
+ // カメラがユーザからの入力で動くように
51
+ camera.attachControl(canvas, true);
52
+ // ライトを作成
53
+ const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
54
+
55
+ // 画像を読み込む
56
+ BABYLON.SceneLoader.ImportMesh("", `/${rcPath}/`, `${rcName}`, scene, function (newMeshes) {
57
+ // Create a default arc rotate camera and light.
58
+ // createArcRotateCamera
59
+ scene.createDefaultCameraOrLight(true, true, true);
60
+ });
61
+ return scene;
62
+ }
63
+
64
+ const scene = createScene();
65
+
66
+
67
+ engine.runRenderLoop(() => {
68
+ scene.render();
69
+ });
70
+
71
+ window.addEventListener('resize', () => {
72
+ engine.resize();
73
+ });
74
+ }
75
+ window.addEventListener('DOMContentLoaded', main);