Spaces:
Runtime error
Runtime error
init app
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- InstanceNorm.py +146 -0
- ai.py +203 -0
- app.py +304 -0
- config.py +2 -0
- decompositioner.py +185 -0
- generate_bash.py +35 -0
- linefiller/__init__.py +0 -0
- linefiller/thinning.py +44 -0
- linefiller/third_party.py +396 -0
- linefiller/trappedball_fill.py +436 -0
- models.py +299 -0
- nets/head.net +3 -0
- nets/inception.net +3 -0
- nets/mat.net +3 -0
- nets/neck.net +3 -0
- nets/norm.net +3 -0
- nets/reader.net +3 -0
- nets/render_head.net +3 -0
- nets/render_neck.net +3 -0
- nets/tail.net +3 -0
- nets/vector.net +3 -0
- nets/vgg7.net +3 -0
- refs/1.png +3 -0
- refs/10.png +3 -0
- refs/11.png +3 -0
- refs/12.png +3 -0
- refs/13.png +3 -0
- refs/14.png +3 -0
- refs/15.png +3 -0
- refs/16.png +3 -0
- refs/17.png +3 -0
- refs/18.png +3 -0
- refs/19.png +3 -0
- refs/2.png +3 -0
- refs/20.png +3 -0
- refs/21.png +3 -0
- refs/22.png +3 -0
- refs/23.png +3 -0
- refs/24.png +3 -0
- refs/25.png +3 -0
- refs/26.png +3 -0
- refs/27.png +3 -0
- refs/28.png +3 -0
- refs/29.png +3 -0
- refs/3.png +3 -0
- refs/30.png +3 -0
- refs/31.png +3 -0
- refs/32.png +3 -0
- refs/4.png +3 -0
- refs/5.png +3 -0
InstanceNorm.py
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from keras.engine import Layer, InputSpec
|
2 |
+
from keras import initializers, regularizers, constraints
|
3 |
+
from keras import backend as K
|
4 |
+
from keras.utils.generic_utils import get_custom_objects
|
5 |
+
|
6 |
+
import tensorflow as tf
|
7 |
+
|
8 |
+
|
9 |
+
class InstanceNormalization(Layer):
|
10 |
+
"""Instance normalization layer (Lei Ba et al, 2016, Ulyanov et al., 2016).
|
11 |
+
Normalize the activations of the previous layer at each step,
|
12 |
+
i.e. applies a transformation that maintains the mean activation
|
13 |
+
close to 0 and the activation standard deviation close to 1.
|
14 |
+
# Arguments
|
15 |
+
axis: Integer, the axis that should be normalized
|
16 |
+
(typically the features axis).
|
17 |
+
For instance, after a `Conv2D` layer with
|
18 |
+
`data_format="channels_first"`,
|
19 |
+
set `axis=1` in `InstanceNormalization`.
|
20 |
+
Setting `axis=None` will normalize all values in each instance of the batch.
|
21 |
+
Axis 0 is the batch dimension. `axis` cannot be set to 0 to avoid errors.
|
22 |
+
epsilon: Small float added to variance to avoid dividing by zero.
|
23 |
+
center: If True, add offset of `beta` to normalized tensor.
|
24 |
+
If False, `beta` is ignored.
|
25 |
+
scale: If True, multiply by `gamma`.
|
26 |
+
If False, `gamma` is not used.
|
27 |
+
When the next layer is linear (also e.g. `nn.relu`),
|
28 |
+
this can be disabled since the scaling
|
29 |
+
will be done by the next layer.
|
30 |
+
beta_initializer: Initializer for the beta weight.
|
31 |
+
gamma_initializer: Initializer for the gamma weight.
|
32 |
+
beta_regularizer: Optional regularizer for the beta weight.
|
33 |
+
gamma_regularizer: Optional regularizer for the gamma weight.
|
34 |
+
beta_constraint: Optional constraint for the beta weight.
|
35 |
+
gamma_constraint: Optional constraint for the gamma weight.
|
36 |
+
# Input shape
|
37 |
+
Arbitrary. Use the keyword argument `input_shape`
|
38 |
+
(tuple of integers, does not include the samples axis)
|
39 |
+
when using this layer as the first layer in a model.
|
40 |
+
# Output shape
|
41 |
+
Same shape as input.
|
42 |
+
# References
|
43 |
+
- [Layer Normalization](https://arxiv.org/abs/1607.06450)
|
44 |
+
- [Instance Normalization: The Missing Ingredient for Fast Stylization](https://arxiv.org/abs/1607.08022)
|
45 |
+
"""
|
46 |
+
def __init__(self,
|
47 |
+
axis=None,
|
48 |
+
epsilon=1e-3,
|
49 |
+
center=True,
|
50 |
+
scale=True,
|
51 |
+
beta_initializer='zeros',
|
52 |
+
gamma_initializer='ones',
|
53 |
+
beta_regularizer=None,
|
54 |
+
gamma_regularizer=None,
|
55 |
+
beta_constraint=None,
|
56 |
+
gamma_constraint=None,
|
57 |
+
**kwargs):
|
58 |
+
super(InstanceNormalization, self).__init__(**kwargs)
|
59 |
+
self.supports_masking = True
|
60 |
+
self.axis = axis
|
61 |
+
self.epsilon = epsilon
|
62 |
+
self.center = center
|
63 |
+
self.scale = scale
|
64 |
+
self.beta_initializer = initializers.get(beta_initializer)
|
65 |
+
self.gamma_initializer = initializers.get(gamma_initializer)
|
66 |
+
self.beta_regularizer = regularizers.get(beta_regularizer)
|
67 |
+
self.gamma_regularizer = regularizers.get(gamma_regularizer)
|
68 |
+
self.beta_constraint = constraints.get(beta_constraint)
|
69 |
+
self.gamma_constraint = constraints.get(gamma_constraint)
|
70 |
+
|
71 |
+
def build(self, input_shape):
|
72 |
+
ndim = len(input_shape)
|
73 |
+
if self.axis == 0:
|
74 |
+
raise ValueError('Axis cannot be zero')
|
75 |
+
|
76 |
+
if (self.axis is not None) and (ndim == 2):
|
77 |
+
raise ValueError('Cannot specify axis for rank 1 tensor')
|
78 |
+
|
79 |
+
self.input_spec = InputSpec(ndim=ndim)
|
80 |
+
|
81 |
+
if self.axis is None:
|
82 |
+
shape = (1,)
|
83 |
+
else:
|
84 |
+
shape = (input_shape[self.axis],)
|
85 |
+
|
86 |
+
if self.scale:
|
87 |
+
self.gamma = self.add_weight(shape=shape,
|
88 |
+
name='gamma',
|
89 |
+
initializer=self.gamma_initializer,
|
90 |
+
regularizer=self.gamma_regularizer,
|
91 |
+
constraint=self.gamma_constraint)
|
92 |
+
else:
|
93 |
+
self.gamma = None
|
94 |
+
if self.center:
|
95 |
+
self.beta = self.add_weight(shape=shape,
|
96 |
+
name='beta',
|
97 |
+
initializer=self.beta_initializer,
|
98 |
+
regularizer=self.beta_regularizer,
|
99 |
+
constraint=self.beta_constraint)
|
100 |
+
else:
|
101 |
+
self.beta = None
|
102 |
+
self.built = True
|
103 |
+
|
104 |
+
def call(self, inputs, training=None):
|
105 |
+
input_shape = K.int_shape(inputs)
|
106 |
+
reduction_axes = list(range(0, len(input_shape)))
|
107 |
+
|
108 |
+
if (self.axis is not None):
|
109 |
+
del reduction_axes[self.axis]
|
110 |
+
|
111 |
+
del reduction_axes[0]
|
112 |
+
|
113 |
+
mean, var = tf.nn.moments(inputs, reduction_axes, keep_dims=True)
|
114 |
+
stddev = tf.sqrt(var) + self.epsilon
|
115 |
+
normed = (inputs - mean) / stddev
|
116 |
+
|
117 |
+
broadcast_shape = [1] * len(input_shape)
|
118 |
+
if self.axis is not None:
|
119 |
+
broadcast_shape[self.axis] = input_shape[self.axis]
|
120 |
+
|
121 |
+
if self.scale:
|
122 |
+
broadcast_gamma = K.reshape(self.gamma, broadcast_shape)
|
123 |
+
normed = normed * broadcast_gamma
|
124 |
+
if self.center:
|
125 |
+
broadcast_beta = K.reshape(self.beta, broadcast_shape)
|
126 |
+
normed = normed + broadcast_beta
|
127 |
+
return normed
|
128 |
+
|
129 |
+
def get_config(self):
|
130 |
+
config = {
|
131 |
+
'axis': self.axis,
|
132 |
+
'epsilon': self.epsilon,
|
133 |
+
'center': self.center,
|
134 |
+
'scale': self.scale,
|
135 |
+
'beta_initializer': initializers.serialize(self.beta_initializer),
|
136 |
+
'gamma_initializer': initializers.serialize(self.gamma_initializer),
|
137 |
+
'beta_regularizer': regularizers.serialize(self.beta_regularizer),
|
138 |
+
'gamma_regularizer': regularizers.serialize(self.gamma_regularizer),
|
139 |
+
'beta_constraint': constraints.serialize(self.beta_constraint),
|
140 |
+
'gamma_constraint': constraints.serialize(self.gamma_constraint)
|
141 |
+
}
|
142 |
+
base_config = super(InstanceNormalization, self).get_config()
|
143 |
+
return dict(list(base_config.items()) + list(config.items()))
|
144 |
+
|
145 |
+
|
146 |
+
get_custom_objects().update({'InstanceNormalization': InstanceNormalization})
|
ai.py
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import tensorflow
|
2 |
+
|
3 |
+
tensorflow.compat.v1.disable_v2_behavior()
|
4 |
+
tf = tensorflow.compat.v1
|
5 |
+
|
6 |
+
import keras
|
7 |
+
import numpy as np
|
8 |
+
from config import *
|
9 |
+
from keras.models import load_model
|
10 |
+
from smoother import *
|
11 |
+
import keras.backend as K
|
12 |
+
from models import *
|
13 |
+
|
14 |
+
|
15 |
+
def ToGray(x):
|
16 |
+
R = x[:, :, :, 0:1]
|
17 |
+
G = x[:, :, :, 1:2]
|
18 |
+
B = x[:, :, :, 2:3]
|
19 |
+
return 0.30 * R + 0.59 * G + 0.11 * B
|
20 |
+
|
21 |
+
|
22 |
+
def RGB2YUV(x):
|
23 |
+
R = x[:, :, :, 0:1]
|
24 |
+
G = x[:, :, :, 1:2]
|
25 |
+
B = x[:, :, :, 2:3]
|
26 |
+
Y = 0.299 * R + 0.587 * G + 0.114 * B
|
27 |
+
U = 0.492 * (B - Y) + 128
|
28 |
+
V = 0.877 * (R - Y) + 128
|
29 |
+
return tf.concat([Y, U, V], axis=3)
|
30 |
+
|
31 |
+
|
32 |
+
def YUV2RGB(x):
|
33 |
+
Y = x[:, :, :, 0:1]
|
34 |
+
U = x[:, :, :, 1:2]
|
35 |
+
V = x[:, :, :, 2:3]
|
36 |
+
R = Y + 1.140 * (V - 128)
|
37 |
+
G = Y - 0.394 * (U - 128) - 0.581 * (V - 128)
|
38 |
+
B = Y + 2.032 * (U - 128)
|
39 |
+
return tf.concat([R, G, B], axis=3)
|
40 |
+
|
41 |
+
|
42 |
+
def VGG2RGB(x):
|
43 |
+
return (x + [103.939, 116.779, 123.68])[:, :, :, ::-1]
|
44 |
+
|
45 |
+
|
46 |
+
def blur(x):
|
47 |
+
return Smoother({'data': tf.pad(x, [[0, 0], [9, 9], [9, 9], [0, 0]], 'SYMMETRIC')}, 7, 2).get_output()[:, 9: -9, 9: -9, :]
|
48 |
+
|
49 |
+
|
50 |
+
def norm_feature(x, core):
|
51 |
+
cs0 = tf.shape(core)[1]
|
52 |
+
cs1 = tf.shape(core)[2]
|
53 |
+
small = tf.image.resize_area(x, (cs0, cs1))
|
54 |
+
avged = tf.nn.avg_pool(tf.pad(small, [[0, 0], [2, 2], [2, 2], [0, 0]], 'REFLECT'), [1, 5, 5, 1], [1, 1, 1, 1], 'VALID')
|
55 |
+
return tf.image.resize_bicubic(avged, tf.shape(x)[1:3])
|
56 |
+
|
57 |
+
|
58 |
+
def upsample(x):
|
59 |
+
return K.resize_images(x, 2, 2, 'channels_last')
|
60 |
+
|
61 |
+
|
62 |
+
def downsample(x):
|
63 |
+
return tf.nn.avg_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
|
64 |
+
|
65 |
+
|
66 |
+
def nts(x):
|
67 |
+
return (x + [103.939, 116.779, 123.68])[:, :, :, ::-1] / 255.0
|
68 |
+
|
69 |
+
|
70 |
+
session = keras.backend.get_session()
|
71 |
+
|
72 |
+
ip1 = tf.placeholder(dtype=tf.float32, shape=(None, None, None, 1))
|
73 |
+
ip3 = tf.placeholder(dtype=tf.float32, shape=(None, None, None, 3))
|
74 |
+
ip4 = tf.placeholder(dtype=tf.float32, shape=(None, None, None, 4))
|
75 |
+
|
76 |
+
print('1')
|
77 |
+
|
78 |
+
vector = make_diff_net()
|
79 |
+
vector_op = 255.0 - tf.nn.sigmoid(vector(ip3 / 255.0)) * 255.0
|
80 |
+
|
81 |
+
print('4')
|
82 |
+
|
83 |
+
reader = load_model('./nets/reader.net')
|
84 |
+
features = reader(ip3 / 255.0)
|
85 |
+
|
86 |
+
print('5')
|
87 |
+
|
88 |
+
head = load_model('./nets/head.net')
|
89 |
+
feed = [1 - ip1 / 255.0, (ip4[:, :, :, 0:3] / 127.5 - 1) * ip4[:, :, :, 3:4] / 255.0]
|
90 |
+
for _ in range(len(features)):
|
91 |
+
feed.append(keras.backend.mean(features[_], axis=[1, 2]))
|
92 |
+
nil0, nil1, head_temp = head(feed)
|
93 |
+
|
94 |
+
print('6')
|
95 |
+
|
96 |
+
neck = load_model('./nets/neck.net')
|
97 |
+
nil2, nil3, neck_temp = neck(feed)
|
98 |
+
feed[0] = tf.clip_by_value(1 - tf.image.resize_bilinear(ToGray(VGG2RGB(head_temp) / 255.0), tf.shape(ip1)[1:3]), 0.0, 1.0)
|
99 |
+
nil4, nil5, head_temp = neck(feed)
|
100 |
+
head_op = VGG2RGB(head_temp)
|
101 |
+
neck_op = VGG2RGB(neck_temp)
|
102 |
+
|
103 |
+
print('7')
|
104 |
+
|
105 |
+
inception = load_model('./nets/inception.net')
|
106 |
+
features_render = inception((ip3 + (downsample(ip1) - blur(downsample(ip1))) * 2.0) / 255.0)
|
107 |
+
precessed_feed = [(ip4[:, :, :, 0:3] / 127.5 - 1) * ip4[:, :, :, 3:4] / 255.0] + [
|
108 |
+
norm_feature(item, features_render[-1]) for item in features_render]
|
109 |
+
|
110 |
+
print('8')
|
111 |
+
|
112 |
+
render_head = load_model('./nets/render_head.net')
|
113 |
+
render_neck = load_model('./nets/render_neck.net')
|
114 |
+
nil6, nil7, render_A = render_head([1 - ip1 / 255.0] + precessed_feed)
|
115 |
+
nil8, nil9, render_B = render_neck(
|
116 |
+
[1 - tf.image.resize_bilinear(ToGray(nts(render_A)), tf.shape(ip1)[1:3])] + precessed_feed)
|
117 |
+
render_op = nts(render_B) * 255.0
|
118 |
+
|
119 |
+
print('9')
|
120 |
+
|
121 |
+
tail = load_model('./nets/tail.net')
|
122 |
+
pads = 7
|
123 |
+
tail_op = tail(tf.pad(ip3 / 255.0, [[0, 0], [pads, pads], [pads, pads], [0, 0]], 'REFLECT'))[:, pads * 2:-pads * 2, pads * 2:-pads * 2, :][:, 1:-1, 1:-1, :] * 255.0
|
124 |
+
|
125 |
+
print('10')
|
126 |
+
|
127 |
+
|
128 |
+
vgg7 = load_model('./nets/vgg7.net')
|
129 |
+
pads = 7
|
130 |
+
vgg7_op = vgg7(tf.pad(ip1 / 255.0, [[0, 0], [pads, pads], [pads, pads], [0, 0]], 'REFLECT'))[:, pads:-pads, pads:-pads, :] * 255.0
|
131 |
+
|
132 |
+
print('11')
|
133 |
+
|
134 |
+
|
135 |
+
mat = make_unet512()
|
136 |
+
mat_op = mat(ip3 / 255.0) * 255.0
|
137 |
+
|
138 |
+
print('11')
|
139 |
+
|
140 |
+
norm = load_model('./nets/norm.net')
|
141 |
+
norm_op = norm(ip1 / 255.0) * 255.0
|
142 |
+
|
143 |
+
print('12')
|
144 |
+
|
145 |
+
session.run(tf.global_variables_initializer())
|
146 |
+
|
147 |
+
print('begin load')
|
148 |
+
|
149 |
+
|
150 |
+
tail.load_weights('./nets/tail.net')
|
151 |
+
vgg7.load_weights('./nets/vgg7.net')
|
152 |
+
head.load_weights('./nets/head.net')
|
153 |
+
neck.load_weights('./nets/neck.net')
|
154 |
+
reader.load_weights('./nets/reader.net')
|
155 |
+
vector.load_weights('./nets/vector.net')
|
156 |
+
render_head.load_weights('./nets/render_head.net')
|
157 |
+
render_neck.load_weights('./nets/render_neck.net')
|
158 |
+
inception.load_weights('./nets/inception.net')
|
159 |
+
mat.load_weights('./nets/mat.net')
|
160 |
+
norm.load_weights('./nets/norm.net')
|
161 |
+
|
162 |
+
|
163 |
+
def go_head(sketch, global_hint, local_hint):
|
164 |
+
return session.run(head_op, feed_dict={
|
165 |
+
ip1: sketch[None, :, :, None], ip3: global_hint[None, :, :, :], ip4: local_hint[None, :, :, :]
|
166 |
+
})[0].clip(0, 255).astype(np.uint8)
|
167 |
+
|
168 |
+
|
169 |
+
def go_render(sketch, segmentation, points):
|
170 |
+
return session.run(render_op, feed_dict={
|
171 |
+
ip1: sketch[None, :, :, None], ip3: segmentation[None, :, :, :], ip4: points[None, :, :, :]
|
172 |
+
})[0].clip(0, 255).astype(np.uint8)
|
173 |
+
|
174 |
+
|
175 |
+
def go_tail(x):
|
176 |
+
return session.run(tail_op, feed_dict={
|
177 |
+
ip3: x[None, :, :, :]
|
178 |
+
})[0].clip(0, 255).astype(np.uint8)
|
179 |
+
|
180 |
+
|
181 |
+
def go_vgg7(x):
|
182 |
+
return session.run(vgg7_op, feed_dict={
|
183 |
+
ip1: x[None, :, :, None]
|
184 |
+
})[0, :, :, 0].clip(0, 255).astype(np.uint8)
|
185 |
+
|
186 |
+
|
187 |
+
def go_vector(x):
|
188 |
+
return session.run(vector_op, feed_dict={
|
189 |
+
ip3: x[None, :, :, :]
|
190 |
+
})[0].clip(0, 255).astype(np.uint8)
|
191 |
+
|
192 |
+
|
193 |
+
def go_mat(x):
|
194 |
+
return session.run(mat_op, feed_dict={
|
195 |
+
ip3: x[None, :, :, :]
|
196 |
+
})[0, :, :, 0].clip(0, 255).astype(np.uint8)
|
197 |
+
|
198 |
+
|
199 |
+
def go_norm(x):
|
200 |
+
return session.run(norm_op, feed_dict={
|
201 |
+
ip1: x[None, :, :, None]
|
202 |
+
})[0].clip(0, 255).astype(np.uint8)
|
203 |
+
|
app.py
ADDED
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import base64
|
3 |
+
import datetime
|
4 |
+
import sys
|
5 |
+
import pickle
|
6 |
+
import gzip
|
7 |
+
import json
|
8 |
+
import time
|
9 |
+
import gradio as gr
|
10 |
+
|
11 |
+
from ai import *
|
12 |
+
from tricks import *
|
13 |
+
from decompositioner import *
|
14 |
+
from rendering import *
|
15 |
+
|
16 |
+
|
17 |
+
import os
|
18 |
+
import glob
|
19 |
+
import shutil
|
20 |
+
|
21 |
+
splash = glob.glob('ui/web-mobile/splash*')[0]
|
22 |
+
os.remove(splash)
|
23 |
+
shutil.copy('res/splash.png', splash)
|
24 |
+
with open('ui/web-mobile/index.html', 'r', encoding='utf-8') as f:
|
25 |
+
page = f.read()
|
26 |
+
with open('ui/web-mobile/index.html', 'w', encoding='utf-8') as f:
|
27 |
+
f.write(page.replace('Cocos Creator | ', ''))
|
28 |
+
|
29 |
+
|
30 |
+
def cv2_encode(image: np.ndarray, name):
|
31 |
+
if image is None:
|
32 |
+
return 'null'
|
33 |
+
if '.jpg' in name:
|
34 |
+
_, data = cv2.imencode('.jpeg', image)
|
35 |
+
return 'data:image/jpeg;base64,' + base64.b64encode(data).decode('utf8')
|
36 |
+
else:
|
37 |
+
_, data = cv2.imencode('.png', image)
|
38 |
+
return 'data:image/png;base64,' + base64.b64encode(data).decode('utf8')
|
39 |
+
|
40 |
+
|
41 |
+
def get_request_image(request, name):
|
42 |
+
img = request.get(name)
|
43 |
+
img = re.sub('^data:image/.+;base64,', '', img)
|
44 |
+
img = base64.b64decode(img)
|
45 |
+
img = np.fromstring(img, dtype=np.uint8)
|
46 |
+
img = cv2.imdecode(img, -1)
|
47 |
+
return img
|
48 |
+
|
49 |
+
|
50 |
+
def npcache(history, room_id, name, data):
|
51 |
+
rooms = list(filter(lambda _room: _room["id"] == room_id, history))
|
52 |
+
if len(rooms) == 0:
|
53 |
+
room = { "id": room_id }
|
54 |
+
history.append(room)
|
55 |
+
else:
|
56 |
+
room = rooms[0]
|
57 |
+
room[name] = data
|
58 |
+
|
59 |
+
|
60 |
+
def npread(history, room_id, name):
|
61 |
+
rooms = list(filter(lambda _room: _room["id"] == room_id, history))
|
62 |
+
if len(rooms) == 0:
|
63 |
+
return None
|
64 |
+
else:
|
65 |
+
room = rooms[0]
|
66 |
+
return room.get(name, None)
|
67 |
+
|
68 |
+
|
69 |
+
def upload_sketch(json_str, history):
|
70 |
+
request = json.loads(json_str)
|
71 |
+
timenow = time.time()
|
72 |
+
ID = datetime.datetime.now().strftime('H%HM%MS%S')
|
73 |
+
room = datetime.datetime.now().strftime('%b%dH%HM%MS%S') + 'R' + str(np.random.randint(100, 999))
|
74 |
+
sketch = from_png_to_jpg(get_request_image(request, 'sketch'))
|
75 |
+
npcache(history, room, 'sketch.original.jpg', sketch)
|
76 |
+
sketch = go_tail(cli_norm(min_resize(sketch, 512)))
|
77 |
+
print('original_sketch saved')
|
78 |
+
s256 = go_vector(go_cal(mk_resize(sketch, 8)))[:, :, 0]
|
79 |
+
print('s256')
|
80 |
+
s512 = go_vector(go_cal(d_resize(sketch, s256.shape, 2.0)))[:, :, 0]
|
81 |
+
print('s512')
|
82 |
+
s1024 = go_vector(go_cal(d_resize(sketch, s256.shape, 4.0)))[:, :, 0]
|
83 |
+
print('s1024')
|
84 |
+
npcache(history, room, 'sketch.s1024.png', s1024)
|
85 |
+
npcache(history, room, 'sketch.s512.png', s512)
|
86 |
+
npcache(history, room, 'sketch.s256.png', s256)
|
87 |
+
print('edge processed')
|
88 |
+
fill = double_fill(s1024, s512, s256)
|
89 |
+
npcache(history, room, 'sketch.fill', fill)
|
90 |
+
print('filled')
|
91 |
+
npcache(history, room, 'sketch.colorization.png', np.min(sketch, axis=2))
|
92 |
+
print('sketch processed')
|
93 |
+
print(time.time() - timenow)
|
94 |
+
if len(history) > 5:
|
95 |
+
history = history[-5:]
|
96 |
+
return room + '_' + ID, history
|
97 |
+
|
98 |
+
|
99 |
+
def request_result(json_str, history):
|
100 |
+
request = json.loads(json_str)
|
101 |
+
timenow = time.time()
|
102 |
+
room = request.get("room")
|
103 |
+
if len(list(filter(lambda _room: _room["id"] == room, history))) == 0:
|
104 |
+
return None, history
|
105 |
+
skipper = str(request.get("skipper"))
|
106 |
+
light_r = float(request.get("r"))
|
107 |
+
light_g = float(request.get("g"))
|
108 |
+
light_b = float(request.get("b"))
|
109 |
+
light_h = float(request.get("h"))
|
110 |
+
light_max = max([light_r, light_g, light_b, light_h])
|
111 |
+
inv4 = int(request.get("inv4"))
|
112 |
+
print('inv4=' + str(inv4))
|
113 |
+
light_r = (light_r + 1e-5) / (light_max + 1e-5)
|
114 |
+
light_g = (light_g + 1e-5) / (light_max + 1e-5)
|
115 |
+
light_b = (light_b + 1e-5) / (light_max + 1e-5)
|
116 |
+
light_h *= 600.0
|
117 |
+
light_d = request.get("d")
|
118 |
+
need_render = int(request.get("need_render"))
|
119 |
+
print([light_r, light_g, light_b, light_d])
|
120 |
+
ID = datetime.datetime.now().strftime('H%HM%MS%S')
|
121 |
+
points = request.get("points")
|
122 |
+
points = json.loads(points)
|
123 |
+
npcache(history, room, 'points.' + ID + '.txt', points)
|
124 |
+
if len(points) > 500:
|
125 |
+
return None, history
|
126 |
+
for _ in range(len(points)):
|
127 |
+
points[_][1] = 1 - points[_][1]
|
128 |
+
fill = npread(history, room, 'sketch.fill')
|
129 |
+
s1024 = npread(history, room, 'sketch.s1024.png')
|
130 |
+
sketch = npread(history, room, 'sketch.colorization.png')
|
131 |
+
print(skipper)
|
132 |
+
if npread(history, room, 'albedo.' + skipper + '.png') is not None:
|
133 |
+
albedo = npread(history, room, 'albedo.' + skipper + '.png')
|
134 |
+
npcache(history, room, 'albedo.' + ID + '.png', albedo)
|
135 |
+
print('albedo readed')
|
136 |
+
else:
|
137 |
+
faceID = int(request.get("faceID")) - 65535
|
138 |
+
print(faceID)
|
139 |
+
if faceID > -1:
|
140 |
+
print('fake ref')
|
141 |
+
face = from_png_to_jpg(cv2.imread("refs/" + str(faceID + 1) + ".png", cv2.IMREAD_UNCHANGED))
|
142 |
+
else:
|
143 |
+
print('get ref')
|
144 |
+
face = from_png_to_jpg(get_request_image(request, 'face'))
|
145 |
+
npcache(history, room, 'face.' + ID + '.jpg', face)
|
146 |
+
face = s_enhance(face)
|
147 |
+
print('request result room = ' + str(room) + ', ID = ' + str(ID))
|
148 |
+
print('processing painting in ' + room)
|
149 |
+
if inv4 > 0:
|
150 |
+
sketch_1024 = k_resize(sketch, 64)
|
151 |
+
else:
|
152 |
+
sketch_1024 = k_resize(sketch, 48)
|
153 |
+
hints_1024 = ini_hint(sketch_1024)
|
154 |
+
hints_1024 = opreate_normal_hint(hints_1024, points, length=2, skip_sp=True)
|
155 |
+
baby = go_head(
|
156 |
+
sketch=sketch_1024,
|
157 |
+
global_hint=k_resize(face, 14),
|
158 |
+
local_hint=hints_1024
|
159 |
+
)
|
160 |
+
npcache(history, room, 'baby.' + ID + '.jpg', baby)
|
161 |
+
print('baby born')
|
162 |
+
composition = d_resize(re_deatlize(deatlize(balance_fill(baby, fill, opreate_normal_hint(ini_hint(s1024), points, length=2, skip_sp=True), s1024)), s1024), sketch.shape)
|
163 |
+
npcache(history, room, 'composition.' + ID + '.jpg', composition)
|
164 |
+
gird = process_overlay(composition, sketch)
|
165 |
+
npcache(history, room, 'gird.' + ID + '.jpg', gird)
|
166 |
+
print('composition saved')
|
167 |
+
if inv4 > 0:
|
168 |
+
albedo = go_render(sketch_1024, d_resize(composition, sketch_1024.shape, 0.5), hints_1024)
|
169 |
+
albedo = go_tail(albedo)
|
170 |
+
albedo = d_resize(re_deatlize(d_resize(albedo, s1024.shape), s1024), sketch.shape)
|
171 |
+
albedo = cv2.cvtColor(albedo, cv2.COLOR_RGB2YUV)
|
172 |
+
albedo[:, :, 0] = go_vgg7(albedo[:, :, 0])
|
173 |
+
albedo = cv2.cvtColor(albedo, cv2.COLOR_YUV2RGB)
|
174 |
+
else:
|
175 |
+
albedo = re_deatlize(d_resize(baby, s1024.shape), s1024)
|
176 |
+
albedo = d_resize(albedo, sketch.shape, 0.25)
|
177 |
+
albedo = go_tail(albedo)
|
178 |
+
albedo = go_tail(albedo)
|
179 |
+
albedo = d_resize(albedo, sketch.shape)
|
180 |
+
boundary = sketch.astype(np.float32)
|
181 |
+
boundary = cv2.GaussianBlur(boundary, (0, 0), 1.618) - boundary
|
182 |
+
boundary = boundary.clip(0, 255)
|
183 |
+
albedo = cv2.cvtColor(albedo, cv2.COLOR_RGB2HSV).astype(np.float32)
|
184 |
+
albedo[:, :, 1] += albedo[:, :, 1] * boundary / 48.0
|
185 |
+
albedo[:, :, 2] -= boundary
|
186 |
+
albedo = cv2.cvtColor(albedo.clip(0, 255).astype(np.uint8), cv2.COLOR_HSV2RGB)
|
187 |
+
npcache(history, room, 'albedo.' + ID + '.png', albedo)
|
188 |
+
print('albedo saved')
|
189 |
+
if need_render == 0:
|
190 |
+
npcache(history, room, 'result.' + ID + '.jpg', albedo)
|
191 |
+
# cv2.imwrite('results/' + room + '.' + ID + '.jpg', albedo)
|
192 |
+
print(time.time() - timenow)
|
193 |
+
return room + '_' + ID, history
|
194 |
+
HSV, YUV, DEL = process_albedo(albedo, composition, sketch)
|
195 |
+
npcache(history, room, 'HSV.' + ID + '.jpg', HSV)
|
196 |
+
npcache(history, room, 'YUV.' + ID + '.jpg', YUV)
|
197 |
+
npcache(history, room, 'DEL.' + ID + '.jpg', DEL)
|
198 |
+
print('HSV YUV DEL')
|
199 |
+
albedo_s1024 = d_resize(albedo, s1024.shape)
|
200 |
+
matting = go_mat(albedo_s1024)
|
201 |
+
matting = np.tile(matting[:, :, None], [1, 1, 3])
|
202 |
+
matting = shade_fill(matting, fill, opreate_normal_hint(ini_hint(s1024), points, length=2, skip_sp=False), s1024)
|
203 |
+
matting = matting[:, :, 0]
|
204 |
+
depth = np.zeros_like(matting, dtype=np.uint8) + 255
|
205 |
+
depth[matting < 127] = 127
|
206 |
+
depth[s1024 < 250] = 0
|
207 |
+
npcache(history, room, 'depth.' + ID + '.jpg', depth)
|
208 |
+
print('depth saved')
|
209 |
+
normal = go_norm(depth).astype(np.float32)
|
210 |
+
normal = ((normal + 1e-4) / (np.max(normal, axis=2, keepdims=True) + 1e-4) * 255.0).clip(0, 255).astype(np.uint8)
|
211 |
+
normal[matting < 127] = 255
|
212 |
+
normal = re_deatlize(normal, s1024)
|
213 |
+
normal = d_resize(normal, sketch.shape)
|
214 |
+
npcache(history, room, 'normal.' + ID + '.jpg', normal)
|
215 |
+
print('norm saved')
|
216 |
+
mask = np.zeros_like(matting, dtype=np.uint8) + 255
|
217 |
+
mask[matting < 127] = 0
|
218 |
+
mask = d_resize(mask, sketch.shape)
|
219 |
+
mask[mask < 127] = 0
|
220 |
+
mask[mask > 0] = 255
|
221 |
+
if int(light_d) == 0:
|
222 |
+
result = small_render(normal, mask, albedo, s1024, r=light_r, g=light_g, b=light_b, h=light_h, left=True, top=True)
|
223 |
+
elif int(light_d) == 1:
|
224 |
+
result = small_render(normal, mask, albedo, s1024, r=light_r, g=light_g, b=light_b, h=light_h, left=False, top=True)
|
225 |
+
elif int(light_d) == 2:
|
226 |
+
result = small_render(normal, mask, albedo, s1024, r=light_r, g=light_g, b=light_b, h=light_h, left=True, top=False)
|
227 |
+
else:
|
228 |
+
result = small_render(normal, mask, albedo, s1024, r=light_r, g=light_g, b=light_b, h=light_h, left=False, top=False)
|
229 |
+
if need_render == 2:
|
230 |
+
npcache(history, room, 'result.' + ID + '.jpg', result)
|
231 |
+
# cv2.imwrite('results/' + room + '.' + ID + '.jpg', result)
|
232 |
+
print(time.time() - timenow)
|
233 |
+
return room + '_' + ID, history
|
234 |
+
print('result saved')
|
235 |
+
preview = np.concatenate([np.tile(sketch[:, :, None], [1, 1, 3]), albedo, result], axis=1)
|
236 |
+
npcache(history, room, 'preview.' + ID + '.jpg', preview)
|
237 |
+
print('preview saved')
|
238 |
+
npcache(history, room, 'result.' + ID + '.jpg', result)
|
239 |
+
# cv2.imwrite('results/' + room + '.' + ID + '.jpg', preview)
|
240 |
+
print(time.time() - timenow)
|
241 |
+
return room + '_' + ID, history
|
242 |
+
|
243 |
+
|
244 |
+
def download_result(name, history):
|
245 |
+
room_id, name = name.split('/')
|
246 |
+
rooms = list(filter(lambda _room: _room["id"] == room_id, history))
|
247 |
+
if len(rooms) == 0:
|
248 |
+
return None
|
249 |
+
else:
|
250 |
+
room = rooms[0]
|
251 |
+
|
252 |
+
real_name = None
|
253 |
+
for k in room.keys():
|
254 |
+
if name in k:
|
255 |
+
real_name = k
|
256 |
+
break
|
257 |
+
if real_name is None:
|
258 |
+
return None
|
259 |
+
name = real_name
|
260 |
+
|
261 |
+
result = room.get(name, None)
|
262 |
+
|
263 |
+
if 'points' in name:
|
264 |
+
return json.dumps(result)
|
265 |
+
|
266 |
+
return cv2_encode(result, name)
|
267 |
+
|
268 |
+
|
269 |
+
with gr.Blocks() as demo:
|
270 |
+
history = gr.State(value=[])
|
271 |
+
with gr.Row():
|
272 |
+
with gr.Column():
|
273 |
+
btn_show = gr.Button("Open Style2Paints V4.2")
|
274 |
+
btn_show.click(None, _js="(_) => open('file/ui/web-mobile/index.html')")
|
275 |
+
|
276 |
+
with gr.Row():
|
277 |
+
with gr.Box():
|
278 |
+
with gr.Row():
|
279 |
+
upload_sketch_json = gr.Textbox(label="upload_sketch(json string)")
|
280 |
+
with gr.Row():
|
281 |
+
upload_sketch_btn = gr.Button(label="Submit sketch json")
|
282 |
+
with gr.Row():
|
283 |
+
upload_sketch_result = gr.Textbox(label="Result", interactive=False)
|
284 |
+
upload_sketch_btn.click(upload_sketch, [upload_sketch_json, history], [upload_sketch_result, history], api_name="upload_sketch")
|
285 |
+
|
286 |
+
with gr.Box():
|
287 |
+
with gr.Row():
|
288 |
+
request_result_json = gr.Textbox(label="request_result(json string)")
|
289 |
+
with gr.Row():
|
290 |
+
request_result_btn = gr.Button(label="Submit json of request for result")
|
291 |
+
with gr.Row():
|
292 |
+
request_result_result = gr.Textbox(label="Result", interactive=False)
|
293 |
+
upload_sketch_btn.click(request_result, [request_result_json, history], [request_result_result, history], api_name="request_result")
|
294 |
+
|
295 |
+
with gr.Box():
|
296 |
+
with gr.Row():
|
297 |
+
download_result_json = gr.Textbox(label="download_result(json string)")
|
298 |
+
with gr.Row():
|
299 |
+
download_result_btn = gr.Button(label="Submit json of download for result")
|
300 |
+
with gr.Row():
|
301 |
+
download_result_result = gr.Textbox(label="Result", interactive=False)
|
302 |
+
upload_sketch_btn.click(download_result, [download_result_json, history], [download_result_result], api_name="download_result")
|
303 |
+
|
304 |
+
demo.launch()
|
config.py
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
multiple_process = True
|
2 |
+
|
decompositioner.py
ADDED
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import numpy as np
|
3 |
+
from scipy.spatial import ConvexHull
|
4 |
+
from sklearn.cluster import MiniBatchKMeans
|
5 |
+
from tricks import *
|
6 |
+
import cv2
|
7 |
+
|
8 |
+
|
9 |
+
ksd = 8
|
10 |
+
mbc = MiniBatchKMeans(ksd)
|
11 |
+
|
12 |
+
|
13 |
+
def get_theme(img):
|
14 |
+
images = np.reshape(cv2.resize(img, (256, 256)), (256 * 256, 3))
|
15 |
+
hull = ConvexHull(images)
|
16 |
+
return hull.points[hull.vertices]
|
17 |
+
|
18 |
+
|
19 |
+
def simplify_points(points, img):
|
20 |
+
labels = mbc.fit(points)
|
21 |
+
new_points = []
|
22 |
+
all_center = np.mean(labels.cluster_centers_, axis=0)
|
23 |
+
distances = np.sum((points - all_center) ** 2, axis=1) ** 0.5
|
24 |
+
|
25 |
+
for idx in range(ksd):
|
26 |
+
candidates = points[labels.labels_ == idx]
|
27 |
+
scores = distances[labels.labels_ == idx]
|
28 |
+
best_id = np.argmax(scores)
|
29 |
+
new_points.append(candidates[best_id])
|
30 |
+
|
31 |
+
new_points.sort(key=np.sum, reverse=True)
|
32 |
+
|
33 |
+
new_points = np.stack(new_points, axis=0)
|
34 |
+
return new_points.clip(0, 255).astype(np.uint8)
|
35 |
+
|
36 |
+
|
37 |
+
def get_ini_layers(miku, points):
|
38 |
+
results = []
|
39 |
+
final_target = miku.astype(np.float32)
|
40 |
+
bg = np.zeros_like(final_target, dtype=np.float32) + points[0]
|
41 |
+
results.append(np.concatenate([bg, np.zeros_like(bg, dtype=np.float32) + 255], axis=2)[:, :, 0:4])
|
42 |
+
current_result = bg.copy()
|
43 |
+
for layer_index in range(1, ksd):
|
44 |
+
current_base = current_result.astype(np.float32)
|
45 |
+
current_color = np.zeros_like(final_target, dtype=np.float32) + points[layer_index]
|
46 |
+
overall_direction = final_target - current_base
|
47 |
+
avaliable_direction = current_color - current_base
|
48 |
+
current_alpha = np.sum(overall_direction * avaliable_direction, axis=2, keepdims=True) / np.sum(
|
49 |
+
avaliable_direction * avaliable_direction, axis=2, keepdims=True)
|
50 |
+
current_alpha = current_alpha.clip(0, 1)
|
51 |
+
current_result = (current_color * current_alpha + current_base * (1 - current_alpha)).clip(0, 255)
|
52 |
+
results.append(np.concatenate([current_color, current_alpha * 255.0], axis=2))
|
53 |
+
return results
|
54 |
+
|
55 |
+
|
56 |
+
def make_reconstruction(layers):
|
57 |
+
bg = np.zeros_like(layers[0], dtype=np.float32)[:, :, 0:3] + 255
|
58 |
+
for item in layers:
|
59 |
+
current_alpha = item[:, :, 3:4] / 255.0
|
60 |
+
bg = item[:, :, 0:3] * current_alpha + bg * (1 - current_alpha)
|
61 |
+
return bg
|
62 |
+
|
63 |
+
|
64 |
+
def improve_layers(layers, miku):
|
65 |
+
reconstruction = make_reconstruction(layers)
|
66 |
+
b = miku - reconstruction
|
67 |
+
new_layers = []
|
68 |
+
for item in layers:
|
69 |
+
new_item = item.copy()
|
70 |
+
new_item[:, :, 0:3] = (new_item[:, :, 0:3] + b).clip(0, 255)
|
71 |
+
new_layers.append(new_item)
|
72 |
+
return new_layers
|
73 |
+
|
74 |
+
|
75 |
+
def cluster_all(labeled_array, num_features):
|
76 |
+
xs = [[] for _ in range(num_features)]
|
77 |
+
ys = [[] for _ in range(num_features)]
|
78 |
+
M = labeled_array.shape[0]
|
79 |
+
N = labeled_array.shape[1]
|
80 |
+
for x in range(M):
|
81 |
+
for y in range(N):
|
82 |
+
i = labeled_array[x, y]
|
83 |
+
xs[i].append(x)
|
84 |
+
ys[i].append(y)
|
85 |
+
result = []
|
86 |
+
for _ in range(num_features):
|
87 |
+
result.append((np.array(xs[_]), np.array(ys[_])))
|
88 |
+
return result
|
89 |
+
|
90 |
+
|
91 |
+
def meder(x):
|
92 |
+
y = x.copy()
|
93 |
+
y = cv2.medianBlur(y, 5)
|
94 |
+
y = cv2.medianBlur(y, 5)
|
95 |
+
y = cv2.medianBlur(y, 3)
|
96 |
+
y = cv2.medianBlur(y, 3)
|
97 |
+
return y
|
98 |
+
|
99 |
+
|
100 |
+
def re_med(s_2048):
|
101 |
+
|
102 |
+
sample_2048 = s_2048.astype(np.float32)
|
103 |
+
sample_1024 = cv2.pyrDown(sample_2048)
|
104 |
+
sample_512 = cv2.pyrDown(sample_1024)
|
105 |
+
sample_256 = cv2.pyrDown(sample_512)
|
106 |
+
|
107 |
+
gradient_2048 = sample_2048 - cv2.pyrUp(sample_1024)
|
108 |
+
gradient_1024 = sample_1024 - cv2.pyrUp(sample_512)
|
109 |
+
gradient_512 = sample_512 - cv2.pyrUp(sample_256)
|
110 |
+
|
111 |
+
rec_256 = meder(sample_256)
|
112 |
+
rec_512 = cv2.pyrUp(rec_256) + meder(gradient_512)
|
113 |
+
rec_1024 = cv2.pyrUp(rec_512) + meder(gradient_1024)
|
114 |
+
rec_2048 = cv2.pyrUp(rec_1024) + meder(gradient_2048)
|
115 |
+
return rec_2048
|
116 |
+
|
117 |
+
|
118 |
+
def process_ctx(sketch, solid, render):
|
119 |
+
solid = solid.astype(np.float32)
|
120 |
+
sketch = d_resize(cv2.cvtColor(sketch, cv2.COLOR_GRAY2RGB), solid.shape).astype(np.float32)
|
121 |
+
render = d_resize(render, solid.shape).astype(np.float32)
|
122 |
+
alpha = sketch / 255.0
|
123 |
+
all_diff = render - solid
|
124 |
+
all_lines = render.copy()
|
125 |
+
all_lines = cv2.erode(all_lines, np.ones((3,3), np.uint8)) * 0.618
|
126 |
+
all_diff = re_med(all_diff)
|
127 |
+
all_lines = re_med(all_lines)
|
128 |
+
recon = solid + all_diff
|
129 |
+
recon = recon * alpha + all_lines * (1 - alpha)
|
130 |
+
recon2 = (solid + all_diff) * alpha + re_med(solid) * (1 - alpha)
|
131 |
+
recon3 = reason_blending(recon2, sketch)
|
132 |
+
return recon.clip(0, 255).astype(np.uint8), recon2.clip(0, 255).astype(np.uint8), recon3.clip(0, 255).astype(np.uint8)
|
133 |
+
|
134 |
+
|
135 |
+
def process_psd(sketch, solid, render, path='./'):
|
136 |
+
recon = process_ctx(sketch, solid, render)
|
137 |
+
points = get_theme(solid)
|
138 |
+
points = simplify_points(points, solid)
|
139 |
+
compositions = get_ini_layers(solid, points)
|
140 |
+
compositions = improve_layers(compositions, solid)
|
141 |
+
for _ in range(ksd):
|
142 |
+
cv2.imwrite(path + str(_ + 1) + '.color.png', compositions[_].clip(0, 255).astype(np.uint8))
|
143 |
+
solid = make_reconstruction(compositions).clip(0, 255).astype(np.uint8)
|
144 |
+
os.makedirs(path, exist_ok=True)
|
145 |
+
alpha = 1 - sketch.astype(np.float32) / 255.0
|
146 |
+
now = solid
|
147 |
+
now = (now.astype(np.float32) + sketch.astype(np.float32) - 255.0).clip(0, 255)
|
148 |
+
sketch = 255 + now - solid
|
149 |
+
cv2.imwrite(path + '9.sketch.png', sketch.clip(0, 255).astype(np.uint8))
|
150 |
+
all_diff = recon.astype(np.float32) - now
|
151 |
+
all_light = all_diff.copy()
|
152 |
+
all_shadow = - all_diff.copy()
|
153 |
+
all_light[all_light < 0] = 0
|
154 |
+
all_shadow[all_shadow < 0] = 0
|
155 |
+
sketch_color = all_light * alpha
|
156 |
+
light = all_light * (1 - alpha)
|
157 |
+
all_shadow = 255 - all_shadow
|
158 |
+
cv2.imwrite(path + '10.sketch_color.png', sketch_color.clip(0, 255).astype(np.uint8))
|
159 |
+
cv2.imwrite(path + '11.light.png', light.clip(0, 255).astype(np.uint8))
|
160 |
+
cv2.imwrite(path + '12.shadow.png', all_shadow.clip(0, 255).astype(np.uint8))
|
161 |
+
return recon
|
162 |
+
|
163 |
+
|
164 |
+
def process_albedo(albedo, composition, sketch):
|
165 |
+
DEL = albedo.astype(np.float32)
|
166 |
+
HSV = cv2.cvtColor(albedo, cv2.COLOR_RGB2HSV).astype(np.float32)
|
167 |
+
YUV = cv2.cvtColor(albedo, cv2.COLOR_RGB2YUV).astype(np.float32)
|
168 |
+
solid = composition.astype(np.float32)
|
169 |
+
light = sketch[:, :, None].astype(np.float32)
|
170 |
+
|
171 |
+
DEL = DEL * light / 255.0 + solid * (1 - light / 255.0)
|
172 |
+
HSV[:, :, 2:3] = np.minimum(HSV[:, :, 2:3], light)
|
173 |
+
YUV[:, :, 0:1] = np.minimum(YUV[:, :, 0:1], light)
|
174 |
+
|
175 |
+
DEL = DEL.clip(0, 255).astype(np.uint8)
|
176 |
+
HSV = HSV.clip(0, 255).astype(np.uint8)
|
177 |
+
YUV = YUV.clip(0, 255).astype(np.uint8)
|
178 |
+
|
179 |
+
return cv2.cvtColor(HSV, cv2.COLOR_HSV2RGB), cv2.cvtColor(YUV, cv2.COLOR_YUV2RGB), DEL
|
180 |
+
|
181 |
+
|
182 |
+
def process_overlay(composition, sketch):
|
183 |
+
RGB = composition.astype(np.float32)
|
184 |
+
alpha = sketch[:, :, None].astype(np.float32) / 255.0
|
185 |
+
return (RGB * alpha).clip(0, 255).astype(np.uint8)
|
generate_bash.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
lines = []
|
2 |
+
|
3 |
+
for _ in range(50, 66):
|
4 |
+
lines.append("screen -dmS \"server.80"+str(_)+"\" bash -c \'while : ;do python3 server.py 80"+str(_)+"; done;\'\n")
|
5 |
+
|
6 |
+
with open('run.bash', 'wt') as f:
|
7 |
+
f.writelines(lines)
|
8 |
+
|
9 |
+
lines = []
|
10 |
+
|
11 |
+
for _ in range(50, 66):
|
12 |
+
lines.append("screen -S \"server.80"+str(_)+"\" -X quit\n")
|
13 |
+
|
14 |
+
with open('kill.bash', 'wt') as f:
|
15 |
+
f.writelines(lines)
|
16 |
+
|
17 |
+
lines = []
|
18 |
+
|
19 |
+
for _ in range(50, 66):
|
20 |
+
lines.append("server 127.0.0.1:80"+str(_)+" weight=4 max_fails=2 fail_timeout=600s;\n")
|
21 |
+
|
22 |
+
with open('nginx.txt', 'wt') as f:
|
23 |
+
f.writelines(lines)
|
24 |
+
|
25 |
+
lines = ["ufw allow 80/tcp\n"]
|
26 |
+
|
27 |
+
for _ in range(50, 66):
|
28 |
+
lines.append("ufw allow 80"+str(_)+"/tcp\n")
|
29 |
+
|
30 |
+
with open('tcp.bash', 'wt') as f:
|
31 |
+
f.writelines(lines)
|
32 |
+
|
33 |
+
#/etc/nginx/sites-available
|
34 |
+
#/var/log/nginx
|
35 |
+
|
linefiller/__init__.py
ADDED
File without changes
|
linefiller/thinning.py
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import cv2
|
3 |
+
from numba import njit
|
4 |
+
|
5 |
+
|
6 |
+
@njit
|
7 |
+
def njit_thin(points, maps):
|
8 |
+
result = maps.copy()
|
9 |
+
h, w = maps.shape[:2]
|
10 |
+
for _ in range(len(points[0])):
|
11 |
+
x = points[0][_]
|
12 |
+
y = points[1][_]
|
13 |
+
if x > 0:
|
14 |
+
a = maps[x-1, y]
|
15 |
+
if a > 0:
|
16 |
+
result[x, y] = a
|
17 |
+
continue
|
18 |
+
if y > 0:
|
19 |
+
a = maps[x, y-1]
|
20 |
+
if a > 0:
|
21 |
+
result[x, y] = a
|
22 |
+
continue
|
23 |
+
if x + 1 < h:
|
24 |
+
a = maps[x+1, y]
|
25 |
+
if a > 0:
|
26 |
+
result[x, y] = a
|
27 |
+
continue
|
28 |
+
if y + 1 < w:
|
29 |
+
a = maps[x, y+1]
|
30 |
+
if a > 0:
|
31 |
+
result[x, y] = a
|
32 |
+
continue
|
33 |
+
return result
|
34 |
+
|
35 |
+
|
36 |
+
def thinning(fillmap, max_iter=100):
|
37 |
+
result = fillmap.copy()
|
38 |
+
for iterNum in range(max_iter):
|
39 |
+
line_points = np.where(result == 0)
|
40 |
+
if not len(line_points[0]) > 0:
|
41 |
+
break
|
42 |
+
result = njit_thin(line_points, result)
|
43 |
+
return result
|
44 |
+
|
linefiller/third_party.py
ADDED
@@ -0,0 +1,396 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
from .thinning import *
|
3 |
+
from .trappedball_fill import *
|
4 |
+
from skimage.measure import block_reduce
|
5 |
+
from skimage.morphology import disk, dilation, erosion
|
6 |
+
from numba import njit
|
7 |
+
|
8 |
+
|
9 |
+
def np_min_pool(x):
|
10 |
+
return block_reduce(x, (2, 2), np.min)
|
11 |
+
|
12 |
+
|
13 |
+
def np_max_pool(x):
|
14 |
+
return block_reduce(x, (2, 2), np.max)
|
15 |
+
|
16 |
+
|
17 |
+
def np_max_441(x):
|
18 |
+
return block_reduce(x, (4, 4, 1), np.max)
|
19 |
+
|
20 |
+
|
21 |
+
def np_max_pool_221(x):
|
22 |
+
return block_reduce(x, (2, 2, 1), np.max)
|
23 |
+
|
24 |
+
|
25 |
+
def np_max_pool_s(x, s):
|
26 |
+
return block_reduce(x, (s, s, 1), np.max)
|
27 |
+
|
28 |
+
|
29 |
+
def binarize(x):
|
30 |
+
xp = x.copy()
|
31 |
+
xp[xp < 250] = 0
|
32 |
+
xp[xp > 0] = 255
|
33 |
+
return xp
|
34 |
+
|
35 |
+
|
36 |
+
def get_initial_fillmap(boundary, merge=True):
|
37 |
+
fillmap = build_fill_map(boundary, flood_fill_multi(boundary, merge=merge))
|
38 |
+
return fillmap
|
39 |
+
|
40 |
+
|
41 |
+
def up_propagate(small_fillmap, big_boundary):
|
42 |
+
new_fillmap = cv2.resize(small_fillmap, (big_boundary.shape[1], big_boundary.shape[0]), interpolation=cv2.INTER_NEAREST)
|
43 |
+
padded_fillmap = np.pad(new_fillmap, [[1, 1], [1, 1]], 'constant', constant_values=0)
|
44 |
+
new_mask = np.ones_like(new_fillmap, dtype=np.uint8) * 255
|
45 |
+
new_mask[new_fillmap > 0] = 0
|
46 |
+
new_mask[big_boundary < 240] = 0
|
47 |
+
fills = flood_fill_multi(new_mask, merge=True)
|
48 |
+
max_id = np.max(new_fillmap)
|
49 |
+
for item in fills:
|
50 |
+
points0 = padded_fillmap[(item[0] + 1, item[1] + 0)]
|
51 |
+
points1 = padded_fillmap[(item[0] + 1, item[1] + 2)]
|
52 |
+
points2 = padded_fillmap[(item[0] + 0, item[1] + 1)]
|
53 |
+
points3 = padded_fillmap[(item[0] + 2, item[1] + 1)]
|
54 |
+
|
55 |
+
all_points = np.concatenate([points0, points1, points2, points3], axis=0)
|
56 |
+
pointsets, pointcounts = np.unique(all_points[all_points > 0], return_counts=True)
|
57 |
+
|
58 |
+
if len(pointsets) > 0:
|
59 |
+
new_fillmap[item] = pointsets[np.argmax(pointcounts)]
|
60 |
+
else:
|
61 |
+
max_id += 1
|
62 |
+
new_fillmap[item] = max_id
|
63 |
+
return new_fillmap
|
64 |
+
|
65 |
+
|
66 |
+
def laplas_fill(b_512, b_256, b_128):
|
67 |
+
b_512 = binarize(b_512)
|
68 |
+
b_256 = binarize(b_256)
|
69 |
+
b_128 = binarize(b_128)
|
70 |
+
f128 = get_initial_fillmap(b_128)
|
71 |
+
f256 = up_propagate(f128, b_256)
|
72 |
+
f512 = up_propagate(f256, b_512)
|
73 |
+
fin = thinning(f512)
|
74 |
+
return fin
|
75 |
+
|
76 |
+
|
77 |
+
@ njit
|
78 |
+
def get_corner(x):
|
79 |
+
corner = x.copy()
|
80 |
+
s0 = corner.shape[0]
|
81 |
+
s1 = corner.shape[1]
|
82 |
+
for i0 in range(1, s0 - 1):
|
83 |
+
for i1 in range(1, s1 - 1):
|
84 |
+
if x[i0, i1] == 0:
|
85 |
+
continue
|
86 |
+
if x[i0, i1 - 1] == 0:
|
87 |
+
if x[i0 - 1, i1 - 1] == 0:
|
88 |
+
continue
|
89 |
+
if x[i0 + 1, i1 - 1] == 0:
|
90 |
+
continue
|
91 |
+
corner[i0, i1] = 0
|
92 |
+
continue
|
93 |
+
if x[i0, i1 + 1] == 0:
|
94 |
+
if x[i0 - 1, i1 + 1] == 0:
|
95 |
+
continue
|
96 |
+
if x[i0 + 1, i1 + 1] == 0:
|
97 |
+
continue
|
98 |
+
corner[i0, i1] = 0
|
99 |
+
continue
|
100 |
+
if x[i0 - 1, i1] == 0:
|
101 |
+
if x[i0 - 1, i1 - 1] == 0:
|
102 |
+
continue
|
103 |
+
if x[i0 - 1, i1 + 1] == 0:
|
104 |
+
continue
|
105 |
+
corner[i0, i1] = 0
|
106 |
+
continue
|
107 |
+
if x[i0 + 1, i1] == 0:
|
108 |
+
if x[i0 + 1, i1 - 1] == 0:
|
109 |
+
continue
|
110 |
+
if x[i0 + 1, i1 + 1] == 0:
|
111 |
+
continue
|
112 |
+
corner[i0, i1] = 0
|
113 |
+
continue
|
114 |
+
return corner
|
115 |
+
|
116 |
+
|
117 |
+
def monogrouh(x):
|
118 |
+
y = 255 - x
|
119 |
+
y = dilation(y, disk(1))
|
120 |
+
y = dilation(y, disk(1))
|
121 |
+
y = erosion(y, disk(1))
|
122 |
+
y = erosion(y, disk(1))
|
123 |
+
y = 255 - y
|
124 |
+
return y
|
125 |
+
|
126 |
+
|
127 |
+
def corners(x):
|
128 |
+
y = x.copy()
|
129 |
+
y = monogrouh(y)
|
130 |
+
y = get_corner(y)
|
131 |
+
y = monogrouh(y)
|
132 |
+
y = get_corner(y)
|
133 |
+
y = monogrouh(y)
|
134 |
+
return y
|
135 |
+
|
136 |
+
|
137 |
+
def save_fill(name, fill):
|
138 |
+
cv2.imwrite(name, show_fill_map(fill))
|
139 |
+
|
140 |
+
|
141 |
+
def double_fill(b_1024, b_512, b256):
|
142 |
+
b256 = binarize(b256)
|
143 |
+
b_512 = binarize(b_512)
|
144 |
+
b_1024 = binarize(b_1024)
|
145 |
+
b_1024 = corners(b_1024)
|
146 |
+
b_512 = np.min(np.stack([b_512, np_min_pool(b_1024)], axis=2), axis=2)
|
147 |
+
b_512 = corners(b_512)
|
148 |
+
b_256 = np.min(np.stack([b256, np_min_pool(b_512)], axis=2), axis=2)
|
149 |
+
b_256 = corners(b_256)
|
150 |
+
b_128 = np_min_pool(b_256)
|
151 |
+
b_128 = corners(b_128)
|
152 |
+
b_64 = np_min_pool(b_128)
|
153 |
+
f64 = get_initial_fillmap(b_64)
|
154 |
+
print('get_initial_fillmap(b_64)')
|
155 |
+
f128 = up_propagate(f64, b_128)
|
156 |
+
print('up_propagate(f64, b_128)')
|
157 |
+
f256 = up_propagate(f128, b_256)
|
158 |
+
print('up_propagate(f128, b_256)')
|
159 |
+
f512 = up_propagate(f256, b_512)
|
160 |
+
print('up_propagate(f256, b_512)')
|
161 |
+
f1024 = up_propagate(f512, b_1024)
|
162 |
+
print('up_propagate(f512, b_1024)')
|
163 |
+
fin = thinning(f1024)
|
164 |
+
print('thinning(f1024)')
|
165 |
+
|
166 |
+
# cv2.imwrite('b_64.png', b_64)
|
167 |
+
# cv2.imwrite('b_128.png', b_128)
|
168 |
+
# cv2.imwrite('b_256.png', b_256)
|
169 |
+
# cv2.imwrite('b_512.png', b_512)
|
170 |
+
# cv2.imwrite('b_1024.png', b_1024)
|
171 |
+
# save_fill('f64.png', f64)
|
172 |
+
# save_fill('f128.png', f128)
|
173 |
+
# save_fill('f256.png', f256)
|
174 |
+
# save_fill('f512.png', f512)
|
175 |
+
# save_fill('f1024.png', f1024)
|
176 |
+
# save_fill('fin.png', fin)
|
177 |
+
|
178 |
+
return find_all(fin)
|
179 |
+
|
180 |
+
|
181 |
+
def single_fill(b_2048, path):
|
182 |
+
b_2048 = corners(binarize(b_2048))
|
183 |
+
f2048 = get_initial_fillmap(b_2048, merge=False)
|
184 |
+
print(path + 'get_initial_fillmap(b_2048, merge=False)')
|
185 |
+
fin = thinning(f2048)
|
186 |
+
print(path + 'thinning(f2048)')
|
187 |
+
# cv2.imwrite(path + 'b_2048.png', b_2048)
|
188 |
+
# save_fill(path + 'f2048.png', f2048)
|
189 |
+
# save_fill(path + 'fin.png', fin)
|
190 |
+
return find_all(fin)
|
191 |
+
|
192 |
+
|
193 |
+
def deatlize(x):
|
194 |
+
x = cv2.GaussianBlur(x, (0, 0), 0.8)
|
195 |
+
x = cv2.medianBlur(x, 3)
|
196 |
+
return x
|
197 |
+
|
198 |
+
|
199 |
+
def low_down(gradient_mask):
|
200 |
+
return 1.0 - cv2.dilate(255 - gradient_mask, np.ones((3, 3), np.uint8), iterations=2).astype(np.float32) / 255.0
|
201 |
+
|
202 |
+
|
203 |
+
def cv2pyrDown(x):
|
204 |
+
return cv2.pyrDown(cv2.medianBlur(cv2.medianBlur(x, 3), 3))
|
205 |
+
|
206 |
+
|
207 |
+
def cv2pyrUp(x):
|
208 |
+
return cv2.pyrUp(cv2.medianBlur(cv2.medianBlur(x, 3), 3))
|
209 |
+
|
210 |
+
|
211 |
+
def re_deatlize(visulized, s1024):
|
212 |
+
|
213 |
+
gradient_mask_1024 = binarize(s1024)
|
214 |
+
gradient_mask_512 = np_min_pool(gradient_mask_1024)
|
215 |
+
gradient_mask_256 = np_min_pool(gradient_mask_512)
|
216 |
+
gradient_mask_128 = np_min_pool(gradient_mask_256)
|
217 |
+
gradient_mask_64 = np_min_pool(gradient_mask_128)
|
218 |
+
|
219 |
+
gradient_mask_1024 = low_down(gradient_mask_1024)
|
220 |
+
gradient_mask_512 = low_down(gradient_mask_512)
|
221 |
+
gradient_mask_256 = low_down(gradient_mask_256)
|
222 |
+
gradient_mask_128 = low_down(gradient_mask_128)
|
223 |
+
gradient_mask_64 = low_down(gradient_mask_64)
|
224 |
+
|
225 |
+
sample_1024 = visulized.astype(np.float32)
|
226 |
+
sample_512 = cv2pyrDown(sample_1024)
|
227 |
+
sample_256 = cv2pyrDown(sample_512)
|
228 |
+
sample_128 = cv2pyrDown(sample_256)
|
229 |
+
sample_64 = cv2pyrDown(sample_128)
|
230 |
+
sample_32 = cv2pyrDown(sample_64)
|
231 |
+
|
232 |
+
gradient_1024 = sample_1024 - cv2pyrUp(sample_512)
|
233 |
+
gradient_512 = sample_512 - cv2pyrUp(sample_256)
|
234 |
+
gradient_256 = sample_256 - cv2pyrUp(sample_128)
|
235 |
+
gradient_128 = sample_128 - cv2pyrUp(sample_64)
|
236 |
+
gradient_64 = sample_64 - cv2pyrUp(sample_32)
|
237 |
+
|
238 |
+
rec_32 = sample_32
|
239 |
+
rec_64 = cv2pyrUp(rec_32) + gradient_64 * (1 - gradient_mask_64[:, :, None])
|
240 |
+
rec_128 = cv2pyrUp(rec_64) + gradient_128 * (1 - gradient_mask_128[:, :, None])
|
241 |
+
rec_256 = cv2pyrUp(rec_128) + gradient_256 * (1 - gradient_mask_256[:, :, None])
|
242 |
+
rec_512 = cv2pyrUp(rec_256) + gradient_512 * (1 - gradient_mask_512[:, :, None])
|
243 |
+
rec_1024 = cv2pyrUp(rec_512) + gradient_1024 * (1 - gradient_mask_1024[:, :, None])
|
244 |
+
|
245 |
+
return rec_1024.clip(0, 255).astype(np.uint8)
|
246 |
+
|
247 |
+
|
248 |
+
def tiny_deatlize(visulized, s2048):
|
249 |
+
gradient_mask_2048 = s2048.copy()
|
250 |
+
gradient_mask_1024 = np_min_pool(gradient_mask_2048)
|
251 |
+
gradient_mask_512 = np_min_pool(gradient_mask_1024)
|
252 |
+
gradient_mask_256 = np_min_pool(gradient_mask_512)
|
253 |
+
|
254 |
+
gradient_mask_2048 = low_down(gradient_mask_2048)
|
255 |
+
gradient_mask_1024 = low_down(gradient_mask_1024)
|
256 |
+
gradient_mask_512 = low_down(gradient_mask_512)
|
257 |
+
gradient_mask_256 = low_down(gradient_mask_256)
|
258 |
+
|
259 |
+
sample_2048 = visulized.astype(np.float32)
|
260 |
+
sample_1024 = cv2.pyrDown(sample_2048)
|
261 |
+
sample_512 = cv2.pyrDown(sample_1024)
|
262 |
+
sample_256 = cv2.pyrDown(sample_512)
|
263 |
+
sample_128 = cv2.pyrDown(sample_256)
|
264 |
+
|
265 |
+
gradient_2048 = sample_2048 - cv2.pyrUp(sample_1024)
|
266 |
+
gradient_1024 = sample_1024 - cv2.pyrUp(sample_512)
|
267 |
+
gradient_512 = sample_512 - cv2.pyrUp(sample_256)
|
268 |
+
gradient_256 = sample_256 - cv2.pyrUp(sample_128)
|
269 |
+
|
270 |
+
rec_128 = sample_128
|
271 |
+
rec_256 = cv2.pyrUp(rec_128) + gradient_256 * (1 - gradient_mask_256[:, :, None])
|
272 |
+
rec_512 = cv2.pyrUp(rec_256) + gradient_512 * (1 - gradient_mask_512[:, :, None])
|
273 |
+
rec_1024 = cv2.pyrUp(rec_512) + gradient_1024 * (1 - gradient_mask_1024[:, :, None])
|
274 |
+
rec_2048 = cv2.pyrUp(rec_1024) + gradient_2048 * (1 - gradient_mask_2048[:, :, None])
|
275 |
+
return rec_2048.clip(0, 255).astype(np.uint8)
|
276 |
+
|
277 |
+
|
278 |
+
def adain(x, y):
|
279 |
+
x_high = cv2.GaussianBlur(x, (0, 0), 3.0)
|
280 |
+
y_high = cv2.GaussianBlur(y, (0, 0), 3.0)
|
281 |
+
return (x.astype(np.float32) - x_high.astype(np.float32) + y_high.astype(np.float32)).clip(0, 255).astype(np.uint8)
|
282 |
+
|
283 |
+
|
284 |
+
def corrupt(x, b128):
|
285 |
+
float_sketch = x.astype(float)
|
286 |
+
float_base = cv2.resize(float_sketch, (b128.shape[1], b128.shape[0]), cv2.INTER_AREA)
|
287 |
+
alpha = b128[:, :, 0] / 255.0
|
288 |
+
float_base = alpha * float_base + (1 - alpha) * np.mean(float_base)
|
289 |
+
float_base = cv2.GaussianBlur(float_base, (0, 0), 8.0)
|
290 |
+
float_base = cv2.resize(float_base, (x.shape[1], x.shape[0]), cv2.INTER_CUBIC)
|
291 |
+
result = float_sketch / (float_base + 1e-10)
|
292 |
+
result = result.clip(0, 1)
|
293 |
+
result -= np.min(result)
|
294 |
+
result /= np.max(result)
|
295 |
+
return (result * 255.0).clip(0, 255).astype(np.uint8)
|
296 |
+
|
297 |
+
|
298 |
+
def fuse_sketch(color, sketch, fills, fixer, points_arr, colors_arr):
|
299 |
+
sketch = cv2.resize(sketch, (color.shape[1], color.shape[0]))
|
300 |
+
fills = cv2.resize(fills, (color.shape[1], color.shape[0]), interpolation=cv2.INTER_NEAREST)
|
301 |
+
fill_id = np.unique(fills.flatten())
|
302 |
+
bg = np.zeros_like(color, dtype=np.uint8)
|
303 |
+
checking_result = np.zeros(dtype=np.int32, shape=(np.max(fills) + 1,)) - 1
|
304 |
+
length_points = int(len(points_arr))
|
305 |
+
for _ in range(length_points):
|
306 |
+
checking_result[fills[points_arr[_][0], points_arr[_][1]]] = _
|
307 |
+
for id in fill_id:
|
308 |
+
points = np.where(fills == id)
|
309 |
+
if len(points[0]) > 0:
|
310 |
+
color_id = checking_result[id]
|
311 |
+
if color_id > -1:
|
312 |
+
bg[points] = np.array(colors_arr[color_id])
|
313 |
+
else:
|
314 |
+
bg[points] = np.median(color[points], axis=0)
|
315 |
+
fixed = adain(fixer(sketch, bg), bg)
|
316 |
+
result = (fixed.astype(np.float32) + sketch[:, :, None].astype(np.float32) - 255.0).clip(0, 255).astype(np.uint8)
|
317 |
+
return result, fixed, bg
|
318 |
+
|
319 |
+
|
320 |
+
def balance_fill(color, fills, points, sizer):
|
321 |
+
color = cv2.resize(color, (sizer.shape[1], sizer.shape[0]), interpolation=cv2.INTER_NEAREST)
|
322 |
+
points = cv2.resize(points, (sizer.shape[1], sizer.shape[0]), interpolation=cv2.INTER_NEAREST)
|
323 |
+
bg = np.zeros_like(color, dtype=np.uint8)
|
324 |
+
for region in fills:
|
325 |
+
if len(region[0]) > 0:
|
326 |
+
region_points = points[region]
|
327 |
+
region_points = region_points[region_points[:, 3] > 0]
|
328 |
+
if region_points.shape[0] > 0:
|
329 |
+
points_color, points_color_count = np.unique(region_points, return_counts=True, axis=0)
|
330 |
+
bg[region] = points_color[np.argmax(points_color_count)][0:3]
|
331 |
+
else:
|
332 |
+
bg[region] = np.median(color[region], axis=0)
|
333 |
+
return bg
|
334 |
+
|
335 |
+
|
336 |
+
def shade_fill(color, fills, points, sizer):
|
337 |
+
color = cv2.resize(color, (sizer.shape[1], sizer.shape[0]), interpolation=cv2.INTER_NEAREST)
|
338 |
+
points = cv2.resize(points, (sizer.shape[1], sizer.shape[0]), interpolation=cv2.INTER_NEAREST)
|
339 |
+
bg = np.zeros_like(color, dtype=np.uint8)
|
340 |
+
for region in fills:
|
341 |
+
if len(region[0]) > 0:
|
342 |
+
region_points = points[region]
|
343 |
+
region_points = region_points[region_points[:, 3] > 0]
|
344 |
+
if region_points.shape[0] > 0:
|
345 |
+
points_color, points_color_count = np.unique(region_points, return_counts=True, axis=0)
|
346 |
+
c = points_color[np.argmax(points_color_count)][0:3]
|
347 |
+
r = c[0]
|
348 |
+
g = c[1]
|
349 |
+
b = c[2]
|
350 |
+
if r == 1 and g == 233 and b == 0:
|
351 |
+
bg[region] = 255
|
352 |
+
elif r == 0 and g == 233 and b == 1:
|
353 |
+
bg[region] = 0
|
354 |
+
else:
|
355 |
+
bg[region] = np.median(color[region], axis=0)
|
356 |
+
else:
|
357 |
+
bg[region] = np.median(color[region], axis=0)
|
358 |
+
return bg
|
359 |
+
|
360 |
+
|
361 |
+
def get_alpha_piece(points):
|
362 |
+
padded_points = np.pad(points, [[1, 1], [1, 1], [0, 0]], 'constant', constant_values=127)
|
363 |
+
lines = 255 - padded_points[:, :, 3]
|
364 |
+
lines[lines < 240] = 0
|
365 |
+
fills = flood_fill_multi(lines, merge=True)
|
366 |
+
result = np.zeros_like(padded_points)
|
367 |
+
for item in fills:
|
368 |
+
points0 = padded_points[(item[0], item[1] + 1)]
|
369 |
+
points1 = padded_points[(item[0], item[1] - 1)]
|
370 |
+
points2 = padded_points[(item[0] + 1, item[1])]
|
371 |
+
points3 = padded_points[(item[0] - 1, item[1])]
|
372 |
+
all_points = np.concatenate([points0, points1, points2, points3], axis=0)
|
373 |
+
all_points = all_points[all_points[:, 3] > 0]
|
374 |
+
all_points = np.unique(all_points, axis=0)
|
375 |
+
if all_points.shape[0] == 1:
|
376 |
+
result[item] = all_points[0]
|
377 |
+
piece = result[1:-1, 1:-1, :]
|
378 |
+
piece = np.maximum(piece, points)
|
379 |
+
return piece, points
|
380 |
+
|
381 |
+
|
382 |
+
def fin_deatlize(color, sketch):
|
383 |
+
|
384 |
+
cf = color.astype(np.float32)
|
385 |
+
alpha = sketch.astype(np.float32)[:, :, None] / 255.0
|
386 |
+
|
387 |
+
plain = cf * alpha
|
388 |
+
lines = cf * (1 - alpha)
|
389 |
+
|
390 |
+
plain = cv2.medianBlur(plain, 5)
|
391 |
+
plain = cv2.medianBlur(plain, 3)
|
392 |
+
|
393 |
+
fin = plain + lines
|
394 |
+
|
395 |
+
return fin.clip(0, 255).astype(np.uint8)
|
396 |
+
|
linefiller/trappedball_fill.py
ADDED
@@ -0,0 +1,436 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import cv2
|
2 |
+
import numpy as np
|
3 |
+
from scipy.ndimage import label
|
4 |
+
from numba import njit
|
5 |
+
|
6 |
+
|
7 |
+
def get_ball_structuring_element(radius):
|
8 |
+
"""Get a ball shape structuring element with specific radius for morphology operation.
|
9 |
+
The radius of ball usually equals to (leaking_gap_size / 2).
|
10 |
+
|
11 |
+
# Arguments
|
12 |
+
radius: radius of ball shape.
|
13 |
+
|
14 |
+
# Returns
|
15 |
+
an array of ball structuring element.
|
16 |
+
"""
|
17 |
+
return cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2 * radius + 1, 2 * radius + 1))
|
18 |
+
|
19 |
+
|
20 |
+
def get_unfilled_point(image):
|
21 |
+
"""Get points belong to unfilled(value==255) area.
|
22 |
+
|
23 |
+
# Arguments
|
24 |
+
image: an image.
|
25 |
+
|
26 |
+
# Returns
|
27 |
+
an array of points.
|
28 |
+
"""
|
29 |
+
y, x = np.where(image == 255)
|
30 |
+
|
31 |
+
return np.stack((x.astype(int), y.astype(int)), axis=-1)
|
32 |
+
|
33 |
+
|
34 |
+
def exclude_area(image, radius):
|
35 |
+
"""Perform erosion on image to exclude points near the boundary.
|
36 |
+
We want to pick part using floodfill from the seed point after dilation.
|
37 |
+
When the seed point is near boundary, it might not stay in the fill, and would
|
38 |
+
not be a valid point for next floodfill operation. So we ignore these points with erosion.
|
39 |
+
|
40 |
+
# Arguments
|
41 |
+
image: an image.
|
42 |
+
radius: radius of ball shape.
|
43 |
+
|
44 |
+
# Returns
|
45 |
+
an image after dilation.
|
46 |
+
"""
|
47 |
+
return cv2.morphologyEx(image, cv2.MORPH_ERODE, get_ball_structuring_element(radius), anchor=(-1, -1), iterations=1)
|
48 |
+
|
49 |
+
|
50 |
+
def trapped_ball_fill_single(image, seed_point, radius):
|
51 |
+
"""Perform a single trapped ball fill operation.
|
52 |
+
|
53 |
+
# Arguments
|
54 |
+
image: an image. the image should consist of white background, black lines and black fills.
|
55 |
+
the white area is unfilled area, and the black area is filled area.
|
56 |
+
seed_point: seed point for trapped-ball fill, a tuple (integer, integer).
|
57 |
+
radius: radius of ball shape.
|
58 |
+
# Returns
|
59 |
+
an image after filling.
|
60 |
+
"""
|
61 |
+
ball = get_ball_structuring_element(radius)
|
62 |
+
|
63 |
+
pass1 = np.full(image.shape, 255, np.uint8)
|
64 |
+
pass2 = np.full(image.shape, 255, np.uint8)
|
65 |
+
|
66 |
+
im_inv = cv2.bitwise_not(image)
|
67 |
+
|
68 |
+
# Floodfill the image
|
69 |
+
mask1 = cv2.copyMakeBorder(im_inv, 1, 1, 1, 1, cv2.BORDER_CONSTANT, 0)
|
70 |
+
_, pass1, _, _ = cv2.floodFill(pass1, mask1, seed_point, 0, 0, 0, 4)
|
71 |
+
|
72 |
+
# Perform dilation on image. The fill areas between gaps became disconnected.
|
73 |
+
pass1 = cv2.morphologyEx(pass1, cv2.MORPH_DILATE, ball, anchor=(-1, -1), iterations=1)
|
74 |
+
mask2 = cv2.copyMakeBorder(pass1, 1, 1, 1, 1, cv2.BORDER_CONSTANT, 0)
|
75 |
+
|
76 |
+
# Floodfill with seed point again to select one fill area.
|
77 |
+
_, pass2, _, rect = cv2.floodFill(pass2, mask2, seed_point, 0, 0, 0, 4)
|
78 |
+
# Perform erosion on the fill result leaking-proof fill.
|
79 |
+
pass2 = cv2.morphologyEx(pass2, cv2.MORPH_ERODE, ball, anchor=(-1, -1), iterations=1)
|
80 |
+
|
81 |
+
return pass2
|
82 |
+
|
83 |
+
|
84 |
+
def trapped_ball_fill_multi(image, radius, method='mean', max_iter=1000):
|
85 |
+
"""Perform multi trapped ball fill operations until all valid areas are filled.
|
86 |
+
|
87 |
+
# Arguments
|
88 |
+
image: an image. The image should consist of white background, black lines and black fills.
|
89 |
+
the white area is unfilled area, and the black area is filled area.
|
90 |
+
radius: radius of ball shape.
|
91 |
+
method: method for filtering the fills.
|
92 |
+
'max' is usually with large radius for select large area such as background.
|
93 |
+
max_iter: max iteration number.
|
94 |
+
# Returns
|
95 |
+
an array of fills' points.
|
96 |
+
"""
|
97 |
+
print('trapped-ball ' + str(radius))
|
98 |
+
|
99 |
+
unfill_area = image
|
100 |
+
filled_area, filled_area_size, result = [], [], []
|
101 |
+
|
102 |
+
for _ in range(max_iter):
|
103 |
+
points = get_unfilled_point(exclude_area(unfill_area, radius))
|
104 |
+
|
105 |
+
if not len(points) > 0:
|
106 |
+
break
|
107 |
+
|
108 |
+
fill = trapped_ball_fill_single(unfill_area, (points[0][0], points[0][1]), radius)
|
109 |
+
unfill_area = cv2.bitwise_and(unfill_area, fill)
|
110 |
+
|
111 |
+
filled_area.append(np.where(fill == 0))
|
112 |
+
filled_area_size.append(len(np.where(fill == 0)[0]))
|
113 |
+
|
114 |
+
filled_area_size = np.asarray(filled_area_size)
|
115 |
+
|
116 |
+
if method == 'max':
|
117 |
+
area_size_filter = np.max(filled_area_size)
|
118 |
+
elif method == 'median':
|
119 |
+
area_size_filter = np.median(filled_area_size)
|
120 |
+
elif method == 'mean':
|
121 |
+
area_size_filter = np.mean(filled_area_size)
|
122 |
+
else:
|
123 |
+
area_size_filter = 0
|
124 |
+
|
125 |
+
result_idx = np.where(filled_area_size >= area_size_filter)[0]
|
126 |
+
|
127 |
+
for i in result_idx:
|
128 |
+
result.append(filled_area[i])
|
129 |
+
|
130 |
+
return result
|
131 |
+
|
132 |
+
|
133 |
+
def flood_fill_single(im, seed_point):
|
134 |
+
"""Perform a single flood fill operation.
|
135 |
+
|
136 |
+
# Arguments
|
137 |
+
image: an image. the image should consist of white background, black lines and black fills.
|
138 |
+
the white area is unfilled area, and the black area is filled area.
|
139 |
+
seed_point: seed point for trapped-ball fill, a tuple (integer, integer).
|
140 |
+
# Returns
|
141 |
+
an image after filling.
|
142 |
+
"""
|
143 |
+
pass1 = np.full(im.shape, 255, np.uint8)
|
144 |
+
|
145 |
+
im_inv = cv2.bitwise_not(im)
|
146 |
+
|
147 |
+
mask1 = cv2.copyMakeBorder(im_inv, 1, 1, 1, 1, cv2.BORDER_CONSTANT, 0)
|
148 |
+
_, pass1, _, _ = cv2.floodFill(pass1, mask1, seed_point, 0, 0, 0, 4)
|
149 |
+
|
150 |
+
return pass1
|
151 |
+
|
152 |
+
|
153 |
+
@njit
|
154 |
+
def count_all(labeled_array, all_counts):
|
155 |
+
M = labeled_array.shape[0]
|
156 |
+
N = labeled_array.shape[1]
|
157 |
+
for x in range(M):
|
158 |
+
for y in range(N):
|
159 |
+
i = labeled_array[x, y] - 1
|
160 |
+
if i > -1:
|
161 |
+
all_counts[i] = all_counts[i] + 1
|
162 |
+
return
|
163 |
+
|
164 |
+
|
165 |
+
@njit
|
166 |
+
def trace_all(labeled_array, xs, ys, cs):
|
167 |
+
M = labeled_array.shape[0]
|
168 |
+
N = labeled_array.shape[1]
|
169 |
+
for x in range(M):
|
170 |
+
for y in range(N):
|
171 |
+
current_label = labeled_array[x, y] - 1
|
172 |
+
if current_label > -1:
|
173 |
+
current_label_count = cs[current_label]
|
174 |
+
xs[current_label][current_label_count] = x
|
175 |
+
ys[current_label][current_label_count] = y
|
176 |
+
cs[current_label] = current_label_count + 1
|
177 |
+
return
|
178 |
+
|
179 |
+
|
180 |
+
def find_all(labeled_array):
|
181 |
+
hist_size = int(np.max(labeled_array))
|
182 |
+
if hist_size == 0:
|
183 |
+
return []
|
184 |
+
all_counts = [0 for _ in range(hist_size)]
|
185 |
+
count_all(labeled_array, all_counts)
|
186 |
+
xs = [np.zeros(shape=(item, ), dtype=np.uint32) for item in all_counts]
|
187 |
+
ys = [np.zeros(shape=(item, ), dtype=np.uint32) for item in all_counts]
|
188 |
+
cs = [0 for item in all_counts]
|
189 |
+
trace_all(labeled_array, xs, ys, cs)
|
190 |
+
filled_area = []
|
191 |
+
for _ in range(hist_size):
|
192 |
+
filled_area.append((xs[_], ys[_]))
|
193 |
+
return filled_area
|
194 |
+
|
195 |
+
|
196 |
+
def flood_fill_multi(image, merge=False):
|
197 |
+
print('floodfill')
|
198 |
+
|
199 |
+
labeled_array, num_features = label(image / 255)
|
200 |
+
print('floodfill_ok1')
|
201 |
+
|
202 |
+
filled_area = find_all(labeled_array)
|
203 |
+
|
204 |
+
print('floodfill_ok2')
|
205 |
+
|
206 |
+
if merge:
|
207 |
+
new_fill = []
|
208 |
+
for item in filled_area:
|
209 |
+
if len(item[0]) > 8:
|
210 |
+
new_fill.append(item)
|
211 |
+
return new_fill
|
212 |
+
|
213 |
+
print('floodfill_ok3')
|
214 |
+
|
215 |
+
return filled_area
|
216 |
+
|
217 |
+
|
218 |
+
def old_flood_fill_multi(image, max_iter=20000):
|
219 |
+
"""Perform multi flood fill operations until all valid areas are filled.
|
220 |
+
This operation will fill all rest areas, which may result large amount of fills.
|
221 |
+
|
222 |
+
# Arguments
|
223 |
+
image: an image. the image should contain white background, black lines and black fills.
|
224 |
+
the white area is unfilled area, and the black area is filled area.
|
225 |
+
max_iter: max iteration number.
|
226 |
+
# Returns
|
227 |
+
an array of fills' points.
|
228 |
+
"""
|
229 |
+
print('floodfill')
|
230 |
+
|
231 |
+
unfill_area = image
|
232 |
+
filled_area = []
|
233 |
+
|
234 |
+
for _ in range(max_iter):
|
235 |
+
points = get_unfilled_point(unfill_area)
|
236 |
+
|
237 |
+
if not len(points) > 0:
|
238 |
+
break
|
239 |
+
|
240 |
+
fill = flood_fill_single(unfill_area, (points[0][0], points[0][1]))
|
241 |
+
unfill_area = cv2.bitwise_and(unfill_area, fill)
|
242 |
+
|
243 |
+
filled_area.append(np.where(fill == 0))
|
244 |
+
|
245 |
+
return filled_area
|
246 |
+
|
247 |
+
|
248 |
+
def mark_fill(image, fills):
|
249 |
+
"""Mark filled areas with 0.
|
250 |
+
|
251 |
+
# Arguments
|
252 |
+
image: an image.
|
253 |
+
fills: an array of fills' points.
|
254 |
+
# Returns
|
255 |
+
an image.
|
256 |
+
"""
|
257 |
+
result = image.copy()
|
258 |
+
|
259 |
+
for fill in fills:
|
260 |
+
result[fill] = 0
|
261 |
+
|
262 |
+
return result
|
263 |
+
|
264 |
+
|
265 |
+
def build_fill_map(image, fills):
|
266 |
+
"""Make an image(array) with each pixel(element) marked with fills' id. id of line is 0.
|
267 |
+
|
268 |
+
# Arguments
|
269 |
+
image: an image.
|
270 |
+
fills: an array of fills' points.
|
271 |
+
# Returns
|
272 |
+
an array.
|
273 |
+
"""
|
274 |
+
result = np.zeros(image.shape[:2], np.int)
|
275 |
+
|
276 |
+
for index, fill in enumerate(fills):
|
277 |
+
|
278 |
+
if(len(fill[0]) == 0):
|
279 |
+
continue
|
280 |
+
|
281 |
+
result[fill] = index + 1
|
282 |
+
|
283 |
+
return result
|
284 |
+
|
285 |
+
|
286 |
+
def show_fill_map(fillmap):
|
287 |
+
"""Mark filled areas with colors. It is useful for visualization.
|
288 |
+
|
289 |
+
# Arguments
|
290 |
+
image: an image.
|
291 |
+
fills: an array of fills' points.
|
292 |
+
# Returns
|
293 |
+
an image.
|
294 |
+
"""
|
295 |
+
# Generate color for each fill randomly.
|
296 |
+
colors = np.random.randint(0, 255, (np.max(fillmap) + 1, 3))
|
297 |
+
# Id of line is 0, and its color is black.
|
298 |
+
colors[0] = [0, 0, 0]
|
299 |
+
|
300 |
+
return colors[fillmap]
|
301 |
+
|
302 |
+
|
303 |
+
def get_bounding_rect(points):
|
304 |
+
"""Get a bounding rect of points.
|
305 |
+
|
306 |
+
# Arguments
|
307 |
+
points: array of points.
|
308 |
+
# Returns
|
309 |
+
rect coord
|
310 |
+
"""
|
311 |
+
x1, y1, x2, y2 = np.min(points[1]), np.min(points[0]), np.max(points[1]), np.max(points[0])
|
312 |
+
return x1, y1, x2, y2
|
313 |
+
|
314 |
+
|
315 |
+
def get_border_bounding_rect(h, w, p1, p2, r):
|
316 |
+
"""Get a valid bounding rect in the image with border of specific size.
|
317 |
+
|
318 |
+
# Arguments
|
319 |
+
h: image max height.
|
320 |
+
w: image max width.
|
321 |
+
p1: start point of rect.
|
322 |
+
p2: end point of rect.
|
323 |
+
r: border radius.
|
324 |
+
# Returns
|
325 |
+
rect coord
|
326 |
+
"""
|
327 |
+
x1, y1, x2, y2 = p1[0], p1[1], p2[0], p2[1]
|
328 |
+
|
329 |
+
x1 = x1 - r if 0 < x1 - r else 0
|
330 |
+
y1 = y1 - r if 0 < y1 - r else 0
|
331 |
+
x2 = x2 + r + 1 if x2 + r + 1 < w else w
|
332 |
+
y2 = y2 + r + 1 if y2 + r + 1 < h else h
|
333 |
+
|
334 |
+
return x1, y1, x2, y2
|
335 |
+
|
336 |
+
|
337 |
+
def get_border_point(points, rect, max_height, max_width):
|
338 |
+
"""Get border points of a fill area
|
339 |
+
|
340 |
+
# Arguments
|
341 |
+
points: points of fill .
|
342 |
+
rect: bounding rect of fill.
|
343 |
+
max_height: image max height.
|
344 |
+
max_width: image max width.
|
345 |
+
# Returns
|
346 |
+
points , convex shape of points
|
347 |
+
"""
|
348 |
+
# Get a local bounding rect.
|
349 |
+
border_rect = get_border_bounding_rect(max_height, max_width, rect[:2], rect[2:], 2)
|
350 |
+
|
351 |
+
# Get fill in rect.
|
352 |
+
fill = np.zeros((border_rect[3] - border_rect[1], border_rect[2] - border_rect[0]), np.uint8)
|
353 |
+
# Move points to the rect.
|
354 |
+
fill[(points[0] - border_rect[1], points[1] - border_rect[0])] = 255
|
355 |
+
|
356 |
+
# Get shape.
|
357 |
+
_, contours, _ = cv2.findContours(fill, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
358 |
+
# approx_shape = cv2.approxPolyDP(contours[0], 0.02 * cv2.arcLength(contours[0], True), True)
|
359 |
+
|
360 |
+
# Get border pixel.
|
361 |
+
# Structuring element in cross shape is used instead of box to get 4-connected border.
|
362 |
+
cross = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
|
363 |
+
border_pixel_mask = cv2.morphologyEx(fill, cv2.MORPH_DILATE, cross, anchor=(-1, -1), iterations=1) - fill
|
364 |
+
border_pixel_points = np.where(border_pixel_mask == 255)
|
365 |
+
|
366 |
+
# Transform points back to fillmap.
|
367 |
+
border_pixel_points = (border_pixel_points[0] + border_rect[1], border_pixel_points[1] + border_rect[0])
|
368 |
+
|
369 |
+
return border_pixel_points
|
370 |
+
|
371 |
+
|
372 |
+
def merge_fill(fillmap, max_iter=20):
|
373 |
+
"""Merge fill areas.
|
374 |
+
|
375 |
+
# Arguments
|
376 |
+
fillmap: an image.
|
377 |
+
max_iter: max iteration number.
|
378 |
+
# Returns
|
379 |
+
an image.
|
380 |
+
"""
|
381 |
+
max_height, max_width = fillmap.shape[:2]
|
382 |
+
result = fillmap.copy()
|
383 |
+
|
384 |
+
for i in range(max_iter):
|
385 |
+
print('merge ' + str(i + 1))
|
386 |
+
|
387 |
+
result[np.where(fillmap == 0)] = 0
|
388 |
+
|
389 |
+
fill_id = np.unique(result.flatten())
|
390 |
+
fills = []
|
391 |
+
|
392 |
+
for j in fill_id:
|
393 |
+
point = np.where(result == j)
|
394 |
+
|
395 |
+
fills.append({
|
396 |
+
'id': j,
|
397 |
+
'point': point,
|
398 |
+
'area': len(point[0]),
|
399 |
+
})
|
400 |
+
|
401 |
+
for j, f in enumerate(fills):
|
402 |
+
# ignore lines
|
403 |
+
if f['id'] == 0:
|
404 |
+
continue
|
405 |
+
|
406 |
+
if f['area'] < 5:
|
407 |
+
result[f['point']] = 0
|
408 |
+
|
409 |
+
if len(fill_id) == len(np.unique(result.flatten())):
|
410 |
+
break
|
411 |
+
|
412 |
+
return result
|
413 |
+
|
414 |
+
|
415 |
+
def merge_one(fillmap):
|
416 |
+
result = fillmap.copy()
|
417 |
+
print('merge')
|
418 |
+
result[np.where(fillmap == 0)] = 0
|
419 |
+
fill_id = np.unique(result.flatten())
|
420 |
+
fills = []
|
421 |
+
for j in fill_id:
|
422 |
+
point = np.where(result == j)
|
423 |
+
fills.append({
|
424 |
+
'id': j,
|
425 |
+
'point': point,
|
426 |
+
'area': len(point[0]),
|
427 |
+
})
|
428 |
+
for j, f in enumerate(fills):
|
429 |
+
# ignore lines
|
430 |
+
if f['id'] == 0:
|
431 |
+
continue
|
432 |
+
|
433 |
+
if f['area'] < 5:
|
434 |
+
result[f['point']] = 0
|
435 |
+
return result
|
436 |
+
|
models.py
ADDED
@@ -0,0 +1,299 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from keras.layers import Conv2D, Activation, Input, Concatenate, LeakyReLU, Lambda, AveragePooling2D, UpSampling2D, Convolution2D, BatchNormalization, Deconvolution2D, Add
|
2 |
+
from keras.models import Model
|
3 |
+
from InstanceNorm import InstanceNormalization
|
4 |
+
|
5 |
+
|
6 |
+
def make_standard_UNET(channels,outs):
|
7 |
+
|
8 |
+
def relu(x):
|
9 |
+
return Activation('relu')(x)
|
10 |
+
|
11 |
+
def concat(x):
|
12 |
+
return Concatenate()(x)
|
13 |
+
|
14 |
+
c0 = Convolution2D(filters=32, kernel_size=3, strides=1, padding='same', name='c0')
|
15 |
+
c1 = Convolution2D(filters=64, kernel_size=4, strides=2, padding='same', name='c1')
|
16 |
+
c2 = Convolution2D(filters=64, kernel_size=3, strides=1, padding='same', name='c2')
|
17 |
+
c3 = Convolution2D(filters=128, kernel_size=4, strides=2, padding='same', name='c3')
|
18 |
+
c4 = Convolution2D(filters=128, kernel_size=3, strides=1, padding='same', name='c4')
|
19 |
+
c5 = Convolution2D(filters=256, kernel_size=4, strides=2, padding='same', name='c5')
|
20 |
+
c6 = Convolution2D(filters=256, kernel_size=3, strides=1, padding='same', name='c6')
|
21 |
+
c7 = Convolution2D(filters=512, kernel_size=4, strides=2, padding='same', name='c7')
|
22 |
+
c8 = Convolution2D(filters=512, kernel_size=3, strides=1, padding='same', name='c8')
|
23 |
+
|
24 |
+
bnc0 = BatchNormalization(axis=3, name='bnc0')
|
25 |
+
bnc1 = BatchNormalization(axis=3, name='bnc1')
|
26 |
+
bnc2 = BatchNormalization(axis=3, name='bnc2')
|
27 |
+
bnc3 = BatchNormalization(axis=3, name='bnc3')
|
28 |
+
bnc4 = BatchNormalization(axis=3, name='bnc4')
|
29 |
+
bnc5 = BatchNormalization(axis=3, name='bnc5')
|
30 |
+
bnc6 = BatchNormalization(axis=3, name='bnc6')
|
31 |
+
bnc7 = BatchNormalization(axis=3, name='bnc7')
|
32 |
+
bnc8 = BatchNormalization(axis=3, name='bnc8')
|
33 |
+
|
34 |
+
dc8 = Deconvolution2D(filters=512, kernel_size=4, strides=2, padding='same', name='dc8_')
|
35 |
+
dc7 = Convolution2D(filters=256, kernel_size=3, strides=1, padding='same', name='dc7')
|
36 |
+
dc6 = Deconvolution2D(filters=256, kernel_size=4, strides=2, padding='same', name='dc6_')
|
37 |
+
dc5 = Convolution2D(filters=128, kernel_size=3, strides=1, padding='same', name='dc5')
|
38 |
+
dc4 = Deconvolution2D(filters=128, kernel_size=4, strides=2, padding='same', name='dc4_')
|
39 |
+
dc3 = Convolution2D(filters=64, kernel_size=3, strides=1, padding='same', name='dc3')
|
40 |
+
dc2 = Deconvolution2D(filters=64, kernel_size=4, strides=2, padding='same', name='dc2_')
|
41 |
+
dc1 = Convolution2D(filters=32, kernel_size=3, strides=1, padding='same', name='dc1')
|
42 |
+
dc0 = Convolution2D(filters=outs, kernel_size=3, strides=1, padding='same', name='dc0')
|
43 |
+
|
44 |
+
bnd1 = BatchNormalization(axis=3, name='bnd1')
|
45 |
+
bnd2 = BatchNormalization(axis=3, name='bnd2')
|
46 |
+
bnd3 = BatchNormalization(axis=3, name='bnd3')
|
47 |
+
bnd4 = BatchNormalization(axis=3, name='bnd4')
|
48 |
+
bnd5 = BatchNormalization(axis=3, name='bnd5')
|
49 |
+
bnd6 = BatchNormalization(axis=3, name='bnd6')
|
50 |
+
bnd7 = BatchNormalization(axis=3, name='bnd7')
|
51 |
+
bnd8 = BatchNormalization(axis=3, name='bnd8')
|
52 |
+
|
53 |
+
x = Input(shape=(128, 128, channels))
|
54 |
+
|
55 |
+
e0 = relu(bnc0(c0(x), training = False))
|
56 |
+
e1 = relu(bnc1(c1(e0), training = False))
|
57 |
+
e2 = relu(bnc2(c2(e1), training = False))
|
58 |
+
e3 = relu(bnc3(c3(e2), training = False))
|
59 |
+
e4 = relu(bnc4(c4(e3), training = False))
|
60 |
+
e5 = relu(bnc5(c5(e4), training = False))
|
61 |
+
e6 = relu(bnc6(c6(e5), training = False))
|
62 |
+
e7 = relu(bnc7(c7(e6), training = False))
|
63 |
+
e8 = relu(bnc8(c8(e7), training = False))
|
64 |
+
|
65 |
+
d8 = relu(bnd8(dc8(concat([e7, e8])), training = False))
|
66 |
+
d7 = relu(bnd7(dc7(d8), training = False))
|
67 |
+
d6 = relu(bnd6(dc6(concat([e6, d7])), training = False))
|
68 |
+
d5 = relu(bnd5(dc5(d6), training = False))
|
69 |
+
d4 = relu(bnd4(dc4(concat([e4, d5])), training = False))
|
70 |
+
d3 = relu(bnd3(dc3(d4), training = False))
|
71 |
+
d2 = relu(bnd2(dc2(concat([e2, d3])), training = False))
|
72 |
+
d1 = relu(bnd1(dc1(d2), training = False))
|
73 |
+
d0 = dc0(concat([e0, d1]))
|
74 |
+
|
75 |
+
model = Model(inputs=x,outputs=d0)
|
76 |
+
|
77 |
+
return model
|
78 |
+
|
79 |
+
|
80 |
+
def make_diff_net():
|
81 |
+
|
82 |
+
def conv(x, filters, name):
|
83 |
+
return Conv2D(filters=filters, strides=(1, 1), kernel_size=(3, 3), padding='same', name=name)(x)
|
84 |
+
|
85 |
+
def relu(x):
|
86 |
+
return Activation('relu')(x)
|
87 |
+
|
88 |
+
def lrelu(x):
|
89 |
+
return LeakyReLU(alpha=0.1)(x)
|
90 |
+
|
91 |
+
def r_block(x, filters, name=None):
|
92 |
+
return relu(conv(relu(conv(x, filters, None if name is None else name + '_c1')), filters,
|
93 |
+
None if name is None else name + '_c2'))
|
94 |
+
|
95 |
+
def cat(a, b):
|
96 |
+
return Concatenate()([UpSampling2D((2, 2))(a), b])
|
97 |
+
|
98 |
+
def dog(x):
|
99 |
+
down = AveragePooling2D((2, 2))(x)
|
100 |
+
up = UpSampling2D((2, 2))(down)
|
101 |
+
diff = Lambda(lambda p: p[0] - p[1])([x, up])
|
102 |
+
return down, diff
|
103 |
+
|
104 |
+
ip = Input(shape=(512, 512, 3))
|
105 |
+
|
106 |
+
c512 = r_block(ip, 16, 'c512')
|
107 |
+
|
108 |
+
c256, l512 = dog(c512)
|
109 |
+
c256 = r_block(c256, 32, 'c256')
|
110 |
+
|
111 |
+
c128, l256 = dog(c256)
|
112 |
+
c128 = r_block(c128, 64, 'c128')
|
113 |
+
|
114 |
+
c64, l128 = dog(c128)
|
115 |
+
c64 = r_block(c64, 128, 'c64')
|
116 |
+
|
117 |
+
c32, l64 = dog(c64)
|
118 |
+
c32 = r_block(c32, 256, 'c32')
|
119 |
+
|
120 |
+
c16, l32 = dog(c32)
|
121 |
+
c16 = r_block(c16, 512, 'c16')
|
122 |
+
|
123 |
+
d32 = cat(c16, l32)
|
124 |
+
d32 = r_block(d32, 256, 'd32')
|
125 |
+
|
126 |
+
d64 = cat(d32, l64)
|
127 |
+
d64 = r_block(d64, 128, 'd64')
|
128 |
+
|
129 |
+
d128 = cat(d64, l128)
|
130 |
+
d128 = r_block(d128, 64, 'd128')
|
131 |
+
|
132 |
+
d256 = cat(d128, l256)
|
133 |
+
d256 = r_block(d256, 32, 'd256')
|
134 |
+
|
135 |
+
d512 = cat(d256, l512)
|
136 |
+
d512 = r_block(d512, 16, 'd512')
|
137 |
+
|
138 |
+
op = conv(d512, 1, 'op')
|
139 |
+
|
140 |
+
return Model(inputs=ip, outputs=op)
|
141 |
+
|
142 |
+
|
143 |
+
def make_wnet256():
|
144 |
+
|
145 |
+
def conv(x, filters):
|
146 |
+
return Conv2D(filters=filters, strides=(1, 1), kernel_size=(3, 3), padding='same')(x)
|
147 |
+
|
148 |
+
def relu(x):
|
149 |
+
return Activation('relu')(x)
|
150 |
+
|
151 |
+
def lrelu(x):
|
152 |
+
return LeakyReLU(alpha=0.1)(x)
|
153 |
+
|
154 |
+
def r_block(x, filters):
|
155 |
+
return relu(conv(relu(conv(x, filters)), filters))
|
156 |
+
|
157 |
+
def res_block(x, filters):
|
158 |
+
return relu(Add()([x, conv(relu(conv(x, filters)), filters)]))
|
159 |
+
|
160 |
+
def cat(a, b):
|
161 |
+
return Concatenate()([UpSampling2D((2, 2))(a), b])
|
162 |
+
|
163 |
+
def dog(x):
|
164 |
+
down = AveragePooling2D((2, 2))(x)
|
165 |
+
up = UpSampling2D((2, 2))(down)
|
166 |
+
diff = Lambda(lambda p: p[0] - p[1])([x, up])
|
167 |
+
return down, diff
|
168 |
+
|
169 |
+
ip_sketch = Input(shape=(256, 256, 1))
|
170 |
+
ip_color = Input(shape=(256, 256, 3))
|
171 |
+
|
172 |
+
c256 = r_block(ip_sketch, 32)
|
173 |
+
|
174 |
+
c128, l256 = dog(c256)
|
175 |
+
c128 = r_block(c128, 64)
|
176 |
+
|
177 |
+
c64, l128 = dog(c128)
|
178 |
+
c64 = r_block(c64, 128)
|
179 |
+
|
180 |
+
c32, l64 = dog(c64)
|
181 |
+
c32 = r_block(Concatenate()([c32, AveragePooling2D((8, 8))(ip_color)]), 256)
|
182 |
+
|
183 |
+
c32 = res_block(c32, 256)
|
184 |
+
c32 = res_block(c32, 256)
|
185 |
+
c32 = res_block(c32, 256)
|
186 |
+
|
187 |
+
c32 = res_block(c32, 256)
|
188 |
+
c32 = res_block(c32, 256)
|
189 |
+
c32 = res_block(c32, 256)
|
190 |
+
|
191 |
+
c32 = res_block(c32, 256)
|
192 |
+
c32 = res_block(c32, 256)
|
193 |
+
c32 = res_block(c32, 256)
|
194 |
+
|
195 |
+
d64 = cat(c32, l64)
|
196 |
+
d64 = r_block(d64, 128)
|
197 |
+
|
198 |
+
d128 = cat(d64, l128)
|
199 |
+
d128 = r_block(d128, 64)
|
200 |
+
|
201 |
+
d256 = cat(d128, l256)
|
202 |
+
d256 = r_block(d256, 32)
|
203 |
+
|
204 |
+
op = conv(d256, 3)
|
205 |
+
|
206 |
+
return Model(inputs=[ip_sketch, ip_color], outputs=op)
|
207 |
+
|
208 |
+
|
209 |
+
def make_unet512():
|
210 |
+
|
211 |
+
def conv(x, filters, strides=(1, 1), kernel_size=(3, 3)):
|
212 |
+
return Conv2D(filters=filters, strides=strides, kernel_size=kernel_size, padding='same')(x)
|
213 |
+
|
214 |
+
def donv(x, filters, strides=(2, 2), kernel_size=(4, 4)):
|
215 |
+
return Deconvolution2D(filters=filters, strides=strides, kernel_size=kernel_size, padding='same')(x)
|
216 |
+
|
217 |
+
def relu(x):
|
218 |
+
return Activation('relu')(x)
|
219 |
+
|
220 |
+
def sigmoid(x):
|
221 |
+
return Activation('sigmoid')(x)
|
222 |
+
|
223 |
+
def norm(x):
|
224 |
+
return InstanceNormalization(axis=3)(x)
|
225 |
+
|
226 |
+
def cat(a, b):
|
227 |
+
return Concatenate()([a, b])
|
228 |
+
|
229 |
+
def res(x, filters):
|
230 |
+
c1 = relu(norm(conv(x, filters // 2)))
|
231 |
+
c2 = norm(conv(c1, filters))
|
232 |
+
ad = Add()([x, c2])
|
233 |
+
return relu(ad)
|
234 |
+
|
235 |
+
ip = Input(shape=(512, 512, 3))
|
236 |
+
|
237 |
+
c512 = relu(norm(conv(ip, 16, strides=(1, 1), kernel_size=(3, 3))))
|
238 |
+
c256 = relu(norm(conv(c512, 32, strides=(2, 2), kernel_size=(4, 4))))
|
239 |
+
|
240 |
+
c128 = relu(norm(conv(c256, 64, strides=(2, 2), kernel_size=(4, 4))))
|
241 |
+
c128 = res(c128, 64)
|
242 |
+
|
243 |
+
c64 = relu(norm(conv(c128, 128, strides=(2, 2), kernel_size=(4, 4))))
|
244 |
+
c64 = res(c64, 128)
|
245 |
+
c64 = res(c64, 128)
|
246 |
+
|
247 |
+
c32 = relu(norm(conv(c64, 256, strides=(2, 2), kernel_size=(4, 4))))
|
248 |
+
c32 = res(c32, 256)
|
249 |
+
c32 = res(c32, 256)
|
250 |
+
c32 = res(c32, 256)
|
251 |
+
c32 = res(c32, 256)
|
252 |
+
c32 = res(c32, 256)
|
253 |
+
c32 = res(c32, 256)
|
254 |
+
c32 = res(c32, 256)
|
255 |
+
c32 = res(c32, 256)
|
256 |
+
|
257 |
+
c16 = relu(norm(conv(c32, 512, strides=(2, 2), kernel_size=(4, 4))))
|
258 |
+
c16 = res(c16, 512)
|
259 |
+
c16 = res(c16, 512)
|
260 |
+
c16 = res(c16, 512)
|
261 |
+
c16 = res(c16, 512)
|
262 |
+
c16 = res(c16, 512)
|
263 |
+
c16 = res(c16, 512)
|
264 |
+
c16 = res(c16, 512)
|
265 |
+
c16 = res(c16, 512)
|
266 |
+
|
267 |
+
c8 = relu(norm(conv(c16, 1024, strides=(2, 2), kernel_size=(4, 4))))
|
268 |
+
c8 = res(c8, 1024)
|
269 |
+
c8 = res(c8, 1024)
|
270 |
+
c8 = res(c8, 1024)
|
271 |
+
c8 = res(c8, 1024)
|
272 |
+
|
273 |
+
e16 = relu(norm(donv(c8, 512, strides=(2, 2), kernel_size=(4, 4))))
|
274 |
+
e16 = cat(e16, c16)
|
275 |
+
e16 = relu(norm(conv(e16, 512, strides=(1, 1), kernel_size=(3, 3))))
|
276 |
+
|
277 |
+
e32 = relu(norm(donv(e16, 256, strides=(2, 2), kernel_size=(4, 4))))
|
278 |
+
e32 = cat(e32, c32)
|
279 |
+
e32 = relu(norm(conv(e32, 256, strides=(1, 1), kernel_size=(3, 3))))
|
280 |
+
|
281 |
+
e64 = relu(norm(donv(e32, 128, strides=(2, 2), kernel_size=(4, 4))))
|
282 |
+
e64 = cat(e64, c64)
|
283 |
+
e64 = relu(norm(conv(e64, 128, strides=(1, 1), kernel_size=(3, 3))))
|
284 |
+
|
285 |
+
e128 = relu(norm(donv(e64, 64, strides=(2, 2), kernel_size=(4, 4))))
|
286 |
+
e128 = cat(e128, c128)
|
287 |
+
e128 = relu(norm(conv(e128, 64, strides=(1, 1), kernel_size=(3, 3))))
|
288 |
+
|
289 |
+
e256 = relu(norm(donv(e128, 32, strides=(2, 2), kernel_size=(4, 4))))
|
290 |
+
e256 = cat(e256, c256)
|
291 |
+
e256 = relu(norm(conv(e256, 32, strides=(1, 1), kernel_size=(3, 3))))
|
292 |
+
|
293 |
+
e512 = relu(norm(donv(e256, 16, strides=(2, 2), kernel_size=(4, 4))))
|
294 |
+
e512 = cat(e512, c512)
|
295 |
+
e512 = relu(norm(conv(e512, 16, strides=(1, 1), kernel_size=(3, 3))))
|
296 |
+
|
297 |
+
ot = sigmoid(conv(e512, 1))
|
298 |
+
|
299 |
+
return Model(inputs=ip, outputs=ot)
|
nets/head.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:fd5f608bf5511293cd582e6d87e55f483259b8d606feba7a5042382a19cde630
|
3 |
+
size 411622416
|
nets/inception.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:9a503841e955ee56f2e0b71219952f0f554c9eaff9e887f82853f600f0ddee80
|
3 |
+
size 41501888
|
nets/mat.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:a2998a3cd6446ad62d87259dd5b279e344a9ba8daaf2c3c88f59ebb5ddfe09cd
|
3 |
+
size 362262288
|
nets/neck.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:e91e4e688379dc95a14ea8f33fa1c2141584b0387e16be5efcc7acb5299ad5d7
|
3 |
+
size 411623808
|
nets/norm.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:08d230c0a595dc21d2924911b2d2aa9f334c2180a6526d947d11a4d00e5ce4d0
|
3 |
+
size 113030088
|
nets/reader.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5a65a53091acb96dbdcf325afb1b233ff374e220cb822fe8b1771dcd65f999df
|
3 |
+
size 41502832
|
nets/render_head.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:cacac78d2e19bd38b2efbdd8eb372594e333e5284e9f2c2b605652c8dd26ffad
|
3 |
+
size 411612752
|
nets/render_neck.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:66050656839363d7977dd970cc2f6d7f81722a8cf5cd67a1793bae8214a07c0b
|
3 |
+
size 411613336
|
nets/tail.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2255073ccdc311d8c61c074ffeb4ad7db200ca9116011e1cf213d6d4b1967e15
|
3 |
+
size 4018208
|
nets/vector.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2738164810e2c998c4e5c7598c9ad991b9aad6c300d075119da9e4201212066a
|
3 |
+
size 31618276
|
nets/vgg7.net
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:84539cc2f46a0937a31ff770af592242e1e2a06f9f3c3fd285ab596a7ab90be6
|
3 |
+
size 1188680
|
refs/1.png
ADDED
Git LFS Details
|
refs/10.png
ADDED
Git LFS Details
|
refs/11.png
ADDED
Git LFS Details
|
refs/12.png
ADDED
Git LFS Details
|
refs/13.png
ADDED
Git LFS Details
|
refs/14.png
ADDED
Git LFS Details
|
refs/15.png
ADDED
Git LFS Details
|
refs/16.png
ADDED
Git LFS Details
|
refs/17.png
ADDED
Git LFS Details
|
refs/18.png
ADDED
Git LFS Details
|
refs/19.png
ADDED
Git LFS Details
|
refs/2.png
ADDED
Git LFS Details
|
refs/20.png
ADDED
Git LFS Details
|
refs/21.png
ADDED
Git LFS Details
|
refs/22.png
ADDED
Git LFS Details
|
refs/23.png
ADDED
Git LFS Details
|
refs/24.png
ADDED
Git LFS Details
|
refs/25.png
ADDED
Git LFS Details
|
refs/26.png
ADDED
Git LFS Details
|
refs/27.png
ADDED
Git LFS Details
|
refs/28.png
ADDED
Git LFS Details
|
refs/29.png
ADDED
Git LFS Details
|
refs/3.png
ADDED
Git LFS Details
|
refs/30.png
ADDED
Git LFS Details
|
refs/31.png
ADDED
Git LFS Details
|
refs/32.png
ADDED
Git LFS Details
|
refs/4.png
ADDED
Git LFS Details
|
refs/5.png
ADDED
Git LFS Details
|