From bb5cb224c9abe4216aaa49a8287b06d9f05dab60 Mon Sep 17 00:00:00 2001 From: xuepengqiang <506321815@qq.com> Date: 星期二, 26 五月 2020 20:03:36 +0800 Subject: [PATCH] add m --- lib/deep_sort/__init__.py | 6 model_dump/yolo_anchors.txt | 1 lib/utils/__pycache__/utils.cpython-36.pyc | 0 lib/core/ds_tracker.py | 118 ++++ lib/deep_sort/__pycache__/track.cpython-36.pyc | 0 lib/yolo3/model.py | 300 ++++++++++++ lib/yolo3/__init__.pyc | 0 lib/yolo3/__pycache__/__init__.cpython-36.pyc | 0 demo.py | 29 + lib/deep_sort/linear_assignment.py | 84 +++ lib/deep_sort/__pycache__/iou_matching.cpython-36.pyc | 0 lib/__pycache__/__init__.cpython-36.pyc | 0 lib/yolo3/__init__.py | 0 lib/utils/__init__.py | 6 lib/deep_sort/track.py | 66 ++ lib/core/__pycache__/yolo.cpython-36.pyc | 0 lib/core/yolo.py | 114 ++++ lib/deep_sort/__pycache__/detection.cpython-36.pyc | 0 lib/core/ds_tracker.pyc | 0 lib/deep_sort/__pycache__/nn_matching.cpython-36.pyc | 0 model_dump/coco_classes.txt | 80 +++ lib/deep_sort/__pycache__/__init__.cpython-36.pyc | 0 lib/yolo3/utils.pyc | 0 lib/deep_sort/kalman_filter.py | 107 ++++ lib/deep_sort/tracker.py | 94 +++ lib/deep_sort/__pycache__/linear_assignment.cpython-36.pyc | 0 lib/yolo3/utils.py | 20 lib/core/__pycache__/__init__.cpython-36.pyc | 0 lib/yolo3/model.pyc | 0 lib/core/__pycache__/generate_detections.cpython-36.pyc | 0 lib/utils/utils.py | 19 lib/core/__init__.py | 6 lib/deep_sort/__pycache__/kalman_filter.cpython-36.pyc | 0 lib/__init__.pyc | 0 lib/core/__init__.pyc | 0 lib/deep_sort/iou_matching.py | 39 + model_dump/voc_classes.txt | 20 lib/yolo3/__pycache__/utils.cpython-36.pyc | 0 lib/__init__.py | 6 lib/deep_sort/nn_matching.py | 62 ++ lib/utils/__pycache__/__init__.cpython-36.pyc | 0 lib/yolo3/__pycache__/model.cpython-36.pyc | 0 lib/core/__pycache__/ds_tracker.cpython-36.pyc | 0 /dev/null | 4 lib/config/cpu_run.dll | 0 lib/deep_sort/detection.py | 19 lib/deep_sort/__pycache__/tracker.cpython-36.pyc | 0 lib/deep_sort/__pycache__/preprocessing.cpython-36.pyc | 0 lib/deep_sort/preprocessing.py | 43 + lib/core/generate_detections.py | 168 +++++++ 50 files changed, 1,407 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 087029a..0000000 --- a/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.cache -nosetests.xml -coverage.xml - -# Translations -*.mo -*.pot - -# Django stuff: -*.log - -# Sphinx documentation -docs/_build/ diff --git a/README.md b/README.md deleted file mode 100644 index 0f5e239..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -## yolo_track_python - -yolo_track_python - diff --git a/demo.py b/demo.py new file mode 100644 index 0000000..13a9864 --- /dev/null +++ b/demo.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 11:55 +# @Author : Scheaven +# @File : demo.py +# @description: +import argparse +from lib.core.ds_tracker import human_tracker +from lib.core.yolo import YOLO +from lib.core import generate_detections as gdet +import os +os.environ["CUDA_VISIBLE_DEVICES"] = "1" + +######################paraters###################### +def parse_args(): + parser = argparse.ArgumentParser(description="Deep SORT") + parser.add_argument("-i", "--in_type", help="camera or video", + default='video', required=False) + parser.add_argument("--c_in", help="camera ", + default='rtsp://admin:a1234567@192.168.5.32:554/h264/ch1/main/av_stream', required=False) + parser.add_argument("--v_in", help="video", + default='../cs01.avi', required=False) + return parser.parse_args() + +if __name__ == '__main__': + args = parse_args() + model_filename = 'model_dump/mars-small128.pb' + encoder = gdet.create_box_encoder(model_filename, batch_size=1) + human_tracker(YOLO(), encoder, args) \ No newline at end of file diff --git a/lib/__init__.py b/lib/__init__.py new file mode 100644 index 0000000..5c1b681 --- /dev/null +++ b/lib/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 11:55 +# @Author : Scheaven +# @File : __init__.py.py +# @description: \ No newline at end of file diff --git a/lib/__init__.pyc b/lib/__init__.pyc new file mode 100644 index 0000000..c202475 --- /dev/null +++ b/lib/__init__.pyc Binary files differ diff --git a/lib/__pycache__/__init__.cpython-36.pyc b/lib/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..7edf4cc --- /dev/null +++ b/lib/__pycache__/__init__.cpython-36.pyc Binary files differ diff --git a/lib/config/cpu_run.dll b/lib/config/cpu_run.dll new file mode 100644 index 0000000..39a478f --- /dev/null +++ b/lib/config/cpu_run.dll Binary files differ diff --git a/lib/core/__init__.py b/lib/core/__init__.py new file mode 100644 index 0000000..10cbb56 --- /dev/null +++ b/lib/core/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 13:39 +# @Author : Scheaven +# @File : __init__.py.py +# @description: \ No newline at end of file diff --git a/lib/core/__init__.pyc b/lib/core/__init__.pyc new file mode 100644 index 0000000..442a60d --- /dev/null +++ b/lib/core/__init__.pyc Binary files differ diff --git a/lib/core/__pycache__/__init__.cpython-36.pyc b/lib/core/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..ad94064 --- /dev/null +++ b/lib/core/__pycache__/__init__.cpython-36.pyc Binary files differ diff --git a/lib/core/__pycache__/ds_tracker.cpython-36.pyc b/lib/core/__pycache__/ds_tracker.cpython-36.pyc new file mode 100644 index 0000000..1202ee6 --- /dev/null +++ b/lib/core/__pycache__/ds_tracker.cpython-36.pyc Binary files differ diff --git a/lib/core/__pycache__/generate_detections.cpython-36.pyc b/lib/core/__pycache__/generate_detections.cpython-36.pyc new file mode 100644 index 0000000..4f4a0a2 --- /dev/null +++ b/lib/core/__pycache__/generate_detections.cpython-36.pyc Binary files differ diff --git a/lib/core/__pycache__/yolo.cpython-36.pyc b/lib/core/__pycache__/yolo.cpython-36.pyc new file mode 100644 index 0000000..20eb745 --- /dev/null +++ b/lib/core/__pycache__/yolo.cpython-36.pyc Binary files differ diff --git a/lib/core/ds_tracker.py b/lib/core/ds_tracker.py new file mode 100644 index 0000000..a95a8f8 --- /dev/null +++ b/lib/core/ds_tracker.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 13:40 +# @Author : Scheaven +# @File : ds_tracker.py +# @description: +from timeit import time +import cv2 +import numpy as np +from PIL import Image +from lib.deep_sort.tracker import Tracker +from lib.deep_sort import preprocessing +from lib.deep_sort import nn_matching +from lib.deep_sort.detection import Detection +from lib.utils.utils import video_open +from collections import deque + +pts = [deque(maxlen=30) for _ in range(9999)] +np.random.seed(100) +COLORS = np.random.randint(0, 255, size=(200, 3), + dtype="uint8") + +def human_tracker(yolo, encoder, args): + max_cosine_distance = 0.3 + nn_budget = None + nms_max_overlap = 1.0 + + metric = nn_matching.NearestNeighborDistanceMetric("cosine", max_cosine_distance, nn_budget) + tracker = Tracker(metric) + + video = video_open(args) + video_capture = video.generate_video() + w = int(video_capture.get(3)) + h = int(video_capture.get(4)) + fourcc = cv2.VideoWriter_fourcc(*'MJPG') + out = cv2.VideoWriter('./output/' + '_output.avi', fourcc, 15, (w, h)) + i = 0 + fps = 0 + track_fps = 0 + while True: + ret, frame = video_capture.read() + if ret != True: + break + t1 = time.time() + image = Image.fromarray(frame) + time3 = time.time() + boxs = yolo.detect_image(image) + time4 = time.time() + print('detect cost is', time4 - time3) + features = encoder(frame, boxs) + print("features shape: ", features.shape) + + detections = [Detection(bbox, 1.0, feature) for bbox, feature in zip(boxs, features)] + + boxes = np.array([d.tlwh for d in detections]) + scores = np.array([d.confidence for d in detections]) + indices = preprocessing.non_max_suppression(boxes, nms_max_overlap, scores) + detections = [detections[i] for i in indices] + + print('features extract is', time4 - time3) + + + + tracker.predict() + tracker.update(detections) + + fps = (fps + (1. / (time.time() - t1))) / 2 + track_fps = (track_fps + (1. / (time.time() - time4))) / 2 + print("fps= %f" % (fps)) + indexIDs = [] + i = int(0) + for track in tracker.tracks: + if track.is_confirmed() and track.time_since_update > 1: + continue + indexIDs.append(int(track.track_id)) + color = [int(c) for c in COLORS[indexIDs[i] % len(COLORS)]] + + bbox = track.to_tlbr() + cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), (color), 2) + # cv2.putText(frame, str(track.track_id), (int(bbox[0]), int(bbox[1])), 0, 5e-3 * 200, (0, 255, 0), 2) + # cv2.putText(frame, str(round(fps, 2)), (100, 100), 0, 5e-3 * 300, (0, 0 , 255), 2) + # cv2.putText(frame, str(track_fps), (100, 50), 0, 5e-3 * 300, (0, 0 , 255), 2) + cv2.putText(frame, str(track.track_id), (int(bbox[0]), int(bbox[1] - 20)), 0, 5e-3 * 150, (color), 2) + + + # cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), (color), 3) + # cv2.putText(frame, str(track.track_id), (int(bbox[0]), int(bbox[1] - 50)), 0, 5e-3 * 150, (color), 2) + + # cv2.putText(frame, str(class_names[0]), (int(bbox[0]), int(bbox[1] - 20)), 0, 5e-3 * 150, (color), 2) + + center = (int(((bbox[0]) + (bbox[2])) / 2), int(((bbox[1]) + (bbox[3])) / 2)) + # track_id[center] + i += 1 + pts[track.track_id].append(center) + thickness = 5 + cv2.circle(frame, (center), 1, color, thickness) + + for j in range(1, len(pts[track.track_id])): + if pts[track.track_id][j - 1] is None or pts[track.track_id][j] is None: + continue + thickness = int(np.sqrt(64 / float(j + 1)) * 2) + cv2.line(frame, (pts[track.track_id][j - 1]), (pts[track.track_id][j]), (color), thickness) + + for det in detections: + bbox = det.to_tlbr() + # cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), (255, 0, 0), 2) + cv2.rectangle(frame, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), (255, 255, 255), 2) + + # cv2.imshow('', cv2.resize(frame,(854, 480))) + out.write(frame) + + + + if cv2.waitKey(1) & 0xFF == ord('q'): + break + + video_capture.release() + cv2.destroyAllWindows() \ No newline at end of file diff --git a/lib/core/ds_tracker.pyc b/lib/core/ds_tracker.pyc new file mode 100644 index 0000000..31469c3 --- /dev/null +++ b/lib/core/ds_tracker.pyc Binary files differ diff --git a/lib/core/generate_detections.py b/lib/core/generate_detections.py new file mode 100644 index 0000000..d5a906c --- /dev/null +++ b/lib/core/generate_detections.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 13:59 +# @Author : Scheaven +# @File : generate_detections.py +# @description: +import os +import errno +import argparse +import numpy as np +import cv2 +import tensorflow as tf + +def _run_in_batches(f, data_dict, out, batch_size): + data_len = len(out) + num_batches = int(data_len / batch_size) + + s, e = 0, 0 + for i in range(num_batches): + s, e = i * batch_size, (i + 1) * batch_size + batch_data_dict = {k: v[s:e] for k, v in data_dict.items()} + out[s:e] = f(batch_data_dict) + if e < len(out): + batch_data_dict = {k: v[e:] for k, v in data_dict.items()} + out[e:] = f(batch_data_dict) + +def extract_image_patch(image, bbox, patch_shape): + bbox = np.array(bbox) + if patch_shape is not None: + target_aspect = float(patch_shape[1]) / patch_shape[0] + new_width = target_aspect * bbox[3] + bbox[0] -= (new_width - bbox[2]) / 2 + bbox[2] = new_width + + bbox[2:] += bbox[:2] + bbox = bbox.astype(np.int) + + bbox[:2] = np.maximum(0, bbox[:2]) + bbox[2:] = np.minimum(np.asarray(image.shape[:2][::-1]) - 1, bbox[2:]) + if np.any(bbox[:2] >= bbox[2:]): + return None + sx, sy, ex, ey = bbox + image = image[sy:ey, sx:ex] + image = cv2.resize(image, tuple(patch_shape[::-1])) + return image + + +class ImageEncoder(object): + + def __init__(self, checkpoint_filename, input_name="images", + output_name="features"): + self.session = tf.Session() + with tf.gfile.GFile(checkpoint_filename, "rb") as file_handle: + graph_def = tf.GraphDef() + graph_def.ParseFromString(file_handle.read()) + tf.import_graph_def(graph_def, name="net") + self.input_var = tf.get_default_graph().get_tensor_by_name( + "net/%s:0" % input_name) + self.output_var = tf.get_default_graph().get_tensor_by_name( + "net/%s:0" % output_name) + + assert len(self.output_var.get_shape()) == 2 + assert len(self.input_var.get_shape()) == 4 + self.feature_dim = self.output_var.get_shape().as_list()[-1] + self.image_shape = self.input_var.get_shape().as_list()[1:] + + def __call__(self, data_x, batch_size=32): + out = np.zeros((len(data_x), self.feature_dim), np.float32) + _run_in_batches( + lambda x: self.session.run(self.output_var, feed_dict=x), + {self.input_var: data_x}, out, batch_size) + return out + +def create_box_encoder(model_filename, input_name="images", + output_name="features", batch_size=32): + image_encoder = ImageEncoder(model_filename, input_name, output_name) + image_shape = image_encoder.image_shape + + def encoder(image, boxes): + image_patches = [] + for box in boxes: + patch = extract_image_patch(image, box, image_shape[:2]) # image 涓殑human_boxs閮ㄥ垎 + if patch is None: + print("WARNING: Failed to extract image patch: %s." % str(box)) + patch = np.random.uniform( + 0., 255., image_shape).astype(np.uint8) + image_patches.append(patch) + image_patches = np.asarray(image_patches) + return image_encoder(image_patches, batch_size) + + return encoder + +def generate_detections(encoder, mot_dir, output_dir, detection_dir=None): + if detection_dir is None: + detection_dir = mot_dir + try: + os.makedirs(output_dir) + except OSError as exception: + if exception.errno == errno.EEXIST and os.path.isdir(output_dir): + pass + else: + raise ValueError( + "Failed to created output directory '%s'" % output_dir) + + for sequence in os.listdir(mot_dir): + print("Processing %s" % sequence) + sequence_dir = os.path.join(mot_dir, sequence) + + image_dir = os.path.join(sequence_dir, "img1") + image_filenames = { + int(os.path.splitext(f)[0]): os.path.join(image_dir, f) + for f in os.listdir(image_dir)} + + detection_file = os.path.join( + detection_dir, sequence, "det/det.txt") + detections_in = np.loadtxt(detection_file, delimiter=',') + detections_out = [] + + frame_indices = detections_in[:, 0].astype(np.int) + min_frame_idx = frame_indices.astype(np.int).min() + max_frame_idx = frame_indices.astype(np.int).max() + for frame_idx in range(min_frame_idx, max_frame_idx + 1): + print("Frame %05d/%05d" % (frame_idx, max_frame_idx)) + mask = frame_indices == frame_idx + rows = detections_in[mask] + + if frame_idx not in image_filenames: + print("WARNING could not find image for frame %d" % frame_idx) + continue + bgr_image = cv2.imread( + image_filenames[frame_idx], cv2.IMREAD_COLOR) + features = encoder(bgr_image, rows[:, 2:6].copy()) + detections_out += [np.r_[(row, feature)] for row, feature + in zip(rows, features)] + + output_filename = os.path.join(output_dir, "%s.npy" % sequence) + np.save( + output_filename, np.asarray(detections_out), allow_pickle=False) + + +def parse_args(): + parser = argparse.ArgumentParser(description="Re-ID feature extractor") + parser.add_argument( + "--model", + default="model_dump/mars-small128.pb", + help="Path to freezed inference graph protobuf.") + parser.add_argument( + "--mot_dir", help="Path to MOTChallenge directory (train or test)", + required=True) + parser.add_argument( + "--detection_dir", help="Path to custom detections. Defaults to " + "standard MOT detections Directory structure should be the default " + "MOTChallenge structure: [sequence]/det/det.txt", default=None) + parser.add_argument( + "--output_dir", help="Output directory. Will be created if it does not" + " exist.", default="detections") + return parser.parse_args() + + +def main(): + args = parse_args() + encoder = create_box_encoder(args.model, batch_size=32) + generate_detections(encoder, args.mot_dir, args.output_dir, + args.detection_dir) + + +if __name__ == "__main__": + main() diff --git a/lib/core/yolo.py b/lib/core/yolo.py new file mode 100644 index 0000000..369a82a --- /dev/null +++ b/lib/core/yolo.py @@ -0,0 +1,114 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- + +import colorsys +import os +import random +from timeit import time +from timeit import default_timer as timer + +import numpy as np +from keras import backend as K +from keras.models import load_model +from PIL import Image, ImageFont, ImageDraw + +from lib.yolo3.model import yolo_eval +from lib.yolo3.utils import letterbox_image + +class YOLO(object): + def __init__(self): + self.model_path = 'model_dump/yolo.h5' + self.anchors_path = 'model_dump/yolo_anchors.txt' + self.classes_path = 'model_dump/coco_classes.txt' + self.score = 0.5 + self.iou = 0.5 + self.class_names = self._get_class() + self.anchors = self._get_anchors() + self.sess = K.get_session() + self.model_image_size = (416, 416) + self.is_fixed_size = self.model_image_size != (None, None) + self.boxes, self.scores, self.classes = self.generate() + + def _get_class(self): + classes_path = os.path.expanduser(self.classes_path) + with open(classes_path) as f: + class_names = f.readlines() + class_names = [c.strip() for c in class_names] + return class_names + + def _get_anchors(self): + anchors_path = os.path.expanduser(self.anchors_path) + with open(anchors_path) as f: + anchors = f.readline() + anchors = [float(x) for x in anchors.split(',')] + anchors = np.array(anchors).reshape(-1, 2) + return anchors + + def generate(self): + model_path = os.path.expanduser(self.model_path) + assert model_path.endswith('.h5'), 'Keras model must be a .h5 file.' + + self.yolo_model = load_model(model_path, compile=False) + print('{} model, anchors, and classes loaded.'.format(model_path)) + + hsv_tuples = [(x / len(self.class_names), 1., 1.) + for x in range(len(self.class_names))] + self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) + self.colors = list( + map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), + self.colors)) + random.seed(10101) + random.shuffle(self.colors) + random.seed(None) + + self.input_image_shape = K.placeholder(shape=(2, )) + boxes, scores, classes = yolo_eval(self.yolo_model.output, self.anchors, + len(self.class_names), self.input_image_shape, + score_threshold=self.score, iou_threshold=self.iou) + return boxes, scores, classes + + def detect_image(self, image): + if self.is_fixed_size: + assert self.model_image_size[0]%32 == 0, 'Multiples of 32 required' + assert self.model_image_size[1]%32 == 0, 'Multiples of 32 required' + boxed_image = letterbox_image(image, tuple(reversed(self.model_image_size))) + else: + new_image_size = (image.width - (image.width % 32), + image.height - (image.height % 32)) + boxed_image = letterbox_image(image, new_image_size) + image_data = np.array(boxed_image, dtype='float32') + + #print(image_data.shape) + image_data /= 255. + image_data = np.expand_dims(image_data, 0) + + out_boxes, out_scores, out_classes = self.sess.run( + [self.boxes, self.scores, self.classes], + feed_dict={ + self.yolo_model.input: image_data, + self.input_image_shape: [image.size[1], image.size[0]], + K.learning_phase(): 0 + }) + return_boxs = [] + for i, c in reversed(list(enumerate(out_classes))): + predicted_class = self.class_names[c] + if predicted_class != 'person' : + continue + box = out_boxes[i] + # score = out_scores[i] + x = int(box[1]) + y = int(box[0]) + w = int(box[3]-box[1]) + h = int(box[2]-box[0]) + if x < 0 : + w = w + x + x = 0 + if y < 0 : + h = h + y + y = 0 + return_boxs.append([x,y,w,h]) + + return return_boxs + + def close_session(self): + self.sess.close() diff --git a/lib/deep_sort/__init__.py b/lib/deep_sort/__init__.py new file mode 100644 index 0000000..df7be20 --- /dev/null +++ b/lib/deep_sort/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 15:04 +# @Author : Scheaven +# @File : __init__.py.py +# @description: \ No newline at end of file diff --git a/lib/deep_sort/__pycache__/__init__.cpython-36.pyc b/lib/deep_sort/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..40268dd --- /dev/null +++ b/lib/deep_sort/__pycache__/__init__.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/detection.cpython-36.pyc b/lib/deep_sort/__pycache__/detection.cpython-36.pyc new file mode 100644 index 0000000..3b79de4 --- /dev/null +++ b/lib/deep_sort/__pycache__/detection.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/iou_matching.cpython-36.pyc b/lib/deep_sort/__pycache__/iou_matching.cpython-36.pyc new file mode 100644 index 0000000..3191df2 --- /dev/null +++ b/lib/deep_sort/__pycache__/iou_matching.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/kalman_filter.cpython-36.pyc b/lib/deep_sort/__pycache__/kalman_filter.cpython-36.pyc new file mode 100644 index 0000000..95673c0 --- /dev/null +++ b/lib/deep_sort/__pycache__/kalman_filter.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/linear_assignment.cpython-36.pyc b/lib/deep_sort/__pycache__/linear_assignment.cpython-36.pyc new file mode 100644 index 0000000..291cf5e --- /dev/null +++ b/lib/deep_sort/__pycache__/linear_assignment.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/nn_matching.cpython-36.pyc b/lib/deep_sort/__pycache__/nn_matching.cpython-36.pyc new file mode 100644 index 0000000..51f9a00 --- /dev/null +++ b/lib/deep_sort/__pycache__/nn_matching.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/preprocessing.cpython-36.pyc b/lib/deep_sort/__pycache__/preprocessing.cpython-36.pyc new file mode 100644 index 0000000..6693dd4 --- /dev/null +++ b/lib/deep_sort/__pycache__/preprocessing.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/track.cpython-36.pyc b/lib/deep_sort/__pycache__/track.cpython-36.pyc new file mode 100644 index 0000000..6a92b35 --- /dev/null +++ b/lib/deep_sort/__pycache__/track.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/__pycache__/tracker.cpython-36.pyc b/lib/deep_sort/__pycache__/tracker.cpython-36.pyc new file mode 100644 index 0000000..bd21cb2 --- /dev/null +++ b/lib/deep_sort/__pycache__/tracker.cpython-36.pyc Binary files differ diff --git a/lib/deep_sort/detection.py b/lib/deep_sort/detection.py new file mode 100644 index 0000000..a329433 --- /dev/null +++ b/lib/deep_sort/detection.py @@ -0,0 +1,19 @@ +import numpy as np + + +class Detection(object): + def __init__(self, tlwh, confidence, feature): + self.tlwh = np.asarray(tlwh, dtype=np.float) + self.confidence = float(confidence) + self.feature = np.asarray(feature, dtype=np.float32) + + def to_tlbr(self): + ret = self.tlwh.copy() + ret[2:] += ret[:2] + return ret + + def to_xyah(self): + ret = self.tlwh.copy() + ret[:2] += ret[2:] / 2 + ret[2] /= ret[3] + return ret diff --git a/lib/deep_sort/iou_matching.py b/lib/deep_sort/iou_matching.py new file mode 100644 index 0000000..8e580b7 --- /dev/null +++ b/lib/deep_sort/iou_matching.py @@ -0,0 +1,39 @@ +from __future__ import absolute_import +import numpy as np +from . import linear_assignment + + +def iou(bbox, candidates): + bbox_tl, bbox_br = bbox[:2], bbox[:2] + bbox[2:] + candidates_tl = candidates[:, :2] + candidates_br = candidates[:, :2] + candidates[:, 2:] + + tl = np.c_[np.maximum(bbox_tl[0], candidates_tl[:, 0])[:, np.newaxis], + np.maximum(bbox_tl[1], candidates_tl[:, 1])[:, np.newaxis]] + br = np.c_[np.minimum(bbox_br[0], candidates_br[:, 0])[:, np.newaxis], + np.minimum(bbox_br[1], candidates_br[:, 1])[:, np.newaxis]] + wh = np.maximum(0., br - tl) + + area_intersection = wh.prod(axis=1) + area_bbox = bbox[2:].prod() + area_candidates = candidates[:, 2:].prod(axis=1) + return area_intersection / (area_bbox + area_candidates - area_intersection) + + +def iou_cost(tracks, detections, track_indices=None, + detection_indices=None): + if track_indices is None: + track_indices = np.arange(len(tracks)) + if detection_indices is None: + detection_indices = np.arange(len(detections)) + + cost_matrix = np.zeros((len(track_indices), len(detection_indices))) + for row, track_idx in enumerate(track_indices): + if tracks[track_idx].time_since_update > 1: + cost_matrix[row, :] = linear_assignment.INFTY_COST + continue + + bbox = tracks[track_idx].to_tlwh() + candidates = np.asarray([detections[i].tlwh for i in detection_indices]) + cost_matrix[row, :] = 1. - iou(bbox, candidates) + return cost_matrix diff --git a/lib/deep_sort/kalman_filter.py b/lib/deep_sort/kalman_filter.py new file mode 100644 index 0000000..14f133c --- /dev/null +++ b/lib/deep_sort/kalman_filter.py @@ -0,0 +1,107 @@ +import numpy as np +import scipy.linalg + + +chi2inv95 = { + 1: 3.8415, + 2: 5.9915, + 3: 7.8147, + 4: 9.4877, + 5: 11.070, + 6: 12.592, + 7: 14.067, + 8: 15.507, + 9: 16.919} + + +class KalmanFilter(object): + + def __init__(self): + ndim, dt = 4, 1. + + self._motion_mat = np.eye(2 * ndim, 2 * ndim) + for i in range(ndim): + self._motion_mat[i, ndim + i] = dt + self._update_mat = np.eye(ndim, 2 * ndim) + + self._std_weight_position = 1. / 20 + self._std_weight_velocity = 1. / 160 + + def initiate(self, measurement): + mean_pos = measurement + mean_vel = np.zeros_like(mean_pos) + mean = np.r_[mean_pos, mean_vel] + + std = [ + 2 * self._std_weight_position * measurement[3], + 2 * self._std_weight_position * measurement[3], + 1e-2, + 2 * self._std_weight_position * measurement[3], + 10 * self._std_weight_velocity * measurement[3], + 10 * self._std_weight_velocity * measurement[3], + 1e-5, + 10 * self._std_weight_velocity * measurement[3]] + covariance = np.diag(np.square(std)) + return mean, covariance + + def predict(self, mean, covariance): + std_pos = [ + self._std_weight_position * mean[3], + self._std_weight_position * mean[3], + 1e-2, + self._std_weight_position * mean[3]] + std_vel = [ + self._std_weight_velocity * mean[3], + self._std_weight_velocity * mean[3], + 1e-5, + self._std_weight_velocity * mean[3]] + motion_cov = np.diag(np.square(np.r_[std_pos, std_vel])) + + mean = np.dot(self._motion_mat, mean) + covariance = np.linalg.multi_dot(( + self._motion_mat, covariance, self._motion_mat.T)) + motion_cov + + return mean, covariance + + def project(self, mean, covariance): + std = [ + self._std_weight_position * mean[3], + self._std_weight_position * mean[3], + 1e-1, + self._std_weight_position * mean[3]] + innovation_cov = np.diag(np.square(std)) + + mean = np.dot(self._update_mat, mean) + covariance = np.linalg.multi_dot(( + self._update_mat, covariance, self._update_mat.T)) + return mean, covariance + innovation_cov + + def update(self, mean, covariance, measurement): + projected_mean, projected_cov = self.project(mean, covariance) + + chol_factor, lower = scipy.linalg.cho_factor( + projected_cov, lower=True, check_finite=False) + kalman_gain = scipy.linalg.cho_solve( + (chol_factor, lower), np.dot(covariance, self._update_mat.T).T, + check_finite=False).T + innovation = measurement - projected_mean + + new_mean = mean + np.dot(innovation, kalman_gain.T) + new_covariance = covariance - np.linalg.multi_dot(( + kalman_gain, projected_cov, kalman_gain.T)) + return new_mean, new_covariance + + def gating_distance(self, mean, covariance, measurements, + only_position=False): + mean, covariance = self.project(mean, covariance) + if only_position: + mean, covariance = mean[:2], covariance[:2, :2] + measurements = measurements[:, :2] + + cholesky_factor = np.linalg.cholesky(covariance) # 鍒嗚В姝e畾鐭╅樀 + d = measurements - mean + z = scipy.linalg.solve_triangular( + cholesky_factor, d.T, lower=True, check_finite=False, + overwrite_b=True) + squared_maha = np.sum(z * z, axis=0) + return squared_maha diff --git a/lib/deep_sort/linear_assignment.py b/lib/deep_sort/linear_assignment.py new file mode 100644 index 0000000..739de32 --- /dev/null +++ b/lib/deep_sort/linear_assignment.py @@ -0,0 +1,84 @@ +from __future__ import absolute_import +import numpy as np +from sklearn.utils.linear_assignment_ import linear_assignment +from . import kalman_filter + + +INFTY_COST = 1e+5 + + +def min_cost_matching( + distance_metric, max_distance, tracks, detections, track_indices=None, + detection_indices=None): + if track_indices is None: + track_indices = np.arange(len(tracks)) + if detection_indices is None: + detection_indices = np.arange(len(detections)) + + if len(detection_indices) == 0 or len(track_indices) == 0: + return [], track_indices, detection_indices # Nothing to match. + + cost_matrix = distance_metric( + tracks, detections, track_indices, detection_indices) + cost_matrix[cost_matrix > max_distance] = max_distance + 1e-5 + indices = linear_assignment(cost_matrix) + + matches, unmatched_tracks, unmatched_detections = [], [], [] + for col, detection_idx in enumerate(detection_indices): + if col not in indices[:, 1]: + unmatched_detections.append(detection_idx) + for row, track_idx in enumerate(track_indices): + if row not in indices[:, 0]: + unmatched_tracks.append(track_idx) + for row, col in indices: + track_idx = track_indices[row] + detection_idx = detection_indices[col] + if cost_matrix[row, col] > max_distance: + unmatched_tracks.append(track_idx) + unmatched_detections.append(detection_idx) + else: + matches.append((track_idx, detection_idx)) + return matches, unmatched_tracks, unmatched_detections + +def matching_cascade( + distance_metric, max_distance, cascade_depth, tracks, detections, + track_indices=None, detection_indices=None): + if track_indices is None: + track_indices = list(range(len(tracks))) + if detection_indices is None: + detection_indices = list(range(len(detections))) + + unmatched_detections = detection_indices + matches = [] + for level in range(cascade_depth): + if len(unmatched_detections) == 0: + break + + track_indices_l = [ + k for k in track_indices + if tracks[k].time_since_update == 1 + level + ] + if len(track_indices_l) == 0: + continue + + matches_l, _, unmatched_detections = \ + min_cost_matching( + distance_metric, max_distance, tracks, detections, + track_indices_l, unmatched_detections) + matches += matches_l + unmatched_tracks = list(set(track_indices) - set(k for k, _ in matches)) + return matches, unmatched_tracks, unmatched_detections + +def gate_cost_matrix( + kf, cost_matrix, tracks, detections, track_indices, detection_indices, + gated_cost=INFTY_COST, only_position=False): + gating_dim = 2 if only_position else 4 + gating_threshold = kalman_filter.chi2inv95[gating_dim] + measurements = np.asarray( + [detections[i].to_xyah() for i in detection_indices]) + for row, track_idx in enumerate(track_indices): + track = tracks[track_idx] + gating_distance = kf.gating_distance( + track.mean, track.covariance, measurements, only_position) + cost_matrix[row, gating_distance > gating_threshold] = gated_cost # 璁剧疆涓篿nf ,璺濈杩滅殑鐩存帴鎵╁ぇ鍒版棤绌� + return cost_matrix diff --git a/lib/deep_sort/nn_matching.py b/lib/deep_sort/nn_matching.py new file mode 100644 index 0000000..afeb53a --- /dev/null +++ b/lib/deep_sort/nn_matching.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 15:04 +# @Author : Scheaven +# @File : nn_matching.py +# @description: +import numpy as np + +def _pdist(a, b): + a, b = np.asarray(a), np.asarray(b) + if len(a) == 0 or len(b) == 0: + return np.zeros((len(a), len(b))) + a2, b2 = np.square(a).sum(axis=1), np.square(b).sum(axis=1) + r2 = -2. * np.dot(a, b.T) + a2[:, None] + b2[None, :] + r2 = np.clip(r2, 0., float(np.inf)) + return r2 + + +def _cosine_distance(a, b, data_is_normalized=False): + if not data_is_normalized: + a = np.asarray(a) / np.linalg.norm(a, axis=1, keepdims=True) + b = np.asarray(b) / np.linalg.norm(b, axis=1, keepdims=True) + return 1. - np.dot(a, b.T) + +def _nn_euclidean_distance(x, y): + distances = _pdist(x, y) + return np.maximum(0.0, distances.min(axis=0)) + + +def _nn_cosine_distance(x, y): + distances = _cosine_distance(x, y) + return distances.min(axis=0) + + +class NearestNeighborDistanceMetric(object): + + def __init__(self, metric, matching_threshold, budget=None): + + + if metric == "euclidean": + self._metric = _nn_euclidean_distance + elif metric == "cosine": + self._metric = _nn_cosine_distance + else: + raise ValueError( + "Invalid metric; must be either 'euclidean' or 'cosine'") + self.matching_threshold = matching_threshold + self.budget = budget + self.samples = {} # 鍙互鍔犺浇绂荤嚎搴� + + def partial_fit(self, features, targets, active_targets): + for feature, target in zip(features, targets): + self.samples.setdefault(target, []).append(feature) + if self.budget is not None: + self.samples[target] = self.samples[target][-self.budget:] + self.samples = {k: self.samples[k] for k in active_targets} + + def distance(self, features, targets): + cost_matrix = np.zeros((len(targets), len(features))) + for i, target in enumerate(targets): + cost_matrix[i, :] = self._metric(self.samples[target], features) + return cost_matrix diff --git a/lib/deep_sort/preprocessing.py b/lib/deep_sort/preprocessing.py new file mode 100644 index 0000000..7baae47 --- /dev/null +++ b/lib/deep_sort/preprocessing.py @@ -0,0 +1,43 @@ +# vim: expandtab:ts=4:sw=4 +import numpy as np +import cv2 + + +def non_max_suppression(boxes, max_bbox_overlap, scores=None): + if len(boxes) == 0: + return [] + + boxes = boxes.astype(np.float) + pick = [] + + x1 = boxes[:, 0] + y1 = boxes[:, 1] + x2 = boxes[:, 2] + boxes[:, 0] + y2 = boxes[:, 3] + boxes[:, 1] + + area = (x2 - x1 + 1) * (y2 - y1 + 1) + if scores is not None: + idxs = np.argsort(scores) + else: + idxs = np.argsort(y2) + + while len(idxs) > 0: + last = len(idxs) - 1 + i = idxs[last] + pick.append(i) + + xx1 = np.maximum(x1[i], x1[idxs[:last]]) + yy1 = np.maximum(y1[i], y1[idxs[:last]]) + xx2 = np.minimum(x2[i], x2[idxs[:last]]) + yy2 = np.minimum(y2[i], y2[idxs[:last]]) + + w = np.maximum(0, xx2 - xx1 + 1) + h = np.maximum(0, yy2 - yy1 + 1) + + overlap = (w * h) / area[idxs[:last]] + + idxs = np.delete( + idxs, np.concatenate( + ([last], np.where(overlap > max_bbox_overlap)[0]))) + + return pick diff --git a/lib/deep_sort/track.py b/lib/deep_sort/track.py new file mode 100644 index 0000000..1c82ec2 --- /dev/null +++ b/lib/deep_sort/track.py @@ -0,0 +1,66 @@ +class TrackState: + + Tentative = 1 + Confirmed = 2 + Deleted = 3 + + +class Track: + + def __init__(self, mean, covariance, track_id, n_init, max_age, + feature=None): + self.mean = mean + self.covariance = covariance + self.track_id = track_id + self.hits = 1 + self.age = 1 + self.time_since_update = 0 + + self.state = TrackState.Tentative + self.features = [] + if feature is not None: + self.features.append(feature) + + self._n_init = n_init + self._max_age = max_age + + def to_tlwh(self): + ret = self.mean[:4].copy() + ret[2] *= ret[3] + ret[:2] -= ret[2:] / 2 + return ret + + def to_tlbr(self): + ret = self.to_tlwh() + ret[2:] = ret[:2] + ret[2:] + return ret + + def predict(self, kf): + self.mean, self.covariance = kf.predict(self.mean, self.covariance) + self.age += 1 + self.time_since_update += 1 + + def update(self, kf, detection): + self.mean, self.covariance = kf.update( + self.mean, self.covariance, detection.to_xyah()) + self.features.append(detection.feature) + + self.hits += 1 + self.time_since_update = 0 + if self.state == TrackState.Tentative and self.hits >= self._n_init: + self.state = TrackState.Confirmed + + def mark_missed(self): + if self.state == TrackState.Tentative: + self.state = TrackState.Deleted + elif self.time_since_update > self._max_age: + self.state = TrackState.Deleted + + def is_tentative(self): + return self.state == TrackState.Tentative + + def is_confirmed(self): + return self.state == TrackState.Confirmed + + def is_deleted(self): + return self.state == TrackState.Deleted diff --git a/lib/deep_sort/tracker.py b/lib/deep_sort/tracker.py new file mode 100644 index 0000000..3e600d7 --- /dev/null +++ b/lib/deep_sort/tracker.py @@ -0,0 +1,94 @@ +# vim: expandtab:ts=4:sw=4 +from __future__ import absolute_import +import numpy as np +from . import kalman_filter +from . import linear_assignment +from . import iou_matching +from .track import Track + + +class Tracker: + + def __init__(self, metric, max_iou_distance=0.7, max_age=300, n_init=3): + self.metric = metric + self.max_iou_distance = max_iou_distance + self.max_age = max_age # max_trace_length + self.n_init = n_init + + self.kf = kalman_filter.KalmanFilter() + self.tracks = [] + self._next_id = 1 + + def predict(self): + for track in self.tracks: + track.predict(self.kf) + + def update(self, detections): + matches, unmatched_tracks, unmatched_detections = \ + self._match(detections) + + for track_idx, detection_idx in matches: + self.tracks[track_idx].update( + self.kf, detections[detection_idx]) + for track_idx in unmatched_tracks: + self.tracks[track_idx].mark_missed() + for detection_idx in unmatched_detections: + self._initiate_track(detections[detection_idx]) + self.tracks = [t for t in self.tracks if not t.is_deleted()] + + active_targets = [t.track_id for t in self.tracks if t.is_confirmed()] + features, targets = [], [] + for track in self.tracks: + if not track.is_confirmed(): + continue + features += track.features + targets += [track.track_id for _ in track.features] + track.features = [] + self.metric.partial_fit( + np.asarray(features), np.asarray(targets), active_targets) + + def _match(self, detections): + + def gated_metric(tracks, dets, track_indices, detection_indices): + features = np.array([dets[i].feature for i in detection_indices]) + targets = np.array([tracks[i].track_id for i in track_indices]) + cost_matrix = self.metric.distance(features, targets) + cost_matrix = linear_assignment.gate_cost_matrix( + self.kf, cost_matrix, tracks, dets, track_indices, + detection_indices) + + return cost_matrix + + confirmed_tracks = [ + i for i, t in enumerate(self.tracks) if t.is_confirmed()] + unconfirmed_tracks = [ + i for i, t in enumerate(self.tracks) if not t.is_confirmed()] + + matches_a, unmatched_tracks_a, unmatched_detections = \ + linear_assignment.matching_cascade( + gated_metric, self.metric.matching_threshold, self.max_age, + self.tracks, detections, confirmed_tracks) + + + iou_track_candidates = unconfirmed_tracks + [ + k for k in unmatched_tracks_a if + self.tracks[k].time_since_update == 1] + unmatched_tracks_a = [ + k for k in unmatched_tracks_a if + self.tracks[k].time_since_update != 1] + + matches_b, unmatched_tracks_b, unmatched_detections = \ + linear_assignment.min_cost_matching( + iou_matching.iou_cost, self.max_iou_distance, self.tracks, + detections, iou_track_candidates, unmatched_detections) + + matches = matches_a + matches_b + unmatched_tracks = list(set(unmatched_tracks_a + unmatched_tracks_b)) + return matches, unmatched_tracks, unmatched_detections + + def _initiate_track(self, detection): + mean, covariance = self.kf.initiate(detection.to_xyah()) + self.tracks.append(Track( + mean, covariance, self._next_id, self.n_init, self.max_age, + detection.feature)) + self._next_id += 1 diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py new file mode 100644 index 0000000..d44529a --- /dev/null +++ b/lib/utils/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 13:53 +# @Author : Scheaven +# @File : __init__.py.py +# @description: \ No newline at end of file diff --git a/lib/utils/__pycache__/__init__.cpython-36.pyc b/lib/utils/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..43fb042 --- /dev/null +++ b/lib/utils/__pycache__/__init__.cpython-36.pyc Binary files differ diff --git a/lib/utils/__pycache__/utils.cpython-36.pyc b/lib/utils/__pycache__/utils.cpython-36.pyc new file mode 100644 index 0000000..32ede12 --- /dev/null +++ b/lib/utils/__pycache__/utils.cpython-36.pyc Binary files differ diff --git a/lib/utils/utils.py b/lib/utils/utils.py new file mode 100644 index 0000000..f946188 --- /dev/null +++ b/lib/utils/utils.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2019/10/30 15:27 +# @Author : Scheaven +# @File : utils.py +# @description: +import cv2 + +class video_open: + def __init__(self,args): + #self.readtype=read_type + if args.in_type == "camera": + self.readtype = args.c_in + else: + self.readtype= args.v_in + + def generate_video(self): + video_capture = cv2.VideoCapture(self.readtype) + return video_capture diff --git a/lib/yolo3/__init__.py b/lib/yolo3/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/yolo3/__init__.py diff --git a/lib/yolo3/__init__.pyc b/lib/yolo3/__init__.pyc new file mode 100644 index 0000000..30f38da --- /dev/null +++ b/lib/yolo3/__init__.pyc Binary files differ diff --git a/lib/yolo3/__pycache__/__init__.cpython-36.pyc b/lib/yolo3/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..3541b5b --- /dev/null +++ b/lib/yolo3/__pycache__/__init__.cpython-36.pyc Binary files differ diff --git a/lib/yolo3/__pycache__/model.cpython-36.pyc b/lib/yolo3/__pycache__/model.cpython-36.pyc new file mode 100644 index 0000000..f8f4f5d --- /dev/null +++ b/lib/yolo3/__pycache__/model.cpython-36.pyc Binary files differ diff --git a/lib/yolo3/__pycache__/utils.cpython-36.pyc b/lib/yolo3/__pycache__/utils.cpython-36.pyc new file mode 100644 index 0000000..35e9ea2 --- /dev/null +++ b/lib/yolo3/__pycache__/utils.cpython-36.pyc Binary files differ diff --git a/lib/yolo3/model.py b/lib/yolo3/model.py new file mode 100644 index 0000000..a8b24d6 --- /dev/null +++ b/lib/yolo3/model.py @@ -0,0 +1,300 @@ +from functools import wraps + +import numpy as np +import tensorflow as tf +from keras import backend as K +from keras.layers import Conv2D, Add, ZeroPadding2D, UpSampling2D, Concatenate +from keras.layers.advanced_activations import LeakyReLU +from keras.layers.normalization import BatchNormalization +from keras.models import Model +from keras.regularizers import l2 + +from lib.yolo3.utils import compose + + +@wraps(Conv2D) +def DarknetConv2D(*args, **kwargs): + darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)} + darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same' + darknet_conv_kwargs.update(kwargs) + return Conv2D(*args, **darknet_conv_kwargs) + +def DarknetConv2D_BN_Leaky(*args, **kwargs): + no_bias_kwargs = {'use_bias': False} + no_bias_kwargs.update(kwargs) + return compose( + DarknetConv2D(*args, **no_bias_kwargs), + BatchNormalization(), + LeakyReLU(alpha=0.1)) + +def resblock_body(x, num_filters, num_blocks): + x = ZeroPadding2D(((1,0),(1,0)))(x) + x = DarknetConv2D_BN_Leaky(num_filters, (3,3), strides=(2,2))(x) + for i in range(num_blocks): + y = compose( + DarknetConv2D_BN_Leaky(num_filters//2, (1,1)), + DarknetConv2D_BN_Leaky(num_filters, (3,3)))(x) + x = Add()([x,y]) + return x + +def darknet_body(x): + x = DarknetConv2D_BN_Leaky(32, (3,3))(x) + x = resblock_body(x, 64, 1) + x = resblock_body(x, 128, 2) + x = resblock_body(x, 256, 8) + x = resblock_body(x, 512, 8) + x = resblock_body(x, 1024, 4) + return x + +def make_last_layers(x, num_filters, out_filters): + x = compose( + DarknetConv2D_BN_Leaky(num_filters, (1,1)), + DarknetConv2D_BN_Leaky(num_filters*2, (3,3)), + DarknetConv2D_BN_Leaky(num_filters, (1,1)), + DarknetConv2D_BN_Leaky(num_filters*2, (3,3)), + DarknetConv2D_BN_Leaky(num_filters, (1,1)))(x) + y = compose( + DarknetConv2D_BN_Leaky(num_filters*2, (3,3)), + DarknetConv2D(out_filters, (1,1)))(x) + return x, y + + +def yolo_body(inputs, num_anchors, num_classes): + darknet = Model(inputs, darknet_body(inputs)) + x, y1 = make_last_layers(darknet.output, 512, num_anchors*(num_classes+5)) + + x = compose( + DarknetConv2D_BN_Leaky(256, (1,1)), + UpSampling2D(2))(x) + x = Concatenate()([x,darknet.layers[152].output]) + x, y2 = make_last_layers(x, 256, num_anchors*(num_classes+5)) + + x = compose( + DarknetConv2D_BN_Leaky(128, (1,1)), + UpSampling2D(2))(x) + x = Concatenate()([x,darknet.layers[92].output]) + x, y3 = make_last_layers(x, 128, num_anchors*(num_classes+5)) + + return Model(inputs, [y1,y2,y3]) + + +def yolo_head(feats, anchors, num_classes, input_shape): + num_anchors = len(anchors) + anchors_tensor = K.reshape(K.constant(anchors), [1, 1, 1, num_anchors, 2]) + + grid_shape = K.shape(feats)[1:3] # height, width + grid_y = K.tile(K.reshape(K.arange(0, stop=grid_shape[0]), [-1, 1, 1, 1]), + [1, grid_shape[1], 1, 1]) + grid_x = K.tile(K.reshape(K.arange(0, stop=grid_shape[1]), [1, -1, 1, 1]), + [grid_shape[0], 1, 1, 1]) + grid = K.concatenate([grid_x, grid_y]) + grid = K.cast(grid, K.dtype(feats)) + + feats = K.reshape( + feats, [-1, grid_shape[0], grid_shape[1], num_anchors, num_classes + 5]) + + box_xy = K.sigmoid(feats[..., :2]) + box_wh = K.exp(feats[..., 2:4]) + box_confidence = K.sigmoid(feats[..., 4:5]) + box_class_probs = K.sigmoid(feats[..., 5:]) + + box_xy = (box_xy + grid) / K.cast(grid_shape[::-1], K.dtype(feats)) + box_wh = box_wh * anchors_tensor / K.cast(input_shape[::-1], K.dtype(feats)) + + return box_xy, box_wh, box_confidence, box_class_probs + + +def yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape): + box_yx = box_xy[..., ::-1] + box_hw = box_wh[..., ::-1] + input_shape = K.cast(input_shape, K.dtype(box_yx)) + image_shape = K.cast(image_shape, K.dtype(box_yx)) + new_shape = K.round(image_shape * K.min(input_shape/image_shape)) + offset = (input_shape-new_shape)/2./input_shape + scale = input_shape/new_shape + box_yx = (box_yx - offset) * scale + box_hw *= scale + + box_mins = box_yx - (box_hw / 2.) + box_maxes = box_yx + (box_hw / 2.) + boxes = K.concatenate([ + box_mins[..., 0:1], + box_mins[..., 1:2], + box_maxes[..., 0:1], + box_maxes[..., 1:2] + ]) + + boxes *= K.concatenate([image_shape, image_shape]) + return boxes + + +def yolo_boxes_and_scores(feats, anchors, num_classes, input_shape, image_shape): + box_xy, box_wh, box_confidence, box_class_probs = yolo_head(feats, + anchors, num_classes, input_shape) + boxes = yolo_correct_boxes(box_xy, box_wh, input_shape, image_shape) + boxes = K.reshape(boxes, [-1, 4]) + box_scores = box_confidence * box_class_probs + box_scores = K.reshape(box_scores, [-1, num_classes]) + return boxes, box_scores + + +def yolo_eval(yolo_outputs, + anchors, + num_classes, + image_shape, + max_boxes=20, + score_threshold=.6, + iou_threshold=.5): + anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] + input_shape = K.shape(yolo_outputs[0])[1:3] * 32 + boxes = [] + box_scores = [] + for l in range(3): + _boxes, _box_scores = yolo_boxes_and_scores(yolo_outputs[l], + anchors[anchor_mask[l]], num_classes, input_shape, image_shape) + boxes.append(_boxes) + box_scores.append(_box_scores) + boxes = K.concatenate(boxes, axis=0) + box_scores = K.concatenate(box_scores, axis=0) + + mask = box_scores >= score_threshold + max_boxes_tensor = K.constant(max_boxes, dtype='int32') + boxes_ = [] + scores_ = [] + classes_ = [] + for c in range(num_classes): + class_boxes = tf.boolean_mask(boxes, mask[:, c]) + class_box_scores = tf.boolean_mask(box_scores[:, c], mask[:, c]) + nms_index = tf.image.non_max_suppression( + class_boxes, class_box_scores, max_boxes_tensor, iou_threshold=iou_threshold) + class_boxes = K.gather(class_boxes, nms_index) + class_box_scores = K.gather(class_box_scores, nms_index) + classes = K.ones_like(class_box_scores, 'int32') * c + boxes_.append(class_boxes) + scores_.append(class_box_scores) + classes_.append(classes) + boxes_ = K.concatenate(boxes_, axis=0) + scores_ = K.concatenate(scores_, axis=0) + classes_ = K.concatenate(classes_, axis=0) + + return boxes_, scores_, classes_ + + +def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes): + anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] + + true_boxes = np.array(true_boxes, dtype='float32') + input_shape = np.array(input_shape, dtype='int32') + boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2 + boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2] + true_boxes[..., 0:2] = boxes_xy/input_shape[::-1] + true_boxes[..., 2:4] = boxes_wh/input_shape[::-1] + + m = true_boxes.shape[0] + grid_shapes = [input_shape//{0:32, 1:16, 2:8}[l] for l in range(3)] + y_true = [np.zeros((m,grid_shapes[l][0],grid_shapes[l][1],len(anchor_mask[l]),5+num_classes), + dtype='float32') for l in range(3)] + + anchors = np.expand_dims(anchors, 0) + anchor_maxes = anchors / 2. + anchor_mins = -anchor_maxes + valid_mask = boxes_wh[..., 0]>0 + + for b in range(m): + wh = boxes_wh[b, valid_mask[b]] + wh = np.expand_dims(wh, -2) + box_maxes = wh / 2. + box_mins = -box_maxes + + intersect_mins = np.maximum(box_mins, anchor_mins) + intersect_maxes = np.minimum(box_maxes, anchor_maxes) + intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.) + intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1] + box_area = wh[..., 0] * wh[..., 1] + anchor_area = anchors[..., 0] * anchors[..., 1] + iou = intersect_area / (box_area + anchor_area - intersect_area) + + best_anchor = np.argmax(iou, axis=-1) + + for t, n in enumerate(best_anchor): + for l in range(3): + if n in anchor_mask[l]: + i = np.floor(true_boxes[b,t,0]*grid_shapes[l][1]).astype('int32') + j = np.floor(true_boxes[b,t,1]*grid_shapes[l][0]).astype('int32') + n = anchor_mask[l].index(n) + c = true_boxes[b,t, 4].astype('int32') + y_true[l][b, j, i, n, 0:4] = true_boxes[b,t, 0:4] + y_true[l][b, j, i, n, 4] = 1 + y_true[l][b, j, i, n, 5+c] = 1 + break + + return y_true + +def box_iou(b1, b2): + b1 = K.expand_dims(b1, -2) + b1_xy = b1[..., :2] + b1_wh = b1[..., 2:4] + b1_wh_half = b1_wh/2. + b1_mins = b1_xy - b1_wh_half + b1_maxes = b1_xy + b1_wh_half + + b2 = K.expand_dims(b2, 0) + b2_xy = b2[..., :2] + b2_wh = b2[..., 2:4] + b2_wh_half = b2_wh/2. + b2_mins = b2_xy - b2_wh_half + b2_maxes = b2_xy + b2_wh_half + + intersect_mins = K.maximum(b1_mins, b2_mins) + intersect_maxes = K.minimum(b1_maxes, b2_maxes) + intersect_wh = K.maximum(intersect_maxes - intersect_mins, 0.) + intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1] + b1_area = b1_wh[..., 0] * b1_wh[..., 1] + b2_area = b2_wh[..., 0] * b2_wh[..., 1] + iou = intersect_area / (b1_area + b2_area - intersect_area) + + return iou + + + +def yolo_loss(args, anchors, num_classes, ignore_thresh=.5): + yolo_outputs = args[:3] + y_true = args[3:] + anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] + input_shape = K.cast(K.shape(yolo_outputs[0])[1:3] * 32, K.dtype(y_true[0])) + grid_shapes = [K.cast(K.shape(yolo_outputs[l])[1:3], K.dtype(y_true[0])) for l in range(3)] + loss = 0 + m = K.shape(yolo_outputs[0])[0] + + for l in range(3): + object_mask = y_true[l][..., 4:5] + true_class_probs = y_true[l][..., 5:] + + pred_xy, pred_wh, pred_confidence, pred_class_probs = yolo_head(yolo_outputs[l], + anchors[anchor_mask[l]], num_classes, input_shape) + pred_box = K.concatenate([pred_xy, pred_wh]) + + xy_delta = (y_true[l][..., :2]-pred_xy)*grid_shapes[l][::-1] + wh_delta = K.log(y_true[l][..., 2:4]) - K.log(pred_wh) + wh_delta = K.switch(object_mask, wh_delta, K.zeros_like(wh_delta)) + box_delta = K.concatenate([xy_delta, wh_delta], axis=-1) + box_delta_scale = 2 - y_true[l][...,2:3]*y_true[l][...,3:4] + + ignore_mask = tf.TensorArray(K.dtype(y_true[0]), size=1, dynamic_size=True) + object_mask_bool = K.cast(object_mask, 'bool') + def loop_body(b, ignore_mask): + true_box = tf.boolean_mask(y_true[l][b,...,0:4], object_mask_bool[b,...,0]) + iou = box_iou(pred_box[b], true_box) + best_iou = K.max(iou, axis=-1) + ignore_mask = ignore_mask.write(b, K.cast(best_iou<ignore_thresh, K.dtype(true_box))) + return b+1, ignore_mask + _, ignore_mask = K.control_flow_ops.while_loop(lambda b,*args: b<m, loop_body, [0, ignore_mask]) + ignore_mask = ignore_mask.stack() + ignore_mask = K.expand_dims(ignore_mask, -1) + + box_loss = object_mask * K.square(box_delta*box_delta_scale) + confidence_loss = object_mask * K.square(1-pred_confidence) + \ + (1-object_mask) * K.square(0-pred_confidence) * ignore_mask + class_loss = object_mask * K.square(true_class_probs-pred_class_probs) + loss += K.sum(box_loss) + K.sum(confidence_loss) + K.sum(class_loss) + return loss / K.cast(m, K.dtype(loss)) diff --git a/lib/yolo3/model.pyc b/lib/yolo3/model.pyc new file mode 100644 index 0000000..3ceaafb --- /dev/null +++ b/lib/yolo3/model.pyc Binary files differ diff --git a/lib/yolo3/utils.py b/lib/yolo3/utils.py new file mode 100644 index 0000000..97d9ae7 --- /dev/null +++ b/lib/yolo3/utils.py @@ -0,0 +1,20 @@ +from functools import reduce + +from PIL import Image + +def compose(*funcs): + if funcs: + return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs) + else: + raise ValueError('Composition of empty sequence not supported.') + +def letterbox_image(image, size): + image_w, image_h = image.size + w, h = size + new_w = int(image_w * min(w*1.0/image_w, h*1.0/image_h)) + new_h = int(image_h * min(w*1.0/image_w, h*1.0/image_h)) + resized_image = image.resize((new_w,new_h), Image.BICUBIC) + + boxed_image = Image.new('RGB', size, (128,128,128)) + boxed_image.paste(resized_image, ((w-new_w)//2,(h-new_h)//2)) + return boxed_image diff --git a/lib/yolo3/utils.pyc b/lib/yolo3/utils.pyc new file mode 100644 index 0000000..6aafabf --- /dev/null +++ b/lib/yolo3/utils.pyc Binary files differ diff --git a/model_dump/coco_classes.txt b/model_dump/coco_classes.txt new file mode 100644 index 0000000..ca76c80 --- /dev/null +++ b/model_dump/coco_classes.txt @@ -0,0 +1,80 @@ +person +bicycle +car +motorbike +aeroplane +bus +train +truck +boat +traffic light +fire hydrant +stop sign +parking meter +bench +bird +cat +dog +horse +sheep +cow +elephant +bear +zebra +giraffe +backpack +umbrella +handbag +tie +suitcase +frisbee +skis +snowboard +sports ball +kite +baseball bat +baseball glove +skateboard +surfboard +tennis racket +bottle +wine glass +cup +fork +knife +spoon +bowl +banana +apple +sandwich +orange +broccoli +carrot +hot dog +pizza +donut +cake +chair +sofa +pottedplant +bed +diningtable +toilet +tvmonitor +laptop +mouse +remote +keyboard +cell phone +microwave +oven +toaster +sink +refrigerator +book +clock +vase +scissors +teddy bear +hair drier +toothbrush diff --git a/model_dump/voc_classes.txt b/model_dump/voc_classes.txt new file mode 100644 index 0000000..8420ab3 --- /dev/null +++ b/model_dump/voc_classes.txt @@ -0,0 +1,20 @@ +aeroplane +bicycle +bird +boat +bottle +bus +car +cat +chair +cow +diningtable +dog +horse +motorbike +person +pottedplant +sheep +sofa +train +tvmonitor diff --git a/model_dump/yolo_anchors.txt b/model_dump/yolo_anchors.txt new file mode 100644 index 0000000..9cdfb96 --- /dev/null +++ b/model_dump/yolo_anchors.txt @@ -0,0 +1 @@ +10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 -- Gitblit v1.8.0