Skip to content

Commit 3737c8b

Browse files
committed
Fix issues with the apply node
1 parent 3546210 commit 3737c8b

File tree

3 files changed

+290
-16
lines changed

3 files changed

+290
-16
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Provides tools for applying and removing fisheye lens effects from images.
1010
- Format options: fullframe and circular
1111
- Adjustable FOV and PFOV parameters
1212

13+
### Tips
14+
- The `orthographic` mapping keeps the entirety of the image data, so going back and forth works well, see `examples/BackAndForth.json`
15+
1316
## Installation
1417

1518
1. Clone this repository into your ComfyUI custom_nodes folder:

examples/BackAndForth.json

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
{
2+
"last_node_id": 8,
3+
"last_link_id": 6,
4+
"nodes": [
5+
{
6+
"id": 4,
7+
"type": "PreviewImage",
8+
"pos": [
9+
140,
10+
-70
11+
],
12+
"size": [
13+
210,
14+
246
15+
],
16+
"flags": {},
17+
"order": 3,
18+
"mode": 0,
19+
"inputs": [
20+
{
21+
"name": "images",
22+
"type": "IMAGE",
23+
"link": 4
24+
}
25+
],
26+
"outputs": [],
27+
"properties": {
28+
"Node name for S&R": "PreviewImage",
29+
"enableTabs": false,
30+
"tabWidth": 65,
31+
"tabXOffset": 10,
32+
"hasSecondTab": false,
33+
"secondTabText": "Send Back",
34+
"secondTabOffset": 80,
35+
"secondTabWidth": 65
36+
},
37+
"widgets_values": []
38+
},
39+
{
40+
"id": 5,
41+
"type": "PreviewImage",
42+
"pos": [
43+
620,
44+
-490
45+
],
46+
"size": [
47+
430,
48+
430
49+
],
50+
"flags": {},
51+
"order": 4,
52+
"mode": 0,
53+
"inputs": [
54+
{
55+
"name": "images",
56+
"type": "IMAGE",
57+
"link": 3
58+
}
59+
],
60+
"outputs": [],
61+
"properties": {
62+
"Node name for S&R": "PreviewImage",
63+
"enableTabs": false,
64+
"tabWidth": 65,
65+
"tabXOffset": 10,
66+
"hasSecondTab": false,
67+
"secondTabText": "Send Back",
68+
"secondTabOffset": 80,
69+
"secondTabWidth": 65
70+
},
71+
"widgets_values": []
72+
},
73+
{
74+
"id": 3,
75+
"type": "LoadImage",
76+
"pos": [
77+
-700,
78+
-580
79+
],
80+
"size": [
81+
470,
82+
450
83+
],
84+
"flags": {},
85+
"order": 0,
86+
"mode": 0,
87+
"inputs": [],
88+
"outputs": [
89+
{
90+
"name": "IMAGE",
91+
"type": "IMAGE",
92+
"links": [
93+
1
94+
],
95+
"slot_index": 0
96+
},
97+
{
98+
"name": "MASK",
99+
"type": "MASK",
100+
"links": null
101+
}
102+
],
103+
"properties": {
104+
"Node name for S&R": "LoadImage",
105+
"enableTabs": false,
106+
"tabWidth": 65,
107+
"tabXOffset": 10,
108+
"hasSecondTab": false,
109+
"secondTabText": "Send Back",
110+
"secondTabOffset": 80,
111+
"secondTabWidth": 65
112+
},
113+
"widgets_values": [
114+
"fisheye-image.jpg",
115+
"image"
116+
]
117+
},
118+
{
119+
"id": 2,
120+
"type": "Defisheye",
121+
"pos": [
122+
-170,
123+
-280
124+
],
125+
"size": [
126+
315,
127+
130
128+
],
129+
"flags": {},
130+
"order": 1,
131+
"mode": 0,
132+
"inputs": [
133+
{
134+
"name": "image",
135+
"type": "IMAGE",
136+
"link": 1
137+
}
138+
],
139+
"outputs": [
140+
{
141+
"name": "IMAGE",
142+
"type": "IMAGE",
143+
"links": [
144+
2,
145+
4
146+
],
147+
"slot_index": 0
148+
}
149+
],
150+
"properties": {
151+
"Node name for S&R": "Defisheye",
152+
"enableTabs": false,
153+
"tabWidth": 65,
154+
"tabXOffset": 10,
155+
"hasSecondTab": false,
156+
"secondTabText": "Send Back",
157+
"secondTabOffset": 80,
158+
"secondTabWidth": 65
159+
},
160+
"widgets_values": [
161+
"orthographic",
162+
"fullframe",
163+
180,
164+
120
165+
]
166+
},
167+
{
168+
"id": 1,
169+
"type": "Fisheye",
170+
"pos": [
171+
240,
172+
-340
173+
],
174+
"size": [
175+
315,
176+
130
177+
],
178+
"flags": {},
179+
"order": 2,
180+
"mode": 0,
181+
"inputs": [
182+
{
183+
"name": "image",
184+
"type": "IMAGE",
185+
"link": 2
186+
}
187+
],
188+
"outputs": [
189+
{
190+
"name": "IMAGE",
191+
"type": "IMAGE",
192+
"links": [
193+
3
194+
],
195+
"slot_index": 0
196+
}
197+
],
198+
"properties": {
199+
"Node name for S&R": "Fisheye",
200+
"enableTabs": false,
201+
"tabWidth": 65,
202+
"tabXOffset": 10,
203+
"hasSecondTab": false,
204+
"secondTabText": "Send Back",
205+
"secondTabOffset": 80,
206+
"secondTabWidth": 65
207+
},
208+
"widgets_values": [
209+
"orthographic",
210+
"fullframe",
211+
180,
212+
120
213+
]
214+
}
215+
],
216+
"links": [
217+
[
218+
1,
219+
3,
220+
0,
221+
2,
222+
0,
223+
"IMAGE"
224+
],
225+
[
226+
2,
227+
2,
228+
0,
229+
1,
230+
0,
231+
"IMAGE"
232+
],
233+
[
234+
3,
235+
1,
236+
0,
237+
5,
238+
0,
239+
"IMAGE"
240+
],
241+
[
242+
4,
243+
2,
244+
0,
245+
4,
246+
0,
247+
"IMAGE"
248+
]
249+
],
250+
"groups": [],
251+
"config": {},
252+
"extra": {
253+
"ds": {
254+
"scale": 0.9229599817706413,
255+
"offset": [
256+
1037.145963590657,
257+
949.7047748071493
258+
]
259+
},
260+
"node_versions": {
261+
"comfy-core": "0.3.12",
262+
"ComfyUI-Fisheye-effects": "0.0.1"
263+
},
264+
"ue_links": [],
265+
"VHS_latentpreview": true,
266+
"VHS_latentpreviewrate": 1
267+
},
268+
"version": 0.4
269+
}

nodes.py

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def tensor_to_image(self, tensor):
2525
img_np = np.array(tensor).astype(np.float32) / 255.0
2626
return torch.from_numpy(img_np).unsqueeze(0)
2727

28+
2829
class FisheyeNode(FisheyeBase):
2930
@classmethod
3031
def INPUT_TYPES(cls):
@@ -33,8 +34,8 @@ def INPUT_TYPES(cls):
3334
"image": ("IMAGE",),
3435
"mapping": (["equidistant", "equisolid", "orthographic", "stereographic"],),
3536
"format": (["fullframe", "circular"],),
36-
"fov": ("FLOAT", {"default": 180.0, "min": 0.0, "max": 360.0, "step": 0.1}),
37-
"pfov": ("FLOAT", {"default": 120.0, "min": 0.0, "max": 360.0, "step": 0.1}),
37+
"fov": ("FLOAT", {"default": 180.0, "min": 0.0, "max": 360.0, "step": 30.0}),
38+
"pfov": ("FLOAT", {"default": 120.0, "min": 0.0, "max": 360.0, "step": 30.0}),
3839
},
3940
}
4041

@@ -47,29 +48,30 @@ def map_fisheye(self, i, j, width, height, dim, xcenter, ycenter):
4748
yd = j - ycenter
4849
rd = hypot(xd, yd)
4950

50-
rn = rd / (dim / 2)
51+
rmax = dim / 2 if self.format == "circular" else hypot(width / 2, height / 2)
5152

52-
theta = rn * (self.fov * pi / 360)
53+
phiang = arctan(rd / rmax)
54+
theta = phiang * self.pfov / (self.fov / 2)
5355

54-
# See https://en.wikipedia.org/wiki/Fisheye_lens
5556
if self.mapping == "equidistant":
56-
r = theta
57+
r = rmax * theta / (pi / 2)
5758
elif self.mapping == "equisolid":
58-
r = 2 * sin(theta / 2)
59+
r = 2 * rmax * sin(theta / 2)
5960
elif self.mapping == "orthographic":
60-
r = sin(theta)
61+
r = rmax * sin(theta)
6162
elif self.mapping == "stereographic":
62-
r = 2 * tan(theta / 2)
63-
64-
r = r * (dim / 2)
63+
r = 2 * rmax * tan(theta / 2)
6564

6665
rdmask = rd != 0
67-
6866
xs = xd.astype(np.float32).copy()
6967
ys = yd.astype(np.float32).copy()
7068

71-
xs[rdmask] = (r[rdmask] / rd[rdmask]) * xd[rdmask] + xcenter
72-
ys[rdmask] = (r[rdmask] / rd[rdmask]) * yd[rdmask] + ycenter
69+
scale = 1.0
70+
if self.mapping == "equidistant":
71+
scale = 1.5
72+
73+
xs[rdmask] = scale * (rd[rdmask] / r[rdmask]) * xd[rdmask] + xcenter
74+
ys[rdmask] = scale * (rd[rdmask] / r[rdmask]) * yd[rdmask] + ycenter
7375

7476
xs[~rdmask] = xcenter
7577
ys[~rdmask] = ycenter
@@ -108,8 +110,8 @@ def INPUT_TYPES(cls):
108110
"image": ("IMAGE",),
109111
"mapping": (["equidistant", "equisolid", "orthographic", "stereographic"],),
110112
"format": (["fullframe", "circular"],),
111-
"fov": ("FLOAT", {"default": 180.0, "min": 0.0, "max": 360.0, "step": 5.0}),
112-
"pfov": ("FLOAT", {"default": 120.0, "min": 0.0, "max": 360.0, "step": 5.0}),
113+
"fov": ("FLOAT", {"default": 180.0, "min": 0.0, "max": 360.0, "step": 30.0}),
114+
"pfov": ("FLOAT", {"default": 120.0, "min": 0.0, "max": 360.0, "step": 30.0}),
113115
},
114116
}
115117

0 commit comments

Comments
 (0)