def RunThread(func, args=(), daemon=True, lock=None, wait_lock=False, wait_timeout=0.5):
	'''
	 @brief Run function in separate thread.

	 @param func Function to run.
	 @param args Input arguments (single argument: (arg1,) - or multiple: (arg1, arg2))
	 @param daemon (True/false) - run as daemon or not.
	 @param lock Mutex lock - wait for it to be available before starting thread.
	 @param wait_lock - Wait for mutex to be available, or raise exception immideately.
	 @param wait_timeout - Time to wait in seconds (if wait_lock==True)

	 return t Threading object
	'''
	t = threading.Thread(target=func, args=args)
	t.daemon = daemon
	if lock == None:
		t.start()
	else:
		if wait_lock:
			timeout = timeit.default_timer()
			while True:
				if not(lock.locked()):
					t.start()
					break
				elif (timeit.default_timer() - timeout) > wait_timeout:
					raise Exception('Runtime error - lock not availble')
		else:
			if not(lock.locked()):
				t.start()
			else:
				raise Exception('Runtime error - lock not availble')
	return t def GetTimestamp(fractional=False):
	'''
	 @brief Get string timestamp

	 @param fractional (return timestamp with fractional seconds (default=False))

	 @return string timestamp 
	'''
	str_time = "%Y_%m_%d__%H_%M_%S"
	if fractional:
		timestamp = datetime.utcnow().strftime(str_time + ".%f")
	else:
		timestamp = datetime.utcnow().strftime(str_time)
	return timestamp def GetTimestampedFolder(timestamp, output_folder, sub_output_folder):
	'''
	 @brief Get timestamp folder for this session.

	 @param timestamp String
	 @param output_folder
	 @param sub_output_folder

	 @return output_folder (updated)
	'''
	if len(sub_output_folder) > 0:
		output_folder += sub_output_folder
	output_folder += timestamp + '/'
	return output_folder def CheckDir(directory):
	'''
	 @brief Create directory

	 @param directory Name of directory.
	'''
	if not(os.path.isdir(directory)):
		try:
			os.makedirs(directory)
		except Exception, err:
			warnings.simplefilter('always')
			warnings.warn(str(err), Warning)
			warnings.simplefilter('default') def RemoveDir(directory):
	'''
	 @brief Delete everything reachable from the directory named in 'top',
			assuming there are no symbolic links.
			CAUTION:  This is dangerous!  For example, if top == '/', it
			could delete all your disk files.
	 @param directory
	'''
	if os.path.isdir(directory):
		for root, dirs, files in os.walk(directory, topdown=False):
			for name in files:
				os.remove(os.path.join(root, name))
			for name in dirs:
				os.rmdir(os.path.join(root, name)) class Settings(SettingsConfigured):
	def __init__(self, reset_settings=None, load_initial_settings=True, skip_checking_for_user_input_cmd_line=False, skip_checking_for_user_input=False):
		'''CONSTRUCTOR'''
		SettingsConfigured.__init__(self, load_initial_settings=load_initial_settings)
		if load_initial_settings:
			self.GetInitialSettings(reset_settings=reset_settings, skip_checking_for_user_input_cmd_line=skip_checking_for_user_input_cmd_line, skip_checking_for_user_input=skip_checking_for_user_input)

	def GetStaticSettings(self):
		'''
		 @brief Get static settings
		'''
		return dict(self._static_settings)

	def GetInitialSettings(self, reset_settings=None, settings_filename=None, skip_checking_for_user_input_cmd_line=False, skip_checking_for_user_input=False):
		'''
		 @brief Get initial settings from given json file, or from default values

		 @param reset_settings (True/False (default=None))
		 @param settings_filename (default=None)
		 @param skip_checking_for_user_input_cmd_line (True/False - default=False)
		 @param skip_checking_for_user_input (True/False - default=False)
		'''
		if not(isinstance(reset_settings, bool)):
			reset_settings = self._static_settings['reset_settings']

		settings_changed = False
		if reset_settings or not(self.LoadSettingsFromJson(settings_filename)):
			self.ResetSettings()
			settings_changed = True
		if not(skip_checking_for_user_input_cmd_line):
			if self.CheckForUserInputSettingsFromTerminal():
				settings_changed = True
		if settings_changed:
			self.SaveSettingsToJson(settings_filename)
		if not(skip_checking_for_user_input):
			self.CheckForUserInputSettings()

	def GetDefaultSettingsFilename(self):
		'''
		 @brief Get default save settings filename

		 @return filename
		'''
		CheckDir(self._static_settings['settings_params_folder'])
		filename = self._static_settings['settings_params_folder'] + self._static_settings['settings_params_fname']
		return filename

	def SaveSettingsToJson(self, filename=None):
		'''
		 @brief Save settings to json file

		 @param filename (Without .json file ending)
		'''
		if not(isinstance(filename, str)):
			filename = self.GetDefaultSettingsFilename()
		filename += '.json'
		with open(filename, 'w') as f:
			try:
				json.dump(self._settings, f, indent=4, sort_keys=True)
			except:
				print 'JSON dump failed: ', self._settings
				raise

	def LoadSettingsFromJson(self, filename=None):
		'''
		 @brief Load settings from json file

		 @param filename (Without .json file ending)

		 @return True/False (loaded successfully)
		'''
		if not(isinstance(filename, str)):
			filename = self.GetDefaultSettingsFilename()
		success = False
		filename += '.json'
		if os.path.isfile(filename):
			self.ResetSettings()
			old_settings = dict(self._settings) # Need to convert json converted value types to correct value types
			with open(filename, 'r') as f:
				try:
					self._settings = json.load(f)
					success = True
				except Exception, err:
					warnings.simplefilter('always')
					warnings.warn(str(err), Warning)
					warnings.simplefilter('default')
			if success:
				for main_key in self._settings:
					for sub_key in self._settings[main_key]:
						if isinstance(old_settings[main_key][sub_key], type(None)) and isinstance(self._settings[main_key][sub_key], unicode): # Some string settings may be set to None
							self._settings[main_key][sub_key] = str(self._settings[main_key][sub_key])
						elif not(isinstance(old_settings[main_key][sub_key], type(None))) and not(isinstance(self._settings[main_key][sub_key], type(None))):
							self._settings[main_key][sub_key] = type(old_settings[main_key][sub_key])(self._settings[main_key][sub_key])
		return success

	def ResetSettings(self, new_settings=None):
		'''
		 @brief Reset the settings. Local initial settings are used if no new settings are given.

		 @param new_settings (Dictionary (default=None))
		'''
		if isinstance(new_settings, dict):
			self._settings = new_settings
		else:
			self.SetInitialSettings()

	def ChangeSetting(self, header, setting, value):
		'''
		 @brief Safely change a setting.
		 	- header 	= Main setting header (f.ex 'BASIC')
		 	- setting 	= setting type (f.ex 'reset_calibration' in 'BASIC')
		 	- value 	= new setting value (Be sure to give correct value type, since this is not assterted.)
		 	Raises ValueError if header or setting is not present in the settings dictionary.
		 @param header
		 @param setting
		 @param value
		'''
		if setting == None:
			raise ValueError('Setting {0} is not a part of the settings[{1}]!'.format(setting, header))
		self.AssertValidSetting(header, setting)
		if not(isinstance(value, type(self._settings[header][setting]))):
			raise ValueError('New setting value is not of the same type as the old one: Old type = {0}, new type = {1}, from setting[{2}][{3}]'.format(type(self._settings[header][setting]), type(value), header, setting))
		self._settings[header][setting] = value

	def GetRawSettings(self):
		'''
		 @brief Get raw settings dictionary

		 @return settings
		'''
		return self._settings

	def GetSettings(self, header=None, setting=None):
		'''
		 @brief Safely get settings.
		 	- header 	= Main setting header (f.ex 'BASIC') - If None, then new settings instance of all settings are returned (default=None).
		 	- setting 	= setting type (f.ex 'reset_calibration' in 'BASIC') - If None, then new settings instance of main setting header is returned (default=None).
		 	Raises ValueError if header or setting is not present in the settings dictionary.
		 @param header
		 @param setting

		 @return settings_val
		'''
		if header == None:
			settings_val = Settings(load_initial_settings=False)
			settings_val.ResetSettings(self._settings)
		else:
			self.AssertValidSetting(header, setting)
			if setting == None:
				if isinstance(self._settings[header], dict):
					settings_val = Settings(load_initial_settings=False)
					settings_val.ResetSettings(self._settings[header])
				else:
					settings_val = self._settings[header]
			else:
				settings_val = self._settings[header][setting]
		return settings_val

	def AssertValidSetting(self, header, setting=None):
		'''
		 @brief Assert valid setting request.
		 	- header 	= Main setting header (f.ex 'BASIC')
		 	- setting 	= setting type (f.ex 'reset_calibration' in 'BASIC') - If None, then it is not checked.
		 	Raises ValueError if header or setting is not present in the settings dictionary.

		  @param header
		  @param setting
		  @param value
		'''
		if not(header in self._settings):
			raise ValueError('Header {0} is not a part of the settings!'.format(header))
		if not(setting == None):
			if not(setting in self._settings[header]):
				raise ValueError('Setting {0} is not a part of the settings[{1}]!'.format(setting, header))

	def CheckForUserInputSettings(self):
		'''
		 @brief Check for settings user input at bootup
		'''
		checkdatabaseKeys 	= ['username', 'password', 'database', 'output_folder', 'sub_output_folder']
		database_key 		= 'DATABASE'
		for key in checkdatabaseKeys:
			if self._settings[database_key][key] == None:
				self._settings[database_key][key] = self.UserSettingsInput(database_key, key)

	def UserSettingsInput(self, settings_key, key):
		'''
		 @brief Get user input to set key in settings

		 @param key

		 @return setting
		'''
		user_text = 'Enter ' + settings_key + ' ' + key + ': '
		if key == 'password':
			setting = getpass(user_text)
		else:
			setting = raw_input(user_text)
		return setting

	def CheckForUserInputSettingsFromTerminal(self):
		'''
		 @brief Check for user input settings directly from command line.
		 	User may change a setting by adding following in the terminal command:
		 		-SETTING_TYPE 			(f.ex -BASIC)
		 		--SETTING 				(f.ex --source_type)
		 		---NEW_SETTING_VALUE	(f.ex ---CAMERA)
		 	Example: -BASIC --source_type ---CAMERA
		 	Note: empty NEW_SETTING_VALUE results on asking user for a python value to set, f.ex: -BASIC --source_type ---
		 	Note: the inputs are forcely uppercased for header and lowercased for setting. For comparison, so '-basic' == 'BASIC' and '--SOURCE_TYPE' == 'source_type'.
		
		 @return True/False (True if settings changed, False if not)
		'''
		new_setting_cmd 	= '-'
		settings_changed 	= False
		i = 0
		while i < len(sys.argv):
			if sys.argv[i].count(new_setting_cmd) == 1 and (i+1 < len(sys.argv)):
				header = sys.argv[i][1:].upper()
				i += 1
				if sys.argv[i].count(new_setting_cmd) == 2:
					setting 	= sys.argv[i][2:].lower()
					old_value 	= self.GetSettings(header, setting)
					if type(old_value) == dict or type(old_value) == list or type(old_value) == tuple:
						print "# Old settings type was of {0}, and cannot be set from the command line.".format(type(old_value))
						if self.AskUserToConfigureSetting(header, setting):
							settings_changed = True
					elif i+1 < len(sys.argv):
						i += 1
						if sys.argv[i].count(new_setting_cmd) == 3:
							new_value = sys.argv[i][3:]
							if type(old_value) == bool:
								if new_value.lower() == 'true' or new_value.lower() == '1':
									new_value = True
								elif new_value.lower() == 'false' or new_value.lower() == '0':
									new_value = False
								else:
									warnings.simplefilter('always')
									warnings.warn("Bool setting from [{0}][{1}] are given as: '---false' || '---0' <-> '---true' || '---1' (upper/lower or mixed cased). You typed this value: {2}".format(header, setting, new_value), Warning)
									warnings.simplefilter('default')
									if self.AskUserToConfigureSetting(header, setting):
										settings_changed = True
							else:
								new_value = type(old_value)(new_value)
							try:
								self.ChangeSetting(header, setting, new_value)
								settings_changed = True
								print '#------------- Changed setting[{0}][{1}] from {2} -> {3} -------------#'.format(header, setting, old_value, new_value)
							except Exception, err:
								warnings.simplefilter('always')
								warnings.warn(str(err), Warning)
								warnings.simplefilter('default')
								if self.AskUserToConfigureSetting(header, setting):
									settings_changed = True
						else:
							if self.AskUserToConfigureSetting(header, setting):
								settings_changed = True
							i -= 1
					else:
						if self.AskUserToConfigureSetting(header, setting):
							settings_changed = True
			i += 1
		return settings_changed

	def PrintSettingsInfo(self, configure_settings=False, ask_user_for_settings_parameters=False):
		'''
		 @brief Print settings information and configure settings if specified.

		 @param configure_settings (Default=False)
		 @param ask_user_for_settings_parameters (default=False)
		'''
		settings_info 		= self.GetSettingsInfo()
		settings_changed 	= False
		if configure_settings:
			print "#------------------------------------------ CONFIGURE SETTINGS ------------------------------------------#"
		else:
			print "#----------------------------------------- SETTINGS INFORMATION -----------------------------------------#"
		for main_settings_key in settings_info:
			print "# Settings type: {0}".format(main_settings_key)
			for sub_settings_key in settings_info[main_settings_key]:
				print "# \t - Setting: {0} \t\t\t - {1}".format(sub_settings_key, settings_info[main_settings_key][sub_settings_key])
				if configure_settings:
					if self.AskUserToConfigureSetting(main_settings_key, sub_settings_key, n_tabs=2):
						settings_changed = True
		print "#------------------------------------- STATIC SETTINGS INFORMATION --------------------------------------#"
		static_settings_info = self.GetStaticSettingsInfo()
		print "# Following settings are static and may only be configured in the SetttingsConfigured unit."
		for static_settings_key in static_settings_info:
			print "# \t - Static setting: {0} \t\t\t - {1}".format(static_settings_key, static_settings_info[static_settings_key])
		print "#--------------------------------------------------------------------------------------------------------#"
		if configure_settings and settings_changed:
			CheckDir(self._static_settings['settings_params_folder'])
			settings_filename = self._static_settings['settings_params_folder'] + self._static_settings['settings_params_fname']
			self.SaveSettingsToJson(settings_filename)
		if ask_user_for_settings_parameters:
			answer = raw_input("# Would you like to see the settings parameters? [Y/n]: ")
			if answer.upper() == 'Y':
				self.PrintSettings()

	def PrintSettings(self):
		'''
		 @brief Print settings
		'''
		print "#----------------------------------------- SETTINGS PARAMETERS ------------------------------------------#"
		for main_settings_key in self._settings:
			print "# Settings type: {0}".format(main_settings_key)
			for sub_settings_key in self._settings[main_settings_key]:
				print "# \t - Setting: {0} \t\t\t - {1}".format(sub_settings_key, self._settings[main_settings_key][sub_settings_key])
		print "#--------------------------------------------------------------------------------------------------------#"

	def AskUserToConfigureSetting(self, main_settings_key, sub_settings_key, n_tabs=0, skip_asking_user_to_configure=False):
		'''
		 @brief Ask user to configure a specific setting.

		 @param main_settings_key
		 @param sub_settings_key
		 @param n_tabs (default=0)
		 @param skip_asking_user_to_configure (True/False - default=False)

		 @return True/False (True if settings changed, False if not)
		'''
		settings_changed 	= False
		tabs 				= '\t'*n_tabs
		answer 				= 'Y'
		if not(skip_asking_user_to_configure):
			answer = raw_input("#{0} Would you like to configure setting[{1}][{2}]? [Y/n]: ".format(tabs, main_settings_key, sub_settings_key))
		if answer.upper() == 'Y':
			settings_type 	= type(self._settings[main_settings_key][sub_settings_key])
			fail_msg 		= "Please enter a valid python value of type {0}".format(settings_type)
			configured 		= False
			while not(configured):
				try:
					new_setting = input("#{0} Please enter new setting of {1} as python value: ".format(tabs, settings_type))
				except:
					print "#{0} InputError: ".format(tabs) + fail_msg
					continue
				if settings_type != type(new_setting) and not(settings_type == type(None) and isinstance(new_setting, str)) and not(settings_type == str and isinstance(new_setting, type(None))):
					print "#{0} ValueError: ".format(tabs) + fail_msg
				else:
					old_setting = self._settings[main_settings_key][sub_settings_key]
					self._settings[main_settings_key][sub_settings_key] = new_setting
					print "#{0} Changed setting[{1}][{2}] from {3} -> {4}".format(tabs, main_settings_key, sub_settings_key, old_setting, new_setting)
					configured = True
					settings_changed = True
		return settings_changed class DroneVisionError(Exception):
	def __init__(self, error_key=None):
		'''CONSTRUCTOR'''

		self.__error_messages = {	'no_blobs_error_msg'								: 'No blobs detected!', \
									'blob_distance_is_not_calibrated_error_msg'			: 'Blob distance is not calibrated!', \
									'feature_descriptor_not_available_error_msg'		: 'Feature descriptor not avaiable - Use block matching!', \
									'find_line_limits_unexpected_angle'					: 'Unexpected angle: provide vertical or horizontal hough lines only',  \
									'error_msg_no_heading_angle_detected' 				: 'No heading angles detected', \
									'error_msg_no_backup_heading_available'				: 'No edge heading detected, and no backup heading available.', \
									'find_line_limits_no_hor_or_vert_found' 			: 'Only vertical or horizontal lines found - need both horizontal and vertical lines to process.', \
									'detect_boundary_edge_not_found_all_edge_lines'		: 'Could not compute all edges lines - need all boundary edge lines to process', \
									'no_3D_point_matches'								: 'Could not compute 3D points - no matching points.', \
									'could_not_get_point_list_from_slave'				: 'Timeout thread - could not get right point list from slave within timeout!', \
									'triangulation_error_msg'							: 'Triangulation failed!', \
									'3D_point_filtration_failed'						: 'Filtration of 3D points failed!'}

		self.msg = None
		if error_key != None:
			self.msg = self.__error_messages[error_key]

	def __str__(self):
		return repr(self.msg)

	def GetErrorMessage(self, error_key):
		'''
		 @brief Return all possible error messages
		'''
		return self.__error_messages[error_key] class PtGreyError(Exception):
	def __init__(self, error_key=None):
		'''CONSTRUCTOR'''
		self.__error_messages = {	'import_fc2_error_msg'					: 'flycapture2 library is not installed - see HOWTO.txt to install.', \
									'camera_not_connected_error_msg'		: 'Camera is not connected!', \
									'camera_not_capturing_error_msg'		: 'Camera has not been started capturing frames!', \
									'timeout_capturing_frame_error_msg'		: 'Timeout capturing frame!', \
									'wiringpi_not_available_error_msg' 		: 'wiringPi2 is not awailable, cannot use pin control.', \
									'failed_capturing_frame_error_msg' 		: 'Failed capturing frame from PtGray (API error)'}

		self.msg = None
		if error_key != None:
			self.msg = self.__error_messages[error_key]

	def __str__(self):
		return repr(self.msg)

	def GetErrorMessage(self, error_key):
		'''
		 @brief Return all possible error messages
		'''
		return self.__error_messages[error_key] class PyQtImage():
	def __init__(self, initialize=True, title='Real-time Plot'):
		'''CONSTRUCTOR'''
		self.__win_open 		= False
		self.__flag_reset 		= False
		self.__title 			= title
		self.__n_plotted_frames = 0
		if initialize:
			self.InitPQImage(title=title)

	def InitPQImage(self, title='Real-time Plot'):
		'''
		 @brief Initialize PyQtImage

		 @param title
		'''
		if not(self.__win_open):
			import pyqtgraph as pg
			self.__pg 		= pg
			self.__title 	= title

			self.__win 		= self.__pg.GraphicsWindow(title=self.__title)
			self.__win.resize(1000,600)

			self.__img_items 	= {}
			self.__view_boxes 	= {}
			self.__win_open 	= True

	def AssertPQImageInititalized(self):
		'''
		 @brief Assert that the PQ image plot is initialized
		'''
		if not(self.__win_open):
			raise Exception('PqtGraph is not initialized. Run "InitPQImage"')

	def ResetPQImagePlot(self):
		'''
		 @brief Reset pqt plot
		'''
		if self.__win_open:
			self.__win.clear()
			self.__img_items 	= {}
			self.__view_boxes 	= {}
			self.TriggProcessEvents()
			self.__flag_reset = True

	def CheckFlagReset(self):
		'''
		 @brief Check flag reset for triggering a second time since pyqthgraph has some issues with resetting

		 @return True/False
		'''
		flag_reset = self.__flag_reset
		self.__flag_reset = False
		return flag_reset

	def AddPQImagePlot(self, key_tag):
		'''
		 @brief Add image plot
		
		 @param key_tag
		'''
		self.AssertPQImageInititalized()
		self.__img_items[key_tag] 	= self.__pg.ImageItem(name=key_tag)
		self.__view_boxes[key_tag] 	= self.__pg.ViewBox(name=key_tag)
		self.__view_boxes[key_tag] 	= self.__win.addViewBox()
		self.__view_boxes[key_tag].addItem(item=self.__img_items[key_tag])

	def UpdatePQImage(self, frame, key_tag, process_events=True, rotate=True, autoDownsample=True):
		'''
		 @brief Plot image in real-time

		 @param frame
		 @param key_tag (also frame title)
		 @param process_events (True/False for processing new events (plotting new data))
		 @param rotate (True/False for rotating the image so that it is shown properly. (default=True))
		 @param autoDownsample (True/False (default=True))
		'''
		if not(self.__win_open):
			self.InitPQImage(title=self.__title)
		self.AssertPQImageInititalized()
		if not(key_tag in self.__img_items):
			self.AddPQImagePlot(key_tag)
		if rotate:
			width, height = GetShape(frame)
			M = cv2.getRotationMatrix2D((height/2, width/2), -90, 1)
			self.__img_items[key_tag].setImage(cv2.warpAffine(frame, M, (height,width)), autoDownsample=autoDownsample)
		else:
			self.__img_items[key_tag].setImage(frame, autoDownsample=autoDownsample)
		if process_events:
			self.TriggProcessEvents()
			if self.CheckFlagReset():
				self.UpdatePQImage(frame, key_tag, process_events, rotate, autoDownsample)

	def UpdatePQImages(self, touple_frames, rotate=True):
		'''
		 @brief Update a set of frames
		 
	 	 @param touple_frames Touple of frames - example: mult_frames = [('img_name1', frame1), ('img_name2', frame2)]
	 	 @param rotate (True/False for rotating the image so that it is shown properly. (default=True))
		'''
		if not(self.__win_open):
			self.InitPQImage(title=self.__title)
		for touple_frame in touple_frames:
			self.UpdatePQImage(touple_frame[1], touple_frame[0], process_events=False, rotate=rotate)
		self.TriggProcessEvents()
		if self.CheckFlagReset():
			self.UpdatePQImages(touple_frames, rotate)

	def TriggProcessEvents(self):
		'''
		 @brief Trigg new processing of events.
		'''
		self.__pg.QtGui.QApplication.processEvents()

	def ClosePQWindow(self):
		'''
		 @brief Close and end this window
		'''
		if self.__win_open:
			self.__win.close()
			self.__win_open = False

	def __call__(self, touple_frames=[], rotate=True, reset=False):
		'''
		 @brief Update a set of frames
		 
	 	 @param touple_frames Touple of frames - example: mult_frames = [('img_name1', frame1), ('img_name2', frame2)]
	 	 @param rotate (True/False for rotating the image so that it is shown properly. (default=True))
	 	 @param reset (Reset pygt plot. Input touple frames are ignored. (default=False))
		'''
		if reset or (len(touple_frames) != self.__n_plotted_frames and self.__n_plotted_frames > 0):
			self.ResetPQImagePlot()
		if len(touple_frames) > 0:
			self.__n_plotted_frames = len(touple_frames)
			self.UpdatePQImages(touple_frames, rotate)

   	def __del__(self):
   		'''DESTRUCTOR'''
   		self.ClosePQWindow() class RealTimePlot():
	def __init__(self, touple_frames=[], main_title='', fig_number=0, interactive_mode=True, savefig_folder='matplot_figs/', default_n_cols=2, save_fig=False, save_fig_only=False, inlude_main_title_in_plot=True):
		'''CONSTRUCTOR'''
		import matplotlib.pyplot as plt
		self.__plt 							= plt
		self.__interactive_mode 			= interactive_mode
		self.__fig_number 					= fig_number
		self.__main_title 					= main_title
		self.__savefig_folder 				= savefig_folder
		self.__default_n_cols 				= default_n_cols
		self.__save_fig 					= save_fig
		self.__save_fig_only 				= save_fig_only
		self.__inlude_main_title_in_plot 	= inlude_main_title_in_plot
		self.__n_plotted_frames 			= 0
		self.__n_plot 						= 0
		if self.__interactive_mode and CheckDisplayAvailable():
			self.__plt.ion()
		self.__call__(touple_frames, main_title)

	def SetSaveFigFolder(self, savefig_folder):
		'''
		 @brief Set save fig folder
		'''
		self.__savefig_folder = savefig_folder

	def __call__(self, touple_frames=[], main_title=None, reset=False):
		'''
		 @brief Call function

		 @param touple_frames
		 @param main_title
		 @param reset (default=False)
		 @param empty_plot Set an empty default plot if the plot were reset (default=True).
		'''
		#self.__plt.gcf().clear()
		if reset or (len(touple_frames) != self.__n_plotted_frames and self.__n_plotted_frames > 0):
			self.__n_plot = 0
			self.__plt.figure(self.__fig_number).clf()
		if main_title == None:
			main_title = self.__main_title
		main_title_n = main_title
		if not(CheckDisplayAvailable()):
			main_title_n += '_'+str(self.__n_plot)
		if len(touple_frames) > 0:
			self.__n_plotted_frames = len(touple_frames)
			MatplotShow(touple_frames, main_title_n, self.__fig_number, self.__interactive_mode, self.__plt, self.__savefig_folder+main_title+'/', self.__default_n_cols, self.__save_fig, self.__save_fig_only, self.__inlude_main_title_in_plot)
			self.__n_plot += 1

	def __del__(self):
		'''DESTRUCTOR'''
		self.__plt.close('all')
		self.__plt.ioff() def WriteImage(frame, image_output_filename):
	'''
	 @brief Write the frame with the output filename using .tif format

	 @param frame Frame to write
	 @param image_output_filename Image filename
	'''
	if not(frame.dtype == np.uint8 or frame.dtype == np.uint16):
		frame = frame.astype(np.uint16)
	cv2.imwrite(image_output_filename+'.tif', frame) def GetImage(image_filename, gray=True):
	'''
	 @brief Get the frame from the input image filename.

	 @param image_filename Image filename
	 @param gray Return image as grayscale (default = True), or 'as is' (gray = False).

	 @return frame (grayscale)
	'''
	if not(os.path.isfile(image_filename)):
		raise Exception('Image file does not exist: {0}'.format(image_filename))
	if gray:
		frame = cv2.imread(image_filename, cv2.IMREAD_GRAYSCALE)
	else:
		frame = cv2.imread(image_filename)
	if not(isinstance(frame, np.ndarray)):
		raise ValueError('Frame was not loaded properly!')
	return frame def CheckDisplayAvailable(linux_embedded_name='posix'):
	'''
	 @brief Check if display is available

	 @param linux_embedded_name (default='posix')

	 @return True/False
	'''
	available = True
	if os.name == linux_embedded_name:
		if os.environ.get('DISPLAY') is None:
			available = False
	return available def GetShape(frame):
	'''
	 @brief Get shape of frame, regardless of depth dimension.

	 @param frame 
	 
	 return (height, width)
	'''
	return frame.shape[:2] class UserInput():
    def __init__(self, input_settings):
        '''CONSTRUCTOR'''
        self.__terminate            = False
        self.__capture_img          = False
        self.__getch_lock           = threading.Lock()
        self.__getch                = Getch() # Get new user input by calling self.__getch()
        self.__action_keys          = {  input_settings.GetSettings('key_terminate'): 'terminate'}
        self.__manual_user_input    = True
        self.__auto                 = input_settings.GetSettings('automatic_mode')
        self.__no_termination       = input_settings.GetSettings('no_termination')

    def GetYesNoFromUser(self, question=''):
        '''
         @brief ask user for a yes / no answer

         @param question (string)

         @return True/False (yes/no)
        '''
        answer      = False
        question    += ' [Y/n]: '
        reply = raw_input(question)
        if reply.upper() == 'Y':
            answer = True
        return answer

    def GetActionKeys(self):
        '''
         @brief Get action keys (dictionary)

         @return action_keys
        '''
        return self.__action_keys

    def GetInput(self):
        '''
         @brief Get user input
            Returns user input as char.
            Returns None if no pressed key was waiting to be read.

         @return char
        '''
        return self.__getch()

    def ForceTermination(self):
        '''
         @brief Force termination
        '''
        self.__terminate = True

    def ResetTermination(self):
        '''
         @brief Reset the force termination key
        '''
        self.__terminate = False

    def CheckTerminated(self):
        '''
         @brief Check if user input is terminated

         @return True/False
        '''
        if self.__manual_user_input:
            self.ManualHandleUserInput()
        return self.__terminate

    def StartAutoHandleUserInput(self):
        '''
         @brief Handle auto user input in thread
        '''
        if self.__auto:
            self.__manual_user_input = False
            if not(self.__no_termination):
                RunThread(self.AutoHandleUserInput)

    def PauseUserInput(self):
        '''
         @brief Pause user input thread
        '''
        while self.__getch_lock.locked():
            pass
        self.__getch_lock.acquire()

    def RestartUserInput(self):
        '''
         @brief Restart user input after invoking a pause
        '''
        if self.__getch_lock.locked():
            self.__getch_lock.release()

    def HandleActionKey(self, action_key):
        '''
         @brief Handle action key

         @param action_key
        '''
        ######## HANDLE ACTION KEYS ##########
        if action_key == 'terminate':
            self.ForceTermination()
        ######################################

    def PrintActionKeys(self):
        '''
         @brief Print action key possibilites
        '''
        print '\nPossible action keys ("action_key" -> action):'
        for key in self.__action_keys:
            print '\t"{0}" -> {1}'.format(key, self.__action_keys[key])
        print '..or just enter to continue'

    def AutoHandleUserInput(self, print_keys=False):
        '''
         @brief Handle user input automatically.

         @param print_keys (Default=False)
        '''
        self.__terminate = False
        while not(self.CheckTerminated()):
            with self.__getch_lock:
                ch = self.GetInput()
            if not(ch == None):
                if print_keys:
                    print ch
                if ch in self.__action_keys:
                    action_key = self.__action_keys[ch]
                    self.HandleActionKey(action_key)

    def ManualHandleUserInput(self, print_keys=False):
        '''
         @brief Handle user input manually.

         @param print_keys (Default=False)
        '''
        self.__terminate = False
        self.PrintActionKeys()
        ch = getpass('Type any of the action keys and enter to continue..')
        if print_keys:
            print ch
        if ch in self.__action_keys:
            action_key = self.__action_keys[ch]
            self.HandleActionKey(action_key)
                    

    def __del__(self):
        '''DESTRUCTOR'''
        self.ForceTermination()
import numpy as np
import timeit, time, warnings
from getpass import getpass
from src.bin.tools import RunThread, GetTimestamp, GetTimestampedFolder, CheckDir, RemoveDir
from Settings.Settings import Settings
from Settings.Exceptions import DroneVisionError, PtGreyError
from src.DroneVision.DroneVision_src.hardware.PyQtImage import PyQtImage
from src.DroneVision.DroneVision_src.hardware.imageTools import RealTimePlot, WriteImage, GetImage, CheckDisplayAvailable
from src.DroneVision.DroneVision_src.imgProcessing.frameTools.frameTools import GetShape
from DataBase.DataBase import DataBase
from MasterSlave.Master import Master
from DroneVision.DroneVision import DroneVision
from src.bin.UserInput.UserInput import UserInput

			if not(print_3D_points) or stereo_error != None or points_error != None:
				points3D = []
			self.RecordData(record_frames=store_frames, insert_to_database=store_to_db, print_progress=True, points3D=points3D)
			#----------------------------------------#

			#-------- SHOW RESULTS REALTIME ---------#
			if self.GetSettings('REAL_TIME_PLOT', 'real_time_plot_on'):
				if not(isinstance(points_error, DroneVisionError)):
					plot_frames = []
					if draw_heading:
						plot_frames.append(('heading', frame_un_l))
					if draw_matches and not(isinstance(stereo_error, DroneVisionError)):
						plot_frames.append(('matches', matches_frame))
					if draw_hough_lines and not(isinstance(boundary_error, DroneVisionError)):
						plot_frames.append(('hough_lines', hough_frame))
					if draw_detected_points:
						plot_frames.append(('points', delta_frame_l))
					self.__realTimePlot(plot_frames)
			#----------------------------------------#

			#---- DELETE FRAMES TO FREE MEMORY ----#
			del frame_un_l
			del delta_frame_l
			del matches_frame
			#----------------------------------------#

			##########################################

		self.ForceTermination()
		self.PrintFinished()
		self.RequestStop()
		self.SendFinishRequest()
		self.CloseMaster()

	def SendFinishRequest(self):
		'''
		 @brief Send finish request from master to slave
		'''
		if self.GetSettings('REAL_TIME_PLOT', 'real_time_plot_on'):
			if (self.GetSettings('REAL_TIME_PLOT', 'use_matplotlib') and self.GetSettings('REAL_TIME_PLOT', 'use_interactive_mode')) or not(self.GetSettings('REAL_TIME_PLOT', 'use_matplotlib')):
				getpass('Hit enter to terminate..')
		self.RequestDisconnect()

	def CheckRunMasterCalibration(self, calibrate_stereopsis=False, calibrate_blob_scale_detector=False):
		'''
		 @brief Check if a new calibration session should be started.

		 @param calibrate_stereopsis (Set calibrate_stereopsis to True for starting a new stereopsis calibration session (default=False))
		 @param calibrate_blob_scale_detector (Set calibrate_blob_scale_detector to True for starting a new blob scale detector calibration session (default=False))
		'''
		n_stereo_saved_calib_frame_sets = 0
		n_blob_saved_calib_frame_sets 	= 0
		if calibrate_stereopsis:
			calib_folder = self.GetSettings('CALIB', 'calib_img_folder_left_cam')
			n_stereo_saved_calib_frame_sets += self.RunMasterCalibration(calib_folder, True)
		if calibrate_blob_scale_detector:
			calib_folder = self.GetSettings('BLOB_SCALE', 'scale_calib_folder')
			n_blob_saved_calib_frame_sets += self.RunMasterCalibration(calib_folder, False)
		if (calibrate_stereopsis or calibrate_blob_scale_detector) and not(self.GetSettings('BASIC', 'reset_calibration')):
			self.__force_stereo_vision_calibration = self.GetYesNoFromUser('Force new stereopsis calibration with new calibration samples?')
			self.__force_blob_calibration = self.GetYesNoFromUser('Force new blob calibration with new calibration samples?')
			self.SendFlagToSlave(self.__force_stereo_vision_calibration)
			self.SendFlagToSlave(self.__force_blob_calibration)
		if self.GetSettings('REAL_TIME_PLOT', 'real_time_plot_on'):
			self.__realTimePlot(reset=True)

	def RunMasterCalibration(self, calib_folder, stereo_or_blob_calib, calib_filename='calib_frame'):
		'''
		 @brief Run master calibration.
		 	Manually trig new images of a chessboard to calibrate the stereopsis system.

		 @param calib_folder (folder where the captured calibration frames are stored)
		 @param stereo_or_blob_calib (True for stereopsis calibration, False for blob scale calibration)
		 @param calib_filename (Basic filename of save frames)

		 @return n_saved_calib_frame_sets
		'''
		first_image_set = True
		if self.GetSettings('REAL_TIME_PLOT', 'real_time_plot_on'):
			self.__realTimePlot(reset=True)
		self.PrintCalibrationSession(calib_folder, stereo_or_blob_calib, calib_filename)
		if stereo_or_blob_calib:
			get_normal_frame_only = True
		else:
			normal_folder 	= calib_folder + 'normal/'
			sl_folder 		= calib_folder + 'sl/'
			get_normal_frame_only = False # blob calibration demands sl frames
		frame_n = 0
		while not(self.CheckFinished()):
			self.SendFlagToSlave(True) # Send flag to slave commanding it to continue
			self.RequestFrameProcessingOnSlave() # Trigger slave to wait for new frame capturing.
			try:
				frame, sl_frame = self.GetRawFrames(get_normal_frame_only=get_normal_frame_only)
			except PtGreyError, err:
				warnings.simplefilter('always')
				warnings.warn(str(err), Warning)
				warnings.simplefilter('default')
				self.RestartCamera()
				self.RequestRestartPtGrey()
				continue
			self.WaitSlaveReady()

			#-------- SHOW RESULTS REALTIME ---------#
			if self.GetSettings('REAL_TIME_PLOT', 'real_time_plot_on'):
				if get_normal_frame_only:
					title 		= 'stereopsis_calibration_session'
					plot_frames = [('frame', frame)]
				else:
					title 		= 'blob_calibration_session'
					plot_frames = [('frame', frame), ('sl_frame', sl_frame)]
				self.__realTimePlot(plot_frames, title)
			#----------------------------------------#

			answer = self.GetYesNoFromUser('Store images to calibration folder?')
			self.SendFlagToSlave(answer)
			if answer:
				if first_image_set:
					first_image_set = False
					if get_normal_frame_only:

						RemoveDir(calib_folder)