def inspect(self): """ Run through calibration images, showing original and undistorted. Intended to be run after saving calibration images. """ # access calibration images if self.calibpath is None: raise RuntimeError('calibpath is unset') imgpath = self.calibpath + '/corners2' fns = [ imgpath + '/' + f for f in listdir(imgpath) if f[-4:].lower() == '.jpg' ] fns.sort() imgs = [cv_imread(f) for f in fns] shape = list(imgs[0].shape) shape.append(len(imgs)) # undistort # go through 1-by-1 b/c asarray(imgs) gets shaped wrong & reshape messes with things ud = zeros(shape, dtype=uint8) for i in range(len(imgs)): ud[..., i] = self.correct(imgs[i].squeeze()) # display side-by-side for i in range(len(imgs)): img = imgs[i].copy() u = ud[..., i].copy() cv_putText(img, fns[i][-10:], (5, 15), 1, 1, (0, 0, 255), thickness=2) cv_putText(img, 'Press space for next image', (5, 30), 1, 1, (0, 0, 255), thickness=2) cv_putText(u, 'Undistort', (5, 15), 1, 1, (0, 0, 255), thickness=2) frame = concatenate((img, u), axis=0) cv_imshow('frame', frame) press = waitKey(-1) if press == ord('q'): break destroyAllWindows()
from argparse import ArgumentParser from cv2 import destroyAllWindows, waitKey from cv2 import imshow as cv_imshow from cv2 import putText as cv_putText from numpy import zeros parser = ArgumentParser( description='Determine how OpenCV\'s `waitKey` interprets keypresses' ) args = parser.parse_args() u = zeros((240, 320, 3)) # reference frame u1 = u.copy() # prealloc mem lpress = 0 # last press (`waitKey` timeout returns 255) try: while 1: u1 = u.copy() # avoid constant text cv_putText(u1, 'q to quit', (5,30), 1,1,(0,0,255), thickness=2) cv_putText(u1, 'Last press: %d' % lpress, (5,15), 1,1,(0,0,255), thickness=2) cv_imshow('win', u1) press = waitKey(20) if press == ord('q'): break if press != 255: # `waitKey` returns 255 on timeout lpress = press except Exception as e: raise e finally: destroyAllWindows()
Normalize alpha from trackbar for `getOptimalNewCameraMatrix` """ global alpha_val alpha_val = val / 100.0 cap = open_camera(cam=args.cam, w=args.w, h=args.h) try: namedWindow('win') createTrackbar('alpha', 'win', 100, 100, getAlpha) W = int(cap.get(CAP_PROP_FRAME_WIDTH)) H = int(cap.get(CAP_PROP_FRAME_HEIGHT)) u = zeros((H * 2, W, 3), dtype='uint8') # pre-initialize display while 1: # Interface cv_imshow('win', u) press = waitKey(20) if press in (27, 81, 113): break # Update Camera Matrix ncm, roi = getOptimalNewCameraMatrix(calib.cameraMatrix, calib.distCoeffs, (W, H), alpha_val) x, y, w, h = roi # Read frames and correct r, f = cap.read() if not r: logging.warning('Unable to read frame') continue
def clean_calib_imgs(self, basepath=None, rawpath=None, cpath=None): """ Provides interface to sanitize calibration images using a tutorial-like imshow() interface. Deletes frames, and removes elements from self.corners_arr, if they are not None. INPUTS (optional) basepath -- str -- Base path for calib frames; None rawpath -- str -- Path to frames for calibration; None cpath -- str -- Path to frames with chessboard; None NOTE The frames in each path must have the same name, e.g. 'f00001.jpg' USAGE SPECIFIED ONLY BASEPATH When all other paths the frame paths will be defined as: rawpath = basepath + '/raw' cpath = basepath + '/corners' PATHS TO ALL FRAMES If rawpath and cpath are all explicitly defined, they will be used as is. basepath will be ignored. """ # Parse paths if basepath is not None: basepath = realpath(basepath) if basepath[basepath.rfind('/') + 1:] not in ('raw', 'corners', 'corners2'): logging.warning( 'Assuming \'%s\' is base dir for all unspecified frames' % basepath) basepath = basepath else: basepath = dirname(basepath) if rawpath is None: rawpath = basepath + '/raw' if cpath is None: cpath = basepath + '/corners' # Select images to save fn_imgs = [f for f in listdir(cpath) if f[-4:].lower() == '.jpg'] fn_imgs.sort() i = 0 while i < len(fn_imgs): f = fn_imgs[i] img = cv_imread(cpath + '/' + f) if img is None: # catch deleted image fn_imgs.pop(i) if self.corners_arr is not None: self.corners_arr.pop(i) i += 1 continue cv_putText(img, f, (5, 15), 1, 1, (0, 0, 255), thickness=2) cv_putText(img, '\'r\' to remove', (5, 30), 1, 1, (0, 0, 255), thickness=2) cv_putText(img, 'left bracket to go back', (5, 45), 1, 1, (0, 0, 255), thickness=2) cv_imshow('image', img) # interface press = waitKey(-1) if press == ord('r'): fn = fn_imgs[i][fn_imgs[i].rfind('f'):] os_remove(rawpath + '/' + fn) os_remove(cpath + '/' + fn) elif press in (27, 81, 113): # esc, q, Q break elif press == 91: # left bracket i -= 2 i += 1 destroyAllWindows()
def record_calib_imgs(self, **kwargs): """ Provides photobooth-esque countdown interface. Saves frames to calib path in subdirectories `raw/` and `corners/`. Be sure to initialize the chessboard first. INPUTS (optional) cam -- str/int -- camera descriptor for VideoCapture; '/dev/psEye' nframes -- int -- number of frames to record for calibration; 15 w -- int -- width (px) to set camera frame; 320 h -- int -- height (px) to set camera frame; 240 fps -- int -- frames per second to set camera; 100 countdown -- int -- seconds to countdown before recording frame; 3 EXCEPTIONS raises RuntimeError when chessboard hasn't been properly initialized by constructor or `init_chessboard`. """ cam = kwargs.get('cam', '/dev/psEye') nframes = kwargs.get('nframes', 15) w = kwargs.get('w', 320) h = kwargs.get('h', 240) countdown = kwargs.get('countdown', 3) if type(nframes) != int: raise TypeError('nframes must be integer') if type(countdown) != int: raise TypeError('countdown must be integer') if self.img_arr is not None or self.calibpath is None: raise RuntimeError('Did you call init_chessboard() first?') cap = open_camera(cam, w, h, 100) # this handles asserts self.w = int(cap.get(CAP_PROP_FRAME_WIDTH)) self.h = int(cap.get(CAP_PROP_FRAME_HEIGHT)) self.img_arr = zeros((self.h, self.w, 1, nframes), dtype=uint8) # raw frames clist = [] # corners frames self.corners_arr = [] self.objpoints = [] # Recording sc = 0 # "sample count" timer_ref = now() timer = lambda: 1 + int(countdown + timer_ref - now() ) # countDOWN 3,2,1 try: # try/except to make sure camera device gets released img = zeros(self.img_arr.shape[:-1]) # for immediate cv_imshow while sc < nframes: # Display at top so can always exit cv_imshow('capture', img) press = waitKey(20) if press in (113, 81, 27): # q, Q, esc: logging.debug('quitting record') break # Find chessboard, if possible ret, raw = cap.read() if not ret: logging.error('Failed to access frame') timer_ref = now() # reset timer when things go wrong continue gray = cvtColor(raw, COLOR_RGB2GRAY) corners = self._find_chessboard(gray) # Compute visual feedback if corners is None: # alert to unfindable chessboard img = raw.copy() cv_putText(img, 'NO CHESSBOARD', (5, 15), 1, 1, (0, 0, 255), thickness=2) timer_ref = now( ) # reset timer when chessboard isn't viable else: # show countdown and progess board1 = drawChessboardCorners(raw, self.boardsize, corners, ret) img = board1.copy() cv_putText(img, 'T-%ds' % timer(), (5, 15), 1, 1, (0, 0, 255), thickness=2) cv_putText(img, '%d/%d' % (sc + 1, nframes), (5, 30), 1, 1, (0, 0, 255), thickness=2) # Capture image if timer() <= 0: # image saving self.img_arr[..., sc] = gray.copy()[..., newaxis] clist.append(board1) # for camera calibration self.corners_arr.append(corners) self.objpoints.append(self.objp) # program progess/display img = zeros(raw.shape, dtype=uint8) + 255 # "flash" camera sc += 1 timer_ref = now() # Save images to file if self.calibpath is None: self._create_calib_path() # Create save directories rawpath = self.calibpath + '/raw' cpath = self.calibpath + '/corners' for p in (rawpath, cpath): if not isdir(p): if os_exists(p): logging.warning( '\'%s\' exists, but is not directory. Overwriting.' % p) os_remove(p) mkdir(p) for i in range(nframes): fn_raw = rawpath + ('/f%s' % str(i + 1).zfill(5)) + '.jpg' fn_c = cpath + ('/f%s' % str(i + 1).zfill(5)) + '.jpg' cv_imwrite(fn_raw, self.img_arr[..., i], (IMWRITE_JPEG_QUALITY, 100)) cv_imwrite(fn_c, clist[i], (IMWRITE_JPEG_QUALITY, 100)) # Close Capture except Exception as e: logging.error(e) finally: cap.release() destroyAllWindows() logging.debug('released \'%s\'' % cam)
def livetest(calibs, cam='/dev/psEye', w=320, h=240, fps=100): """ Visually inspect calibrations on images actively streaming in from cam. INPUTS calibs -- str/CalibratePSEye -- fn to CSV calibrations or object with calibrations already loaded (optional) cam -- str/int -- camera descriptor for VideoCapture; '/dev/psEye' w -- int -- width (px) to set camera frame; 320 h -- int -- height (px) to set camera frame; 240 fps -- int -- frames per second to set camera; 100 CSV FORMAT - delimiter: ', ' - no floats, round and convert to int using known precision - strings in double quotes INTERFACE 'space' to pause stream, 'q' to quit stream. """ # Load calibrations if necessary if type(calibs) == str: fn = calibs # resave to overwrite `calibs` calibs = CalibratePSEye() calibs.load_calibrations(fn) if type(calibs) != CalibratePSEye: raise RuntimeError('\'calibs\' is not type \'CalibratePSEye\'') # open_camera handles type asserts for camera params cap = open_camera(cam, w, h, fps) # Stream undistortion # try/finally to ensure cap.release() is called try: while 1: ret, raw = cap.read() if not ret: logging.warning('failed to read frame from \'%s\'' % cam) ud = calibs.correct(raw) # label data for user cv_putText(raw, 'Space to pause, q to quit', (5, 15), 1, 1, (0, 0, 255), thickness=2) cv_putText(ud, 'undistort', (5, 15), 1, 1, (0, 0, 255), thickness=2) total = concatenate((raw, ud), axis=0) # 1 frame for cleanliness cv_imshow('total', total) press = waitKey(30) if press == ord('q'): break elif press == ord(' '): if waitKey(-1) == ord('q'): break finally: cap.release() destroyAllWindows()