Amirhosein commited on
Commit
eb9da7f
·
1 Parent(s): 2177524
Map.py ADDED
@@ -0,0 +1,493 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import pandas as pd
4
+ import random
5
+ from functools import singledispatchmethod
6
+ import math
7
+ import os
8
+
9
+ from sympy import false
10
+ import config
11
+
12
+ class MapIn:
13
+ # thickness configs
14
+ tree_safe_dist = config.TREE_SAFE_DIST
15
+ facility_safe_dist = config.FACILITY_SAFE_DIST
16
+ # parcel min area
17
+ parcel_minimum_area = config.AXIS_MIN_AREA
18
+ # ratio of access to boundry to stop halving
19
+ access_ratio = config.ACCESS_RATIO
20
+
21
+ """
22
+ map class to handle raw inputs
23
+ RGB (round access, green factor, boundary)
24
+ Background is white by deafult
25
+ Black shows fixed facilities
26
+ """
27
+ @singledispatchmethod
28
+ def __init__(self) -> None:
29
+ assert False, 'bad input'
30
+ @__init__.register(str)
31
+ def _first__(self,src:str,src_block:str,src_ff:str,parcel_cnt:int,arch_choice:config.ArchStyles,map_id:int) -> None:
32
+ self.roud_thickness = config.ROAD_SIZE_MAX
33
+ self.map_id = map_id
34
+ self.frame = cv2.imread(src)
35
+ self.frame_shape = self.frame.shape
36
+ self.arch_choice = arch_choice
37
+ self.parcel_cnt = parcel_cnt
38
+ self.centers = (int(self.frame_shape[0]/2),int(self.frame_shape[1]/2))
39
+ self.trees_mask, self.fixed_f_mask,self.access_mask, self.boundry_mask = self.create_masks()
40
+ self.trees_binary_mask = cv2.threshold(self.trees_mask, 127, 255, cv2.THRESH_BINARY)[1]
41
+ # read block mask
42
+ self.block_mask = cv2.imread(src_block)
43
+ self.block_mask = cv2.cvtColor(self.block_mask,cv2.COLOR_BGR2GRAY)
44
+ # read facility filled mask
45
+ self.facility_filled_mask = cv2.imread(src_ff)
46
+ self.facility_filled_mask = cv2.cvtColor(self.facility_filled_mask,cv2.COLOR_BGR2GRAY)
47
+ self.print_report()
48
+
49
+ # Axis maps initialization
50
+ @__init__.register(np.ndarray)
51
+ def _second__(self,split_mask:np.ndarray,parent_map,line_mask:np.ndarray,map_id:int,dir:int,line_p) -> None:
52
+ self.line_p = line_p
53
+ # self.axis_center = parent_map.axis_center
54
+ self.split_mask = split_mask
55
+ self.dir = dir # 0 up 1 down
56
+ self.roud_thickness = config.ROAD_SIZE_MAX - config.ROAD_STEP*int(math.log(map_id+1,2))
57
+ if self.roud_thickness <= config.ROAD_STEP: self.roud_thickness=config.ROAD_SIZE_MIN
58
+ config.log(f"roud thickness:{self.roud_thickness} map_id:{map_id}")
59
+ self.map_id = map_id
60
+ # split_mask_3d = np.zeros((self.frame_shape))
61
+ # self.frame = parent_map.frame & split_mask
62
+ split_3d_mask = np.zeros(parent_map.frame_shape, dtype=np.uint8)
63
+ split_3d_mask[:,:,:] = split_mask[:,:,np.newaxis]
64
+ self.frame = parent_map.frame & split_3d_mask
65
+ self.frame_shape = self.frame.shape
66
+ self.arch_choice = parent_map.arch_choice
67
+ self.centers = (int(self.frame_shape[0]/2),int(self.frame_shape[1]/2))
68
+ self.trees_mask = parent_map.trees_mask & split_mask
69
+ self.trees_binary_mask = parent_map.trees_binary_mask & split_mask
70
+ self.fixed_f_mask = parent_map.fixed_f_mask & split_mask
71
+ self.boundry_mask = parent_map.boundry_mask & split_mask
72
+ self.old_boundry_mask = parent_map.boundry_mask & split_mask
73
+ self.block_mask = parent_map.block_mask & split_mask
74
+ self.facility_filled_mask = parent_map.facility_filled_mask & split_mask
75
+ # add new line to access
76
+ new_access_line = self.block_mask & line_mask
77
+ self.access_mask = parent_map.access_mask & split_mask
78
+ self.access_mask = self.access_mask | new_access_line
79
+ # add new line to boundries
80
+ self.boundry_mask = self.boundry_mask | new_access_line
81
+ self.new_access_line = new_access_line
82
+ if map_id > 2:
83
+ self.parent_access_line = parent_map.new_access_line
84
+ self.parent_line_p = parent_map.line_p
85
+ else:
86
+ self.parent_access_line = self.new_access_line
87
+ self.parent_line_p = self.line_p
88
+ self.save_map()
89
+ self.print_report()
90
+
91
+ # Parcels initilization
92
+ @__init__.register(int)
93
+ def _third__(self,parcel_id:int,split_mask:np.ndarray,parent_map,parcel_area,lines_points_tup,parcel_type) -> None:
94
+ self.dir = parent_map.dir
95
+ self.parent_line_p = parent_map.parent_line_p
96
+ self.line_p = parent_map.line_p
97
+ self.parent_access_line = parent_map.parent_access_line & split_mask
98
+ self.access_line = parent_map.new_access_line & split_mask
99
+ self.parcel_id = parcel_id
100
+ self.curr_size = parcel_area
101
+ self.bounding_lines = lines_points_tup
102
+ self.parcel_type = parcel_type
103
+ self.map_id = parent_map.map_id
104
+ split_3d_mask = np.zeros(parent_map.frame_shape, dtype=np.uint8)
105
+ split_3d_mask[:,:,:] = split_mask[:,:,np.newaxis]
106
+ self.frame = parent_map.frame & split_3d_mask
107
+ self.frame_shape = self.frame.shape
108
+ self.arch_choice = parent_map.arch_choice
109
+ self.trees_mask = parent_map.trees_mask & split_mask
110
+ self.trees_binary_mask = parent_map.trees_binary_mask & split_mask
111
+ self.fixed_f_mask = parent_map.fixed_f_mask & split_mask
112
+ self.boundry_mask = parent_map.boundry_mask & split_mask
113
+ self.block_mask = parent_map.block_mask & split_mask
114
+ self.facility_filled_mask = parent_map.facility_filled_mask & split_mask
115
+ # make center of new parcel
116
+ block_mask = self.block_mask.astype(np.uint8)
117
+ contours, _ = cv2.findContours(block_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
118
+ cnts = sorted(contours, key=cv2.contourArea, reverse=True)
119
+ M = cv2.moments(cnts[0])
120
+ cX = int(M["m10"] / M["m00"])
121
+ cY = int(M["m01"] / M["m00"])
122
+ self.parcel_center = (cY,cX)
123
+ # self.save_map()
124
+ self.print_report()
125
+
126
+ def print_report(self):
127
+ config.log(f"Map {self.map_id} Area : {np.sum(self.block_mask)/255}")
128
+ config.log(f"Map {self.map_id} Tree Area : {np.sum(self.trees_binary_mask)/255}")
129
+ config.log(f"Map {self.map_id} Fixed-Facility Area : {np.sum(self.facility_filled_mask)/255}")
130
+ config.log(f"Map {self.map_id} Sparse Area : {np.sum(self.block_mask & np.bitwise_not(self.facility_filled_mask) & np.bitwise_not(self.trees_binary_mask))/255}")
131
+
132
+ def set_map_axis_center(self,point):
133
+ self.axis_center = point
134
+ def set_line_point(self,point:tuple):
135
+ self.line_points = point
136
+
137
+ def save_map(self) -> None:
138
+ if config.WRITE_UNNECESSARY:
139
+ cv2.imwrite(f'outputs/map{self.map_id}.bmp',self.frame)
140
+ cv2.imwrite(f'outputs/access_mask{self.map_id}.bmp',self.access_mask)
141
+ cv2.imwrite(f'outputs/boundry_mask{self.map_id}.bmp',self.boundry_mask)
142
+
143
+ def create_masks(self) -> tuple:
144
+ res = []
145
+ img_re = self.frame.reshape(-1,3)
146
+ df = pd.DataFrame(img_re,columns=['b','g','r'])
147
+ df['r'].astype(np.uint8)
148
+ df['g'].astype(np.uint8)
149
+ df['b'].astype(np.uint8)
150
+
151
+ indx_trees = df.apply(lambda x: x.b==0 and 0<x.g<=255 and x.r==0, axis=1)
152
+ df_trees = df.copy()
153
+ df_trees[np.logical_not(indx_trees)] = [0,0,0]
154
+ # df_trees[indx_trees] = [255,255,255]
155
+ out = df_trees.values.reshape(self.frame_shape)
156
+ out = out.astype(np.uint8)
157
+ out[:,:,0] = 0
158
+ out[:,:,2] = 0
159
+ out = cv2.threshold(out, 127, 255, cv2.THRESH_BINARY)[1][:,:,1]
160
+ res.append(out)
161
+ cv2.imwrite('outputs/tree_mask.bmp',out)
162
+
163
+ indx_fixed_fac = df.apply(lambda x: x.b==0 and x.g==0 and x.r==0, axis=1)
164
+ df_ff = df.copy()
165
+ df_ff[np.logical_not(indx_fixed_fac)] = [0,0,0]
166
+ df_ff[indx_fixed_fac] = [255,255,255]
167
+ out = df_ff.values.reshape(self.frame_shape)
168
+ out = out.astype(np.uint8)
169
+ out = cv2.cvtColor(out,cv2.COLOR_BGR2GRAY)
170
+ res.append(out)
171
+ cv2.imwrite('outputs/facility_mask.bmp',out)
172
+
173
+ indx_access = df.apply(lambda x: x.g==0 and x.r==255, axis=1)
174
+ df_ac = df.copy()
175
+ df_ac[np.logical_not(indx_access)] = [0,0,0]
176
+ df_ac[indx_access] = [255,255,255]
177
+ out = df_ac.values.reshape(self.frame_shape)
178
+ out = out.astype(np.uint8)
179
+ out = cv2.cvtColor(out,cv2.COLOR_BGR2GRAY)
180
+ res.append(out)
181
+ cv2.imwrite('outputs/access_mask.bmp',out)
182
+
183
+ indx_boundry = df.apply(lambda x: x.b==255 and x.g==0, axis=1)
184
+ df_b = df.copy()
185
+ df_b[np.logical_not(indx_boundry)] = [0,0,0]
186
+ df_b[indx_boundry] = [255,255,255]
187
+ out = df_b.values.reshape(self.frame_shape)
188
+ out = out.astype(np.uint8)
189
+ out = cv2.cvtColor(out,cv2.COLOR_BGR2GRAY)
190
+ res.append(out)
191
+ cv2.imwrite('outputs/boundary_mask.bmp',out)
192
+ return tuple(res)
193
+
194
+ def correct_input(self) -> None:
195
+ img_re = self.frame.reshape(-1,3)
196
+ df = pd.DataFrame(img_re,columns=['b','g','r'])
197
+ df['r'].astype(np.uint8)
198
+ df['g'].astype(np.uint8)
199
+ df['b'].astype(np.uint8)
200
+ # ----set tree values to random
201
+ random.seed(13)
202
+ df = df.apply(lambda x: [0,random.randint(1,255),0] if x['b']==0 and x['g']==255 and x['r']==0 else x,axis=1)
203
+ # ----convert white to black
204
+ # indx = df.apply(lambda x: x['b']==255 and x['g']==255 and x['r']==255,axis=1)
205
+ # df[indx] = [0,0,0]
206
+ # ----set rgb(0,255,255) to rgb(255,0,255)
207
+ # indx = df.apply(lambda x: x['b']==255 and x['g']==255 and x['r']==0,axis=1)
208
+ # df[indx] = [255,0,255]
209
+ # ----save output
210
+ out = df.values.reshape(self.frame_shape)
211
+ out = out.astype(np.uint8)
212
+ cv2.imwrite('outputs/kan_pre.bmp',out)
213
+ self.frame = out
214
+ # ----print
215
+ # cv2.imshow("kan_pre_out", out)
216
+ # cv2.waitKey(0)
217
+ # cv2.destroyAllWindows()
218
+
219
+ """
220
+ returns above the line mask and below the line mask
221
+ """
222
+ def line_split_mask_maker(self,p0:tuple,p1:tuple):
223
+ # points are (y,x) oriented
224
+ img_pixels = self.frame_shape[0]*self.frame_shape[1]
225
+ img_x = self.frame_shape[1]
226
+ # numpy image is (y*x*3)
227
+ # !! unit16 may not be enough
228
+ y_index = (np.arange(img_pixels).reshape(self.frame_shape[:2])/img_x).astype(np.uint32)
229
+ x_index = np.arange(img_pixels).reshape(self.frame_shape[:2])%img_x
230
+ if p1[1] == p0[1]:
231
+ up_down_line = x_index - p0[1]
232
+ else:
233
+ slope = (p1[0]-p0[0])/(p1[1]-p0[1])
234
+ intercept = p0[0] - (slope*p0[1])
235
+ up_down_line = x_index*slope + intercept - y_index
236
+ # down part (becareful about center)
237
+ down_mask = np.where(up_down_line>=0,255,0).reshape(self.frame_shape[:2])
238
+ up_mask = np.where(up_down_line>=0,0,255).reshape(self.frame_shape[:2])
239
+ return (up_mask,down_mask)
240
+ """
241
+ returns only line mask on main image
242
+ """
243
+ def line_mask_maker(self,p0:tuple,p1:tuple):
244
+ plain = np.zeros((self.block_mask.shape))
245
+ plain = cv2.line(plain,(p0[1],p0[0]),(p1[1],p1[0]),255,2)
246
+ return plain.astype(np.uint8)
247
+
248
+
249
+ """
250
+ check whether the half map has a feasible condition
251
+ or supports the finishing condtion.
252
+ """
253
+ def isfeasible(self):
254
+ # check if the part has more than 60% access
255
+ access = np.sum(self.access_mask)/255
256
+ boundry = np.sum(self.boundry_mask)/255
257
+ access_ratio = access/boundry
258
+ access_cond = access_ratio<self.access_ratio
259
+ # area of part not smaller than standard
260
+ block_size = np.sum(self.block_mask)/255
261
+ size_cond = block_size>self.parcel_minimum_area
262
+ config.log(f'block size:{block_size} access_ratio:{access_ratio} map_id:{self.map_id}')
263
+ self.curr_access = access_ratio
264
+ self.curr_size = block_size
265
+ return access_cond and size_cond
266
+
267
+ class CVLineThickness:
268
+ """
269
+ method selects cv2line arg
270
+ depending on the pixel width
271
+ """
272
+ @staticmethod
273
+ def thickness_solver(desired_thickness):
274
+ if desired_thickness == 1:
275
+ return 1
276
+ if desired_thickness == 2:
277
+ # assert false, f"change road size or road step to odd number: {desired_thickness}"
278
+ return 2
279
+ if desired_thickness == 3:
280
+ return 2
281
+ if desired_thickness % 2 == 0:
282
+ # assert false, f"change road size or road step to odd number: {desired_thickness}"
283
+ return desired_thickness - 1
284
+
285
+ return desired_thickness - 2
286
+
287
+
288
+ class MapOut:
289
+ def __init__(self,src:str,lines_axis:list) -> None:
290
+ self.img = cv2.imread(src)
291
+ self.img_axised = self.img.copy()
292
+ self.img_partitioned = None
293
+ self.img_built = None
294
+ self.img_last = None
295
+ self.axis_lines = lines_axis
296
+ self.partitioning_lines = None
297
+ self.parcels_dic = {}
298
+ self.building_masks = None
299
+ # export details
300
+ self.total_carbon = 0
301
+ self.total_trees = 0
302
+ self.total_carbon_loss = 0
303
+ self.total_cut_tree = 0
304
+ self.total_axis_length = 0
305
+ self.total_axis_per_block_pr = 0
306
+ self.total_num_parcels = 0
307
+ self.total_num_parcels_types = {p_type:0 for p_type in config.ParcelType._member_names_}
308
+ self.total_sum_ff = 0
309
+
310
+ def reset_map_for_partitioning(self):
311
+ self.total_num_parcels = 0
312
+ self.total_num_parcels_types = {p_type:0 for p_type in config.ParcelType._member_names_}
313
+ self.total_sum_ff = 0
314
+ self.partitioning_lines = None
315
+ if self.img_partitioned is not None:
316
+ self.img_partitioned = None
317
+ self.img_last = self.img_axised.copy()
318
+
319
+ def reset_map_for_location_finding(self):
320
+ self.total_sum_ff = 0
321
+ self.building_masks = None
322
+ if self.img_built is not None:
323
+ self.img_built = None
324
+ self.img_last = self.img_partitioned.copy()
325
+
326
+ def add_partition_report(self,report):
327
+ self.total_num_parcels += report['cnt']
328
+ report.pop('cnt')
329
+ for p_type in report.keys():
330
+ self.total_num_parcels_types[p_type] += report[p_type]
331
+
332
+ def report(self):
333
+ self.block_mask = cv2.imread(config.MAIN_MAP_FILLED_BLOCK_MASK)
334
+ self.tree_mask = cv2.imread('outputs/tree_mask.bmp')
335
+ self.binary_tree_mask = cv2.threshold(self.tree_mask, 127, 255, cv2.THRESH_BINARY)[1]
336
+ self.facility_filled_mask = cv2.imread(config.MAIN_MAP_FILLED_F_F_MASK)
337
+ # total trees carbon
338
+ self.total_carbon = np.sum(self.tree_mask)/3
339
+ self.total_trees = np.sum(self.binary_tree_mask)/(255*3)
340
+ # calculate tree cut and carbon
341
+ self.img_last = self.img_last & self.block_mask
342
+ self.img_last = self.img_last.astype(np.uint8)
343
+ self.img_mask = cv2.threshold(self.img_last, 127, 255, cv2.THRESH_BINARY)[1]
344
+ # omit roads mask and building mask from it
345
+ self.roads_mask = cv2.imread('outputs/roads_mask.bmp')
346
+ collision3dmask = self.roads_mask
347
+ if os.path.exists('outputs/buildings_mask.bmp'):
348
+ collision3dmask = collision3dmask | cv2.imread('outputs/buildings_mask.bmp')
349
+ if os.path.exists('outputs/partitioning_mask.bmp'):
350
+ collision3dmask = collision3dmask | cv2.imread('outputs/partitioning_mask.bmp')
351
+ cv2.imwrite('outputs/constructed_mask.bmp', collision3dmask)
352
+ self.total_carbon_loss = np.sum(collision3dmask & self.tree_mask)/3
353
+ self.total_cut_tree = np.sum(collision3dmask & self.binary_tree_mask)/(255*3)
354
+ config.log(f"Total Trees:{self.total_trees} Total Carbon Values:{self.total_carbon}")
355
+ config.log(f"Total Cut Trees:{self.total_cut_tree} Total Carbon Loss:{self.total_carbon_loss}")
356
+ config.log(f"Total Cut Precentage:{self.total_cut_tree/self.total_trees}")
357
+ # calculate axis reports
358
+ img_re = self.img_last.reshape(-1,3)
359
+ df = pd.DataFrame(img_re,columns=['b','g','r'])
360
+ df['r'].astype(np.uint8)
361
+ df['g'].astype(np.uint8)
362
+ df['b'].astype(np.uint8)
363
+ indx_axis = df.apply(lambda x:x.g == 0 and 0<x.r<=255 and x.b == 0, axis=1)
364
+ df_axis = df.copy()
365
+ df_axis[np.logical_not(indx_axis)] = [0,0,0]
366
+ out = df_axis.values.reshape(self.img_last.shape)
367
+ out = out.astype(np.uint8)
368
+ out = cv2.cvtColor(out,cv2.COLOR_BGR2GRAY)
369
+ out = cv2.threshold(out, 1, 255, cv2.THRESH_BINARY)[1]
370
+ self.total_axis_length = np.sum(out)/255
371
+ self.total_axis_per_block_pr = self.total_axis_length / (np.sum(self.block_mask)/(255*3))
372
+ sparse_area = np.sum(self.block_mask & np.bitwise_not(self.facility_filled_mask) & np.bitwise_not(self.binary_tree_mask))/(255*3)
373
+ self.total_axis_per_sparse_pr = self.total_axis_length / sparse_area
374
+ config.log(f"Total Axis Area:{self.total_axis_length}")
375
+ config.log(f"Total Block Area:{np.sum(self.block_mask)/(255*3)}")
376
+ config.log(f"Total Sparse Area:{sparse_area}")
377
+ config.log(f"Total Axis Per Block Precentage:{self.total_axis_per_block_pr}")
378
+ config.log(f"Total Axis Per Sparse Precentage:{self.total_axis_per_sparse_pr}")
379
+ # Parcels number plus types
380
+ config.log(f"Total Parcels:{self.total_num_parcels}")
381
+ config.log(f"Total Parcel types:{self.total_num_parcels_types}")
382
+ config.log(f"Total Parcels With FF:{self.total_sum_ff}")
383
+
384
+
385
+
386
+ def draw_axis(self):
387
+ for line in self.axis_lines:
388
+ p0=line[0][0]
389
+ p1=line[0][1]
390
+ thickness=config.ROAD_SIZE_MAX - config.ROAD_STEP*int(math.log(line[1]+1,2))
391
+ if thickness <= config.ROAD_SIZE_MIN:
392
+ thickness=config.ROAD_SIZE_MIN
393
+ # draw line of axis
394
+ self.img_axised = cv2.line(self.img_axised,(p0[1],p0[0]),(p1[1],p1[0]),(0,0,127),CVLineThickness.thickness_solver(thickness+2))
395
+ # rotate points
396
+ self.img_axised = cv2.line(self.img_axised,(p0[1],p0[0]),(p1[1],p1[0]),(0,0,255),CVLineThickness.thickness_solver(thickness))
397
+ cv2.imwrite('outputs/final_axis.bmp',self.img_axised)
398
+ # save road lines mask
399
+ img_re = self.img_axised.reshape(-1,3).copy()
400
+ df = pd.DataFrame(img_re,columns=['b','g','r'])
401
+ df['r'].astype(np.uint8)
402
+ df['g'].astype(np.uint8)
403
+ df['b'].astype(np.uint8)
404
+ indx_axis = df.apply(lambda x:x.g == 0 and 0<x.r<=255 and x.b == 0, axis=1)
405
+ df[np.logical_not(indx_axis)] = [0,0,0]
406
+ out = df.values.reshape(self.img_axised.shape)
407
+ out = out.astype(np.uint8)
408
+ out = cv2.cvtColor(out,cv2.COLOR_BGR2GRAY)
409
+ out = cv2.threshold(out, 1, 255, cv2.THRESH_BINARY)[1]
410
+ self.img_last = self.img_axised.copy()
411
+ cv2.imwrite('outputs/roads_mask.bmp', out)
412
+
413
+ def draw_partitions(self,iteration:int,map:MapIn,lines_parcels:list):
414
+ map_id = map.map_id
415
+ if lines_parcels is not None:
416
+ self.parcels_dic[map_id] = lines_parcels
417
+ lines_mask = np.zeros(self.img_last.shape, dtype=np.uint8)
418
+ for line in lines_parcels:
419
+ p0=line[0]
420
+ p1=line[1]
421
+ # rotate points
422
+ lines_mask = cv2.line(lines_mask,(p0[1],p0[0]),(p1[1],p1[0]),(120,120,120),1)
423
+ split_3d_mask = np.zeros(self.img_last.shape, dtype=np.uint8)
424
+ split_3d_mask[:,:,:] = map.block_mask[:,:,np.newaxis]
425
+ lines_mask = lines_mask & split_3d_mask
426
+ self.img_partitioned = self.img_last.astype(np.uint8) & np.bitwise_not(lines_mask)
427
+ # write partitioning mask
428
+ lines_mask = np.where(lines_mask>0,(255,255,255), (0,0,0))
429
+ if self.partitioning_lines is not None:
430
+ self.partitioning_lines |= lines_mask
431
+ else:
432
+ self.partitioning_lines = lines_mask
433
+ if config.WRITE_UNNECESSARY:
434
+ cv2.imwrite(f'outputs/final_map_{iteration}_{map_id}.bmp',self.img_partitioned)
435
+ self.img_last = self.img_partitioned.copy()
436
+
437
+ def draw_partitioning_results(self):
438
+ cv2.imwrite(f'outputs/partitioning_mask.bmp',self.partitioning_lines)
439
+ cv2.imwrite(f'outputs/final_map_partitioning.bmp',self.img_partitioned)
440
+
441
+
442
+ def draw_building(self,building_mask,iteration,map_id,parcel_id,has_building,parcel_type,block_mask):
443
+ color3d_mask = np.zeros(self.img_last.shape, dtype=np.uint8)
444
+ color3d_mask[:,:,:] = block_mask[:,:,np.newaxis]
445
+ if has_building:
446
+ self.total_sum_ff += 1
447
+ color3d_mask = np.where(color3d_mask>0,(100,100,100),(255,255,255))
448
+ elif parcel_type == config.ParcelType.O:
449
+ color3d_mask = np.where(color3d_mask>0,(0,255,255),(255,255,255))
450
+ elif parcel_type == config.ParcelType.A:
451
+ color3d_mask = np.where(color3d_mask>0,(51,255,255),(255,255,255))
452
+ elif parcel_type == config.ParcelType.B:
453
+ color3d_mask = np.where(color3d_mask>0,(102,255,255),(255,255,255))
454
+ elif parcel_type == config.ParcelType.C:
455
+ color3d_mask = np.where(color3d_mask>0,(153,255,255),(255,255,255))
456
+ elif parcel_type == config.ParcelType.U:
457
+ color3d_mask = np.where(color3d_mask>0,(40,0,255),(255,255,255))
458
+
459
+ build3d_mask = np.zeros(self.img.shape, dtype=np.uint8)
460
+ build3d_mask[:,:,:] = building_mask[:,:,np.newaxis]
461
+ if self.building_masks is not None:
462
+ self.building_masks &= build3d_mask
463
+ self.img_last &= self.img_partitioned & build3d_mask
464
+ self.img_built &= self.img_partitioned & build3d_mask & color3d_mask
465
+ else:
466
+ self.building_masks = build3d_mask
467
+ self.img_last = self.img_partitioned & build3d_mask
468
+ self.img_built = self.img_partitioned & build3d_mask & color3d_mask
469
+ if config.WRITE_UNNECESSARY:
470
+ cv2.imwrite(f'outputs/final_map_{iteration}_{map_id}_{parcel_id}.bmp',self.img_built)
471
+
472
+ def draw_building_results(self):
473
+ cv2.imwrite(f'outputs/buildings_mask.bmp',np.bitwise_not(self.building_masks))
474
+ cv2.imwrite(f'outputs/final_map_location_finding.bmp',self.img_built)
475
+
476
+
477
+ def draw_collision(self):
478
+ trees_mask = cv2.imread('outputs/tree_mask.bmp')
479
+ fixed_facility_mask = cv2.imread('outputs/facility_mask.bmp')
480
+ roads_mask = cv2.imread('outputs/roads_mask.bmp')
481
+ collide_mask = roads_mask
482
+ if os.path.exists('outputs/buildings_mask.bmp'):
483
+ collide_mask |= cv2.imread('outputs/buildings_mask.bmp')
484
+ if os.path.exists('outputs/partitioning_mask.bmp'):
485
+ collide_mask |= cv2.imread('outputs/partitioning_mask.bmp')
486
+ # change here when building mask is av
487
+ collision3dmask = trees_mask | fixed_facility_mask
488
+ collision3dmask = collide_mask & collision3dmask
489
+ img = self.img_last.copy()
490
+ pixels = [100,50,100]*int(len(img[collision3dmask>0])/3)
491
+ img[collision3dmask>0] = pixels
492
+
493
+ cv2.imwrite(f'outputs/collision_map.bmp',img)
app.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from axis_finder import gradio_inference
3
+
4
+ def run_axis_finder(input_str):
5
+ input_str = input_str.split(' ')
6
+ return gradio_inference(input_str[0], input_str[1], input_str[2])
7
+
8
+ demo = gr.Interface(
9
+ run_axis_finder,
10
+ gr.Textbox(value="Road Access[0.1:0.9], Starting Point(y,x), Carbon Prefix[0:7], \n exp:0.7 (44, 119) 2"),
11
+ "image")
12
+ demo.launch()
axis_finder.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from Map import MapIn,MapOut
2
+ from Axis import AxisFinder
3
+ import cv2
4
+ import pickle
5
+ import config
6
+ import time
7
+
8
+ def run_axis_finder(total_execution_time):
9
+ maps_list = []
10
+ lines_list = []
11
+ axis_division_cond = True
12
+ maps_processing_queue = []
13
+ map_id = 0
14
+ iteration = 0
15
+ # axis finding process (1.1)
16
+ config.log_axis_finding_settings()
17
+ config.log(f"------Axis Finder------")
18
+ start_time = time.time()
19
+ while axis_division_cond:
20
+ config.log(f"----Step {iteration}----")
21
+ if (map_id == 0):
22
+ # read map and make masks
23
+ mymap = MapIn(config.MAIN_MAP_ADDR,config.MAIN_MAP_FILLED_BLOCK_MASK,config.MAIN_MAP_FILLED_F_F_MASK,
24
+ config.PARCELS_COUNT,config.ARCH,map_id=map_id)
25
+ # set random values for carbon
26
+ # mymap.correct_input()
27
+ else:
28
+ mymap = maps_processing_queue.pop(0)
29
+
30
+ # makes axis_finder object to split
31
+ myaxis = AxisFinder(mymap)
32
+ # Arch Selection on start division is the same
33
+ if mymap.arch_choice == config.ArchStyles.Customized:
34
+ # axis_center = myaxis.get_center(mymap.block_mask)
35
+ # mymap.set_map_axis_center(axis_center)
36
+ cv2.imshow(f'Map{mymap.map_id}', mymap.frame)
37
+ cv2.setMouseCallback(f'Map{mymap.map_id}', AxisFinder.click_callback)
38
+ cv2.waitKey(0)
39
+ cv2.destroyAllWindows()
40
+ p_f = AxisFinder.clicked_points.pop(0)
41
+ p_s = AxisFinder.clicked_points.pop(0)
42
+ points=[]
43
+ points.append((1,p_f,p_s,p_f,p_s))
44
+ elif map_id == 0 and mymap.arch_choice == config.ArchStyles.Human_Centered_AI:
45
+ p_f = ()
46
+ if config.ARCH_FIRST_INPUT == None:
47
+ cv2.imshow(f'Map{mymap.map_id}', mymap.frame)
48
+ cv2.setMouseCallback(f'Map{mymap.map_id}', AxisFinder.click_callback)
49
+ cv2.waitKey(0)
50
+ cv2.destroyAllWindows()
51
+ p_f = AxisFinder.clicked_points.pop(0)
52
+ else:
53
+ p_f = config.ARCH_FIRST_INPUT
54
+ points = myaxis.iterate_throughall(mymap,p_f)
55
+ elif map_id == 0:
56
+ points = myaxis.iterate_throughall(mymap)
57
+ else:
58
+ points = myaxis.iterate_old_boundries_new_york()
59
+
60
+ # dump axis if hit fixed_facility
61
+ if points[-1][0] == -1:
62
+ if not maps_processing_queue:
63
+ axis_division_cond = False
64
+ continue
65
+
66
+ # draw the found line carbon_fitness
67
+ config.log(f"Map div best score:{points[-1][0]} map_id:{mymap.map_id}")
68
+ config.log(f"Map div best access split fitness:{points[-1][5][0]}")
69
+ config.log(f"Map div best area split fitness:{points[-1][5][1]}")
70
+ config.log(f"Map div best fixed facilities fitness:{points[-1][5][2]}")
71
+ config.log(f"Map div best carbon fitness:{points[-1][5][3]}")
72
+
73
+ lines_list.append((points[-1][3:5],mymap.map_id))
74
+ # make map for split two parts (add line as access)
75
+ split_mask_u,split_mask_d = mymap.line_split_mask_maker(points[-1][1],points[-1][2])
76
+ line_mask = mymap.line_mask_maker(points[-1][1],points[-1][2])
77
+ up_map = MapIn(split_mask_u,mymap,line_mask,map_id+1,0,points[-1][1:3])
78
+ down_map = MapIn(split_mask_d,mymap,line_mask,map_id+2,1,points[-1][1:3])
79
+ parcels = AxisFinder.cal_split_parcels(up_map,down_map,mymap.parcel_cnt)
80
+ up_map.parcel_cnt,down_map.parcel_cnt = parcels
81
+ config.log(f"Map:{map_id+1} Parcels:{up_map.parcel_cnt}")
82
+ config.log(f"Map:{map_id+2} Parcels:{down_map.parcel_cnt}")
83
+ # check finishing condition
84
+ up_feasibility, down_feasibility = (up_map.isfeasible(), down_map.isfeasible())
85
+ # add new maps to processing queue
86
+ if up_feasibility:
87
+ config.log(f"Map:{map_id+1} Added!")
88
+ up_map.set_line_point(points[-1][1:])
89
+ maps_processing_queue.append(up_map)
90
+ # was the last axis so add axis to final answer
91
+ else:
92
+ config.log(f"Map:{map_id+1} Added As Answer")
93
+ up_map.set_line_point(points[-1][1:])
94
+ maps_list.append(up_map)
95
+ if down_feasibility:
96
+ config.log(f"Map:{map_id+2} Added!")
97
+ down_map.set_line_point(points[-1][1:])
98
+ maps_processing_queue.append(down_map)
99
+ else:
100
+ config.log(f"Map:{map_id+2} Added As Answer")
101
+ down_map.set_line_point(points[-1][1:])
102
+ maps_list.append(down_map)
103
+
104
+ if not maps_processing_queue:
105
+ axis_division_cond = False
106
+ map_id += 2
107
+ iteration+=1
108
+ config.log('-'*8)
109
+
110
+ # sort maps by size
111
+ maps_list.sort(key=lambda x: x.curr_size,reverse=True)
112
+ cv2.destroyAllWindows()
113
+ # save maps
114
+ with open('outputs/maps_list.pickle', 'wb') as f:
115
+ pickle.dump(maps_list, f)
116
+ with open('outputs/lines_list.pickle', 'wb') as f:
117
+ pickle.dump(lines_list, f)
118
+
119
+ # export split result
120
+ config.log(f"------Axis Result------")
121
+ export_map = MapOut(config.MAIN_MAP_ADDR,lines_list)
122
+ export_map.draw_axis()
123
+ export_map.draw_collision()
124
+ export_map.report()
125
+ # save
126
+ with open('outputs/export_map.pickle', 'wb') as f:
127
+ pickle.dump(export_map, f)
128
+
129
+ config.log("--- Axis Finder Finished In %s seconds ---" % (time.time() - start_time))
130
+ total_execution_time += time.time() - start_time
131
+ return total_execution_time
132
+
133
+ def gradio_inference(access_ratio,start_point,carbon_weight):
134
+ config.ACCESS_RATIO = float(access_ratio)
135
+ config.ARCH_FIRST_INPUT = tuple(start_point)
136
+ config.A_CARBON_WEIGHT = int(carbon_weight)
137
+ run_axis_finder(0)
138
+ image = cv2.imread(f'outputs/collision_map.bmp')
139
+ return image
140
+
141
+ if __name__ == "__main__":
142
+ run_axis_finder(0)
config.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import enum
2
+ import datetime
3
+ # enum classes
4
+ class ArchStyles(enum.Enum):
5
+ New_York = 1
6
+ Human_Centered_AI = 2
7
+ Customized = 3
8
+ class ParcelType(enum.Enum):
9
+ O = 0 # oversize
10
+ A = 1
11
+ B = 2
12
+ C = 3
13
+ U = 4 # undersize
14
+
15
+ # main run input args
16
+ MAIN_MAP_ADDR = 'inputs/Sample_test120-70_total_map_fringe.bmp'
17
+ MAIN_MAP_FILLED_BLOCK_MASK = 'inputs/Sample_test120-70_boundry_mask_fringe.bmp'
18
+ MAIN_MAP_FILLED_F_F_MASK = 'inputs/Sample_test120-70_FiFa_mask_fringe.bmp'
19
+ PARCELS_COUNT = 12
20
+ ARCH = ArchStyles.Human_Centered_AI
21
+ ARCH_FIRST_INPUT = (44, 119) #(70, 120) #(Y,X) or None
22
+
23
+ # Axis finding args
24
+ VALID_POINT_DISTANCE = 10
25
+ ROAD_SIZE_MAX = 3 # can be: 1,3,5,7,...
26
+ ROAD_SIZE_MIN = 1 # can be: 1,3,5,...
27
+ ROAD_STEP = 2 # can be multiplication of 2
28
+
29
+ ACCESS_RATIO = 0.6 # 0.55 for newyork # 0.7 for star
30
+
31
+ AXIS_MIN_AREA = 1000 # 12000 for newyork # 75000 for star
32
+
33
+ FACILITY_SAFE_DIST = 2
34
+ TREE_SAFE_DIST = 1
35
+
36
+ # axis finder fitness weights
37
+ A_ACCESS_SPLIT_WEIGHT = 1
38
+ A_AREA_SPLIT_WEIGHT = 1
39
+ A_FIXED_FACILITIES_WEIGHT = 1
40
+ A_CARBON_WEIGHT = 1
41
+
42
+ # Partitioning args
43
+ PROCESSING_CORES = 8
44
+
45
+ P_GA_LOSS_THOLD = 0.3
46
+
47
+ TYPE_A_AREA = 700
48
+ TYPE_B_AREA = 500
49
+ TYPE_C_AREA = 400
50
+
51
+ TYPE_AREA_STEP = 100
52
+
53
+ # partiotioning fitness weights
54
+ P_FF_WEIGHT = 2
55
+ P_AREA_WEIGHT = 2
56
+ P_TREE_WEIGHT = 1
57
+
58
+ # location finding args
59
+ BUILDING_ADDR = ['inputs/rectangle_6-15.bmp','inputs/rectangle_6-15.bmp','inputs/rectangle_6-15.bmp','inputs/white.bmp']
60
+ BUILDING_BORDER_GAP = 10
61
+
62
+ # location finding fitness weights
63
+ L_DIST_WEIGHT = 1
64
+ L_OUT_BORDER_WEIGHT = 2
65
+ L_CARBON_WEIGHT = 1
66
+ L_ON_ROAD_WEIGHT = 2
67
+
68
+ def building_sel(p_type:ParcelType):
69
+ if p_type == ParcelType.O: return BUILDING_ADDR[0]
70
+ if p_type == ParcelType.A: return BUILDING_ADDR[0]
71
+ if p_type == ParcelType.B: return BUILDING_ADDR[1]
72
+ if p_type == ParcelType.C: return BUILDING_ADDR[2]
73
+ if p_type == ParcelType.U: return BUILDING_ADDR[3]
74
+
75
+ # output args and loggers
76
+ WRITE_UNNECESSARY = False
77
+ WRITE_LOGS = True
78
+ def log_axis_finding_settings():
79
+ log(f'====== Axis Finding inputs ======')
80
+ log(f'PARCELS_COUNT : {PARCELS_COUNT}')
81
+ log(f'ARCH : {ARCH}')
82
+ log(f'ARCH_FIRST_INPUT : {ARCH_FIRST_INPUT}')
83
+ log(f'VALID_POINT_DISTANCE : {VALID_POINT_DISTANCE}')
84
+ log(f'ROAD_SIZE_MAX : {ROAD_SIZE_MAX}')
85
+ log(f'ROAD_SIZE_MIN : {ROAD_SIZE_MIN}')
86
+ log(f'ROAD_STEP : {ROAD_STEP}')
87
+ log(f'ACCESS_RATIO : {ACCESS_RATIO}')
88
+ log(f'AXIS_MIN_AREA : {AXIS_MIN_AREA}')
89
+ log(f'FACILITY_SAFE_DIST : {FACILITY_SAFE_DIST}')
90
+ log(f'TREE_SAFE_DIST : {TREE_SAFE_DIST}')
91
+ log(f'A_ACCESS_SPLIT_WEIGHT : {A_ACCESS_SPLIT_WEIGHT}')
92
+ log(f'A_AREA_SPLIT_WEIGHT : {A_AREA_SPLIT_WEIGHT}')
93
+ log(f'A_FIXED_FACILITIES_WEIGHT : {A_FIXED_FACILITIES_WEIGHT}')
94
+ log(f'A_CARBON_WEIGHT : {A_CARBON_WEIGHT}')
95
+ def log_partitioning_settings():
96
+ log(f'====== Partitioning inputs ======')
97
+ log(f'PARCELS_COUNT : {PARCELS_COUNT}')
98
+ log(f'PROCESSING_CORES : {PROCESSING_CORES}')
99
+ log(f'P_GA_LOSS_THOLD : {P_GA_LOSS_THOLD}')
100
+ log(f'TYPE_A_AREA : {TYPE_A_AREA}')
101
+ log(f'TYPE_B_AREA : {TYPE_B_AREA}')
102
+ log(f'TYPE_C_AREA : {TYPE_C_AREA}')
103
+ log(f'TYPE_AREA_STEP : {TYPE_AREA_STEP}')
104
+ log(f'P_FF_WEIGHT : {P_FF_WEIGHT}')
105
+ log(f'P_AREA_WEIGHT : {P_AREA_WEIGHT}')
106
+ log(f'P_TREE_WEIGHT : {P_TREE_WEIGHT}')
107
+ def log_location_finding_settings():
108
+ log(f'====== Location Finding inputs ======')
109
+ log(f'PARCELS_COUNT : {PARCELS_COUNT}')
110
+ log(f'BUILDING_ADDR : {BUILDING_ADDR}')
111
+ log(f'BUILDING_BORDER_GAP : {BUILDING_BORDER_GAP}')
112
+ log(f'TYPE_A_AREA : {TYPE_A_AREA}')
113
+ log(f'TYPE_B_AREA : {TYPE_B_AREA}')
114
+ log(f'TYPE_C_AREA : {TYPE_C_AREA}')
115
+ log(f'TYPE_AREA_STEP : {TYPE_AREA_STEP}')
116
+ log(f'L_DIST_WEIGHT : {L_DIST_WEIGHT}')
117
+ log(f'L_OUT_BORDER_WEIGHT : {L_OUT_BORDER_WEIGHT}')
118
+ log(f'L_CARBON_WEIGHT : {L_CARBON_WEIGHT}')
119
+ log(f'L_ON_ROAD_WEIGHT : {L_ON_ROAD_WEIGHT}')
120
+ def log(msg):
121
+ if WRITE_LOGS:
122
+ with open('./outputs/logs.txt', 'a') as f:
123
+ f.writelines(f'{datetime.datetime.now()} : {str(msg)}\n')
124
+ print(str(msg))
inputs/.DS_Store ADDED
Binary file (6.15 kB). View file
 
inputs/Sample_test120-70_FiFa_mask.bmp ADDED
inputs/Sample_test120-70_FiFa_mask_fringe.bmp ADDED
inputs/Sample_test120-70_boundry_mask.bmp ADDED
inputs/Sample_test120-70_boundry_mask_fringe.bmp ADDED
inputs/Sample_test120-70_total_map.bmp ADDED
inputs/Sample_test120-70_total_map_fringe.bmp ADDED
inputs/rectangle_6-15.bmp ADDED
inputs/white.bmp ADDED
outputs/.DS_Store ADDED
Binary file (6.15 kB). View file