Advertisement
Guest User

Untitled

a guest
Sep 2nd, 2015
82
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.53 KB | None | 0 0
  1. #! env python3
  2. #
  3. # Simple utilite for merging map tiles sessions generated by hafen clients
  4. # 'maps' or 'map' contains source sessions. Result will be placed to 'tiles'
  5.  
  6. from __future__ import print_function
  7. import os
  8. import sys
  9. import re
  10. import shutil
  11. from PIL import Image
  12. import hashlib
  13.  
  14.  
  15. script_root = os.path.dirname(os.path.abspath(__file__))
  16.  
  17. if os.path.exists(os.path.join(script_root, 'map')):
  18. raw_tiles_root = os.path.join(script_root, 'map')
  19. else:
  20. raw_tiles_root = os.path.join(script_root, 'maps')
  21.  
  22. tiles_root = os.path.join(script_root, 'tiles') # result directory
  23. processed_tiles_root = os.path.join(script_root, 'processed') # processed sessions
  24. broken_tiles_root = os.path.join(script_root, 'broken') # sessions which can not be prosessed due tile numbers conflict
  25.  
  26. # index file format: lines like 'hash x y'
  27. index_path = os.path.join(script_root, 'hafen_tiles.index')
  28.  
  29. def load_index():
  30. index = {}
  31. if os.path.exists(index_path):
  32. index = {}
  33. with open(index_path, 'r') as f:
  34. for line in f:
  35. h, x, y = line.split()
  36. index[h] = (int(x), int(y))
  37. return index
  38.  
  39. def save_index(index):
  40. if os.path.exists(index_path):
  41. shutil.copy(index_path, index_path + '.bak')
  42. with open(index_path, 'w') as f:
  43. for h in index:
  44. x, y = index[h]
  45. f.write('%s %s %s\n' % (h, x, y))
  46.  
  47. def rebuild_index(index = {}):
  48. for root, dirs, files in os.walk(tiles_root):
  49. for f in files:
  50. filename, file_extension = os.path.splitext(f)
  51. if file_extension != '.png':
  52. continue
  53. img = Image.open(os.path.join(root, f))
  54. h = calc_image_hash(img)
  55. if not h:
  56. continue
  57. index[h] = tile_offset(filename)
  58. return index
  59.  
  60. def tile_offset(filename):
  61. x, y = re.search(u'.*_([-0-9]+)_([-0-9]+).*', filename).groups()
  62. return (int(x), int(y))
  63.  
  64. def calc_image_hash(img):
  65. '''
  66. Calc hash based on biom borders shape
  67. '''
  68. width, height = img.size
  69. m = hashlib.md5()
  70. offset = None # some clients generate tiles with offset
  71. black_count = 0 # we can not buld hash for tiles without borders
  72. for x in range(5, width - 5):
  73. for y in range(5, height - 5):
  74. pixel = img.getpixel((x,y))
  75. if pixel[0] == 0 and pixel[1] == 0 and pixel[2] == 0:
  76. black_count += 1
  77. if not offset:
  78. offset = (x, y)
  79. ox, oy = offset
  80. m.update(str(x - ox).encode('utf-8'))
  81. m.update(str(y - oy).encode('utf-8'))
  82. return m.hexdigest() if black_count > 0 else None
  83.  
  84. def most_common(lst):
  85. return max(set(lst), key=lst.count)
  86.  
  87. def is_cave(img, weight=100):
  88. '''
  89. Detect cave tiles by counting pixels which is not gray
  90. '''
  91. width, height = img.size
  92. color_count = 0
  93. for x in range(5, width - 5):
  94. for y in range(5, height - 5):
  95. pixel = img.getpixel((x,y))
  96. avg = (pixel[0] + pixel[1] + pixel[2]) / 3.0
  97. if pixel[0] == 0 and pixel[1] == 0 and pixel[2] == 0:
  98. continue
  99. if abs(pixel[0] / avg - 1) > 0.2 or \
  100. abs(pixel[1] / avg - 1) > 0.2 or \
  101. abs(pixel[2] / avg - 1) > 0.2:
  102. color_count += 1
  103. return color_count < weight
  104.  
  105. print('Reading index...')
  106. index = load_index()
  107. print(len(index), ' items')
  108.  
  109. # trying rebuild index if it is empty
  110. if len(index) < 50:
  111. print('Rebuilding index...')
  112. index = rebuild_index(index=index)
  113. print(len(index), ' items')
  114.  
  115. print('New tiles analysing...')
  116. founded_tiles = 0
  117. cache = {}
  118. cave_cache = {}
  119. iteration_founded = None
  120. while iteration_founded is None or iteration_founded > 0:
  121. founded_tiles += iteration_founded if iteration_founded else 0
  122. iteration_founded = 0
  123. offsets = {}
  124. broken_sessions = []
  125. session_hashes = {}
  126. # Scaning sessions
  127. for root, dirs, files in os.walk(raw_tiles_root):
  128. for f in files:
  129. filename, file_extension = os.path.splitext(f)
  130. if file_extension != '.png':
  131. continue
  132. full_path = os.path.join(root, f)
  133.  
  134. h = cache[full_path] if full_path in cache else calc_image_hash(Image.open(full_path))
  135. cache[full_path] = h
  136. if not h:
  137. continue
  138.  
  139. in_cave = cave_cache[full_path] if full_path in cave_cache else is_cave(Image.open(full_path))
  140. cave_cache[full_path] = in_cave
  141. if in_cave:
  142. continue
  143.  
  144. if not root in session_hashes:
  145. session_hashes[root] = {}
  146. # detect equal tiles with different names into one session
  147. if h in session_hashes[root]:
  148. broken_sessions += [root]
  149. continue
  150. session_hashes[root][h] = filename
  151.  
  152. if h in index.keys():
  153. print('%s %s match' % (full_path, h))
  154. x, y = tile_offset(filename)
  155. ix, iy = index[h]
  156. if not root in offsets:
  157. offsets[root] = []
  158. offsets[root] += [(ix - x, iy - y)]
  159.  
  160. # Selecting session offset related (0, 0)
  161. founded = {}
  162. for d in offsets.keys():
  163. var = most_common(offsets[d])
  164. if offsets[d].count(var) >= 3 or len(index) < 150:
  165. founded[d] = var
  166. else:
  167. print('"%s" contains not enought matched tiles (%s). Ignored' % (d, offsets[d].count(var)))
  168.  
  169. if not os.path.exists(tiles_root):
  170. os.makedirs(tiles_root)
  171.  
  172. # Copy session tiles to result direcory
  173. for root, dirs, files in os.walk(raw_tiles_root):
  174. for f in files:
  175. # if session offset is not found
  176. if not root in founded:
  177. continue
  178. if root in broken_sessions:
  179. if not os.path.exists(broken_tiles_root):
  180. os.makedirs(broken_tiles_root)
  181. if os.path.exists(root):
  182. print('"%s" has tile names conflict. Moved to ""' % (root, broken_tiles_root))
  183. shutil.move(root, broken_tiles_root)
  184. continue
  185. filename, file_extension = os.path.splitext(f)
  186. if file_extension != '.png':
  187. continue
  188. x, y = tile_offset(filename)
  189. ix, iy = founded[root]
  190. full_path = os.path.join(root, f)
  191.  
  192. h = cache[full_path] if full_path in cache else calc_image_hash(Image.open(full_path))
  193. cache[full_path] = h
  194.  
  195. in_cave = cave_cache[full_path] if full_path in cave_cache else is_cave(Image.open(full_path))
  196. cave_cache[full_path] = in_cave
  197. if in_cave:
  198. continue
  199.  
  200. if h:
  201. if h in index and index[h] != (x + ix, y + iy):
  202. print('Warning! %s (%s, %s) (%s) may has wrong name. Already indexed as (%s, %s)' % \
  203. (h, x + ix, y + iy, full_name, index[h][0], index[h][1]))
  204. else:
  205. index[h] = (x + ix, y + iy)
  206.  
  207. new_filename = 'tile_%s_%s.png' % (x + ix, y + iy)
  208. shutil.copy(os.path.join(root, f), os.path.join(tiles_root, new_filename))
  209. print('%s -> %s' % (full_path, os.path.join(tiles_root, new_filename)))
  210. iteration_founded += 1
  211.  
  212. if not os.path.exists(processed_tiles_root):
  213. os.makedirs(processed_tiles_root)
  214. for d in founded.keys():
  215. if os.path.exists(d):
  216. shutil.move(d, processed_tiles_root)
  217.  
  218. save_index(index)
  219.  
  220. print('%s new tiles in "%s"' % (founded_tiles, tiles_root))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement