一般来说,我是 Vispy 和 opengl 的新手。我已将 realtime_signals 演示改编为我的数据集。我使用的数据是非平稳的,通常呈现出某种趋势。因此,放大数据通常效果不佳,因为漂移值会超出窗口。
我正在尝试将沿 y 轴的 min-max 归一化添加到示例代码中。这样,无论我的缩放级别是什么,数据都应该保持在窗口的中心。但是,我的解决方案产生了我无法解释的故障。
from vispy import gloo
from vispy import app
import numpy as np
import math
import numpy.ma as ma
#Data
num_samples = 1000
num_features = 3
df_raw = np.reshape(1+(np.random.normal(size=num_samples*num_features, loc=[0], scale=[0.01])), [num_samples, num_features]).cumprod(0).astype('float32')
df_raw_std = 1+((df_raw-np.min(df_raw,0))*2)/(np.min(df_raw,0)-np.max(df_raw,0))
# Generate the signals as a (num_features, num_samples) array. 320x1000
y = df_raw_std.transpose()
# Signal 2D index of each vertex (row and col) and x-index (sample index
# within each signal).
index_col = np.c_[np.tile(np.arange(num_samples), num_features),np.repeat(np.arange(num_features), num_samples)].astype(np.float32)
y_flat = np.reshape(y,y.shape[0]*y.shape[1])
index_y_scaled_orig = np.c_[-1 + 2*(index_col[:,0] / num_samples),y_flat].astype(np.float32)
index_y_scaled = index_y_scaled_orig.copy()
index_min = np.c_[np.tile(np.arange(num_samples), num_features),np.repeat(1, num_samples*num_features)].astype(np.float32)
index_max = np.c_[np.tile(np.arange(num_samples), num_features),np.repeat(1, num_samples*num_features)].astype(np.float32)
#This is called once for each vertex
VERT_SHADER = """
#version 120
//scaling. Running minimum and maximum of visible time series
attribute float index_min;
attribute float index_max;
// y coordinate of the position.
attribute float y;
// row, and time index.
attribute vec2 index_col;
// 2D scaling factor (zooming).
uniform vec2 scale;
uniform vec2 num_features;
// Number of samples per signal.
uniform float num_samples;
// for fragment shader
varying vec2 v_index;
// Varying variables used for clipping in the fragment shader.
varying vec2 v_position;
varying vec4 v_ab;
void main() {
float nrows = num_features.x;
// Compute the x coordinate from the time index
float x = -1 + 2 * (index_col.x / (num_samples-1));
//0 is zoom from center. 1 is zoom on the right. -1 is zoom on the left. WE should map mouse x pos to this.
float zoom_x_pos = 0.0;
// RELATIVE LINE POSITION
// =============================
// Manipulate x/y position here?
// =============================
// vec2 position = vec2(x - (1 - 1 / scale.x)*zoom_x_pos,y); // DEACTIVATED SCALING, NICE PLOTS EMERGE
vec2 position = vec2(x - (1 - 1 / scale.x)*zoom_x_pos,(y-index_min)/(index_max-index_min)); //SCALING, GLITCHY
// SPREAD
//does not scale the x pos, just the y pos by an equal amount per row
float spread = 1;
vec2 a = vec2(spread, spread/nrows);
// LOCATION
vec2 b = vec2(0, -1 + 2*(index_col.y+.5) / nrows);
// COMBINE RELATIVE LINE POSITION + SPREAD + LOCATION
gl_Position = vec4(a*scale*position+b, 0.0, 1.0);
// WRAP UP
v_index = index_col;
// For clipping test in the fragment shader.
v_position = gl_Position.xy;
v_ab = vec4(a, b);
}
"""
FRAG_SHADER = """
#version 120
varying vec2 v_index;
varying vec2 v_position;
varying vec4 v_ab;
void main() {
gl_FragColor = vec4(1., 1., 1., 1.);
// Discard the fragments between the signals (emulate glMultiDrawArrays).
if (fract(v_index.y) > 0.)
discard;
// Clipping test.
vec2 test = abs((v_position.xy-v_ab.zw)/v_ab.xy);
if ((test.x > 1) || (test.y > 1))
discard;
}
"""
class Canvas(app.Canvas):
def __init__(self):
app.Canvas.__init__(self, title='Use your wheel to zoom!',
keys='interactive')
self.program = gloo.Program(VERT_SHADER, FRAG_SHADER)
self.program['y'] = y.reshape(-1, 1)
self.program['index_col'] = index_col
self.program['scale'] = (1., 1.)
self.program['num_features'] = (num_features, 1)
self.program['num_samples'] = num_samples
self.program['index_min'] = index_min[:,0].reshape(-1, 1)
self.program['index_max'] = index_max[:,0].reshape(-1, 1)
gloo.set_viewport(0, 0, *self.physical_size)
self._timer = app.Timer('auto', connect=self.on_timer, start=True)
gloo.set_state(clear_color='black', blend=True,
blend_func=('src_alpha', 'one_minus_src_alpha'))
self.show()
def on_resize(self, event):
gloo.set_viewport(0, 0, *event.physical_size)
def on_mouse_wheel(self, event):
dx = np.sign(event.delta[1]) * .05
scale_x, scale_y = self.program['scale']
index_y_scaled[:,0] = index_y_scaled_orig[:,0] * scale_x
index_y_scaled[:, 1] = index_y_scaled_orig[:, 1] * scale_x
valid = ((index_y_scaled[:,0]>-1)*(index_y_scaled[:,0]<1))
index_y_scaled_reshaped = (np.reshape(index_y_scaled[:, 1],[num_features,num_samples]))
shown = ma.masked_array(index_y_scaled_reshaped, mask=np.logical_not(valid))
runmin = np.array(np.min(shown, 1))
runmax = np.array(np.max(shown, 1))
index_min[:, 1] = np.repeat(runmin, num_samples)
index_max[:, 1] = np.repeat(runmax, num_samples)
print(scale_x)
print(runmin)
print(runmax)
self.program['index_min'] = index_min[:,1].reshape(-1, 1)
self.program['index_max'] = index_max[:,1].reshape(-1, 1)
#print(self.program['print_position'])
scale_x_new, scale_y_new = (scale_x * math.exp(1.0*dx),
scale_y * math.exp(1.0*dx))
#print(scale_x_new)
self.program['scale'] = (max(1, scale_x_new), max(1, scale_y_new))
self.update()
def on_timer(self, event):
"""Add some data at the end of each signal (real-time signals)."""
self.program['y'].set_data(y.ravel().astype(np.float32)) #(10920,)
self.update()
def on_draw(self, event):
gloo.clear()
self.program.draw('line_strip')
if __name__ == '__main__':
c = Canvas()
app.run()
- 我究竟做错了什么?我正在“估计”opengl 外部的可见线选择,并将比例校正参数传递给 opengl 管道。然而,我得到明显的视觉故障以及扭曲的线条
- 在 vispy 中有没有更聪明的方法来解决这个问题?也许是解决片段着色器中的标准化或通过相机技巧的一种方法?