From 0014b92ac3e3d28a32af674738f55acb7e3a8f42 Mon Sep 17 00:00:00 2001 From: Cees Bassa Date: Tue, 25 Feb 2020 23:22:05 +0100 Subject: [PATCH] Boilerplate acquisition program for ASI cameras --- acquire.py | 135 +++++++++++++++++++++++++++++++++++++++++++++ settings.json | 12 ++++ stphot/__init__.py | 0 stphot/io.py | 46 +++++++++++++++ 4 files changed, 193 insertions(+) create mode 100755 acquire.py create mode 100644 settings.json create mode 100644 stphot/__init__.py create mode 100644 stphot/io.py diff --git a/acquire.py b/acquire.py new file mode 100755 index 0000000..e223357 --- /dev/null +++ b/acquire.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +import argparse +import os +import sys +import json +import time +import cv2 +import zwoasi as asi +import numpy as np +from stphot.io import write_fits_file + +if __name__ == "__main__": + # Parse arguments + parser = argparse.ArgumentParser( + description="Capture images with an ASI CMOS camera") + parser.add_argument("-s", "--settings", help="JSON file with settings", default=None) + parser.add_argument("-p", "--path", help="Output path", default=None) + args = parser.parse_args() + + # Check arguments + if args.settings == None or args.path == None: + parser.print_help() + sys.exit() + + # Read settings + try: + with open(args.settings, "r") as fp: + settings = json.load(fp) + except Exception as e: + print(e) + sys.exit(1) + + # Check path + path = os.path.abspath(args.path) + if not os.path.exists(path): + os.makedirs(path) + + # Intialize SDK library + try: + asi.init(os.getenv("ZWO_ASI_LIB")) + except Exception as e: + print(e) + sys.exit(1) + + # Find cameras + ncam = asi.get_num_cameras() + if ncam == 0: + print("No ZWO ASI cameras found") + sys.exit(1) + + # Decode settings + texp_us = 1000 * int(settings["exposure"]) + gain = int(settings["gain"]) + + # Initialize camera 0 + camera = asi.Camera(0) + camera_info = camera.get_camera_property() + + # Set control values + camera.set_control_value(asi.ASI_BANDWIDTHOVERLOAD, int(settings["usb"])) + camera.set_control_value(asi.ASI_EXPOSURE, texp_us, auto=False) + #camera.set_control_value(asi.ASI_AUTO_MAX_EXP, texp_us_max // 1000) + camera.set_control_value(asi.ASI_GAIN, gain, auto=False) + #camera.set_control_value(asi.ASI_AUTO_MAX_GAIN, gain_max) + camera.set_control_value(asi.ASI_WB_B, int(settings["wbb"])) + camera.set_control_value(asi.ASI_WB_R, int(settings["wbr"])) + camera.set_control_value(asi.ASI_GAMMA, int(settings["gamma"])) + camera.set_control_value(asi.ASI_BRIGHTNESS, int(settings["brightness"])) + camera.set_control_value(asi.ASI_FLIP, int(settings["flip"])) + camera.set_control_value(asi.ASI_AUTO_MAX_BRIGHTNESS, 80) + camera.disable_dark_subtract() + camera.set_roi(bins=int(settings["bin"])) + + # Start capture + camera.start_video_capture() + + # Set image format + if int(settings["type"]) == asi.ASI_IMG_RAW8: + camera.set_image_type(asi.ASI_IMG_RAW8) + elif int(settings["type"]) == asi.ASI_IMG_RGB24: + camera.set_image_type(asi.ASI_IMG_RGB24) + elif int(settings["type"]) == asi.ASI_IMG_RAW16: + camera.set_image_type(asi.ASI_IMG_RAW16) + else: + camera.set_image_type(asi.ASI_IMG_RAW8) + + # Forever loop + while True: + # Capture frame + t0 = time.time() + img = camera.capture_video_frame() + + # Get settings + camera_settings = camera.get_control_values() + + # Stability test + if texp_us == camera_settings["Exposure"] and gain == camera_settings["Gain"]: + stable = True + + # Extract settings + texp_us = camera_settings["Exposure"] + texp = float(texp_us) / 1000000 + gain = camera_settings["Gain"] + temp = float(camera_settings["Temperature"]) / 10 + + # Format start time + nfd = "%s.%03d" % (time.strftime("%Y-%m-%dT%T", + time.gmtime(t0)), int((t0 - np.floor(t0)) * 1000)) + + print(nfd, texp, gain, temp) + + # Store FITS file + write_fits_file(os.path.join(path, "%s.fits" % nfd), img, nfd, texp, gain, temp) + + # Get RGB image + if int(settings["type"]) == asi.ASI_IMG_RAW8: + ny, nx = img.shape + rgb_img = cv2.cvtColor(img, cv2.COLOR_BAYER_BG2BGR) + elif int(settings["type"]) == asi.ASI_IMG_RGB24: + ny, nx, nc = img.shape + rgb_img = img + elif int(settings["type"]) == asi.ASI_IMG_RAW16: + ny, nx = img.shape + img_8bit = np.clip((img/256).astype("uint8"), 0, 255) + rgb_img = cv2.cvtColor(img_8bit, cv2.COLOR_BAYER_BG2BGR) + + # Store image + if stable: + cv2.imwrite(os.path.join(path, "%s.jpg" % nfd), rgb_img) + + # Stop capture + camera.stop_video_capture() + + # Close file + fstat.close() diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..472aa1b --- /dev/null +++ b/settings.json @@ -0,0 +1,12 @@ +{ + "exposure":"10000", + "gain":"100", + "gamma":"100", + "brightness":"10", + "wbr":"70", + "wbb":"90", + "bin":"1", + "type":"0", + "usb":"40", + "flip":"0" +} diff --git a/stphot/__init__.py b/stphot/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stphot/io.py b/stphot/io.py new file mode 100644 index 0000000..0327aa4 --- /dev/null +++ b/stphot/io.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import numpy as np +from astropy.time import Time +from astropy.io import fits + +def write_fits_file(fname, img, nfd, texp, gain, temp): + # Extract image shape and reorder in case of RGB24 + if len(img.shape)==3: + ny, nx, nc = img.shape + img = np.moveaxis(img, 2, 0) + elif len(img.shape)==2: + ny, nx = img.shape + + # FITS header + hdr = fits.Header() + hdr['DATE-OBS'] = "%s" % nfd + hdr['MJD-OBS'] = Time(nfd, format="isot").mjd + hdr['EXPTIME'] = texp + hdr['GAIN'] = gain + hdr['TEMP'] = temp + hdr['CRPIX1'] = float(nx) / 2 + hdr['CRPIX2'] = float(ny) / 2 + hdr['CRVAL1'] = 0.0 + hdr['CRVAL2'] = 0.0 + hdr['CD1_1'] = 1.0 / 3600.0 + hdr['CD1_2'] = 0.0 + hdr['CD2_1'] = 0.0 + hdr['CD2_2'] = 1.0 / 3600.0 + hdr['CTYPE1'] = "RA---TAN" + hdr['CTYPE2'] = "DEC--TAN" + hdr['CUNIT1'] = "deg" + hdr['CUNIT2'] = "deg" + hdr['CRRES1'] = 0.0 + hdr['CRRES2'] = 0.0 + hdr['EQUINOX'] = 2000.0 + hdr['RADECSYS'] = "ICRS" + hdr['COSPAR'] = 4171 + hdr['TRACKED'] = 0 + hdr['OBSERVER'] = "Cees Bassa" + + # Write FITS file + hdu = fits.PrimaryHDU(data=img, + header=hdr) + hdu.writeto(fname, overwrite=True) + + return