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

@@ -118,6 +118,23 @@ class PreferencesTest(BlenderTestCase):
geometry = properties.geometry_model_enum_items(None, None)
self.assertEqual(tuple(item[0] for item in geometry), expected)
def test_model_preferences_keep_valid_choices_and_normalize_retired_keys(self):
module = self.anyimage.preferences
stored = self.fake_bpy.context.preferences.addons["anyimage"].preferences
for attribute, reader, valid, retired, default in (
("geometry_model", module.configured_geometry_model, "MOGE3_VITL", "MOGE2_VITL_NORMAL", "MOGE2_VITS_NORMAL"),
("background_model", module.configured_background_model, "BEN2_BASE", "UNKNOWN", "BIREFNET_LITE"),
("upscale_model", module.configured_upscale_model, "REALESRGAN_X4PLUS", "PISA_SR", "REALESRGAN_GENERAL_WDN_X4V3"),
):
setattr(stored, attribute, valid)
self.assertEqual(reader(), valid)
setattr(stored, attribute, retired)
self.assertEqual(reader(), default)
self.assertEqual(getattr(stored, attribute), default)
self.assertEqual({item[0]: item[-1] for item in module.BACKGROUND_MODEL_ITEMS}["BEN2_BASE"], 0)
self.assertEqual({item[0]: item[-1] for item in module.UPSCALE_MODEL_ITEMS}["REALESRGAN_X4PLUS"], 2)
self.assertEqual({item[0]: item[-1] for item in self.anyimage.properties.geometry_model_enum_items(None, None)}["MOGE3_VITL"], 3)
def test_global_ai_input_size_defaults_and_clamps(self):
preference_module = self.anyimage.preferences
@@ -212,7 +229,7 @@ class PreferencesTest(BlenderTestCase):
preferences.layout = Layout()
preferences.geometry_model = "MOGE2_VITS_NORMAL"
preferences.background_model = "BEN2_BASE"
preferences.upscale_model = "SAFMN_REAL_X4"
preferences.upscale_model = "HAT_GAN_X4_SHARPER"
catalog = tuple(
{
"key": key,
@@ -225,7 +242,7 @@ class PreferencesTest(BlenderTestCase):
("MOGE2_VITS_NORMAL", "moge"),
("BEN2_BASE", "ben2"),
("REALESRGAN_GENERAL_WDN_X4V3", "upscale"),
("SAFMN_REAL_X4", "upscale"),
("HAT_GAN_X4_SHARPER", "upscale"),
)
)
with (
@@ -249,7 +266,7 @@ class PreferencesTest(BlenderTestCase):
self.assertEqual(
[download.model for download in downloads],
["MOGE2_VITS_NORMAL", "BEN2_BASE", "SAFMN_REAL_X4"],
["MOGE2_VITS_NORMAL", "BEN2_BASE", "HAT_GAN_X4_SHARPER"],
)
self.assertEqual(
[
@@ -409,7 +426,7 @@ class PreferencesTest(BlenderTestCase):
operator.analysis_input_path = __file__
operator.source_object_name = "Source"
operator.mesh_detail = 0
operator.model = self.anyimage.properties.DEFAULT_MOGE2_MODEL_KEY
operator.model = self.anyimage.preferences.DEFAULT_GEOMETRY_MODEL_KEY
operator.resolution_level = 5
operator.max_input_size = 1536
context = SimpleNamespace(
@@ -450,7 +467,7 @@ class PreferencesTest(BlenderTestCase):
patch.object(
self.upscale,
"configured_upscale_model",
return_value="SAFMN_REAL_X4",
return_value="HAT_GAN_X4_SHARPER",
),
patch.object(
self.upscale,
@@ -471,5 +488,5 @@ class PreferencesTest(BlenderTestCase):
result = operator.execute(context)
self.assertEqual(result, {"RUNNING_MODAL"})
self.assertEqual(operator.model, "SAFMN_REAL_X4")
self.assertEqual(operator.model, "HAT_GAN_X4_SHARPER")
self.assertEqual(operator.max_input_size, 1024)