-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathmultiResolution_textureSynthesis.py
304 lines (256 loc) · 11.9 KB
/
multiResolution_textureSynthesis.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#imports
from skimage import io
from skimage.transform import pyramid_gaussian, pyramid_laplacian
from skimage.transform.pyramids import resize
from sklearn.neighbors import KDTree
import numpy as np
import matplotlib.pyplot as plt
import cv2
from math import floor, ceil
from IPython.display import clear_output
from PIL import Image
import os
import random
import json
def multiResolution_textureSynthesis(parms, userExample = None):
#check if save dir exists
checkIfDirectoryExists(parms["saveImgsPath"])
#write params
saveParms(parms)
#1. load example image and build pyramids
if parms["pyramidType"] == "gaussian":
exampleMap = gaussianPyramid(loadExampleMap(parms["exampleMapPath"]), levels = parms["pyramidLevels"])
canvas = gaussianPyramid(initCanvas(parms["outputSize"]), levels = exampleMap.levels)
elif parms["pyramidType"] == "laplacian":
exampleMap = laplacianPyramid(loadExampleMap(parms["exampleMapPath"]), levels = parms["pyramidLevels"])
canvas = laplacianPyramid(initCanvas(parms["outputSize"]), levels = exampleMap.levels)
else:
raise Exception('Please, use either "gaussian" or "laplacian" for pyramidType')
#track what has been filed
filledMap = gaussianPyramid(initCanvas(parms["outputSize"]), levels = exampleMap.levels)
#check if we have user example
if userExample is not None:
#resize user user example to be same size level 0
userExampleImg = loadExampleMap(userExample["userExamplePath"])
userExampleImg = resize(userExampleImg, np.shape(canvas.pyramid[0]))
#copy to level 0
canvas.pyramid[0], filledMap.pyramid[0] = copyMap2Map(exampleMap.pyramid[0], canvas.pyramid[0], filledMap.pyramid[0])
canvas.pyramid[0] = userExampleImg
else:
#random init level 0
randomRow2Map(exampleMap.pyramid[0], canvas.pyramid[0], filledMap.pyramid[0], 2)
#2. main resolve loop
index = 0
Cs = []
for pLvl in range(0, canvas.levels+1):
rows, cols, _ = np.shape(canvas.pyramid[pLvl])
#build kD-tree for this level
kD, samples = kDtree(exampleMap.pyramid, pLvl, parms)
for r in range(rows):
for c in range(cols):
#check if not already resolved
if filledMap.pyramid[pLvl][r, c][0] == 0:
C = findBestMatchCoord(canvas, exampleMap, kD, pLvl, [r,c], parms, samples, k=min(samples, 1))
canvas.pyramid[pLvl][r, c] = exampleMap.pyramid[pLvl][C[0], C[1]]
filledMap.pyramid[pLvl][r, c] = np.ones((3,))
print(pLvl, " > ", canvas.levels)
print(r, " > ", rows)
#SAVE IMAGE EVERY ROW
showLiveUpdate(canvas, exampleMap, parms["pyramidType"])
saveImg(canvas, parms["pyramidType"], pLvl, parms["saveImgsPath"], index)
index += 1
#copy for visualization purposes (only if gaussian)
if parms["pyramidType"] == "gaussian":
if pLvl+1<=canvas.levels:
canvas.pyramid[pLvl+1] = visualizeNextLevel(canvas.pyramid[pLvl], canvas.pyramid[pLvl+1], filledMap.pyramid[pLvl+1])
def visualizeNextLevel(prevLevel, nextLevel, filledMapNextLevel):
rows, cols, _ = np.shape(nextLevel)
img = Image.fromarray(np.uint8(prevLevel*255))
img = img.resize((cols, rows), resample=0, box=None)
img = np.asarray(img)/255.0
return img * (1-filledMapNextLevel) + nextLevel * filledMapNextLevel
def saveParms(parms):
path = parms["saveImgsPath"] + 'parms.txt'
with open(path, 'w') as file:
file.write(json.dumps(parms))
def resize(in_img, size):
img = Image.fromarray(np.uint8(in_img*255))
img = img.resize((size[1], size[0]), resample=0, box=None)
return np.asarray(img)/255.0
def randomRow2Map(copyFrom, copyTo, filledMap, rowsToCopy):
rowsEx, colsEx, _ = np.shape(copyFrom)
rows, cols, _ = np.shape(copyTo)
rand_r = random.randint(ceil((rowsEx - rowsToCopy)/4), int(rowsEx - rowsToCopy))
rand_c = random.randint(0, int(colsEx/2))
copyTo[0:rowsToCopy,0:int(colsEx/2)] = copyFrom[rand_r:rand_r+rowsToCopy, rand_c:rand_c+int(colsEx/2)]
filledMap[0:rowsToCopy,0:int(colsEx/2)] = 1
return copyTo, filledMap
def copyMap2Map(copyFrom, copyTo, filledMap):
rows, cols, _ = np.shape(copyTo)
img = Image.fromarray(np.uint8(copyFrom*255))
img = img.resize((cols, rows), resample=0, box=None)
copyTo = np.asarray(img)/255.0
filledMap = np.ones(np.shape(filledMap))
return copyTo, filledMap
def showLiveUpdate(canvas, exampleMap, pyramidType):
#show live update
if pyramidType=="gaussian":
fig=plt.figure(figsize=(30, 30))
fig_cols = len(canvas.pyramid)
for c in range(1,fig_cols+1):
fig.add_subplot(7, fig_cols, c)
plt.imshow(canvas.pyramid[c-1])
fig.add_subplot(7, fig_cols, c + fig_cols)
plt.imshow(exampleMap.pyramid[c-1])
else:
fig=plt.figure(figsize=(30, 30))
fig_cols = len(canvas.pyramid) + 1
for c in range(1,fig_cols):
fig.add_subplot(7, fig_cols, c)
plt.imshow(canvas.pyramid[c-1])
fig.add_subplot(7, fig_cols, c + fig_cols)
plt.imshow(exampleMap.pyramid[c-1])
fig.add_subplot(7, fig_cols, fig_cols)
plt.imshow(canvas.reconstruct())
fig.add_subplot(7, fig_cols, fig_cols*2)
plt.imshow(exampleMap.reconstruct())
clear_output(wait=True)
display(plt.show())
def saveImg(canvas, pyramidType, pyramidLevel, savePath, index):
#save img
if pyramidType == "gaussian":
img = Image.fromarray(np.uint8(canvas.pyramid[pyramidLevel]*255))
else:
img = Image.fromarray(np.uint8(np.clip(canvas.reconstruct(), 0.0, 1.0)*255))
img = img.resize((300, 300), resample=0, box=None)
img.save(savePath+ str(index) + '.jpg')
def checkIfDirectoryExists(path):
if not os.path.exists(path):
os.makedirs(path)
def getSingleLevelNeighbourhood(pyramid, level, coord, kernel, mode):
if mode=='parent':
coord[0] = floor(coord[0]/2)
coord[1] = floor(coord[1]/2)
half_kernel = floor(kernel / 2)
#pad the image
padded = padding(pyramid[level], half_kernel)
#get neighbourhood
shifted_row = coord[0] + half_kernel
shifted_col = coord[1] + half_kernel
row_start = shifted_row - half_kernel
row_end = shifted_row + half_kernel + 1
col_start = shifted_col - half_kernel
col_end = shifted_col + half_kernel + 1
padded = padded[row_start:row_end, col_start:col_end]
if mode=='parent':
return padded.reshape(np.shape(padded)[0]*np.shape(padded)[1], 3)
if mode=='child': #then return only the half of the neighbourhood which was already resolved (we are going in scan-like order)
return padded.reshape(np.shape(padded)[0]*np.shape(padded)[1], 3)[0:floor(kernel*kernel/2), :]
def getNeighbourhood(pyramid, pyramidLevel, coord, parms, mirror_hor=False, mirror_vert=False):
Nchild = getSingleLevelNeighbourhood(pyramid, pyramidLevel, coord, parms["child_kernel_size"], mode='child')
if pyramidLevel-1>=0:
Nparent = getSingleLevelNeighbourhood(pyramid, pyramidLevel-1, coord, parms["parent_kernel_size"], mode='parent')
else:
Nparent = np.zeros((parms["parent_kernel_size"] * parms["parent_kernel_size"], 3))
#combine into a single neighbourhood
N = np.concatenate((Nchild, Nparent), axis=0)
return N
def findBestMatchCoord(canvas, exampleMap, kD, pyramidLevel, coord, parms, samples, k = 4):
N = getNeighbourhood(canvas.pyramid, pyramidLevel, coord, parms)
#find best neighbours
dist, ind = kD.query([N.reshape(-1)], k=k)
dist = dist[0]
ind = ind[0]
#choose random valid patch
#PARM_truncation = 0.8
#PARM_attenuation = 2
#probabilities = distances2probability(dist, PARM_truncation, PARM_attenuation)
chosen = ind[0] #np.random.choice(ind, 1, p=probabilities)[0]
#make flat coord id to 2d coord
chosenCoord = id2coord(chosen, np.shape(exampleMap.pyramid[pyramidLevel]))
return chosenCoord
def id2coord(coordFlat, imgSize):
row = floor(coordFlat / imgSize[1])
col = coordFlat - row * imgSize[1]
return [row, col]
def kDtree(pyramid, pyramidlevel, parms):
#get all the possible neighbourhood "coordinates/samples":
rows, cols, _ = np.shape(pyramid[pyramidlevel])
samples = []
for r in range(rows):
for c in range(cols):
N = getNeighbourhood(pyramid, pyramidlevel, [r, c], parms)
samples.append(N.reshape(-1))
#N = getNeighbourhood(pyramid, pyramidlevel, [r, c], parms, mirror_hor=True)
#samples.append(N.reshape(-1))
#N = getNeighbourhood(pyramid, pyramidlevel, [r, c], parms, mirror_vert=True)
#samples.append(N.reshape(-1))
return KDTree(samples), int(len(samples))
def distances2probability(distances, PARM_truncation, PARM_attenuation):
probabilities = 1 - distances / np.max(distances)
probabilities *= (probabilities > PARM_truncation)
probabilities = pow(probabilities, PARM_attenuation) #attenuate the values
#check if we didn't truncate everything!
if np.sum(probabilities) == 0:
#then just revert it
probabilities = 1 - distances / np.max(distances)
probabilities *= (probabilities > PARM_truncation*np.max(probabilities)) # truncate the values (we want top truncate%)
probabilities = pow(probabilities, PARM_attenuation)
probabilities /= np.sum(probabilities) #normalize so they add up to one
return probabilities
def padding(img, pad):
npad = ((pad, pad), (pad, pad), (0, 0))
return np.lib.pad(img, npad, "constant", constant_values=0) #constant_values=127 'wrap') #,
def initCanvas(size):
return np.zeros((size[0], size[1], 3), dtype="float64")
def loadExampleMap(exampleMapPath):
exampleMap = io.imread(exampleMapPath) #returns an MxNx3 array
exampleMap = exampleMap / 255.0 #normalize
#make sure it is 3channel RGB
if (np.shape(exampleMap)[-1] > 3):
exampleMap = exampleMap[:,:,:3] #remove Alpha Channel
elif (len(np.shape(exampleMap)) == 2):
exampleMap = np.repeat(exampleMap[np.newaxis, :, :], 3, axis=0) #convert from Grayscale to RGB
return exampleMap
class gaussianPyramid:
def __init__(self, in_img, levels = None):
self.levels = levels
self.pyramid = self.build(in_img)
def build(self, img):
G = img.copy()
gP = [G]
if self.levels is None:
self.levels = 0
#loop until we have only 1x1 map
while np.shape(G)[0] > 1:
G = cv2.pyrDown(G)
gP.insert(0,G)
self.levels += 1
else:
for i in range(self.levels):
G = cv2.pyrDown(G)
gP.insert(0,G)
return gP
def reconstruct(self):
return self.pyramid[-1]
class laplacianPyramid:
def __init__(self, in_img, levels = None):
self.levels = levels
self.pyramid = self.build(in_img)
def build(self, img):
gpB = gaussianPyramid(img, self.levels)
if self.levels is None:
self.levels = gpB.levels
gpB = gpB.pyramid
lpB = [gpB[0]]
for i in range(0,len(gpB)-1):
GE = cv2.pyrUp(gpB[i])
L = gpB[i+1] - GE
lpB.append(L)
return lpB
def reconstruct(self):
ls_ = self.pyramid[0].copy()
for i in range(1,len(self.pyramid)):
ls_ = cv2.pyrUp(ls_.astype('float64'))
ls_ = ls_ + self.pyramid[i]
return ls_