|
36 | 36 |
|
37 | 37 | sys.path.append('extensions/stable-diffusion-webui-depthmap-script/scripts')
|
38 | 38 |
|
| 39 | +from stereoimage_generation import * |
| 40 | + |
39 | 41 | # midas imports
|
40 | 42 | from midas.dpt_depth import DPTDepthModel
|
41 | 43 | from midas.midas_net import MidasNet
|
@@ -106,7 +108,7 @@ def ui(self, is_img2img):
|
106 | 108 | with gr.Row():
|
107 | 109 | stereo_divergence = gr.Slider(minimum=0.05, maximum=10.005, step=0.01, label='Divergence (3D effect)', value=2.5)
|
108 | 110 | with gr.Row():
|
109 |
| - stereo_fill = gr.Dropdown(label="Gap fill technique", choices=['none', 'naive', 'naive_interpolating', 'polylines_soft', 'polylines_sharp'], value='polylines_sharp', type="index", elem_id="stereo_fill_type") |
| 111 | + stereo_fill = gr.Dropdown(label="Gap fill technique", choices=['none', 'naive', 'naive_interpolating', 'polylines_soft', 'polylines_sharp'], value='polylines_sharp', type="value", elem_id="stereo_fill_type") |
110 | 112 | stereo_balance = gr.Slider(minimum=-1.0, maximum=1.0, step=0.05, label='Balance between eyes', value=0.0)
|
111 | 113 | with gr.Group():
|
112 | 114 | with gr.Row():
|
@@ -486,7 +488,7 @@ def run_depthmap(processed, outpath, inputimages, inputnames, compute_device, mo
|
486 | 488 | outimages.append(stereotb_img)
|
487 | 489 | if gen_anaglyph:
|
488 | 490 | print("Generating Anaglyph image..")
|
489 |
| - anaglyph_img = overlap(left_image, right_image) |
| 491 | + anaglyph_img = overlap_red_cyan(left_image, right_image) |
490 | 492 | outimages.append(anaglyph_img)
|
491 | 493 | if (processed is not None):
|
492 | 494 | if gen_stereo:
|
@@ -853,224 +855,6 @@ def run_makevideo(fn_mesh, vid_numframes, vid_fps, vid_traj, vid_shift, vid_bord
|
853 | 855 | return fn_saved[-1], fn_saved[-1], ''
|
854 | 856 |
|
855 | 857 |
|
856 |
| -def apply_stereo_divergence(original_image, depth, divergence, fill_technique): |
857 |
| - depth_min = depth.min() |
858 |
| - depth_max = depth.max() |
859 |
| - depth = (depth - depth_min) / (depth_max - depth_min) |
860 |
| - divergence_px = (divergence / 100.0) * original_image.shape[1] |
861 |
| - |
862 |
| - if fill_technique in [0, 1, 2]: |
863 |
| - return apply_stereo_divergence_naive(original_image, depth, divergence_px, fill_technique) |
864 |
| - if fill_technique in [3, 4]: |
865 |
| - return apply_stereo_divergence_polylines(original_image, depth, divergence_px, fill_technique) |
866 |
| - |
867 |
| -@njit |
868 |
| -def apply_stereo_divergence_naive(original_image, normalized_depth, divergence_px: float, fill_technique): |
869 |
| - h, w, c = original_image.shape |
870 |
| - |
871 |
| - derived_image = np.zeros_like(original_image) |
872 |
| - filled = np.zeros(h * w, dtype=np.uint8) |
873 |
| - |
874 |
| - for row in prange(h): |
875 |
| - # Swipe order should ensure that pixels that are closer overwrite |
876 |
| - # (at their destination) pixels that are less close |
877 |
| - for col in range(w) if divergence_px < 0 else range(w - 1, -1, -1): |
878 |
| - col_d = col + int((1 - normalized_depth[row][col] ** 2) * divergence_px) |
879 |
| - if 0 <= col_d < w: |
880 |
| - derived_image[row][col_d] = original_image[row][col] |
881 |
| - filled[row * w + col_d] = 1 |
882 |
| - |
883 |
| - # Fill the gaps |
884 |
| - if fill_technique == 2: # naive_interpolating |
885 |
| - for row in range(h): |
886 |
| - for l_pointer in range(w): |
887 |
| - # This if (and the next if) performs two checks that are almost the same - for performance reasons |
888 |
| - if sum(derived_image[row][l_pointer]) != 0 or filled[row * w + l_pointer]: |
889 |
| - continue |
890 |
| - l_border = derived_image[row][l_pointer - 1] if l_pointer > 0 else np.zeros(3, dtype=np.uint8) |
891 |
| - r_border = np.zeros(3, dtype=np.uint8) |
892 |
| - r_pointer = l_pointer + 1 |
893 |
| - while r_pointer < w: |
894 |
| - if sum(derived_image[row][r_pointer]) != 0 and filled[row * w + r_pointer]: |
895 |
| - r_border = derived_image[row][r_pointer] |
896 |
| - break |
897 |
| - r_pointer += 1 |
898 |
| - if sum(l_border) == 0: |
899 |
| - l_border = r_border |
900 |
| - elif sum(r_border) == 0: |
901 |
| - r_border = l_border |
902 |
| - # Example illustrating positions of pointers at this point in code: |
903 |
| - # is filled? : + - - - - + |
904 |
| - # pointers : l r |
905 |
| - # interpolated: 0 1 2 3 4 5 |
906 |
| - # In total: 5 steps between two filled pixels |
907 |
| - total_steps = 1 + r_pointer - l_pointer |
908 |
| - step = (r_border.astype(np.float_) - l_border) / total_steps |
909 |
| - for col in range(l_pointer, r_pointer): |
910 |
| - derived_image[row][col] = l_border + (step * (col - l_pointer + 1)).astype(np.uint8) |
911 |
| - return derived_image |
912 |
| - elif fill_technique == 1: # naive |
913 |
| - derived_fix = np.copy(derived_image) |
914 |
| - for pos in np.where(filled == 0)[0]: |
915 |
| - row = pos // w |
916 |
| - col = pos % w |
917 |
| - row_times_w = row * w |
918 |
| - for offset in range(1, abs(int(divergence_px)) + 2): |
919 |
| - r_offset = col + offset |
920 |
| - l_offset = col - offset |
921 |
| - if r_offset < w and filled[row_times_w + r_offset]: |
922 |
| - derived_fix[row][col] = derived_image[row][r_offset] |
923 |
| - break |
924 |
| - if 0 <= l_offset and filled[row_times_w + l_offset]: |
925 |
| - derived_fix[row][col] = derived_image[row][l_offset] |
926 |
| - break |
927 |
| - return derived_fix |
928 |
| - else: # none |
929 |
| - return derived_image |
930 |
| - |
931 |
| -@njit(parallel=True) # fastmath=True does not reasonably improve performance |
932 |
| -def apply_stereo_divergence_polylines(original_image, normalized_depth, divergence_px: float, fill_technique): |
933 |
| - # This code treats rows of the image as polylines |
934 |
| - # It generates polylines, morphs them (applies divergence) to them, and then rasterizes them |
935 |
| - EPSILON = 1e-7 |
936 |
| - PIXEL_HALF_WIDTH = 0.45 if fill_technique == 4 else 0.0 |
937 |
| - # PERF_COUNTERS = [0, 0, 0] |
938 |
| - |
939 |
| - h, w, c = original_image.shape |
940 |
| - derived_image = np.zeros_like(original_image) |
941 |
| - for row in prange(h): |
942 |
| - # generating the vertices of the morphed polyline |
943 |
| - # format: new coordinate of the vertex, divergence (closeness), column of pixel that contains the point's color |
944 |
| - pt = np.zeros((5 + 2 * w, 3), dtype=np.float_) |
945 |
| - pt_end: int = 0 |
946 |
| - pt[pt_end] = [-3.0 * abs(divergence_px), 0.0, 0.0] |
947 |
| - pt_end += 1 |
948 |
| - for col in range(0, w): |
949 |
| - coord_d = (1 - normalized_depth[row][col] ** 2) * divergence_px |
950 |
| - coord_x = col + 0.5 + coord_d |
951 |
| - if PIXEL_HALF_WIDTH < EPSILON: |
952 |
| - pt[pt_end] = [coord_x, abs(coord_d), col] |
953 |
| - pt_end += 1 |
954 |
| - else: |
955 |
| - pt[pt_end] = [coord_x - PIXEL_HALF_WIDTH, abs(coord_d), col] |
956 |
| - pt[pt_end + 1] = [coord_x + PIXEL_HALF_WIDTH, abs(coord_d), col] |
957 |
| - pt_end += 2 |
958 |
| - pt[pt_end] = [w + 3.0 * abs(divergence_px), 0.0, w - 1] |
959 |
| - pt_end += 1 |
960 |
| - |
961 |
| - # generating the segments of the morphed polyline |
962 |
| - # format: coord_x, coord_d, color_i of the first point, then the same for the second point |
963 |
| - sg_end: int = pt_end - 1 |
964 |
| - sg = np.zeros((sg_end, 6), dtype=np.float_) |
965 |
| - for i in range(sg_end): |
966 |
| - sg[i] += np.concatenate((pt[i], pt[i + 1])) |
967 |
| - # Here is an informal proof that this (morphed) polyline does not self-intersect: |
968 |
| - # Draw a plot with two axes: coord_x and coord_d. Now draw the original line - it will be positioned at the |
969 |
| - # bottom of the graph (that is, for every point coord_d == 0). Now draw the morphed line using the vertices of |
970 |
| - # the original polyline. Observe that for each vertex in the new polyline, its increments |
971 |
| - # (from the corresponding vertex in the old polyline) over coord_x and coord_d are in direct proportion. |
972 |
| - # In fact, this proportion is equal for all the vertices and it is equal either -1 or +1, |
973 |
| - # depending on the sign of divergence_px. Now draw the lines from each old vertex to a corresponding new vertex. |
974 |
| - # Since the proportions are equal, these lines have the same angle with an axe and are parallel. |
975 |
| - # So, these lines do not intersect. Now rotate the plot by 45 or -45 degrees and observe that |
976 |
| - # each dot of the polyline is further right from the last dot, |
977 |
| - # which makes it impossible for the polyline to self-interset. QED. |
978 |
| - |
979 |
| - # sort segments and points using insertion sort |
980 |
| - # has a very good performance in practice, since these are almost sorted to begin with |
981 |
| - for i in range(1, sg_end): |
982 |
| - u = i - 1 |
983 |
| - while pt[u][0] > pt[u + 1][0] and 0 <= u: |
984 |
| - pt[u], pt[u + 1] = np.copy(pt[u + 1]), np.copy(pt[u]) |
985 |
| - sg[u], sg[u + 1] = np.copy(sg[u + 1]), np.copy(sg[u]) |
986 |
| - u -= 1 |
987 |
| - |
988 |
| - # rasterizing |
989 |
| - # at each point in time we keep track of segments that are "active" (or "current") |
990 |
| - csg = np.zeros((5 * int(abs(divergence_px)) + 25, 6), dtype=np.float_) |
991 |
| - csg_end: int = 0 |
992 |
| - sg_pointer: int = 0 |
993 |
| - # and index of the point that should be processed next |
994 |
| - pt_i: int = 0 |
995 |
| - for col in range(w): # iterate over regions (that will be rasterizeed into pixels) |
996 |
| - color = np.full(c, 0.5, dtype=np.float_) # we start with 0.5 because of how floats are converted to ints |
997 |
| - while pt[pt_i][0] < col: |
998 |
| - pt_i += 1 |
999 |
| - pt_i -= 1 # pt_i now points to the dot before the region start |
1000 |
| - # Finding segment' parts that contribute color to the region |
1001 |
| - while pt[pt_i][0] < col + 1: |
1002 |
| - coord_from = max(col, pt[pt_i][0]) + EPSILON |
1003 |
| - coord_to = min(col + 1, pt[pt_i + 1][0]) - EPSILON |
1004 |
| - significance = coord_to - coord_from |
1005 |
| - # the color at center point is the same as the average of color of segment part |
1006 |
| - coord_center = coord_from + 0.5 * significance |
1007 |
| - |
1008 |
| - # adding semgents that now may contribute |
1009 |
| - while sg_pointer < sg_end and sg[sg_pointer][0] < coord_center: |
1010 |
| - csg[csg_end] = sg[sg_pointer] |
1011 |
| - sg_pointer += 1 |
1012 |
| - csg_end += 1 |
1013 |
| - # removing segments that will no longer contribute |
1014 |
| - csg_i = 0 |
1015 |
| - while csg_i < csg_end: |
1016 |
| - if csg[csg_i][3] < coord_center: |
1017 |
| - csg[csg_i] = csg[csg_end - 1] |
1018 |
| - csg_end -= 1 |
1019 |
| - else: |
1020 |
| - csg_i += 1 |
1021 |
| - # finding the closest segment (segment with most divergence) |
1022 |
| - # note that this segment will be the closest from coord_from right up to coord_to, since there |
1023 |
| - # no new segments "appearing" inbetween these two and _the polyline does not self-intersect_ |
1024 |
| - best_csg_i: int = 0 |
1025 |
| - # PERF_COUNTERS[0] += 1 |
1026 |
| - if csg_end != 1: |
1027 |
| - # PERF_COUNTERS[1] += 1 |
1028 |
| - best_csg_closeness: float = -EPSILON |
1029 |
| - for csg_i in range(csg_end): |
1030 |
| - ip_k = (coord_center - csg[csg_i][0]) / (csg[csg_i][3] - csg[csg_i][0]) |
1031 |
| - # assert 0.0 <= ip_k <= 1.0 |
1032 |
| - closeness = (1.0 - ip_k) * csg[csg_i][1] + ip_k * csg[csg_i][4] |
1033 |
| - if best_csg_closeness < closeness and 0.0 < ip_k < 1.0: |
1034 |
| - best_csg_closeness = closeness |
1035 |
| - best_csg_i = csg_i |
1036 |
| - # getting the color |
1037 |
| - col_l: int = int(csg[best_csg_i][2] + EPSILON) |
1038 |
| - col_r: int = int(csg[best_csg_i][5] + EPSILON) |
1039 |
| - if col_l == col_r: |
1040 |
| - color += original_image[row][col_l] * significance |
1041 |
| - else: |
1042 |
| - # PERF_COUNTERS[2] += 1 |
1043 |
| - ip_k = (coord_center - csg[best_csg_i][0]) / (csg[best_csg_i][3] - csg[best_csg_i][0]) |
1044 |
| - color += (original_image[row][col_l] * (1.0 - ip_k) + original_image[row][col_r] * ip_k) \ |
1045 |
| - * significance |
1046 |
| - pt_i += 1 |
1047 |
| - derived_image[row][col] = np.asarray(color, dtype=np.uint8) |
1048 |
| - # print(PERF_COUNTERS) |
1049 |
| - return derived_image |
1050 |
| - |
1051 |
| -@njit(parallel=True) |
1052 |
| -def overlap(im1, im2): |
1053 |
| - width1 = im1.shape[1] |
1054 |
| - height1 = im1.shape[0] |
1055 |
| - width2 = im2.shape[1] |
1056 |
| - height2 = im2.shape[0] |
1057 |
| - |
1058 |
| - # final image |
1059 |
| - composite = np.zeros((height2, width2, 3), np.uint8) |
1060 |
| - |
1061 |
| - # iterate through "left" image, filling in red values of final image |
1062 |
| - for i in prange(height1): |
1063 |
| - for j in range(width1): |
1064 |
| - composite[i, j, 0] = im1[i, j, 0] |
1065 |
| - |
1066 |
| - # iterate through "right" image, filling in blue/green values of final image |
1067 |
| - for i in prange(height2): |
1068 |
| - for j in range(width2): |
1069 |
| - composite[i, j, 1] = im2[i, j, 1] |
1070 |
| - composite[i, j, 2] = im2[i, j, 2] |
1071 |
| - |
1072 |
| - return composite |
1073 |
| - |
1074 | 858 | # called from depth tab
|
1075 | 859 | def run_generate(depthmap_mode,
|
1076 | 860 | depthmap_image,
|
@@ -1216,7 +1000,7 @@ def on_ui_tabs():
|
1216 | 1000 | with gr.Row():
|
1217 | 1001 | stereo_divergence = gr.Slider(minimum=0.05, maximum=10.005, step=0.01, label='Divergence (3D effect)', value=2.5)
|
1218 | 1002 | with gr.Row():
|
1219 |
| - stereo_fill = gr.Dropdown(label="Gap fill technique", choices=['none', 'naive', 'naive_interpolating', 'polylines_soft', 'polylines_sharp'], value='polylines_sharp', type="index", elem_id="stereo_fill_type") |
| 1003 | + stereo_fill = gr.Dropdown(label="Gap fill technique", choices=['none', 'naive', 'naive_interpolating', 'polylines_soft', 'polylines_sharp'], value='polylines_sharp', type="value", elem_id="stereo_fill_type") |
1220 | 1004 | stereo_balance = gr.Slider(minimum=-1.0, maximum=1.0, step=0.05, label='Balance between eyes', value=0.0)
|
1221 | 1005 | with gr.Group():
|
1222 | 1006 | with gr.Row():
|
|
0 commit comments