feat: Implement background depth distance feature for depth processing
- Added `background_depth_distance` property to `ActiveJob` and `RunJob` classes to manage background depth settings. - Updated job execution to handle background depth distance, allowing for fixed negative displacement in depth calculations. - Introduced new functions in the worker module to apply background depth distance to depth frames and manage original background depth. - Enhanced tests to cover new functionality, ensuring correct behavior of background depth processing and reference depth extraction. - Refactored job submission to run in a separate thread, improving responsiveness during execution.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
@@ -76,9 +77,13 @@ class ActiveJob:
|
||||
displacement_via: str = "MODIFIER"
|
||||
unshaded: bool = False
|
||||
remove_background: bool = False
|
||||
background_depth_distance: float = 0.0
|
||||
temporary_input_path: Path | None = None
|
||||
cancelled: bool = False
|
||||
uses_host: bool = False
|
||||
started: bool = False
|
||||
start_error: str = ""
|
||||
start_thread: threading.Thread | None = None
|
||||
|
||||
|
||||
_active_job = None
|
||||
@@ -92,12 +97,14 @@ def cancel_active_job():
|
||||
if _active_job is None:
|
||||
return
|
||||
_active_job.cancelled = True
|
||||
if not getattr(_active_job, "started", True):
|
||||
return
|
||||
if _active_job.uses_host:
|
||||
try:
|
||||
runtime.cancel_host_job(_active_job.status_path.stem)
|
||||
except RuntimeError:
|
||||
pass
|
||||
elif _active_job.process.poll() is None:
|
||||
elif _active_job.process is not None and _active_job.process.poll() is None:
|
||||
_active_job.process.terminate()
|
||||
|
||||
|
||||
@@ -190,8 +197,9 @@ class RunJob(bpy.types.Operator):
|
||||
default=False,
|
||||
options={"HIDDEN", "SKIP_SAVE"},
|
||||
)
|
||||
snapform_remove_background: bpy.props.BoolProperty(
|
||||
default=False,
|
||||
snapform_background_depth_distance: bpy.props.FloatProperty(
|
||||
default=0.0,
|
||||
min=0.0,
|
||||
options={"HIDDEN", "SKIP_SAVE"},
|
||||
)
|
||||
|
||||
@@ -205,39 +213,18 @@ class RunJob(bpy.types.Operator):
|
||||
settings = context.scene.snapform_settings
|
||||
try:
|
||||
command, paths = self._prepare_command(context, settings)
|
||||
uses_host = isinstance(command, runtime.HostJobRequest)
|
||||
log_path = runtime.host_log_path() if uses_host else paths["log"]
|
||||
working_directory = runtime.storage_root()
|
||||
creation_flags = runtime.creation_flags()
|
||||
except (OSError, RuntimeError, ValueError) as error:
|
||||
self.report({"ERROR"}, str(error))
|
||||
return {"CANCELLED"}
|
||||
|
||||
uses_host = isinstance(command, runtime.HostJobRequest)
|
||||
log_file = None
|
||||
log_path = paths["log"]
|
||||
try:
|
||||
if uses_host:
|
||||
runtime.submit_host_job(command)
|
||||
process = None
|
||||
log_path = runtime.host_log_path()
|
||||
else:
|
||||
if self.job_type == "INSTALL_RUNTIME":
|
||||
runtime.stop_host()
|
||||
log_file = paths["log"].open("w", encoding="utf-8")
|
||||
process = subprocess.Popen(
|
||||
command,
|
||||
stdout=log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=runtime.storage_root(),
|
||||
creationflags=runtime.creation_flags(),
|
||||
)
|
||||
except (OSError, RuntimeError) as error:
|
||||
if log_file is not None:
|
||||
log_file.close()
|
||||
self.report({"ERROR"}, f"Unable to start task: {error}")
|
||||
return {"CANCELLED"}
|
||||
|
||||
_active_job = ActiveJob(
|
||||
job = ActiveJob(
|
||||
kind=self.job_type,
|
||||
process=process,
|
||||
log_file=log_file,
|
||||
process=None,
|
||||
log_file=None,
|
||||
log_path=log_path,
|
||||
status_path=paths["status"],
|
||||
result_directory=paths["result"],
|
||||
@@ -253,7 +240,8 @@ class RunJob(bpy.types.Operator):
|
||||
delete_source=self.snapform_delete_source,
|
||||
displacement_via=self.snapform_displacement_via,
|
||||
unshaded=self.snapform_unshaded,
|
||||
remove_background=self.snapform_remove_background,
|
||||
remove_background=self.snapform_background_depth_distance > 0.0,
|
||||
background_depth_distance=self.snapform_background_depth_distance,
|
||||
temporary_input_path=(
|
||||
Path(self.snapform_input_path)
|
||||
if self.job_type in {
|
||||
@@ -267,6 +255,7 @@ class RunJob(bpy.types.Operator):
|
||||
),
|
||||
uses_host=uses_host,
|
||||
)
|
||||
_active_job = job
|
||||
settings.status = self._starting_message(settings)
|
||||
settings.progress = 0.0
|
||||
self._timer = context.window_manager.event_timer_add(
|
||||
@@ -275,13 +264,56 @@ class RunJob(bpy.types.Operator):
|
||||
)
|
||||
context.window_manager.modal_handler_add(self)
|
||||
self._redraw_ui(context, force=True)
|
||||
job.start_thread = threading.Thread(
|
||||
target=self._start_job,
|
||||
args=(
|
||||
job,
|
||||
command,
|
||||
working_directory,
|
||||
creation_flags,
|
||||
),
|
||||
name="SnapFormJobStart",
|
||||
daemon=True,
|
||||
)
|
||||
job.start_thread.start()
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def _start_job(self, job, command, working_directory, creation_flags):
|
||||
if job.cancelled:
|
||||
job.started = True
|
||||
return
|
||||
try:
|
||||
if getattr(job, "uses_host", False):
|
||||
runtime.submit_host_job(command)
|
||||
else:
|
||||
if job.kind == "INSTALL_RUNTIME":
|
||||
runtime.stop_host()
|
||||
job.log_file = job.log_path.open("w", encoding="utf-8")
|
||||
job.process = subprocess.Popen(
|
||||
command,
|
||||
stdout=job.log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
cwd=working_directory,
|
||||
creationflags=creation_flags,
|
||||
)
|
||||
job.started = True
|
||||
if job.cancelled:
|
||||
if job.uses_host:
|
||||
runtime.cancel_host_job(job.status_path.stem)
|
||||
elif job.process is not None:
|
||||
job.process.terminate()
|
||||
except (OSError, RuntimeError) as error:
|
||||
if job.log_file is not None:
|
||||
job.log_file.close()
|
||||
job.log_file = None
|
||||
job.start_error = str(error)
|
||||
|
||||
def _prepare_command(self, _context, settings):
|
||||
paths = runtime.new_job_paths(self.job_type)
|
||||
remove_background = bool(
|
||||
getattr(self, "snapform_remove_background", False)
|
||||
background_depth_distance = float(
|
||||
getattr(self, "snapform_background_depth_distance", 0.0)
|
||||
)
|
||||
remove_background = background_depth_distance > 0.0
|
||||
if self.job_type == "INSTALL_RUNTIME":
|
||||
self._require_online_access()
|
||||
return runtime.install_runtime_command(paths["status"]), paths
|
||||
@@ -338,6 +370,11 @@ class RunJob(bpy.types.Operator):
|
||||
BEN2_MODEL.key,
|
||||
start_frame=start_frame,
|
||||
device=self._task_device(settings),
|
||||
background_depth_distance=(
|
||||
background_depth_distance
|
||||
if self.job_type == ORIGINAL_BACKGROUND_JOB
|
||||
else 0.0
|
||||
),
|
||||
),
|
||||
paths,
|
||||
)
|
||||
@@ -399,6 +436,11 @@ class RunJob(bpy.types.Operator):
|
||||
self.job_type == IMAGE_DEPTH_JOB
|
||||
and remove_background
|
||||
),
|
||||
background_depth_distance=(
|
||||
background_depth_distance
|
||||
if self.job_type == IMAGE_DEPTH_JOB
|
||||
else 0.0
|
||||
),
|
||||
device=self._task_device(settings),
|
||||
),
|
||||
paths,
|
||||
@@ -455,6 +497,20 @@ class RunJob(bpy.types.Operator):
|
||||
return {"CANCELLED"}
|
||||
|
||||
self._update_status(context, job)
|
||||
if job.start_error:
|
||||
settings = context.scene.snapform_settings
|
||||
settings.status = "Task failed to start"
|
||||
settings.progress = 0.0
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
f"Unable to start task: {job.start_error}",
|
||||
)
|
||||
self._remove_timer(context)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
if not job.started:
|
||||
return {"RUNNING_MODAL"}
|
||||
if job.uses_host:
|
||||
status = runtime.read_status(job.status_path) or {}
|
||||
state = status.get("state")
|
||||
@@ -532,6 +588,8 @@ class RunJob(bpy.types.Operator):
|
||||
if job.cancelled:
|
||||
settings.status = "Task cancelled"
|
||||
settings.progress = 0.0
|
||||
if getattr(job, "uses_host", False):
|
||||
runtime.mark_host_job_complete(job.status_path.stem)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
@@ -550,6 +608,8 @@ class RunJob(bpy.types.Operator):
|
||||
{"ERROR"},
|
||||
f"{error or fallback_error}; log: {job.log_path}",
|
||||
)
|
||||
if job.uses_host:
|
||||
runtime.mark_host_job_complete(job.status_path.stem)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
@@ -576,6 +636,7 @@ class RunJob(bpy.types.Operator):
|
||||
settings.status = "Depth import failed"
|
||||
settings.progress = 0.0
|
||||
self.report({"ERROR"}, str(import_error))
|
||||
runtime.mark_host_job_complete(job.status_path.stem)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
@@ -590,6 +651,7 @@ class RunJob(bpy.types.Operator):
|
||||
settings.status = "Depth import failed"
|
||||
settings.progress = 0.0
|
||||
self.report({"ERROR"}, str(import_error))
|
||||
runtime.mark_host_job_complete(job.status_path.stem)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
@@ -604,6 +666,7 @@ class RunJob(bpy.types.Operator):
|
||||
settings.status = "Image update failed"
|
||||
settings.progress = 0.0
|
||||
self.report({"ERROR"}, str(import_error))
|
||||
runtime.mark_host_job_complete(job.status_path.stem)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"CANCELLED"}
|
||||
@@ -615,6 +678,8 @@ class RunJob(bpy.types.Operator):
|
||||
job.status_path.unlink(missing_ok=True)
|
||||
except OSError:
|
||||
pass
|
||||
if getattr(job, "uses_host", False):
|
||||
runtime.mark_host_job_complete(job.status_path.stem)
|
||||
close_active_job()
|
||||
self._redraw_ui(context, force=True)
|
||||
return {"FINISHED"}
|
||||
@@ -677,6 +742,11 @@ class RunJob(bpy.types.Operator):
|
||||
snapform_depth_frames_dir=(
|
||||
str(result_directory) if is_sequence else ""
|
||||
),
|
||||
snapform_depth_npz_path=(
|
||||
str(result_directory / "original-depth.npz")
|
||||
if job.kind == ORIGINAL_BACKGROUND_JOB
|
||||
else ""
|
||||
),
|
||||
snapform_subdivision_level=job.subdivision_level,
|
||||
snapform_delete_source=job.delete_source,
|
||||
snapform_displacement_via=job.displacement_via,
|
||||
|
||||
Reference in New Issue
Block a user