vispy 0.9.5__cp38-cp38-win_amd64.whl → 0.14.0__cp38-cp38-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of vispy might be problematic. Click here for more details.
- vispy/app/backends/_glfw.py +2 -2
- vispy/app/backends/_pyglet.py +8 -2
- vispy/app/backends/_qt.py +88 -63
- vispy/app/backends/_wx.py +6 -1
- vispy/app/canvas.py +4 -2
- vispy/app/tests/test_canvas.py +52 -1
- vispy/app/tests/test_context.py +5 -3
- vispy/color/color_array.py +8 -1
- vispy/color/colormap.py +5 -25
- vispy/geometry/meshdata.py +76 -38
- vispy/geometry/rect.py +6 -0
- vispy/geometry/tests/test_meshdata.py +72 -0
- vispy/gloo/buffer.py +12 -0
- vispy/gloo/gl/_constants.py +9 -5
- vispy/gloo/gl/_es2.py +8 -4
- vispy/gloo/gl/_gl2.py +2 -3
- vispy/gloo/gl/_proxy.py +1 -1
- vispy/gloo/gl/_pyopengl2.py +12 -7
- vispy/gloo/gl/tests/test_names.py +3 -0
- vispy/gloo/glir.py +26 -13
- vispy/gloo/program.py +39 -22
- vispy/gloo/tests/test_program.py +9 -2
- vispy/gloo/tests/test_texture.py +19 -2
- vispy/gloo/texture.py +46 -16
- vispy/gloo/wrappers.py +4 -2
- vispy/glsl/build_spatial_filters.py +241 -293
- vispy/glsl/misc/spatial-filters.frag +1299 -254
- vispy/io/_data/spatial-filters.npy +0 -0
- vispy/io/datasets.py +2 -2
- vispy/io/image.py +1 -1
- vispy/io/stl.py +3 -3
- vispy/scene/cameras/base_camera.py +6 -2
- vispy/scene/cameras/panzoom.py +10 -14
- vispy/scene/cameras/perspective.py +6 -0
- vispy/scene/cameras/tests/test_cameras.py +27 -0
- vispy/scene/cameras/tests/test_perspective.py +37 -0
- vispy/scene/cameras/turntable.py +39 -23
- vispy/scene/canvas.py +9 -5
- vispy/scene/events.py +9 -0
- vispy/scene/node.py +19 -2
- vispy/scene/tests/test_canvas.py +30 -1
- vispy/scene/tests/test_visuals.py +113 -0
- vispy/scene/visuals.py +6 -1
- vispy/scene/widgets/viewbox.py +3 -2
- vispy/testing/_runners.py +6 -12
- vispy/testing/_testing.py +3 -4
- vispy/util/check_environment.py +4 -4
- vispy/util/gallery_scraper.py +50 -32
- vispy/util/tests/test_gallery_scraper.py +2 -0
- vispy/util/transforms.py +1 -1
- vispy/util/wrappers.py +1 -1
- vispy/version.py +2 -3
- vispy/visuals/__init__.py +2 -0
- vispy/visuals/_scalable_textures.py +20 -17
- vispy/visuals/collections/array_list.py +3 -3
- vispy/visuals/collections/base_collection.py +1 -1
- vispy/visuals/ellipse.py +1 -1
- vispy/visuals/filters/__init__.py +3 -2
- vispy/visuals/filters/base_filter.py +120 -0
- vispy/visuals/filters/clipping_planes.py +24 -12
- vispy/visuals/filters/markers.py +28 -0
- vispy/visuals/filters/mesh.py +61 -6
- vispy/visuals/filters/tests/test_primitive_picking_filters.py +70 -0
- vispy/visuals/graphs/graph.py +1 -1
- vispy/visuals/image.py +114 -26
- vispy/visuals/image_complex.py +130 -0
- vispy/visuals/instanced_mesh.py +152 -0
- vispy/visuals/isocurve.py +1 -1
- vispy/visuals/line/dash_atlas.py +46 -41
- vispy/visuals/line/line.py +2 -5
- vispy/visuals/markers.py +310 -384
- vispy/visuals/mesh.py +2 -2
- vispy/visuals/shaders/function.py +3 -0
- vispy/visuals/shaders/tests/test_function.py +6 -0
- vispy/visuals/tests/test_axis.py +2 -2
- vispy/visuals/tests/test_image.py +92 -2
- vispy/visuals/tests/test_image_complex.py +36 -0
- vispy/visuals/tests/test_instanced_mesh.py +50 -0
- vispy/visuals/tests/test_markers.py +6 -0
- vispy/visuals/tests/test_mesh.py +17 -0
- vispy/visuals/tests/test_text.py +11 -0
- vispy/visuals/tests/test_volume.py +218 -12
- vispy/visuals/text/_sdf_cpu.cp38-win_amd64.pyd +0 -0
- vispy/visuals/text/_sdf_cpu.pyx +21 -23
- vispy/visuals/text/text.py +9 -3
- vispy/visuals/tube.py +2 -2
- vispy/visuals/visual.py +144 -3
- vispy/visuals/volume.py +300 -131
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/LICENSE.txt +1 -1
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/METADATA +218 -198
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/RECORD +93 -96
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/WHEEL +1 -1
- vispy/glsl/antialias/__init__.py +0 -0
- vispy/glsl/arrowheads/__init__.py +0 -0
- vispy/glsl/arrows/__init__.py +0 -0
- vispy/glsl/collections/__init__.py +0 -0
- vispy/glsl/colormaps/__init__.py +0 -0
- vispy/glsl/lines/__init__.py +0 -0
- vispy/glsl/markers/__init__.py +0 -0
- vispy/glsl/math/__init__.py +0 -0
- vispy/glsl/misc/__init__.py +0 -0
- vispy/glsl/transforms/__init__.py +0 -0
- {vispy-0.9.5.dist-info → vispy-0.14.0.dist-info}/top_level.txt +0 -0
|
@@ -49,22 +49,22 @@ using 2 1D-convolution with same 1d-kernel (= the lookup table values).
|
|
|
49
49
|
Available filters:
|
|
50
50
|
|
|
51
51
|
- Nearest (radius 0.5)
|
|
52
|
-
-
|
|
53
|
-
- Hanning (radius 1
|
|
54
|
-
- Hamming (radius 1
|
|
55
|
-
- Hermite (radius 1
|
|
56
|
-
- Kaiser (radius 1
|
|
52
|
+
- Linear (radius 1)
|
|
53
|
+
- Hanning (radius 1)
|
|
54
|
+
- Hamming (radius 1)
|
|
55
|
+
- Hermite (radius 1)
|
|
56
|
+
- Kaiser (radius 1)
|
|
57
57
|
- Quadric (radius 1.5)
|
|
58
|
-
-
|
|
59
|
-
- CatRom (radius 2
|
|
60
|
-
- Mitchell (radius 2
|
|
61
|
-
- Spline16 (radius 2
|
|
62
|
-
- Spline36 (radius 4
|
|
63
|
-
- Gaussian (radius 2
|
|
58
|
+
- Cubic (radius 2)
|
|
59
|
+
- CatRom (radius 2)
|
|
60
|
+
- Mitchell (radius 2)
|
|
61
|
+
- Spline16 (radius 2)
|
|
62
|
+
- Spline36 (radius 4)
|
|
63
|
+
- Gaussian (radius 2)
|
|
64
64
|
- Bessel (radius 3.2383)
|
|
65
|
-
- Sinc (radius 4
|
|
66
|
-
- Lanczos (radius 4
|
|
67
|
-
- Blackman (radius 4
|
|
65
|
+
- Sinc (radius 4)
|
|
66
|
+
- Lanczos (radius 4)
|
|
67
|
+
- Blackman (radius 4)
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
Note::
|
|
@@ -76,13 +76,13 @@ Note::
|
|
|
76
76
|
|
|
77
77
|
import math
|
|
78
78
|
import numpy as np
|
|
79
|
+
from inspect import cleandoc
|
|
80
|
+
from itertools import product
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
class SpatialFilter
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def __init__(self, radius=1.0):
|
|
85
|
-
self.radius = radius
|
|
83
|
+
class SpatialFilter:
|
|
84
|
+
def __init__(self, radius=1):
|
|
85
|
+
self.radius = math.ceil(radius)
|
|
86
86
|
|
|
87
87
|
def weight(self, x):
|
|
88
88
|
"""
|
|
@@ -94,119 +94,38 @@ class SpatialFilter(object):
|
|
|
94
94
|
"""
|
|
95
95
|
raise NotImplementedError
|
|
96
96
|
|
|
97
|
-
def kernel(self, size=4*512):
|
|
98
|
-
|
|
99
|
-
r = int(max(1.0, math.ceil(radius)))
|
|
100
|
-
samples = int(size / r)
|
|
97
|
+
def kernel(self, size=4 * 512):
|
|
98
|
+
samples = int(size / self.radius)
|
|
101
99
|
n = size # r*samples
|
|
102
100
|
kernel = np.zeros(n)
|
|
103
|
-
X = np.linspace(0,
|
|
101
|
+
X = np.linspace(0, self.radius, n)
|
|
104
102
|
for i in range(n):
|
|
105
103
|
kernel[i] = self.weight(X[i])
|
|
106
104
|
N = np.zeros(samples)
|
|
107
|
-
for i in range(
|
|
108
|
-
N += kernel[::+1][i*samples:(i+1)*samples]
|
|
109
|
-
N += kernel[::-1][i*samples:(i+1)*samples]
|
|
110
|
-
for i in range(
|
|
111
|
-
kernel[i*samples:(i+1)*samples
|
|
105
|
+
for i in range(self.radius):
|
|
106
|
+
N += kernel[::+1][i * samples:(i + 1) * samples]
|
|
107
|
+
N += kernel[::-1][i * samples:(i + 1) * samples]
|
|
108
|
+
for i in range(self.radius):
|
|
109
|
+
kernel[i * samples:(i + 1) * samples] /= N
|
|
112
110
|
return kernel
|
|
113
111
|
|
|
114
|
-
def filter_code(self):
|
|
115
|
-
|
|
116
|
-
n = int(math.ceil(self.radius))
|
|
117
|
-
filter_1 = 'filter1D_radius%d' % n
|
|
118
|
-
filter_2 = 'filter2D_radius%d' % n
|
|
119
|
-
|
|
120
|
-
code = ''
|
|
121
|
-
code += 'vec4\n'
|
|
122
|
-
code += '%s( sampler2D kernel, float index, float x, ' % filter_1
|
|
123
|
-
for i in range(2*n):
|
|
124
|
-
if i == 2*n-1:
|
|
125
|
-
code += 'vec4 c%d )\n' % i
|
|
126
|
-
else:
|
|
127
|
-
code += 'vec4 c%d, ' % i
|
|
128
|
-
code += '{\n'
|
|
129
|
-
code += ' float w, w_sum = 0.0;\n'
|
|
130
|
-
code += ' vec4 r = vec4(0.0,0.0,0.0,0.0);\n'
|
|
131
|
-
for i in range(n):
|
|
132
|
-
code += ' w = unpack_interpolate(kernel, vec2(%f+(x/%.1f), index));\n' % (1.0 - (i + 1) / float(n), n) # noqa
|
|
133
|
-
code += ' w = w*kernel_scale + kernel_bias;\n' # noqa
|
|
134
|
-
# code += ' w_sum += w;'
|
|
135
|
-
code += ' r += c%d * w;\n' % i
|
|
136
|
-
code += ' w = unpack_interpolate(kernel, vec2(%f-(x/%.1f), index));\n' % ((i+1)/float(n), n) # noqa
|
|
137
|
-
code += ' w = w*kernel_scale + kernel_bias;\n'
|
|
138
|
-
# code += ' w_sum += w;'
|
|
139
|
-
code += ' r += c%d * w;\n' % (i + n)
|
|
140
|
-
# code += ' return r/w_sum;\n'
|
|
141
|
-
code += ' return r;\n'
|
|
142
|
-
code += '}\n'
|
|
143
|
-
code += "\n"
|
|
144
|
-
code += 'vec4\n'
|
|
145
|
-
code += '%s' % filter_2
|
|
146
|
-
code += '(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel)\n' # noqa
|
|
147
|
-
code += '{\n'
|
|
148
|
-
code += ' vec2 texel = uv/pixel - vec2(0.5, 0.5) ;\n'
|
|
149
|
-
code += ' vec2 f = fract(texel);\n'
|
|
150
|
-
code += ' texel = (texel-fract(texel) + vec2(0.001, 0.001)) * pixel;\n' # noqa
|
|
151
|
-
for i in range(2*n):
|
|
152
|
-
code += ' vec4 t%d = %s(kernel, index, f.x,\n' % (i, filter_1)
|
|
153
|
-
for j in range(2*n):
|
|
154
|
-
x, y = (-n+1+j, -n+1+i)
|
|
155
|
-
code += ' texture2D( texture, texel + vec2(%d, %d) * pixel),\n' % (x, y) # noqa
|
|
156
|
-
|
|
157
|
-
# Remove last trailing',' and close function call
|
|
158
|
-
code = code[:-2] + ');\n'
|
|
159
|
-
|
|
160
|
-
code += ' return %s(kernel, index, f.y, ' % filter_1
|
|
161
|
-
for i in range(2*n):
|
|
162
|
-
code += 't%d, ' % i
|
|
163
|
-
|
|
164
|
-
# Remove last trailing',' and close function call
|
|
165
|
-
code = code[:-2] + ');\n'
|
|
166
|
-
code += '}\n'
|
|
167
|
-
|
|
168
|
-
return code
|
|
169
|
-
|
|
170
112
|
def call_code(self, index):
|
|
171
|
-
code =
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
code += 'vec4 %s(sampler2D texture, vec2 shape, vec2 uv)\n' % self.__class__.__name__ # noqa
|
|
177
|
-
code += '{'
|
|
178
|
-
code += ' return %s(texture, u_kernel, %f, uv, 1.0/shape); ' % (filter_2, index) # noqa
|
|
179
|
-
code += '}\n'
|
|
180
|
-
return code
|
|
113
|
+
code = cleandoc(f'''
|
|
114
|
+
vec4 {self.__class__.__name__}2D(sampler2D texture, vec2 shape, vec2 uv) {{
|
|
115
|
+
return filter2D_radius{self.radius}(texture, u_kernel, {index}, uv, 1 / shape);
|
|
116
|
+
}}
|
|
181
117
|
|
|
118
|
+
vec4 {self.__class__.__name__}3D(sampler3D texture, vec3 shape, vec3 uv) {{
|
|
119
|
+
return filter3D_radius{self.radius}(texture, u_kernel, {index}, uv, 1 / shape);
|
|
120
|
+
}}
|
|
121
|
+
''')
|
|
182
122
|
|
|
183
|
-
class Nearest(SpatialFilter):
|
|
184
|
-
"""
|
|
185
|
-
Nearest (=None) filter (radius = 0.5).
|
|
186
|
-
|
|
187
|
-
Weight function::
|
|
188
|
-
|
|
189
|
-
w(x) = 1
|
|
190
|
-
"""
|
|
191
|
-
|
|
192
|
-
def __init__(self):
|
|
193
|
-
SpatialFilter.__init__(self, radius=.5)
|
|
194
|
-
|
|
195
|
-
def weight(self, x):
|
|
196
|
-
return 1.0
|
|
197
|
-
|
|
198
|
-
def _get_code(self):
|
|
199
|
-
self.build_LUT()
|
|
200
|
-
code = 'vec4\n'
|
|
201
|
-
code += 'interpolate(sampler2D texture, sampler1D kernel, vec2 uv, vec2 pixel)\n' # noqa
|
|
202
|
-
code += '{\n return texture2D(texture, uv);\n}\n'
|
|
203
123
|
return code
|
|
204
|
-
code = property(_get_code, doc='''filter functions code''')
|
|
205
124
|
|
|
206
125
|
|
|
207
|
-
class
|
|
126
|
+
class Linear(SpatialFilter):
|
|
208
127
|
"""
|
|
209
|
-
|
|
128
|
+
Linear filter (radius = 1).
|
|
210
129
|
|
|
211
130
|
Weight function::
|
|
212
131
|
|
|
@@ -214,16 +133,13 @@ class Bilinear(SpatialFilter):
|
|
|
214
133
|
|
|
215
134
|
"""
|
|
216
135
|
|
|
217
|
-
def __init__(self):
|
|
218
|
-
SpatialFilter.__init__(self, radius=1.0)
|
|
219
|
-
|
|
220
136
|
def weight(self, x):
|
|
221
|
-
return 1
|
|
137
|
+
return 1 - x
|
|
222
138
|
|
|
223
139
|
|
|
224
140
|
class Hanning(SpatialFilter):
|
|
225
141
|
"""
|
|
226
|
-
Hanning filter (radius = 1
|
|
142
|
+
Hanning filter (radius = 1).
|
|
227
143
|
|
|
228
144
|
Weight function::
|
|
229
145
|
|
|
@@ -231,16 +147,13 @@ class Hanning(SpatialFilter):
|
|
|
231
147
|
|
|
232
148
|
"""
|
|
233
149
|
|
|
234
|
-
def __init__(self):
|
|
235
|
-
SpatialFilter.__init__(self, radius=1.0)
|
|
236
|
-
|
|
237
150
|
def weight(self, x):
|
|
238
151
|
return 0.5 + 0.5 * math.cos(math.pi * x)
|
|
239
152
|
|
|
240
153
|
|
|
241
154
|
class Hamming(SpatialFilter):
|
|
242
155
|
"""
|
|
243
|
-
Hamming filter (radius = 1
|
|
156
|
+
Hamming filter (radius = 1).
|
|
244
157
|
|
|
245
158
|
Weight function::
|
|
246
159
|
|
|
@@ -248,15 +161,12 @@ class Hamming(SpatialFilter):
|
|
|
248
161
|
|
|
249
162
|
"""
|
|
250
163
|
|
|
251
|
-
def __init__(self):
|
|
252
|
-
SpatialFilter.__init__(self, radius=1.0)
|
|
253
|
-
|
|
254
164
|
def weight(self, x):
|
|
255
165
|
return 0.54 + 0.46 * math.cos(math.pi * x)
|
|
256
166
|
|
|
257
167
|
|
|
258
168
|
class Hermite(SpatialFilter):
|
|
259
|
-
"""Hermite filter (radius = 1
|
|
169
|
+
"""Hermite filter (radius = 1).
|
|
260
170
|
|
|
261
171
|
Weight function::
|
|
262
172
|
|
|
@@ -264,11 +174,8 @@ class Hermite(SpatialFilter):
|
|
|
264
174
|
|
|
265
175
|
"""
|
|
266
176
|
|
|
267
|
-
def __init__(self):
|
|
268
|
-
SpatialFilter.__init__(self, radius=1.0)
|
|
269
|
-
|
|
270
177
|
def weight(self, x):
|
|
271
|
-
return (2
|
|
178
|
+
return (2 * x - 3) * x**2 + 1
|
|
272
179
|
|
|
273
180
|
|
|
274
181
|
class Quadric(SpatialFilter):
|
|
@@ -277,28 +184,27 @@ class Quadric(SpatialFilter):
|
|
|
277
184
|
|
|
278
185
|
Weight function::
|
|
279
186
|
|
|
280
|
-
| 0
|
|
187
|
+
| 0 ≤ x < 0.5: 0.75 - x*x
|
|
281
188
|
w(x) = | 0.5 ≤ x < 1.5: 0.5 - (x-1.5)^2
|
|
282
189
|
| 1.5 ≤ x : 0
|
|
283
190
|
|
|
284
191
|
"""
|
|
285
192
|
|
|
286
193
|
def __init__(self):
|
|
287
|
-
|
|
194
|
+
super().__init__(radius=1.5)
|
|
288
195
|
|
|
289
196
|
def weight(self, x):
|
|
290
197
|
if x < 0.75:
|
|
291
|
-
return 0.75 - x
|
|
198
|
+
return 0.75 - x**2
|
|
292
199
|
elif x < 1.5:
|
|
293
200
|
t = x - 1.5
|
|
294
|
-
return 0.5 * t
|
|
295
|
-
|
|
296
|
-
return 0.0
|
|
201
|
+
return 0.5 * t**2
|
|
202
|
+
return 0
|
|
297
203
|
|
|
298
204
|
|
|
299
|
-
class
|
|
205
|
+
class Cubic(SpatialFilter):
|
|
300
206
|
"""
|
|
301
|
-
|
|
207
|
+
Cubic filter (radius = 2).
|
|
302
208
|
|
|
303
209
|
Weight function::
|
|
304
210
|
|
|
@@ -306,24 +212,20 @@ class Bicubic(SpatialFilter):
|
|
|
306
212
|
"""
|
|
307
213
|
|
|
308
214
|
def __init__(self):
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
def pow3(self, x):
|
|
312
|
-
if x <= 0:
|
|
313
|
-
return 0
|
|
314
|
-
else:
|
|
315
|
-
return x * x * x
|
|
215
|
+
super().__init__(radius=2)
|
|
316
216
|
|
|
317
217
|
def weight(self, x):
|
|
318
|
-
return (1
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
218
|
+
return (1 / 6) * (
|
|
219
|
+
(x + 2)**3
|
|
220
|
+
- 4 * (x + 1)**3
|
|
221
|
+
+ 6 * x**3
|
|
222
|
+
- 4 * (x - 1)**3
|
|
223
|
+
)
|
|
322
224
|
|
|
323
225
|
|
|
324
226
|
class Kaiser(SpatialFilter):
|
|
325
227
|
"""
|
|
326
|
-
Kaiser filter (radius = 1
|
|
228
|
+
Kaiser filter (radius = 1).
|
|
327
229
|
|
|
328
230
|
|
|
329
231
|
Weight function::
|
|
@@ -335,29 +237,29 @@ class Kaiser(SpatialFilter):
|
|
|
335
237
|
def __init__(self, b=6.33):
|
|
336
238
|
self.a = b
|
|
337
239
|
self.epsilon = 1e-12
|
|
338
|
-
self.i0a = 1
|
|
339
|
-
|
|
240
|
+
self.i0a = 1 / self.bessel_i0(b)
|
|
241
|
+
super().__init__(radius=1)
|
|
340
242
|
|
|
341
243
|
def bessel_i0(self, x):
|
|
342
|
-
s = 1
|
|
343
|
-
y = x
|
|
244
|
+
s = 1
|
|
245
|
+
y = x**2 / 4
|
|
344
246
|
t = y
|
|
345
247
|
i = 2
|
|
346
248
|
while t > self.epsilon:
|
|
347
249
|
s += t
|
|
348
|
-
t *= float(y) /
|
|
250
|
+
t *= float(y) / i**2
|
|
349
251
|
i += 1
|
|
350
252
|
return s
|
|
351
253
|
|
|
352
254
|
def weight(self, x):
|
|
353
255
|
if x > 1:
|
|
354
256
|
return 0
|
|
355
|
-
return self.bessel_i0(self.a * math.sqrt(1
|
|
257
|
+
return self.bessel_i0(self.a * math.sqrt(1 - x**2)) * self.i0a
|
|
356
258
|
|
|
357
259
|
|
|
358
260
|
class CatRom(SpatialFilter):
|
|
359
261
|
"""
|
|
360
|
-
Catmull-Rom filter (radius = 2
|
|
262
|
+
Catmull-Rom filter (radius = 2).
|
|
361
263
|
|
|
362
264
|
Weight function::
|
|
363
265
|
|
|
@@ -367,21 +269,21 @@ class CatRom(SpatialFilter):
|
|
|
367
269
|
|
|
368
270
|
"""
|
|
369
271
|
|
|
370
|
-
def __init__(self
|
|
371
|
-
|
|
272
|
+
def __init__(self):
|
|
273
|
+
super().__init__(radius=2)
|
|
372
274
|
|
|
373
275
|
def weight(self, x):
|
|
374
|
-
if x < 1
|
|
375
|
-
return 0.5 * (2
|
|
376
|
-
elif x < 2
|
|
377
|
-
return 0.5 * (4
|
|
276
|
+
if x < 1:
|
|
277
|
+
return 0.5 * (2 + x**2 * (-5 + x * 3))
|
|
278
|
+
elif x < 2:
|
|
279
|
+
return 0.5 * (4 + x * (-8 + x * (5 - x)))
|
|
378
280
|
else:
|
|
379
|
-
return 0
|
|
281
|
+
return 0
|
|
380
282
|
|
|
381
283
|
|
|
382
284
|
class Mitchell(SpatialFilter):
|
|
383
285
|
"""
|
|
384
|
-
Mitchell-Netravali filter (radius = 2
|
|
286
|
+
Mitchell-Netravali filter (radius = 2).
|
|
385
287
|
|
|
386
288
|
Weight function::
|
|
387
289
|
|
|
@@ -391,28 +293,28 @@ class Mitchell(SpatialFilter):
|
|
|
391
293
|
|
|
392
294
|
"""
|
|
393
295
|
|
|
394
|
-
def __init__(self, b=1
|
|
395
|
-
self.p0 = (6
|
|
396
|
-
self.p2 = (-18
|
|
397
|
-
self.p3 = (12
|
|
398
|
-
self.q0 = (8
|
|
399
|
-
self.q1 = (-12
|
|
400
|
-
self.q2 = (6
|
|
401
|
-
self.q3 = (-b - 6
|
|
402
|
-
|
|
296
|
+
def __init__(self, b=1/3, c=1/3):
|
|
297
|
+
self.p0 = (6 - 2 * b) / 6
|
|
298
|
+
self.p2 = (-18 + 12 * b + 6 * c) / 6
|
|
299
|
+
self.p3 = (12 - 9 * b - 6 * c) / 6
|
|
300
|
+
self.q0 = (8 * b + 24 * c) / 6
|
|
301
|
+
self.q1 = (-12 * b - 48 * c) / 6
|
|
302
|
+
self.q2 = (6 * b + 30 * c) / 6
|
|
303
|
+
self.q3 = (-b - 6 * c) / 6
|
|
304
|
+
super().__init__(radius=2)
|
|
403
305
|
|
|
404
306
|
def weight(self, x):
|
|
405
|
-
if x < 1
|
|
406
|
-
return self.p0 + x *
|
|
407
|
-
elif x < 2
|
|
307
|
+
if x < 1:
|
|
308
|
+
return self.p0 + x**2 * (self.p2 + x * self.p3)
|
|
309
|
+
elif x < 2:
|
|
408
310
|
return self.q0 + x * (self.q1 + x * (self.q2 + x * self.q3))
|
|
409
311
|
else:
|
|
410
|
-
return 0
|
|
312
|
+
return 0
|
|
411
313
|
|
|
412
314
|
|
|
413
315
|
class Spline16(SpatialFilter):
|
|
414
316
|
"""
|
|
415
|
-
Spline16 filter (radius = 2
|
|
317
|
+
Spline16 filter (radius = 2).
|
|
416
318
|
|
|
417
319
|
Weight function::
|
|
418
320
|
|
|
@@ -423,18 +325,18 @@ class Spline16(SpatialFilter):
|
|
|
423
325
|
"""
|
|
424
326
|
|
|
425
327
|
def __init__(self):
|
|
426
|
-
|
|
328
|
+
super().__init__(radius=2)
|
|
427
329
|
|
|
428
330
|
def weight(self, x):
|
|
429
|
-
if x < 1
|
|
430
|
-
return ((x - 9
|
|
331
|
+
if x < 1:
|
|
332
|
+
return ((x - 9/5) * x - 1/5) * x + 1
|
|
431
333
|
else:
|
|
432
|
-
return ((-1
|
|
334
|
+
return ((-1/3 * (x - 1) + 4/5) * (x - 1) - 7/15) * (x - 1)
|
|
433
335
|
|
|
434
336
|
|
|
435
337
|
class Spline36(SpatialFilter):
|
|
436
338
|
"""
|
|
437
|
-
Spline36 filter (radius = 3
|
|
339
|
+
Spline36 filter (radius = 3).
|
|
438
340
|
|
|
439
341
|
Weight function::
|
|
440
342
|
|
|
@@ -444,20 +346,20 @@ class Spline36(SpatialFilter):
|
|
|
444
346
|
"""
|
|
445
347
|
|
|
446
348
|
def __init__(self):
|
|
447
|
-
|
|
349
|
+
super().__init__(radius=3)
|
|
448
350
|
|
|
449
351
|
def weight(self, x):
|
|
450
|
-
if x < 1
|
|
451
|
-
return ((13
|
|
452
|
-
elif x < 2
|
|
453
|
-
return ((-6
|
|
352
|
+
if x < 1:
|
|
353
|
+
return ((13/11 * x - 453/209) * x - 3/209) * x + 1
|
|
354
|
+
elif x < 2:
|
|
355
|
+
return ((-6/11 * (x - 1) + 270/209) * (x - 1) - 156 / 209) * (x - 1)
|
|
454
356
|
else:
|
|
455
|
-
return ((1
|
|
357
|
+
return ((1/11 * (x - 2) - 45/209) * (x - 2) + 26/209) * (x - 2)
|
|
456
358
|
|
|
457
359
|
|
|
458
360
|
class Gaussian(SpatialFilter):
|
|
459
361
|
"""
|
|
460
|
-
Gaussian filter (radius = 2
|
|
362
|
+
Gaussian filter (radius = 2).
|
|
461
363
|
|
|
462
364
|
Weight function::
|
|
463
365
|
|
|
@@ -467,7 +369,7 @@ class Gaussian(SpatialFilter):
|
|
|
467
369
|
|
|
468
370
|
This filter does not seem to be correct since:
|
|
469
371
|
|
|
470
|
-
x = np.linspace(0, 1
|
|
372
|
+
x = np.linspace(0, 1, 100 )
|
|
471
373
|
f = weight
|
|
472
374
|
z = f(x+1)+f(x)+f(1-x)+f(2-x)
|
|
473
375
|
|
|
@@ -476,17 +378,17 @@ class Gaussian(SpatialFilter):
|
|
|
476
378
|
"""
|
|
477
379
|
|
|
478
380
|
def __init__(self):
|
|
479
|
-
|
|
381
|
+
super().__init__(radius=2)
|
|
480
382
|
|
|
481
383
|
def weight(self, x):
|
|
482
|
-
return math.exp(-2
|
|
384
|
+
return math.exp(-2 * x**2) * math.sqrt(2 / math.pi)
|
|
483
385
|
|
|
484
386
|
|
|
485
387
|
class Bessel(SpatialFilter):
|
|
486
388
|
"""Bessel filter (radius = 3.2383)."""
|
|
487
389
|
|
|
488
390
|
def __init__(self):
|
|
489
|
-
|
|
391
|
+
super().__init__(radius=3.2383)
|
|
490
392
|
|
|
491
393
|
def besj(self, x, n):
|
|
492
394
|
"""Function BESJ calculates Bessel function of first kind of order n.
|
|
@@ -521,7 +423,7 @@ class Bessel(SpatialFilter):
|
|
|
521
423
|
|
|
522
424
|
"""
|
|
523
425
|
if n < 0:
|
|
524
|
-
return 0
|
|
426
|
+
return 0
|
|
525
427
|
x = float(x) # force float type
|
|
526
428
|
|
|
527
429
|
d = 1e-6
|
|
@@ -551,7 +453,7 @@ class Bessel(SpatialFilter):
|
|
|
551
453
|
m8 = -1
|
|
552
454
|
|
|
553
455
|
imax = m2 - 2
|
|
554
|
-
for i in range(1, imax+1):
|
|
456
|
+
for i in range(1, imax + 1):
|
|
555
457
|
c6 = 2 * (m2 - i) * c2 / x - c3
|
|
556
458
|
c3 = c2
|
|
557
459
|
c2 = c6
|
|
@@ -572,139 +474,185 @@ class Bessel(SpatialFilter):
|
|
|
572
474
|
m2 += 3
|
|
573
475
|
|
|
574
476
|
def weight(self, x):
|
|
575
|
-
if x == 0
|
|
576
|
-
return math.pi/4
|
|
477
|
+
if x == 0:
|
|
478
|
+
return math.pi / 4
|
|
577
479
|
else:
|
|
578
|
-
return self.besj(math.pi * x, 1) / (2
|
|
480
|
+
return self.besj(math.pi * x, 1) / (2 * x)
|
|
579
481
|
|
|
580
482
|
|
|
581
483
|
class Sinc(SpatialFilter):
|
|
582
|
-
"""Sinc filter (radius = 4
|
|
484
|
+
"""Sinc filter (radius = 4)."""
|
|
583
485
|
|
|
584
|
-
def __init__(self
|
|
585
|
-
|
|
486
|
+
def __init__(self):
|
|
487
|
+
super().__init__(radius=4)
|
|
586
488
|
|
|
587
489
|
def weight(self, x):
|
|
588
|
-
if x == 0
|
|
589
|
-
return 1
|
|
490
|
+
if x == 0:
|
|
491
|
+
return 1
|
|
590
492
|
x *= math.pi
|
|
591
493
|
return (math.sin(x) / x)
|
|
592
494
|
|
|
593
495
|
|
|
594
496
|
class Lanczos(SpatialFilter):
|
|
595
|
-
"""Lanczos filter (radius = 4
|
|
497
|
+
"""Lanczos filter (radius = 4)."""
|
|
596
498
|
|
|
597
|
-
def __init__(self
|
|
598
|
-
|
|
499
|
+
def __init__(self):
|
|
500
|
+
super().__init__(radius=4)
|
|
599
501
|
|
|
600
502
|
def weight(self, x):
|
|
601
|
-
if x == 0
|
|
602
|
-
return 1
|
|
503
|
+
if x == 0:
|
|
504
|
+
return 1
|
|
603
505
|
elif x > self.radius:
|
|
604
|
-
return 0
|
|
506
|
+
return 0
|
|
605
507
|
x *= math.pi
|
|
606
508
|
xr = x / self.radius
|
|
607
|
-
return (math.sin(x) / x) * (math.sin(xr)/xr)
|
|
509
|
+
return (math.sin(x) / x) * (math.sin(xr) / xr)
|
|
608
510
|
|
|
609
511
|
|
|
610
512
|
class Blackman(SpatialFilter):
|
|
611
|
-
"""Blackman filter (radius = 4
|
|
513
|
+
"""Blackman filter (radius = 4)."""
|
|
612
514
|
|
|
613
|
-
def __init__(self
|
|
614
|
-
|
|
515
|
+
def __init__(self):
|
|
516
|
+
super().__init__(radius=4)
|
|
615
517
|
|
|
616
518
|
def weight(self, x):
|
|
617
|
-
if x == 0
|
|
618
|
-
return 1
|
|
519
|
+
if x == 0:
|
|
520
|
+
return 1
|
|
619
521
|
elif x > self.radius:
|
|
620
|
-
return 0
|
|
522
|
+
return 0
|
|
621
523
|
x *= math.pi
|
|
622
524
|
xr = x / self.radius
|
|
623
|
-
return (math.sin(x) / x) * (0.42 + 0.5*math.cos(xr) + 0.08*math.cos(2*xr))
|
|
525
|
+
return (math.sin(x) / x) * (0.42 + 0.5 * math.cos(xr) + 0.08 * math.cos(2 * xr))
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def generate_filter_code(radius):
|
|
529
|
+
n = int(math.ceil(radius))
|
|
530
|
+
|
|
531
|
+
nl = '\n' # cannot use backslash in fstring
|
|
532
|
+
code = cleandoc(f'''
|
|
533
|
+
vec4 filter1D_radius{n}(sampler2D kernel, float index, float x{''.join(f', vec4 c{i}' for i in range(n * 2))}) {{
|
|
534
|
+
float w, w_sum = 0;
|
|
535
|
+
vec4 r = vec4(0);
|
|
536
|
+
{''.join(f"""
|
|
537
|
+
w = unpack_interpolate(kernel, vec2({1 - (i + 1) / n} + (x / {n}), index));
|
|
538
|
+
w = w * kernel_scale + kernel_bias;
|
|
539
|
+
r += c{i} * w;
|
|
540
|
+
w = unpack_interpolate(kernel, vec2({(i + 1) / n} - (x / {n}), index));
|
|
541
|
+
w = w * kernel_scale + kernel_bias;
|
|
542
|
+
r += c{i + n} * w;"""
|
|
543
|
+
for i in range(n))}
|
|
544
|
+
return r;
|
|
545
|
+
}}
|
|
546
|
+
|
|
547
|
+
vec4 filter2D_radius{n}(sampler2D texture, sampler2D kernel, float index, vec2 uv, vec2 pixel) {{
|
|
548
|
+
vec2 texel = uv / pixel - vec2(0.5);
|
|
549
|
+
vec2 f = fract(texel);
|
|
550
|
+
texel = (texel - fract(texel) + vec2(0.001)) * pixel;
|
|
551
|
+
{''.join(f"""
|
|
552
|
+
vec4 t{i} = filter1D_radius{n}(kernel, index, f.x{f''.join(
|
|
553
|
+
f',{nl} texture2D(texture, texel + vec2({-n + 1 + j}, {-n + 1 + i}) * pixel)'
|
|
554
|
+
for j in range(n * 2))});"""
|
|
555
|
+
for i in range(n * 2))}
|
|
556
|
+
return filter1D_radius{n}(kernel, index, f.y{''.join(f', t{i}' for i in range(2*n))});
|
|
557
|
+
}}
|
|
558
|
+
|
|
559
|
+
vec4 filter3D_radius{n}(sampler3D texture, sampler2D kernel, float index, vec3 uv, vec3 pixel) {{
|
|
560
|
+
vec3 texel = uv / pixel - vec3(0.5);
|
|
561
|
+
vec3 f = fract(texel);
|
|
562
|
+
texel = (texel - fract(texel) + vec3(0.001)) * pixel;
|
|
563
|
+
{''.join(f"""
|
|
564
|
+
vec4 t{i}{j} = filter1D_radius{n}(kernel, index, f.x{f''.join(
|
|
565
|
+
f',{nl} texture3D(texture, texel + vec3({-n + 1 + k}, {-n + 1 + j}, {-n + 1 + i}) * pixel)'
|
|
566
|
+
for k in range(n * 2))});"""
|
|
567
|
+
for i, j in product(range(n * 2), range(n * 2)))}
|
|
568
|
+
{f''.join(f"""
|
|
569
|
+
vec4 t{i} = filter1D_radius{n}(kernel, index, f.y{"".join(
|
|
570
|
+
f", t{i}{j}" for j in range(n * 2))});"""
|
|
571
|
+
for i in range(n * 2))}
|
|
572
|
+
return filter1D_radius{n}(kernel, index, f.z{''.join(f', t{i}' for i in range(2*n))});
|
|
573
|
+
}}
|
|
574
|
+
''')
|
|
575
|
+
|
|
576
|
+
return code
|
|
624
577
|
|
|
625
578
|
|
|
626
579
|
def main():
|
|
627
580
|
# Generate kernels texture (16 x 1024)
|
|
628
|
-
filters = [
|
|
629
|
-
|
|
581
|
+
filters = [Linear(), Hanning(), Hamming(), Hermite(), Kaiser(), Quadric(),
|
|
582
|
+
Cubic(), CatRom(), Mitchell(), Spline16(), Spline36(), Gaussian(),
|
|
630
583
|
Bessel(), Sinc(), Lanczos(), Blackman()]
|
|
631
584
|
|
|
632
585
|
n = 1024
|
|
633
|
-
K = np.zeros((
|
|
586
|
+
K = np.zeros((len(filters), n))
|
|
634
587
|
for i, f in enumerate(filters):
|
|
635
588
|
K[i] = f.kernel(n)
|
|
636
589
|
|
|
637
590
|
bias = K.min()
|
|
638
|
-
scale = K.max()-K.min()
|
|
639
|
-
K = (K-bias)/scale
|
|
591
|
+
scale = K.max() - K.min()
|
|
592
|
+
K = (K - bias) / scale
|
|
640
593
|
np.save("spatial-filters.npy", K.astype(np.float32))
|
|
641
594
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
code += '
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
code += '
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
F = SpatialFilter(4.0)
|
|
699
|
-
print(F.filter_code())
|
|
700
|
-
|
|
701
|
-
# Generate filter functions
|
|
702
|
-
# Special case for nearest
|
|
703
|
-
print("""vec4 Nearest(sampler2D texture, vec2 shape, vec2 uv)""")
|
|
704
|
-
print("""{ return texture2D(texture,uv); }\n""")
|
|
595
|
+
code = cleandoc(f'''
|
|
596
|
+
// ------------------------------------
|
|
597
|
+
// Automatically generated, do not edit
|
|
598
|
+
// ------------------------------------
|
|
599
|
+
const float kernel_bias = {bias};
|
|
600
|
+
const float kernel_scale = {scale};
|
|
601
|
+
const float kernel_size = {n};
|
|
602
|
+
const vec4 bits = vec4(1, {1 / 256}, {1 / (256 * 256)}, {1 / (256 * 256 * 256)});
|
|
603
|
+
uniform sampler2D u_kernel;
|
|
604
|
+
''')
|
|
605
|
+
|
|
606
|
+
# add basic unpack functions
|
|
607
|
+
code += '\n\n' + cleandoc('''
|
|
608
|
+
float unpack_unit(vec4 rgba) {
|
|
609
|
+
// return rgba.r; // uncomment this for r32f debugging
|
|
610
|
+
return dot(rgba, bits);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
float unpack_ieee(vec4 rgba) {
|
|
614
|
+
// return rgba.r; // uncomment this for r32f debugging
|
|
615
|
+
rgba.rgba = rgba.abgr * 255;
|
|
616
|
+
float sign = 1 - step(128 , rgba[0]) * 2;
|
|
617
|
+
float exponent = 2 * mod(rgba[0] , 128) + step(128 , rgba[1]) - 127;
|
|
618
|
+
float mantissa = mod(rgba[1] , 128) * 65536 + rgba[2] * 256 + rgba[3] + float(0x800000);
|
|
619
|
+
return sign * exp2(exponent) * (mantissa * exp2(-23.));
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
float unpack_interpolate(sampler2D kernel, vec2 uv) {
|
|
624
|
+
// return texture2D(kernel, uv).r; //uncomment this for r32f debug without interpolation
|
|
625
|
+
float kpixel = 1. / kernel_size;
|
|
626
|
+
float u = uv.x / kpixel;
|
|
627
|
+
float v = uv.y;
|
|
628
|
+
float uf = fract(u);
|
|
629
|
+
u = (u - uf) * kpixel;
|
|
630
|
+
float d0 = unpack_unit(texture2D(kernel, vec2(u, v)));
|
|
631
|
+
float d1 = unpack_unit(texture2D(kernel, vec2(u + 1. * kpixel, v)));
|
|
632
|
+
return mix(d0, d1, uf);
|
|
633
|
+
}
|
|
634
|
+
''')
|
|
635
|
+
|
|
636
|
+
# add 1d, 2d and 3d filter code
|
|
637
|
+
for radius in range(4):
|
|
638
|
+
code += '\n\n' + generate_filter_code(radius + 1)
|
|
639
|
+
|
|
640
|
+
# add call functions for 2D and 3D filters
|
|
641
|
+
# special case for nearest
|
|
642
|
+
code += '\n\n' + cleandoc('''
|
|
643
|
+
vec4 Nearest2D(sampler2D texture, vec2 shape, vec2 uv) {
|
|
644
|
+
return texture2D(texture, uv);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
vec4 Nearest3D(sampler3D texture, vec3 shape, vec3 uv) {
|
|
648
|
+
return texture3D(texture, uv);
|
|
649
|
+
}
|
|
650
|
+
''')
|
|
705
651
|
|
|
706
652
|
for i, f in enumerate(filters):
|
|
707
|
-
|
|
653
|
+
code += '\n\n' + f.call_code((i + 0.5) / 16)
|
|
654
|
+
|
|
655
|
+
print(code)
|
|
708
656
|
|
|
709
657
|
|
|
710
658
|
if __name__ == "__main__":
|