modly-image-to-3d
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseModly Image-to-3D Skill
Modly 图像转3D技能
Skill by ara.so — Daily 2026 Skills collection.
Modly is a local, open-source desktop application (Windows/Linux) that converts photos into 3D mesh models using AI models running entirely on your GPU — no cloud, no API keys required.
该技能来自ara.so — 2026每日技能合集。
Modly是一款本地开源桌面应用(支持Windows/Linux),它依靠完全在本地GPU上运行的AI模型将照片转换为3D网格模型——无需云端服务,也不需要API密钥。
Architecture Overview
架构概述
modly/
├── src/ # Electron + TypeScript frontend
│ ├── main/ # Electron main process
│ ├── renderer/ # React UI (renderer process)
│ └── preload/ # IPC bridge
├── api/ # Python FastAPI backend
│ ├── generator.py # Core generation logic
│ └── requirements.txt
├── resources/
│ └── icons/
├── launcher.bat # Windows quick-start
├── launcher.sh # Linux quick-start
└── package.jsonThe app runs as an Electron shell over a local Python FastAPI server. Extensions are GitHub repos with a + that plug into the extension system.
manifest.jsongenerator.pymodly/
├── src/ # Electron + TypeScript 前端
│ ├── main/ # Electron 主进程
│ ├── renderer/ # React UI(渲染进程)
│ └── preload/ # IPC 桥接层
├── api/ # Python FastAPI 后端
│ ├── generator.py # 核心生成逻辑
│ └── requirements.txt
├── resources/
│ └── icons/
├── launcher.bat # Windows 快速启动脚本
├── launcher.sh # Linux 快速启动脚本
└── package.json该应用以Electron外壳运行在本地Python FastAPI服务器之上。扩展是包含和的GitHub仓库,可接入扩展系统。
manifest.jsongenerator.pyInstallation
安装步骤
Quick start (no build required)
快速启动(无需构建)
bash
undefinedbash
undefinedWindows
Windows
launcher.bat
launcher.bat
Linux
Linux
chmod +x launcher.sh
./launcher.sh
undefinedchmod +x launcher.sh
./launcher.sh
undefinedDevelopment setup
开发环境搭建
bash
undefinedbash
undefined1. Clone
1. 克隆仓库
git clone https://github.com/lightningpixel/modly
cd modly
git clone https://github.com/lightningpixel/modly
cd modly
2. Install JS dependencies
2. 安装JS依赖
npm install
npm install
3. Set up Python backend
3. 配置Python后端
cd api
python -m venv .venv
cd api
python -m venv .venv
Activate (Windows)
激活虚拟环境(Windows)
.venv\Scripts\activate
.venv\Scripts\activate
Activate (Linux/macOS)
激活虚拟环境(Linux/macOS)
source .venv/bin/activate
pip install -r requirements.txt
cd ..
source .venv/bin/activate
pip install -r requirements.txt
cd ..
4. Run dev mode (starts Electron + Python backend)
4. 启动开发模式(同时启动Electron和Python后端)
npm run dev
undefinednpm run dev
undefinedProduction build
生产环境构建
bash
undefinedbash
undefinedBuild installers for current platform
为当前平台构建安装包
npm run build
npm run build
Output goes to dist/
输出文件在dist/目录下
---
---Key npm Scripts
关键npm脚本
bash
npm run dev # Start app in development mode (hot reload)
npm run build # Package app for distribution
npm run lint # Run ESLint
npm run typecheck # TypeScript type checkingbash
npm run dev # 以开发模式启动应用(支持热重载)
npm run build # 打包应用用于分发
npm run lint # 运行ESLint检查
npm run typecheck # TypeScript类型检查Extension System
扩展系统
Extensions are GitHub repositories containing:
- — metadata and model variants
manifest.json - — generation logic implementing the Modly extension interface
generator.py
扩展是包含以下文件的GitHub仓库:
- — 元数据和模型变体配置
manifest.json - — 实现Modly扩展接口的生成逻辑
generator.py
manifest.json structure
manifest.json 结构
json
{
"name": "My 3D Extension",
"id": "my-extension-id",
"description": "Generates 3D models using XYZ model",
"version": "1.0.0",
"author": "Your Name",
"repository": "https://github.com/yourname/my-modly-extension",
"variants": [
{
"id": "model-small",
"name": "Small (faster)",
"description": "Lighter variant for faster generation",
"size_gb": 4.2,
"vram_gb": 6,
"files": [
{
"url": "https://huggingface.co/yourorg/yourmodel/resolve/main/weights.safetensors",
"filename": "weights.safetensors",
"sha256": "abc123..."
}
]
}
]
}json
{
"name": "My 3D Extension",
"id": "my-extension-id",
"description": "Generates 3D models using XYZ model",
"version": "1.0.0",
"author": "Your Name",
"repository": "https://github.com/yourname/my-modly-extension",
"variants": [
{
"id": "model-small",
"name": "Small (faster)",
"description": "Lighter variant for faster generation",
"size_gb": 4.2,
"vram_gb": 6,
"files": [
{
"url": "https://huggingface.co/yourorg/yourmodel/resolve/main/weights.safetensors",
"filename": "weights.safetensors",
"sha256": "abc123..."
}
]
}
]
}generator.py interface
generator.py 接口
python
undefinedpython
undefinedapi/extensions/<extension-id>/generator.py
api/extensions/<extension-id>/generator.py
Required interface every extension must implement
所有扩展必须实现的必填接口
import sys
import json
from pathlib import Path
def generate(
image_path: str,
output_path: str,
variant_id: str,
models_dir: str,
**kwargs
) -> dict:
"""
Required entry point for all Modly extensions.
Args:
image_path: Path to input image file
output_path: Path where output .glb/.obj should be saved
variant_id: Which model variant to use
models_dir: Directory where downloaded model weights live
Returns:
dict with keys:
success (bool)
output_file (str) — path to generated mesh
error (str, optional)
"""
try:
# Load your model weights
weights = Path(models_dir) / variant_id / "weights.safetensors"
# Run your inference
mesh = run_inference(str(weights), image_path)
# Save output
mesh.export(output_path)
return {
"success": True,
"output_file": output_path
}
except Exception as e:
return {
"success": False,
"error": str(e)
}undefinedimport sys
import json
from pathlib import Path
def generate(
image_path: str,
output_path: str,
variant_id: str,
models_dir: str,
**kwargs
) -> dict:
"""
所有Modly扩展的必填入口函数。
参数:
image_path: 输入图像文件的路径
output_path: 输出.glb/.obj文件的保存路径
variant_id: 要使用的模型变体ID
models_dir: 已下载模型权重的存储目录
返回:
包含以下键的字典:
success (bool) — 是否成功
output_file (str) — 生成的网格文件路径
error (str, 可选) — 错误信息
"""
try:
# 加载模型权重
weights = Path(models_dir) / variant_id / "weights.safetensors"
# 运行推理
mesh = run_inference(str(weights), image_path)
# 保存输出结果
mesh.export(output_path)
return {
"success": True,
"output_file": output_path
}
except Exception as e:
return {
"success": False,
"error": str(e)
}undefinedInstalling an extension (UI flow)
安装扩展(UI流程)
- Open Modly → go to Models page
- Click Install from GitHub
- Paste the HTTPS URL, e.g.
https://github.com/lightningpixel/modly-hunyuan3d-mini-extension - After install, click Download on the desired model variant
- Select the installed model and upload an image to generate
- 打开Modly → 进入模型页面
- 点击从GitHub安装
- 粘贴HTTPS链接,例如
https://github.com/lightningpixel/modly-hunyuan3d-mini-extension - 安装完成后,点击所需模型变体旁的下载
- 选择已安装的模型并上传图像即可开始生成
Official Extensions
官方扩展
| Extension | Model |
|---|---|
| modly-hunyuan3d-mini-extension | Hunyuan3D 2 Mini |
| 扩展 | 模型 |
|---|---|
| modly-hunyuan3d-mini-extension | Hunyuan3D 2 Mini |
Python Backend API (FastAPI)
Python后端API(FastAPI)
The backend runs locally. Key endpoints used by the Electron frontend:
python
undefined后端在本地运行。Electron前端调用的关键接口:
python
undefinedTypical backend route patterns (api/main.py or similar)
典型后端路由示例(api/main.py或类似文件)
GET /extensions — list installed extensions
GET /extensions — 列出已安装的扩展
GET /extensions/{id} — get extension details + variants
GET /extensions/{id} — 获取扩展详情和变体信息
POST /extensions/install — install extension from GitHub URL
POST /extensions/install — 从GitHub URL安装扩展
POST /generate — trigger 3D generation
POST /generate — 触发3D模型生成
GET /generate/status — poll generation progress
GET /generate/status — 查询生成进度
GET /models — list downloaded model variants
GET /models — 列出已下载的模型变体
POST /models/download — download a model variant
POST /models/download — 下载模型变体
undefinedundefinedCalling the backend from Electron (IPC pattern)
从Electron调用后端(IPC模式)
typescript
// src/preload/index.ts — exposing backend calls to renderer
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('modly', {
generate: (imagePath: string, extensionId: string, variantId: string) =>
ipcRenderer.invoke('generate', { imagePath, extensionId, variantId }),
installExtension: (repoUrl: string) =>
ipcRenderer.invoke('install-extension', { repoUrl }),
listExtensions: () =>
ipcRenderer.invoke('list-extensions'),
})typescript
// src/main/ipc-handlers.ts — main process handling
import { ipcMain } from 'electron'
ipcMain.handle('generate', async (_event, { imagePath, extensionId, variantId }) => {
const response = await fetch('http://localhost:PORT/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image_path: imagePath, extension_id: extensionId, variant_id: variantId }),
})
return response.json()
})typescript
// src/renderer/components/GenerateButton.tsx — UI usage
declare global {
interface Window {
modly: {
generate: (imagePath: string, extensionId: string, variantId: string) => Promise<{ success: boolean; output_file?: string; error?: string }>
installExtension: (repoUrl: string) => Promise<{ success: boolean }>
listExtensions: () => Promise<Extension[]>
}
}
}
async function handleGenerate(imagePath: string) {
const result = await window.modly.generate(
imagePath,
'modly-hunyuan3d-mini-extension',
'hunyuan3d-mini-turbo'
)
if (result.success) {
console.log('Mesh saved to:', result.output_file)
} else {
console.error('Generation failed:', result.error)
}
}typescript
// src/preload/index.ts — 向后端暴露调用接口给渲染进程
import { contextBridge, ipcRenderer } from 'electron'
contextBridge.exposeInMainWorld('modly', {
generate: (imagePath: string, extensionId: string, variantId: string) =>
ipcRenderer.invoke('generate', { imagePath, extensionId, variantId }),
installExtension: (repoUrl: string) =>
ipcRenderer.invoke('install-extension', { repoUrl }),
listExtensions: () =>
ipcRenderer.invoke('list-extensions'),
})typescript
// src/main/ipc-handlers.ts — 主进程处理逻辑
import { ipcMain } from 'electron'
ipcMain.handle('generate', async (_event, { imagePath, extensionId, variantId }) => {
const response = await fetch('http://localhost:PORT/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ image_path: imagePath, extension_id: extensionId, variant_id: variantId }),
})
return response.json()
})typescript
// src/renderer/components/GenerateButton.tsx — UI层调用示例
declare global {
interface Window {
modly: {
generate: (imagePath: string, extensionId: string, variantId: string) => Promise<{ success: boolean; output_file?: string; error?: string }>
installExtension: (repoUrl: string) => Promise<{ success: boolean }>
listExtensions: () => Promise<Extension[]>
}
}
}
async function handleGenerate(imagePath: string) {
const result = await window.modly.generate(
imagePath,
'modly-hunyuan3d-mini-extension',
'hunyuan3d-mini-turbo'
)
if (result.success) {
console.log('网格模型已保存至:', result.output_file)
} else {
console.error('生成失败:', result.error)
}
}Writing a Custom Extension
编写自定义扩展
Minimal extension repository structure
最小化扩展仓库结构
my-modly-extension/
├── manifest.json
└── generator.pymy-modly-extension/
├── manifest.json
└── generator.pyExample: wrapping a HuggingFace diffusion model
示例:封装HuggingFace扩散模型
python
undefinedpython
undefinedgenerator.py
generator.py
import torch
from PIL import Image
from pathlib import Path
def generate(image_path, output_path, variant_id, models_dir, **kwargs):
device = "cuda" if torch.cuda.is_available() else "cpu"
weights_dir = Path(models_dir) / variant_id
try:
# Load model (example pattern)
from your_model_lib import ImageTo3DPipeline
pipe = ImageTo3DPipeline.from_pretrained(
str(weights_dir),
torch_dtype=torch.float16
).to(device)
image = Image.open(image_path).convert("RGB")
with torch.no_grad():
mesh = pipe(image).mesh
mesh.export(output_path)
return {"success": True, "output_file": output_path}
except Exception as e:
return {"success": False, "error": str(e)}
---import torch
from PIL import Image
from pathlib import Path
def generate(image_path, output_path, variant_id, models_dir, **kwargs):
device = "cuda" if torch.cuda.is_available() else "cpu"
weights_dir = Path(models_dir) / variant_id
try:
# 加载模型(示例代码)
from your_model_lib import ImageTo3DPipeline
pipe = ImageTo3DPipeline.from_pretrained(
str(weights_dir),
torch_dtype=torch.float16
).to(device)
image = Image.open(image_path).convert("RGB")
with torch.no_grad():
mesh = pipe(image).mesh
mesh.export(output_path)
return {"success": True, "output_file": output_path}
except Exception as e:
return {"success": False, "error": str(e)}
---Configuration & Environment
配置与环境
Modly runs fully locally — no environment variables or API keys needed. GPU/CUDA is auto-detected by PyTorch in extensions.
Relevant configuration lives in:
package.json # Electron app metadata, build targets
api/requirements.txt # Python dependencies for backendIf you need to configure the backend port or extension directory, check the Electron main process config (typically ) for constants like or .
src/main/index.tsAPI_PORTEXTENSIONS_DIRModly完全在本地运行——无需环境变量或API密钥。扩展中的PyTorch会自动检测GPU/CUDA。
相关配置文件:
package.json # Electron应用元数据、构建目标
api/requirements.txt # 后端Python依赖如果需要配置后端端口或扩展目录,请查看Electron主进程配置(通常是)中的常量,例如或。
src/main/index.tsAPI_PORTEXTENSIONS_DIRCommon Patterns
常见模式
Check if CUDA is available in an extension
在扩展中检查CUDA是否可用
python
import torch
def get_device():
if torch.cuda.is_available():
print(f"Using GPU: {torch.cuda.get_device_name(0)}")
return "cuda"
print("No GPU found, falling back to CPU (slow)")
return "cpu"python
import torch
def get_device():
if torch.cuda.is_available():
print(f"使用GPU: {torch.cuda.get_device_name(0)}")
return "cuda"
print("未检测到GPU,回退至CPU(速度较慢)")
return "cpu"Progress reporting from generator.py
从generator.py上报进度
python
import sys
import json
def report_progress(percent: int, message: str):
"""Write progress to stdout so Modly can display it."""
print(json.dumps({"progress": percent, "message": message}), flush=True)
def generate(image_path, output_path, variant_id, models_dir, **kwargs):
report_progress(0, "Loading model...")
# ... load model ...
report_progress(30, "Processing image...")
# ... inference ...
report_progress(90, "Exporting mesh...")
# ... export ...
report_progress(100, "Done")
return {"success": True, "output_file": output_path}python
import sys
import json
def report_progress(percent: int, message: str):
"""将进度写入标准输出,以便Modly显示。"""
print(json.dumps({"progress": percent, "message": message}), flush=True)
def generate(image_path, output_path, variant_id, models_dir, **kwargs):
report_progress(0, "加载模型中...")
# ... 加载模型 ...
report_progress(30, "处理图像中...")
# ... 推理计算 ...
report_progress(90, "导出网格模型中...")
# ... 导出结果 ...
report_progress(100, "完成")
return {"success": True, "output_file": output_path}Adding a new page in the renderer (React)
在渲染进程中添加新页面(React)
typescript
// src/renderer/pages/MyPage.tsx
import React, { useEffect, useState } from 'react'
interface Extension {
id: string
name: string
description: string
}
export default function MyPage() {
const [extensions, setExtensions] = useState<Extension[]>([])
useEffect(() => {
window.modly.listExtensions().then(setExtensions)
}, [])
return (
<div>
<h1>Installed Extensions</h1>
{extensions.map(ext => (
<div key={ext.id}>
<h2>{ext.name}</h2>
<p>{ext.description}</p>
</div>
))}
</div>
)
}typescript
// src/renderer/pages/MyPage.tsx
import React, { useEffect, useState } from 'react'
interface Extension {
id: string
name: string
description: string
}
export default function MyPage() {
const [extensions, setExtensions] = useState<Extension[]>([])
useEffect(() => {
window.modly.listExtensions().then(setExtensions)
}, [])
return (
<div>
<h1>已安装扩展</h1>
{extensions.map(ext => (
<div key={ext.id}>
<h2>{ext.name}</h2>
<p>{ext.description}</p>
</div>
))}
</div>
)
}Troubleshooting
故障排除
| Problem | Fix |
|---|---|
| Ensure venv is set up: |
| CUDA out of memory | Use a smaller model variant or close other GPU processes |
| Extension install fails | Verify the GitHub URL is HTTPS and the repo contains |
| Generation hangs | Check that your GPU drivers and CUDA toolkit match the PyTorch version in |
| App won't launch on Linux | Make |
| Model download stalls | Check disk space; large models (4–10 GB) need adequate free space |
| Ensure PyTorch is in |
| 问题 | 解决方法 |
|---|---|
| 确保虚拟环境已正确配置: |
| CUDA内存不足 | 使用更小的模型变体,或关闭其他占用GPU的进程 |
| 扩展安装失败 | 验证GitHub URL是HTTPS格式,且仓库根目录包含 |
| 生成过程卡住 | 检查GPU驱动和CUDA工具包版本是否与 |
| Linux下应用无法启动 | 确保 |
| 模型下载停滞 | 检查磁盘空间;大型模型(4–10 GB)需要足够的可用空间 |
扩展中找不到 | 确保PyTorch已添加到 |
Verifying GPU is detected
验证GPU是否被检测到
bash
cd api
source .venv/bin/activate # or .venv\Scripts\activate on Windows
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'no GPU')"bash
cd api
source .venv/bin/activate # Windows系统使用.venv\Scripts\activate
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'no GPU')"Resources
相关资源
- Homepage: https://modly3d.app
- Releases: https://github.com/lightningpixel/modly/releases/latest
- Official extension: https://github.com/lightningpixel/modly-hunyuan3d-mini-extension
- Discord: https://discord.gg/FjzjRgweVk
- License: MIT (attribution required — credit Modly + Lightning Pixel in forks)
- 官网: https://modly3d.app
- 发布版本: https://github.com/lightningpixel/modly/releases/latest
- 官方扩展: https://github.com/lightningpixel/modly-hunyuan3d-mini-extension
- Discord社区: https://discord.gg/FjzjRgweVk
- 许可证: MIT(衍生作品需要注明出处——需标注Modly和Lightning Pixel)