def return_to_start(self): """Move back to starting position.""" if self.relative_starting_position is not None: log('Returning to starting location...', message_type='info', title='camera-calibration') self._move(self.relative_starting_position)
def find_pattern(self, img, move_back=False, save_output=None): """Find calibration pattern circles in single image.""" if img is None: log('ERROR: Calibration failed. Image missing.', message_type='error', title='camera-calibration') self.success_flag = False original = img.copy() # first pass with basic pre-processing img = self.preprocess(original, basic=True) ret, centers = self.detect_circles(img) if not ret: # second pass with heavier pre-processing img = self.preprocess(original) ret, centers = self.detect_circles(img, downsample=True) if save_output is not None: cv2.drawChessboardCorners(img, self.pattern['size'], centers, ret) save_output(ret, img, original) if not ret and save_output is None: if move_back: self.return_to_start() log('ERROR: Calibration failed, calibration object not ' + 'detected in image. Check recent photos.', message_type='error', title='camera-calibration') self.success_flag = False return ret, centers
def move_and_capture(self): """Move the bot along x and y axes, take photos, and detect circles.""" for i, movement in enumerate(RELATIVE_MOVEMENTS): if i > 0: if self.relative_starting_position is None: self.relative_starting_position = {'x': 0, 'y': 0, 'z': 0} log('Moving to next camera calibration photo location.', message_type='info', title='camera-calibration') self._move(movement) for axis in movement: self.relative_starting_position[axis] -= movement[axis] log('Taking camera calibration photo. ({}/3)'.format(i + 1), message_type='info', title='camera-calibration') img_filename = self.capture() if USE_FARMWARE_TOOLS: coordinates = device.get_current_position() for axis, coordinate in coordinates.items(): coordinates[axis] = float(coordinate) else: coordinates = {'z': 0} img = cv2.imread(img_filename, 1) os.remove(img_filename) ret, centers = self.find_pattern(img, True) if not self.success_flag: self.save_image(img, str(i + 1)) return self.success_flag self.dot_images[i]['circles'] = centers self.dot_images[i]['found'] = ret self.dot_images[i]['image'] = img self.dot_images[i]['coordinates'] = coordinates self.return_to_start() return self.success_flag
def move_and_capture(self): """Move the bot along x and y axes, take photos, and detect circles.""" for i, move in enumerate(RELATIVE_MOVEMENTS): if i > 0: log('Moving to next camera calibration photo location.', message_type='info', title='camera-calibration') if USE_FARMWARE_TOOLS: device.move_relative(move['x'], move['y'], move['z'], 100) else: CeleryPy.move_relative((move['x'], move['y'], move['z']), speed=100) log('Taking camera calibration photo. ({}/3)'.format(i + 1), message_type='info', title='camera-calibration') img_filename = self.capture() if USE_FARMWARE_TOOLS: coordinates = device.get_current_position() for axis, coordinate in coordinates.items(): coordinates[axis] = float(coordinate) else: coordinates = {'z': 0} img = cv2.imread(img_filename, 1) os.remove(img_filename) ret, centers = self.find_pattern(img) if not self.success_flag: self.save_image(img, str(i + 1)) return self.success_flag self.dot_images[i]['circles'] = centers self.dot_images[i]['found'] = ret self.dot_images[i]['image'] = img self.dot_images[i]['coordinates'] = coordinates return self.success_flag
def load(self, filename): """Load image from file.""" self.images['original'] = cv2.imread(filename, 1) if self.plant_db.coordinates is None: self.plant_db.getcoordinates(test_coordinates=False) if self.plant_db.coordinates is None and self.plant_db.app: log("ERROR: Could not get image coordinates.", message_type='error', title='take-photo') sys.exit(0) if self.images['original'] is None: print("ERROR: Incorrect image path ({}).".format(filename)) sys.exit(0) self.image_name = os.path.splitext(os.path.basename(filename))[0] self._prepare()
def calibration(self): """Determine pixel to coordinate scale and image rotation angle.""" total_rotation_angle = 0 warning_issued = False if self.debug: self.cparams.print_input() if self.calibration_params['easy_calibration']: from plant_detection.PatternCalibration import PatternCalibration pattern_calibration = PatternCalibration(self.calibration_params) result_flag = pattern_calibration.move_and_capture() if not result_flag: fail_flag = True return fail_flag result_flag = pattern_calibration.calibrate() if not result_flag: fail_flag = True return fail_flag self.image.images['marked'] = pattern_calibration.output_img self.image.grid(self) fail_flag = False return fail_flag for i in range(0, self.calibration_params['calibration_iters']): self.image.initial_processing() self.image.find(calibration=True) # find objects # If not the last iteration, determine camera rotation angle if i != (self.calibration_params['calibration_iters'] - 1): # Check number of objects detected and notify user if needed. if len(self.plant_db.calibration_pixel_locations) == 0: log("ERROR: Calibration failed. No objects detected.", message_type='error', title='camera-calibration') return True if self.plant_db.object_count > 2: if not warning_issued: log(" Warning: {} objects detected. " "Exactly 2 recommended. " "Incorrect results possible. Check output.".format( self.plant_db.object_count), message_type='warn', title='camera-calibration') warning_issued = True if self.plant_db.object_count < 2: log(" ERROR: {} objects detected. " "At least 2 required. Exactly 2 recommended.".format( self.plant_db.object_count), message_type='error', title='camera-calibration') return True # Use detected objects to determine required rotation angle self.rotationdetermination() if abs(self.rotationangle) > 120: log(" ERROR: Excessive rotation required. " "Check that the calibration objects are " "parallel with the desired axis and that " "they are the only two objects detected.", message_type='error', title='camera-calibration') return True self.image.rotate_main_images(self.rotationangle) total_rotation_angle += self.rotationangle self.determine_scale() fail_flag = self._calibration_output(total_rotation_angle) return fail_flag
def _coordinate_conversion(self): # determine detected object coordinates # Load calibration data load_data_from = None calibration_data = None if self.args['from_env_var']: load_data_from = 'env_var' elif self.args['from_file']: load_data_from = 'file' else: # use data saved in self.params calibration_data = self.params.calibration_data # Initialize coordinate conversion module self.p2c = Pixel2coord(self.plant_db, load_data_from=load_data_from, calibration_data=calibration_data) self.p2c.debug = self.args['debug'] # Check for coordinate conversion calibration results present = { 'coord_scale': False, 'camera_z': False, 'center_pixel_location': False, 'total_rotation_angle': False } try: for key in present: present[key] = self.p2c.calibration_params[key] except KeyError: log( "ERROR: Coordinate conversion calibration values " "not found. Run calibration first.", message_type='error', title='plant-detection') sys.exit(0) # Validate coordinate conversion calibration data for image calibration_data_valid = self.p2c.validate_calibration_data( self.image.images['current']) if not calibration_data_valid: log( "ERROR: Coordinate conversion calibration values " "invalid for provided image.", message_type='error', title='plant-detection') sys.exit(0) # Determine object coordinates self.image.coordinates(self.p2c, draw_contours=self.args['draw_contours']) # Organize objects into plants and weeds self.plant_db.identify(self.params.parameters) if self.plant_db.plants['safe_remove']: self.image.safe_remove(self.p2c)
def camera_check(self): """Check for camera at ports 0 and 1.""" if not os.path.exists('/dev/video' + str(self.camera_port)): if not self.silent: print('No camera detected at video{}.'.format( self.camera_port)) self.camera_port = 1 if not self.silent: print('Trying video{}...'.format(self.camera_port)) if not os.path.exists('/dev/video' + str(self.camera_port)): if not self.silent: print('No camera detected at video{}.'.format( self.camera_port)) log('USB Camera not detected.', message_type='error', title='take-photo')
def capture(self): """Take a photo.""" WIDTH = os.getenv('take_photo_width', '640') HEIGHT = os.getenv('take_photo_height', '480') if 'NONE' in CAMERA: log('No camera selected. Choose a camera on the device page.', message_type='error', title='take-photo') self.exit() elif 'RPI' in CAMERA: # With Raspberry Pi Camera: image_filename = self.save(filename_only=True) width = min(int(WIDTH), 4056) height = min(int(HEIGHT), 3040) size = ['-w', str(width), '-h', str(height)] if height > 1500: size = ['-md', '3'] try: retcode = call(['raspistill'] + size + ['-o', image_filename]) except OSError: log('Raspberry Pi Camera not detected.', message_type='error', title='take-photo') self.exit() else: if retcode == 0: print('Image saved: {}'.format(image_filename)) return image_filename else: log('Problem getting image.', message_type='error', title='take-photo') self.exit() else: # With USB camera: self.camera_port = 0 image_width = int(WIDTH) image_height = int(HEIGHT) discard_frames = 20 self.camera_check() # check for camera camera = cv2.VideoCapture(self.camera_port) sleep(0.1) try: camera.set(cv2.CAP_PROP_FRAME_WIDTH, image_width) camera.set(cv2.CAP_PROP_FRAME_HEIGHT, image_height) except AttributeError: camera.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, image_width) camera.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, image_height) for _ in range(discard_frames): camera.grab() self.ret, self.image = camera.read() camera.release() if not self.ret: log('Problem getting image.', message_type='error', title='take-photo') self.exit() self.image_captured = True return self.save()
def find_pattern(self, img): """Find calibration pattern circles in single image.""" if img is None: log("ERROR: Calibration failed. Image missing.", message_type='error', title='camera-calibration') self.success_flag = False img = cv2.bitwise_not(img.copy()) ret, centers = cv2.findCirclesGrid(img, self.pattern['size'], flags=self.pattern['type']) if not ret: log("ERROR: Calibration failed, calibration object not " + "detected in image. Check recent photos.", message_type='error', title='camera-calibration') self.success_flag = False return ret, centers
def capture(self): """Take a photo.""" if 'NONE' in CAMERA: log('No camera selected. Choose a camera on the device page.', message_type='error', title='take-photo') sys.exit(0) elif 'RPI' in CAMERA: # With Raspberry Pi Camera: image_filename = self.save(filename_only=True) try: retcode = call([ 'raspistill', '-w', '640', '-h', '480', '-o', image_filename ]) except OSError: log('Raspberry Pi Camera not detected.', message_type='error', title='take-photo') sys.exit(0) else: if retcode == 0: print('Image saved: {}'.format(image_filename)) return image_filename else: log('Problem getting image.', message_type='error', title='take-photo') sys.exit(0) else: # With USB camera: self.camera_port = 0 # image_width = 1600 # image_height = 1200 discard_frames = 20 self.camera_check() # check for camera camera = cv2.VideoCapture(self.camera_port) sleep(0.1) # try: # camera.set(cv2.CAP_PROP_FRAME_WIDTH, image_width) # camera.set(cv2.CAP_PROP_FRAME_HEIGHT, image_height) # except AttributeError: # camera.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, image_width) # camera.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, image_height) for _ in range(discard_frames): camera.grab() self.ret, self.image = camera.read() camera.release() if not self.ret: log('Problem getting image.', message_type='error', title='take-photo') sys.exit(0) self.image_captured = True return self.save()
def _calibration_output(self): # save calibration data if self.args['save'] or self.args['debug']: self.p2c.image.images['current'] = self.p2c.image.images['marked'] self.p2c.image.save('calibration_result') # Print verbose results if self.args['verbose'] and self.args['text_output']: if self.p2c.calibration_params['total_rotation_angle'] != 0: print(" Note: required rotation of " "{:.2f} degrees executed.".format( self.p2c.calibration_params['total_rotation_angle'])) if self.args['debug']: # print number of objects detected self.plant_db.print_count(calibration=True) # print coordinate locations of calibration objects self.p2c.p2c(self.plant_db) self.plant_db.print_coordinates() print('') # Print condensed output if verbose output is not chosen if self.args['text_output'] and not self.args['verbose']: print("Calibration complete. (rotation:{}, scale:{})".format( self.p2c.calibration_params['total_rotation_angle'], self.p2c.calibration_params['coord_scale'])) # Send calibration result log toast if self.args['app']: log( 'Camera calibration complete; setting pixel coordinate scale' ' to {} and camera rotation to {} degrees.'.format( self.p2c.calibration_params['coord_scale'], self.p2c.calibration_params['total_rotation_angle']), 'success', 'Success', ['toast'], True) # Save calibration data if self.args['from_env_var']: # to environment variable self.p2c.save_calibration_data_to_env() elif self.args['from_file']: # to file self.p2c.save_calibration_parameters() else: # to Parameters() instance self.params.calibration_data = self.p2c.calibration_params
def _calibration_output(self, total_rotation_angle): if self.viewoutputimage: self.image.images['current'] = self.image.images['marked'] self.image.show() while abs(total_rotation_angle) > 360: if total_rotation_angle < 0: total_rotation_angle += 360 else: total_rotation_angle -= 360 self.calibration_params['total_rotation_angle'] = round( total_rotation_angle, 2) self.calibration_params['camera_z'] = self.plant_db.coordinates[2] try: self.calibration_params['coord_scale'] # pylint:disable=W0104 failure_flag = False except KeyError: log("ERROR: Calibration failed.", message_type='error', title='camera-calibration') failure_flag = True return failure_flag
def find_pattern(self, img, move_back=False): """Find calibration pattern circles in single image.""" if img is None: log('ERROR: Calibration failed. Image missing.', message_type='error', title='camera-calibration') self.success_flag = False img = cv2.bitwise_not(img.copy()) img = cv2.medianBlur(img, 5) ret, centers = cv2.findCirclesGrid(img, self.pattern['size'], flags=self.pattern['type']) if not ret: if move_back: self.return_to_start() log('ERROR: Calibration failed, calibration object not ' + 'detected in image. Check recent photos.', message_type='error', title='camera-calibration') self.success_flag = False return ret, centers
def capture(self): """Take a photo.""" if CAMERA == 'RPI': # With Raspberry Pi Camera: image_filename = self.save(filename_only=True) try: retcode = call([ "raspistill", "-w", "640", "-h", "480", "-o", image_filename ]) except OSError: log("Raspberry Pi Camera not detected.", message_type='error', title='take-photo') sys.exit(0) else: if retcode == 0: print("Image saved: {}".format(image_filename)) return image_filename else: log("Problem getting image.", message_type='error', title='take-photo') sys.exit(0) else: # With USB camera: self.camera_port = 0 # image_width = 1600 # image_height = 1200 discard_frames = 20 self.camera_check() # check for camera camera = cv2.VideoCapture(self.camera_port) sleep(0.1) # try: # camera.set(cv2.CAP_PROP_FRAME_WIDTH, image_width) # camera.set(cv2.CAP_PROP_FRAME_HEIGHT, image_height) # except AttributeError: # camera.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, image_width) # camera.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, image_height) for _ in range(discard_frames): camera.grab() self.ret, self.image = camera.read() camera.release() if not self.ret: log("Problem getting image.", message_type='error', title='take-photo') sys.exit(0) self.image_captured = True return self.save()
"""Download an image from the Web App and use it to calibrate the camera. download the image corresponding to the ID provided and run calibration """ import os import sys sys.path.insert(1, os.path.join(sys.path[0], '..')) from plant_detection.PlantDetection import PlantDetection from plant_detection import ENV from plant_detection.Log import log if __name__ == "__main__": IMAGE_ID = ENV.load('CAMERA_CALIBRATION_selected_image', get_json=False) if IMAGE_ID is None: log('No image selected.', message_type='error', title='historical-camera-calibration') sys.exit(0) PD = PlantDetection(coordinates=True, app=True, app_image_id=IMAGE_ID) PD.calibrate()