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
|