diff --git a/scripts/run.jl b/scripts/run.jl
index 682065eca3b57b6986c225d14ea9bcdaf0880212..e7a2ed44c0575a68a380081e867b9f6e86866fc2 100644
--- a/scripts/run.jl
+++ b/scripts/run.jl
@@ -9,9 +9,6 @@ ctx = Util.Context(datapath, outpath)
 
 # algorithm comparison
 ctx(experiment_convergence_rate, "fem/convergence/rate")
-# L1 convergence, difference in algorithm
-
-# L1 non-convergence / oscillations
 
 
 # adaptivity
@@ -25,6 +22,8 @@ ctx(experiment_convergence_rate, "fem/convergence/rate")
 # applications
 
 # denoising
-# adaptive
-# optical flow
+ctx(experiment_denoise, "fem/denoise")
+# inpainting
+ctx(experiment_inpaint, "fem/inpaint")
+# adaptive optical flow
 ctx(experiment_optflow_middlebury_all, "fem/optflow/middlebury")
diff --git a/scripts/run_experiments.jl b/scripts/run_experiments.jl
index 02af05e6dd0632acf55ca4084e7c5c144d588106..53937cdb7ab1ded938b5ea879bb25b31fe6b58a6 100644
--- a/scripts/run_experiments.jl
+++ b/scripts/run_experiments.jl
@@ -1,6 +1,7 @@
 using LinearAlgebra: I, det, dot, norm, normalize
 using SparseArrays: sparse, ishermitian
 using Statistics: mean
+using Random: MersenneTwister
 
 using Colors: Gray
 # avoid world-age-issues by preloading ColorTypes
@@ -808,7 +809,7 @@ function denoise(ctx)
 
     project_image! = project_l2_lagrange!
     eps_newton = 1e-5 # cauchy criterion for inner newton loop
-    n_refine = 5
+    n_refine = 0
 
     # convert to cartesian coordinates
     g_arr = from_img(ctx.params.g_arr)
@@ -877,25 +878,42 @@ function denoise(ctx)
     #CSV.write(joinpath(ctx.outdir, "energies.csv"), df)
 
     u_sampled = sample(st.u)
-    saveimg(joinpath(ctx.outdir, "g.png"), to_img(g_arr))
+    saveimg(joinpath(ctx.outdir, "g.png"), grayclamp.(to_img(g_arr)))
     saveimg(joinpath(ctx.outdir, "output.png"), grayclamp.(to_img(u_sampled)))
     savedata(joinpath(ctx.outdir, "data.tex");
         eps_newton, n_refine,
         st.alpha1, st.alpha2, st.lambda, st.beta, st.gamma1, st.gamma2,
+        ctx.params.noise_sigma, ctx.params.noise_p,
         width=size(u_sampled, 1), height=size(u_sampled, 2))
     return st
 end
 
 function experiment_denoise(ctx)
     g_arr = loadimg(joinpath(ctx.indir, "input.png"))
+
+    noise_sigma = 0.1
+    noise_p = 0.02
+
+    rng = (seed = 42; MersenneTwister(seed))
+    #g_arr = clamp.(g_arr .+ noise_sigma * randn(rng, Float64, size(g_arr)), 0., 1.)
+    g_arr .+= noise_sigma * randn(rng, Float64, size(g_arr))
+    for i in eachindex(g_arr)
+        if rand(rng) < noise_p
+            g_arr[i] = rand(Bool) ? 1. : 0.
+        end
+    end
+
+    saveimg(joinpath(ctx.outdir, "input_noisy.png"), grayclamp.(g_arr))
+
     mesh = init_grid(g_arr;)
 
     df = DataFrame()
     denoise(Util.Context(ctx; name = "test", df,
         g_arr, mesh,
-        alpha1 = 0., alpha2 = 50., lambda = 1., beta = 1e-5,
+        alpha1 = 0.2, alpha2 = 8., lambda = 1., beta = 0.,
         gamma1 = 1e-4, gamma2 = 1e-4,
         eps_newton = 1e-5, adaptive = true,
+        noise_sigma, noise_p
     ))
 end
 
@@ -1340,6 +1358,7 @@ function inpaint(ctx)
     u_sampled = sample(st.u)
     saveimg(joinpath(ctx.outdir, "g.png"), to_img(g_arr))
     saveimg(joinpath(ctx.outdir, "output.png"), grayclamp.(to_img(u_sampled)))
+    save_csv(joinpath(ctx.outdir, "mesh.csv"), st.u)
     savedata(joinpath(ctx.outdir, "data.tex");
         ctx.params.eps_newton, n_refine,
         st.alpha1, st.alpha2, st.lambda, st.beta, st.gamma1, st.gamma2,