feat: Add MoGe-2 support and enhance depth plane functionality
- Introduced new MoGe-2 depth plane group with geometry and material displacement options. - Updated existing depth plane group to improve background depth handling and subdivision settings. - Enhanced tests to validate MoGe-2 models and ensure proper integration with existing systems. - Modified installation environment to include new dependencies for MoGe-2 and utils3d. - Updated vendor lock file to include new artifacts for MoGe and utils3d. - Refactored download and model handling to accommodate new MoGe-2 models. - Improved validation scripts for depth plane assets to ensure compliance with new specifications.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
@@ -9,6 +10,7 @@ import bpy
|
||||
from . import client
|
||||
from .models import (
|
||||
BEN2_MODEL,
|
||||
MOGE2_MODEL,
|
||||
get_downloadable_model,
|
||||
get_model,
|
||||
selected_model_key,
|
||||
@@ -17,6 +19,8 @@ from .models import (
|
||||
|
||||
RECONSTRUCTION_JOB = "RECONSTRUCT"
|
||||
IMAGE_DEPTH_JOB = "IMAGE_DEPTH"
|
||||
MOGE2_DEPTH_JOB = "MOGE2_DEPTH"
|
||||
MOGE2_DEBUG_JOB = "MOGE2_DEBUG"
|
||||
IMAGE_MESH_JOB = "IMAGE_MESH"
|
||||
BACKGROUND_REMOVAL_JOB = "REMOVE_BACKGROUND"
|
||||
IMAGE_BACKGROUND_JOB = "IMAGE_BACKGROUND"
|
||||
@@ -26,6 +30,7 @@ DA3_IMAGE_JOBS = frozenset({IMAGE_DEPTH_JOB, IMAGE_MESH_JOB})
|
||||
IMAGE_EMPTY_JOBS = frozenset(
|
||||
{
|
||||
*DA3_IMAGE_JOBS,
|
||||
MOGE2_DEPTH_JOB,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
ORIGINAL_FRAMES_JOB,
|
||||
@@ -34,6 +39,7 @@ IMAGE_EMPTY_JOBS = frozenset(
|
||||
PRODUCTION_AI_JOBS = frozenset(
|
||||
{
|
||||
*DA3_IMAGE_JOBS,
|
||||
MOGE2_DEPTH_JOB,
|
||||
IMAGE_BACKGROUND_JOB,
|
||||
ORIGINAL_BACKGROUND_JOB,
|
||||
}
|
||||
@@ -156,6 +162,8 @@ class RunJob(bpy.types.Operator):
|
||||
("DOWNLOAD_MODEL", "Download Model", ""),
|
||||
(RECONSTRUCTION_JOB, "Export DA3 Result", ""),
|
||||
(IMAGE_DEPTH_JOB, "Convert Image to Depth Plane", ""),
|
||||
(MOGE2_DEPTH_JOB, "Convert Image with MoGe-2", ""),
|
||||
(MOGE2_DEBUG_JOB, "Test MoGe-2 API", ""),
|
||||
(IMAGE_MESH_JOB, "Convert Image to Surface", ""),
|
||||
(BACKGROUND_REMOVAL_JOB, "Test BEN2 Background Removal", ""),
|
||||
(IMAGE_BACKGROUND_JOB, "Remove Image Empty Background", ""),
|
||||
@@ -210,6 +218,12 @@ class RunJob(bpy.types.Operator):
|
||||
default=False,
|
||||
options={"HIDDEN", "SKIP_SAVE"},
|
||||
)
|
||||
snapform_moge_resolution_level: bpy.props.IntProperty(
|
||||
default=5,
|
||||
min=0,
|
||||
max=9,
|
||||
options={"HIDDEN", "SKIP_SAVE"},
|
||||
)
|
||||
|
||||
def execute(self, context):
|
||||
global _active_job
|
||||
@@ -236,6 +250,8 @@ class RunJob(bpy.types.Operator):
|
||||
export_format = "mini_npz"
|
||||
elif self.job_type == IMAGE_MESH_JOB:
|
||||
export_format = "mesh_data"
|
||||
elif self.job_type in {MOGE2_DEPTH_JOB, MOGE2_DEBUG_JOB}:
|
||||
export_format = "moge_npz"
|
||||
|
||||
job = ActiveJob(
|
||||
kind=self.job_type,
|
||||
@@ -300,12 +316,20 @@ class RunJob(bpy.types.Operator):
|
||||
if job.kind == "INSTALL_ENV":
|
||||
client.stop_service()
|
||||
job.log_file = job.log_path.open("w", encoding="utf-8")
|
||||
process_environment = None
|
||||
if job.kind == "INSTALL_ENV":
|
||||
process_environment = os.environ.copy()
|
||||
process_environment["PYTHONIOENCODING"] = (
|
||||
"utf-8:backslashreplace"
|
||||
)
|
||||
process_environment["PYTHONUTF8"] = "1"
|
||||
job.process = subprocess.Popen(
|
||||
command,
|
||||
stdout=job.log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=working_directory,
|
||||
creationflags=creation_flags,
|
||||
env=process_environment,
|
||||
)
|
||||
job.started = True
|
||||
if job.cancelled:
|
||||
@@ -401,6 +425,35 @@ class RunJob(bpy.types.Operator):
|
||||
),
|
||||
paths,
|
||||
)
|
||||
if self.job_type in {MOGE2_DEPTH_JOB, MOGE2_DEBUG_JOB}:
|
||||
model_key = self.snapform_model_key or MOGE2_MODEL.key
|
||||
model = get_downloadable_model(model_key)
|
||||
if not client.model_ready(model_key):
|
||||
raise RuntimeError(f"Download {model.label} first")
|
||||
if remove_background and not client.model_ready(BEN2_MODEL.key):
|
||||
raise RuntimeError(f"Download {BEN2_MODEL.label} first")
|
||||
return (
|
||||
client.moge_request(
|
||||
settings,
|
||||
paths["status"],
|
||||
paths["result"],
|
||||
input_path=input_path,
|
||||
resolution_level=(
|
||||
self.snapform_moge_resolution_level
|
||||
if self.job_type == MOGE2_DEPTH_JOB
|
||||
else settings.moge_resolution_level
|
||||
),
|
||||
include_points=self.job_type == MOGE2_DEBUG_JOB,
|
||||
remove_background=(
|
||||
self.job_type == MOGE2_DEPTH_JOB
|
||||
and remove_background
|
||||
),
|
||||
start_frame=start_frame,
|
||||
device=self._task_device(settings),
|
||||
model_key=model_key,
|
||||
),
|
||||
paths,
|
||||
)
|
||||
if self.job_type not in {
|
||||
RECONSTRUCTION_JOB,
|
||||
IMAGE_DEPTH_JOB,
|
||||
@@ -478,6 +531,8 @@ class RunJob(bpy.types.Operator):
|
||||
return f"Starting {settings.export_format} export"
|
||||
if self.job_type == IMAGE_DEPTH_JOB:
|
||||
return "Starting depth estimation"
|
||||
if self.job_type in {MOGE2_DEPTH_JOB, MOGE2_DEBUG_JOB}:
|
||||
return "Starting MoGe-2 inference"
|
||||
if self.job_type == IMAGE_MESH_JOB:
|
||||
return "Starting perspective surface generation"
|
||||
if self.job_type == BACKGROUND_REMOVAL_JOB:
|
||||
@@ -626,6 +681,7 @@ class RunJob(bpy.types.Operator):
|
||||
if job.kind in {
|
||||
RECONSTRUCTION_JOB,
|
||||
BACKGROUND_REMOVAL_JOB,
|
||||
MOGE2_DEBUG_JOB,
|
||||
}:
|
||||
try:
|
||||
open_job_directory(job.result_directory)
|
||||
@@ -635,8 +691,10 @@ class RunJob(bpy.types.Operator):
|
||||
message = (
|
||||
f"Exported {job.export_format} to {job.result_directory}"
|
||||
)
|
||||
else:
|
||||
elif job.kind == BACKGROUND_REMOVAL_JOB:
|
||||
message = f"Removed background to {job.result_directory}"
|
||||
else:
|
||||
message = f"Exported MoGe-2 data to {job.result_directory}"
|
||||
self.report({"INFO"}, message)
|
||||
if job.kind == IMAGE_DEPTH_JOB:
|
||||
try:
|
||||
@@ -653,6 +711,23 @@ class RunJob(bpy.types.Operator):
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
status["message"] = "Depth plane is ready"
|
||||
if job.kind == MOGE2_DEPTH_JOB:
|
||||
try:
|
||||
settings.status = "Building MoGe-2 depth plane"
|
||||
settings.progress = 0.96
|
||||
self._redraw_ui(context, force=True)
|
||||
self._build_moge2_depth_plane(job)
|
||||
except (OSError, RuntimeError, ValueError) as import_error:
|
||||
settings.status = "MoGe-2 depth import failed"
|
||||
settings.progress = 0.0
|
||||
self.report({"ERROR"}, str(import_error))
|
||||
client.mark_service_job_complete(
|
||||
client.job_id_from_status(job.status_path)
|
||||
)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
status["message"] = "MoGe-2 depth plane is ready"
|
||||
if job.kind == IMAGE_MESH_JOB:
|
||||
try:
|
||||
settings.status = "Building perspective surface"
|
||||
@@ -790,6 +865,35 @@ class RunJob(bpy.types.Operator):
|
||||
if "FINISHED" not in undo_result:
|
||||
raise RuntimeError("Unable to create the depth plane Undo step")
|
||||
|
||||
def _build_moge2_depth_plane(self, job):
|
||||
result_directory = job.result_directory
|
||||
is_sequence = bool(list(result_directory.glob("frame.*")))
|
||||
foreground_sequence = bool(
|
||||
list(result_directory.glob("foreground.*.png"))
|
||||
)
|
||||
result = bpy.ops.snapform.build_moge2_depth_plane(
|
||||
snapform_source_object_name=job.source_object_name,
|
||||
snapform_npz_path=str(result_directory / "results.npz"),
|
||||
snapform_background_path=(
|
||||
str(result_directory)
|
||||
if job.remove_background and foreground_sequence
|
||||
else str(result_directory / "foreground.png")
|
||||
if job.remove_background
|
||||
else ""
|
||||
),
|
||||
snapform_color_frames_dir=(
|
||||
str(result_directory) if is_sequence else ""
|
||||
),
|
||||
snapform_data_frames_dir=str(result_directory),
|
||||
snapform_delete_source=job.delete_source,
|
||||
snapform_unshaded=job.unshaded,
|
||||
)
|
||||
if "FINISHED" not in result:
|
||||
raise RuntimeError("Unable to build the MoGe-2 depth plane")
|
||||
undo_result = bpy.ops.ed.undo_push(message="Convert to Depth Plane")
|
||||
if "FINISHED" not in undo_result:
|
||||
raise RuntimeError("Unable to create the depth plane Undo step")
|
||||
|
||||
def _apply_removed_background(self, job, push_undo=True):
|
||||
result_directory = job.result_directory
|
||||
foreground_paths = sorted(result_directory.glob("foreground.*.png"))
|
||||
|
||||
Reference in New Issue
Block a user