private static float[,] SolvePoissonNeimanMultiLattice(float[,] rho, int steps_max, float stop_dpd, SolutionReporter callback) { // Making lower resolutions int W = rho.GetLength(0); int H = rho.GetLength(1); List<int> ww = new List<int>(); List<int> hh = new List<int>(); int divides = 0, wt = W, ht = H; ww.Add(wt); hh.Add(ht); while (wt > 1 && ht > 1 && divides < 5) { wt /= 2; ht /= 2; ww.Add(wt); hh.Add(ht); divides ++; } List<float[,]> Rho = new List<float[,]>(); Rho.Add(rho); for (int p = 1; p <= divides; p++) { int w = ww[p - 1]; int h = hh[p - 1]; float[,] rho_new = new float[ww[p], hh[p]]; for (int i = 0; i < w; i++) for (int j = 0; j < h; j++) { if (i / 2 < ww[p] && j / 2 < hh[p]) rho_new[i / 2, j / 2] += 0.25f * Rho[p - 1][i, j]; } Rho.Add(rho_new); } float[,] I = new float[Rho[divides].GetLength(0), Rho[divides].GetLength(1)]; float old_progress = 0; for (int p = divides; p >= 0; p--) { SolvePoissonNeiman(I, Rho[p], steps_max, stop_dpd, delegate (float progress, float[,] solution) { if (callback != null) { float complete_effort = 0; for (int q = divides; q > p; q--) complete_effort += (float)ww[q] * hh[q]; float full_effort = complete_effort; for (int q = p; q >= 0; q--) full_effort += (float)ww[q] * hh[q]; float new_progress = (complete_effort + ww[p] * hh[p] * progress) / full_effort; if (new_progress > old_progress) old_progress = new_progress; callback(old_progress, solution); } }); if (p > 0) { I = Upsample2(I, ww[p - 1], hh[p - 1]); } } return I; }
private static bool SolvePoissonNeiman(float[,] I0, float[,] rho, int steps_max, float stop_dpd, SolutionReporter callback) { int w = rho.GetLength(0), h = rho.GetLength(1); float[,] I = new float[w + 2, h + 2]; float[,] Inew = new float[w + 2, h + 2]; // Setting initial values for (int i = 0; i < w + 2; i++) for (int j = 0; j < h + 2; j++) { int i1 = i; if (i == 0) i1 = 1; if (i == w + 1) i1 = w; int j1 = j; if (j == 0) j1 = 1; if (j == h + 1) j1 = h; I[i, j] = I0[i1 - 1, j1 - 1]; Inew[i, j] = I0[i1 - 1, j1 - 1]; } float delta = 0; float delta_prev = 10000; object delta_lock = new object(); for (int step = 0; step < steps_max; step ++) { // *** Horizontal iterations *** int threads_num = 6; Thread[] threads = new Thread[threads_num]; for (int q = 0; q < threads_num; q++) { threads[q] = new Thread(delegate (object obj) { int i1 = ((thread_data)obj).i1; int i2 = ((thread_data)obj).i2; float my_delta = 0; for (int i = i1; i < i2; i++) { // Run, Thomas, run! float[] alpha = new float[h + 3]; float[] beta = new float[h + 3]; alpha[1] = 0.25f; beta[1] = 0.25f * (I[i + 1, 0] + Inew[i - 1, 0]); for (int j = 1; j < h + 2; j++) { alpha[j + 1] = 1.0f / (4 - alpha[j]); float Fj; if (j < h + 1) Fj = I[i + 1, j] + Inew[i - 1, j] - 2 * rho[i - 1, j - 1]; else Fj = I[i + 1, j] + Inew[i - 1, j]; beta[j + 1] = (Fj + beta[j]) / (4f - alpha[j]); } Inew[i, h + 1] = beta[h + 2]; for (int j = h; j >= 0; j--) { double iold = I[i, j]; Inew[i, j] = alpha[j + 1] * Inew[i, j + 1] + beta[j + 1]; my_delta += (float)Math.Abs(Inew[i, j] - iold); } } lock (delta_lock) { delta += my_delta; } }); } // Starting horizontal threads for (int q = 0; q < threads_num; q++) { thread_data td = new thread_data(); td.i1 = (w / threads_num) * q + 1; if (q < threads_num - 1) { td.i2 = (w / threads_num) * (q + 1) + 1; } else { td.i2 = w + 1; } threads[q].Start(td); } // Waiting for horizontal threads for (int q = 0; q < threads_num; q++) { threads[q].Join(); } // Restoring Neiman boundary conditions after horizontal iterations for (int i = 0; i < w + 2; i++) { Inew[i, 0] = Inew[i, 1]; Inew[i, h + 1] = Inew[i, h]; } for (int j = 0; j < h + 2; j++) { Inew[0, j] = Inew[1, j]; Inew[w + 1, j] = Inew[w, j]; } // Controlling the constant after horizontal iterations float m = 0; for (int i = 0; i < w + 2; i++) for (int j = 0; j < h + 2; j++) { m += Inew[i, j]; } m /= (w+2) * (h+2); for (int i = 0; i < w + 2; i++) for (int j = 0; j < h + 2; j++) { I[i, j] = Inew[i, j] - m; } // *** Vertical iterations *** threads = new Thread[threads_num]; for (int q = 0; q < threads_num; q++) { threads[q] = new Thread(delegate (object obj) { int j1 = ((thread_data)obj).i1; int j2 = ((thread_data)obj).i2; float my_delta = 0; for (int j = j1; j < j2; j++) { // Run, Thomas, run! float[] alpha = new float[w + 3]; float[] beta = new float[w + 3]; alpha[1] = 0.25f; beta[1] = 0.25f * (I[0, j + 1] + Inew[0, j - 1]); for (int i = 1; i < w + 2; i++) { alpha[i + 1] = 1.0f / (4 - alpha[i]); float Fi; if (i < w + 1) Fi = I[i, j + 1] + Inew[i, j - 1] - 2 * rho[i - 1, j - 1]; else Fi = I[i, j + 1] + Inew[i, j - 1]; beta[i + 1] = (Fi + beta[i]) / (4f - alpha[i]); } Inew[w + 1, j] = beta[w + 2]; for (int i = w; i >= 0; i--) { double iold = I[i, j]; Inew[i, j] = alpha[i + 1] * Inew[i + 1, j] + beta[i + 1]; my_delta += (float)Math.Abs(Inew[i, j] - iold); } } lock (delta_lock) { delta += my_delta; } }); } // Starting vertical threads for (int q = 0; q < threads_num; q++) { thread_data td = new thread_data(); td.i1 = (h / threads_num) * q + 1; if (q < threads_num - 1) { td.i2 = (h / threads_num) * (q + 1) + 1; } else { td.i2 = h + 1; } threads[q].Start(td); } // Waiting for vertical threads for (int q = 0; q < threads_num; q++) { threads[q].Join(); } // Restoring Neiman boundary conditions after vertical iterations for (int i = 0; i < w + 2; i++) { Inew[i, 0] = Inew[i, 1]; Inew[i, h + 1] = Inew[i, h]; } for (int j = 0; j < h + 2; j++) { Inew[0, j] = Inew[1, j]; Inew[w + 1, j] = Inew[w, j]; } // Controlling the constant after vertical iterations m = 0; for (int i = 0; i < w + 2; i++) for (int j = 0; j < h + 2; j++) { m += Inew[i, j]; } m /= (w+2) * (h+2); for (int i = 0; i < w + 2; i++) for (int j = 0; j < h + 2; j++) { I[i, j] = Inew[i, j] - m; } for (int i = 1; i < w + 1; i++) for (int j = 1; j < h + 1; j++) { I0[i - 1, j - 1] = I[i, j]; } delta /= (float)Math.Sqrt(w * h); float dpd = Math.Abs((delta - delta_prev) / delta); // This formula is found experimentally float progress = (float)Math.Min(Math.Pow(stop_dpd / (dpd + 0.000001), 0.78), 0.999); if (callback != null) { callback(progress, I0); } if (dpd < stop_dpd) { return true; } delta_prev = delta; delta = 0; } return false; }