From a03c10f842efaa68bc31398cb038db60443c3b5b Mon Sep 17 00:00:00 2001 From: Aaron Snoswell Date: Fri, 3 May 2019 09:42:25 +1000 Subject: [PATCH 1/3] Add OpenCV render window mode --- dm2gym/dm_control_env.py | 81 ++++++++++++++++++++++++++++++++++++---- setup.py | 5 ++- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/dm2gym/dm_control_env.py b/dm2gym/dm_control_env.py index 02a0a1b..41c406b 100644 --- a/dm2gym/dm_control_env.py +++ b/dm2gym/dm_control_env.py @@ -1,9 +1,11 @@ from collections import OrderedDict import numpy as np + import gym from gym import spaces from gym.envs.registration import EnvSpec + from dm_control.rl.specs import ArraySpec from dm_control.rl.specs import BoundedArraySpec @@ -29,8 +31,29 @@ def convert_dm_control_to_gym_space(dm_control_space): class DMControlEnv(gym.Env): - def __init__(self, env): + + def __init__(self, env, *, render_window_mode='gym'): + """Constructor + + Args: + env (dm_control.rl.control.Environment) The dm_control environment + to wrap + render_window_mode (str): Which render window mode to use. Options + are; + - 'gym' (default): Use + `gym.envs.classic_control.rendering.SimpleImageViewer`, + which is backed by pyglet + - 'opencv': Use an OpenCV rendering window mode + """ + + # Verify render window mode + assert render_window_mode in ['gym', 'opencv'],\ + "Invalid value for render_window_mode: {}".format( + render_window_mode + ) + self.env = env + self.render_window_mode = render_window_mode self.metadata = {'render.modes': ['human', 'rgb_array'], 'video.frames_per_second': int(np.round(1.0/self.env.control_timestep()))} @@ -56,18 +79,62 @@ def reset(self): return timestep.observation def render(self, mode='human', **kwargs): + if 'camera_id' not in kwargs: - kwargs['camera_id'] = 0 # tracking camera + # Tracking camera + kwargs['camera_id'] = 0 + img = self.env.physics.render(**kwargs) + if mode == 'rgb_array': + return img + elif mode == "human": - from gym.envs.classic_control import rendering - if self.viewer is None: - self.viewer = rendering.SimpleImageViewer(maxwidth=1024) - self.viewer.imshow(img) - return self.viewer.isopen + + if self.render_window_mode == 'gym': + + # Use a gym-backed render window + from gym.envs.classic_control import rendering + + # Construct viewer + if self.viewer is None: + self.viewer = rendering.SimpleImageViewer(maxwidth=1024) + + self.viewer.imshow(img) + + return self.viewer.isopen + + elif self.render_window_mode == 'opencv': + + # Use an opencv-backed render window + import cv2 + + # Construct viewer, saving the window ID string to self.viewer + if self.viewer is None: + self.viewer = self.env.__str__() + cv2.namedWindow(self.viewer, cv2.WINDOW_AUTOSIZE) + + # Convert to BGR and show + cv2.imshow(self.viewer, img[:, :, [2, 1, 0]]) + + # Listen for escape key, then exit if pressed + if cv2.waitKey(1) in [27]: + exit() + + return True + + + else: + + raise ValueError( + "Invalid value for render_window_mode: {}".format( + self.render_window_mode + ) + ) + else: + raise NotImplementedError def close(self): diff --git a/setup.py b/setup.py index f2a585b..7fbca8d 100644 --- a/setup.py +++ b/setup.py @@ -7,9 +7,10 @@ with codecs.open('README.md', encoding='utf-8') as f: long_description = f.read() -# Minimal requried dependencies (full dependencies in requirements.txt) +# Minimal requried dependencies install_requires = ['numpy', - 'gym'] + 'gym', + 'opencv-python'] tests_require = ['pytest', 'flake8', 'sphinx', From 4c040820695138ca5513edd538d4401a0e59d0af Mon Sep 17 00:00:00 2001 From: Aaron Snoswell Date: Sun, 12 May 2019 11:44:46 +1000 Subject: [PATCH 2/3] Make a separate OpenCVImageViewer class --- dm2gym/__init__.py | 1 + dm2gym/dm_control_env.py | 77 ++++++++--------------------------- dm2gym/opencv_image_viewer.py | 39 ++++++++++++++++++ 3 files changed, 57 insertions(+), 60 deletions(-) create mode 100644 dm2gym/opencv_image_viewer.py diff --git a/dm2gym/__init__.py b/dm2gym/__init__.py index 11846f2..57a700f 100644 --- a/dm2gym/__init__.py +++ b/dm2gym/__init__.py @@ -1 +1,2 @@ from .dm_control_env import DMControlEnv +from .opencv_image_viewer import OpenCVImageViewer diff --git a/dm2gym/dm_control_env.py b/dm2gym/dm_control_env.py index 41c406b..412cff1 100644 --- a/dm2gym/dm_control_env.py +++ b/dm2gym/dm_control_env.py @@ -32,28 +32,8 @@ def convert_dm_control_to_gym_space(dm_control_space): class DMControlEnv(gym.Env): - def __init__(self, env, *, render_window_mode='gym'): - """Constructor - - Args: - env (dm_control.rl.control.Environment) The dm_control environment - to wrap - render_window_mode (str): Which render window mode to use. Options - are; - - 'gym' (default): Use - `gym.envs.classic_control.rendering.SimpleImageViewer`, - which is backed by pyglet - - 'opencv': Use an OpenCV rendering window mode - """ - - # Verify render window mode - assert render_window_mode in ['gym', 'opencv'],\ - "Invalid value for render_window_mode: {}".format( - render_window_mode - ) - + def __init__(self, env): self.env = env - self.render_window_mode = render_window_mode self.metadata = {'render.modes': ['human', 'rgb_array'], 'video.frames_per_second': int(np.round(1.0/self.env.control_timestep()))} @@ -78,12 +58,18 @@ def reset(self): timestep = self.env.reset() return timestep.observation - def render(self, mode='human', **kwargs): + def render(self, mode='human', *, render_window_mode='gym', **kwargs): if 'camera_id' not in kwargs: # Tracking camera kwargs['camera_id'] = 0 + # Verify render window mode + assert render_window_mode in ['gym', 'opencv'],\ + "Invalid value for render_window_mode: {}".format( + render_window_mode + ) + img = self.env.physics.render(**kwargs) if mode == 'rgb_array': @@ -92,46 +78,17 @@ def render(self, mode='human', **kwargs): elif mode == "human": - if self.render_window_mode == 'gym': - - # Use a gym-backed render window - from gym.envs.classic_control import rendering - - # Construct viewer - if self.viewer is None: + if self.viewer is None: + # Open viewer + if render_window_mode == 'gym': + from gym.envs.classic_control import rendering self.viewer = rendering.SimpleImageViewer(maxwidth=1024) + elif render_window_mode == 'opencv': + from dm2gym import OpenCVImageViewer + self.viewer = OpenCVImageViewer() - self.viewer.imshow(img) - - return self.viewer.isopen - - elif self.render_window_mode == 'opencv': - - # Use an opencv-backed render window - import cv2 - - # Construct viewer, saving the window ID string to self.viewer - if self.viewer is None: - self.viewer = self.env.__str__() - cv2.namedWindow(self.viewer, cv2.WINDOW_AUTOSIZE) - - # Convert to BGR and show - cv2.imshow(self.viewer, img[:, :, [2, 1, 0]]) - - # Listen for escape key, then exit if pressed - if cv2.waitKey(1) in [27]: - exit() - - return True - - - else: - - raise ValueError( - "Invalid value for render_window_mode: {}".format( - self.render_window_mode - ) - ) + self.viewer.imshow(img) + return self.viewer.isopen else: diff --git a/dm2gym/opencv_image_viewer.py b/dm2gym/opencv_image_viewer.py new file mode 100644 index 0000000..4698089 --- /dev/null +++ b/dm2gym/opencv_image_viewer.py @@ -0,0 +1,39 @@ +"""A simple OpenCV based viewer for dm_control images""" + +import cv2 +import uuid + + +class OpenCVImageViewer(): + """A simple OpenCV highgui based dm_control image viewer + + This class is meant to be a drop-in replacement for + `gym.envs.classic_control.rendering.SimpleImageViewer` + """ + + def __init__(self, *, escape_to_exit=False): + """Construct the viewing window""" + self._escape_to_exit = escape_to_exit + self._window_name = str(uuid.uuid4()) + cv2.namedWindow(self._window_name, cv2.WINDOW_AUTOSIZE) + self._isopen = True + + def __del__(self): + """Close the window""" + cv2.destroyWindow(self._window_name) + self._isopen = False + + def imshow(self, img): + """Show an image""" + + # Convert image to BGR format + cv2.imshow(self._window_name, img[:, :, [2, 1, 0]]) + + # Listen for escape key, then exit if pressed + if cv2.waitKey(1) in [27] and self._escape_to_exit: + exit() + + @property + def isopen(self): + """Is the window open?""" + return self._isopen From 8e1e2f793c9bf49dba430727e56081b9114c5aa2 Mon Sep 17 00:00:00 2001 From: Aaron Snoswell Date: Wed, 15 May 2019 12:28:52 +1000 Subject: [PATCH 3/3] Remove whitespace to match coding style --- dm2gym/dm_control_env.py | 11 ----------- dm2gym/opencv_image_viewer.py | 3 --- 2 files changed, 14 deletions(-) diff --git a/dm2gym/dm_control_env.py b/dm2gym/dm_control_env.py index 412cff1..9ab27aa 100644 --- a/dm2gym/dm_control_env.py +++ b/dm2gym/dm_control_env.py @@ -31,7 +31,6 @@ def convert_dm_control_to_gym_space(dm_control_space): class DMControlEnv(gym.Env): - def __init__(self, env): self.env = env self.metadata = {'render.modes': ['human', 'rgb_array'], @@ -59,25 +58,18 @@ def reset(self): return timestep.observation def render(self, mode='human', *, render_window_mode='gym', **kwargs): - if 'camera_id' not in kwargs: # Tracking camera kwargs['camera_id'] = 0 - # Verify render window mode assert render_window_mode in ['gym', 'opencv'],\ "Invalid value for render_window_mode: {}".format( render_window_mode ) - img = self.env.physics.render(**kwargs) - if mode == 'rgb_array': - return img - elif mode == "human": - if self.viewer is None: # Open viewer if render_window_mode == 'gym': @@ -86,12 +78,9 @@ def render(self, mode='human', *, render_window_mode='gym', **kwargs): elif render_window_mode == 'opencv': from dm2gym import OpenCVImageViewer self.viewer = OpenCVImageViewer() - self.viewer.imshow(img) return self.viewer.isopen - else: - raise NotImplementedError def close(self): diff --git a/dm2gym/opencv_image_viewer.py b/dm2gym/opencv_image_viewer.py index 4698089..acb90f4 100644 --- a/dm2gym/opencv_image_viewer.py +++ b/dm2gym/opencv_image_viewer.py @@ -10,7 +10,6 @@ class OpenCVImageViewer(): This class is meant to be a drop-in replacement for `gym.envs.classic_control.rendering.SimpleImageViewer` """ - def __init__(self, *, escape_to_exit=False): """Construct the viewing window""" self._escape_to_exit = escape_to_exit @@ -25,10 +24,8 @@ def __del__(self): def imshow(self, img): """Show an image""" - # Convert image to BGR format cv2.imshow(self._window_name, img[:, :, [2, 1, 0]]) - # Listen for escape key, then exit if pressed if cv2.waitKey(1) in [27] and self._escape_to_exit: exit()