Repository for M.A.I.L system's analysis server.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

depthsensing.py 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. #!python3
  2. """
  3. Python 3 wrapper for identifying objects in images
  4. Requires DLL compilation
  5. Original *nix 2.7: https://github.com/pjreddie/darknet/blob/0f110834f4e18b30d5f101bf8f1724c34b7b83db/python/darknet.py
  6. Windows Python 2.7 version: https://github.com/AlexeyAB/darknet/blob/fc496d52bf22a0bb257300d3c79be9cd80e722cb/build/darknet/x64/darknet.py
  7. @author: Philip Kahn, Aymeric Dujardin
  8. @date: 20180911
  9. """
  10. # pylint: disable=R, W0401, W0614, W0703
  11. import os
  12. import sys
  13. import time
  14. import logging
  15. import random
  16. from random import randint
  17. import math
  18. import statistics
  19. import getopt
  20. from ctypes import *
  21. import numpy as np
  22. import cv2
  23. import pyzed.sl as sl
  24. import pandas as pd
  25. from pandas import DataFrame as df
  26. # Get the top-level logger object
  27. log = logging.getLogger(__name__)
  28. logging.basicConfig(level=logging.INFO)
  29. def sample(probs):
  30. s = sum(probs)
  31. probs = [a/s for a in probs]
  32. r = random.uniform(0, 1)
  33. for i in range(len(probs)):
  34. r = r - probs[i]
  35. if r <= 0:
  36. return i
  37. return len(probs)-1
  38. def c_array(ctype, values):
  39. arr = (ctype*len(values))()
  40. arr[:] = values
  41. return arr
  42. class BOX(Structure):
  43. _fields_ = [("x", c_float),
  44. ("y", c_float),
  45. ("w", c_float),
  46. ("h", c_float)]
  47. class DETECTION(Structure):
  48. _fields_ = [("bbox", BOX),
  49. ("classes", c_int),
  50. ("prob", POINTER(c_float)),
  51. ("mask", POINTER(c_float)),
  52. ("objectness", c_float),
  53. ("sort_class", c_int),
  54. ("uc", POINTER(c_float)),
  55. ("points", c_int),
  56. ("embeddings", POINTER(c_float)),
  57. ("embedding_size", c_int),
  58. ("sim", c_float),
  59. ("track_id", c_int)]
  60. class IMAGE(Structure):
  61. _fields_ = [("w", c_int),
  62. ("h", c_int),
  63. ("c", c_int),
  64. ("data", POINTER(c_float))]
  65. class METADATA(Structure):
  66. _fields_ = [("classes", c_int),
  67. ("names", POINTER(c_char_p))]
  68. #lib = CDLL("/home/pjreddie/documents/darknet/libdarknet.so", RTLD_GLOBAL)
  69. #lib = CDLL("darknet.so", RTLD_GLOBAL)
  70. hasGPU = True
  71. if os.name == "nt":
  72. cwd = os.path.dirname(__file__)
  73. os.environ['PATH'] = cwd + ';' + os.environ['PATH']
  74. winGPUdll = os.path.join(cwd, "yolo_cpp_dll.dll")
  75. winNoGPUdll = os.path.join(cwd, "yolo_cpp_dll_nogpu.dll")
  76. envKeys = list()
  77. for k, v in os.environ.items():
  78. envKeys.append(k)
  79. try:
  80. try:
  81. tmp = os.environ["FORCE_CPU"].lower()
  82. if tmp in ["1", "true", "yes", "on"]:
  83. raise ValueError("ForceCPU")
  84. else:
  85. log.info("Flag value '"+tmp+"' not forcing CPU mode")
  86. except KeyError:
  87. # We never set the flag
  88. if 'CUDA_VISIBLE_DEVICES' in envKeys:
  89. if int(os.environ['CUDA_VISIBLE_DEVICES']) < 0:
  90. raise ValueError("ForceCPU")
  91. try:
  92. global DARKNET_FORCE_CPU
  93. if DARKNET_FORCE_CPU:
  94. raise ValueError("ForceCPU")
  95. except NameError:
  96. pass
  97. # log.info(os.environ.keys())
  98. # log.warning("FORCE_CPU flag undefined, proceeding with GPU")
  99. if not os.path.exists(winGPUdll):
  100. raise ValueError("NoDLL")
  101. lib = CDLL(winGPUdll, RTLD_GLOBAL)
  102. except (KeyError, ValueError):
  103. hasGPU = False
  104. if os.path.exists(winNoGPUdll):
  105. lib = CDLL(winNoGPUdll, RTLD_GLOBAL)
  106. log.warning("Notice: CPU-only mode")
  107. else:
  108. # Try the other way, in case no_gpu was
  109. # compile but not renamed
  110. lib = CDLL(winGPUdll, RTLD_GLOBAL)
  111. log.warning("Environment variables indicated a CPU run, but we didn't find `" +
  112. winNoGPUdll+"`. Trying a GPU run anyway.")
  113. else:
  114. lib = CDLL("/root/darknet/libdarknet.so", RTLD_GLOBAL)
  115. lib.network_width.argtypes = [c_void_p]
  116. lib.network_width.restype = c_int
  117. lib.network_height.argtypes = [c_void_p]
  118. lib.network_height.restype = c_int
  119. predict = lib.network_predict
  120. predict.argtypes = [c_void_p, POINTER(c_float)]
  121. predict.restype = POINTER(c_float)
  122. if hasGPU:
  123. set_gpu = lib.cuda_set_device
  124. set_gpu.argtypes = [c_int]
  125. make_image = lib.make_image
  126. make_image.argtypes = [c_int, c_int, c_int]
  127. make_image.restype = IMAGE
  128. get_network_boxes = lib.get_network_boxes
  129. get_network_boxes.argtypes = [c_void_p, c_int, c_int, c_float, c_float, POINTER(
  130. c_int), c_int, POINTER(c_int), c_int]
  131. get_network_boxes.restype = POINTER(DETECTION)
  132. make_network_boxes = lib.make_network_boxes
  133. make_network_boxes.argtypes = [c_void_p]
  134. make_network_boxes.restype = POINTER(DETECTION)
  135. free_detections = lib.free_detections
  136. free_detections.argtypes = [POINTER(DETECTION), c_int]
  137. free_ptrs = lib.free_ptrs
  138. free_ptrs.argtypes = [POINTER(c_void_p), c_int]
  139. network_predict = lib.network_predict
  140. network_predict.argtypes = [c_void_p, POINTER(c_float)]
  141. reset_rnn = lib.reset_rnn
  142. reset_rnn.argtypes = [c_void_p]
  143. load_net = lib.load_network
  144. load_net.argtypes = [c_char_p, c_char_p, c_int]
  145. load_net.restype = c_void_p
  146. load_net_custom = lib.load_network_custom
  147. load_net_custom.argtypes = [c_char_p, c_char_p, c_int, c_int]
  148. load_net_custom.restype = c_void_p
  149. do_nms_obj = lib.do_nms_obj
  150. do_nms_obj.argtypes = [POINTER(DETECTION), c_int, c_int, c_float]
  151. do_nms_sort = lib.do_nms_sort
  152. do_nms_sort.argtypes = [POINTER(DETECTION), c_int, c_int, c_float]
  153. free_image = lib.free_image
  154. free_image.argtypes = [IMAGE]
  155. letterbox_image = lib.letterbox_image
  156. letterbox_image.argtypes = [IMAGE, c_int, c_int]
  157. letterbox_image.restype = IMAGE
  158. load_meta = lib.get_metadata
  159. lib.get_metadata.argtypes = [c_char_p]
  160. lib.get_metadata.restype = METADATA
  161. load_image = lib.load_image_color
  162. load_image.argtypes = [c_char_p, c_int, c_int]
  163. load_image.restype = IMAGE
  164. rgbgr_image = lib.rgbgr_image
  165. rgbgr_image.argtypes = [IMAGE]
  166. predict_image = lib.network_predict_image
  167. predict_image.argtypes = [c_void_p, IMAGE]
  168. predict_image.restype = POINTER(c_float)
  169. def array_to_image(arr):
  170. import numpy as np
  171. # need to return old values to avoid python freeing memory
  172. arr = arr.transpose(2, 0, 1)
  173. c = arr.shape[0]
  174. h = arr.shape[1]
  175. w = arr.shape[2]
  176. arr = np.ascontiguousarray(arr.flat, dtype=np.float32) / 255.0
  177. data = arr.ctypes.data_as(POINTER(c_float))
  178. im = IMAGE(w, h, c, data)
  179. return im, arr
  180. def classify(net, meta, im):
  181. out = predict_image(net, im)
  182. res = []
  183. for i in range(meta.classes):
  184. if altNames is None:
  185. name_tag = meta.names[i]
  186. else:
  187. name_tag = altNames[i]
  188. res.append((name_tag, out[i]))
  189. res = sorted(res, key=lambda x: -x[1])
  190. return res
  191. def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45, debug=False):
  192. """
  193. Performs the detection
  194. """
  195. custom_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
  196. custom_image = cv2.resize(custom_image, (lib.network_width(
  197. net), lib.network_height(net)), interpolation=cv2.INTER_LINEAR)
  198. im, arr = array_to_image(custom_image)
  199. num = c_int(0)
  200. pnum = pointer(num)
  201. predict_image(net, im)
  202. dets = get_network_boxes(
  203. net, image.shape[1], image.shape[0], thresh, hier_thresh, None, 0, pnum, 0)
  204. num = pnum[0]
  205. if nms:
  206. do_nms_sort(dets, num, meta.classes, nms)
  207. res = []
  208. if debug:
  209. log.debug("about to range")
  210. for j in range(num):
  211. for i in range(meta.classes):
  212. if dets[j].prob[i] > 0:
  213. b = dets[j].bbox
  214. if altNames is None:
  215. name_tag = meta.names[i]
  216. else:
  217. name_tag = altNames[i]
  218. res.append((name_tag, dets[j].prob[i], (b.x, b.y, b.w, b.h), i))
  219. res = sorted(res, key=lambda x: -x[1])
  220. free_detections(dets, num)
  221. return res
  222. netMain = None
  223. metaMain = None
  224. altNames = None
  225. def get_object_depth(depth, bounds):
  226. '''
  227. Calculates the median x, y, z position of top slice(area_div) of point cloud
  228. in camera frame.
  229. Arguments:
  230. depth: Point cloud data of whole frame.
  231. bounds: Bounding box for object in pixels.
  232. bounds[0]: x-center
  233. bounds[1]: y-center
  234. bounds[2]: width of bounding box.
  235. bounds[3]: height of bounding box.
  236. Return:
  237. x, y, z: Location of object in meters.
  238. '''
  239. area_div = 2
  240. x_vect = []
  241. y_vect = []
  242. z_vect = []
  243. for j in range(int(bounds[0] - area_div), int(bounds[0] + area_div)):
  244. for i in range(int(bounds[1] - area_div), int(bounds[1] + area_div)):
  245. z = depth[i, j, 2]
  246. if not np.isnan(z) and not np.isinf(z):
  247. x_vect.append(depth[i, j, 0])
  248. y_vect.append(depth[i, j, 1])
  249. z_vect.append(z)
  250. try:
  251. x_median = statistics.median(x_vect)
  252. y_median = statistics.median(y_vect)
  253. z_median = statistics.median(z_vect)
  254. except Exception:
  255. x_median = -1
  256. y_median = -1
  257. z_median = -1
  258. pass
  259. return x_median, y_median, z_median
  260. def generate_color(meta_path):
  261. '''
  262. Generate random colors for the number of classes mentioned in data file.
  263. Arguments:
  264. meta_path: Path to .data file.
  265. Return:
  266. color_array: RGB color codes for each class.
  267. '''
  268. random.seed(42)
  269. with open(meta_path, 'r') as f:
  270. content = f.readlines()
  271. class_num = int(content[0].split("=")[1])
  272. color_array = []
  273. for x in range(0, class_num):
  274. color_array.append((randint(0, 255), randint(0, 255), randint(0, 255)))
  275. return color_array
  276. def main(argv):
  277. thresh = 0.25
  278. darknet_path="../libdarknet/"
  279. config_path = darknet_path + "cfg/yolov3-tiny.cfg"
  280. weight_path = "yolov3-tiny.weights"
  281. meta_path = "coco.data"
  282. svo_path = None
  283. zed_id = 0
  284. help_str = 'darknet_zed.py -c <config> -w <weight> -m <meta> -t <threshold> -s <svo_file> -z <zed_id>'
  285. try:
  286. opts, args = getopt.getopt(
  287. argv, "hc:w:m:t:s:z:o:", ["config=", "weight=", "meta=", "threshold=", "svo_file=", "zed_id=", "output_path="])
  288. except getopt.GetoptError:
  289. log.exception(help_str)
  290. sys.exit(2)
  291. for opt, arg in opts:
  292. if opt == '-h':
  293. log.info(help_str)
  294. sys.exit()
  295. elif opt in ("-c", "--config"):
  296. config_path = arg
  297. elif opt in ("-w", "--weight"):
  298. weight_path = arg
  299. elif opt in ("-m", "--meta"):
  300. meta_path = arg
  301. elif opt in ("-t", "--threshold"):
  302. thresh = float(arg)
  303. elif opt in ("-s", "--svo_file"):
  304. svo_path = arg
  305. elif opt in ("-z", "--zed_id"):
  306. zed_id = int(arg)
  307. elif opt in ("-o", "--output_path"):
  308. output_path = arg
  309. output_path_nms = arg[:-4] + '_nms' + arg[-4:]
  310. input_type = sl.InputType()
  311. if svo_path is not None:
  312. log.info("SVO file : " + svo_path)
  313. input_type.set_from_svo_file(svo_path)
  314. else:
  315. # Launch camera by id
  316. input_type.set_from_camera_id(zed_id)
  317. init = sl.InitParameters(input_t=input_type)
  318. init.coordinate_units = sl.UNIT.METER
  319. cam = sl.Camera()
  320. if not cam.is_opened():
  321. log.info("Opening ZED Camera...")
  322. status = cam.open(init)
  323. if status != sl.ERROR_CODE.SUCCESS:
  324. log.error(repr(status))
  325. exit()
  326. runtime = sl.RuntimeParameters()
  327. # Use STANDARD sensing mode
  328. runtime.sensing_mode = sl.SENSING_MODE.STANDARD
  329. mat = sl.Mat()
  330. point_cloud_mat = sl.Mat()
  331. # Import the global variables. This lets us instance Darknet once,
  332. # then just call performDetect() again without instancing again
  333. global metaMain, netMain, altNames # pylint: disable=W0603
  334. assert 0 < thresh < 1, "Threshold should be a float between zero and one (non-inclusive)"
  335. if not os.path.exists(config_path):
  336. raise ValueError("Invalid config path `" +
  337. os.path.abspath(config_path)+"`")
  338. if not os.path.exists(weight_path):
  339. raise ValueError("Invalid weight path `" +
  340. os.path.abspath(weight_path)+"`")
  341. if not os.path.exists(meta_path):
  342. raise ValueError("Invalid data file path `" +
  343. os.path.abspath(meta_path)+"`")
  344. if netMain is None:
  345. netMain = load_net_custom(config_path.encode(
  346. "ascii"), weight_path.encode("ascii"), 0, 1) # batch size = 1
  347. if metaMain is None:
  348. metaMain = load_meta(meta_path.encode("ascii"))
  349. if altNames is None:
  350. # In thon 3, the metafile default access craps out on Windows (but not Linux)
  351. # Read the names file and create a list to feed to detect
  352. try:
  353. with open(meta_path) as meta_fh:
  354. meta_contents = meta_fh.read()
  355. import re
  356. match = re.search("names *= *(.*)$", meta_contents,
  357. re.IGNORECASE | re.MULTILINE)
  358. if match:
  359. result = match.group(1)
  360. else:
  361. result = None
  362. try:
  363. if os.path.exists(result):
  364. with open(result) as names_fh:
  365. names_list = names_fh.read().strip().split("\n")
  366. altNames = [x.strip() for x in names_list]
  367. except TypeError:
  368. pass
  369. except Exception:
  370. pass
  371. color_array = generate_color(meta_path)
  372. #log.info("Running...")
  373. df1 = df(data={'x': [], 'y': [], 'z': [], 'frame': []})
  374. df_nms = df(data={'x': [], 'y': [], 'z': [],'nms':[] ,'frame': []})
  375. count = 0
  376. frame = 1
  377. svo_frame = cam.get_svo_number_of_frames()
  378. quit_timer = time.time()
  379. key = ''
  380. while frame != svo_frame: # for 'q' key
  381. start_time = time.time() # start time of the loop
  382. err = cam.grab(runtime)
  383. if err == sl.ERROR_CODE.SUCCESS:
  384. cam.retrieve_image(mat, sl.VIEW.LEFT)
  385. image = mat.get_data()
  386. cam.retrieve_measure(
  387. point_cloud_mat, sl.MEASURE.XYZRGBA)
  388. depth = point_cloud_mat.get_data()
  389. # Do the detection
  390. detections = detect(netMain, metaMain, image, thresh)
  391. log.info(chr(27) + "[2J"+"**** " + str(len(detections)) + " Results ****")
  392. for detection in detections:
  393. log.info(detection)
  394. label = detection[0]
  395. confidence = detection[1]
  396. pstring = label+": "+str(np.rint(100 * confidence))+"%"
  397. log.info(pstring)
  398. bounds = detection[2]
  399. y_extent = int(bounds[3])
  400. x_extent = int(bounds[2])
  401. # Coordinates are around the center
  402. x_coord = int(bounds[0] - bounds[2]/2)
  403. y_coord = int(bounds[1] - bounds[3]/2)
  404. #boundingBox = [[x_coord, y_coord], [x_coord, y_coord + y_extent], [x_coord + x_extent, y_coord + y_extent], [x_coord + x_extent, y_coord]]
  405. thickness = 1
  406. x, y, z = get_object_depth(depth, bounds)
  407. distance = math.sqrt(x * x + y * y + z * z)
  408. distance = "{:.8f}".format(distance)
  409. cv2.rectangle(image, (x_coord - thickness, y_coord - thickness),
  410. (x_coord + x_extent + thickness, y_coord + (18 + thickness*4)),
  411. color_array[detection[3]], -1)
  412. cv2.putText(image, label + " " + (str(distance) + " m"),
  413. (x_coord + (thickness * 4), y_coord + (10 + thickness * 4)),
  414. cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
  415. cv2.rectangle(image, (x_coord - thickness, y_coord - thickness),
  416. (x_coord + x_extent + thickness, y_coord + y_extent + thickness),
  417. color_array[detection[3]], int(thickness*2))
  418. x = x_coord-thickness
  419. x1 = x_coord + x_extent+thickness
  420. y = y_coord-thickness
  421. y1 = y_coord + y_extent+thickness
  422. df1.loc[count] = [int(x_coord), int(y_coord), distance, int(frame)]
  423. df_nms.loc[count] = [int(x_coord), int(y_coord), distance, np.rint(100*confidence), int(frame)]
  424. count += 1
  425. #cv2.imshow("ZED", image)
  426. #key = cv2.waitKey(5)
  427. log.info("FPS: {}".format(1.0 / (time.time() - start_time)))
  428. log.info("\n"+str(frame))
  429. frame += 1
  430. else:
  431. pass
  432. #key = cv2.waitKey(5)
  433. #cv2.destroyAllWindows()
  434. cam.close()
  435. df1.to_csv(output_path, index=False)
  436. df_nms.to_csv(output_path_nms, index=False)
  437. log.info("\nFINISH")
  438. if __name__ == "__main__":
  439. main(sys.argv[1:])