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

@@ -98,104 +98,9 @@ def export_realesrgan_wdn_x4(checkpoint_path, destination):
_validate_export(onnx, onnxruntime, torch, model, sample, destination)
def export_safmn_real_x4(checkpoint_path, destination):
onnx, onnxruntime, torch = _require_build_dependencies()
nn = torch.nn
functional = torch.nn.functional
class LayerNorm(nn.Module):
def __init__(self, channels):
super().__init__()
self.weight = nn.Parameter(torch.ones(channels))
self.bias = nn.Parameter(torch.zeros(channels))
self.eps = 1e-6
def forward(self, value):
mean = value.mean(1, keepdim=True)
variance = (value - mean).pow(2).mean(1, keepdim=True)
value = (value - mean) / torch.sqrt(variance + self.eps)
return self.weight[:, None, None] * value + self.bias[:, None, None]
class SAFM(nn.Module):
def __init__(self, channels):
super().__init__()
chunk_channels = channels // 4
self.mfr = nn.ModuleList(
nn.Conv2d(chunk_channels, chunk_channels, 3, 1, 1, groups=chunk_channels)
for _ in range(4)
)
self.aggr = nn.Conv2d(channels, channels, 1)
self.act = nn.GELU()
def forward(self, value):
height, width = value.shape[-2:]
outputs = []
for level, chunk in enumerate(value.chunk(4, dim=1)):
if level:
size = (height // 2**level, width // 2**level)
chunk = functional.adaptive_max_pool2d(chunk, size)
chunk = self.mfr[level](chunk)
if level:
chunk = functional.interpolate(
chunk,
size=(height, width),
mode="nearest",
)
outputs.append(chunk)
return self.act(self.aggr(torch.cat(outputs, dim=1))) * value
class CCM(nn.Module):
def __init__(self, channels):
super().__init__()
self.ccm = nn.Sequential(
nn.Conv2d(channels, channels * 2, 3, 1, 1),
nn.GELU(),
nn.Conv2d(channels * 2, channels, 1),
)
def forward(self, value):
return self.ccm(value)
class AttBlock(nn.Module):
def __init__(self, channels):
super().__init__()
self.norm1 = LayerNorm(channels)
self.norm2 = LayerNorm(channels)
self.safm = SAFM(channels)
self.ccm = CCM(channels)
def forward(self, value):
value = self.safm(self.norm1(value)) + value
return self.ccm(self.norm2(value)) + value
class SAFMN(nn.Module):
def __init__(self):
super().__init__()
self.to_feat = nn.Conv2d(3, 128, 3, 1, 1)
self.feats = nn.Sequential(*(AttBlock(128) for _ in range(16)))
self.to_img = nn.Sequential(
nn.Conv2d(128, 3 * 4**2, 3, 1, 1),
nn.PixelShuffle(4),
)
def forward(self, value):
value = self.to_feat(value)
return self.to_img(self.feats(value) + value)
model = SAFMN()
model.load_state_dict(
_load_weights(torch, checkpoint_path, "params_ema"),
strict=True,
)
sample = torch.rand(1, 3, 256, 256)
_export(torch, model, sample, destination)
_validate_export(onnx, onnxruntime, torch, model, sample, destination)
def export_model(exporter, checkpoint_path, destination):
exporters = {
"realesrgan_wdn_x4": export_realesrgan_wdn_x4,
"safmn_real_x4": export_safmn_real_x4,
}
try:
export = exporters[exporter]