evaluator.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import argparse
  2. import glob
  3. import os
  4. import time
  5. import cv2
  6. import numpy as np
  7. import pybgs as bgs
  8. ALGORITHMS_TO_EVALUATE = [
  9. (cv2.bgsegm.createBackgroundSubtractorGSOC, "GSoC", {}),
  10. (bgs.SuBSENSE, "SuBSENSE", {}),
  11. ]
  12. # https://github.com/opencv/opencv_contrib/blob/master/modules/bgsegm/samples/evaluation.py
  13. def contains_relevant_files(root):
  14. return os.path.isdir(os.path.join(root, "groundtruth")) and os.path.isdir(
  15. os.path.join(root, "input"),
  16. )
  17. def find_relevant_dirs(root):
  18. relevant_dirs = []
  19. for d in sorted(os.listdir(root)):
  20. d = os.path.join(root, d)
  21. if os.path.isdir(d):
  22. if contains_relevant_files(d):
  23. relevant_dirs += [d]
  24. else:
  25. relevant_dirs += find_relevant_dirs(d)
  26. return relevant_dirs
  27. def load_sequence(root):
  28. gt_dir, frames_dir = os.path.join(root, "groundtruth"), os.path.join(root, "input")
  29. gt = sorted(glob.glob(os.path.join(gt_dir, "*.png")))
  30. f = sorted(glob.glob(os.path.join(frames_dir, "*.jpg")))
  31. assert len(gt) == len(f)
  32. return gt, f
  33. def evaluate_algorithm(gt, frames, algo, algo_arguments):
  34. # instantiate background subtraction model
  35. bgs = algo(**algo_arguments)
  36. mask = []
  37. # start time evaluation
  38. t_start = time.time()
  39. for i in range(len(gt)):
  40. # read frames
  41. frame = np.uint8(cv2.imread(frames[i], cv2.IMREAD_COLOR))
  42. # feed the frames into the model
  43. mask.append(bgs.apply(frame))
  44. average_duration = (time.time() - t_start) / len(gt)
  45. average_precision, average_recall, average_f1, average_accuracy = [], [], [], []
  46. # initiate iteration over GT frames
  47. for i in range(len(gt)):
  48. # get GT masks
  49. gt_mask = np.uint8(cv2.imread(gt[i], cv2.IMREAD_GRAYSCALE))
  50. # obtain region of interest
  51. roi = (gt_mask == 255) | (gt_mask == 0)
  52. if roi.sum() > 0:
  53. gt_answer, answer = gt_mask[roi], mask[i][roi]
  54. # calculate true positives, true negatives, false positives, false negatives
  55. tp = ((answer == 255) & (gt_answer == 255)).sum()
  56. tn = ((answer == 0) & (gt_answer == 0)).sum()
  57. fp = ((answer == 255) & (gt_answer == 0)).sum()
  58. fn = ((answer == 0) & (gt_answer == 255)).sum()
  59. # compute precision, recall, F1, accuracy to evaluate BS-model work
  60. if tp + fp > 0:
  61. average_precision.append(float(tp) / (tp + fp))
  62. if tp + fn > 0:
  63. average_recall.append(float(tp) / (tp + fn))
  64. if tp + fn + fp > 0:
  65. average_f1.append(2.0 * tp / (2.0 * tp + fn + fp))
  66. average_accuracy.append(float(tp + tn) / (tp + tn + fp + fn))
  67. return (
  68. average_duration,
  69. np.mean(average_precision),
  70. np.mean(average_recall),
  71. np.mean(average_f1),
  72. np.mean(average_accuracy),
  73. )
  74. def evaluate_on_sequence(seq, summary):
  75. gt, frames = load_sequence(seq)
  76. category, video_name = os.path.basename(os.path.dirname(seq)), os.path.basename(seq)
  77. print("=== %s:%s ===" % (category, video_name))
  78. for algo, algo_name, algo_arguments in ALGORITHMS_TO_EVALUATE:
  79. print("Algorithm name: %s" % algo_name)
  80. sec_per_step, precision, recall, f1, accuracy = evaluate_algorithm(
  81. gt, frames, algo, algo_arguments,
  82. )
  83. print("Average accuracy: %.3f" % accuracy)
  84. print("Average precision: %.3f" % precision)
  85. print("Average recall: %.3f" % recall)
  86. print("Average F1: %.3f" % f1)
  87. print("Average sec. per step: %.4f" % sec_per_step)
  88. print("")
  89. if category not in summary:
  90. summary[category] = {}
  91. if algo_name not in summary[category]:
  92. summary[category][algo_name] = []
  93. summary[category][algo_name].append((precision, recall, f1, accuracy))
  94. def main():
  95. parser = argparse.ArgumentParser(
  96. description="Evaluate all background subtractors using Change Detection dataset",
  97. )
  98. parser.add_argument(
  99. "--dataset_path",
  100. help="Path to the directory with dataset. It may contain multiple inner directories. It will be scanned recursively.",
  101. required=True,
  102. )
  103. parser.add_argument("--algorithm", help="Test particular algorithm instead of all.")
  104. args = parser.parse_args()
  105. dataset_dirs = find_relevant_dirs(args.dataset_path)
  106. assert len(dataset_dirs) > 0, (
  107. "Passed directory must contain at least one sequence from the Change Detection dataset. There is no relevant directories in %s. Check that this directory is correct."
  108. % (args.dataset_path)
  109. )
  110. if args.algorithm is not None:
  111. global ALGORITHMS_TO_EVALUATE
  112. ALGORITHMS_TO_EVALUATE = filter(
  113. lambda a: a[1].lower() == args.algorithm.lower(), ALGORITHMS_TO_EVALUATE,
  114. )
  115. summary = {}
  116. for seq in dataset_dirs:
  117. evaluate_on_sequence(seq, summary)
  118. for category in summary:
  119. for algo_name in summary[category]:
  120. summary[category][algo_name] = np.mean(summary[category][algo_name], axis=0)
  121. algorithms_results = {
  122. "GSoC": [],
  123. "SuBSENSE": [],
  124. }
  125. for category in summary:
  126. print("=== SUMMARY for %s (Precision, Recall, F1, Accuracy) ===" % category)
  127. for algo_name in summary[category]:
  128. print(
  129. "%05s: %.3f %.3f %.3f %.3f"
  130. % ((algo_name,) + tuple(summary[category][algo_name])),
  131. )
  132. algorithms_results[algo_name].append(summary[category][algo_name])
  133. print("=== SUMMARY for all video categories (Precision, Recall, F1, Accuracy) ===")
  134. for algo_name in algorithms_results:
  135. algorithms_results[algo_name] = np.mean(
  136. np.array(algorithms_results[algo_name]), axis=0,
  137. )
  138. res_array = algorithms_results[algo_name]
  139. print(
  140. "{}: {:.3f}, {:.3f}, {:.3f}, {:.3f}".format(
  141. algo_name, res_array[0], res_array[1], res_array[2], res_array[3],
  142. ),
  143. )
  144. if __name__ == "__main__":
  145. main()