setup.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import argparse
  2. import cv2
  3. import json
  4. import datetime
  5. import time
  6. from shutil import copyfile
  7. from os.path import exists
  8. from os import makedirs
  9. def main():
  10. ap = argparse.ArgumentParser()
  11. ap.add_argument("-i", "--input", required=True, help="input image")
  12. ap.add_argument("-s", "--scale", type=float, default=0.25, help="scales input image by this factor (default: x0.25)")
  13. ap.add_argument("-c", "--config", type=str, default="config.json",
  14. help="name file to save config (default: config.json)")
  15. args = vars(ap.parse_args())
  16. img = cv2.imread(args["input"])
  17. height, width, _ = img.shape
  18. scale = args["scale"]
  19. window_name = "Setup"
  20. cv2.namedWindow(window_name)
  21. cv2.imshow(window_name, cv2.resize(img, (0, 0), fx=scale, fy=scale))
  22. cv2.setMouseCallback(window_name, null_callback)
  23. show_menu()
  24. food_color = FoodColor(img, scale, window_name)
  25. board_setup = BoardLimits(img, scale, window_name)
  26. done = False
  27. state = 0
  28. setup_vars = {'Aspect Ratio': 1.0, 'Discrete Height': 100, 'Discrete Width': 100}
  29. while not done:
  30. cv2.waitKey(10)
  31. if state == 0:
  32. cv2.imshow(window_name, cv2.resize(img, (0, 0), fx=scale, fy=scale))
  33. cv2.setMouseCallback(window_name, null_callback)
  34. key = input("Enter command: ")
  35. if key == "q":
  36. done = True
  37. elif key == "1":
  38. state = 1
  39. board_setup.clear()
  40. board_setup.help()
  41. cv2.setMouseCallback(window_name, board_setup.on_mouse)
  42. elif key == "2":
  43. state = 2
  44. food_color.clear()
  45. food_color.help()
  46. cv2.setMouseCallback(window_name, food_color.on_mouse)
  47. elif key == "3":
  48. setup_vars['Aspect Ratio'] = -1
  49. while setup_vars['Aspect Ratio'] <= 0:
  50. try:
  51. setup_vars['Aspect Ratio'] = float(input("Insert image height by width ratio here: "))
  52. except ValueError:
  53. setup_vars['Aspect Ratio'] = -1
  54. if setup_vars['Aspect Ratio'] <= 0:
  55. print("Insert only a floating number with dot as separator.")
  56. show_menu()
  57. elif key == "4":
  58. setup_vars['Discrete Height'] = -1
  59. while setup_vars['Discrete Height'] <= 0:
  60. try:
  61. setup_vars['Discrete Height'] = int(input("Insert height discrete resolution: "))
  62. except ValueError:
  63. setup_vars['Discrete Height'] = -1
  64. if setup_vars['Discrete Height'] <= 0:
  65. print("Insert only round numbers.")
  66. setup_vars['Discrete Width'] = -1
  67. while setup_vars['Discrete Width'] <= 0:
  68. try:
  69. setup_vars['Discrete Width'] = int(input("Insert width discrete resolution: "))
  70. except ValueError:
  71. setup_vars['Discrete Width'] = -1
  72. if setup_vars['Discrete Width'] <= 0:
  73. print("Insert only round numbers.")
  74. show_menu()
  75. elif key == "s":
  76. ts = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d_%H.%M.%S-')
  77. if exists(args["config"]):
  78. if not exists("bkp/"):
  79. makedirs("bkp")
  80. copyfile(args["config"], "bkp/" + ts + args["config"])
  81. with open(args["config"], "w") as file:
  82. json.dump({**setup_vars, **board_setup.toJSON(), **food_color.toJSON()}, file)
  83. done = True
  84. else:
  85. print("Error: Unrecognised Command.")
  86. show_menu()
  87. elif state == 1:
  88. board_setup.draw()
  89. if board_setup.done:
  90. state = 0
  91. show_menu()
  92. elif state == 2:
  93. food_color.draw()
  94. if food_color.done:
  95. state = 0
  96. show_menu()
  97. def show_menu():
  98. print("\nCommands: ")
  99. print("\tEnter '1' to setup board limits")
  100. print("\tEnter '2' to setup food color")
  101. print("\tEnter '3' to insert image aspect ratio")
  102. print("\tEnter '4' to insert discrete image height and width")
  103. print("\tEnter 's' to save & quit")
  104. print("\tEnter 'q' to quit without saving")
  105. def null_callback(event, x, y, flags, param):
  106. return
  107. class BoardLimits:
  108. def __init__(self, img, scale, window_name):
  109. self.limits = []
  110. self.max_limits = 4
  111. self.orig = img
  112. self.img = img.copy()
  113. self.scale = scale
  114. self.window_name = window_name
  115. self.done = False
  116. self.limits_drawn = False
  117. def add_limit(self, x, y):
  118. x_img = int(x / self.scale)
  119. y_img = int(y / self.scale)
  120. self.limits.append((x_img, y_img))
  121. cv2.drawMarker(self.img, (x_img, y_img), (0, 0, 255), thickness=5)
  122. def draw(self):
  123. if len(self.limits) == self.max_limits and not self.limits_drawn:
  124. for i, limit in enumerate(self.limits):
  125. cv2.line(self.img, self.limits[i-1], limit, (0, 0, 255), thickness=3)
  126. self.limits_drawn = True
  127. cv2.imshow(self.window_name, cv2.resize(self.img, (0, 0), fx=self.scale, fy=self.scale))
  128. if self.enough_data():
  129. self.confirm()
  130. def enough_data(self):
  131. return len(self.limits) == self.max_limits
  132. def compute(self):
  133. return self.limits
  134. def toJSON(self):
  135. return {'Limits': self.limits}
  136. def help(self):
  137. print("--- Board Setup: Click on the {} corners of the board".format(self.max_limits))
  138. print("--- Board Setup: Please start from left corner and do it in the right order")
  139. def clear(self):
  140. self.limits = []
  141. self.limits_drawn = False
  142. self.img = self.orig.copy()
  143. self.done = False
  144. def on_mouse(self, event, x, y, flags, param):
  145. if event == cv2.EVENT_LBUTTONUP and not self.enough_data() :
  146. if len(self.limits) < self.max_limits:
  147. self.add_limit(x, y)
  148. def confirm(self):
  149. print("--- Board Setup: Press enter if you're ok with data or any other key if you want to restart setup...")
  150. key = cv2.waitKey(0)
  151. if key == 13: # Enter
  152. print("--- Board Setup: " + str(self.compute()))
  153. self.done = True
  154. else:
  155. self.clear()
  156. self.help()
  157. class FoodColor:
  158. def __init__(self, img, scale, window_name, max_qt=0):
  159. self.colors = []
  160. self.orig = img
  161. self.img = img.copy()
  162. self.scale = scale
  163. self.max_qt = max_qt
  164. self.window_name = window_name
  165. self.done = False
  166. def add(self, x, y):
  167. x_img = int(x / self.scale)
  168. y_img = int(y / self.scale)
  169. self.colors.append(self.orig[y_img, x_img])
  170. cv2.drawMarker(self.img, (x_img, y_img), (0, 0, 255), thickness=5)
  171. def draw(self):
  172. cv2.imshow(self.window_name, cv2.resize(self.img, (0, 0), fx=self.scale, fy=self.scale))
  173. if self.enough_data():
  174. self.confirm()
  175. def enough_data(self):
  176. if self.max_qt == 0:
  177. key = cv2.waitKey(10)
  178. return key == 13 # Enter
  179. else:
  180. return len(self.colors) == self.max_qt
  181. def compute(self):
  182. low_color = [255, 255, 255]
  183. high_color = [0, 0, 0]
  184. if len(self.colors) == 0:
  185. return tuple(high_color), tuple(low_color)
  186. for color in self.colors:
  187. for i, c in enumerate(color):
  188. if c < low_color[i]:
  189. low_color[i] = c
  190. if c > high_color[i]:
  191. high_color[i] = c
  192. return tuple(low_color), tuple(high_color)
  193. def toJSON(self):
  194. l, h = self.compute()
  195. l = tuple([int(x) for x in l])
  196. h = tuple([int(x) for x in h])
  197. return {'Low Food Color': l, 'High Food Color': h}
  198. def help(self):
  199. if self.max_qt == 0:
  200. print("--- Color Setup: Click several times on foods to setup food color and then press enter.")
  201. else:
  202. print("--- Color Setup: Click {} times on food to setup food color".format(self.max_qt))
  203. def clear(self):
  204. self.colors = []
  205. self.img = self.orig.copy()
  206. self.done = False
  207. def on_mouse(self, event, x, y, flags, param):
  208. if event == cv2.EVENT_LBUTTONUP and not self.enough_data() :
  209. self.add(x, y)
  210. def confirm(self):
  211. print("--- Color Setup: Press enter if you're ok with data or any other key if you want to restart setup...")
  212. key = cv2.waitKey(0)
  213. if key == 13: # Enter
  214. print("--- Color Setup: " + str(self.compute()))
  215. self.done = True
  216. else:
  217. self.clear()
  218. self.help()
  219. if __name__ == "__main__":
  220. main()