implemented new feats, fixed bugs.
This commit is contained in:
@@ -5,3 +5,4 @@ __pycache__
|
||||
*.pth
|
||||
*.ini
|
||||
py/nodes_xfeat.py
|
||||
examples/xfeat
|
||||
@@ -1,902 +0,0 @@
|
||||
{
|
||||
"last_node_id": 178,
|
||||
"last_link_id": 279,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 68,
|
||||
"type": "AG Blur Median",
|
||||
"pos": [
|
||||
-724.2387084960938,
|
||||
310.97021484375
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 80
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dst",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
232
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Blur Median"
|
||||
},
|
||||
"widgets_values": [
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 45,
|
||||
"type": "LoadImage",
|
||||
"pos": [
|
||||
-1346.0712890625,
|
||||
311.7904357910156
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
314
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
139
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "v0.3.10",
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"01_Raw.jpg",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 167,
|
||||
"type": "AG Threshold",
|
||||
"pos": [
|
||||
-132.14578247070312,
|
||||
311.88262939453125
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
106
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 231
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dst",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
233
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Threshold"
|
||||
},
|
||||
"widgets_values": [
|
||||
30,
|
||||
255,
|
||||
"BINARY"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 81,
|
||||
"type": "AG Bitwise Not",
|
||||
"pos": [
|
||||
-368.5704345703125,
|
||||
311.11083984375
|
||||
],
|
||||
"size": [
|
||||
214.20001220703125,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 232
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dst",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
231
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Bitwise Not"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"type": "AG To Nparray",
|
||||
"pos": [
|
||||
-968.87451171875,
|
||||
312.17279052734375
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 139
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
80,
|
||||
276
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG To Nparray"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"type": "AG Hough Lines Accumulator",
|
||||
"pos": [
|
||||
223.89540100097656,
|
||||
310.5812683105469
|
||||
],
|
||||
"size": [
|
||||
365.4000244140625,
|
||||
198
|
||||
],
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "np_bins",
|
||||
"type": "NPARRAY",
|
||||
"link": 233
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dst",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
137,
|
||||
260,
|
||||
277
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "ticks",
|
||||
"type": "*",
|
||||
"links": [
|
||||
261
|
||||
],
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Hough Lines Accumulator"
|
||||
},
|
||||
"widgets_values": [
|
||||
0.19999999999999998,
|
||||
0,
|
||||
90,
|
||||
1,
|
||||
-0.505324,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 178,
|
||||
"type": "AG K Largest Points",
|
||||
"pos": [
|
||||
803.9663696289062,
|
||||
734.7616577148438
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
78
|
||||
],
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 277
|
||||
},
|
||||
{
|
||||
"name": "ticks",
|
||||
"type": "*",
|
||||
"link": null,
|
||||
"shape": 7
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "points",
|
||||
"type": "*",
|
||||
"links": [
|
||||
278
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG K Largest Points"
|
||||
},
|
||||
"widgets_values": [
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 175,
|
||||
"type": "AG K Largest Points",
|
||||
"pos": [
|
||||
808.4725952148438,
|
||||
1024.1015625
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
78
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 260
|
||||
},
|
||||
{
|
||||
"name": "ticks",
|
||||
"type": "*",
|
||||
"link": 261,
|
||||
"shape": 7
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "points",
|
||||
"type": "*",
|
||||
"links": [
|
||||
266
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG K Largest Points"
|
||||
},
|
||||
"widgets_values": [
|
||||
50
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 149,
|
||||
"type": "AG Draw Circles",
|
||||
"pos": [
|
||||
1054.504638671875,
|
||||
738.0717163085938
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
126
|
||||
],
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 250
|
||||
},
|
||||
{
|
||||
"name": "points",
|
||||
"type": "*",
|
||||
"link": 278
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dst",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
244
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Draw Circles"
|
||||
},
|
||||
"widgets_values": [
|
||||
10,
|
||||
"RED",
|
||||
-1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 169,
|
||||
"type": "AG Point to Line Params",
|
||||
"pos": [
|
||||
1060.9705810546875,
|
||||
1067.9710693359375
|
||||
],
|
||||
"size": [
|
||||
218.39999389648438,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "points",
|
||||
"type": "*",
|
||||
"link": 266
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "lines",
|
||||
"type": "*",
|
||||
"links": [
|
||||
240
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Point to Line Params"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 170,
|
||||
"type": "AG Hough Lines Plotter",
|
||||
"pos": [
|
||||
1331.994384765625,
|
||||
986.458740234375
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
102
|
||||
],
|
||||
"flags": {},
|
||||
"order": 14,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 276
|
||||
},
|
||||
{
|
||||
"name": "lines",
|
||||
"type": "*",
|
||||
"link": 240
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dst",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
242
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Hough Lines Plotter"
|
||||
},
|
||||
"widgets_values": [
|
||||
"GREEN",
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 137,
|
||||
"type": "AG To Image",
|
||||
"pos": [
|
||||
1604.931884765625,
|
||||
985.5106811523438
|
||||
],
|
||||
"size": [
|
||||
140,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 16,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 242
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
157
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG To Image"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 171,
|
||||
"type": "AG To Image",
|
||||
"pos": [
|
||||
1603.8665771484375,
|
||||
739.9141845703125
|
||||
],
|
||||
"size": [
|
||||
140,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 244
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
245
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG To Image"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 114,
|
||||
"type": "AG Colormap Hot Gray",
|
||||
"pos": [
|
||||
737.6822509765625,
|
||||
311.9242858886719
|
||||
],
|
||||
"size": [
|
||||
289.79998779296875,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 137
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "dst",
|
||||
"type": "NPARRAY",
|
||||
"links": [
|
||||
138,
|
||||
250
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG Colormap Hot Gray"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 82,
|
||||
"type": "AG To Image",
|
||||
"pos": [
|
||||
1592.6949462890625,
|
||||
317.0210266113281
|
||||
],
|
||||
"size": [
|
||||
140,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "nparrays",
|
||||
"type": "NPARRAY",
|
||||
"link": 138
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
120
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "archigraph",
|
||||
"ver": "02af834a760fe748de630be7273371718830ac54",
|
||||
"Node name for S&R": "AG To Image"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 172,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1840,
|
||||
650
|
||||
],
|
||||
"size": [
|
||||
317.501953125,
|
||||
255.177001953125
|
||||
],
|
||||
"flags": {},
|
||||
"order": 15,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 245
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "v0.3.10",
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 138,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1846.479248046875,
|
||||
996.264892578125
|
||||
],
|
||||
"size": [
|
||||
638.4635620117188,
|
||||
247.25985717773438
|
||||
],
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 157
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "v0.3.10",
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 75,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
1841.726318359375,
|
||||
313.2637634277344
|
||||
],
|
||||
"size": [
|
||||
305.1395263671875,
|
||||
253.12794494628906
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 120
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "v0.3.10",
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
80,
|
||||
56,
|
||||
0,
|
||||
68,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
120,
|
||||
82,
|
||||
0,
|
||||
75,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
137,
|
||||
85,
|
||||
0,
|
||||
114,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
138,
|
||||
114,
|
||||
0,
|
||||
82,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
139,
|
||||
45,
|
||||
0,
|
||||
56,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
157,
|
||||
137,
|
||||
0,
|
||||
138,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
231,
|
||||
81,
|
||||
0,
|
||||
167,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
232,
|
||||
68,
|
||||
0,
|
||||
81,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
233,
|
||||
167,
|
||||
0,
|
||||
85,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
240,
|
||||
169,
|
||||
0,
|
||||
170,
|
||||
1,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
242,
|
||||
170,
|
||||
0,
|
||||
137,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
244,
|
||||
149,
|
||||
0,
|
||||
171,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
245,
|
||||
171,
|
||||
0,
|
||||
172,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
250,
|
||||
114,
|
||||
0,
|
||||
149,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
260,
|
||||
85,
|
||||
0,
|
||||
175,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
261,
|
||||
85,
|
||||
1,
|
||||
175,
|
||||
1,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
266,
|
||||
175,
|
||||
0,
|
||||
169,
|
||||
0,
|
||||
"*"
|
||||
],
|
||||
[
|
||||
276,
|
||||
56,
|
||||
0,
|
||||
170,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
277,
|
||||
85,
|
||||
0,
|
||||
178,
|
||||
0,
|
||||
"NPARRAY"
|
||||
],
|
||||
[
|
||||
278,
|
||||
178,
|
||||
0,
|
||||
149,
|
||||
1,
|
||||
"*"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.26734863064137815,
|
||||
"offset": [
|
||||
2324.5850053987074,
|
||||
671.1494564543829
|
||||
]
|
||||
},
|
||||
"node_versions": {
|
||||
"ComfyUI-ArchiGraph": "02af834a760fe748de630be7273371718830ac54",
|
||||
"comfy-core": "v0.3.10",
|
||||
"comfyui-custom-scripts": "1.2.5"
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// below implementation is from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
||||
|
||||
import { app } from "../../../scripts/app.js";
|
||||
import { ComfyWidgets } from "../../../scripts/widgets.js";
|
||||
|
||||
// Displays input text on a node
|
||||
|
||||
app.registerExtension({
|
||||
name: "AG Printf",
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === "AG Printf") {
|
||||
function populate(text) {
|
||||
if (this.widgets) {
|
||||
// On older frontend versions there is a hidden converted-widget
|
||||
const isConvertedWidget = +!!this.inputs?.[0].widget;
|
||||
for (let i = isConvertedWidget; i < this.widgets.length; i++) {
|
||||
this.widgets[i].onRemove?.();
|
||||
}
|
||||
this.widgets.length = isConvertedWidget;
|
||||
}
|
||||
|
||||
const v = [...text];
|
||||
if (!v[0]) {
|
||||
v.shift();
|
||||
}
|
||||
for (let list of v) {
|
||||
// Force list to be an array, not sure why sometimes it is/isn't
|
||||
if (!(list instanceof Array)) list = [list];
|
||||
for (const l of list) {
|
||||
const w = ComfyWidgets["STRING"](this, "text_" + this.widgets?.length ?? 0, ["STRING", { multiline: true }], app).widget;
|
||||
w.inputEl.readOnly = true;
|
||||
w.inputEl.style.opacity = 0.6;
|
||||
w.value = l;
|
||||
}
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const sz = this.computeSize();
|
||||
if (sz[0] < this.size[0]) {
|
||||
sz[0] = this.size[0];
|
||||
}
|
||||
if (sz[1] < this.size[1]) {
|
||||
sz[1] = this.size[1];
|
||||
}
|
||||
this.onResize?.(sz);
|
||||
app.graph.setDirtyCanvas(true, false);
|
||||
});
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments);
|
||||
populate.call(this, message.text);
|
||||
};
|
||||
|
||||
const VALUES = Symbol();
|
||||
const configure = nodeType.prototype.configure;
|
||||
nodeType.prototype.configure = function () {
|
||||
// Store unmodified widget values as they get removed on configure by new frontend
|
||||
this[VALUES] = arguments[0]?.widgets_values;
|
||||
return configure?.apply(this, arguments);
|
||||
};
|
||||
|
||||
const onConfigure = nodeType.prototype.onConfigure;
|
||||
nodeType.prototype.onConfigure = function () {
|
||||
onConfigure?.apply(this, arguments);
|
||||
const widgets_values = this[VALUES];
|
||||
if (widgets_values?.length) {
|
||||
// In newer frontend there seems to be a delay in creating the initial widget
|
||||
requestAnimationFrame(() => {
|
||||
populate.call(this, widgets_values.slice(+(widgets_values.length > 1 && this.inputs?.[0].widget)));
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
+17
-1
@@ -25,6 +25,8 @@ try:
|
||||
"JsonLoad",
|
||||
"NparraySave",
|
||||
"NparrayLoad",
|
||||
"SaveNparrayImage",
|
||||
"PreviewNparrayImage",
|
||||
|
||||
"Printf",
|
||||
"SliceList",
|
||||
@@ -55,6 +57,7 @@ try:
|
||||
"MedianBlur",
|
||||
"Blur",
|
||||
|
||||
"Sobel",
|
||||
"Canny",
|
||||
"HoughLines",
|
||||
"HoughLinesP",
|
||||
@@ -86,9 +89,22 @@ try:
|
||||
"KLargestPoints",
|
||||
"PointToLineParams",
|
||||
"IntersectImageWithMask",
|
||||
|
||||
"MorphologyByRegions",
|
||||
"MorphologyByRegionsWithStats",
|
||||
|
||||
"DrawQuiver",
|
||||
"LabelsToContours",
|
||||
"ContoursToLabels",
|
||||
"LabelsPreview",
|
||||
"CreateGVF",
|
||||
"GVFSnake",
|
||||
|
||||
):
|
||||
if hasattr(nodes_xfeat, name):
|
||||
AddMapping(getattr(nodes_xfeat, name))
|
||||
|
||||
# from .py.nodes_xfeat import MorphCloseByRegions
|
||||
# AddMapping(MorphCloseByRegions)
|
||||
|
||||
except ImportError as e:
|
||||
log("Premium nodes ignored", msg_color="BRIGHT_GREEN")
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import cv2
|
||||
|
||||
ColorEnum = {
|
||||
"WHITE": (255, 255, 255),
|
||||
"BLACK": (0, 0, 0),
|
||||
"RED": (0, 0, 255),
|
||||
"GREEN": (0, 255, 0),
|
||||
"BLUE": (255, 0, 0),
|
||||
}
|
||||
|
||||
ThreshEnum = {
|
||||
"BINARY": cv2.THRESH_BINARY,
|
||||
"BINARY_INV": cv2.THRESH_BINARY_INV,
|
||||
"TRUNC": cv2.THRESH_TRUNC,
|
||||
"TOZERO": cv2.THRESH_TOZERO,
|
||||
"TOZERO_INV": cv2.THRESH_TOZERO_INV,
|
||||
}
|
||||
|
||||
BorderEnum = {
|
||||
"DEFAULT": cv2.BORDER_DEFAULT,
|
||||
"CONSTANT": cv2.BORDER_CONSTANT,
|
||||
"REPLICATE": cv2.BORDER_REPLICATE,
|
||||
"WRAP": cv2.BORDER_WRAP,
|
||||
}
|
||||
|
||||
ContourModeEnum = {
|
||||
"RETR_EXTERNAL": cv2.RETR_EXTERNAL,
|
||||
"RETR_LIST": cv2.RETR_LIST,
|
||||
"RETR_CCOMP": cv2.RETR_CCOMP,
|
||||
"RETR_TREE": cv2.RETR_TREE,
|
||||
}
|
||||
|
||||
ContourMethodEnum = {
|
||||
"CHAIN_APPROX_NONE": cv2.CHAIN_APPROX_NONE,
|
||||
"CHAIN_APPROX_SIMPLE": cv2.CHAIN_APPROX_SIMPLE,
|
||||
"CHAIN_APPROX_TC89_L1": cv2.CHAIN_APPROX_TC89_L1,
|
||||
"CHAIN_APPROX_TC89_KCOS": cv2.CHAIN_APPROX_TC89_KCOS,
|
||||
}
|
||||
|
||||
MorphEnum = {
|
||||
"ERODE": cv2.MORPH_ERODE,
|
||||
"DILATE": cv2.MORPH_DILATE,
|
||||
"OPEN": cv2.MORPH_OPEN,
|
||||
"CLOSE": cv2.MORPH_CLOSE,
|
||||
"GRADIENT": cv2.MORPH_GRADIENT,
|
||||
"TOPHAT": cv2.MORPH_TOPHAT,
|
||||
"BLACKHAT": cv2.MORPH_BLACKHAT,
|
||||
}
|
||||
|
||||
DepthEnum = {
|
||||
"CV_8U": cv2.CV_8U,
|
||||
"CV_8S": cv2.CV_8S,
|
||||
"CV_16U": cv2.CV_16U,
|
||||
"CV_16S": cv2.CV_16S,
|
||||
"CV_32S": cv2.CV_32S,
|
||||
"CV_32F": cv2.CV_32F,
|
||||
"CV_64F": cv2.CV_64F,
|
||||
}
|
||||
|
||||
ColormapEnum = {
|
||||
'AUTUMN': cv2.COLORMAP_AUTUMN,
|
||||
'BONE': cv2.COLORMAP_BONE,
|
||||
'JET': cv2.COLORMAP_JET,
|
||||
'WINTER': cv2.COLORMAP_WINTER,
|
||||
'RAINBOW': cv2.COLORMAP_RAINBOW, # 真正的彩虹色阶
|
||||
'OCEAN': cv2.COLORMAP_OCEAN,
|
||||
'SUMMER': cv2.COLORMAP_SUMMER,
|
||||
'SPRING': cv2.COLORMAP_SPRING,
|
||||
'COOL': cv2.COLORMAP_COOL,
|
||||
'HSV': cv2.COLORMAP_HSV,
|
||||
'PINK': cv2.COLORMAP_PINK,
|
||||
'HOT': cv2.COLORMAP_HOT,
|
||||
'PARULA': cv2.COLORMAP_PARULA,
|
||||
'MAGMA': cv2.COLORMAP_MAGMA,
|
||||
'INFERNO': cv2.COLORMAP_INFERNO,
|
||||
'PLASMA': cv2.COLORMAP_PLASMA,
|
||||
'VIRIDIS': cv2.COLORMAP_VIRIDIS,
|
||||
'CIVIDIS': cv2.COLORMAP_CIVIDIS,
|
||||
'TWILIGHT': cv2.COLORMAP_TWILIGHT,
|
||||
'TWILIGHT_SHIFTED': cv2.COLORMAP_TWILIGHT_SHIFTED,
|
||||
'TURBO': cv2.COLORMAP_TURBO,
|
||||
'DEEPGREEN': cv2.COLORMAP_DEEPGREEN,
|
||||
}
|
||||
+116
-129
@@ -1,10 +1,12 @@
|
||||
CURRENT_CATEGORY = "🐦🔥 ArchiGraph/⭕ OpenCV" # organized by py file
|
||||
CURRENT_CATEGORY = "🐦🔥 ArchiGraph/📀 OpenCV" # organized by py file
|
||||
|
||||
import torch
|
||||
import numpy as np
|
||||
import cv2
|
||||
import comfy.model_management as model_management
|
||||
from .anyType import ANY
|
||||
from .enums import ColorEnum, ThreshEnum, BorderEnum, ContourModeEnum, \
|
||||
ContourMethodEnum, DepthEnum, ColormapEnum
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
# Helpers
|
||||
@@ -133,19 +135,11 @@ class Threshold:
|
||||
DESCRIPTION = "OpenCV Threshold. Applies a fixed-level threshold to each array element."
|
||||
|
||||
def execute(self, nparrays, thresh, maxval, type):
|
||||
TypeEnum = {
|
||||
"BINARY": cv2.THRESH_BINARY,
|
||||
"BINARY_INV": cv2.THRESH_BINARY_INV,
|
||||
"TRUNC": cv2.THRESH_TRUNC,
|
||||
"TOZERO": cv2.THRESH_TOZERO,
|
||||
"TOZERO_INV": cv2.THRESH_TOZERO_INV,
|
||||
}
|
||||
|
||||
batch_size = nparrays.shape[0]
|
||||
result = np.zeros_like(nparrays)
|
||||
|
||||
for b in range(batch_size):
|
||||
_, result[b] = cv2.threshold(nparrays[b], thresh, maxval, TypeEnum[type])
|
||||
_, result[b] = cv2.threshold(nparrays[b], thresh, maxval, ThreshEnum[type])
|
||||
|
||||
return (result,)
|
||||
|
||||
@@ -195,12 +189,6 @@ class GaussianBlur:
|
||||
DESCRIPTION = "OpenCV GaussianBlur. Default anchor is center of the kernel."
|
||||
|
||||
def execute(self, nparrays, ksize_x, ksize_y, sigma_x, sigma_y, borderType):
|
||||
BorderEnum = {
|
||||
"DEFAULT": cv2.BORDER_DEFAULT,
|
||||
"CONSTANT": cv2.BORDER_CONSTANT,
|
||||
"REPLICATE": cv2.BORDER_REPLICATE,
|
||||
"WRAP": cv2.BORDER_WRAP,
|
||||
}
|
||||
if ksize_y == 1: ksize_y = ksize_x
|
||||
|
||||
batch_size = nparrays.shape[0]
|
||||
@@ -282,12 +270,6 @@ class Blur:
|
||||
DESCRIPTION = "OpenCV Blur. Default anchor is center of the kernel."
|
||||
|
||||
def execute(self, nparrays, ksize_x, ksize_y, borderType):
|
||||
BorderEnum = {
|
||||
"DEFAULT": cv2.BORDER_DEFAULT,
|
||||
"CONSTANT": cv2.BORDER_CONSTANT,
|
||||
"REPLICATE": cv2.BORDER_REPLICATE,
|
||||
"WRAP": cv2.BORDER_WRAP,
|
||||
}
|
||||
if ksize_y == 1: ksize_y = ksize_x
|
||||
|
||||
batch_size = nparrays.shape[0]
|
||||
@@ -297,7 +279,71 @@ class Blur:
|
||||
result[b] = cv2.blur(nparrays[b], (ksize_x, ksize_y), borderType=BorderEnum[borderType])
|
||||
|
||||
return (result,)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class Sobel:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"nparrays": ("NPARRAY",),
|
||||
"dx": ("INT", {
|
||||
"default": 1,
|
||||
"min": 0,
|
||||
"max": 2,
|
||||
"tooltip": "Order of the derivative x."
|
||||
}),
|
||||
"dy": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 2,
|
||||
"tooltip": "Order of the derivative y."
|
||||
}),
|
||||
"ksize": ("INT", {
|
||||
"default": 3,
|
||||
"min": 1,
|
||||
"max": 31,
|
||||
"step": 2, # Must be odd number
|
||||
"tooltip": "Aperture size for the Sobel operator. Must be odd and greater than 1."
|
||||
}),
|
||||
"ddepth": (["CV_8U", "CV_8S", "CV_16U", "CV_16S", "CV_32S", "CV_32F", "CV_64F"], {
|
||||
"default": "CV_32F",
|
||||
"tooltip": "Desired depth of the destination image. Use CV_32F/64F for float output."
|
||||
}),
|
||||
"converted": ("BOOLEAN", {
|
||||
"default": True,
|
||||
"tooltip": "If true, the output will be converted with cv2.convertScaleAbs() to 8-bit unsigned integer. If false, the output will remain unprocessed."
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("NPARRAY",)
|
||||
RETURN_NAMES = ("dst",)
|
||||
FUNCTION = "execute"
|
||||
NAME = "Sobel"
|
||||
CATEGORY = CURRENT_CATEGORY
|
||||
DESCRIPTION = "OpenCV Sobel. Computes the first, second, third, or mixed image derivatives using an extended Sobel operator."
|
||||
|
||||
def execute(self, nparrays, dx, dy, ksize, ddepth, converted):
|
||||
batch_size = nparrays.shape[0]
|
||||
|
||||
batches = []
|
||||
for b in range(batch_size):
|
||||
# force input to be gray
|
||||
if (len(nparrays[b].shape) == 3 and nparrays[b].shape[2] == 3):
|
||||
gray = cv2.cvtColor(nparrays[b], cv2.COLOR_BGR2GRAY)
|
||||
else:
|
||||
gray = nparrays[b]
|
||||
|
||||
if converted:
|
||||
batch = cv2.convertScaleAbs(cv2.Sobel(gray, DepthEnum[ddepth], dx, dy, ksize=ksize))
|
||||
else:
|
||||
batch = cv2.Sobel(gray, DepthEnum[ddepth], dx, dy, ksize=ksize)
|
||||
batches.append(batch)
|
||||
|
||||
result = np.stack(batches, axis=0)
|
||||
return (result,)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class Canny:
|
||||
@classmethod
|
||||
@@ -396,7 +442,7 @@ class HoughLines:
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = (ANY,)
|
||||
RETURN_TYPES = ("LIST_LINE",)
|
||||
RETURN_NAMES = ("lines",)
|
||||
FUNCTION = "execute"
|
||||
NAME = "Hough Lines"
|
||||
@@ -459,7 +505,7 @@ class HoughLinesP:
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = (ANY,)
|
||||
RETURN_TYPES = ("LIST_LINE",)
|
||||
RETURN_NAMES = ("lines",)
|
||||
FUNCTION = "execute"
|
||||
NAME = "Hough Lines Probabilistic"
|
||||
@@ -498,7 +544,7 @@ class FindContours:
|
||||
"tooltip": "Binary image. Non-zero pixels are treated as 1's. \nYou can use compare(), threshold(), inRange(), Canny() etc. to create a binary image."
|
||||
}),
|
||||
"mode": (["RETR_EXTERNAL", "RETR_LIST", "RETR_CCOMP", "RETR_TREE"], {
|
||||
"default": "RETR_EXTERNAL",
|
||||
"default": "RETR_CCOMP",
|
||||
"tooltip": "Contour retrieval mode"
|
||||
}),
|
||||
"method": (["CHAIN_APPROX_NONE", "CHAIN_APPROX_SIMPLE", "CHAIN_APPROX_TC89_L1", "CHAIN_APPROX_TC89_KCOS"], {
|
||||
@@ -508,8 +554,9 @@ class FindContours:
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = (ANY, ANY, )
|
||||
RETURN_NAMES = ("contours", "hierachy", )
|
||||
SINGULAR_ONLY = True
|
||||
RETURN_TYPES = ("CV_CONTOURS", "CV_HIERARCHY", )
|
||||
RETURN_NAMES = ("contours", "hierarchy", )
|
||||
FUNCTION = "execute"
|
||||
NAME = "Find Contours"
|
||||
CATEGORY = CURRENT_CATEGORY
|
||||
@@ -518,39 +565,18 @@ class FindContours:
|
||||
"Return: - hierarchy, containing info about topology. Same length as contours. Each has 4 elements, [next, prev, first child, parent]."
|
||||
|
||||
def execute(self, Np_bins, mode, method):
|
||||
ModeEnum = {
|
||||
"RETR_EXTERNAL": cv2.RETR_EXTERNAL,
|
||||
"RETR_LIST": cv2.RETR_LIST,
|
||||
"RETR_CCOMP": cv2.RETR_CCOMP,
|
||||
"RETR_TREE": cv2.RETR_TREE,
|
||||
}
|
||||
MethodEnum = {
|
||||
"CHAIN_APPROX_NONE": cv2.CHAIN_APPROX_NONE,
|
||||
"CHAIN_APPROX_SIMPLE": cv2.CHAIN_APPROX_SIMPLE,
|
||||
"CHAIN_APPROX_TC89_L1": cv2.CHAIN_APPROX_TC89_L1,
|
||||
"CHAIN_APPROX_TC89_KCOS": cv2.CHAIN_APPROX_TC89_KCOS,
|
||||
}
|
||||
if Np_bins.shape[0] != 1:
|
||||
raise ValueError("FindContours node only supports batch size of 1.")
|
||||
|
||||
batch_size = Np_bins.shape[0]
|
||||
# Convert to grayscale if needed
|
||||
if len(Np_bins[0].shape) == 3 and Np_bins[0].shape[2] == 3:
|
||||
gray = cv2.cvtColor(Np_bins[0], cv2.COLOR_BGR2GRAY)
|
||||
else:
|
||||
gray = Np_bins[0]
|
||||
|
||||
list_contours = []
|
||||
list_hierarchy = []
|
||||
contours, hierarchy = cv2.findContours(gray, ContourModeEnum[mode], ContourMethodEnum[method])
|
||||
|
||||
for b in range(batch_size):
|
||||
# Convert to grayscale if needed
|
||||
if len(Np_bins[b].shape) == 3 and Np_bins[b].shape[2] == 3:
|
||||
gray = cv2.cvtColor(Np_bins[b], cv2.COLOR_BGR2GRAY)
|
||||
else:
|
||||
gray = Np_bins[b]
|
||||
|
||||
contours, hierarchy = cv2.findContours(gray, ModeEnum[mode], MethodEnum[method])
|
||||
list_contours.append(contours)
|
||||
list_hierarchy.append(hierarchy)
|
||||
|
||||
# contour_image = np.zeros_like(Np_bins[b])
|
||||
# cv2.drawContours(contour_image, contours, -1, (255, 255, 255), 1)
|
||||
|
||||
return (list_contours, list_hierarchy, )
|
||||
return (contours, hierarchy, )
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class DrawContours:
|
||||
@@ -561,7 +587,7 @@ class DrawContours:
|
||||
"nparrays": ("NPARRAY", {
|
||||
"tooltip": "Input image to draw contours on. Should be color image."
|
||||
}),
|
||||
"contours": (ANY, {
|
||||
"contours": ("CV_CONTOURS", {
|
||||
"tooltip": "Contours to draw. Use FindContours node to get contours."
|
||||
}),
|
||||
"color": (["WHITE", "BLACK", "RED", "GREEN", "BLUE"], {
|
||||
@@ -570,10 +596,10 @@ class DrawContours:
|
||||
}),
|
||||
"thickness": ("INT", {
|
||||
"default": 2,
|
||||
"min": 1,
|
||||
"min": -1,
|
||||
"max": 20,
|
||||
"step": 1,
|
||||
"tooltip": "Thickness of the contour lines."
|
||||
"tooltip": "Thickness of the contour lines. -1 means filled."
|
||||
}),
|
||||
"hideBackground": ("BOOLEAN", {
|
||||
"default": False,
|
||||
@@ -582,6 +608,7 @@ class DrawContours:
|
||||
},
|
||||
}
|
||||
|
||||
SINGULAR_ONLY = True
|
||||
RETURN_TYPES = ("NPARRAY",)
|
||||
RETURN_NAMES = ("dst",)
|
||||
FUNCTION = "execute"
|
||||
@@ -590,28 +617,22 @@ class DrawContours:
|
||||
DESCRIPTION = "OpenCV Draw Contours. Always draws all contours in the list."
|
||||
|
||||
def execute(self, nparrays, contours, color, thickness, hideBackground):
|
||||
batch_size = min(nparrays.shape[0], len(contours)) # in case different batch size, use the smaller ones
|
||||
colorEnum = {
|
||||
"WHITE": (255, 255, 255),
|
||||
"BLACK": (0, 0, 0),
|
||||
"RED": (0, 0, 255),
|
||||
"GREEN": (0, 255, 0),
|
||||
"BLUE": (255, 0, 0),
|
||||
}
|
||||
|
||||
result = np.zeros_like(nparrays)
|
||||
if nparrays.shape[0] != 1:
|
||||
raise ValueError("DrawContours node only supports batch size of 1.")
|
||||
|
||||
if hideBackground:
|
||||
height = nparrays.shape[1]
|
||||
width = nparrays.shape[2]
|
||||
input = np.zeros((height, width, 3), dtype=np.uint8)
|
||||
for b in range(batch_size):
|
||||
result[b] = cv2.drawContours(input, contours[b], -1, colorEnum[color], thickness)
|
||||
else:
|
||||
for b in range(batch_size):
|
||||
result[b] = cv2.drawContours(nparrays[b], contours[b], -1, colorEnum[color], thickness)
|
||||
|
||||
return (result,)
|
||||
# convert to color if needed
|
||||
if len(nparrays[0].shape) == 2 or nparrays[0].shape[2] == 1:
|
||||
input = cv2.cvtColor(nparrays[0], cv2.COLOR_GRAY2BGR)
|
||||
else:
|
||||
input = nparrays[0]
|
||||
|
||||
result = cv2.drawContours(input, contours, -1, ColorEnum[color], thickness)
|
||||
return (result[np.newaxis, :],)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class Circles:
|
||||
@@ -655,13 +676,6 @@ class Circles:
|
||||
|
||||
def execute(self, nparrays, points, radius, color, thickness):
|
||||
batch_size = nparrays.shape[0]
|
||||
colorEnum = {
|
||||
"WHITE": (255, 255, 255),
|
||||
"BLACK": (0, 0, 0),
|
||||
"RED": (0, 0, 255),
|
||||
"GREEN": (0, 255, 0),
|
||||
"BLUE": (255, 0, 0),
|
||||
}
|
||||
|
||||
result = np.zeros((batch_size, nparrays.shape[1], nparrays.shape[2], 3), dtype=np.uint8)
|
||||
|
||||
@@ -673,7 +687,7 @@ class Circles:
|
||||
result[b] = nparrays[b]
|
||||
|
||||
for point in points[b]:
|
||||
result[b] = cv2.circle(result[b], point, radius, colorEnum[color], thickness)
|
||||
result[b] = cv2.circle(result[b], point, radius, ColorEnum[color], thickness)
|
||||
|
||||
return (result,)
|
||||
|
||||
@@ -690,53 +704,28 @@ class ConnectedComponentsWithStats:
|
||||
"default": "8",
|
||||
"tooltip": "Pixel connectivity to use."
|
||||
}),
|
||||
"colormap": (list(cls._COLORMAPS.keys()), {
|
||||
"default": "HSV",
|
||||
"tooltip": "Colormap to use for preview."
|
||||
"removeSurrounding": ("BOOLEAN", {
|
||||
"default": True,
|
||||
"tooltip": "If true, the surrounding part (different from background) will be removed from output."
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
SINGULAR_ONLY = True
|
||||
RETURN_TYPES = ("INT", "NPARRAY", ANY, ANY, "NPARRAY",)
|
||||
RETURN_NAMES = ("num_labels", "labels", "stats", "centroids", "preview",)
|
||||
RETURN_TYPES = ("INT", "NPARRAY", ANY, ANY,)
|
||||
RETURN_NAMES = ("num_labels", "labels", "stats", "centroids",)
|
||||
FUNCTION = "execute"
|
||||
NAME = "Connected Components With Stats"
|
||||
CATEGORY = CURRENT_CATEGORY
|
||||
DESCRIPTION = "OpenCV Connected Components With Stats. Labels connected components in a binary image and computes statistics for each component."
|
||||
|
||||
_COLORMAPS = {
|
||||
'AUTUMN': cv2.COLORMAP_AUTUMN,
|
||||
'BONE': cv2.COLORMAP_BONE,
|
||||
'JET': cv2.COLORMAP_JET,
|
||||
'WINTER': cv2.COLORMAP_WINTER,
|
||||
'RAINBOW': cv2.COLORMAP_RAINBOW, # 真正的彩虹色阶
|
||||
'OCEAN': cv2.COLORMAP_OCEAN,
|
||||
'SUMMER': cv2.COLORMAP_SUMMER,
|
||||
'SPRING': cv2.COLORMAP_SPRING,
|
||||
'COOL': cv2.COLORMAP_COOL,
|
||||
'HSV': cv2.COLORMAP_HSV,
|
||||
'PINK': cv2.COLORMAP_PINK,
|
||||
'HOT': cv2.COLORMAP_HOT,
|
||||
'PARULA': cv2.COLORMAP_PARULA,
|
||||
'MAGMA': cv2.COLORMAP_MAGMA,
|
||||
'INFERNO': cv2.COLORMAP_INFERNO,
|
||||
'PLASMA': cv2.COLORMAP_PLASMA,
|
||||
'VIRIDIS': cv2.COLORMAP_VIRIDIS,
|
||||
'CIVIDIS': cv2.COLORMAP_CIVIDIS,
|
||||
'TWILIGHT': cv2.COLORMAP_TWILIGHT,
|
||||
'TWILIGHT_SHIFTED': cv2.COLORMAP_TWILIGHT_SHIFTED,
|
||||
'TURBO': cv2.COLORMAP_TURBO,
|
||||
'DEEPGREEN': cv2.COLORMAP_DEEPGREEN,
|
||||
}
|
||||
|
||||
def execute(self, Np_bin, connectivity, colormap):
|
||||
def execute(self, Np_bin, connectivity, removeSurrounding):
|
||||
if Np_bin.shape[0] != 1:
|
||||
raise ValueError("ConnectedComponentsWithStats node only supports batch size of 1.")
|
||||
ConnectivityInput = 4 if connectivity == "4" else 8
|
||||
|
||||
# Convert to grayscale if needed
|
||||
if len(Np_bin.shape) == 3 and Np_bin.shape[2] == 3:
|
||||
if len(Np_bin[0].shape) == 3 and Np_bin[0].shape[2] == 3:
|
||||
gray = cv2.cvtColor(Np_bin[0], cv2.COLOR_BGR2GRAY)
|
||||
else:
|
||||
gray = Np_bin[0]
|
||||
@@ -745,17 +734,15 @@ class ConnectedComponentsWithStats:
|
||||
|
||||
if num_labels <= 1:
|
||||
raise ValueError("No objects found in the binary image.")
|
||||
elif num_labels == 2:
|
||||
# use black and white directly
|
||||
preview = (labels * 255).astype(np.uint8)[np.newaxis, :]
|
||||
else:
|
||||
# normalize the labels to 0-255 range for visualization
|
||||
num_objects = num_labels - 1 # Exclude background label 0
|
||||
normalized_labels = labels * (255 // num_objects) # Normalize to 0-255, will never reach 1
|
||||
colored = cv2.applyColorMap(normalized_labels.astype(np.uint8), self._COLORMAPS[colormap])
|
||||
# force label=0 to be black, because colormap[0] is usually not pure black (like deep blue/purple)
|
||||
colored[labels == 0] = [0, 0, 0]
|
||||
preview = colored[np.newaxis, :]
|
||||
|
||||
if removeSurrounding:
|
||||
corner_labels = (labels[0,0], labels[0,-1], labels[-1,0], labels[-1,-1])
|
||||
# if more than 3 corners share the same label, consider it as surrounding
|
||||
surrounding_label = max(set(corner_labels), key=corner_labels.count)
|
||||
if surrounding_label != 0 and corner_labels.count(surrounding_label) >= 3:
|
||||
labels[labels == surrounding_label] = 0 # set surrounding to background
|
||||
# re-label the largest label to use surrounding_label
|
||||
labels[labels == num_labels - 1] = surrounding_label
|
||||
num_labels -= 1
|
||||
|
||||
|
||||
return (num_labels, labels, stats, centroids, preview,)
|
||||
return (num_labels, labels, stats, centroids, )
|
||||
+105
-13
@@ -1,11 +1,16 @@
|
||||
CURRENT_CATEGORY = "🐦🔥 ArchiGraph/🛠️ Utils" # organized by py file
|
||||
|
||||
import os
|
||||
import random
|
||||
|
||||
import torch
|
||||
from comfy.utils import common_upscale
|
||||
import numpy as np
|
||||
from .anyType import ANY
|
||||
from .nodes_opencv import Nparray2Image
|
||||
|
||||
# import comfyui core node
|
||||
from nodes import SaveImage
|
||||
import folder_paths
|
||||
NPARRAY_DIR = os.path.join(folder_paths.get_output_directory(), "nparrays")
|
||||
JSON_DIR = os.path.join(folder_paths.get_output_directory(), "jsons")
|
||||
@@ -62,10 +67,13 @@ class JsonLoad:
|
||||
files = []
|
||||
else:
|
||||
files = sorted([f for f in os.listdir(JSON_DIR) if f.endswith('.json')])
|
||||
|
||||
if len(files) == 0:
|
||||
files = ["None"] # Placeholder to avoid empty list issues
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"jsonfile": (sorted(files), ),
|
||||
"jsonfile": (files, ),
|
||||
"filename_noExt": ("STRING", {
|
||||
"default": "",
|
||||
"tooltip": "Filename without extension (extension .json will be added automatically). If specified, npyfile will be ignored.",
|
||||
@@ -85,6 +93,10 @@ class JsonLoad:
|
||||
|
||||
if filename_noExt != "":
|
||||
jsonfile = filename_noExt + ".json"
|
||||
else:
|
||||
if jsonfile == "None":
|
||||
raise FileNotFoundError("JSON files not found in the jsons directory.")
|
||||
|
||||
file_path = os.path.join(JSON_DIR, jsonfile)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
@@ -136,6 +148,9 @@ class NparrayLoad:
|
||||
files = []
|
||||
else:
|
||||
files = sorted([f for f in os.listdir(NPARRAY_DIR) if f.endswith('.npy')])
|
||||
|
||||
if len(files) == 0:
|
||||
files = ["None"] # Placeholder to avoid empty list issues
|
||||
|
||||
return {
|
||||
"required": {
|
||||
@@ -157,6 +172,10 @@ class NparrayLoad:
|
||||
def execute(self, npyfile, filename_noExt=""):
|
||||
if filename_noExt != "":
|
||||
npyfile = filename_noExt + ".npy"
|
||||
else:
|
||||
if npyfile == "None":
|
||||
raise FileNotFoundError("Npy files not found in the nparrays directory.")
|
||||
|
||||
file_path = os.path.join(NPARRAY_DIR, npyfile)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
@@ -166,18 +185,72 @@ class NparrayLoad:
|
||||
|
||||
return (nparray,)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class SaveNparrayImage(SaveImage):
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"nparrays": ("NPARRAY", {"tooltip": "The nparray images to save."}),
|
||||
"filename_prefix": ("STRING", {"default": "ComfyUI", "tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% or %Empty Latent Image.width% to include values from nodes."})
|
||||
},
|
||||
"hidden": {
|
||||
"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"
|
||||
},
|
||||
}
|
||||
|
||||
OUTPUT_NODE = True
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "execute"
|
||||
NAME = "Save Nparray Image"
|
||||
CATEGORY = CURRENT_CATEGORY
|
||||
DESCRIPTION = "Saves the input nparray images to your ComfyUI output directory."
|
||||
|
||||
def execute(self, nparrays, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
|
||||
images = Nparray2Image().execute(nparrays)[0]
|
||||
return super().save_images(images, filename_prefix, prompt, extra_pnginfo)
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class PreviewNparrayImage(SaveNparrayImage):
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_temp_directory()
|
||||
self.type = "temp"
|
||||
self.prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5))
|
||||
self.compress_level = 1
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required":
|
||||
{"nparrays": ("NPARRAY", ), },
|
||||
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||
}
|
||||
|
||||
NAME = "Preview Nparray Image"
|
||||
DESCRIPTION = "Previews the input nparray images by saving them to a temporary directory."
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class Printf:
|
||||
'''
|
||||
Later can be used to unravel list and show specified elements only.
|
||||
'''
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"any": (ANY, {
|
||||
"forceInput": True,
|
||||
"tooltip": "Select objects to print."
|
||||
}),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
},
|
||||
}
|
||||
|
||||
OUTPUT_NODE = True
|
||||
# INPUT_IS_LIST = True # if the input is a list, workflow will pass each element separately
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("string",)
|
||||
FUNCTION = "execute"
|
||||
@@ -185,28 +258,47 @@ class Printf:
|
||||
CATEGORY = CURRENT_CATEGORY
|
||||
DESCRIPTION = "Print object info to console for debugging. Supports any input type."
|
||||
|
||||
def execute(self, any):
|
||||
message = ""
|
||||
def execute(self, any, unique_id=None, extra_pnginfo=None):
|
||||
# prepare text, caution: text is parse at frontend as list of strings
|
||||
text = []
|
||||
if isinstance(any, np.ndarray):
|
||||
message = f"Type: Nparray, Shape: {any.shape}, Dtype: {any.dtype}"
|
||||
text.append(f"Type: Nparray, Shape: {any.shape}, Dtype: {any.dtype}")
|
||||
elif isinstance(any, torch.Tensor):
|
||||
message = f"Type: Tensor, Shape: {any.shape}, Dtype: {any.dtype}, Device: {any.device}"
|
||||
text.append(f"Type: Tensor, Shape: {any.shape}, Dtype: {any.dtype}, Device: {any.device}")
|
||||
elif isinstance(any, list):
|
||||
message = f"Type: List, Length: {len(any)}\n"
|
||||
text.append(f"Type: List, Length: {len(any)}")
|
||||
for i, item in enumerate(any):
|
||||
message += f"--[{i}]: Type: {type(item)}, Value:\n{item}\n"
|
||||
text.append(f"[{i}]: Type: {type(item)}, Value:\n{item}")
|
||||
elif isinstance(any, dict):
|
||||
message = f"Type: Dict, Length: {len(any)}\n"
|
||||
text.append(f"Type: Dict, Length: {len(any)}")
|
||||
for key, value in any.items():
|
||||
message += f"--Key: {key}, Type: {type(value)}, Value:\n{value}\n"
|
||||
text.append(f"Key: {key}, Type: {type(value)}, Value:\n{value}")
|
||||
elif isinstance(any, tuple):
|
||||
message = f"Type: Tuple, Length: {len(any)}\n"
|
||||
text.append(f"Type: Tuple, Length: {len(any)}")
|
||||
for i, item in enumerate(any):
|
||||
message += f"--[{i}]: Type: {type(item)}, Value:\n{item}\n"
|
||||
text.append(f"[{i}]: Type: {type(item)}, Value:\n{item}")
|
||||
else:
|
||||
message = f"Type: {type(any)}, Value:\n{any}\n"
|
||||
text.append(f"Type: {type(any)}, Value:\n{any}")
|
||||
|
||||
return (message,)
|
||||
# save to workflow pnginfo
|
||||
if unique_id is not None and extra_pnginfo is not None:
|
||||
if not isinstance(extra_pnginfo, list):
|
||||
print("Error: extra_pnginfo is not a list")
|
||||
elif (
|
||||
not isinstance(extra_pnginfo[0], dict)
|
||||
or "workflow" not in extra_pnginfo[0]
|
||||
):
|
||||
print("Error: extra_pnginfo[0] is not a dict or missing 'workflow' key")
|
||||
else:
|
||||
workflow = extra_pnginfo[0]["workflow"]
|
||||
node = next(
|
||||
(x for x in workflow["nodes"] if str(x["id"]) == str(unique_id[0])),
|
||||
None,
|
||||
)
|
||||
if node:
|
||||
node["widgets_values"] = [text]
|
||||
|
||||
return {"ui": {"text": text}, "result": (text,)}
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------#
|
||||
class SliceList:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "archigraph"
|
||||
description = "Implementation of architectural related graph algorithm in ComfyUI."
|
||||
version = "0.0.5"
|
||||
version = "0.0.6"
|
||||
license = {file = "LICENSE"}
|
||||
|
||||
[project.urls]
|
||||
|
||||
Reference in New Issue
Block a user