Refactor image handling and update foreground processing

- Changed the logic in `remove_background_faces` to keep only faces with all foreground vertices.
- Updated background job to save foreground images with a new naming convention (`color.0001.png`).
- Replaced `copy_frames` with `copy_color_images` in various jobs to ensure consistent image naming.
- Introduced a new function `copy_color_images` to handle the copying of images with a specific naming format.
- Updated the BEN2 model to save output images with a new prefix for better clarity.
- Modified tests to reflect changes in image naming and processing logic.
- Added a new shadeless node group for improved material handling in geometry nodes.
- Enhanced validation scripts to check for new image naming conventions and ensure proper functionality.
This commit is contained in:
2026-08-19 01:45:59 +08:00
parent 835225d264
commit 20b653bb30
28 changed files with 663 additions and 383 deletions

View File

@@ -686,7 +686,7 @@ class AddonTest(unittest.TestCase):
operator.source_object_name = "Reference"
operator.remove_background = False
operator.delete_source = True
operator.unshaded = False
operator.shadeless = False
result = object()
context = object()
@@ -701,7 +701,7 @@ class AddonTest(unittest.TestCase):
source_object_name="Reference",
remove_background=False,
delete_source=True,
unshaded=False,
shadeless=False,
)
def test_entry_operator_dispatches_plain_properties(self):
@@ -709,7 +709,7 @@ class AddonTest(unittest.TestCase):
operator.model = "MOGE2_VITS_NORMAL"
operator.resolution_level = 7
operator.keep_original = False
operator.unshaded = True
operator.shadeless = True
operator.remove_background = False
source_object = SimpleNamespace(
name="Reference",

View File

@@ -291,12 +291,12 @@ class ImageDepthTest(unittest.TestCase):
"微信视频2026-07-23_192756_414",
)
def test_removed_background_name_strips_video_extension(self):
def test_color_name_strips_video_extension(self):
source_object = SimpleNamespace(data=SimpleNamespace(name="clip.mp4"))
self.assertEqual(
self.image_depth.removed_background_data_name(source_object),
"clip.foreground",
self.image_depth.color_data_name(source_object),
"clip.color",
)
def test_force_sequence_source_keeps_loaded_sequence(self):
@@ -634,19 +634,19 @@ class ImageDepthTest(unittest.TestCase):
self.assertEqual(depth_image.name, "img.depth")
def test_removed_background_name_uses_source_image_base_name(self):
def test_color_name_uses_source_image_base_name(self):
source_object = SimpleNamespace(
data=SimpleNamespace(name="img.png"),
)
self.assertEqual(
self.image_depth.removed_background_data_name(source_object),
"img.foreground",
self.image_depth.color_data_name(source_object),
"img.color",
)
source_object.data.name = "img.png.001"
self.assertEqual(
self.image_depth.removed_background_data_name(source_object),
"img.001.foreground",
self.image_depth.color_data_name(source_object),
"img.001.color",
)
def test_surface_displacement_is_set_to_displacement_only(self):

View File

@@ -376,7 +376,7 @@ class ServerTest(unittest.TestCase):
)
status = self.wait_for_job(context)
self.assertTrue(
(context.directory / "frame.0001.jpg").is_file()
(context.directory / "color.0001.jpg").is_file()
)
self.assertEqual(status["state"], "succeeded")

View File

@@ -127,9 +127,24 @@ class WorkerTest(unittest.TestCase):
cls.foreground_job = load_server_module("jobs.foreground")
cls.download = load_server_module("model_download")
def test_surface_inference_progress_omits_single_frame_count(self):
self.assertEqual(
self.surface_job.inference_progress_message("MoGe-2", 0, 1),
"Estimating MoGe-2 geometry",
)
self.assertEqual(
self.surface_job.inference_progress_message("DA3", 0, 1),
"Estimating DA3 geometry",
)
self.assertEqual(
self.surface_job.inference_progress_message("DA3", 1, 3),
"Estimating DA3 geometry (2/3)",
)
def test_surface_has_one_model_independent_builder(self):
self.assertTrue(callable(self.surface.build_surface))
self.assertTrue(callable(self.surface_outputs.write_surface_archive))
self.assertFalse(hasattr(self.surface_outputs, "write_surface_texture"))
self.assertFalse(hasattr(self.surface, "export_da3_surface"))
self.assertFalse(hasattr(self.surface, "export_moge2_surface"))
@@ -226,6 +241,33 @@ class WorkerTest(unittest.TestCase):
)
self.assertIsNone(temporary_directory)
def test_copy_color_images_numbers_a_still_image(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
source = root / "source.png"
source.write_bytes(b"frame")
output = root / "result"
output.mkdir()
self.media.copy_color_images([source], output)
self.assertEqual(
(output / "color.0001.png").read_bytes(),
b"frame",
)
def test_copy_frames_skips_a_still_image_by_default(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
source = root / "source.png"
source.touch()
output = root / "result"
output.mkdir()
self.media.copy_frames([source], output)
self.assertEqual(list(output.iterdir()), [])
def test_extract_video_frames_reads_frames_sequentially(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
@@ -497,7 +539,7 @@ class WorkerTest(unittest.TestCase):
{"input": str(input_path), "model": "BEN2", "device": "cpu"},
)
self.assertTrue((root / "result" / "foreground.png").is_file())
self.assertTrue((root / "result" / "color.0001.png").is_file())
self.assertEqual(
inference_options[0][1],
@@ -590,16 +632,10 @@ class WorkerTest(unittest.TestCase):
result_directory = context.directory
self.assertTrue(
(result_directory / "foreground.0001.png").is_file()
(result_directory / "color.0001.png").is_file()
)
self.assertTrue(
(result_directory / "foreground.0002.png").is_file()
)
self.assertTrue(
(result_directory / "frame.0001.jpg").is_file()
)
self.assertTrue(
(result_directory / "frame.0002.jpg").is_file()
(result_directory / "color.0002.png").is_file()
)
self.assertEqual(len(inference_options), 2)
@@ -630,7 +666,7 @@ class WorkerTest(unittest.TestCase):
for path in result_directory.iterdir()
if path.is_file()
),
["frame.0001.jpg", "frame.0002.jpg", "frame.0003.jpg"],
["color.0001.jpg", "color.0002.jpg", "color.0003.jpg"],
)
self.assertEqual(context.status["progress"], 1.0)
self.assertIn("3 frame", context.status["message"])
@@ -889,10 +925,10 @@ class WorkerTest(unittest.TestCase):
result_directory = context.directory
self.assertTrue(
(result_directory / "frame.0001.jpg").is_file()
(result_directory / "color.0001.jpg").is_file()
)
self.assertTrue(
(result_directory / "frame.0002.jpg").is_file()
(result_directory / "color.0002.jpg").is_file()
)
self.assertEqual(len(inference_inputs), 2)
self.assertTrue(all(len(paths) == 1 for paths in inference_inputs))
@@ -1107,19 +1143,19 @@ class WorkerTest(unittest.TestCase):
)
np.testing.assert_array_equal(compact_faces, [[0, 1, 2]])
def test_surface_background_keeps_faces_with_any_foreground_vertex(self):
def test_surface_background_requires_every_vertex_to_be_foreground(self):
faces = np.asarray(
((0, 1, 4), (0, 4, 3), (1, 2, 5), (1, 5, 4)),
dtype=np.int32,
)
foreground = np.asarray(
((False, False, False), (False, True, False)),
((True, True, False), (False, True, False)),
dtype=bool,
)
filtered = self.geometry.remove_background_faces(faces, foreground)
np.testing.assert_array_equal(filtered, faces[[0, 1, 3]])
np.testing.assert_array_equal(filtered, faces[[0]])
def test_surface_background_mask_keeps_any_nonzero_alpha(self):
from PIL import Image
@@ -1255,6 +1291,3 @@ class WorkerTest(unittest.TestCase):
if __name__ == "__main__":
unittest.main()