Add new tests and functionality for image processing and geometry handling
- Introduced color_image.py for preparing standalone color images for tests. - Added hdr_image.py to create HDR textures for testing. - Implemented test_image_depth_panorama.py to evaluate panorama topology, UVs, and depth discontinuities. - Created test_shadeless.py to validate the opacity and HDR color endpoints of the Shadeless group. - Developed moge3_export.py for exporting dynamic MoGe-3 backbone and ONNX graphs. - Added material.py to inherit material slots onto generated geometry. - Built image_depth_panorama.py to construct a quad panorama with depth separation and sampling.
This commit is contained in:
@@ -78,17 +78,25 @@ def create_image_plane_object(
|
||||
thickness=0.0,
|
||||
):
|
||||
mesh = create_image_plane_mesh(name, bounds)
|
||||
mesh.materials.append(material)
|
||||
plane = bpy.data.objects.new(name, mesh)
|
||||
context.collection.objects.link(plane)
|
||||
if matrix_world is None:
|
||||
from mathutils import Matrix
|
||||
plane = None
|
||||
try:
|
||||
mesh.materials.append(material)
|
||||
plane = bpy.data.objects.new(name, mesh)
|
||||
context.collection.objects.link(plane)
|
||||
if matrix_world is None:
|
||||
from mathutils import Matrix
|
||||
|
||||
matrix_world = Matrix.Identity(4)
|
||||
plane.matrix_world = build_image_plane_matrix(matrix_world, bounds)
|
||||
add_image_plane_processing(
|
||||
plane,
|
||||
subdivisions=subdivisions,
|
||||
thickness=thickness,
|
||||
)
|
||||
matrix_world = Matrix.Identity(4)
|
||||
plane.matrix_world = build_image_plane_matrix(matrix_world, bounds)
|
||||
add_image_plane_processing(
|
||||
plane,
|
||||
subdivisions=subdivisions,
|
||||
thickness=thickness,
|
||||
)
|
||||
except Exception:
|
||||
if plane is not None:
|
||||
bpy.data.objects.remove(plane, do_unlink=True)
|
||||
if mesh.users == 0:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
raise
|
||||
return plane
|
||||
|
||||
@@ -3,13 +3,9 @@
|
||||
import bpy
|
||||
|
||||
from ...common.image import (
|
||||
color_data_name,
|
||||
configure_color_alpha,
|
||||
image_empty_bounds,
|
||||
image_pixels,
|
||||
load_color_result_image,
|
||||
load_normal_result_image,
|
||||
require_image_empty,
|
||||
require_conversion_source,
|
||||
)
|
||||
from ...common.depth import (
|
||||
depth_uniform_scale,
|
||||
@@ -17,6 +13,7 @@ from ...common.depth import (
|
||||
load_depth_result_image,
|
||||
)
|
||||
from ...common.material import create_image_material
|
||||
from ...common.color_image import material_color_image
|
||||
from ...common.object import (
|
||||
finalize_object_result,
|
||||
modifier_input_identifier,
|
||||
@@ -30,20 +27,6 @@ from .image_plane import (
|
||||
)
|
||||
|
||||
|
||||
def create_color_image(source_object):
|
||||
source = source_object.data
|
||||
image = source.copy()
|
||||
if getattr(source, "source", None) == "GENERATED" or getattr(
|
||||
source, "is_dirty", False
|
||||
):
|
||||
pixels = image_pixels(source)
|
||||
image.pixels.foreach_set(pixels)
|
||||
image.update()
|
||||
image.name = color_data_name(source_object)
|
||||
configure_color_alpha(image)
|
||||
return image
|
||||
|
||||
|
||||
def create_plane_object(context, source_object, subdivisions, material):
|
||||
source_name = source_object.name
|
||||
plane = create_image_plane_object(
|
||||
@@ -54,10 +37,17 @@ def create_plane_object(context, source_object, subdivisions, material):
|
||||
matrix_world=source_object.matrix_world,
|
||||
subdivisions=subdivisions,
|
||||
)
|
||||
for polygon in plane.data.polygons:
|
||||
polygon.use_smooth = True
|
||||
plane["anyimage_mesh_shape"] = "PLANE"
|
||||
return finalize_object_result(context, source_object, plane)
|
||||
try:
|
||||
for polygon in plane.data.polygons:
|
||||
polygon.use_smooth = True
|
||||
plane["anyimage_mesh_shape"] = "PLANE"
|
||||
return finalize_object_result(context, source_object, plane)
|
||||
except Exception:
|
||||
mesh = plane.data
|
||||
bpy.data.objects.remove(plane, do_unlink=True)
|
||||
if mesh.users == 0:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
raise
|
||||
|
||||
|
||||
def create_depth_plane_object(
|
||||
@@ -102,54 +92,43 @@ def create_depth_plane_object(
|
||||
modifier_input_identifier(node_group, name),
|
||||
value,
|
||||
)
|
||||
return finalize_object_result(context, source_object, plane)
|
||||
except Exception:
|
||||
if plane is not None:
|
||||
bpy.data.objects.remove(plane, do_unlink=True)
|
||||
if mesh.users == 0:
|
||||
bpy.data.meshes.remove(mesh)
|
||||
raise
|
||||
return finalize_object_result(context, source_object, plane)
|
||||
|
||||
|
||||
def create_depth_plane_from_result(context, result, operator):
|
||||
normal_space = {"DEPTH": "OBJECT", "RELIEF": "TANGENT"}[operator.plane_type]
|
||||
source_object = require_image_empty(operator.source_object_name)
|
||||
color_image = load_color_result_image(source_object, result.directory)
|
||||
color_image.name = color_data_name(source_object)
|
||||
source_object = require_conversion_source(operator)
|
||||
material = None
|
||||
depth_image = None
|
||||
normal_image = None
|
||||
try:
|
||||
depth_metadata = load_depth_metadata(result.file("depth_metadata"))
|
||||
depth_image = load_depth_result_image(
|
||||
result.file("depth"),
|
||||
source_object,
|
||||
depth_metadata,
|
||||
)
|
||||
normal_image = load_normal_result_image(
|
||||
result.file(f"{normal_space.lower()}_normal"),
|
||||
source_object,
|
||||
)
|
||||
material = create_image_material(
|
||||
source_object.data,
|
||||
color_image,
|
||||
normal_image=normal_image,
|
||||
normal_space=normal_space,
|
||||
scene=context.scene,
|
||||
)
|
||||
create_depth_plane_object(
|
||||
context,
|
||||
source_object,
|
||||
operator.mesh_detail,
|
||||
material,
|
||||
depth_image,
|
||||
depth_metadata,
|
||||
operator.plane_type,
|
||||
)
|
||||
with material_color_image(input_path=operator.color_path) as color_image:
|
||||
depth_metadata = load_depth_metadata(result.file("depth_metadata"))
|
||||
depth_image = load_depth_result_image(
|
||||
result.file("depth"), source_object, depth_metadata,
|
||||
)
|
||||
normal_image = load_normal_result_image(
|
||||
result.file(f"{normal_space.lower()}_normal"), source_object,
|
||||
)
|
||||
material = create_image_material(
|
||||
source_object.data, color_image,
|
||||
normal_image=normal_image, normal_space=normal_space,
|
||||
scene=context.scene,
|
||||
)
|
||||
create_depth_plane_object(
|
||||
context, source_object, operator.mesh_detail, material,
|
||||
depth_image, depth_metadata, operator.plane_type,
|
||||
)
|
||||
except Exception:
|
||||
if material is not None and material.users == 0:
|
||||
bpy.data.materials.remove(material, do_unlink=True)
|
||||
for image in (color_image, depth_image, normal_image):
|
||||
for image in (depth_image, normal_image):
|
||||
if image is not None and image.users == 0:
|
||||
bpy.data.images.remove(image, do_unlink=True)
|
||||
raise
|
||||
|
||||
@@ -15,18 +15,18 @@ from ...common.ai import (
|
||||
require_environment,
|
||||
require_input_path,
|
||||
require_model,
|
||||
report_ai_error,
|
||||
)
|
||||
from ...common.image import (
|
||||
cleanup_image_input,
|
||||
image_user_settings,
|
||||
is_image_empty,
|
||||
prepare_image_input,
|
||||
source_start_frame,
|
||||
video_total_frames,
|
||||
)
|
||||
from ...common.image import is_image_empty
|
||||
from ...common.material import create_image_material
|
||||
from ...common.color_image import (
|
||||
cleanup_material_color_input,
|
||||
material_analysis_input,
|
||||
material_color_image,
|
||||
prepare_material_color_input,
|
||||
require_static_color_image,
|
||||
)
|
||||
from .object import (
|
||||
create_color_image,
|
||||
create_depth_plane_from_result,
|
||||
create_plane_object,
|
||||
)
|
||||
@@ -49,7 +49,10 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
starting_message = "Preparing depth conversion"
|
||||
|
||||
input_path: bpy.props.StringProperty(options=_HIDDEN)
|
||||
color_path: bpy.props.StringProperty(options=_HIDDEN)
|
||||
source_object_name: bpy.props.StringProperty(options=_HIDDEN)
|
||||
source_identity: bpy.props.StringProperty(options=_HIDDEN)
|
||||
image_identity: bpy.props.StringProperty(options=_HIDDEN)
|
||||
plane_type: bpy.props.EnumProperty(
|
||||
items=(("DEPTH", "Depth Plane", ""), ("RELIEF", "Relief Plane", "")),
|
||||
default="DEPTH",
|
||||
@@ -64,7 +67,6 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
max=MAX_MAX_AI_INPUT_SIZE,
|
||||
options=_HIDDEN,
|
||||
)
|
||||
delete_input: bpy.props.BoolProperty(default=False, options=_HIDDEN)
|
||||
|
||||
def request(self, context):
|
||||
require_environment()
|
||||
@@ -74,7 +76,7 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
"input": str(input_path),
|
||||
"device": production_device().lower(),
|
||||
"video_frames": 1,
|
||||
"start_frame": source_start_frame(self.source_object_name),
|
||||
"start_frame": 1,
|
||||
"max_input_size": int(self.max_input_size),
|
||||
"plane_type": self.plane_type,
|
||||
}
|
||||
@@ -84,7 +86,7 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
input_path,
|
||||
self.model,
|
||||
self.resolution_level,
|
||||
source_start_frame(self.source_object_name),
|
||||
1,
|
||||
)
|
||||
)
|
||||
return parameters
|
||||
@@ -94,38 +96,42 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
|
||||
return f"{self.plane_type.title()} Plane is ready"
|
||||
|
||||
def cleanup(self):
|
||||
cleanup_image_input(self.input_path, self.delete_input)
|
||||
cleanup_material_color_input(self.color_path)
|
||||
|
||||
|
||||
def start_depth_plane_job(operator, context, plane_type):
|
||||
source_object = context.object
|
||||
if video_total_frames(source_object.data) != 1:
|
||||
operator.report({"ERROR"}, "Depth conversion currently supports still images")
|
||||
return {"CANCELLED"}
|
||||
setup_result = invoke_ai_setup_if_needed()
|
||||
if setup_result is not None:
|
||||
return setup_result
|
||||
try:
|
||||
source_path, delete_input = prepare_image_input(
|
||||
source_object.data,
|
||||
context.scene,
|
||||
)
|
||||
model, resolution_level = depth_plane_settings(context)
|
||||
except (RuntimeError, ValueError) as error:
|
||||
require_static_color_image(source_object.data)
|
||||
except ValueError as error:
|
||||
operator.report({"ERROR"}, str(error))
|
||||
return {"CANCELLED"}
|
||||
result = bpy.ops.anyimage.generate_depth_plane(
|
||||
input_path=str(source_path),
|
||||
source_object_name=source_object.name,
|
||||
mesh_detail=operator.mesh_detail,
|
||||
plane_type=plane_type,
|
||||
model=model,
|
||||
resolution_level=resolution_level,
|
||||
max_input_size=configured_max_ai_input_size(context),
|
||||
delete_input=delete_input,
|
||||
)
|
||||
if "CANCELLED" in result and delete_input:
|
||||
cleanup_image_input(source_path, True)
|
||||
setup_result = invoke_ai_setup_if_needed(operator)
|
||||
if setup_result is not None:
|
||||
return setup_result
|
||||
color_path = None
|
||||
try:
|
||||
color_path = prepare_material_color_input(source_object.data)
|
||||
source_path = material_analysis_input(color_path)
|
||||
model, resolution_level = depth_plane_settings(context)
|
||||
result = bpy.ops.anyimage.generate_depth_plane(
|
||||
input_path=str(source_path),
|
||||
color_path=str(color_path),
|
||||
source_object_name=source_object.name,
|
||||
source_identity=str(source_object.as_pointer()),
|
||||
image_identity=str(source_object.data.as_pointer()),
|
||||
mesh_detail=operator.mesh_detail,
|
||||
plane_type=plane_type,
|
||||
model=model,
|
||||
resolution_level=resolution_level,
|
||||
max_input_size=configured_max_ai_input_size(context),
|
||||
)
|
||||
except (OSError, RuntimeError, ValueError) as error:
|
||||
cleanup_material_color_input(color_path)
|
||||
report_ai_error(operator, error)
|
||||
return {"CANCELLED"}
|
||||
if "CANCELLED" in result:
|
||||
cleanup_material_color_input(color_path)
|
||||
return {"CANCELLED"} if "CANCELLED" in result else {"FINISHED"}
|
||||
|
||||
|
||||
@@ -143,20 +149,26 @@ class ConvertToPlane(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
source_object = context.object
|
||||
color_image = create_color_image(source_object)
|
||||
frame_start, frame_offset, frame_duration, use_cyclic = image_user_settings(
|
||||
source_object
|
||||
)
|
||||
material = create_image_material(
|
||||
source_object.data,
|
||||
color_image,
|
||||
frame_start=frame_start,
|
||||
color_frame_offset=frame_offset,
|
||||
color_frame_duration=frame_duration,
|
||||
use_cyclic=use_cyclic,
|
||||
scene=context.scene,
|
||||
)
|
||||
create_plane_object(context, source_object, self.mesh_detail, material)
|
||||
try:
|
||||
require_static_color_image(source_object.data)
|
||||
except ValueError as error:
|
||||
self.report({"ERROR"}, str(error))
|
||||
return {"CANCELLED"}
|
||||
material = None
|
||||
try:
|
||||
with material_color_image(source_object.data) as color_image:
|
||||
material = create_image_material(
|
||||
source_object.data, color_image,
|
||||
scene=context.scene,
|
||||
)
|
||||
create_plane_object(context, source_object, self.mesh_detail, material)
|
||||
except Exception as error:
|
||||
if material is not None and material.users == 0:
|
||||
bpy.data.materials.remove(material, do_unlink=True)
|
||||
if isinstance(error, ValueError):
|
||||
self.report({"ERROR"}, str(error))
|
||||
return {"CANCELLED"}
|
||||
raise
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user