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

@@ -6,6 +6,33 @@ from unittest.mock import patch
from tests.anyimage.server.runtime_support import ServerTestCase
class ModelManagerTest(ServerTestCase):
def test_background_cache_switches_model_directory_and_device_and_closes(self):
from server.models import background
cache = self.create_cache()
loads = []
def create(directory, device):
result = object()
loads.append((str(directory), device, result))
return result
with patch.object(background, "model_adapter", return_value=SimpleNamespace(create_session=create)):
first, _ = cache.get_background("a/birefnet-lite", "cpu")
assert cache.get_background("a/birefnet-lite", "cpu") == (first, 0.0)
second, _ = cache.get_background("a/BEN2-ONNX", "cpu")
third, _ = cache.get_background("a/birefnet-hr-matting", "cpu")
fourth, _ = cache.get_background("b/birefnet-hr-matting", "cpu")
assert cache.get_background("b/birefnet-hr-matting", "directml") == (fourth, 0.0)
fifth, _ = cache.get_background("b/birefnet-lite", "directml")
assert len(loads) == 5
assert len({id(item) for item in (first, second, third, fourth, fifth)}) == 5
assert cache.snapshot()["background"] == "birefnet-lite"
assert loads[2][1] == loads[3][1] == "cpu"
cache.close()
assert cache.background_model is None
assert cache.snapshot()["background"] == ""
def test_required_model_download_skips_ready_model(self):
manager = self.ModelManager(Path("."))
downloads = []
@@ -39,7 +66,7 @@ class ModelManagerTest(ServerTestCase):
snapshot,
{
"loaded": {
"ben2": "",
"background": "",
"geometry": "",
"upscale": "",
}
@@ -49,7 +76,7 @@ class ModelManagerTest(ServerTestCase):
self.assertNotIn("model_catalog", sys.modules)
def test_ben2_model_is_reused_for_matching_runtime_key(self):
def test_background_model_is_reused_for_matching_runtime_key(self):
loads = []
ben2 = ModuleType("server.models.onnx_ben2")
@@ -59,12 +86,12 @@ class ModelManagerTest(ServerTestCase):
cache = self.create_cache()
device = SimpleNamespace(type="cuda")
with patch.dict(sys.modules, {"server.models.onnx_ben2": ben2}):
first, _load_ms = cache.get_ben2("model", device)
second, cached_load_ms = cache.get_ben2("model", device)
first, _load_ms = cache.get_background("BEN2-ONNX", device)
second, cached_load_ms = cache.get_background("BEN2-ONNX", device)
self.assertIs(first, second)
self.assertEqual(loads, [("model", "cuda")])
self.assertEqual(cache.snapshot()["ben2"], "model")
self.assertEqual(loads, [("BEN2-ONNX", "cuda")])
self.assertEqual(cache.snapshot()["background"], "BEN2-ONNX")
self.assertEqual(cached_load_ms, 0.0)
@@ -73,8 +100,8 @@ class ModelManagerTest(ServerTestCase):
ben2 = object()
old_moge = object()
upscale = object()
cache.ben2_model = ben2
cache.ben2_key = ("ben2", "cuda")
cache.background_model = ben2
cache.background_key = ("ben2", "cuda")
cache.moge_model = old_moge
cache.moge_key = ("old-moge", "cuda")
cache.upscale_model = upscale
@@ -85,7 +112,7 @@ class ModelManagerTest(ServerTestCase):
with patch.dict(sys.modules, {"server.models.onnx_moge2": fake_moge}):
self.assertIs(cache.get_moge("new-moge", "cuda"), new_moge)
self.assertIs(cache.ben2_model, ben2)
self.assertIs(cache.background_model, ben2)
self.assertIs(cache.upscale_model, upscale)
self.assertEqual(cache.snapshot()["geometry"], "new-moge")
@@ -160,11 +187,11 @@ class ModelManagerTest(ServerTestCase):
"server.models.onnx_upscale": upscale,
},
):
first_ben2, _load_ms = cache.get_ben2("ben2", "cuda")
first_ben2, _load_ms = cache.get_background("BEN2-ONNX", "cuda")
assert_reused(
first_ben2,
lambda: cache.get_ben2("ben2", "cuda")[0],
"release_ben2",
lambda: cache.get_background("BEN2-ONNX", "cuda")[0],
"release_background",
)
first_moge2 = cache.get_moge("moge2", "cuda")