Source code for mesoSPIM.src.utils.utility_functions

'''
Contains a variety of mesoSPIM utility functions
'''
import os
import psutil
import ctypes
import logging
logger = logging.getLogger(__name__)

# Windows API binding
GetCurrentProcessorNumber = ctypes.windll.kernel32.GetCurrentProcessorNumber

[docs] def convert_seconds_to_string(delta_t): ''' Converts an input value in seconds into a string in the format hh:mm:ss Interestingly, a variant using np.divmod is around 4-5x slower in initial tests. ''' if delta_t <= 0: return '--:--:--' else: hours, remainder = divmod(delta_t, 3600) minutes, seconds = divmod(remainder, 60) return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
[docs] def format_data_size(bytes): ''' Converts bytes into human-readable format (kb, MB, GB) ''' try: bytes = float(bytes) kb = bytes / 1024 except Exception as e: print(f"{e}") return None if kb >= 1024: M = kb / 1024 if M >= 1024: G = M / 1024 return "%.1f GB" % (G) else: return "%.1f MB" % (M) else: return "%.1f kb" % (kb)
[docs] def write_line(file, key='', value=''): ''' Little helper method to write a single line with a key and value for metadata Adds a line break at the end. ''' if key != '': file.write('['+str(key)+'] '+str(value) + '\n') else: file.write('\n')
[docs] def gb_size_of_array_shape(shape): '''Given a tuple of array shape, return the size in GB of a uint16 array. Args: shape (tuple[int]): Array dimensions, e.g. ``(100, 2048, 2048)``. Returns: float: Size in gibibytes (GiB). ''' for idx,ii in enumerate(shape): if idx == 0: total = ii else: total *= ii total = total * 16 / 8 return total / 1024**3
[docs] def replace_with_underscores(string): '''Replace spaces, slashes and percent signs with underscores or ASCII equivalents. Used for sanitising file and folder names produced from user inputs. Args: string (str): Raw string, e.g. a filter name like ``"488 nm / 50%"``. Returns: str: Sanitised string safe for use in file paths. ''' s = string.replace(' ', '_').replace('/', '_').replace('%', 'pct') return s
[docs] def log_cpu_core(logger, msg=""): '''Log (at DEBUG level) which logical CPU core the calling thread is currently running on. Useful for verifying thread affinity in the Core / Camera / Writer thread model——each Qt thread should remain pinned to a consistent CPU core. Args: logger (logging.Logger): Logger instance used for the debug message. msg (str): Optional prefix string included in the log message. ''' pid = os.getpid() proc = psutil.Process(pid) #core = proc.cpu_num() # returns the current logical CPU number. Linux only. core = GetCurrentProcessorNumber() # Windows only. logger.debug(f"{msg} running on logical CPU core: {core}")
[docs] def timed(func): '''Decorator to time functions and log the elapsed time''' import time def wrapper(*args, **kwargs): start = time.perf_counter() result = func(*args, **kwargs) elapsed_ms = (time.perf_counter() - start) * 1000 logger.info(f"{func.__name__} took {elapsed_ms:.1f} ms") return result return wrapper