--- /dev/null
+#version 450 core
+
+in vec2 tc;
+out vec4 equation;
+
+uniform sampler2D I_x_y_tex, I_t_tex;
+uniform sampler2D diff_flow_tex, flow_tex;
+uniform sampler2D beta_0_tex;
+uniform sampler2D smoothness_x_tex, smoothness_y_tex;
+
+// TODO: Consider a specialized version for the case where we know that du = dv = 0,
+// since we run so few iterations.
+
+// The base flow needs to be normalized.
+// TODO: Should we perhaps reduce this to a separate two-component
+// texture when calculating the derivatives?
+vec2 normalize_flow(vec3 flow)
+{
+ return flow.xy / flow.z;
+}
+
+// This must be a macro, since the offset needs to be a constant expression.
+#define get_flow(x_offs, y_offs) \
+ (normalize_flow(textureOffset(flow_tex, tc, ivec2((x_offs), (y_offs))).xyz) + \
+ textureOffset(diff_flow_tex, tc, ivec2((x_offs), (y_offs))).xy)
+
+void main()
+{
+ // Read the flow (on top of the u0/v0 flow).
+ vec2 diff_flow = texture(diff_flow_tex, tc).xy;
+ float du = diff_flow.x; // FIXME: convert to pixels?
+ float dv = diff_flow.y;
+
+ // Read the first derivatives.
+ vec2 I_x_y = texture(I_x_y_tex, tc).xy;
+ float I_x = I_x_y.x;
+ float I_y = I_x_y.y;
+ float I_t = texture(I_t_tex, tc).x;
+
+ // E_I term. Note that we don't square β_0, in line with DeepFlow,
+ // even though it's probably an error.
+ //
+ // TODO: Evaluate squaring β_0.
+ // FIXME: Should the penalizer be adjusted for 0..1 intensity range instead of 0..255?
+ // TODO: Multiply by some alpha.
+ float beta_0 = texture(beta_0_tex, tc).x;
+ float k1 = beta_0 * inversesqrt(beta_0 * (I_x * du + I_y * dv + I_t) * (I_x * du + I_y * dv + I_t) + 1e-6);
+ float A11 = k1 * I_x * I_x;
+ float A12 = k1 * I_x * I_y;
+ float A22 = k1 * I_y * I_y;
+ float b1 = -k1 * I_t;
+ float b2 = -k1 * I_t;
+
+ // Compute the second derivatives. First I_xx and I_xy.
+ vec2 I_x_y_m2 = textureOffset(I_x_y_tex, tc, ivec2(-2, 0)).xy;
+ vec2 I_x_y_m1 = textureOffset(I_x_y_tex, tc, ivec2(-1, 0)).xy;
+ vec2 I_x_y_p1 = textureOffset(I_x_y_tex, tc, ivec2( 1, 0)).xy;
+ vec2 I_x_y_p2 = textureOffset(I_x_y_tex, tc, ivec2( 2, 0)).xy;
+ vec2 I_xx_yx = (I_x_y_p1 - I_x_y_m1) * (2.0/3.0) + (I_x_y_m2 - I_x_y_p2) * (1.0/12.0);
+ float I_xx = I_xx_yx.x;
+ float I_xy = I_xx_yx.y;
+
+ // And now I_yy; I_yx = I_xy, bar rounding differences, so we don't
+ // bother computing it. We still have to sample the x component,
+ // though, but we can throw it away immediately.
+ float I_y_m2 = textureOffset(I_x_y_tex, tc, ivec2(0, -2)).y;
+ float I_y_m1 = textureOffset(I_x_y_tex, tc, ivec2(0, -1)).y;
+ float I_y_p1 = textureOffset(I_x_y_tex, tc, ivec2(0, 1)).y;
+ float I_y_p2 = textureOffset(I_x_y_tex, tc, ivec2(0, 2)).y;
+ float I_yy = (I_y_p1 - I_y_m1) * (2.0/3.0) + (I_y_m2 - I_y_p2) * (1.0/12.0);
+
+ // Finally I_xt and I_yt. (We compute these as I_tx and I_yt.)
+ vec2 I_t_m2 = textureOffset(I_t_tex, tc, ivec2(-2, 0)).xy;
+ vec2 I_t_m1 = textureOffset(I_t_tex, tc, ivec2(-1, 0)).xy;
+ vec2 I_t_p1 = textureOffset(I_t_tex, tc, ivec2( 1, 0)).xy;
+ vec2 I_t_p2 = textureOffset(I_t_tex, tc, ivec2( 2, 0)).xy;
+ vec2 I_tx_ty = (I_t_p1 - I_t_m1) * (2.0/3.0) + (I_t_m2 - I_t_p2) * (1.0/12.0);
+ float I_xt = I_tx_ty.x;
+ float I_yt = I_tx_ty.y;
+
+ // E_G term. Same TODOs as E_I. Same normalization as beta_0
+ // (see derivatives.frag).
+ float beta_x = 1.0 / (I_xx * I_xx + I_xy * I_xy + 1e-7);
+ float beta_y = 1.0 / (I_xy * I_xy + I_yy * I_yy + 1e-7);
+ float k2 = inversesqrt(
+ beta_x * (I_xx * du + I_xy * dv + I_xt) * (I_xx * du + I_xy * dv + I_xt) +
+ beta_y * (I_xy * du + I_yy * dv + I_yt) * (I_xy * du + I_yy * dv + I_yt) +
+ 1e-6);
+ float k_x = k2 * beta_x;
+ float k_y = k2 * beta_y;
+ A11 += k_x * I_xx * I_xx + k_y * I_xy * I_xy;
+ A12 += k_x * I_xx * I_xy + k_y * I_xy * I_yy;
+ A22 += k_x * I_xy * I_xy + k_y * I_yy * I_yy;
+ b1 -= k_x * I_xx * I_xt + k_y * I_xy * I_yt;
+ b2 -= k_x * I_xy * I_xt + k_y * I_yy * I_yt;
+
+ // E_S term, sans the part on the right-hand side that deals with
+ // the neighboring pixels.
+ // TODO: Multiply by some gamma.
+ float smooth_l = textureOffset(smoothness_x_tex, tc, ivec2(-1, 0)).x;
+ float smooth_r = texture(smoothness_x_tex, tc).x;
+ float smooth_d = textureOffset(smoothness_y_tex, tc, ivec2(-1, 0)).x;
+ float smooth_u = texture(smoothness_y_tex, tc).x;
+ A11 -= smooth_l + smooth_r + smooth_d + smooth_u;
+ A22 -= smooth_l + smooth_r + smooth_d + smooth_u;
+
+ // Laplacian of (u0 + du, v0 + dv), sans the central term.
+ vec2 laplacian =
+ smooth_l * get_flow(-1, 0) +
+ smooth_r * get_flow(1, 0) +
+ smooth_d * get_flow(0, -1) +
+ smooth_u * get_flow(0, 1);
+ b1 -= laplacian.x;
+ b2 -= laplacian.y;
+
+ // The central term of the Laplacian, for (u0, v0) only.
+ // (The central term for (du, dv) is what we are solving for.)
+ vec2 central = (smooth_l + smooth_r + smooth_d + smooth_u) * normalize_flow(texture(flow_tex, tc).xyz);
+ b1 += central.x;
+ b2 += central.y;
+
+ // Encode the equation down into four uint32s.
+ equation.x = floatBitsToUint(A11);
+ equation.y = floatBitsToUint(A12);
+ equation.z = floatBitsToUint(A22);
+ equation.w = packHalf2x16(vec2(b1, b2));
+}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
+// Calculate the smoothness constraints between neighboring pixels;
+// s_x(x,y) stores smoothness between pixel (x,y) and (x+1,y),
+// and s_y(x,y) stores between (x,y) and (x,y+1). We'll sample with
+// border color (0,0) later, so that there's zero diffusion out of
+// the border.
+//
+// See variational_refinement.txt for more information.
+class ComputeSmoothness {
+public:
+ ComputeSmoothness();
+ void exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height);
+
+private:
+ GLuint smoothness_vs_obj;
+ GLuint smoothness_fs_obj;
+ GLuint smoothness_program;
+ GLuint smoothness_vao;
+
+ GLuint uniform_flow_tex, uniform_diff_flow_tex;
+};
+
+ComputeSmoothness::ComputeSmoothness()
+{
+ smoothness_vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
+ smoothness_fs_obj = compile_shader(read_file("smoothness.frag"), GL_FRAGMENT_SHADER);
+ smoothness_program = link_program(smoothness_vs_obj, smoothness_fs_obj);
+
+ // Set up the VAO containing all the required position/texcoord data.
+ glCreateVertexArrays(1, &smoothness_vao);
+ glBindVertexArray(smoothness_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo);
+
+ GLint position_attrib = glGetAttribLocation(smoothness_program, "position");
+ glEnableVertexArrayAttrib(smoothness_vao, position_attrib);
+ glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+
+ uniform_flow_tex = glGetUniformLocation(smoothness_program, "flow_tex");
+ uniform_diff_flow_tex = glGetUniformLocation(smoothness_program, "diff_flow_tex");
+}
+
+void ComputeSmoothness::exec(GLuint flow_tex, GLuint diff_flow_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, int level_width, int level_height)
+{
+ glUseProgram(smoothness_program);
+
+ bind_sampler(smoothness_program, uniform_flow_tex, 0, flow_tex, nearest_sampler);
+ bind_sampler(smoothness_program, uniform_diff_flow_tex, 1, diff_flow_tex, nearest_sampler);
+
+ GLuint smoothness_fbo; // TODO: cleanup
+ glCreateFramebuffers(1, &smoothness_fbo);
+ GLenum bufs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
+ glNamedFramebufferDrawBuffers(smoothness_fbo, 2, bufs);
+ glNamedFramebufferTexture(smoothness_fbo, GL_COLOR_ATTACHMENT0, smoothness_x_tex, 0);
+ glNamedFramebufferTexture(smoothness_fbo, GL_COLOR_ATTACHMENT1, smoothness_y_tex, 0);
+
+ glViewport(0, 0, level_width, level_height);
+
+ // Make sure the smoothness on the right and upper borders is zero.
+ // We could have done this by making a (W-1)x(H-1) texture instead
+ // (we're sampling smoothness with all-zero border color), but we'd
+ // have to adjust the sampling coordinates, which is annoying.
+ glScissor(0, 0, level_width - 1, level_height - 1);
+ glEnable(GL_SCISSOR_TEST);
+
+ glDisable(GL_BLEND);
+ glBindVertexArray(smoothness_vao);
+ glBindFramebuffer(GL_FRAMEBUFFER, smoothness_fbo);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+ glDisable(GL_SCISSOR_TEST);
+}
+
+// Set up the equations set (two equations in two unknowns, per pixel).
+// We store five floats; the three non-redundant elements of the 2x2 matrix (A)
+// as 32-bit floats, and the two elements on the right-hand side (b) as 16-bit
+// floats. This fits into four u32 values; R, G, B for the matrix (the last
+// element is symmetric) and A for the two b values. All the values of the
+// energy term (E_I, E_G, E_S), except the smoothness terms that depend on
+// other pixels, are calculated in one pass.
+//
+// See variational_refinement.txt for more information.
+class SetupEquations {
+public:
+ SetupEquations();
+ void exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint flow_tex, GLuint beta_0_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height);
+
+private:
+ GLuint smoothness_sampler;
+
+ GLuint equations_vs_obj;
+ GLuint equations_fs_obj;
+ GLuint equations_program;
+ GLuint equations_vao;
+
+ GLuint uniform_I_x_y_tex, uniform_I_t_tex;
+ GLuint uniform_diff_flow_tex, uniform_flow_tex;
+ GLuint uniform_beta_0_tex;
+ GLuint uniform_smoothness_x_tex, uniform_smoothness_y_tex;
+
+};
+
+SetupEquations::SetupEquations()
+{
+ // The smoothness is sampled so that once we get to a smoothness involving
+ // a value outside the border, the diffusivity between the two becomes zero.
+ glCreateSamplers(1, &smoothness_sampler);
+ glSamplerParameteri(smoothness_sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glSamplerParameteri(smoothness_sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glSamplerParameteri(smoothness_sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glSamplerParameteri(smoothness_sampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ float zero[] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ glSamplerParameterfv(smoothness_sampler, GL_TEXTURE_BORDER_COLOR, zero);
+
+ equations_vs_obj = compile_shader(read_file("vs.vert"), GL_VERTEX_SHADER);
+ equations_fs_obj = compile_shader(read_file("equations.frag"), GL_FRAGMENT_SHADER);
+ equations_program = link_program(equations_vs_obj, equations_fs_obj);
+
+ // Set up the VAO containing all the required position/texcoord data.
+ glCreateVertexArrays(1, &equations_vao);
+ glBindVertexArray(equations_vao);
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_vbo);
+
+ GLint position_attrib = glGetAttribLocation(equations_program, "position");
+ glEnableVertexArrayAttrib(equations_vao, position_attrib);
+ glVertexAttribPointer(position_attrib, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
+
+ uniform_I_x_y_tex = glGetUniformLocation(equations_program, "I_x_y_tex");
+ uniform_I_t_tex = glGetUniformLocation(equations_program, "I_t_tex");
+ uniform_diff_flow_tex = glGetUniformLocation(equations_program, "diff_flow_tex");
+ uniform_flow_tex = glGetUniformLocation(equations_program, "flow_tex");
+ uniform_beta_0_tex = glGetUniformLocation(equations_program, "beta_0_tex");
+ uniform_smoothness_x_tex = glGetUniformLocation(equations_program, "smoothness_x_tex");
+ uniform_smoothness_y_tex = glGetUniformLocation(equations_program, "smoothness_y_tex");
+}
+
+void SetupEquations::exec(GLuint I_x_y_tex, GLuint I_t_tex, GLuint diff_flow_tex, GLuint flow_tex, GLuint beta_0_tex, GLuint smoothness_x_tex, GLuint smoothness_y_tex, GLuint equation_tex, int level_width, int level_height)
+{
+ glUseProgram(equations_program);
+
+ bind_sampler(equations_program, uniform_I_x_y_tex, 0, I_x_y_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_I_t_tex, 1, I_t_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_diff_flow_tex, 2, diff_flow_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_flow_tex, 3, flow_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_beta_0_tex, 4, beta_0_tex, nearest_sampler);
+ bind_sampler(equations_program, uniform_smoothness_x_tex, 5, smoothness_x_tex, smoothness_sampler);
+ bind_sampler(equations_program, uniform_smoothness_y_tex, 6, smoothness_y_tex, smoothness_sampler);
+
+ GLuint equations_fbo; // TODO: cleanup
+ glCreateFramebuffers(1, &equations_fbo);
+ glNamedFramebufferTexture(equations_fbo, GL_COLOR_ATTACHMENT0, equation_tex, 0);
+
+ glViewport(0, 0, level_width, level_height);
+ glDisable(GL_BLEND);
+ glBindVertexArray(equations_vao);
+ glBindFramebuffer(GL_FRAMEBUFFER, equations_fbo);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+}
+
int main(void)
{
if (SDL_Init(SDL_INIT_EVERYTHING) == -1) {
Densify densify;
Prewarp prewarp;
Derivatives derivatives;
+ ComputeSmoothness compute_smoothness;
+ SetupEquations setup_equations;
GLuint query;
glGenQueries(1, &query);
glTextureStorage2D(beta_0_tex, 1, GL_R16F, level_width, level_height);
derivatives.exec(I_tex, I_x_y_tex, beta_0_tex, level_width, level_height);
+ // We need somewhere to store du and dv (the flow increment, relative
+ // to the non-refined base flow u0 and v0). It starts at zero.
+ GLuint du_dv_tex;
+ glCreateTextures(GL_TEXTURE_2D, 1, &du_dv_tex);
+ glTextureStorage2D(du_dv_tex, 1, GL_RG16F, level_width, level_height);
+
+ // And for smoothness.
+ GLuint smoothness_x_tex, smoothness_y_tex;
+ glCreateTextures(GL_TEXTURE_2D, 1, &smoothness_x_tex);
+ glCreateTextures(GL_TEXTURE_2D, 1, &smoothness_y_tex);
+ glTextureStorage2D(smoothness_x_tex, 1, GL_R16F, level_width, level_height);
+ glTextureStorage2D(smoothness_y_tex, 1, GL_R16F, level_width, level_height);
+
+ // And finally for the equation set. See SetupEquations for
+ // the storage format.
+ GLuint equation_tex;
+ glCreateTextures(GL_TEXTURE_2D, 1, &equation_tex);
+ glTextureStorage2D(equation_tex, 1, GL_RGBA32UI, level_width, level_height);
+
+ for (int outer_idx = 0; outer_idx < level + 1; ++outer_idx) {
+ // Calculate the smoothness terms between the neighboring pixels,
+ // both in x and y direction.
+ compute_smoothness.exec(dense_flow_tex, du_dv_tex, smoothness_x_tex, smoothness_y_tex, level_width, level_height);
+
+ // Set up the 2x2 equation system for each pixel.
+ setup_equations.exec(I_x_y_tex, I_t_tex, du_dv_tex, dense_flow_tex, beta_0_tex, smoothness_x_tex, smoothness_y_tex, equation_tex, level_width, level_height);
+ }
+
prev_level_flow_tex = dense_flow_tex;
}
glEndQuery(GL_TIME_ELAPSED);
--- /dev/null
+#version 450 core
+
+in vec2 tc;
+out float smoothness_x, smoothness_y;
+const float eps_sq = 0.001 * 0.001;
+
+uniform sampler2D flow_tex, diff_flow_tex;
+
+// The base flow needs to be normalized.
+// TODO: Should we perhaps reduce this to a separate two-component
+// texture when calculating the derivatives?
+vec2 normalize_flow(vec3 flow)
+{
+ return flow.xy / flow.z;
+}
+
+// This must be a macro, since the offset needs to be a constant expression.
+#define get_flow(x_offs, y_offs) \
+ (normalize_flow(textureOffset(flow_tex, tc, ivec2((x_offs), (y_offs))).xyz) + \
+ textureOffset(diff_flow_tex, tc, ivec2((x_offs), (y_offs))).xy)
+
+float diffusivity(float u_x, float u_y, float v_x, float v_y)
+{
+ return -inversesqrt(u_x * u_x + u_y * u_y + v_x * v_x + v_y * v_y + eps_sq);
+}
+
+void main()
+{
+ float g, g_right, g_up;
+
+ // These are shared between some of the diffusivities.
+ vec2 flow_0_0 = get_flow(0, 0);
+ vec2 flow_1_1 = get_flow(1, 1);
+
+ // Find diffusivity (g) for this pixel, using central differences.
+ {
+ vec2 uv_x = get_flow(1, 0) - get_flow(-1, 0);
+ vec2 uv_y = get_flow(0, 1) - get_flow( 0, -1);
+ g = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y);
+ }
+
+ // Now find diffusivity for the pixel to the right.
+ {
+ vec2 uv_x = get_flow(2, 0) - flow_0_0;
+ vec2 uv_y = flow_1_1 - get_flow( 1, -1);
+ g_right = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y);
+ }
+
+ // And up.
+ {
+ vec2 uv_x = flow_1_1 - get_flow(-1, 1);
+ vec2 uv_y = get_flow(0, 2) - flow_0_0;
+ g_up = diffusivity(uv_x.x, uv_y.x, uv_x.y, uv_y.y);
+ }
+
+ smoothness_x = 0.5 * (g + g_right);
+ smoothness_y = 0.5 * (g + g_up);
+}