Feather Smart Tile focus patches without masks

This commit is contained in:
TTPlanetPIG
2026-07-03 23:13:36 +08:00
parent e078f6c90f
commit b7faaa090b
2 changed files with 94 additions and 0 deletions
+39
View File
@@ -749,6 +749,43 @@ def _ttp_tile_object_mask_array(tile, out_width, out_height, mask_blend_mode="ma
return array[:, :, None]
def _ttp_should_rect_feather_tile(tile, is_large_tile, mask_blend_mode):
if str(mask_blend_mode) not in ("auto", "mask_feather"):
return False
if is_large_tile or tile.get("object_mask"):
return False
label = str(tile.get("label", "") or "").lower()
name = str(tile.get("name", "") or "").lower()
source = str(tile.get("source", "") or "").lower()
text = " ".join((label, name, source))
if any(word in text for word in ("background", "context", "full image", "full_image", "large context")):
return False
focus_keywords = (
"face", "head", "eye", "eyes", "eyelash", "eyebrow", "glasses",
"hand", "hands", "finger", "fingers", "mouth", "lip", "lips",
"teeth", "nose", "ear", "ears", "text", "letter", "logo",
"detail", "focus", "foreground", "object",
)
if any(keyword in text for keyword in focus_keywords):
return True
if source in ("auto", "paint", "sam", "sam3", "sam3.1", "qwen", "qwenvl"):
return float(tile.get("occlusion_priority", 0.0)) >= 100.0 or float(tile.get("layer", 0.0)) >= 2.0
edges = tile.get("overlap_edges_px_source", {})
has_surrounding_overlap = all(float(edges.get(edge, 0.0)) > 0.0 for edge in ("left", "right", "top", "bottom"))
return has_surrounding_overlap and (
float(tile.get("occlusion_priority", 0.0)) > 0.0 or float(tile.get("layer", 0.0)) > 0.0
)
def _ttp_rect_feather_mask(width, height, feather):
feather = int(round(float(feather)))
if feather <= 0:
return np.ones((height, width, 1), dtype=np.float32)
edge_limit = max(1, min(int(width), int(height)) // 3)
feather = max(1, min(feather, edge_limit))
return _ttp_create_feather_mask(int(width), int(height), feather)
def _ttp_decode_interactive_paint_mask(mask_payload, image_width, image_height):
try:
payload = json.loads(str(mask_payload or "").strip() or "{}")
@@ -3957,6 +3994,8 @@ class TTP_Smart_Tile_Assemble_Experimental:
mask = object_mask
else:
mask = mask * object_mask
elif _ttp_should_rect_feather_tile(tile, is_large_tile, str(mask_blend_mode)):
mask = mask * _ttp_rect_feather_mask(out_w, out_h, blend)
coverage_mask = np.clip(mask.copy(), 0.0, 1.0)
importance = max(0.0, float(tile.get("importance", 1.0)))
priority = max(0.0, float(tile.get("priority", 50.0)))
+55
View File
@@ -1019,6 +1019,61 @@ small_above, _small_above_weights = assemble_node.assemble_tiles(
small_above_pixel = small_above.array[0, 4, 4]
assert_equal(float(small_above_pixel[2]) > float(small_above_pixel[0]), True, "small_tile_on_top should stack smaller tiles above larger context tiles")
feather_stack_meta = {
"type": "ttp_smart_tile",
"original_size": [16, 16],
"tiles": [
{
"name": "background",
"label": "background",
"core_box": [0, 0, 16, 16],
"sample_box": [0, 0, 16, 16],
"tile_canvas_size": [16, 16],
"tile_canvas_box": [0, 0, 16, 16],
"overlap_edges_px_source": {"left": 0, "right": 0, "top": 0, "bottom": 0},
"blend": 0,
"importance": 1.0,
"priority": 0.0,
"layer": 0,
"occlusion_priority": 0,
},
{
"name": "face_detail",
"label": "face detail",
"core_box": [4, 4, 8, 8],
"sample_box": [4, 4, 8, 8],
"tile_canvas_size": [8, 8],
"tile_canvas_box": [0, 0, 8, 8],
"overlap_edges_px_source": {"left": 0, "right": 0, "top": 0, "bottom": 0},
"blend": 4,
"importance": 1.0,
"priority": 0.0,
"layer": 2,
"occlusion_priority": 1000,
},
],
}
feather_tile_set = {
"type": "ttp_smart_tile_set",
"tile_meta": feather_stack_meta,
"tile_images": [
ttp.pil2tensor(Image.new("RGB", (16, 16), (220, 20, 20)))[0],
ttp.pil2tensor(Image.new("RGB", (8, 8), (20, 20, 220)))[0],
],
}
feathered_stack, _feathered_weights = assemble_node.assemble_tiles(
blend_multiplier=1.0,
output_scale=1.0,
use_priority=True,
base_canvas_mode="black",
mask_blend_mode="mask_feather",
tile_set=feather_tile_set,
)
feather_edge_pixel = feathered_stack.array[0, 4, 8]
feather_center_pixel = feathered_stack.array[0, 8, 8]
assert_equal(float(feather_edge_pixel[0]) > float(feather_edge_pixel[2]), True, "focus tile fallback feather should keep lower pixels visible at the rectangle edge")
assert_equal(float(feather_center_pixel[2]) > float(feather_center_pixel[0]), True, "focus tile fallback feather should keep the focus tile strong in the center")
deferred_output, deferred_weights = assemble_node.assemble_tiles(
blend_multiplier=1.0,
output_scale=1.0,