diff --git a/pykick/imagereaders.py b/pykick/imagereaders.py index 25e26d5..89ee4cf 100644 --- a/pykick/imagereaders.py +++ b/pykick/imagereaders.py @@ -9,16 +9,40 @@ except: class NaoImageReader(object): + """Class for reading images from NAO. + + Depends on ALProxy from NAOqi SDK. + + """ RESOLUTIONS = { - 0: (120, 160), - 1: (240, 320), + 0: (120, 160), # 160x120 + 1: (240, 320), # and so on 2: (480, 640), 3: (960, 1280) } - def __init__(self, ip, port=9559, res=1, fps=30, cam_id=0, video_file=None): - ip = bytes(ip) + def __init__(self, ip, port=9559, res=1, fps=30, cam_id=0, + video_file=None): + """ + Parameters + ---------- + ip : string + IP-Address of the NAO, + port : int + Port on NAO where ALProxy listens. + res : int + Resolution, see RESOLUTIONS above + fps : int + Frames per second, not sure if it does anything. + cam_id : 0 or 1 + 0 for top camera, 1 for lower camera. + video_file : string or None + Use None, otherwise is likely to crash. + + """ + + ip = bytes(ip) # Python2 thing self.res_id = res self.recording = [] self.res = self.RESOLUTIONS[res] @@ -32,15 +56,43 @@ class NaoImageReader(object): ) def to_angles(self, x, y): + """Transform relative x, y coordinates into camera angles. + + Parameters + ---------- + x, y : float + Relative X and Y coordinate (0.0 to 1.0) as returned by + `to_relative`. + + Returns + ------- + tuple + (x_ang, y_ang) angular coordinates in camera frame in radians. + + """ return self.vd.getAngularPositionFromImagePosition( self.cam_id, [x, y] ) def to_relative(self, x, y): + """Transform pixel coordinates into relative coordinates. + + Parameters + ---------- + x, y : float + Pixel coordinates to transform. + + Returns + ------- + tuple + (x_rel, y_rel) Relative (0.0 to 1.0) coordinates. + + """ print('Camera resolution:', self.res) return x / self.res[1], y / self.res[0] def get_frame(self): + """Get the next frame as a BGR OpenCV image.""" result = self.vd.getImageRemote(self.sub) self.vd.releaseImage(self.sub) if result is None or result[6] is None: @@ -56,6 +108,7 @@ class NaoImageReader(object): return frame def close(self): + """Stop and clean up.""" self.vd.unsubscribe(self.sub) print(self.sub + 'captured %s frames' % len(self.recording)) print('Writing to', self.video_file) @@ -69,6 +122,7 @@ class NaoImageReader(object): vf.release() def restart(self): + """Restart the proxy if something went wrong.""" self.vd.unsubscribe(self.sub) self.sub = self.vd.subscribeCamera( self.sub, self.cam_id, self.res_id, 13, self.fps @@ -77,13 +131,25 @@ class NaoImageReader(object): class VideoReader(object): + """Class with the same interface as `NaoImageReader` for videofiles.""" def __init__(self, filename=0, loop=False): + """ + Parameters + ---------- + filename : string or 0 + Will try to read from file if name given or from webcam if 0 + specified. + loop : bool + If video is over, start again or stop? + + """ self.cap = cv2.VideoCapture(filename) self.loop = loop if filename else False self.ctr = 0 def get_frame(self): + """Get the next frame as a BGR OpenCV image.""" succ, frame = self.cap.read() if not succ: raise ValueError('Error while reading video.\n' + @@ -96,11 +162,12 @@ class VideoReader(object): return frame def close(self): + """Cleanup and stop.""" self.cap.release() class PictureReader(object): - "Dummy class for maybe convenience." + "Dummy class for reading image files for maybe convenience." def __init__(self, filename): self.frame = cv2.imread(filename) @@ -109,4 +176,4 @@ class PictureReader(object): return self.frame.copy() def close(self): - pass + self.frame = None