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

@@ -2,8 +2,8 @@ import bpy
from ..preferences import ADDON_PACKAGE
from ..runtime import JobOperatorBase, runtime
from ..server.model_catalog import DEFAULT_MODEL_KEYS
from ..properties import (
REQUIRED_MODEL_KEYS,
ai_status,
model_download_size,
model_record,
@@ -42,7 +42,7 @@ class SetupAIEnvironment(bpy.types.Operator):
if not status["environment_ready"]:
required_size = sum(
model_download_size(model_key)
for model_key in REQUIRED_MODEL_KEYS
for model_key in DEFAULT_MODEL_KEYS
)
layout.label(
text="AnyImage AI features run in an isolated Python environment."
@@ -52,7 +52,7 @@ class SetupAIEnvironment(bpy.types.Operator):
)
layout.label(
text=(
"It also downloads MoGe-2 ViT-S Normal, BEN2 Base and "
"It also downloads MoGe-2 ViT-S Normal, BiRefNet Lite and "
"Real-ESRGAN General WDN x4v3 "
f"({format_download_size(required_size)})."
)
@@ -154,7 +154,7 @@ class DownloadRequiredModels(JobOperatorBase, bpy.types.Operator):
require_environment()
if not bpy.app.online_access:
raise RuntimeError("Online Access is disabled in Blender Preferences")
return {"models": list(REQUIRED_MODEL_KEYS)}
return {"models": list(DEFAULT_MODEL_KEYS)}
class ClearModels(bpy.types.Operator):

View File

@@ -6,7 +6,8 @@ from ...common.ai import invoke_ai_setup_if_needed, production_device, report_ai
from ...common.image import is_image_empty, video_total_frames
from ...common.color_image import prepare_material_color_input, material_analysis_input, cleanup_material_color_input
from ...preferences import addon_preferences, configured_max_ai_input_size
from ...properties import DEFAULT_MOGE2_MODEL_KEY
from ...preferences import configured_geometry_model
from ...server.model_catalog import DEFAULT_GEOMETRY_MODEL_KEY
from ...runtime import JobOperatorBase
from ...server.media.resolution import DEFAULT_MAX_AI_INPUT_SIZE, MAX_MAX_AI_INPUT_SIZE, MIN_MAX_AI_INPUT_SIZE
from .object import create_panorama_from_result
@@ -26,7 +27,7 @@ class GeneratePanorama(JobOperatorBase, bpy.types.Operator):
source_identity: bpy.props.StringProperty(options=_HIDDEN)
image_identity: bpy.props.StringProperty(options=_HIDDEN)
mesh_detail: bpy.props.IntProperty(default=7, min=0, max=8, options=_HIDDEN)
model: bpy.props.StringProperty(default=DEFAULT_MOGE2_MODEL_KEY, options=_HIDDEN)
model: bpy.props.StringProperty(default=DEFAULT_GEOMETRY_MODEL_KEY, options=_HIDDEN)
resolution_level: bpy.props.IntProperty(default=5, min=0, max=9, options=_HIDDEN)
max_input_size: bpy.props.IntProperty(default=DEFAULT_MAX_AI_INPUT_SIZE,
min=MIN_MAX_AI_INPUT_SIZE, max=MAX_MAX_AI_INPUT_SIZE, options=_HIDDEN)
@@ -82,7 +83,7 @@ class ConvertToPanorama(bpy.types.Operator):
input_path=str(path), color_path=str(color_path), source_object_name=source.name,
source_identity=str(source.as_pointer()),
image_identity=str(image.as_pointer()), mesh_detail=self.mesh_detail,
model=getattr(preferences, "geometry_model", DEFAULT_MOGE2_MODEL_KEY),
model=configured_geometry_model(context),
resolution_level=int(getattr(preferences, "geometry_resolution_level", 5)),
max_input_size=configured_max_ai_input_size(context),
)

View File

@@ -1,7 +1,7 @@
import bpy
from ...preferences import addon_preferences, configured_max_ai_input_size
from ...properties import DEFAULT_MOGE2_MODEL_KEY
from ...preferences import addon_preferences, configured_max_ai_input_size, configured_geometry_model
from ...server.model_catalog import DEFAULT_GEOMETRY_MODEL_KEY
from ...runtime import JobOperatorBase
from ...server.media.resolution import (
DEFAULT_MAX_AI_INPUT_SIZE,
@@ -37,7 +37,7 @@ _HIDDEN = {"HIDDEN", "SKIP_SAVE"}
def depth_plane_settings(context):
preferences = addon_preferences(context)
return (
getattr(preferences, "geometry_model", DEFAULT_MOGE2_MODEL_KEY),
configured_geometry_model(context),
int(getattr(preferences, "geometry_resolution_level", 5)),
)
@@ -59,7 +59,7 @@ class GenerateDepthPlane(JobOperatorBase, bpy.types.Operator):
options=_HIDDEN,
)
mesh_detail: bpy.props.IntProperty(default=6, min=0, max=10, options=_HIDDEN)
model: bpy.props.StringProperty(default=DEFAULT_MOGE2_MODEL_KEY, options=_HIDDEN)
model: bpy.props.StringProperty(default=DEFAULT_GEOMETRY_MODEL_KEY, options=_HIDDEN)
resolution_level: bpy.props.IntProperty(default=5, min=0, max=9, options=_HIDDEN)
max_input_size: bpy.props.IntProperty(
default=DEFAULT_MAX_AI_INPUT_SIZE,

View File

@@ -6,15 +6,16 @@ import bpy
from ...preferences import (
addon_preferences,
configured_geometry_model,
configured_max_ai_input_size,
configured_max_cutout_mesh_resolution,
)
from ...properties import (
DEFAULT_MOGE2_MODEL_KEY,
cutout_alpha_threshold_property,
cutout_fine_outline_property,
)
from ...runtime import JobOperatorBase
from ...server.model_catalog import DEFAULT_GEOMETRY_MODEL_KEY
from ...server.media.resolution import (
DEFAULT_MAX_AI_INPUT_SIZE,
MAX_MAX_AI_INPUT_SIZE,
@@ -169,7 +170,7 @@ def _generated_color_result(input_path, source_bounds):
def cutout_geometry_settings(context):
preferences = addon_preferences(context)
model = getattr(preferences, "geometry_model", DEFAULT_MOGE2_MODEL_KEY)
model = configured_geometry_model(context)
return (
model,
int(getattr(preferences, "geometry_resolution_level", 5)),
@@ -196,7 +197,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
options=_HIDDEN,
)
model: bpy.props.StringProperty(
default=DEFAULT_MOGE2_MODEL_KEY,
default=DEFAULT_GEOMETRY_MODEL_KEY,
options=_HIDDEN,
)
resolution_level: bpy.props.IntProperty(

View File

@@ -1,6 +1,6 @@
import bpy
from ..properties import BEN2_MODEL_KEY
from ..preferences import configured_background_model
from ..runtime import JobOperatorBase
from ..common.ai import (
require_input_path,
@@ -22,7 +22,7 @@ _HIDDEN = {"HIDDEN", "SKIP_SAVE"}
class RemoveImageBackground(bpy.types.Operator):
bl_idname = "anyimage.remove_image_background"
bl_label = "Remove Background"
bl_description = "Remove the image background with BEN2"
bl_description = "Remove the image background with the selected AI model"
bl_options = {"REGISTER"}
@classmethod
@@ -84,11 +84,12 @@ class RunBackgroundRemoval(JobOperatorBase, bpy.types.Operator):
def request(self, context):
require_environment()
require_model(BEN2_MODEL_KEY)
model = configured_background_model(context)
require_model(model)
settings = context.scene.anyimage_settings
parameters = {
"input": str(require_input_path(self.input_path)),
"model": BEN2_MODEL_KEY,
"model": model,
"device": production_device().lower(),
"video_frames": settings.video_frames,
"start_frame": self._image_target.start_frame,

View File

@@ -1,7 +1,8 @@
import bpy
from ..preferences import configured_max_ai_input_size
from ..properties import configured_upscale_model, upscale_model_label
from ..preferences import configured_upscale_model
from ..properties import upscale_model_label
from ..server.media.resolution import (
DEFAULT_MAX_AI_INPUT_SIZE,
MAX_MAX_AI_INPUT_SIZE,