mirror of
https://github.com/hanzoskill/bot-meshyai.git
synced 2026-10-30 21:07:28 +00:00
102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Create a Meshy Image-to-3D task and download the resulting OBJ (and MTL if present).
|
|
|
|
Usage:
|
|
# Local image file
|
|
python3 image_to_3d_obj.py --image ./input.png --out-dir ./out
|
|
|
|
# Public URL
|
|
python3 image_to_3d_obj.py --image-url "https://example.com/input.png" --out-dir ./out
|
|
|
|
Env:
|
|
MESHY_API_KEY (required)
|
|
MESHY_BASE_URL (optional)
|
|
|
|
Docs:
|
|
https://docs.meshy.ai/en/api/image-to-3d
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
|
|
from meshy_client import MeshyError, create_task, poll_task, download, file_to_data_uri
|
|
|
|
|
|
def _slug(path_or_url: str, max_len: int = 64) -> str:
|
|
s = path_or_url.strip().lower()
|
|
s = re.sub(r"^https?://", "", s)
|
|
s = re.sub(r"[^a-z0-9]+", "-", s)
|
|
s = re.sub(r"-+", "-", s).strip("-")
|
|
return (s[:max_len] or "meshy")
|
|
|
|
|
|
def main() -> None:
|
|
ap = argparse.ArgumentParser()
|
|
src = ap.add_mutually_exclusive_group(required=True)
|
|
src.add_argument("--image", help="Local .png/.jpg/.jpeg path")
|
|
src.add_argument("--image-url", help="Public URL or data: URI")
|
|
|
|
ap.add_argument("--ai-model", default="latest")
|
|
ap.add_argument("--model-type", default=None, choices=[None, "standard", "lowpoly"], help="standard|lowpoly")
|
|
ap.add_argument("--topology", default=None, choices=[None, "triangle", "quad"])
|
|
ap.add_argument("--target-polycount", type=int, default=None)
|
|
ap.add_argument("--should-texture", action="store_true", help="Force should_texture=true")
|
|
ap.add_argument("--no-texture", action="store_true", help="Force should_texture=false")
|
|
|
|
ap.add_argument("--out-dir", default="./meshy-out")
|
|
ap.add_argument("--timeout", type=int, default=1800)
|
|
args = ap.parse_args()
|
|
|
|
image_url = args.image_url
|
|
if args.image:
|
|
image_url = file_to_data_uri(args.image)
|
|
|
|
payload = {
|
|
"image_url": image_url,
|
|
"ai_model": args.ai_model,
|
|
}
|
|
if args.model_type:
|
|
payload["model_type"] = args.model_type
|
|
if args.topology:
|
|
payload["topology"] = args.topology
|
|
if args.target_polycount is not None:
|
|
payload["target_polycount"] = args.target_polycount
|
|
|
|
if args.should_texture and args.no_texture:
|
|
raise MeshyError("Choose only one of --should-texture or --no-texture")
|
|
if args.should_texture:
|
|
payload["should_texture"] = True
|
|
if args.no_texture:
|
|
payload["should_texture"] = False
|
|
|
|
task_id = create_task("/openapi/v1/image-to-3d", payload)
|
|
task = poll_task(f"/openapi/v1/image-to-3d/{task_id}", timeout_s=args.timeout)
|
|
|
|
status = str(task.get("status", "")).upper()
|
|
if status != "SUCCEEDED":
|
|
raise MeshyError(f"Image-to-3D task did not succeed. status={status} task={task}")
|
|
|
|
model_urls = task.get("model_urls") or {}
|
|
if not isinstance(model_urls, dict) or not model_urls.get("obj"):
|
|
raise MeshyError(f"No model_urls.obj in task response: {task}")
|
|
|
|
out_dir = os.path.join(args.out_dir, f"image-to-3d_{task_id}_{_slug(args.image or args.image_url)}")
|
|
os.makedirs(out_dir, exist_ok=True)
|
|
|
|
obj_path = os.path.join(out_dir, "model.obj")
|
|
download(str(model_urls["obj"]), obj_path)
|
|
print(obj_path)
|
|
|
|
# If present, also download MTL.
|
|
if model_urls.get("mtl"):
|
|
mtl_path = os.path.join(out_dir, "model.mtl")
|
|
download(str(model_urls["mtl"]), mtl_path)
|
|
print(mtl_path)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|