Spaces:
Runtime error
Runtime error
Commit
·
d8d9de5
1
Parent(s):
101f749
Create app.py
Browse files
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()
|