Source code for pyplis.custom_image_import

# -*- coding: utf-8 -*-
#
# Pyplis is a Python library for the analysis of UV SO2 camera data
# Copyright (C) 2017 Jonas Gliss (jonasgliss@gmail.com)
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License a
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Custom image load methods for different camera standards.

.. note::

  This file can be used to include cusotmised image import method. Please
  re-install pyplis after defining your customised import method here.
  The method requires the following input / output:

    1. Input: ``str``, file_path ->  full file path of the image
    2. Optional input: dict, dictionary specifying image meta information
        (e.g. extracted from file name before image load)
    3. Two return parameters

      1. ``ndarray``, the image data (2D numpy array)
      2. ``dict``, additional meta information (is required as return value
        , if no meta data is imported from your custom method, then simply
        return an empty dictionary. Please also make sure to use valid
        pyplis image meta data keys (listed below)

Valid keys for import of image meta information:

'start_acq', 'stop_acq', 'texp', 'focal_length', 'pix_width', 'pix_height',
'bit_depth', 'f_num', 'read_gain', 'filter', 'path', 'file_name', 'file_type',
'device_id', 'ser_no', 'wvlngth', 'fits_idx', 'temperature', 'user_param1',
'user_param2', 'user_param3'

"""
from __future__ import (absolute_import, division)
# from matplotlib.pyplot import imread
from pyplis import logger, print_log
from cv2 import imread
from numpy import swapaxes, flipud, asarray, rot90

from astropy.io import fits
from cv2 import resize
from os.path import basename
from datetime import datetime, timedelta
from re import sub
from .helpers import matlab_datenum_to_datetime


[docs]def load_ecII_fits(file_path, meta=None, **kwargs): """Load NILU ECII camera FITS file and import meta information.""" if meta is None: meta = {} hdu = fits.open(file_path) ec2header = hdu[0].header img = hdu[0].data hdu.close() gain_info = {"LOW": 0, "HIGH": 1} meta["texp"] = float(ec2header['EXP']) * 10 ** -6 # unit s meta["bit_depth"] = 12 meta["device_id"] = 'ECII' meta["file_type"] = 'fts' meta["start_acq"] = datetime.strptime(ec2header['STIME'], '%Y-%m-%d %H:%M:%S.%f') meta["stop_acq"] = datetime.strptime(ec2header['ETIME'], '%Y-%m-%d %H:%M:%S.%f') meta["read_gain"] = gain_info[ec2header['GAIN']] meta["pix_width"] = meta["pix_height"] = 4.65e-6 # m meta.update(ec2header) return (img, meta)
[docs]def load_hd_custom(file_path, meta=None, **kwargs): """Load image from HD custom camera. The camera specs can be found in `Kern et al. 2015, Intercomparison of SO2 camera systems for imaging volcanic gas plumes <http://www.sciencedirect.com/science/article/pii/ S0377027314002662#>`__ Images recorded with this camera type are stored as .tiff files and are :param file_path: image file path :param dict meta: optional, meta info dictionary to which additional meta information is suppose to be appended :return: - ndarray, image data - dict, dictionary containing meta information """ if meta is None: meta = {} im = imread(file_path, -1) # [1::, 1::] img = flipud(swapaxes(resize(im, (1024, 1024)), 0, 1)) try: f = sub('.tiff', '.txt', file_path) file = open(f) spl = file.read().split('\n') spl2 = spl[0].split("_") try: meta["texp"] = float(spl[1].split("Exposure Time: ")[1]) / 1000.0 except BaseException: meta["texp"] = float(spl[1].split("Exposure Time: ")[1]. replace(",", ".")) meta["start_acq"] = datetime.strptime(spl2[0] + spl2[1], '%Y%m%d%H%M%S%f') except BaseException: raise print_log.warning("Failed to read image meta data from text file (cam_id: hd)") return (img, meta)
[docs]def load_hd_new(file_path, meta=None, **kwargs): """Load new format from Heidelberg group. This format contains IPTC information :param file_path: image file path :param dict meta: optional, meta info dictionary to which additional meta information is supposed to be appended :return: - ndarray, image data - dict, dictionary containing meta information """ if meta is None: meta = {} try: from PIL.Image import open read = open(file_path) meta["texp"] = float(read.tag_v2[270].split(" ")[0].split("s")[0]) img = asarray(read) except ModuleNotFoundError: print_log.warning("Python Imaging Library (PIL) could not be imported. Using " "opencv method for image import. Cannot import exposure time " "info from tiff header...please install PIL") img = imread(file_path, -1) # img = asarray(im)[::-1, 0::] #flip img = rot90(rot90(img)) meta["start_acq"] = datetime.strptime( "_".join(basename(file_path).split("_")[:3]), "%Y%m%d_%H%M%S_%f") return (img, meta)
[docs]def load_qsi_lmv(file_path, meta=None, **kwargs): u"""Load images for QSI cam from LMV. Laboratoire Magmas et Volcans, Université Clermont Auvergne - CNRS - IRD, OPGC This format contains IPTC information Parameters ---------- file_path : str image file path meta : dict optional, meta info dictionary to which additional meta information is supposed to be appended Returns ------- tuple 2-element tuple, containing: - ndarray, image data - dict, dictionary containing meta information """ if meta is None: meta = {} img = imread(file_path, -1) # img = asarray(im)[::-1, 0::] #flip img = rot90(rot90(img)) return (img, meta)
[docs]def load_usgs_multifits(file_path, meta=None): if meta is None: meta = {} img = None if "filter_id" not in meta: logger.warning("filter_id (i.e. on or off) in input arg meta not specified." "Using default filter_id=on") meta["filter_id"] = "on" try: f = fits.open(file_path) idx = 2 if meta["filter_id"] == "off" else 1 hdu = f[idx] h = hdu.header try: meta["start_acq"] = matlab_datenum_to_datetime(h["DATETIME"]) meta["texp"] = h["EXPTIME"] * h["NUMEXP"] / 1000 meta["bit_depth"] = h["BITDEPTH"] except BaseException: print_log.warning("Failed to import image specific meta information from image " "HDU") h = f[0].header try: meta["lon"] = h["LON"] meta["lat"] = h["LAT"] meta["altitude"] = h["ALT"] meta["elev"] = h["ELEVANGL"] meta["azim"] = h["AZMTANGL"] except BaseException: print_log.warning("Failed to import camera specific meta information from " "primary HDU of FITS file...") img = hdu.data f.close() except Exception as e: raise IOError("Failed to import image data using custom method\n" "Error message: %s" % repr(e)) return (img, meta)
[docs]def load_usgs_multifits_uncompr(file_path, meta=None): if meta is None: meta = {} img = None if "filter_id" not in meta: logger.warning("filter_id (i.e. on or off) in input arg meta not specified." "Using default filter_id=on") meta["filter_id"] = "on" try: f = fits.open(file_path) idx = 1 if meta["filter_id"] == "off" else 0 hdu = f[idx] h = hdu.header try: meta["start_acq"] = matlab_datenum_to_datetime(h["DATETIME"]) meta["texp"] = h["EXPTIME"] * h["NUMEXP"] / 1000 meta["bit_depth"] = h["BITDEPTH"] except: print_log.warning("Failed to import image specific meta information from image " "HDU") h = f[0].header try: meta["lon"] = h["LON"] meta["lat"] = h["LAT"] meta["altitude"] = h["ALT"] meta["elev"] = h["ELEVANGL"] meta["azim"] = h["AZMTANGL"] except: print_log.warning("Failed to import camera specific meta information from " "primary HDU of FITS file...") img = hdu.data f.close() except Exception as e: raise IOError("Failed to import image data using custom method\n" "Error message: %s" % repr(e)) return (img, meta)
def _read_binary_timestamp(timestamp): u"""Read timestamp from pco camware binary format. This converts an (1,14)-array of pixel as given by the pco camware software to a valid datetime. Parameters ---------- timestamp : array array containg 14 pixel which code as the following 0 pixel 1 image counter (MSB) (00 … 99) 1 pixel 2 image counter (00 … 99) 2 pixel 3 image counter (00 … 99) 3 pixel 4 image counter (LSB) (00 … 99) 4 pixel 5 year (MSB) (20) 5 pixel 6 year (LSB) (03 … 99) 6 pixel 7 month (01 … 12) 7 pixel 8 day (01 ... 31) 8 pixel 9 h (00 … 23) 9 pixel 10 min (00 … 59) 10 pixel 11 s (00 … 59) 11 pixel 12 μs * 10000 (00 … 99) 12 pixel 13 μs * 100 (00 … 99) 13 pixel 14 μs (00 … 90) Returns ------- datetime.datetime 3-element tuple containing """ try: values = [10 * (timestamp[0, j] >> 4) + timestamp[0, j] - ((timestamp[0, j] >> 4) << 4) for j in range(14)] except: try: values = [10 * (timestamp[j] >> 4) + timestamp[j] - ((timestamp[j] >> 4) << 4) for j in range(14)] except: logger.info('Failed to convert the binary timestamp.') year = int(values[4] * 100 + values[5]) microsecond = int(values[11] * 10000 + values[12] * 100 + values[13]) endtime = datetime(year, values[6], values[7], values[8], values[9], values[10], microsecond) return endtime
[docs]def load_comtessa(file_path, meta=None): """Load image from a multi-layered fits file (several images in one file). Meta data is available only inside the header. This corresponds to image data from the COMTESSA project at Norwegian Institute for Air Research. Note ---- The comtessa *.fits files have several timestamps: 1) Filename --> minute in which the image was saved. 2) Meta information in the image header --> computer time when the image was saved. 3) First 14 image pixels contain a binary timestamp --> time when exposure was finished. Here nr 3) is saved as meta['stop_acq']. meta['start_acq'] is calculated from meta['stop_acq'] and meta['texp']. meta['user_param1'] is the gain (float type). Parameters ---------- file_path : string image file path meta: dictionary optional, meta info dictionary to which additional meta information is appended. The image index should be provided with key "fits_idx". Returns ------- ndarray image data dict dictionary containing meta information """ if meta is None: meta = {} hdulist = fits.open(file_path) try: img_hdu = meta['fits_idx'] except: img_hdu = 0 meta['fits_idx'] = 0 print_log.warning("Loading of comtessa fits file without providing the image index " "of desired image within the file. Image index was set to 0. " "Provide the image index via the meta = {'fits_idx':0} keyword.") # Load the image image = hdulist[img_hdu].data # read and replace binary time stamp endtime = _read_binary_timestamp(image) image[0, 0:14] = image[1, 0:14] # load meta data imageHeader = hdulist[img_hdu].header ms = int(imageHeader['EXP']) * 1000 meta.update({"start_acq": endtime - timedelta(microseconds=ms), "stop_acq": endtime, "texp": float(imageHeader['EXP']) / 1000., # in seconds "temperature": float(imageHeader['TCAM']), "ser_no": imageHeader['SERNO'], "user_param1": float(imageHeader['GAIN']) }) return (image, meta)