diff --git a/README.md b/README.md index 6f3c668..8915b47 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,16 @@ # DeepLabCut-live SDKDLC LIVE! +Code style: black ![PyPI - Python Version](https://img.shields.io/pypi/v/deeplabcut-live) -[![License](https://img.shields.io/pypi/l/deeplabcutcore.svg)](https://github.com/DeepLabCut/deeplabcutlive/raw/master/LICENSE) ![PyPI - Downloads](https://img.shields.io/pypi/dm/deeplabcut-live?color=purple) ![Python package](https://github.com/DeepLabCut/DeepLabCut-live/workflows/Python%20package/badge.svg) +[![GitHub stars](https://img.shields.io/github/stars/DeepLabCut/DeepLabCut-live.svg?style=social&label=Star)](https://github.com/DeepLabCut/DeepLabCut-live) +[![GitHub forks](https://img.shields.io/github/forks/DeepLabCut/DeepLabCut-live.svg?style=social&label=Fork)](https://github.com/DeepLabCut/DeepLabCut-live) + +[![License](https://img.shields.io/pypi/l/deeplabcutcore.svg)](https://github.com/DeepLabCut/deeplabcutlive/raw/master/LICENSE) +[![Image.sc forum](https://img.shields.io/badge/dynamic/json.svg?label=forum&url=https%3A%2F%2Fforum.image.sc%2Ftags%2Fdeeplabcut.json&query=%24.topic_list.tags.0.topic_count&colorB=brightgreen&&suffix=%20topics&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAABPklEQVR42m3SyyqFURTA8Y2BER0TDyExZ+aSPIKUlPIITFzKeQWXwhBlQrmFgUzMMFLKZeguBu5y+//17dP3nc5vuPdee6299gohUYYaDGOyyACq4JmQVoFujOMR77hNfOAGM+hBOQqB9TjHD36xhAa04RCuuXeKOvwHVWIKL9jCK2bRiV284QgL8MwEjAneeo9VNOEaBhzALGtoRy02cIcWhE34jj5YxgW+E5Z4iTPkMYpPLCNY3hdOYEfNbKYdmNngZ1jyEzw7h7AIb3fRTQ95OAZ6yQpGYHMMtOTgouktYwxuXsHgWLLl+4x++Kx1FJrjLTagA77bTPvYgw1rRqY56e+w7GNYsqX6JfPwi7aR+Y5SA+BXtKIRfkfJAYgj14tpOF6+I46c4/cAM3UhM3JxyKsxiOIhH0IO6SH/A1Kb1WBeUjbkAAAAAElFTkSuQmCC)](https://forum.image.sc/tags/deeplabcut) +[![Gitter](https://badges.gitter.im/DeepLabCut/community.svg)](https://gitter.im/DeepLabCut/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Twitter Follow](https://img.shields.io/twitter/follow/DeepLabCut.svg?label=DeepLabCut&style=social)](https://twitter.com/DeepLabCut) This package contains a [DeepLabCut](http://www.mousemotorlab.org/deeplabcut) inference pipeline for real-time applications that has minimal (software) dependencies. Thus, it is as easy to install as possible (in particular, on atypical systems like [NVIDIA Jetson boards](https://developer.nvidia.com/buy-jetson)). @@ -31,7 +38,7 @@ Note, you can test your installation by running: `python /check_install/check_install.py` -This will download +If installed properly, this script will i) create a temporary folder ii) download the full_dog model from the [DeepLabCut Model Zoo](http://www.mousemotorlab.org/dlc-modelzoo), iii) download a short video clip of a dog, and iv) run inference while displaying keypoints. v) remove the temporary folder. ### Quick Start: instructions for use: diff --git a/benchmarking/run.py b/benchmarking/run.py index 3a3104e..db9d222 100644 --- a/benchmarking/run.py +++ b/benchmarking/run.py @@ -13,26 +13,35 @@ from dlclive import benchmark_model_by_size # Update the datafolder to where the data is: -datafolder='/your/path/to/data/here' +datafolder = "/your/path/to/data/here" -n_frames = 1000 #change to 10000 for testing on a GPU! +n_frames = 1000 # change to 10000 for testing on a GPU! pixels = [2500, 10000, 40000, 160000, 320000, 640000] -dog_models = glob.glob(datafolder + '/dog/*[!avi]') -dog_video = glob.glob(datafolder + '/dog/*.avi')[0] -mouse_models = glob.glob(datafolder + '/mouse_lick/*[!avi]') -mouse_video = glob.glob(datafolder + '/mouse_lick/*.avi')[0] +dog_models = glob.glob(datafolder + "/dog/*[!avi]") +dog_video = glob.glob(datafolder + "/dog/*.avi")[0] +mouse_models = glob.glob(datafolder + "/mouse_lick/*[!avi]") +mouse_video = glob.glob(datafolder + "/mouse_lick/*.avi")[0] this_dir = os.path.dirname(os.path.realpath(__file__)) -#storing results in /benchmarking/results: (for your PR) -out_dir = os.path.normpath(this_dir + '/results') +# storing results in /benchmarking/results: (for your PR) +out_dir = os.path.normpath(this_dir + "/results") for ind_m, m in enumerate(dog_models): print("\n\nMODEL {:d} / 8".format(ind_m)) - benchmark_model_by_size(m, dog_video, ind_m, out_dir=out_dir, n_frames=n_frames, pixels=pixels) + benchmark_model_by_size( + m, dog_video, ind_m, out_dir=out_dir, n_frames=n_frames, pixels=pixels + ) -offset=ind_m+1 +offset = ind_m + 1 for ind_m, m in enumerate(mouse_models): print("\n\nMODEL {:d} / 8".format(ind_m)) - benchmark_model_by_size(m, mouse_video, ind_m + offset, out_dir=out_dir, n_frames=n_frames, pixels=pixels) + benchmark_model_by_size( + m, + mouse_video, + ind_m + offset, + out_dir=out_dir, + n_frames=n_frames, + pixels=pixels, + ) diff --git a/check_install/check_install.py b/check_install/check_install.py index 88694bc..2c3c60d 100755 --- a/check_install/check_install.py +++ b/check_install/check_install.py @@ -27,7 +27,7 @@ def main(): # download dog video clip from github print("\nDownloading dog video clip...\n") - #video_url = "https://github.com/DeepLabCut/DeepLabCut-live/raw/master/check_install/dog_clip.avi" + # video_url = "https://github.com/DeepLabCut/DeepLabCut-live/raw/master/check_install/dog_clip.avi" video_url = '"https://docs.google.com/uc?export=download&id=1W_5AOl1SewXR2q5QC1K5Chm71I9LAmld"' os.system(f"curl -L {video_url} -o dog_clip.avi") @@ -45,4 +45,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/dlclive/bench.py b/dlclive/bench.py index 4ba0b91..f8b1f55 100644 --- a/dlclive/bench.py +++ b/dlclive/bench.py @@ -33,6 +33,7 @@ from dlclive.utils import decode_fourcc + def get_system_info() -> dict: """ Return summary info for system running benchmark @@ -51,14 +52,13 @@ def get_system_info() -> dict: * ``dlclive_version`` (str): dlclive version from :data:`dlclive.VERSION` """ - ### get os op_sys = platform.platform() - host_name = platform.node().replace(' ', '') + host_name = platform.node().replace(" ", "") # A string giving the absolute path of the executable binary for the Python interpreter, on systems where this makes sense. - if platform.system() == 'Windows': + if platform.system() == "Windows": host_python = sys.executable.split(os.path.sep)[-2] else: host_python = sys.executable.split(os.path.sep)[-3] @@ -67,8 +67,10 @@ def get_system_info() -> dict: dlc_basedir = os.path.dirname(os.path.dirname(dlcfile)) git_hash = None try: - git_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=dlc_basedir) - git_hash = git_hash.decode('utf-8').rstrip('\n') + git_hash = subprocess.check_output( + ["git", "rev-parse", "HEAD"], cwd=dlc_basedir + ) + git_hash = git_hash.decode("utf-8").rstrip("\n") except subprocess.CalledProcessError: # not installed from git repo, eg. pypi # fine, pass quietly @@ -80,31 +82,46 @@ def get_system_info() -> dict: if tf.test.is_gpu_available(): gpu_name = tf.test.gpu_device_name() from tensorflow.python.client import device_lib - dev_desc = [d.physical_device_desc for d in device_lib.list_local_devices() if d.name == gpu_name] - dev = [d.split(",")[1].split(':')[1].strip() for d in dev_desc] + + dev_desc = [ + d.physical_device_desc + for d in device_lib.list_local_devices() + if d.name == gpu_name + ] + dev = [d.split(",")[1].split(":")[1].strip() for d in dev_desc] dev_type = "GPU" else: from cpuinfo import get_cpu_info - dev = get_cpu_info() #[get_cpu_info()['brand']] + + dev = get_cpu_info() # [get_cpu_info()['brand']] dev_type = "CPU" # return a dictionary rather than a tuple for inspectability's sake return { - 'host_name': host_name, - 'op_sys' : op_sys, - 'python': host_python, - 'device_type': dev_type, - 'device': dev, - 'freeze': list(freeze.freeze()), # pip freeze to get versions of all packages - 'python_version': sys.version, - 'git_hash': git_hash, - 'dlclive_version': VERSION + "host_name": host_name, + "op_sys": op_sys, + "python": host_python, + "device_type": dev_type, + "device": dev, + "freeze": list(freeze.freeze()), # pip freeze to get versions of all packages + "python_version": sys.version, + "git_hash": git_hash, + "dlclive_version": VERSION, } -def run_benchmark(model_path, video_path, tf_config=None, - resize=None, pixels=None, n_frames=10000, - print_rate=False, display=False, pcutoff=0.0, - display_radius=3) -> typing.Tuple[np.ndarray, int, bool, dict]: + +def run_benchmark( + model_path, + video_path, + tf_config=None, + resize=None, + pixels=None, + n_frames=10000, + print_rate=False, + display=False, + pcutoff=0.0, + display_radius=3, +) -> typing.Tuple[np.ndarray, int, bool, dict]: """ Benchmark on inference times for a given DLC model and video Parameters @@ -145,7 +162,14 @@ def run_benchmark(model_path, video_path, tf_config=None, ### initialize live object - live = DLCLive(model_path, tf_config=tf_config, resize=resize, display=display, pcutoff=pcutoff, display_radius=display_radius) + live = DLCLive( + model_path, + tf_config=tf_config, + resize=resize, + display=display, + pcutoff=pcutoff, + display_radius=display_radius, + ) live.init_inference(frame) TFGPUinference = True if len(live.outputs) == 1 else False @@ -159,9 +183,13 @@ def run_benchmark(model_path, video_path, tf_config=None, ret, frame = cap.read() if not ret: - warnings.warn("Did not complete {:d} frames. There probably were not enough frames in the video {}.".format(n_frames, video_path)) + warnings.warn( + "Did not complete {:d} frames. There probably were not enough frames in the video {}.".format( + n_frames, video_path + ) + ) break - + start_pose = time.time() live.get_pose(frame) inf_times[i] = time.time() - start_pose @@ -170,7 +198,7 @@ def run_benchmark(model_path, video_path, tf_config=None, print("pose rate = {:d}".format(int(1 / inf_times[i]))) if print_rate: - print("mean pose rate = {:d}".format(int(np.mean(1/inf_times)))) + print("mean pose rate = {:d}".format(int(np.mean(1 / inf_times)))) ### close video and tensorflow session @@ -198,41 +226,45 @@ def run_benchmark(model_path, video_path, tf_config=None, except: frame_count = None - - meta = { - 'video_path': video_path, - 'video_codec': fourcc, - 'video_pixel_format': pix_fmt, - 'video_fps': fps, - 'video_total_frames': frame_count, - 'resize': resize, - 'original_frame_size': im_size, - 'resized_frame_size': (im_size[0]*resize, im_size[1]*resize), - 'pixels': pixels, - 'dlclive_params': live.parameterization + "video_path": video_path, + "video_codec": fourcc, + "video_pixel_format": pix_fmt, + "video_fps": fps, + "video_total_frames": frame_count, + "resize": resize, + "original_frame_size": im_size, + "resized_frame_size": (im_size[0] * resize, im_size[1] * resize), + "pixels": pixels, + "dlclive_params": live.parameterization, } cap.release() live.close() - return inf_times, resize*im_size[0] * resize*im_size[1], TFGPUinference, meta + return inf_times, resize * im_size[0] * resize * im_size[1], TFGPUinference, meta -def get_savebenchmarkfn(sys_info ,i, fn_ind, out_dir=None): - ''' get filename to save data (definitions see save_benchmark)''' + +def get_savebenchmarkfn(sys_info, i, fn_ind, out_dir=None): + """ get filename to save data (definitions see save_benchmark)""" out_dir = out_dir if out_dir is not None else os.getcwd() - base_name = "benchmark_{}_{}_{}_{}.pickle".format(sys_info['host_name'], sys_info['device_type'], fn_ind, i) - datafilename = out_dir + '/' + base_name + base_name = "benchmark_{}_{}_{}_{}.pickle".format( + sys_info["host_name"], sys_info["device_type"], fn_ind, i + ) + datafilename = out_dir + "/" + base_name return datafilename -def save_benchmark(sys_info: dict, - inf_times: np.ndarray, - pixels: typing.Union[np.ndarray, float], - iter: int, - TFGPUinference: bool = None, - model: str = None, - out_dir: str = None, - meta: dict=None): + +def save_benchmark( + sys_info: dict, + inf_times: np.ndarray, + pixels: typing.Union[np.ndarray, float], + iter: int, + TFGPUinference: bool = None, + model: str = None, + out_dir: str = None, + meta: dict = None, +): """ Save benchmarking data with system information to a pickle file Parameters @@ -264,30 +296,30 @@ def save_benchmark(sys_info: dict, model_type = None if model is not None: - if 'resnet' in model: - model_type = 'resnet' - elif 'mobilenet' in model: - model_type = 'mobilenet' + if "resnet" in model: + model_type = "resnet" + elif "mobilenet" in model: + model_type = "mobilenet" else: model_type = None fn_ind = 0 - base_name = "benchmark_{}_{}_{}_{}.pickle".format(sys_info['host_name'], - sys_info['device'][0], - fn_ind, - iter) - while os.path.isfile(os.path.normpath(out_dir + '/' + base_name)): + base_name = "benchmark_{}_{}_{}_{}.pickle".format( + sys_info["host_name"], sys_info["device"][0], fn_ind, iter + ) + while os.path.isfile(os.path.normpath(out_dir + "/" + base_name)): fn_ind += 1 - base_name = "benchmark_{}_{}_{}_{}.pickle".format(sys_info['host_name'], - sys_info['device'][0], - fn_ind, - iter) - - data = {'model': model, - 'model_type': model_type, - 'TFGPUinference': TFGPUinference, - 'pixels': pixels, - 'inference_times': inf_times} + base_name = "benchmark_{}_{}_{}_{}.pickle".format( + sys_info["host_name"], sys_info["device"][0], fn_ind, iter + ) + + data = { + "model": model, + "model_type": model_type, + "TFGPUinference": TFGPUinference, + "pixels": pixels, + "inference_times": inf_times, + } data.update(sys_info) @@ -297,16 +329,30 @@ def save_benchmark(sys_info: dict, data.update(sys_info) datafilename = os.path.normpath(f"{out_dir}/{base_name}") - pickle.dump(data, open(os.path.normpath(datafilename), 'wb')) + pickle.dump(data, open(os.path.normpath(datafilename), "wb")) return True + def read_pickle(filename): """ Read the pickle file """ with open(filename, "rb") as handle: return pickle.load(handle) -def benchmark_model_by_size(model_path, video_path, output=None, n_frames=10000, tf_config=None, resize=None, pixels=None, print_rate=False, display=False, pcutoff=0.5, display_radius=3): + +def benchmark_model_by_size( + model_path, + video_path, + output=None, + n_frames=10000, + tf_config=None, + resize=None, + pixels=None, + print_rate=False, + display=False, + pcutoff=0.5, + display_radius=3, +): """Benchmark DLC model by image size Parameters @@ -351,7 +397,7 @@ def benchmark_model_by_size(model_path, video_path, output=None, n_frames=10000, for i in range(len(resize)): - print("\nRun {:d} / {:d}\n".format(i+1, len(resize))) + print("\nRun {:d} / {:d}\n".format(i + 1, len(resize))) inf_times, pixels_out, TFGPUinference, benchmark_meta = run_benchmark( model_path, @@ -363,42 +409,51 @@ def benchmark_model_by_size(model_path, video_path, output=None, n_frames=10000, print_rate=print_rate, display=display, pcutoff=pcutoff, - display_radius=display_radius) + display_radius=display_radius, + ) - #TODO: check if a part has already been complted? + # TODO: check if a part has already been complted? ### saving results intermediately - save_benchmark(sys_info, inf_times, pixels_out, i, TFGPUinference, - model=os.path.basename(model_path), - out_dir = output, - meta=benchmark_meta) + save_benchmark( + sys_info, + inf_times, + pixels_out, + i, + TFGPUinference, + model=os.path.basename(model_path), + out_dir=output, + meta=benchmark_meta, + ) + def main(): parser = argparse.ArgumentParser() - parser.add_argument('model_path', type=str) - parser.add_argument('video_path', type=str) - parser.add_argument('-o', '--output', type=str, default=os.getcwd()) - parser.add_argument('-n', '--n-frames', type=int, default=10000) - parser.add_argument('-r', '--resize', type=float, nargs='+') - parser.add_argument('-p', '--pixels', type=float, nargs='+') - parser.add_argument('-v', '--print_rate', default=False, action='store_true') - parser.add_argument('-d', '--display', default=False, action='store_true') - parser.add_argument('-l', '--pcutoff', default=0.5, type=float) - parser.add_argument('-s', '--display-radius', default=3, type=int) + parser.add_argument("model_path", type=str) + parser.add_argument("video_path", type=str) + parser.add_argument("-o", "--output", type=str, default=os.getcwd()) + parser.add_argument("-n", "--n-frames", type=int, default=10000) + parser.add_argument("-r", "--resize", type=float, nargs="+") + parser.add_argument("-p", "--pixels", type=float, nargs="+") + parser.add_argument("-v", "--print_rate", default=False, action="store_true") + parser.add_argument("-d", "--display", default=False, action="store_true") + parser.add_argument("-l", "--pcutoff", default=0.5, type=float) + parser.add_argument("-s", "--display-radius", default=3, type=int) args = parser.parse_args() - - benchmark_model_by_size(args.model_path, - args.video_path, - output=args.output, - resize=args.resize, - pixels=args.pixels, - n_frames=args.n_frames, - print_rate=args.print_rate, - display=args.display, - pcutoff=args.pcutoff, - display_radius=args.display_radius) + benchmark_model_by_size( + args.model_path, + args.video_path, + output=args.output, + resize=args.resize, + pixels=args.pixels, + n_frames=args.n_frames, + print_rate=args.print_rate, + display=args.display, + pcutoff=args.pcutoff, + display_radius=args.display_radius, + ) if __name__ == "__main__": diff --git a/dlclive/benchmark.py b/dlclive/benchmark.py index f6a682c..7379084 100644 --- a/dlclive/benchmark.py +++ b/dlclive/benchmark.py @@ -197,7 +197,7 @@ def benchmark( ) n_frames = int(n_frames) im_size = (cap.get(cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) - + ### get resize factor if pixels is not None: @@ -226,12 +226,14 @@ def benchmark( if save_poses: try: - import pandas as pd + import pandas as pd + use_pandas = True except: use_pandas = False - warnings.warn("Could not find installation of pandas; saving poses as a numpy array with the dimensions (n_frames, n_keypoints, [x, y, likelihood]).") - + warnings.warn( + "Could not find installation of pandas; saving poses as a numpy array with the dimensions (n_frames, n_keypoints, [x, y, likelihood])." + ) ### initialize DLCLive and perform inference @@ -372,7 +374,7 @@ def benchmark( f"{out_dir}/{os.path.splitext(out_vid_base)[0]}_DLCLIVE_POSES.h5" ) pose_df.to_hdf(out_dlc_file, key="df_with_missing", mode="w") - + else: out_vid_base = os.path.basename(video_path) @@ -381,7 +383,6 @@ def benchmark( ) np.save(out_dlc_file, poses) - return inf_times, im_size, TFGPUinference, meta @@ -452,6 +453,7 @@ def save_inf_times( return True + def benchmark_videos( model_path, video_path, @@ -589,6 +591,7 @@ def benchmark_videos( output=output, ) + def main(): """Provides a command line interface :function:`benchmark_videos` """ @@ -627,5 +630,6 @@ def main(): save_video=args.save_video, ) + if __name__ == "__main__": main() diff --git a/dlclive/display.py b/dlclive/display.py index 534cb90..647d0ef 100644 --- a/dlclive/display.py +++ b/dlclive/display.py @@ -12,7 +12,7 @@ class Display(object): - ''' + """ Simple object to display frames with DLC labels. Parameters @@ -21,10 +21,9 @@ class Display(object): string indicating the Matoplotlib colormap to use. pcutoff : float likelihood threshold to display points - ''' + """ - - def __init__(self, cmap='bmy', radius=3, pcutoff=0.5): + def __init__(self, cmap="bmy", radius=3, pcutoff=0.5): """ Constructor method """ @@ -34,7 +33,6 @@ def __init__(self, cmap='bmy', radius=3, pcutoff=0.5): self.pcutoff = pcutoff self.window = None - def set_display(self, im_size, bodyparts): """ Create tkinter window to display image @@ -52,11 +50,10 @@ def set_display(self, im_size, bodyparts): self.lab.pack() all_colors = getattr(cc, self.cmap) - self.colors = all_colors[::int(len(all_colors)/bodyparts)] - + self.colors = all_colors[:: int(len(all_colors) / bodyparts)] def display_frame(self, frame, pose=None): - ''' + """ Display the image with DeepLabCut labels using opencv imshow Parameters @@ -66,7 +63,7 @@ def display_frame(self, frame, pose=None): pose :class:`numpy.ndarray` the pose estimated by DeepLabCut for the image - ''' + """ im_size = (frame.shape[1], frame.shape[0]) @@ -79,25 +76,42 @@ def display_frame(self, frame, pose=None): draw = ImageDraw.Draw(img) for i in range(pose.shape[0]): - if pose[i,2] > self.pcutoff: + if pose[i, 2] > self.pcutoff: try: - x0 = pose[i,0] - self.radius if pose[i,0] - self.radius > 0 else 0 - x1 = pose[i,0] + self.radius if pose[i,0] + self.radius < im_size[1] else im_size[1] - y0 = pose[i,1] - self.radius if pose[i,1] - self.radius > 0 else 0 - y1 = pose[i,1] + self.radius if pose[i,1] + self.radius < im_size[0] else im_size[0] + x0 = ( + pose[i, 0] - self.radius + if pose[i, 0] - self.radius > 0 + else 0 + ) + x1 = ( + pose[i, 0] + self.radius + if pose[i, 0] + self.radius < im_size[1] + else im_size[1] + ) + y0 = ( + pose[i, 1] - self.radius + if pose[i, 1] - self.radius > 0 + else 0 + ) + y1 = ( + pose[i, 1] + self.radius + if pose[i, 1] + self.radius < im_size[0] + else im_size[0] + ) coords = [x0, y0, x1, y1] - draw.ellipse(coords, fill=self.colors[i], outline=self.colors[i]) + draw.ellipse( + coords, fill=self.colors[i], outline=self.colors[i] + ) except Exception as e: print(e) - + img_tk = ImageTk.PhotoImage(image=img, master=self.window) self.lab.configure(image=img_tk) self.window.update() - def destroy(self): - ''' + """ Destroys the opencv image window - ''' + """ - self.window.destroy() \ No newline at end of file + self.window.destroy() diff --git a/dlclive/dlclive.py b/dlclive/dlclive.py index 3fdcdf1..3f1990c 100644 --- a/dlclive/dlclive.py +++ b/dlclive/dlclive.py @@ -257,7 +257,7 @@ def init_inference(self, frame=None, **kwargs): # process frame - if frame is None and (self.model_type == 'tflite'): + if frame is None and (self.model_type == "tflite"): raise DLCLiveError( "No image was passed to initialize inference. An image must be passed to the init_inference method" ) diff --git a/dlclive/exceptions.py b/dlclive/exceptions.py index 46ef17c..5d7a1aa 100644 --- a/dlclive/exceptions.py +++ b/dlclive/exceptions.py @@ -5,11 +5,14 @@ Licensed under GNU Lesser General Public License v3.0 """ + class DLCLiveError(Exception): - ''' Generic error type for incorrect use of the DLCLive class ''' + """ Generic error type for incorrect use of the DLCLive class """ + pass class DLCLiveWarning(Warning): - ''' Generic warning for incorrect use of the DLCLive class ''' - pass \ No newline at end of file + """ Generic warning for incorrect use of the DLCLive class """ + + pass diff --git a/dlclive/graph.py b/dlclive/graph.py index d2d16e3..dfe3da7 100644 --- a/dlclive/graph.py +++ b/dlclive/graph.py @@ -10,7 +10,7 @@ def read_graph(file): - ''' + """ Loads the graph from a protobuf file Parameters @@ -22,16 +22,16 @@ def read_graph(file): -------- graph_def :class:`tensorflow.tf.compat.v1.GraphDef` The graph definition of the DeepLabCut model found at the object's path - ''' + """ - with tf.io.gfile.GFile(file, 'rb') as f: + with tf.io.gfile.GFile(file, "rb") as f: graph_def = tf.compat.v1.GraphDef() graph_def.ParseFromString(f.read()) return graph_def def finalize_graph(graph_def): - ''' + """ Finalize the graph and get inputs to model Parameters @@ -45,7 +45,7 @@ def finalize_graph(graph_def): The finalized graph of the DeepLabCut model inputs :class:`tensorflow.Tensor` Input tensor(s) for the model - ''' + """ graph = tf.Graph() with graph.as_default(): @@ -56,7 +56,7 @@ def finalize_graph(graph_def): def get_output_nodes(graph): - ''' + """ Get the output node names from a graph Parameters @@ -68,10 +68,10 @@ def get_output_nodes(graph): -------- output : list the output node names as a list of strings - ''' + """ op_names = [str(op.name) for op in graph.get_operations()] - if 'concat_1' in op_names[-1]: + if "concat_1" in op_names[-1]: output = [op_names[-1]] else: output = [op_names[-1], op_names[-2]] @@ -80,7 +80,7 @@ def get_output_nodes(graph): def get_output_tensors(graph): - ''' + """ Get the names of the output tensors from a graph Parameters @@ -92,10 +92,10 @@ def get_output_tensors(graph): -------- output : list the output tensor names as a list of strings - ''' + """ output_nodes = get_output_nodes(graph) - output_tensor = [out+":0" for out in output_nodes] + output_tensor = [out + ":0" for out in output_nodes] return output_tensor @@ -106,7 +106,7 @@ def get_input_tensor(graph): def extract_graph(graph, tf_config=None): - ''' + """ Initializes a tensorflow session with the specified graph and extracts the model's inputs and outputs Parameters @@ -121,7 +121,7 @@ def extract_graph(graph, tf_config=None): a tensorflow session with the specified graph definition outputs :class:`tensorflow.Tensor` the output tensor(s) for the model - ''' + """ input_tensor = get_input_tensor(graph) output_tensor = get_output_tensors(graph) diff --git a/dlclive/pose.py b/dlclive/pose.py index 3db557e..5c61f04 100644 --- a/dlclive/pose.py +++ b/dlclive/pose.py @@ -10,7 +10,7 @@ def extract_cnn_output(outputs, cfg): - ''' + """ Extract location refinement and score map from DeepLabCut network Parameters @@ -31,23 +31,23 @@ def extract_cnn_output(outputs, cfg): locref : ? location refinement - ''' + """ scmap = outputs[0] scmap = np.squeeze(scmap) locref = None - if cfg['location_refinement']: + if cfg["location_refinement"]: locref = np.squeeze(outputs[1]) shape = locref.shape locref = np.reshape(locref, (shape[0], shape[1], -1, 2)) - locref *= cfg['locref_stdev'] - if len(scmap.shape)==2: #for single body part! - scmap=np.expand_dims(scmap,axis=2) + locref *= cfg["locref_stdev"] + if len(scmap.shape) == 2: # for single body part! + scmap = np.expand_dims(scmap, axis=2) return scmap, locref def argmax_pose_predict(scmap, offmat, stride): - ''' + """ Combines score map and offsets to the final pose Parameters @@ -65,18 +65,17 @@ def argmax_pose_predict(scmap, offmat, stride): -------- pose :class:`numpy.ndarray` pose as a numpy array - ''' + """ num_joints = scmap.shape[2] pose = [] for joint_idx in range(num_joints): - maxloc = np.unravel_index(np.argmax(scmap[:, :, joint_idx]), - scmap[:, :, joint_idx].shape) + maxloc = np.unravel_index( + np.argmax(scmap[:, :, joint_idx]), scmap[:, :, joint_idx].shape + ) offset = np.array(offmat[maxloc][joint_idx])[::-1] - pos_f8 = (np.array(maxloc).astype('float') * stride + 0.5 * stride + - offset) - pose.append(np.hstack((pos_f8[::-1], - [scmap[maxloc][joint_idx]]))) + pos_f8 = np.array(maxloc).astype("float") * stride + 0.5 * stride + offset + pose.append(np.hstack((pos_f8[::-1], [scmap[maxloc][joint_idx]]))) return np.array(pose) @@ -84,21 +83,21 @@ def multi_pose_predict(scmap, locref, stride, num_outputs): Y, X = get_top_values(scmap[None], num_outputs) Y, X = Y[:, 0], X[:, 0] num_joints = scmap.shape[2] - DZ=np.zeros((num_outputs,num_joints,3)) + DZ = np.zeros((num_outputs, num_joints, 3)) for m in range(num_outputs): for k in range(num_joints): x = X[m, k] y = Y[m, k] - DZ[m,k,:2]=locref[y,x,k,:] - DZ[m,k,2]=scmap[y,x,k] + DZ[m, k, :2] = locref[y, x, k, :] + DZ[m, k, 2] = scmap[y, x, k] - X = X.astype('float32')*stride + .5*stride + DZ[:,:,0] - Y = Y.astype('float32')*stride + .5*stride + DZ[:,:,1] + X = X.astype("float32") * stride + 0.5 * stride + DZ[:, :, 0] + Y = Y.astype("float32") * stride + 0.5 * stride + DZ[:, :, 1] P = DZ[:, :, 2] - pose = np.empty((num_joints, num_outputs*3), dtype='float32') - pose[:,0::3] = X.T - pose[:,1::3] = Y.T - pose[:,2::3] = P.T + pose = np.empty((num_joints, num_outputs * 3), dtype="float32") + pose[:, 0::3] = X.T + pose[:, 1::3] = Y.T + pose[:, 2::3] = P.T return pose diff --git a/dlclive/processor/processor.py b/dlclive/processor/processor.py index 3bdf702..8a52f5f 100644 --- a/dlclive/processor/processor.py +++ b/dlclive/processor/processor.py @@ -5,19 +5,19 @@ Licensed under GNU Lesser General Public License v3.0 """ -''' +""" Default processor class. Processors must contain two methods: i) process: takes in a pose, performs operations, and returns a pose ii) save: saves any internal data generated by the processor (such as timestamps for commands to external hardware) -''' +""" -class Processor(object): +class Processor(object): def __init__(self, **kwargs): pass def process(self, pose, **kwargs): return pose - def save(self, file=''): + def save(self, file=""): return 0 diff --git a/dlclive/utils.py b/dlclive/utils.py index 07176e2..4b0deaa 100644 --- a/dlclive/utils.py +++ b/dlclive/utils.py @@ -12,18 +12,23 @@ try: import skimage + SK_IM = True except Exception: SK_IM = False try: import cv2 + OPEN_CV = True except Exception: from PIL import Image + OPEN_CV = False warnings.warn( - "OpenCV is not installed. Using pillow for image processing, which is slower.", DLCLiveWarning) + "OpenCV is not installed. Using pillow for image processing, which is slower.", + DLCLiveWarning, + ) def convert_to_ubyte(frame): @@ -95,7 +100,9 @@ def img_to_rgb(frame): else: warnings.warn( - f"Image has {frame.ndim} dimensions. Must be 2 or 3 dimensions to convert to RGB", DLCLiveWarning) + f"Image has {frame.ndim} dimensions. Must be 2 or 3 dimensions to convert to RGB", + DLCLiveWarning, + ) return frame @@ -115,7 +122,7 @@ def gray_to_rgb(frame): else: img = Image.fromarray(frame) - img = img.convert('RGB') + img = img.convert("RGB") return np.asarray(img) @@ -135,7 +142,7 @@ def bgr_to_rgb(frame): else: img = Image.fromarray(frame) - img = img.convert('RGB') + img = img.convert("RGB") return np.asarray(img) @@ -177,14 +184,15 @@ def _img_as_ubyte_np(frame): elif np.issubdtype(im_type, np.integer): im_type_info = np.iinfo(im_type) - frame *= 255/im_type_info.max + frame *= 255 / im_type_info.max frame[frame < 0] = 0 return frame.astype(np.uint8) else: raise TypeError( - "image of type {} could not be converted to ubyte".format(im_type)) + "image of type {} could not be converted to ubyte".format(im_type) + ) def decode_fourcc(cc): diff --git a/dlclive/version.py b/dlclive/version.py index 0b4a5dc..a28c9db 100644 --- a/dlclive/version.py +++ b/dlclive/version.py @@ -7,5 +7,5 @@ """ -__version__ = '0.0.1' +__version__ = "0.0.1" VERSION = __version__ diff --git a/example_processors/DogJumpLED/izzy_jump.py b/example_processors/DogJumpLED/izzy_jump.py index d843e84..f24356d 100644 --- a/example_processors/DogJumpLED/izzy_jump.py +++ b/example_processors/DogJumpLED/izzy_jump.py @@ -141,4 +141,3 @@ def process(self, pose, **kwargs): def save(self, filename): return IzzyJump.save(self, filename) - diff --git a/example_processors/DogJumpLED/izzy_jump_offline.py b/example_processors/DogJumpLED/izzy_jump_offline.py index a92c3b8..9e574ab 100644 --- a/example_processors/DogJumpLED/izzy_jump_offline.py +++ b/example_processors/DogJumpLED/izzy_jump_offline.py @@ -121,4 +121,3 @@ def process(self, pose, **kwargs): def save(self, filename): return IzzyJumpOffline.save(self, filename) - diff --git a/setup.py b/setup.py index 2257ed6..02b6ebd 100755 --- a/setup.py +++ b/setup.py @@ -26,7 +26,9 @@ ] if "tegra" in platform.platform(): - warnings.warn("Not installing the following packages:\nopencv-python\ntensorflow\npandas\ntables\nPlease follow instructions on github to install opencv and tensorflow. If you want to use the benchmark_videos function to save poses from a video, then please install pandas and tables (pip install pandas tables)") + warnings.warn( + "Not installing the following packages:\nopencv-python\ntensorflow\npandas\ntables\nPlease follow instructions on github to install opencv and tensorflow. If you want to use the benchmark_videos function to save poses from a video, then please install pandas and tables (pip install pandas tables)" + ) else: install_requires.append("opencv-python") install_requires.append("tensorflow==1.13.1")