]> git.sesse.net Git - nageru/commitdiff
Calculate smoothness and set up the equations. Still fairly buggy (missing 0..1 norma...
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 19 Jul 2018 21:17:12 +0000 (23:17 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 19 Jul 2018 22:04:49 +0000 (00:04 +0200)
equations.frag [new file with mode: 0644]
flow.cpp
smoothness.frag [new file with mode: 0644]

diff --git a/equations.frag b/equations.frag
new file mode 100644 (file)
index 0000000..4509e34
--- /dev/null
@@ -0,0 +1,127 @@
+#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));
+}
index 8250aa3a885e63c633988ad76c3015a142aa0767..bd94cba1c0b86174cd0753ad92366cb1652149a6 100644 (file)
--- a/flow.cpp
+++ b/flow.cpp
@@ -523,6 +523,163 @@ void Derivatives::exec(GLuint input_tex, GLuint I_x_y_tex, GLuint beta_0_tex, in
        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) {
@@ -585,6 +742,8 @@ int main(void)
        Densify densify;
        Prewarp prewarp;
        Derivatives derivatives;
+       ComputeSmoothness compute_smoothness;
+       SetupEquations setup_equations;
 
        GLuint query;
        glGenQueries(1, &query);
@@ -660,6 +819,34 @@ int main(void)
                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);
diff --git a/smoothness.frag b/smoothness.frag
new file mode 100644 (file)
index 0000000..99b9791
--- /dev/null
@@ -0,0 +1,58 @@
+#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);
+}