Implement BiRefNet and HAT Sharper models with background removal capabilities

- Added validation documentation for AI model tiers.
- Introduced `background.py` for background removal logic, including inference functions for alpha and foreground extraction.
- Created `onnx_birefnet.py` to handle BiRefNet model sessions and alpha inference.
- Developed tests for background models, ensuring proper dispatch and session reuse.
- Added tests for BiRefNet ONNX model to validate alpha output and error handling for invalid values.
- Implemented model export scripts for BiRefNet and HAT Sharper, ensuring proper weight compression and licensing.
- Created `deform_sampling.py` for deformable convolution operations used in BiRefNet.
- Added utility functions for compressing ONNX weights without altering graph arithmetic.
- Introduced a reference plane node for depth field adjustments in geometry processing.
This commit is contained in:
2026-09-10 20:50:44 +08:00
parent 8ae3c1d5b7
commit e8129fe495
70 changed files with 1890 additions and 1208 deletions

View File

@@ -16,7 +16,7 @@ class ServerJobsTest(unittest.TestCase):
cls.background = load_server_module("jobs.remove_background")
cls.ben2 = load_server_module("models.ben2")
cls.background_model = load_server_module("models.background")
cls.onnx_upscale = load_server_module("models.onnx_upscale")
@@ -96,7 +96,7 @@ class ServerJobsTest(unittest.TestCase):
patch.object(onnx_ben2, "infer_alpha", return_value=np.full((1, 2), 128 / 255, np.float32)) as infer,
patch.object(Image.Image, "save", autospec=True, side_effect=Image.Image.save) as save,
):
self.background.run(context, {"input": str(input_path), "model": "BEN2", "device": "cpu"})
self.background.run(context, {"input": str(input_path), "model": "BEN2_BASE", "device": "cpu"})
with Image.open(root / "result" / "image.0001.png") as image:
self.assertEqual(image.getpixel((0, 0)), (255, 64, 32, 64))
self.assertEqual(infer.call_args.kwargs, {"release_memory": True})
@@ -126,15 +126,15 @@ class ServerJobsTest(unittest.TestCase):
patch.object(onnx_ben2, "create_session", return_value=object()),
patch.object(onnx_ben2, "infer_alpha", side_effect=infer),
):
result, _load_ms, _inference_ms = self.ben2.infer_foreground(
result, _load_ms, _inference_ms = self.background_model.infer_foreground(
input_path,
"model",
"BEN2-ONNX",
"cpu",
preserve_input_alpha=True,
)
raw_result, _load_ms, _inference_ms = self.ben2.infer_foreground(
raw_result, _load_ms, _inference_ms = self.background_model.infer_foreground(
input_path,
"model",
"BEN2-ONNX",
"cpu",
)
@@ -166,7 +166,7 @@ class ServerJobsTest(unittest.TestCase):
patch.object(onnx_ben2, "create_session", return_value=object()),
patch.object(onnx_ben2, "infer_alpha", return_value=np.ones((1, 2), np.float32)) as infer,
):
self.background.run(context, {"input": str(frames), "model": "BEN2", "device": "cpu", "video_frames": 8})
self.background.run(context, {"input": str(frames), "model": "BEN2_BASE", "device": "cpu", "video_frames": 8})
self.assertEqual([call.kwargs["release_memory"] for call in infer.call_args_list], [False, True])
for index in (1, 2):
with Image.open(context.directory / f"image.{index:04d}.png") as image:
@@ -186,7 +186,7 @@ class ServerJobsTest(unittest.TestCase):
patch.object(onnx_ben2, "create_session", return_value=object()),
patch.object(onnx_ben2, "infer_alpha", return_value=alpha),
):
result = self.background.run(context, {"input": str(path), "model": "BEN2", "device": "cpu", "output_kind": "alpha"})
result = self.background.run(context, {"input": str(path), "model": "BEN2_BASE", "device": "cpu", "output_kind": "alpha"})
self.assertEqual(result, {"alpha": "alpha.npy"})
np.testing.assert_array_equal(np.load(context.directory / result["alpha"], allow_pickle=False), alpha)
self.assertFalse(list(context.directory.glob("*.png")))