static bool so_seams_intersect(so_seam_t a, so_seam_t b) { #if false // don't join intersected seams return(false); #endif // compare bounding boxes first if (a.x_min > b.x_max || b.x_min >= a.x_max || a.y_min > b.y_max || b.y_min >= a.y_max) { return(false); } // bounds intersect . check each individual texel for intersection if (a.texels.capacity > b.texels.capacity) // swap so that we always loop over the smaller set { so_seam_t tmp = a; a = b; b = tmp; } for (int i = 0; i < a.texels.capacity; i++) { if (a.texels.texels[i].x != -1) { if (so_texel_set_contains(b.texels, a.texels.texels[i])) { return(true); } } } return(false); }
static void so_seams_in_place_merge(so_seam_t dst, so_seam_t src) { // expand bounding box dst.x_min = Mathf.Min(src.x_min, dst.x_min); dst.y_min = Mathf.Min(src.y_min, dst.y_min); dst.x_max = Mathf.Max(src.x_max, dst.x_max); dst.y_max = Mathf.Max(src.y_max, dst.y_max); // insert src elements so_texel_set_add(dst.texels, src.texels.texels, src.texels.count, src.texels.capacity); so_stitching_points_append(dst.stitchingPoints, src.stitchingPoints); }
static void so_seam_add(so_seam_t seam, so_stitching_point_t point) { for (int side = 0; side < 2; side++) { for (int texel = 0; texel < 4; texel++) { so_texel_t t = point.sides[side].texels[texel]; seam.x_min = Mathf.Min(t.x, seam.x_min); seam.y_min = Mathf.Min(t.y, seam.y_min); seam.x_max = Mathf.Max(t.x, seam.x_min); seam.y_max = Mathf.Max(t.y, seam.y_min); } so_texel_set_add(seam.texels, point.sides[side].texels, 4); } so_stitching_points_add(seam.stitchingPoints, point); }
public static bool so_seam_optimize(so_seam_t seam, Color[] data, int w, int h, int c = 3, float lambda = 0.1f) { so_texel_set_t texels = seam.texels; so_stitching_points_t stitchingPoints = seam.stitchingPoints; int m = stitchingPoints.count; int n = texels.count; so_texel_t[] texelsFlat = new so_texel_t[n]; for (int i = 0; i < n; i++) { texelsFlat[i] = new so_texel_t(0, 0); } float[] A = new float[(m + n) * 8]; int[] AsparseIndices = new int[(m + n) * 8]; float[] b = new float[m + n]; float[] Atb = new float[n]; float[] x = new float[n]; for (int i = 0, j = 0; i < texels.capacity && j < n; i++) { if (texels.texels[i].x != -1) { texelsFlat[j++] = texels.texels[i]; } } System.Array.Sort(texelsFlat, so_texel_cmp); int r = 0; for (int i = 0; i < m; i++) { int[] column0 = { 0, 0, 0, 0 }; int[] column1 = { 0, 0, 0, 0 }; bool side0valid = false, side1valid = false; for (int k = 0; k < 4; k++) { so_texel_t t0 = stitchingPoints.points[i].sides[0].texels[k]; so_texel_t t1 = stitchingPoints.points[i].sides[1].texels[k]; column0[k] = System.Array.BinarySearch(texelsFlat, t0, so_texel_cmp); column1[k] = System.Array.BinarySearch(texelsFlat, t1, so_texel_cmp); if (column0[k] == -1) { side0valid = false; break; } if (column1[k] == -1) { side1valid = false; break; } // test for validity of stitching point for (int ci = 0; ci < c; ci++) { side0valid |= data[t0.y * w + t0.x][ci] > 0.0f; side1valid |= data[t1.y * w + t1.x][ci] > 0.0f; } } if (side0valid && side1valid) { for (int k = 0; k < 4; k++) { A[r * 8 + k * 2 + 0] = stitchingPoints.points[i].sides[0].weights[k]; AsparseIndices[r * 8 + k * 2 + 0] = column0[k]; A[r * 8 + k * 2 + 1] = -stitchingPoints.points[i].sides[1].weights[k]; AsparseIndices[r * 8 + k * 2 + 1] = column1[k]; } r++; } } m = r; // add error terms for deviation from original pixel value (scaled by lambda) for (int i = 0; i < n; i++) { A[(m + i) * 8] = lambda; AsparseIndices[(m + i) * 8 + 0] = i; AsparseIndices[(m + i) * 8 + 1] = -1; } so_sparse_entries_t AtA = so_matrix_At_times_A(A, AsparseIndices, 8, m + n, n); so_sparse_entries_t L = so_matrix_cholesky_prepare(AtA, n); if (L.count == 0) { return(false); // Cholesky decomposition failed } so_sparse_entries_t Lcols = new so_sparse_entries_t(L.count); for (int i = 0; i < L.count; i++) { so_sparse_matrix_add(Lcols, (L.entries[i].index % n) * n + (L.entries[i].index / n), L.entries[i].value); } so_sparse_matrix_sort(Lcols); // solve each color channel independently for (int ci = 0; ci < c; ci++) { for (int i = 0; i < n; i++) { b[m + i] = lambda * data[texelsFlat[i].y * w + texelsFlat[i].x][ci]; } so_matrix_At_times_b(A, m + n, n, b, Atb, AsparseIndices, 8); so_matrix_cholesky_solve(L, Lcols, x, Atb, n); // write out results for (int i = 0; i < n; i++) { data[texelsFlat[i].y * w + texelsFlat[i].x][ci] = Mathf.Max(x[i], 0); } } return(true); }
static void so_seams_add_seam(List <so_seam_t> seams, Vector2 a0, Vector2 a1, Vector2 b0, Vector2 b1, Color[] data, int w, int h, int c) { Vector2 s = new Vector2(w - 1, h - 1); a0 = Extensions.Multiply(a0, s); a1 = Extensions.Multiply(a1, s); b0 = Extensions.Multiply(b0, s); b1 = Extensions.Multiply(b1, s); Vector2 ad = a1 - a0; Vector2 bd = b1 - b0; float l = Mathf.Max(ad.magnitude, bd.magnitude); int iterations = (int)(l * 5.0f); // TODO: is this the best value? float step = 1.0f / iterations; so_seam_t currentSeam = new so_seam_t(); currentSeam.x_min = w; currentSeam.y_min = h; currentSeam.x_max = 0; currentSeam.y_max = 0; // so_seam_alloc currentSeam.stitchingPoints = new so_stitching_points_t(iterations + 1); currentSeam.texels = new so_texel_set_t(); for (int i = 0; i <= iterations; i++) { float t = i * step; Vector2 a = a0 + ad * t; Vector2 b = b0 + bd * t; // Kanglai: shouldn't be Round here int ax = Mathf.FloorToInt(a.x), ay = Mathf.FloorToInt(a.y); int bx = Mathf.FloorToInt(b.x), by = Mathf.FloorToInt(b.y); float au = a.x - ax, av = a.y - ay, nau = 1.0f - au, nav = 1.0f - av; float bu = b.x - bx, bv = b.y - by, nbu = 1.0f - bu, nbv = 1.0f - bv; so_texel_t ta0 = new so_texel_t(ax, ay); so_texel_t ta1 = new so_texel_t(Mathf.Min(ax + 1, w - 1), ay); so_texel_t ta2 = new so_texel_t(ax, Mathf.Min(ay + 1, h - 1)); so_texel_t ta3 = new so_texel_t(Mathf.Min(ax + 1, w - 1), Mathf.Min(ay + 1, h - 1)); so_texel_t tb0 = new so_texel_t(bx, by); so_texel_t tb1 = new so_texel_t(Mathf.Min(bx + 1, w - 1), by); so_texel_t tb2 = new so_texel_t(bx, Mathf.Min(by + 1, h - 1)); so_texel_t tb3 = new so_texel_t(Mathf.Min(bx + 1, w - 1), Mathf.Min(by + 1, h - 1)); /* * so_fill_with_closest(ta0.x, ta0.y, data, w, h, c); * so_fill_with_closest(ta1.x, ta1.y, data, w, h, c); * so_fill_with_closest(ta2.x, ta2.y, data, w, h, c); * so_fill_with_closest(ta3.x, ta3.y, data, w, h, c); * * so_fill_with_closest(tb0.x, tb0.y, data, w, h, c); * so_fill_with_closest(tb1.x, tb1.y, data, w, h, c); * so_fill_with_closest(tb2.x, tb2.y, data, w, h, c); * so_fill_with_closest(tb3.x, tb3.y, data, w, h, c); */ so_stitching_point_t sp = new so_stitching_point_t(); sp.sides[0].texels[0] = ta0; sp.sides[0].texels[1] = ta1; sp.sides[0].texels[2] = ta2; sp.sides[0].texels[3] = ta3; sp.sides[0].weights[0] = nau * nav; sp.sides[0].weights[1] = au * nav; sp.sides[0].weights[2] = nau * av; sp.sides[0].weights[3] = au * av; sp.sides[1].texels[0] = tb0; sp.sides[1].texels[1] = tb1; sp.sides[1].texels[2] = tb2; sp.sides[1].texels[3] = tb3; sp.sides[1].weights[0] = nbu * nbv; sp.sides[1].weights[1] = bu * nbv; sp.sides[1].weights[2] = nbu * bv; sp.sides[1].weights[3] = bu * bv; so_seam_add(currentSeam, sp); } so_seam_t dstSeam = null; for (int i = 0; i < seams.Count; i++) { so_seam_t seam = seams[i]; // Kanglai: we put seams that intersects together if (so_seams_intersect(currentSeam, seam)) { if (dstSeam == null) // found a seam that the edge is connected to . add current edge to that seam { so_seams_in_place_merge(seam, currentSeam); dstSeam = seam; } else // found another seam that the edge is connected to . merge those seams { so_seams_in_place_merge(dstSeam, seam); // remove current seam from seams seams.Remove(seam); i--; } } } if (dstSeam == null) // did not find a seam that the edge is connected to . make a new one { seams.Add(currentSeam); } }