Symbiomatrix commited on
Commit
d8d9de5
·
1 Parent(s): 101f749

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -0
app.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GPT3 code. Nonsensical on the sketch.
2
+
3
+ import cv2
4
+ import numpy as np
5
+ import gradio as gr
6
+ import colorsys
7
+
8
+ POLYFACTOR = 1.5 # Small lines are detected as shapes.
9
+ CCOLOUR = 0 # Should be a form controlled thing.
10
+ COLREG = None # Computer colour regions. Array. Extended whenever a new colour is requested.
11
+ IDIM = 256
12
+ CBLACK = 255
13
+ VARIANT = 0 # Ensures that the sketch canvas is actually refreshed.
14
+
15
+ # BREAKTHROUGH:
16
+ # Sketch can be overridden via controlnet method of creation, an np array with type,
17
+ # when varying the shape a bit.
18
+
19
+ def generate_unique_colors(n):
20
+ """Generate n visually distinct colors as a list of RGB tuples.
21
+
22
+ Uses the hue of hsv, with balanced saturation & value.
23
+ """
24
+ hsv_colors = [(x*1.0/n, 0.5, 0.5) for x in range(n)]
25
+ rgb_colors = [tuple(int(i * CBLACK) for i in colorsys.hsv_to_rgb(*hsv)) for hsv in hsv_colors]
26
+ return rgb_colors
27
+
28
+ def deterministic_colours(n, lcol = None):
29
+ """Generate n visually distinct & consistent colours as a list of RGB tuples.
30
+
31
+ Uses the hue of hsv, with balanced saturation & value.
32
+ Goes around the cyclical 0-256 and picks each /2 value for every round.
33
+ Continuation rules: If pcyv != ccyv in next round, then we don't care.
34
+ If pcyv == ccyv, we want to get the cval + delta of last elem.
35
+ If lcol > n, will return it as is.
36
+ """
37
+ if n <= 0:
38
+ return None
39
+ pcyc = -1
40
+ cval = 0
41
+ if lcol is None:
42
+ st = 0
43
+ elif n <= len(lcol):
44
+ # return lcol[:n] # Truncating the list is accurate, but pointless.
45
+ return lcol
46
+ else:
47
+ st = len(lcol)
48
+ if st > 0:
49
+ pcyc = np.ceil(np.log2(st))
50
+ # This is erroneous on st=2^n, but we don't care.
51
+ dlt = 1 / (2 ** pcyc)
52
+ cval = dlt + 2 * dlt * (st % (2 ** (pcyc - 1)) - 1)
53
+
54
+ lhsv = []
55
+ for i in range(st,n):
56
+ ccyc = np.ceil(np.log2(i + 1))
57
+ if ccyc == 0: # First col = 0.
58
+ cval = 0
59
+ pcyc = ccyc
60
+ elif pcyc != ccyc: # New cycle, start from the half point between 0 and first point.
61
+ dlt = 1 / (2 ** ccyc)
62
+ cval = dlt
63
+ pcyc = ccyc
64
+ else:
65
+ cval = cval + 2 * dlt # Jumps over existing vals.
66
+ lhsv.append(cval)
67
+ lhsv = [(v, 0.5, 0.5) for v in lhsv] # Hsv conversion only works 0:1.
68
+ lrgb = [colorsys.hsv_to_rgb(*hsv) for hsv in lhsv]
69
+ lrgb = (np.array(lrgb) * (CBLACK + 1)).astype(np.uint8) # Convert to colour uints.
70
+ lrgb = lrgb.reshape(-1, 3)
71
+ if lcol is not None:
72
+ lrgb = np.concatenate([lcol, lrgb])
73
+ return lrgb
74
+
75
+ def detect_polygons(img,num):
76
+ global CCOLOUR
77
+ global COLREG
78
+ global VARIANT
79
+
80
+ # I dunno why, but mask has a 4th colour channel, which contains nothing. Alpha?
81
+ if VARIANT != 0:
82
+ out = img["image"][:-VARIANT,:-VARIANT,:3]
83
+ img = img["mask"][:-VARIANT,:-VARIANT,:3]
84
+ else:
85
+ out = img["image"][:,:,:3]
86
+ img = img["mask"][:,:,:3]
87
+
88
+ # Convert the binary image to grayscale
89
+ if img is None:
90
+ img = np.zeros([IDIM,IDIM,3],dtype = np.uint8) + CBLACK # Stupid cv.
91
+ if out is None:
92
+ out = np.zeros_like(img) + CBLACK # Stupid cv.
93
+ bimg = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
94
+
95
+ # Find contours in the image
96
+ # Must reverse colours, otherwise draws an outer box (0->255). Dunno why gradio uses 255 for white anyway.
97
+ contours, hierarchy = cv2.findContours(bimg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
98
+
99
+ #img2 = np.zeros_like(img) + 255 # Fresh image.
100
+ img2 = out # Update current image.
101
+ # color = np.random.randint(0,255,3)
102
+ # color = deterministic_colours(CCOLOUR + 1)[-1]
103
+ # CCOLOUR = CCOLOUR +1
104
+
105
+ COLREG = deterministic_colours(int(num) + 1, COLREG)
106
+ color = COLREG[int(num),:]
107
+ # Loop through each contour and detect polygons
108
+ for cnt in contours:
109
+ # Approximate the contour to a polygon
110
+ approx = cv2.approxPolyDP(cnt, 0.0001 * cv2.arcLength(cnt, True), True)
111
+
112
+ # If the polygon has 3 or more sides and is fully enclosed, fill it with a random color
113
+ # if len(approx) >= 3: # BAD test.
114
+ if cv2.contourArea(cnt) > cv2.arcLength(cnt, True) * POLYFACTOR: # Better, still messes up on large brush.
115
+ #SBM BUGGY, prevents contours from . cv2.pointPolygonTest(approx, (approx[0][0][0], approx[0][0][1]), False) >= 0:
116
+ # Check if the polygon has already been filled
117
+ # if i not in filled_polygons: # USELESS
118
+
119
+ # Draw the polygon on the image with a new random color
120
+ color = [int(v) for v in color] # Opencv is dumb / C based and can't handle an int64 array.
121
+ #cv2.drawContours(img2, [approx], 0, color = color) # Only outer sketch.
122
+ cv2.fillPoly(img2,[approx],color = color)
123
+
124
+
125
+ # Add the polygon to the set of filled polygons
126
+ # filled_polygons.add(i)
127
+
128
+ # Convert the grayscale image back to RGB
129
+ #img2 = cv2.cvtColor(img2, cv2.COLOR_GRAY2RGB) # Converting to grayscale is dumb.
130
+
131
+ skimg = create_canvas(img2.shape[0], img2.shape[1])
132
+ if VARIANT != 0:
133
+ skimg[:-VARIANT,:-VARIANT,:] = img2
134
+ else:
135
+ skimg[:,:,:] = img2
136
+
137
+ return skimg, num + 1 if num + 1 <= CBLACK else num
138
+
139
+ def detect_mask(img,num):
140
+ color = deterministic_colours(int(num) + 1)[-1]
141
+ color = color.reshape([1,1,3])
142
+ mask = ((img["image"] == color).all(-1)) * CBLACK
143
+ return mask
144
+
145
+ def create_canvas(h, w):
146
+ """New canvas area.
147
+
148
+ Small variant value is added (and ignored later) due to gradio refresh bug.
149
+ """
150
+ global VARIANT
151
+ VARIANT = 1 - VARIANT
152
+ vret = np.zeros(shape=(h + VARIANT, w + VARIANT, 3), dtype=np.uint8) + CBLACK
153
+ return vret
154
+
155
+ # Define the Gradio interface
156
+
157
+ # Create the Gradio interface and link it to the polygon detection function
158
+ # gr.Interface(detect_polygons, inputs=[sketch,output], outputs=output, title="Polygon Detection",
159
+ # description="Detect and fill closed shapes with different random colors.").launch()
160
+
161
+ with gr.Blocks() as demo:
162
+ with gr.Row():
163
+ with gr.Column():
164
+ # Gradio shape is dumb.
165
+ # sketch = gr.Image(shape=(IDIM, IDIM),source = "canvas", tool = "color-sketch")#,brush_radius = 1) # No brush radius in 16.2.
166
+ sketch = gr.Image(source = "upload", mirror_webcam = False, type = "numpy", tool = "sketch")
167
+ # sketch = gr.Image(shape=(256, 256),source = "upload", tool = "color-sketch")
168
+ #num = gr.Number(value = 0)
169
+ num = gr.Slider(label="Region", minimum=0, maximum=CBLACK, step=1, value=0)
170
+ btn = gr.Button(value = "Draw region")
171
+ btn2 = gr.Button(value = "Display mask")
172
+ canvas_width = gr.Slider(label="Canvas Width", minimum=64, maximum=2048, value=512, step=8)
173
+ canvas_height = gr.Slider(label="Canvas Height", minimum=64, maximum=2048, value=512, step=8)
174
+ cbtn = gr.Button(value="Create mask area")
175
+ with gr.Column():
176
+ # Cannot update sketch in 16.2, must add to different image.
177
+ # output = gr.Image(shape=(IDIM, IDIM), source = "upload")
178
+ # output = gr.Image(source = "upload")
179
+ output2 = gr.Image(shape=(IDIM, IDIM))
180
+
181
+ btn.click(detect_polygons, inputs = [sketch,num], outputs = [sketch,num])
182
+ btn2.click(detect_mask, inputs = [sketch,num], outputs = [output2])
183
+ cbtn.click(fn=create_canvas, inputs=[canvas_height, canvas_width], outputs=[sketch])
184
+
185
+ if __name__ == "__main__":
186
+ demo.launch()