def test_preconditioners(): atol = 1e-6 index = 1 scale = 0.2 name = f"GT{index:02d}" # print(name) image_dir = "data" image = load_image(f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear") trimap = load_image( f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "nearest", ) A, b = make_linear_system(cf_laplacian(image), trimap) preconditioners = [ ("no", lambda A: None), ("jacobi", lambda A: jacobi(A)), ("icholt", lambda A: ichol(A, max_nnz=500000)), ("vcycle", lambda A: vcycle(A, trimap.shape)), ] expected_iterations = { "no": 532, "jacobi": 250, "icholt": 3, "vcycle": 88, } for preconditioner_name, preconditioner in preconditioners: callback = CounterCallback() M = preconditioner(A) x = cg(A, b, M=M, atol=atol, rtol=0, maxiter=10000, callback=callback) r = b - A.dot(x) norm_r = np.linalg.norm(r) assert norm_r <= atol n_expected = expected_iterations[preconditioner_name] if callback.n > n_expected: print( "WARNING: Unexpected number of iterations. Expected %d, but got %d" % (n_expected, callback.n)) assert callback.n <= n_expected
def test_lkm(): index = 1 scale = 0.2 epsilon = 1e-7 radius = 2 image_dir = "data" name = f"GT{index:02d}" image = load_image(f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear") trimap = load_image( f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "nearest", ) L_lkm, diag_L_lkm = lkm_laplacian(image, epsilon=epsilon, radius=radius) L_cf = cf_laplacian(image, epsilon=epsilon, radius=radius) A_cf, b, c = make_linear_system(L_cf, trimap, return_c=True) def A_lkm(x): return L_lkm(x) + c * x inv_diag_A_lkm = 1.0 / (diag_L_lkm + c) def jacobi_lkm(r): return inv_diag_A_lkm * r jacobi_cf = jacobi(A_cf) lkm_callback = ProgressCallback() cf_callback = ProgressCallback() x_lkm = cg(A_lkm, b, M=jacobi_lkm) x_cf = cg(A_cf, b, M=jacobi_cf) difference = np.linalg.norm(x_lkm - x_cf) assert difference < 2e-4 assert abs(lkm_callback.n - cf_callback.n) <= 2
def compute_alpha(image, trimap, laplacian_name, is_fg, is_bg, is_known): laplacians = dict( (laplacian.__name__, laplacian) for laplacian in LAPLACIANS) atol = ATOL * np.sum(is_known) if laplacian_name == "lkm_laplacian": L_matvec, diag_L = lkm_laplacian(image) lambda_value = 100.0 c = lambda_value * is_known.ravel() b = lambda_value * is_fg.ravel() inv_diag_A = 1.0 / (diag_L + c) A = lambda x: L_matvec(x) + c * x # jacobi preconditioner for lkm_laplacian M = lambda x: inv_diag_A * x else: laplacian = laplacians[laplacian_name] A, b = make_linear_system(laplacian(image), trimap) preconditioner = { "knn_laplacian": jacobi, "rw_laplacian": jacobi, "cf_laplacian": ichol, "lbdm_laplacian": ichol, "uniform_laplacian": ichol, }[laplacian_name] M = preconditioner(A) x = cg(A, b, M=M, atol=atol) alpha = np.clip(x, 0, 1).reshape(trimap.shape) return alpha
def test_laplacians(): indices = np.arange(27) + 1 scale = 0.1 image_dir = "data" # allow 1% regression allowed_error = 0.01 expected_errors = { "cf_laplacian": [ 1.0839111942763648, 3.841759348043566, 3.358489754678929, 5.079739853533443, 1.8166309691629416, 1.4385024442876313, 1.501023996041348, 4.18173542822268, 3.4705502834921487, 1.9351918367749816, 2.488966062209934, 1.3989649710604704, 3.8741776417276306, 0.9728827692868615, 2.5399897862272747, 10.642191399781298, 1.6438877577185722, 2.1543490658816635, 1.1489311470229537, 0.954323791336118, 4.63568149096655, 1.3580148342589717, 1.1415671048773133, 2.6984843343086196, 4.691593185812834, 6.003127907648125, 4.390608172576709, ], "knn_laplacian": [ 2.089130250752722, 3.767524785077177, 3.7231290999572506, 4.248310409891682, 2.577488769545899, 3.966983051036645, 2.672147903806734, 5.083407281314084, 4.325617257887361, 2.491360039941334, 3.4146860032738173, 2.1208129283441575, 5.147092338936775, 2.1976345580461056, 3.0719533535155263, 13.440447444054444, 3.1829279859509327, 3.711063622279358, 2.4566255493763935, 1.9078593671486113, 4.956619727222683, 3.1646762552974868, 2.783888863957631, 4.714904428201373, 4.496672172372184, 10.402315008230564, 5.270881922679586, ], "lbdm_laplacian": [ 1.0839897312877216, 3.841573408975021, 3.365742999572245, 5.077997360572911, 1.8211938024322178, 1.4392778020952708, 1.5006381799933766, 4.242864047447403, 3.4904348404779753, 1.9357254296106827, 2.48917998418364, 1.4043779651168937, 3.8737045683526024, 0.973164964965189, 2.5443819959543457, 10.651958437589906, 1.6551494481797882, 2.155449159543011, 1.1490544867887889, 0.9538815333644246, 4.635535802794727, 1.3595341099477465, 1.143076373539195, 2.707384007058893, 4.698042368150364, 6.0029066668301, 4.390146320671879, ], "rw_laplacian": [ 4.865828574946059, 6.1494492984553615, 5.309294879045085, 9.319890125482436, 5.133000114540576, 5.6589208995955405, 6.225056464430212, 9.487026325071337, 7.871287695275896, 5.059639993759375, 5.810823041482708, 4.846480646386845, 10.15011136629045, 4.9853995176605395, 4.285341458168511, 19.431455009600853, 5.8216134507328015, 5.952428504902877, 4.0253392720779555, 5.8843985862801045, 8.728913826808643, 5.5895832018589084, 4.840184561383416, 6.210095889788392, 7.375539945860434, 14.111658110736256, 10.200776413201114, ], "uniform_laplacian": [ 4.0444203072543266, 5.2284413228378135, 9.019003158309758, 9.778390304677947, 3.6483079178591966, 5.0698226038235195, 4.712467105116259, 8.21743217218649, 8.122577721412982, 4.696993284565137, 4.268656408720733, 3.477238604177124, 10.00684602150998, 3.481498583276864, 3.9349298092727087, 11.370794857942638, 4.369772744405769, 5.607500940968039, 2.8416197864350137, 4.550807753215429, 8.621295976221598, 4.878478538957252, 4.979139178621439, 4.5556096152615835, 7.20245540436063, 11.036244338204016, 7.370920965316346, ], } debug = False actual_errors = {} for laplacian in LAPLACIANS: laplacian_name = laplacian.__name__ print("testing", laplacian_name) errors = [] for index, expected_error in zip(indices, expected_errors[laplacian_name]): name = f"GT{index:02d}" if debug: print(name) image = load_image( f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear", ) trimap = load_image( f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "nearest", ) true_alpha = load_image( f"{image_dir}/gt_training_lowres/{name}.png", "gray", scale, "bilinear" ) A, b = make_linear_system(laplacian(image), trimap) x = scipy.sparse.linalg.spsolve(A, b) alpha = np.clip(x, 0, 1).reshape(trimap.shape) is_unknown = trimap_split(trimap, flatten=False)[3] difference_unknown = np.abs(alpha - true_alpha)[is_unknown] error = np.linalg.norm(difference_unknown) additional_error = (error - expected_error) / expected_error if additional_error > allowed_error: print("Regression:") print(laplacian_name) print(f"Performance decreased by {100.0 * additional_error:.3f} %") assert additional_error < allowed_error errors.append(error) actual_errors[laplacian_name] = errors print("Errors:") print(actual_errors)
def run_solver_single_image(solver_name, scale, index): # Load images name = f"GT{index:02d}.png" image_path = os.path.join(IMAGE_DIR, "input_training_lowres", name) trimap_path = os.path.join(IMAGE_DIR, "trimap_training_lowres/Trimap1", name) image = load_image(image_path, "rgb", scale, "bilinear") trimap = load_image(trimap_path, "gray", scale, "nearest") # Create linear system L = cf_laplacian(image) A, b = make_linear_system(L, trimap) is_fg, is_bg, is_known, is_unknown = trimap_split(trimap) atol = ATOL * np.sum(is_known) rtol = atol / np.linalg.norm(b) # Compute various matrix representations Acsr = A.tocsr() Acsc = A.tocsc() Acoo = A.tocoo() AL = scipy.sparse.tril(Acoo) # Start memory usage measurement thread memory_usage = [get_memory_usage()] thread = threading.Thread(target=log_memory_usage, args=(memory_usage, )) thread.is_running = True # All threads should die if the solver thread crashes so we can at least # carry on with the other solvers. thread.daemon = True thread.start() # Measure solver build time # Note that it is not easily possible to separate build time from solve time # for every solver, which is why only the sum of build_time and solve_time # should be compared for fairness. start_time = time.perf_counter() run_solver = build_solver(solver_name, A, Acsr, Acsc, Acoo, AL, b, atol, rtol) build_time = time.perf_counter() - start_time # Measure actual solve time start_time = time.perf_counter() x = run_solver() solve_time = time.perf_counter() - start_time # Stop memory usage measuring thread thread.is_running = False thread.join() # Compute relative error r = b - A.dot(x) norm_r = np.linalg.norm(r) # Store results h, w = trimap.shape result = dict( solver_name=str(solver_name), image_name=str(name), scale=float(scale), index=int(index), norm_r=float(norm_r), build_time=float(build_time), solve_time=float(solve_time), atol=float(atol), rtol=float(rtol), width=int(w), height=int(h), n_fg=int(np.sum(is_fg)), n_bg=int(np.sum(is_bg)), n_known=int(np.sum(is_known)), n_unknown=int(np.sum(is_unknown)), memory_usage=memory_usage, ) print(result) # Ensure that everything worked as expected assert norm_r <= atol # Inspect alpha for debugging if 0: alpha = np.clip(x, 0, 1).reshape(h, w) show_images([alpha]) return result
def test_laplacians(): indices = np.arange(27) + 1 scale = 0.1 atol = 1e-5 image_dir = "data" # allow 1% regression allowed_error = 0.01 expected_errors = { "cf_laplacian": [ 4.355988465052598, 7.754267319636701, 4.747200315117146, 7.644794385858358, 4.384527830585845, 4.531604199822819, 4.1031381858764835, 6.0839679119939385, 4.524651010229449, 4.358659056252233, 5.890604883965759, 3.5985421897613152, 10.065349782695913, 2.585264548989054, 3.7208956259331214, 16.362808646457598, 3.8009339105572795, 6.408594319626463, 6.017566616442684, 4.044413987769631, 11.785953665340037, 5.430424431633303, 3.21780354668552, 5.1231383764470655, 9.431651112452275, 13.036791155586041, 8.988754957709757, ], "knn_laplacian": [ 3.529027369985819, 6.779684407351125, 5.315215932104659, 7.98386599862108, 4.201669915857062, 5.292745196567874, 4.154624688607045, 6.386926684070437, 5.405970320706553, 4.908653603147019, 6.359468813893265, 3.2796344351763556, 14.078147343580305, 2.8225579528031197, 4.833168662156053, 13.893654956704204, 4.55905119644519, 6.511696323731112, 4.901962902239601, 4.058804576038566, 11.250593534800391, 5.708992860151824, 3.9937473660706355, 7.521799056342947, 12.451095645355371, 19.559055742483523, 10.23295095188048, ], "lbdm_laplacian": [ 4.355917047411817, 7.755294676926569, 4.754459256408494, 7.6527379119497985, 4.388543223502785, 4.532265356616695, 4.101334275889063, 6.157890368267364, 4.542514790384632, 4.354168371741936, 5.8910132753382936, 3.597996913221781, 10.046433903595707, 2.5842975995911734, 3.7271507175548657, 16.377298831555507, 3.8079423147251483, 6.411020196265968, 6.018502476088196, 4.044885752161074, 11.78269622620083, 5.430059151842563, 3.2183422118639493, 5.132075679579669, 9.434882862603411, 13.025182853722477, 8.987652819180946, ], "rw_laplacian": [ 5.41122022101416, 8.433097819805354, 5.831135210315725, 13.5612231500563, 6.709520989020883, 7.070404131242503, 8.386298390789808, 12.322855420059101, 8.021969631117862, 6.081774217141341, 7.7249956991680255, 5.764913649046752, 16.450934622522023, 5.160830048929572, 5.1776067921810425, 19.8520195558823, 6.6178806558435515, 8.427877366337041, 6.402179991723246, 8.23741566966435, 13.829571729270182, 7.287711407153692, 6.013004768961857, 8.373112239007229, 14.367435257885573, 21.48881471389073, 15.311157506616729, ], "uniform_laplacian": [ 9.060169551556976, 10.412435398963028, 13.59968223667663, 13.846523627602492, 7.920606255132267, 9.742329228146986, 9.842485562589646, 12.678815442427544, 11.046849004040244, 9.072405246677807, 10.491323657975121, 6.997344821901131, 16.996103185024563, 6.70609069103123, 6.738930012060983, 13.416425113943234, 7.636413320373581, 11.921912128705248, 8.430136202987251, 9.091272603814666, 16.26958162705805, 10.192918063068861, 8.775645747372451, 8.203250649100266, 12.316984833694821, 20.0647516222413, 12.544650300995547, ], } debug = False for laplacian in LAPLACIANS: laplacian_name = laplacian.__name__ print("testing", laplacian_name) errors = [] for index, expected_error in zip(indices, expected_errors[laplacian_name]): name = f"GT{index:02d}" if debug: print(name) image = load_image( f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear", ) trimap = load_image( f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "bilinear", ) true_alpha = load_image( f"{image_dir}/gt_training_lowres/{name}.png", "gray", scale, "nearest") A, b = make_linear_system(laplacian(image), trimap) x = scipy.sparse.linalg.spsolve(A, b) r = b - A.dot(x) norm_r = np.linalg.norm(r) alpha = np.clip(x, 0, 1).reshape(trimap.shape) is_unknown = trimap_split(trimap, flatten=False)[3] difference_unknown = np.abs(alpha - true_alpha)[is_unknown] error = np.linalg.norm(difference_unknown) additional_error = (error - expected_error) / expected_error if additional_error > allowed_error: print("Regression:") print(laplacian_name) print( f"Performance decreased by {100.0 * additional_error:.3f} %" ) assert additional_error < allowed_error errors.append(error)
def main(): solvers = [] if 0: from pymatting import cg, ichol def solve_cg_icholt(A, b, atol): M = ichol(A, discard_threshold=1e-3, shifts=[0.002]) return cg(A, b, M=M, atol=atol, rtol=0) solvers.append( ("cg_icholt", lambda: solve_cg_icholt(Acsc, b, atol=atol))) if 1: import pyamg from pymatting import cg def solve_pyamg(A, b, atol): M = pyamg.smoothed_aggregation_solver(A).aspreconditioner() return cg(A, b, M=M, atol=atol, rtol=0) solvers.append(("pyamg", lambda: solve_pyamg(Acsr, b, atol=atol))) if 0: from solve_mumps import solve_mumps_coo, init_mpi, finalize_mpi init_mpi() def solve_mumps(A, b): return solve_mumps_coo(A.data, A.row, A.col, b, is_symmetric=True) solvers.append(("mumps", lambda: solve_mumps(AL, b))) if 1: from solve_petsc import solve_petsc_coo, init_petsc, finalize_petsc init_petsc() def solve_petsc(A, b, atol): return solve_petsc_coo(A.data, A.row, A.col, b, atol=atol, gamg_threshold=0.1) solvers.append(("petsc", lambda: solve_petsc(Acoo, b, atol=atol))) if 1: from solve_amgcl import solve_amgcl_csr def solve_amgcl(A, b, atol): return solve_amgcl_csr(A.data, A.indices, A.indptr, b, atol=atol, rtol=0) solvers.append(("amgcl", lambda: solve_amgcl(Acsr, b, atol=atol))) if 0: solvers.append( ("umfpack", lambda: scipy.sparse.linalg.spsolve(Acsc, b, use_umfpack=True))) if 0: def solve_superLU(A, b): # Usually the same as: # scipy.sparse.linalg.spsolve(A, b, use_umfpack=False) return scipy.sparse.linalg.splu(A).solve(b) solvers.append(("superlu", lambda: solve_superLU(Acsc, b))) if 1: from solve_eigen import solve_eigen_cholesky_coo def solve_eigen_cholesky(A, b): return solve_eigen_cholesky_coo(A.data, A.row, A.col, b) solvers.append( ("eigen_cholesky", lambda: solve_eigen_cholesky(Acoo, b))) if 0: from solve_eigen import solve_eigen_icholt_coo def solve_eigen_icholt(A, b, rtol): # just large enough to not fail for given images (might fail with larger/different images) initial_shift = 2e-4 return solve_eigen_icholt_coo(A.data, A.row, A.col, b, rtol=rtol, initial_shift=initial_shift) solvers.append( ("eigen_icholt", lambda: solve_eigen_icholt(Acoo, b, rtol=rtol))) indices = np.arange(27) scales = np.sqrt(np.linspace(0.1, 1.0, 11)) # indices = np.int32([1, 2]) # scales = [0.1] atol0 = 1e-7 image_dir = "data" for solver_name, solver in solvers: results = [] for scale in scales: for index in indices + 1: name = f"GT{index:02d}" image = load_image( f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear", ) trimap = load_image( f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "bilinear", ) L = cf_laplacian(image) A, b = make_linear_system(L, trimap) is_fg, is_bg, is_known, is_unknown = trimap_split(trimap) atol = atol0 * np.sum(is_known) rtol = atol / np.linalg.norm(b) Acsr = A.tocsr() Acsc = A.tocsc() Acoo = A.tocoo() AL = scipy.sparse.tril(Acoo) def log_memory_usage(memory_usage): while running: memory_usage.append(get_memory_usage()) time.sleep(0.01) memory_usage = [get_memory_usage()] running = True thread = threading.Thread(target=log_memory_usage, args=(memory_usage, )) thread.start() start_time = time.perf_counter() x = solver() elapsed_time = time.perf_counter() - start_time running = False thread.join() r = b - A.dot(x) norm_r = np.linalg.norm(r) h, w = trimap.shape result = dict( solver_name=str(solver_name), scale=float(scale), index=int(index), norm_r=float(norm_r), t=float(elapsed_time), atol=float(atol), rtol=float(rtol), width=int(w), height=int(h), n_fg=int(np.sum(is_fg)), n_bg=int(np.sum(is_bg)), n_known=int(np.sum(is_known)), n_unknown=int(np.sum(is_unknown)), memory_usage=memory_usage, ) print(result) assert norm_r <= atol if 0: alpha = np.clip(x, 0, 1).reshape(h, w) show_images([alpha]) results.append(result) path = f"benchmarks/results/solver/{solver_name}/results.json" dir_name = os.path.split(path)[0] if len(dir_name) > 0: os.makedirs(dir_name, exist_ok=True) with open(path, "w") as f: json.dump(results, f, indent=4)
def main(): indices = 1 + np.arange(27) scale = 1.0 image_dir = "../data" info = {} laplacian_name = "lkm_laplacian" print(laplacian_name) errors = {} for index in indices: name = f"GT{index:02d}" print(name) image = load_image(f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear") trimap = load_image(f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "bilinear") true_alpha = load_image(f"{image_dir}/gt_training_lowres/{name}.png", "gray", scale, "nearest") L_matvec, diag_L = lkm_laplacian(image) is_fg, is_bg, is_known, is_unknown = trimap_split(trimap) atol = 1e-7 * np.sum(is_known) lambda_value = 100.0 c = lambda_value * is_known b = lambda_value * is_fg inv_diag_A = 1.0 / (diag_L + c) def A_matvec(x): return L_matvec(x) + c * x def jacobi(x): return inv_diag_A * x x = cg(A_matvec, b, M=jacobi, atol=atol) alpha = np.clip(x, 0, 1).reshape(trimap.shape) difference_unknown = np.abs(alpha - true_alpha)[is_unknown.reshape(trimap.shape)] error = np.linalg.norm(difference_unknown) errors[str(index)] = error info[laplacian_name] = errors for laplacian in LAPLACIANS: laplacian_name = laplacian.__name__ print(laplacian_name) errors = {} for index in indices: name = f"GT{index:02d}" print(name) image = load_image(f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear") trimap = load_image(f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "bilinear") true_alpha = load_image(f"{image_dir}/gt_training_lowres/{name}.png", "gray", scale, "nearest") A, b = make_linear_system(laplacian(image), trimap) is_fg, is_bg, is_known, is_unknown = trimap_split(trimap, flatten=False) atol = 1e-7 * np.sum(is_known) preconditioner = { "knn_laplacian": jacobi, "rw_laplacian": jacobi, "cf_laplacian": ichol, "lbdm_laplacian": ichol, "uniform_laplacian": ichol, }[laplacian_name] x = cg(A, b, M=preconditioner(A), atol=atol) alpha = np.clip(x, 0, 1).reshape(trimap.shape) difference_unknown = np.abs(alpha - true_alpha)[is_unknown] error = np.linalg.norm(difference_unknown) errors[str(index)] = error info[laplacian_name] = errors with open("results/laplacians.json", "w") as f: print(info) json.dump(info, f, indent=4)
def test_lkm(): index = 1 scale = 0.2 atol = 1e-5 epsilon = 1e-7 radius = 2 image_dir = "data" errors = [] name = f"GT{index:02d}" # print(name) image = load_image( f"{image_dir}/input_training_lowres/{name}.png", "rgb", scale, "bilinear" ) trimap = load_image( f"{image_dir}/trimap_training_lowres/Trimap1/{name}.png", "gray", scale, "bilinear", ) true_alpha = load_image( f"{image_dir}/gt_training_lowres/{name}.png", "gray", scale, "nearest" ) L_lkm, diag_L_lkm = lkm_laplacian(image, epsilon=epsilon, radius=radius) L_cf = cf_laplacian(image, epsilon=epsilon, radius=radius) A_cf, b, c = make_linear_system(L_cf, trimap, return_c=True) def A_lkm(x): return L_lkm(x) + c * x inv_diag_A_lkm = 1.0 / (diag_L_lkm + c) def jacobi_lkm(r): return inv_diag_A_lkm * r jacobi_cf = jacobi(A_cf) lkm_callback = ProgressCallback() cf_callback = ProgressCallback() x_lkm = cg(A_lkm, b, M=jacobi_lkm) x_cf = cg(A_cf, b, M=jacobi_cf) if 0: # show result show_images( [x_lkm.reshape(trimap.shape), x_cf.reshape(trimap.shape),] ) difference = np.linalg.norm(x_lkm - x_cf) # print("norm(x_lkm - x_cf):") # print(difference) # print("iterations:") # print("lkm:", lkm_callback.n) # print("cf:", cf_callback.n) assert difference < 1e-5 assert abs(lkm_callback.n - cf_callback.n) <= 2