forked from daminik124124-ops/ComfyUI-SAM3-JSON-Boxes-API
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjson_regions_to_sam3_boxes_api.py
More file actions
135 lines (105 loc) · 4.44 KB
/
Copy pathjson_regions_to_sam3_boxes_api.py
File metadata and controls
135 lines (105 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import json
class JSONRegionsToSAM3BoxesAPI:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"json_text": ("STRING", {
"multiline": True,
"default": '{\n "regions": [\n { "x1": 78, "y1": 0, "width": 234, "height": 119 }\n ]\n}'
}),
"padding": ("INT", {"default": 0, "min": 0, "max": 512, "step": 1}),
}
}
RETURN_TYPES = ("SAM3_BOXES_PROMPT", "SAM3_BOXES_PROMPT", "STRING")
RETURN_NAMES = ("positive_boxes", "negative_boxes", "debug_info")
FUNCTION = "run"
CATEGORY = "SAM3/prompts"
def _extract_regions(self, data):
if isinstance(data, list):
return data, []
if not isinstance(data, dict):
raise ValueError("JSON must be an object or a list of region objects.")
positive = (
data.get("positive_regions")
or data.get("regions")
or data.get("boxes")
or data.get("positive_boxes")
or []
)
negative = data.get("negative_regions") or data.get("negative_boxes") or []
return positive, negative
def _region_to_xyxy(self, r, idx):
if isinstance(r, dict):
if all(k in r for k in ("x1", "y1", "x2", "y2")):
return float(r["x1"]), float(r["y1"]), float(r["x2"]), float(r["y2"])
if all(k in r for k in ("x1", "y1", "width", "height")):
x1 = float(r["x1"])
y1 = float(r["y1"])
return x1, y1, x1 + float(r["width"]), y1 + float(r["height"])
if all(k in r for k in ("x", "y", "w", "h")):
x1 = float(r["x"])
y1 = float(r["y"])
return x1, y1, x1 + float(r["w"]), y1 + float(r["h"])
if all(k in r for k in ("x", "y", "width", "height")):
x1 = float(r["x"])
y1 = float(r["y"])
return x1, y1, x1 + float(r["width"]), y1 + float(r["height"])
raise ValueError(
f"Bad region at index {idx}: {r}. "
"Supported: x1/y1/x2/y2, x1/y1/width/height, x/y/w/h, x/y/width/height."
)
if isinstance(r, (list, tuple)) and len(r) >= 4:
return tuple(map(float, r[:4]))
raise ValueError(f"Bad region at index {idx}: {r}")
def _to_prompt(self, regions, label, img_w, img_h, padding):
boxes = []
labels = []
for idx, r in enumerate(regions):
x1, y1, x2, y2 = self._region_to_xyxy(r, idx)
if x2 < x1:
x1, x2 = x2, x1
if y2 < y1:
y1, y2 = y2, y1
x1 = max(0.0, x1 - padding)
y1 = max(0.0, y1 - padding)
x2 = min(float(img_w), x2 + padding)
y2 = min(float(img_h), y2 + padding)
if x2 <= x1 or y2 <= y1:
continue
x1n = x1 / img_w
y1n = y1 / img_h
x2n = x2 / img_w
y2n = y2 / img_h
cx = (x1n + x2n) / 2.0
cy = (y1n + y2n) / 2.0
w = x2n - x1n
h = y2n - y1n
boxes.append([cx, cy, w, h])
labels.append(bool(label))
return {"boxes": boxes, "labels": labels}
def run(self, image, json_text, padding=0):
img_h = int(image.shape[1])
img_w = int(image.shape[2])
if not str(json_text).strip():
raise ValueError("json_text is empty.")
data = json.loads(json_text)
positive_regions, negative_regions = self._extract_regions(data)
positive_prompt = self._to_prompt(positive_regions, True, img_w, img_h, padding)
negative_prompt = self._to_prompt(negative_regions, False, img_w, img_h, padding)
debug = (
f"Mode: API/json_text\n"
f"Image: {img_w}x{img_h}\n"
f"Positive regions: {len(positive_regions)} -> boxes: {len(positive_prompt['boxes'])}\n"
f"Negative regions: {len(negative_regions)} -> boxes: {len(negative_prompt['boxes'])}\n"
f"Padding: {padding}px\n"
f"Supported input: x1/y1/x2/y2, x1/y1/width/height, x/y/w/h"
)
return (positive_prompt, negative_prompt, debug)
NODE_CLASS_MAPPINGS = {
"JSONRegionsToSAM3BoxesAPI": JSONRegionsToSAM3BoxesAPI,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"JSONRegionsToSAM3BoxesAPI": "JSON Regions to SAM3 Boxes API",
}