mirror of
https://github.com/gradio-app/gradio.git
synced 2024-11-21 01:01:05 +08:00
add 3d image model
This commit is contained in:
parent
842f337038
commit
07b2a4992f
7474
demo/model/files/Bunny.obj
Normal file
7474
demo/model/files/Bunny.obj
Normal file
File diff suppressed because it is too large
Load Diff
BIN
demo/model/files/Duck.glb
Normal file
BIN
demo/model/files/Duck.glb
Normal file
Binary file not shown.
1777
demo/model/files/Fox.gltf
Normal file
1777
demo/model/files/Fox.gltf
Normal file
File diff suppressed because one or more lines are too long
6
demo/model/files/source.txt
Normal file
6
demo/model/files/source.txt
Normal file
@ -0,0 +1,6 @@
|
||||
Stanford Bunny:
|
||||
https://graphics.stanford.edu/data/3Dscanrep/
|
||||
https://graphics.stanford.edu/~mdfisher/Data/Meshes/bunny.obj
|
||||
|
||||
Duck & Fox:
|
||||
https://github.com/KhronosGroup/glTF-Sample-Models
|
26
demo/model/run.py
Normal file
26
demo/model/run.py
Normal file
@ -0,0 +1,26 @@
|
||||
import os.path
|
||||
|
||||
import gradio as gr
|
||||
|
||||
|
||||
def load_mesh(mesh_file_name):
|
||||
file_dir = "files"
|
||||
mesh_path = os.path.join(file_dir, mesh_file_name)
|
||||
return mesh_path
|
||||
|
||||
|
||||
iface = gr.Interface(
|
||||
load_mesh,
|
||||
inputs=[
|
||||
gr.inputs.Dropdown(["Duck.glb", "Bunny.obj", "Fox.gltf"], type="value", default="Duck.glb", label="Mesh File")
|
||||
],
|
||||
outputs=[
|
||||
"model"
|
||||
|
||||
# to specify options use the object initializer
|
||||
# gr.outputs.Model(clear_color=[1.0, 1.0, 1.0], label="3D Model")
|
||||
]
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
iface.launch()
|
@ -853,6 +853,57 @@ class State(OutputComponent):
|
||||
}
|
||||
|
||||
|
||||
class Model(OutputComponent):
|
||||
'''
|
||||
Used for 3d model output.
|
||||
Output type: filepath
|
||||
Demos: hello_model
|
||||
'''
|
||||
|
||||
def __init__(self, clear_color=None, label=None):
|
||||
'''
|
||||
Parameters:
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
super().__init__(label)
|
||||
self.clear_color = clear_color
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"model": {},
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Parameters:
|
||||
y (str): path to the model
|
||||
Returns:
|
||||
(str): file name
|
||||
(str): file extension
|
||||
(str): base64 url data
|
||||
"""
|
||||
|
||||
if self.clear_color is None:
|
||||
self.clear_color = [0.2, 0.2, 0.2]
|
||||
|
||||
return {
|
||||
"name": os.path.basename(y),
|
||||
"extension": os.path.splitext(y)[1],
|
||||
"clearColor": self.clear_color,
|
||||
"data": processing_utils.encode_file_to_base64(y, type="model"),
|
||||
}
|
||||
|
||||
def deserialize(self, x):
|
||||
return processing_utils.decode_base64_to_file(x).name
|
||||
|
||||
def save_flagged(self, dir, label, data, encryption_key):
|
||||
"""
|
||||
Returns: (str) path to model file
|
||||
"""
|
||||
return self.save_flagged_file(dir, label, data['data'], encryption_key)
|
||||
|
||||
|
||||
def get_output_instance(iface: Interface):
|
||||
if isinstance(iface, str):
|
||||
shortcut = OutputComponent.get_all_shortcut_implementations()[iface]
|
||||
|
6
package.json
Normal file
6
package.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"three": "^0.138.2",
|
||||
"vitest": "^0.5.9"
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
"svelte": "^3.46.3",
|
||||
"svelte-check": "^2.4.1",
|
||||
"svelte-i18n": "^3.3.13",
|
||||
"vitest": "^0.3.2"
|
||||
"vitest": "^0.3.2",
|
||||
"three": "^0.138.2"
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import OutputTextbox from "./output/Textbox/config.js";
|
||||
import OutputVideo from "./output/Video/config.js";
|
||||
import OutputTimeSeries from "./output/TimeSeries/config.js";
|
||||
import OutputChatbot from "./output/Chatbot/config.js";
|
||||
import OutputModel from "./output/Model/config.js";
|
||||
|
||||
import StaticButton from "./static/Button/config.js";
|
||||
import StaticMarkdown from "./static/Markdown/config.js";
|
||||
@ -58,7 +59,8 @@ export const output_component_map = {
|
||||
textbox: OutputTextbox,
|
||||
timeseries: OutputTimeSeries,
|
||||
video: OutputVideo,
|
||||
chatbot: OutputChatbot
|
||||
chatbot: OutputChatbot,
|
||||
model: OutputModel
|
||||
};
|
||||
|
||||
export const static_component_map = {
|
||||
|
85
ui/packages/app/src/components/output/Model/Model.svelte
Normal file
85
ui/packages/app/src/components/output/Model/Model.svelte
Normal file
@ -0,0 +1,85 @@
|
||||
<script lang="ts">
|
||||
export let value: string;
|
||||
export let theme: string;
|
||||
|
||||
componentDidMount() {
|
||||
const canvas = document.getElementById("renderCanvas");
|
||||
const engine = new BABYLON.Engine(canvas, true);
|
||||
|
||||
this.scene = new BABYLON.Scene(engine);
|
||||
this.scene.createDefaultCameraOrLight();
|
||||
|
||||
const clearColor = this.props.value["clearColor"]
|
||||
this.scene.clearColor = new BABYLON.Color3(clearColor[0], clearColor[1], clearColor[2]);
|
||||
|
||||
this.addNewModel();
|
||||
|
||||
engine.runRenderLoop(() => {
|
||||
this.scene.render();
|
||||
});
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
engine.resize();
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
this.addNewModel();
|
||||
}
|
||||
|
||||
addNewModel() {
|
||||
// remove all existing models
|
||||
for (let mesh of this.scene.meshes) {
|
||||
mesh.dispose();
|
||||
}
|
||||
|
||||
// add new model
|
||||
let base64_model_content = this.props.value["data"];
|
||||
let raw_content = BABYLON.Tools.DecodeBase64(base64_model_content);
|
||||
let blob = new Blob([raw_content]);
|
||||
let url = URL.createObjectURL(blob);
|
||||
BABYLON.SceneLoader.Append("", url, this.scene, () => {
|
||||
this.scene.createDefaultCamera(true, true, true);
|
||||
}, undefined, undefined, this.props.value["extension"]);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.value) {
|
||||
return (
|
||||
<div className="output_model">
|
||||
<canvas id="renderCanvas"></canvas>
|
||||
<a href={this.props.value["data"]} download={this.props.value["name"]} className="download_link">Download {this.props.value["name"]}</a>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="output-image w-full h-60 flex justify-center items-center bg-gray-200 dark:bg-gray-600 relative"
|
||||
{theme}
|
||||
>
|
||||
<div className="output_model_example">{value}</div>;
|
||||
|
||||
<div className="output_model">
|
||||
<canvas id="renderCanvas"></canvas>
|
||||
<a href={this.props.value["data"]} download={value} className="download_link">Download {value}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// class ModelOutputExample extends ComponentExample {
|
||||
// render() {
|
||||
// return <div className="output_model_example">{this.props.value}</div>;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export {ModelOutput, ModelOutputExample};
|
5
ui/packages/app/src/components/output/Model/config.ts
Normal file
5
ui/packages/app/src/components/output/Model/config.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import Component from "./Model.svelte";
|
||||
|
||||
export default {
|
||||
component: Component
|
||||
};
|
Loading…
Reference in New Issue
Block a user