Refactor image mesh processing and background removal functionality
- Updated NODE_GROUP_NAME from "O Refine DA3 Surface" to "O DA3 Surface". - Introduced `remove_surface_background` function to filter out background faces and compact geometry. - Enhanced `build_image_mesh` to conditionally remove background faces based on a new property. - Added `snapform_remove_background` and `snapform_keep_original` properties to operators for better control over background handling. - Updated UI to reflect new properties and improved resolution scaling logic. - Refactored tests to cover new background removal functionality and ensure proper geometry handling. - Adjusted geometry nodes and validation scripts to remove references to background handling.
This commit is contained in:
@@ -214,7 +214,6 @@ class AddonTest(unittest.TestCase):
|
||||
"snapform_edge_culling",
|
||||
"snapform_face_reduction",
|
||||
"snapform_depth_smoothing",
|
||||
"snapform_remove_background",
|
||||
"snapform_postprocess_method",
|
||||
):
|
||||
with self.subTest(property_name=property_name):
|
||||
@@ -222,6 +221,18 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertFalse(annotations["snapform_create_camera"]["default"])
|
||||
self.assertFalse(annotations["snapform_unshaded"]["default"])
|
||||
self.assertEqual(annotations["snapform_unshaded"]["name"], "Unshaded")
|
||||
self.assertFalse(annotations["snapform_remove_background"]["default"])
|
||||
self.assertFalse(annotations["snapform_keep_original"]["default"])
|
||||
self.assertEqual(
|
||||
annotations["snapform_process_res_factor"]["default"],
|
||||
100.0,
|
||||
)
|
||||
self.assertEqual(annotations["snapform_process_res_factor"]["min"], 1.0)
|
||||
self.assertEqual(annotations["snapform_process_res_factor"]["max"], 100.0)
|
||||
self.assertEqual(
|
||||
annotations["snapform_process_res_factor"]["subtype"],
|
||||
"PERCENTAGE",
|
||||
)
|
||||
|
||||
def test_image_mesh_uses_o_prefixed_attributes_and_bundled_nodes(self):
|
||||
image_mesh = self.addon.image_mesh
|
||||
@@ -231,7 +242,7 @@ class AddonTest(unittest.TestCase):
|
||||
|
||||
self.assertTrue(all(name.startswith("o_") for name in names))
|
||||
self.assertTrue(image_mesh.node_asset_path().is_file())
|
||||
self.assertEqual(image_mesh.NODE_GROUP_NAME, "O Refine DA3 Surface")
|
||||
self.assertEqual(image_mesh.NODE_GROUP_NAME, "O DA3 Surface")
|
||||
self.assertEqual(image_mesh.NODE_ASSET_NAME, "O_SnapForm.blend")
|
||||
|
||||
def test_depth_plane_uses_bundled_nodes_and_material_attributes(self):
|
||||
@@ -242,10 +253,6 @@ class AddonTest(unittest.TestCase):
|
||||
image_depth.DEPTH_PLANE_NODE_GROUP_NAME,
|
||||
"O DA3 Depth Plane",
|
||||
)
|
||||
self.assertEqual(
|
||||
image_depth.REMOVE_BACKGROUND_ATTRIBUTE,
|
||||
"o_remove_background",
|
||||
)
|
||||
self.assertEqual(
|
||||
image_depth.MATERIAL_DISPLACEMENT_ATTRIBUTE,
|
||||
"o_material_displacement",
|
||||
@@ -279,6 +286,33 @@ class AddonTest(unittest.TestCase):
|
||||
float(np.median(edge_lengths) * np.sqrt(10.0) * 2.0),
|
||||
)
|
||||
|
||||
def test_surface_background_filter_compacts_geometry_and_attributes(self):
|
||||
vertices = np.arange(18, dtype=np.float32).reshape(6, 3)
|
||||
faces = np.asarray(((0, 1, 2), (2, 3, 4)), dtype=np.int32)
|
||||
uv = np.arange(12, dtype=np.float32).reshape(6, 2)
|
||||
attributes = {
|
||||
key: np.arange(6 if name in self.addon.image_mesh.POINT_ATTRIBUTES else 2)
|
||||
for name, (_data_type, key) in {
|
||||
**self.addon.image_mesh.POINT_ATTRIBUTES,
|
||||
**self.addon.image_mesh.FACE_ATTRIBUTES,
|
||||
}.items()
|
||||
}
|
||||
attributes["face_background"] = np.asarray((True, False))
|
||||
|
||||
result = self.addon.image_mesh.remove_surface_background(
|
||||
vertices,
|
||||
faces,
|
||||
uv,
|
||||
attributes,
|
||||
)
|
||||
|
||||
filtered_vertices, filtered_faces, filtered_uv, filtered_attributes = result
|
||||
np.testing.assert_array_equal(filtered_vertices, vertices[[2, 3, 4]])
|
||||
np.testing.assert_array_equal(filtered_faces, ((0, 1, 2),))
|
||||
np.testing.assert_array_equal(filtered_uv, uv[[2, 3, 4]])
|
||||
self.assertEqual(len(filtered_attributes["point_depth"]), 3)
|
||||
self.assertEqual(len(filtered_attributes["face_background"]), 1)
|
||||
|
||||
def test_registers_image_background_operators(self):
|
||||
self.assertIn(self.addon.RemoveImageBackground, self.addon.CLASSES)
|
||||
self.assertIn(self.addon.ApplyRemovedBackground, self.addon.CLASSES)
|
||||
@@ -481,7 +515,8 @@ class AddonTest(unittest.TestCase):
|
||||
operator = self.addon.ConvertImageToDepthPlane()
|
||||
operator.snapform_depth_source = "ORIGINAL"
|
||||
operator.snapform_unshaded = False
|
||||
operator.snapform_delete_original = False
|
||||
operator.snapform_remove_background = True
|
||||
operator.snapform_keep_original = True
|
||||
source_object = SimpleNamespace(
|
||||
name="Reference",
|
||||
data=SimpleNamespace(name="img.png"),
|
||||
@@ -513,7 +548,8 @@ class AddonTest(unittest.TestCase):
|
||||
operator = self.addon.ConvertImageToDepthPlane()
|
||||
operator.snapform_depth_source = "ORIGINAL"
|
||||
operator.snapform_unshaded = False
|
||||
operator.snapform_delete_original = False
|
||||
operator.snapform_remove_background = True
|
||||
operator.snapform_keep_original = True
|
||||
source_object = SimpleNamespace(
|
||||
name="Reference",
|
||||
data=SimpleNamespace(
|
||||
@@ -552,6 +588,39 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertFalse(calls[0]["snapform_delete_input"])
|
||||
self.assertFalse(calls[0]["snapform_delete_source"])
|
||||
|
||||
def test_original_depth_without_background_uses_frame_extraction(self):
|
||||
operator = self.addon.ConvertImageToDepthPlane()
|
||||
operator.snapform_depth_source = "ORIGINAL"
|
||||
operator.snapform_unshaded = False
|
||||
operator.snapform_remove_background = False
|
||||
operator.snapform_keep_original = False
|
||||
source_object = SimpleNamespace(
|
||||
name="Reference",
|
||||
data=SimpleNamespace(name="img.png"),
|
||||
)
|
||||
context = SimpleNamespace(object=source_object, scene=object())
|
||||
calls = []
|
||||
self.fake_bpy.ops = SimpleNamespace(
|
||||
snapform=SimpleNamespace(
|
||||
run_job=lambda **options: calls.append(options)
|
||||
or {"RUNNING_MODAL"},
|
||||
),
|
||||
)
|
||||
|
||||
with TemporaryDirectory() as directory:
|
||||
input_path = Path(directory) / "input.png"
|
||||
input_path.touch()
|
||||
with patch.object(
|
||||
self.addon.image_depth,
|
||||
"prepare_image_input",
|
||||
return_value=(input_path, False),
|
||||
):
|
||||
result = operator.execute(context)
|
||||
|
||||
self.assertEqual(result, {"FINISHED"})
|
||||
self.assertEqual(calls[0]["job_type"], "ORIGINAL_FRAMES")
|
||||
self.assertTrue(calls[0]["snapform_delete_source"])
|
||||
|
||||
def test_conversion_dialog_exposes_requested_settings(self):
|
||||
operator = self.addon.ConvertImageToDepthPlane
|
||||
|
||||
@@ -562,10 +631,21 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertTrue(operator.__annotations__["snapform_use_ray_pose"]["default"])
|
||||
self.assertEqual(
|
||||
operator.__annotations__["snapform_process_res_factor"]["default"],
|
||||
100.0,
|
||||
)
|
||||
self.assertEqual(
|
||||
operator.__annotations__["snapform_process_res_factor"]["min"],
|
||||
1.0,
|
||||
)
|
||||
self.assertTrue(
|
||||
operator.__annotations__["snapform_delete_original"]["default"]
|
||||
self.assertEqual(
|
||||
operator.__annotations__["snapform_process_res_factor"]["max"],
|
||||
100.0,
|
||||
)
|
||||
self.assertFalse(
|
||||
operator.__annotations__["snapform_remove_background"]["default"]
|
||||
)
|
||||
self.assertFalse(
|
||||
operator.__annotations__["snapform_keep_original"]["default"]
|
||||
)
|
||||
self.assertFalse(
|
||||
operator.__annotations__["snapform_unshaded"]["default"]
|
||||
@@ -578,7 +658,8 @@ class AddonTest(unittest.TestCase):
|
||||
"snapform_model",
|
||||
"snapform_use_ray_pose",
|
||||
"snapform_process_res_factor",
|
||||
"snapform_delete_original",
|
||||
"snapform_remove_background",
|
||||
"snapform_keep_original",
|
||||
},
|
||||
)
|
||||
depth_sources = operator.__annotations__["snapform_depth_source"]
|
||||
@@ -690,9 +771,10 @@ class AddonTest(unittest.TestCase):
|
||||
operator.snapform_depth_source = "ESTIMATE"
|
||||
operator.snapform_model = "BASE"
|
||||
operator.snapform_use_ray_pose = True
|
||||
operator.snapform_process_res_factor = 1.0
|
||||
operator.snapform_process_res_factor = 50.0
|
||||
operator.snapform_unshaded = False
|
||||
operator.snapform_delete_original = False
|
||||
operator.snapform_remove_background = True
|
||||
operator.snapform_keep_original = True
|
||||
calls = []
|
||||
self.fake_bpy.ops = SimpleNamespace(
|
||||
snapform=SimpleNamespace(
|
||||
@@ -817,9 +899,10 @@ class AddonTest(unittest.TestCase):
|
||||
operator.snapform_depth_source = "ESTIMATE"
|
||||
operator.snapform_model = "GIANT_1_1"
|
||||
operator.snapform_use_ray_pose = True
|
||||
operator.snapform_process_res_factor = 0.5
|
||||
operator.snapform_process_res_factor = 50.0
|
||||
operator.snapform_unshaded = True
|
||||
operator.snapform_delete_original = True
|
||||
operator.snapform_remove_background = True
|
||||
operator.snapform_keep_original = False
|
||||
image = SimpleNamespace(
|
||||
name="img.png",
|
||||
filepath="",
|
||||
@@ -855,7 +938,7 @@ class AddonTest(unittest.TestCase):
|
||||
self.assertEqual(result, {"FINISHED"})
|
||||
save_packed_image.assert_called_once_with(image, context.scene)
|
||||
self.assertEqual(calls[0]["snapform_input_path"], str(packed_path))
|
||||
self.assertEqual(calls[0]["snapform_process_res"], 960)
|
||||
self.assertEqual(calls[0]["snapform_process_res"], 966)
|
||||
self.assertEqual(calls[0]["snapform_model_key"], "GIANT_1_1")
|
||||
self.assertTrue(calls[0]["snapform_remove_background"])
|
||||
self.assertTrue(calls[0]["snapform_unshaded"])
|
||||
|
||||
Reference in New Issue
Block a user