"""Utility functions for camtools."""fromconcurrent.futuresimportProcessPoolExecutor,ThreadPoolExecutor,as_completedtry:fromtypingimportModuleType# Python <3.10exceptImportError:fromtypesimportModuleType# Python 3.10+fromtypingimportAny,Callable,Iterable,Union,Optionalfromfunctoolsimportlru_cachefromtqdmimporttqdm
[docs]defmt_loop(func:Callable[[Any],Any],inputs:Iterable[Any],**kwargs,)->list:""" Applies a function to each item in the given list in parallel using multi-threading. Args: func: Callable function that accepts a single argument. inputs: Iterable of inputs to process with the function. **kwargs: Additional keyword arguments to pass to ``func``. Returns: A list of results from applying ``func`` to each item in ``inputs``. """desc=f"[mt] {func.__name__}"withThreadPoolExecutor()asexecutor:future_to_index={executor.submit(func,item,**kwargs):ifori,iteminenumerate(inputs)}results=[None]*len(inputs)forfutureintqdm(as_completed(future_to_index),total=len(inputs),desc=desc):results[future_to_index[future]]=future.result()returnresults
[docs]defmp_loop(func:Callable[[Any],Any],inputs:Iterable[Any],**kwargs,)->list:""" Applies a function to each item in the given list in parallel using multi-processing. Args: func: Callable function that accepts a single argument. inputs: Iterable of inputs to process with the function. **kwargs: Additional keyword arguments to pass to ``func``. Returns: A list of results from applying ``func`` to each item in ``inputs``. """desc=f"[mp] {func.__name__}"withProcessPoolExecutor()asexecutor:future_to_index={executor.submit(func,item,**kwargs):ifori,iteminenumerate(inputs)}results=[None]*len(inputs)forfutureintqdm(as_completed(future_to_index),total=len(inputs),desc=desc):results[future_to_index[future]]=future.result()returnresults
[docs]defquery_yes_no(question:str,default:Optional[bool]=None)->bool:""" Ask a yes/no question via raw_input() and return their answer. Args: question: The question that is presented to the user. default: Presumed answer if the user just hits <Enter>. - True: The answer is assumed to be yes. - False: The answer is assumed to be no. - None: The answer is required from the user. Returns: True for "yes" or False for "no". Examples: .. code-block:: python if query_yes_no("Continue?", default=True): print("Proceeding.") else: print("Aborted.") if not query_yes_no("Continue?", default=True): print("Aborted.") return # Or exit(0) print("Proceeding.") """ifdefaultisNone:prompt="[y/n]"elifdefault==True:prompt="[Y/n]"elifdefault==False:prompt="[y/N]"else:raiseValueError(f"Invalid default answer: '{default}'")response_to_bool={"yes":True,"y":True,"ye":True,"no":False,"n":False,True:True,False:False,}whileTrue:print(f"{question}{prompt} ",end="")choice=input().lower()ifdefaultisnotNoneandchoice=="":returnresponse_to_bool[default]elifchoiceinresponse_to_bool:returnresponse_to_bool[choice]else:print('Please respond with "yes" or "no" (or "y" or "n").')
@lru_cache(maxsize=1)def_safely_import_torch()->Optional[ModuleType]:""" Open3D has an issue where it must be imported before torch. If Open3D is installed, this function will import Open3D before torch. Otherwise, it will return simply import and return torch. Use this function to import torch within camtools to handle the Open3D import order issue. That is, within camtools, we shall avoid ``import torch``, and instead use ``from camtools.backend import torch``. As torch is an optional dependency for camtools, this function will return None if torch is not available. Returns: Optional[ModuleType]: The torch module if available, otherwise None. """try:__import__("open3d")exceptImportError:passtry:_torch=__import__("torch")return_torchexceptImportError:returnNone_safe_torch=_safely_import_torch()