def read_exif(fname, exiftool='exiftool'): # pragma: no cover """ Read the EXIF information Gets the EXIF information using exiftool Note: Assumes the `exiftool` is installed Args: fname {str} -- Name of file (CR2) to read Keyword Args: exiftool {str} -- Location of exiftool (default: {'/usr/bin/exiftool'}) Returns: dict -- Dictonary of EXIF information """ assert os.path.exists(fname), warn("File does not exist: {}".format(fname)) exif = {} try: # Build the command for this file command = '{} -j {}'.format(exiftool, fname) cmd_list = command.split() # Run the command exif = loads(subprocess.check_output(cmd_list).decode('utf-8')) except subprocess.CalledProcessError as err: raise error.InvalidSystemCommand( msg="File: {} \n err: {}".format(fname, err)) return exif[0]
def cr2_to_pgm(cr2_fname, pgm_fname=None, overwrite=True, *args, **kwargs): # pragma: no cover """ Convert CR2 file to PGM Converts a raw Canon CR2 file to a netpbm PGM file via `dcraw`. Assumes `dcraw` is installed on the system Note: This is a blocking call Arguments: cr2_fname {str} -- Name of CR2 file to convert **kwargs {dict} -- Additional keywords to pass to script Keyword Arguments: pgm_fname {str} -- Name of PGM file to output, if None (default) then use same name as CR2 (default: {None}) dcraw {str} -- Path to installed `dcraw` (default: {'dcraw'}) overwrite {bool} -- A bool indicating if existing PGM should be overwritten (default: {True}) Returns: str -- Filename of PGM that was created """ verbose = kwargs.get('verbose', False) dcraw = shutil.which('dcraw') if dcraw is None: raise error.InvalidCommand('dcraw not found') if pgm_fname is None: pgm_fname = cr2_fname.replace('.cr2', '.pgm') if os.path.exists(pgm_fname) and not overwrite: if verbose: print("PGM file exists, returning existing file: {}".format( pgm_fname)) else: try: # Build the command for this file command = '{} -t 0 -D -4 {}'.format(dcraw, cr2_fname) cmd_list = command.split() if verbose: print("PGM Conversion command: \n {}".format(cmd_list)) # Run the command if subprocess.check_call(cmd_list) == 0: if verbose: print("PGM Conversion command successful") except subprocess.CalledProcessError as err: raise error.InvalidSystemCommand( msg="File: {} \n err: {}".format(cr2_fname, err)) return pgm_fname
def solve_field(fname, timeout=15, solve_opts=None, *args, **kwargs): """ Plate solves an image. Note: This is a low-level wrapper around the underlying `solve-field` program. See `get_solve_field` for more typical usage and examples. Args: fname(str, required): Filename to solve in .fits extension. timeout(int, optional): Timeout for the solve-field command, defaults to 60 seconds. solve_opts(list, optional): List of options for solve-field. """ solve_field_script = shutil.which('solve-field') if solve_field_script is None: # pragma: no cover raise error.InvalidSystemCommand( f"Can't find solve-field, is astrometry.net installed?") # Add the options for solving the field if solve_opts is not None: options = solve_opts else: # Default options options = [ '--guess-scale', '--cpulimit', str(timeout), '--no-verify', '--crpix-center', '--temp-axy', '--index-xyls', 'none', '--solved', 'none', '--match', 'none', '--rdls', 'none', '--corr', 'none', '--downsample', '4', '--no-plots', ] if 'ra' in kwargs: options.append('--ra') options.append(str(kwargs.get('ra'))) if 'dec' in kwargs: options.append('--dec') options.append(str(kwargs.get('dec'))) if 'radius' in kwargs: options.append('--radius') options.append(str(kwargs.get('radius'))) # Gather all the kwargs that start with `--` and are not already present. logger.debug(f'Adding kwargs: {kwargs!r}') def _modify_opt(opt, val): if isinstance(val, bool): opt_string = str(opt) else: opt_string = f'{opt}={val}' return opt_string options.extend([ _modify_opt(opt, val) for opt, val in kwargs.items() if opt.startswith('--') and opt not in options and not isinstance(val, bool) ]) cmd = [solve_field_script] + options + [fname] logger.debug(f'Solving with: {cmd}') try: proc = subprocess.Popen(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except Exception as e: raise error.PanError(f"Problem plate-solving in solve_field: {e!r}") return proc
def make_timelapse( directory, fn_out=None, glob_pattern='20[1-9][0-9]*T[0-9]*.jpg', overwrite=False, timeout=60, verbose=False, **kwargs): """Create a timelapse. A timelapse is created from all the images in a given `directory` Args: directory (str): Directory containing image files fn_out (str, optional): Full path to output file name, if not provided, defaults to `directory` basename. glob_pattern (str, optional): A glob file pattern of images to include, default '20[1-9][0-9]*T[0-9]*.jpg', which corresponds to the observation images but excludes any pointing images. The pattern should be relative to the local directory. overwrite (bool, optional): Overwrite timelapse if exists, default False. timeout (int): Timeout for making movie, default 60 seconds. verbose (bool, optional): Show output, default False. **kwargs (dict): Valid keywords: verbose Returns: str: Name of output file Raises: error.InvalidSystemCommand: Raised if ffmpeg command is not found. FileExistsError: Raised if fn_out already exists and overwrite=False. """ if fn_out is None: head, tail = os.path.split(directory) if tail is '': head, tail = os.path.split(head) field_name = head.split('/')[-2] cam_name = head.split('/')[-1] fname = '{}_{}_{}.mp4'.format(field_name, cam_name, tail) fn_out = os.path.normpath(os.path.join(directory, fname)) if verbose: print("Timelapse file: {}".format(fn_out)) if os.path.exists(fn_out) and not overwrite: raise FileExistsError("Timelapse exists. Set overwrite=True if needed") ffmpeg = shutil.which('ffmpeg') if ffmpeg is None: raise error.InvalidSystemCommand("ffmpeg not found, can't make timelapse") inputs_glob = os.path.join(directory, glob_pattern) try: ffmpeg_cmd = [ ffmpeg, '-r', '3', '-pattern_type', 'glob', '-i', inputs_glob, '-s', 'hd1080', '-vcodec', 'libx264', ] if overwrite: ffmpeg_cmd.append('-y') ffmpeg_cmd.append(fn_out) if verbose: print(ffmpeg_cmd) proc = subprocess.Popen(ffmpeg_cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: # Don't wait forever outs, errs = proc.communicate(timeout=timeout) except subprocess.TimeoutExpired: proc.kill() outs, errs = proc.communicate() finally: if verbose: print(outs) print(errs) # Double-check for file existence if not os.path.exists(fn_out): fn_out = None except Exception as e: warn("Problem creating timelapse in {}: {!r}".format(fn_out, e)) fn_out = None return fn_out
def solve_field(fname, timeout=15, solve_opts=None, **kwargs): """ Plate solves an image. Args: fname(str, required): Filename to solve in .fits extension. timeout(int, optional): Timeout for the solve-field command, defaults to 60 seconds. solve_opts(list, optional): List of options for solve-field. verbose(bool, optional): Show output, defaults to False. """ verbose = kwargs.get('verbose', False) if verbose: print("Entering solve_field") solve_field_script = shutil.which('panoptes-solve-field') if solve_field_script is None: # pragma: no cover raise error.InvalidSystemCommand( "Can't find panoptes-solve-field: {}".format(solve_field_script)) # Add the options for solving the field if solve_opts is not None: options = solve_opts else: options = [ '--guess-scale', '--cpulimit', str(timeout), '--no-verify', '--no-plots', '--crpix-center', '--match', 'none', '--corr', 'none', '--wcs', 'none', '--downsample', '4', ] if kwargs.get('overwrite', False): options.append('--overwrite') if kwargs.get('skip_solved', False): options.append('--skip-solved') if 'ra' in kwargs: options.append('--ra') options.append(str(kwargs.get('ra'))) if 'dec' in kwargs: options.append('--dec') options.append(str(kwargs.get('dec'))) if 'radius' in kwargs: options.append('--radius') options.append(str(kwargs.get('radius'))) if fname.endswith('.fz'): options.append('--extension=1') cmd = [solve_field_script] + options + [fname] if verbose: print("Cmd:", cmd) try: proc = subprocess.Popen(cmd, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError as e: raise error.InvalidCommand( "Can't send command to panoptes-solve-field: {} \t {}".format( e, cmd)) except ValueError as e: raise error.InvalidCommand( "Bad parameters to solve_field: {} \t {}".format(e, cmd)) except Exception as e: raise error.PanError("Timeout on plate solving: {}".format(e)) if verbose: print("Returning proc from solve_field") return proc