"""
Contains classes and functions pertaining to the creation of YOLOv3 datasets
from a collated DICOM database.

Please note that there is no standard YOLOv3 data format.  This module
exports a DICOM database to the format expected for a custom dataset usable
by the following YOLOv3 implementation:
    https://github.com/eriklindernoren/PyTorch-YOLOv3
"""
import logging
import os

import numpy as np

from breakdb.io.export import DatabaseEntryExporter, make_directory, \
    export_image, ExportEntryFormatError
from breakdb.io.image import transform_coordinate_collection

class YOLODatabaseEntryExporter(DatabaseEntryExporter):
    """
    Represents a mechanism to export a single entry of a collated DICOM
    database to the format used by a PyTorch YOLOv3 algorithm.
    """

    def __init__(self):
        super().__init__()

    def create_directory_structure(self, base_dir, force=False):
        annotation_dir = os.path.join(base_dir, "labels")
        image_dir = os.path.join(base_dir, "images")

        make_directory(base_dir, force)
        make_directory(annotation_dir, force)
        make_directory(image_dir, force)

        return annotation_dir, image_dir, base_dir

    def export(self, entry, base_dir, target_width=None, target_height=None,
               ignore_scaling=False, ignore_windowing=True,
               keep_aspect_ratio=True, no_upscale=False, skip_broken=False):
        logger = logging.getLogger(__name__)

        ds, name = entry

        try:
            annotation_path = os.path.join(base_dir, "labels", name) + ".txt"
            image_path = os.path.join(base_dir, "images", name) + ".jpg"

            logger.info("Exporting database entry: {}.", name)
            logger.debug("Exporting image for: {} to: {}.", name, image_path)

            dims, transform = export_image(ds, image_path, target_width,
                                           target_height, ignore_scaling,
                                           ignore_windowing, keep_aspect_ratio,
                                           no_upscale)

            if ds.Annotation:
                logger.debug("Exporting annotations for: {} to: {}.", name,
                             annotation_path)

                ds.Annotation = transform_coordinate_collection(ds.Annotation,
                                                                transform[0],
                                                                transform[1])
                txts = create_annotations(int(ds.Classification), ds.Annotation,
                                          dims[0], dims[1])

                with open(annotation_path, "w") as f:
                    for txt in txts:
                        print(txt, file=f)
            else:
                logger.debug("No annotations to export for: {}.", name)

            write_auxiliary_files(base_dir, ["negative", "positive"])

            return annotation_path, int(ds.Classification)
        except Exception as ex:
            if skip_broken:
                logger.warning("Could not export database entry: {}.", name)
                logger.warning("  Reason: {}.", ex)

                return ()
            else:
                raise ExportEntryFormatError(name, "YOLOv3") from ex

def create_annotation(classification, coords, width, height):
    """
    Creates a single YOLOv3 compatible text annotation with the specified
    classification and collection of coordinates, each of which is scaled to
    the specifiied width and height.

    :param classification: The classification, or label, of an annotated image.
    :param coords: The DICOM annotation as a list of single coordinates.
    :param width: The width of an annotated image.
    :param height: The height of an annotated image.
    :return: A YOLOv3 annotation.
    """
    return f"{classification} " \
           f"{' '.join(map(str, create_bounding_box(coords, width, height)))}"

def create_annotations(classification, annotations, width, height):
    """
    Creates a collection of YOLOv3 compatible text annotations for the
    specified annotation collection, each with the specified classification
    and scaled to the specified width and height.

    :param classification: The classification, or label, of an annotated image.
    :param annotations: A collection of annotations from an image.
    :param width: The width of an annotated image.
    :param height: The height of an annotated image.
    :return: A collection of YOLOv3 annotations.
    """
    txts = []

    for annot in annotations:
        txts.append(
            create_annotation(classification, annot, width, height)
        )

    return txts

def create_bounding_box(coords, width, height):
    """
    Converts the specified collection of coordinates and specified image
    dimensions into a YOLOv3 compatible bounding box.

    :param coords: The DICOM annotation as a list of single coordinates.
    :param width: The width of an image.
    :param height: The height of an image.
    :return: A YOLOv3 bounding box.
    """
    x = np.array([value for value in coords[0::2]], dtype=np.float)
    y = np.array([value for value in coords[1::2]], dtype=np.float)

    x_max = np.max(x)
    x_min = np.min(x)

    y_max = np.max(y)
    y_min = np.min(y)

    return [
        (x_max + x_min) / (2.0 * width),
        (y_max + y_min) / (2.0 * height),
        (x_max - x_min) / width,
        (y_max - y_min) / height
    ]

def write_auxiliary_files(dir_path, class_names):
    """
    Outputs additional files to complete the YOLOv3 dataset, including
    class names.

    :param dir_path: The directory in which to create the files.
    :param class_names: The collection of class names to use.
    """
    with open(os.path.join(dir_path, "classes.names"), "w") as class_file:
        for name in class_names:
            print(name, file=class_file)


import json
import numpy as np
import os
import tensorflow as tf

from luminoth.models import get_model
from luminoth.datasets import get_dataset

class PredictorNetwork(object):
    """Instantiates a network in order to get predictions from it.

    If a checkpoint exists in the job's directory, load it.  The names of the
    classes will be obtained from the dataset directory.

    Returns a list of objects detected, which is a dict of its coordinates,
    label and probability, ordered by probability.
    """

    def __init__(self, config):

        if config.dataset.dir:
            # Gets the names of the classes
            classes_file = os.path.join(config.dataset.dir, 'classes.json')
            if tf.gfile.Exists(classes_file):
                self.class_labels = json.load(tf.gfile.GFile(classes_file))
            else:
                self.class_labels = None

        # Don't use data augmentation in predictions
        config.dataset.data_augmentation = None

        dataset_class = get_dataset(config.dataset.type)
        model_class = get_model(config.model.type)
        dataset = dataset_class(config)
        model = model_class(config)

        graph = tf.Graph()
        self.session = tf.Session(graph=graph)

        with graph.as_default():
            self.image_placeholder = tf.placeholder(
                tf.float32, (None, None, 3)
            )
            image_tf, _, process_meta = dataset.preprocess(
                self.image_placeholder
            )
            pred_dict = model(image_tf)

            # Restore checkpoint
            if config.train.job_dir:
                job_dir = config.train.job_dir
                if config.train.run_name:
                    job_dir = os.path.join(job_dir, config.train.run_name)
                ckpt = tf.train.get_checkpoint_state(job_dir)
                if not ckpt or not ckpt.all_model_checkpoint_paths:
                    raise ValueError('Could not find checkpoint in {}.'.format(
                        job_dir
                    ))
                ckpt = ckpt.all_model_checkpoint_paths[-1]
                saver = tf.train.Saver(sharded=True, allow_empty=True)
                saver.restore(self.session, ckpt)
                tf.logging.info('Loaded checkpoint.')
            else:
                # A prediction without checkpoint is just used for testing
                tf.logging.warning(
                    'Could not load checkpoint. Using initialized model.')
                init_op = tf.group(
                    tf.global_variables_initializer(),
                    tf.local_variables_initializer()
                )
                self.session.run(init_op)

            if config.model.type == 'ssd':
                cls_prediction = pred_dict['classification_prediction']
                objects_tf = cls_prediction['objects']
                objects_labels_tf = cls_prediction['labels']
                objects_labels_prob_tf = cls_prediction['probs']
            elif config.model.type == 'fasterrcnn':
                if config.model.network.get('with_rcnn', False):
                    cls_prediction = pred_dict['classification_prediction']
                    objects_tf = cls_prediction['objects']
                    objects_labels_tf = cls_prediction['labels']
                    objects_labels_prob_tf = cls_prediction['probs']
                else:
                    rpn_prediction = pred_dict['rpn_prediction']
                    objects_tf = rpn_prediction['proposals']
                    objects_labels_prob_tf = rpn_prediction['scores']
                    # All labels without RCNN are zero
                    objects_labels_tf = tf.zeros(
                        tf.shape(objects_labels_prob_tf), dtype=tf.int32
                    )
            else:
                raise ValueError(
                    "Model type '{}' not supported".format(config.model.type)
                )

            self.fetches = {
                'objects': objects_tf,
                'labels': objects_labels_tf,
                'probs': objects_labels_prob_tf,
                'scale_factor': process_meta['scale_factor']
            }

            # If in debug mode, return the full prediction dictionary.
            if config.train.debug:
                self.fetches['_debug'] = pred_dict

    def predict_image(self, image):
        fetched = self.session.run(self.fetches, feed_dict={
            self.image_placeholder: np.array(image)
        })

        objects = fetched['objects']
        labels = fetched['labels'].tolist()
        probs = fetched['probs'].tolist()
        scale_factor = fetched['scale_factor']

        if self.class_labels is not None:
            labels = [self.class_labels[label] for label in labels]

        # Scale objects to original image dimensions
        if isinstance(scale_factor, tuple):
            # If scale factor is a tuple, it means we need to scale height and
            # width by a different amount. In that case scale factor is:
            # (scale_factor_height, scale_factor_width)
            objects /= [scale_factor[1], scale_factor[0],
                        scale_factor[1], scale_factor[0]]
        else:
            # If scale factor is a scalar, height and width get scaled by the
            # same amount
            objects /= scale_factor

        # Cast to int to consistently return the same type in Python 2 and 3
        objects = [
            [int(round(coord)) for coord in obj]
            for obj in objects.tolist()
        ]

        predictions = sorted([
            {
                'bbox': obj,
                'label': label,
                'prob': round(prob, 4),
            } for obj, label, prob in zip(objects, labels, probs)
        ], key=lambda x: x['prob'], reverse=True)

        return predictions


'''Train the model'''
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import datetime
import functools
import os
import json
import gc

import tensorflow as tf
import numpy as np
import scipy.misc

import densecap.model as model
import densecap.util as util

FLAGS = tf.app.flags.FLAGS
tf.app.flags.DEFINE_string('train_dir', os.getcwd(), 'Directory with training data (images)')
tf.app.flags.DEFINE_string('region_desc', '', 'Region descriptions file (Visual Genome format)')
tf.app.flags.DEFINE_string('log_dir', os.path.join(os.getcwd(), 'logs'), 'Directory with logs for tensorboard')
tf.app.flags.DEFINE_string('ckpt_dir', os.path.join(os.getcwd(), 'ckpt'), 'Directory for model checkpoints')
tf.app.flags.DEFINE_integer('batch_size', 64, 'Batch size')
tf.app.flags.DEFINE_integer('limit', 0, 'Limit training process to first `limit` files per epoch')
tf.app.flags.DEFINE_integer('epoch', 10, 'Epoch count')
tf.app.flags.DEFINE_integer('log_every', 100, 'Print log messages every `log_every` steps')
tf.app.flags.DEFINE_integer('save_every', 100, 'Save model checkpoint every `save_every` steps')
tf.app.flags.DEFINE_integer('eval_every', 100, 'Eval model every `eval_every` steps')
tf.app.flags.DEFINE_float('learning_rate', 0.01, 'Initial learning rate')

def train_data(filename, limit):
    with open(FLAGS.region_desc) as ifile:
        data = json.load(ifile)

    limit = limit or len(data)
    for idx in range(limit):
        record = data[idx]

        filename = os.path.join(
            FLAGS.train_dir, str(record['id']) + '.jpg')
        # XXX: resize to have 600/720 longer side
        image = scipy.misc.imread(filename, mode='RGB')
        height, width, _ = image.shape
        fraction = 720.0 / max(height, width)
        image = scipy.misc.imresize(image, fraction)
        gt_boxes = np.array([[r['y'], r['x'], r['height'], r['width']]
                             for r in record['regions']])
        yield (image, gt_boxes)

def load_vgg16_weights(sess):
    with np.load('data/vgg16_weights.npz') as ifile:
        for v in tf.global_variables():
            name = v.name.replace('weights', 'W').replace('biases', 'b').replace('/', '_')[:-2]
            if name in ifile:
                sess.run(tf.assign(v, ifile[name]))

def main(_):
    '''entry point'''

    image_input = tf.placeholder(tf.float32, shape=[1, None, None, 3])

    vgg16 = model.VGG16(image_input)
    rpn = model.RegionProposalNetwork(vgg16.layers['conv5_3'])

    current_run_log_dir = os.path.join(
        FLAGS.log_dir,
        datetime.datetime.now().isoformat()
    )
    writer = tf.train.SummaryWriter(current_run_log_dir, graph=tf.get_default_graph())
    saver = tf.train.Saver()
    saved_model = tf.train.latest_checkpoint(FLAGS.ckpt_dir)

    if not os.path.exists(FLAGS.ckpt_dir):
        os.makedirs(FLAGS.ckpt_dir)

    @functools.lru_cache()
    def get_data():
        return list(train_data(FLAGS.region_desc, FLAGS.limit))

    with tf.Session() as sess:
        if saved_model:
            saver.restore(sess, saved_model)
        else:
            print('Prevous model not found, starting from scratch.')
            sess.run(tf.global_variables_initializer())
            load_vgg16_weights(sess)

        for epoch in range(FLAGS.epoch):
            for image, gt_boxes in get_data():
                height, width, _ = image.shape

                p_bbox, p_score, p_label, n_bbox, n_score, n_label = sess.run(
                    [rpn.positive_bbox, rpn.positive_scores, rpn.positive_labels,
                     rpn.negative_bbox, rpn.negative_scores, rpn.negative_labels], {
                        vgg16.input: [image],
                        rpn.image_height: height,
                        rpn.image_width: width,
                        rpn.ground_truth_pre: gt_boxes,
                        rpn.ground_truth_num: len(gt_boxes)
                    })

                (p_bbox, p_score, p_label), (n_bbox, n_score, n_label) = model.generate_batches(
                    (p_bbox, p_score, np.expand_dims(p_label, axis=1)),
                    (n_bbox, n_score, np.expand_dims(n_label, axis=1)),
                    rpn.batch_size
                )

                merged = tf.summary.merge_all()
                loss, step, summary, _ = sess.run([rpn.loss, rpn.global_step, merged, rpn.train_op], {
                        vgg16.input: [image],
                        rpn.image_height: height,
                        rpn.image_width: width,
                        rpn.ground_truth_pre: gt_boxes,
                        rpn.ground_truth_num: len(gt_boxes),
                        rpn.pos_boxes: p_bbox,
                        rpn.pos_scores: p_score,
                        rpn.true_pos_scores: np.squeeze(p_label),
                        rpn.neg_boxes: n_bbox,
                        rpn.neg_scores: n_score,
                        rpn.true_neg_scores: np.squeeze(n_label),
                    })

                writer.add_summary(summary, global_step=step)

                gc.collect()

                if not step % FLAGS.log_every:
                    print('\rEpoch {:<2} step {:<6} loss: {:<8.2f}'\
                        .format(epoch+1, step, loss), end='')

                if not step % FLAGS.save_every:
                    saver.save(
                        sess,
                        os.path.join(FLAGS.ckpt_dir, 'densecap'),
                        global_step=rpn.global_step)

                if not step % FLAGS.eval_every:
                    k = 300
                    boxes, scores = sess.run(
                        [rpn.proposals, tf.nn.softmax(rpn.scores)], {
                            rpn.image_height: height,
                            rpn.image_width: width,
                            vgg16.input: [image]
                        })
                    np_proposals = np.squeeze(boxes[np.argsort(scores[:, 1])][-k:])

                    proposals = tf.placeholder(tf.float32, [None, 4])
                    ground_truth = tf.placeholder(tf.float32, [None, 4])
                    proposals_num = tf.placeholder(tf.int32)
                    ground_truth_num = tf.placeholder(tf.int32)

                    recall, precision = sess.run(
                        [model.recall(proposals, proposals_num, ground_truth, ground_truth_num, 0.5),
                         model.precision(proposals, proposals_num, ground_truth, ground_truth_num, 0.5)], {
                            proposals: np_proposals,
                            proposals_num: len(np_proposals),
                            ground_truth: gt_boxes,
                            ground_truth_num: len(gt_boxes)
                        })

                    summary = tf.Summary(value=[
                        tf.Summary.Value(tag='recall', simple_value=float(recall)),
                        tf.Summary.Value(tag='precision', simple_value=float(precision)),
                    ])
                    writer.add_summary(summary, global_step=step)

        print()

    writer.close()

if __name__ == '__main__':
    tf.app.run()


#!/usr/bin/env pnpython3
#
# A class to read SmartSolo SEG-D Version
# based on SmartSolo Software User Manual Version 1.3
# and segdreader.py by Steve Azevedo
#
# Lan Dam, March 2021
#

import sys
import logging
import os
import exceptions

import numpy as np
try:
    from astropy.time import Time as asTime
except ImportError:
    errmsg = ("astropy package is needed to run this command. "
              "Please run 'conda install astropy' to install it.")
    raise ImportError(errmsg)

from ph5.core import segd_h_smartsolo as segd_h
from ph5.core.timedoy import TimeDOY

PROG_VERSION = "2021.155"
LOGGER = logging.getLogger(__name__)

class InputsError (exceptions.Exception):
    def __init__(self, args=None):
        self.args = args

class ReelHeaders (object):
    '''   Container to hold receiver record related headers   '''
    __slots__ = ['storage_unit_label', 'general_header_blocks',
                 'channel_set_descriptor', 'extended_headers',
                 'external_header',
                 'external_header_shot',
                 'channel_set_to_streamer_cable_map']

    def __init__(self):
        self.storage_unit_label = None
        self.general_header_blocks = []
        self.channel_set_descriptor = []
        self.extended_headers = []
        self.external_header = None
        self.external_header_shot = []
        self.channel_set_to_streamer_cable_map = None

class TraceHeaders (object):
    '''   Container to hold trace related headers   '''
    __slots__ = ['trace_header', 'trace_header_N',
                 'line_number', 'receiver_point',
                 'event_number', 'trace_epoch',
                 'preamp_gain_db',
                 'lat', 'lon', 'ele']

    def __init__(self):
        self.trace_header = None
        self.trace_header_N = []
        self.line_number = None
        self.receiver_point = None
        self.event_number = None
        self.trace_epoch = None
        self.lat = None
        self.lon = None
        self.ele = None
        self.preamp_gain_db = None

class Reader ():
    def __init__(self, infile=None):
        self.manufacturer = 'SmartSolo'
        self.infile = infile
        self.FH = None
        self.endianess = 'big'  # SEG-D is always big endian(?)
        # From General headers
        self.file_number = None
        self.record_length_sec = None
        self.chan_sets_per_record = None
        self.sample_rate = None
        self.deploy_epoch = None
        # From Extended headers
        self.pickup_epoch = None
        # From Channel set headers
        self.channel_set_start_time_sec = None
        self.channel_set_end_time_sec = None
        self.preamp_gain_db = None
        # From trace headers
        self.samples = None
        self.bytes_read = 0
        self.id_number = None

    def open_infile(self):
        try:
            self.FH = open(self.infile)
        except Exception as e:
            LOGGER.error(e)
            self.FH = None

    def read_buf(self, size):
        buf = None
        if not self.FH:
            self.open_infile()

        try:
            buf = self.FH.read(size)
        except Exception as e:
            LOGGER.error(e)
            self.FH.close()

        if buf:
            self.bytes_read += len(buf)

        return buf

    def name(self):
        '''   Return name of open file   '''
        if self.FH:
            return self.FH.name
        else:
            return None

    def read_block(self, size, block_obj):
        buf = self.read_buf(size)
        container = block_obj.parse(buf)
        return container

    def get_deploy_epoch(self, ghb):
        t = TimeDOY(year=2000 + ghb.first_shot_point_year,
                    doy=ghb.first_shot_point_doy,
                    hour=ghb.hour_of_day_utc,
                    minute=ghb.minute_of_hour_utc,
                    second=ghb.second_of_minute_utc)
        return t.epoch()

    def getLocationDecimal(self, integer, fraction):
        """
        OLD FORMAT: degree2decimal
        lat degree: DDMMSS.sss
        lon degree: DDDMMSS.sss
        return decimal = deg + min/60 + sec/3600
        NEW FORMAT: combine signed integer with string
        integer is signed ingeter
        return signed_integer.fraction
        """
        """
        # OLD FORMAT DDMMSS.sss
        degree_str = "%.3f" % (integer + fraction)
        sec = float(degree_str[-6:])
        min = float(degree_str[-8:-6])
        deg = float(degree_str[:-8])
        decimal = deg + min/60. + sec/3600.
        return decimal
        """
        # NEW FORMAT: ingeger is a signed one
        if integer < 0:
            degree_str = integer - fraction
        else:
            degree_str = integer + fraction
        return degree_str

    def process_general_headers(self):
        self.reel_headers = ReelHeaders()

        for i in range(3):
            self.reel_headers.general_header_blocks.append(
                self.read_block(32, segd_h.General_header_block(i)))

        # Set record length
        if self.reel_headers.general_header_blocks[0].record_length == 0xFFF:
            self.record_length_sec = self.reel_headers. \
                general_header_blocks[1].extended_record_length
        else:
            self.record_length_sec = self.reel_headers.general_header_blocks[
                                         0].record_length * 0.512
        # Set number of channel sets
        if self.reel_headers.general_header_blocks[
                0].chan_sets_per_scan == 0xFF:
            self.chan_sets_per_scan = self.reel_headers. \
                general_header_blocks[1].extended_chan_sets_per_scan_type
        else:
            self.chan_sets_per_scan = self.reel_headers. \
                general_header_blocks[0].chan_sets_per_scan

        self.sample_rate = int(
            (1. / (self.reel_headers.
                   general_header_blocks[0].base_scan_interval / 16.)) * 1000.)

        self.deploy_epoch = self.get_deploy_epoch(
            self.reel_headers.general_header_blocks[0])

    def process_channel_set_descriptors(self):
        """ name in SmartSolo Doc.: Scan Type Header """
        def create_key():
            # Create a mapping between channel sets to streamer cable numbers
            kv = {}
            for cs in self.reel_headers.channel_set_descriptor:
                k = cs.chan_set_number
                v = cs.streamer_cable_number
                kv[k] = v

            return kv

        for i in range(self.chan_sets_per_scan):
            cs = self.read_block(32, segd_h.Channel_set_descriptor())
            self.reel_headers.channel_set_descriptor.append(cs)
            # ***   Should we get current channel set number   ***
            # Channel set start time in seconds
            self.channel_set_start_time_sec = \
                self.reel_headers.channel_set_descriptor[
                    i]. chan_set_start_time_ms / 1000
            # Channel set end time in seconds
            self.channel_set_end_time_sec = \
                self.reel_headers.channel_set_descriptor[
                    i].chan_set_end_time_ms / 1000
            # ***   Calculate scale factor for mili-volts.
            # SmartSolo data recorded as mili-volts   ***
        self.reel_headers.channel_set_to_streamer_cable_map = create_key()
        # Calculate preamp_gain
        self.preamp_gain_db = None
        mp = self.reel_headers.channel_set_descriptor[
            0].MP_factor_descaler_multiplier
        self.MP_factor_descaler_multiplier = mp
        mp_hex = "{0:x}".format(mp)
        if mp == 0:         # 0x0000
            self.preamp_gain_db = 0
        elif mp == 132:     # 0x0084
            self.preamp_gain_db = 6
        elif mp == 136:     # 0x0088
            self.preamp_gain_db = 12
        elif mp == 140:     # 0x008c
            self.preamp_gain_db = 18
        elif mp == 144:     # 0x0090
            self.preamp_gain_db = 24
        elif mp == 148:     # 0x0094
            self.preamp_gain_db = 30
        elif mp == 152:     # 0x0098
            self.preamp_gain_db = 36
        else:
            LOGGER.error("MP_factor_descaler_multiplier is Ox{0} while it "
                         "should be one of the following values:\n"
                         "0x0000,0x0084,0x0088,0x008c,0x0090,0x0094,"
                         "0x0098".format(mp_hex))

    def process_extended_headers(self):
        self.reel_headers.extended_headers.append(
            self.read_block(1024, segd_h.Extended_header_block()))
        sample_rate = int((10**6) / self.reel_headers.extended_headers[0]
                          .sample_rate_microsec)
        if sample_rate != self.sample_rate:
            raise InputsError("Sample rate in Extended Header[0] is conflict"
                              "with base_scan_interval")
        number_of_traces = self.reel_headers.extended_headers[
            0].number_of_traces
        number_of_samples_in_traces = self.reel_headers.extended_headers[
            0].number_of_samples_in_traces
        self.pickup_epoch = self.deploy_epoch + (
            number_of_samples_in_traces * number_of_traces / sample_rate
        )

    def process_external_headers(self):
        self.reel_headers.external_header = self.read_block(
            1024, segd_h.External_header_block())

    def process_trace_headers(self):
        self.trace_headers = TraceHeaders()

        self.trace_headers.trace_header = self.read_block(
            20, segd_h.Trace_header())
        n = self.trace_headers.trace_header.trace_extension_blocks
        for i in range(n):
            self.trace_headers.trace_header_N.append(
                self.read_block(32, segd_h.Trace_header_extension(i)))

        self.id_number = self.trace_headers.trace_header_N[
            5].unit_serial_number
        self.trace_headers.event_number = \
            self.trace_headers.trace_header.trace_number

        thN0 = self.trace_headers.trace_header_N[0]
        self.samples = thN0.samples_per_trace
        # ignore fraction part
        self.trace_headers.line_number = \
            thN0.extended_receiver_line_number_integer
        self.trace_headers.receiver_point = \
            thN0.extended_receiver_point_number_integer

        thN3 = self.trace_headers.trace_header_N[3]
        self.trace_headers.lat = self.getLocationDecimal(
            thN3.IGU_GPS_lat_integer, thN3.IGU_GPS_lat_fraction)
        self.trace_headers.lon = self.getLocationDecimal(
            thN3.IGU_GPS_lon_integer, thN3.IGU_GPS_lon_fraction)
        self.trace_headers.ele = thN3.IGU_GPS_height
        self.trace_headers.preamp_gain_db = self.preamp_gain_db
        TB_GPS_time_time_sec = self.trace_headers.trace_header_N[
                                  1].TB_GPS_time_microsec/1000000.
        # Use astropy package to convert time from gps to utc
        # https: // docs.astropy.org / en / stable / time /
        gps_time = asTime(TB_GPS_time_time_sec, format='gps')
        trace_epoch_sec = asTime(gps_time, format='unix', scale='utc').value
        trace_epoch_ms = trace_epoch_sec * 10.**3
        self.trace_headers.trace_epoch = trace_epoch_sec * 10.**6   # microsec
        number_of_samples_in_traces = self.trace_headers.trace_header_N[
            0].samples_per_trace
        trace_endtime_ms = trace_epoch_ms + (
            number_of_samples_in_traces / self.sample_rate)

        trace_endtime = trace_endtime_ms / float(
            10**(len(str(trace_endtime_ms)) - len(str(int(self.pickup_epoch))))
        )

        if self.pickup_epoch < trace_endtime:
            self.pickup_epoch = trace_endtime

        return self.samples

    def read_trace(self, number_of_samples):
        '''   Read data trace and return as numpy array
              8015 -- 20 bit binary
              8022 -- 8 bit quanternary
              8024 -- 16 bit quanternary
              8036 -- 24 bit 2s compliment integer
              8038 -- 32 bit 2s compliment integer
              8042 -- 8 bit hexidecimal
              8044 -- 16 bit hexidecimal
              8048 -- 32 bit hexidecimal
              8058 -- 32 bit IEEE float   '''

        f = self.trace_fmt = self.reel_headers.\
            general_header_blocks[0].data_sample_format_code

        bytes_per_sample = 4  # Assumes 32 bit IEEE floats
        buf = self.read_buf(bytes_per_sample * number_of_samples)
        # IEEE floats - 4 byte - Should be big endian
        if f == 8058:
            try:
                if self.endianess != sys.byteorder:
                    # Swap 4 byte
                    ret = np.fromstring(buf, dtype=np.float32)
                    ret = ret.byteswap()
                else:
                    ret = np.fromstring(buf, dtype=np.float32)
            except Exception as e:
                raise InputsError(
                    "Error: Could not read data trace: {0}".format(e))

        else:
            raise InputsError("Format code of {0} not supported!".format(f))

        return ret

    def process_trace(self, trace_index):

        if trace_index == 0:
            """
            With SmartSolo, segd2ph5 need to run process_trace_headers()
            to get some info in trace_header at the beginning
            => not run self.process_trace_headers() for the first trace
            """
            samples = self.samples
        else:
            samples = self.process_trace_headers()
        ret = self.read_trace(samples)
        cs = self.trace_headers.trace_header.channel_set
        # Return trace and channel set number
        return ret, cs

    def isEOF(self):
        if self.FH.closed:
            return True

        try:
            n = len(self.FH.read(20))
            if n != 20:
                raise EOFError
            self.FH.seek(-20, os.SEEK_CUR)
            return False
        except EOFError:
            self.FH.close()
            return True


from flask import Flask, request, abort
from pathlib import Path
import linebot
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import ImageMessage, MessageEvent, TextMessage, TextSendMessage
import base64
import requests, json, os, io
from io import BytesIO
from PIL import Image
import numpy as np
from keras.preprocessing.image import img_to_array, load_img
from keras.models import load_model
import gc

print('***ライブラリのインポート***')

app = Flask(__name__)

line_bot_api = linebot.LineBotApi(os.environ['LINE_CHANNEL_ACCESS_TOKEN'])
handler = linebot.WebhookHandler(os.environ['LINE_CHANNEL_SECRET'])

header = {
    "Content-Type": "application/json",
    "Authorization":  "Bearer " + os.environ['LINE_CHANNEL_ACCESS_TOKEN']
}

# model はグローバルで宣言し、初期化しておく
model = None

print('****前準備****')
@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'OK'

    print('****コールバック****')

# テキストを受け取る部分
@handler.add(MessageEvent, message=TextMessage)
def handler_message(event):
     print("****handle_message****:", event)
     line_bot_api.reply_message(event.reply_token,TextSendMessage('コーギー、パグ、柴犬、キャバリア、チワワ、ダックス、プードル'))

# 画像を受け取る部分
@handler.add(MessageEvent, message=ImageMessage)
def handle_image(event):
    print("****handle_image****:", event)

    message_id = event.message.id

    # message_idから画像のバイナリデータを取得
    message_content = line_bot_api.get_message_content(message_id)
    SRC_IMAGE_PATH = "static/images/{}.jpg"
    save_path = Path(SRC_IMAGE_PATH.format(message_id)).absolute()

    with open(save_path, "wb") as f:
        for chunk in message_content.iter_content():
            f.write(chunk)

    image_text = get_text_by_ms(event,save_path)
    line_bot_api.reply_message(event.reply_token,TextSendMessage(text=image_text))

def get_text_by_ms(event,result):

    # 90行目で保存した url から画像を書き出す。
    img = img_to_array(load_img(result, target_size=(256,256)))
    face=""
    # グローバル変数を取得する
    global model

    # model をロードする
    model = load_model('./acc_77-.h5')

    # 0-1に変換
    img_nad = (img_to_array(img)/255)

    # 4次元配列に
    img_nad = img_nad[None, ...]

    predict = model.predict(img_nad)
    print('***predict***',predict)
    faceNumLabel=np.argmax(predict)
    score = np.max(predict)
    text = detect_who(faceNumLabel)
    score = '{:.1%}'.format(score)
    text = text + ':' + score

    print('***text***',text)

    del img_nad,img,model
    gc.collect()

    return text

def detect_who(faceNumLabel):

    # 「判定」
    if faceNumLabel == 0:
        face = "コーギー"
    elif faceNumLabel == 1:
        face = "柴犬"
    elif faceNumLabel == 2:
        face = "パグ"
    elif faceNumLabel == 3:
        face = "キャバリア"
    elif faceNumLabel == 4:
        face = "チワワ"
    elif faceNumLabel == 5:
        face = "ダックスフンド"
    elif faceNumLabel == 6:
        face = "プードル"

    print('****face****',face)
    return face

if __name__ == "__main__":
    port = int(os.getenv("PORT", 5000))
    app.run(host="0.0.0.0", port=port)


"""plot_utils"""
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.basemap import Basemap
import numpy as np

projections = {}
projections['wr50a'] = {'urcrnrlat': 27.511827255753555,
                        'urcrnrlon': 16.90845094934209,
                        'llcrnrlat': 16.534986367884521,
                        'llcrnrlon': 189.2229322311162,
                        'projection': 'lcc',
                        'rsphere': 6371200.0,
                        'lon_0': -114,
                        'lat_0': 90}
default_projection = projections['wr50a']

default_map = None  # set again at end of module

class Bmap(object):
    """Wrapper container for Basemap object and map indices"""
    def __init__(self, projection=default_projection):
        self.projection = projection
        self.m = Basemap(**self.projection)

    def set_map_inds(self, lons, lats):
        """Set the map indices
        lons : numpy.ndarray
            Array of grid cell longitudes.
        lons : numpy.ndarray
            Array of grid cell latitudes.
        """
        self.xi, self.yi = self.m(np.squeeze(lons), np.squeeze(lats))
        self.inds_set = True

def make_bmap(projection=default_projection, lons=None, lats=None):
    """
    Make a bmap object storing the projection and basemap indices for a series
    of plots.

    Parameters
    ----------
    projection : dict
        Projection keywords to be passed to `Basemap`.
    lons : numpy.ndarray
        Array of grid cell longitudes.
    lons : numpy.ndarray
        Array of grid cell latitudes.

    Returns
    ----------
    bmap : Bmap
        Bmap object.
    """
    bmap = Bmap(projection=projection)

    if lons is not None and lats is not None:
        bmap.set_map_inds(lons, lats)
    return bmap

# -------------------------------------------------------------------- #
def cmap_discretize(cmap, n_colors=10):
    """Return discretized colormap.

    Parameters
    ----------
    cmap : str or colormap object
        Colormap to discretize.
    n_colors : int
        Number of discrete colors to divide `cmap` into.

    Returns
    ----------
    disc_cmap : LinearSegmentedColormap
        Discretized colormap.
    """
    try:
        cmap = cm.get_cmap(cmap)
    except:
        cmap = cm.get_cmap(eval(cmap))
    colors_i = np.concatenate((np.linspace(0, 1., n_colors), (0., 0., 0., 0.)))
    colors_rgba = cmap(colors_i)
    indices = np.linspace(0, 1., n_colors + 1)
    cdict = {}
    for ki, key in enumerate(('red', 'green', 'blue')):
        cdict[key] = [(indices[i], colors_rgba[i - 1, ki], colors_rgba[i, ki])
                      for i in range(n_colors + 1)]

    return mpl.colors.LinearSegmentedColormap(cmap.name + "_%d" % n_colors,
                                              cdict, 1024)
# -------------------------------------------------------------------- #

# -------------------------------------------------------------------- #

def sub_plot_pcolor(data,
                    title=None,
                    cmap='Spectral_r',
                    vmin=None,
                    vmax=None,
                    cbar=True,
                    cbar_location='bottom',
                    units=None,
                    cbar_extend='neither',
                    map_obj=default_map,
                    ax=None):
    """Plot data into a subplot using pcolormesh.

    Parameters
    ----------
    data : 2d numpy array
        Array of data to plot using pcolormesh.
    title : str, optional
        Title to add to subplot.
    cmap : str or colormap object, optional
        Colormap to use in pcolormesh.
    vmin : scalar, optional
        Minimum value for color range.
    vmax : scalar, optional
        Maximum value for color range.
    cbar_location : str, optional
        Location of colorbar.  Value is passed to `Basemap.colorbar()`
    cbar_extend : str, optional
        Extend colorbar ends.  Value is passed to `Basemap.colorbar()`
    units : str
        Colorbar label.
    map_obj : Bmap, optional
        `Bmap` object containing `Basemap` object as well as plot coordinates.
        Set using `make_bmap()`.
    ax : axis object, optional
        Axis object to use for subplot.  If None, `ax=plt.gca()`.
    """

    if vmin is None:
        vmin = data.min()
    if vmax is None:
        vmax = data.max()

    if ax is None:
        ax = plt.gca()

    map_obj.m.pcolormesh(map_obj.xi, map_obj.yi, np.squeeze(data),
                         vmin=vmin, vmax=vmax, cmap=cmap, ax=ax)
    map_obj.m.drawparallels(np.arange(-80., 81., 20.))
    map_obj.m.drawmeridians(np.arange(-180., 181., 20.))
    map_obj.m.drawcoastlines(color='k', linewidth=0.25)

    if title is not None:
        plt.title(title, size=13)
    if cbar is not None:
        cbar = map_obj.m.colorbar(location=cbar_location, extend=cbar_extend)
    cbar.set_label(units)

    return
# -------------------------------------------------------------------- #

default_map = make_bmap()


from enum import Enum
from typing import Tuple

import torch
from albumentations import Compose, SmallestMaxSize, CenterCrop, Rotate, HorizontalFlip, BasicTransform
from pietoolbelt.models import ModelsContainer
from pietoolbelt.tta import HFlipTTA, CLAHETTA, VFlipTTA, RotateTTA
from torchvision.models.utils import load_state_dict_from_url
import os
import numpy as np

from pietoolbelt.steps.segmentation.inference import SegmentationInference

__all__ = ['Segmentation']

class Segmentation:
    """
    Person whole stature segmentation

    Args:
        accuracy_lvl (Level): level of prediction accuracy. !WARNING! Performance decaying with increasing level of accuracy

    Accuracy levels:
        LEVEL_0 - maximum performance, minimum accuracy

        LEVEL_1 - medium performance, medium accuracy

        LEVEL_2 - low performance, maximum accuracy
    """

    class Level(Enum):
        LEVEL_0 = 1,
        LEVEL_1 = 2,
        LEVEL_2 = 3

    def __init__(self, accuracy_lvl: Level = Level.LEVEL_1):
        try:
            if accuracy_lvl == Segmentation.Level.LEVEL_2:
                self._model = ModelsContainer([
                    load_state_dict_from_url("https://github.com/HumanParsingSDK/weights/raw/master/segmentation/model0.pth",
                                             model_dir=os.path.join(os.path.expanduser("~"), '.human_parsing', 'segmentation')),
                    load_state_dict_from_url("https://github.com/HumanParsingSDK/weights/raw/master/segmentation/model1.pth",
                                             model_dir=os.path.join(os.path.expanduser("~"), '.human_parsing', 'segmentation')),
                    load_state_dict_from_url("https://github.com/HumanParsingSDK/weights/raw/master/segmentation/model2.pth",
                                             model_dir=os.path.join(os.path.expanduser("~"), '.human_parsing', 'segmentation'))
                ], reduction=lambda x: torch.mean(x, dim=0))
            else:
                self._model = load_state_dict_from_url("https://github.com/HumanParsingSDK/weights/raw/master/segmentation/model0.pth",
                                                       model_dir=os.path.join(os.path.expanduser("~"), '.human_parsing',
                                                                              'segmentation'))
        except Exception as err:
            raise RuntimeError(
                "Can't download weights. Check internet connection or try to update human_parsing sdk. Error message: [{}]".format(
                    err))

        target_transform = Compose([Rotate(limit=(-90, -90), p=1), HorizontalFlip(p=1)])
        self._inference = SegmentationInference(self._model).set_target_transform(target_transform)

        if accuracy_lvl == Segmentation.Level.LEVEL_1:
            self._inference.set_tta([HFlipTTA(), RotateTTA(angle_range=(107, 107)), RotateTTA(angle_range=(34, 34)),
                                     RotateTTA(angle_range=(63, 63))])
        elif accuracy_lvl == Segmentation.Level.LEVEL_2:
            self._inference.set_tta([HFlipTTA(), RotateTTA(angle_range=(-84, -84)), RotateTTA(angle_range=(69, 69)),
                                     RotateTTA(angle_range=(64, 64))])

    def process(self, image: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """
        Process one image

        Args:
            image (np.ndarray): image in RGB color scheme, and in a shape: (H, W, C)

        Return:
            tuple of [transformed image, prediction result]
        """
        return self._inference.run_image(image)

    def set_device(self, device: str) -> 'Segmentation':
        """
        Set target device

        Args:
            device (str): target device ('cpu', 'cuda', 'cuda:0', e.t.c)

        Return:
            self instance
        """
        self._inference.set_device(device)
        return self


import soundfile as sf
import numpy as np
import xlsxwriter
import pyslm
import h5py
import os

def save(params: dict, results: dict, timestamp: dict, file_name: str):
    # Saving raw data to a .wav file
    if params['saveRawData']:
        RawData = h5py.File(name=file_name, mode='r')
        audio = np.asarray(RawData.get('recSignal'))
        file_audio = file_name.replace(".h5", ".wav")
        if params['template'] != 'reverberationTime':
            sf.write(file=file_audio, data=audio[int(0.15*params['fs']):results['framesRead']], samplerate=params["fs"])
        else:
            sf.write(file=file_audio, data=audio, samplerate=params["fs"])
    else:
        file_audio = None
    file_name = (file_name.replace("(raw data) ", "")).replace(".h5", ".xlsx")

    if params['version'] == 'AdvFreqAnalyzer':
        if params['template'] != 'reverberationTime':
            # Time vector
            time = np.arange(results['Lglobal'].size) * params['tau']

        # time weighting string
        if params['tau'] == 0.035:
            strTau = 'Impulse'
        elif params['tau'] == 0.125:
            strTau = 'Fast'
        else:
            strTau = 'Slow'
        # Frequencies labels
        freq = {20.: '20', 25.: '25', 31.5: '31.5', 40.: '40', 50.: '50', 63. :'63', 80.: '80', 100.: '100', 125.: '125',
                160.: '160', 200.: '200', 250.: '250', 315.: '315', 400.: '400', 500.: '500', 630.: '630', 800.: '800',
                1000.: '1k', 1250.: '1.25k', 1600.: '1.6k', 2000.: '2k', 2500.: '2.5k', 3150.: '3.15k', 4000.: '4k',
                5000.: '5k', 6300.: '6.3k', 8000.: '8k', 10000.: '10k', 12500.: '12.5k', 16000.: '16k', 20000.: '20k'}
        strBands = list()
        for i in range(results['bands'].size):
            if results['bands'][i] in freq.keys():
                strBands.append(freq[results['bands'][i]])

        # Creating the spreadsheet
        workbook = xlsxwriter.Workbook(file_name)
        bold = workbook.add_format({'bold': True})
        sheetResults = workbook.add_worksheet(name="Results")
        sheetSetup      = workbook.add_worksheet(name="Settings")

        # Formatting templates
        formatSection = workbook.add_format({'bold': True, 'border': 4, 'align': 'center', 'valign': 'vcenter', 'fg_color': '#7030a0'})
        formatInformationT = workbook.add_format({'bold': True, 'border': 4, 'align': 'center', 'valign': 'vcenter', 'fg_color': '#CCCCCC'})

        # Save data for Adv. Frequency Analyzer template
        if params['template'] == 'frequencyAnalyzer':
            # Measurement parameters (sheet Setup)
            sheetSetup.set_column(0,0,30,bold)
            sheetSetup.merge_range(0,0,0,10, 'Measurement parameters', formatSection)
            sheetSetup.write("A2", "Version of software", bold);              sheetSetup.write("B2", "Advanced Frequency Analyzer")
            sheetSetup.write("A3", "Template", bold);                         sheetSetup.write("B3", "Frequency Analyzer")
            sheetSetup.write("A4", "Frequency range", bold);                  sheetSetup.write("B4", '%.2f Hz to %.2f kHz'%(params['fstart'], np.round(params['fend']/1000, 2)))
            sheetSetup.write("A5", "Octave band", bold);                      sheetSetup.write("B5", '1/%d'%params['b'])
            sheetSetup.write("A6", "Duration", bold);                         sheetSetup.write("B6", seconds2HMS(params['duration']))
            sheetSetup.write("A7", "Frequency weighting", bold);              sheetSetup.write("B7", params['fweighting'])
            sheetSetup.write("A8", "Integration time", bold);                 sheetSetup.write("B8", strTau)
            sheetSetup.write("A9", "Sampling rate", bold);                    sheetSetup.write("B9",'%d Hz'%params['fs'])
            sheetSetup.write("A10", "Microphone sensitivity", bold);          sheetSetup.write("B10", '%.2f mV/Pa'%params['sensitivity'])
            sheetSetup.write("A11", "Correction", bold);                      sheetSetup.write("B11", '%.2f dB'%params['corrFactor'])
            sheetSetup.write("A12", "Adjustment factor", bold);               sheetSetup.write("B12", '%.2f'%params['calibFactor'])
            sheetSetup.write("A13", "Reference pressure", bold);              sheetSetup.write("B13", '%d dB'%params['pCalib'])
            sheetSetup.write("A14", "Reference frequency", bold);             sheetSetup.write("B14", '%d Hz'%params['fCalib'])
            sheetSetup.write("A15", "Raw data", bold);                        sheetSetup.write("B15", str(file_audio))
            sheetSetup.write("A16", "Project", bold);                         sheetSetup.write("B16", params['currentProject'])
            sheetSetup.write("A17", "Project path", bold);                    sheetSetup.write("B17", os.path.join(params['pathProject'], params['currentProject']))
            sheetSetup.write("A18", "Microphone correction file", bold);      sheetSetup.write("B18", params['micCorrFile'])
            sheetSetup.write("A19", "Was the mic correction applied?", bold); sheetSetup.write("B19", 'Yes' if params['applyMicCorr']==True else 'No')
            sheetSetup.write("A20", "ADC correction file", bold);             sheetSetup.write("B20", params['adcCorrFile'])
            sheetSetup.write("A21", "Was the ADC correction applied?", bold); sheetSetup.write("B21", 'Yes' if params['applyAdcCorr']==True else 'No')
            sheetSetup.write("A22", "Start of measurement", bold);            sheetSetup.write("B22", timestamp['play'])
            sheetSetup.write("A23", "End of measurement", bold);              sheetSetup.write("B23", timestamp['stop'])

            # Results (sheet Results)
            # Frequency Analysis
            if params['b'] == 1:
                sheetResults.merge_range(0,0,0,2*results['bands'].size, 'Frequency Analysis', formatSection)
            else:
                sheetResults.merge_range(0,0,0,results['bands'].size, 'Frequency Analysis', formatSection)
            sheetResults.set_column(0,0,15,bold)
            sheetResults.set_row(0,15,bold)
            sheetResults.write("A2", "Frequency Hz", formatInformationT)
            sheetResults.write("A3", "L{},{},Min       dB".format(params['fweighting'], strTau[0]), formatInformationT)
            sheetResults.write("A4", "L{},{},Max      dB".format(params['fweighting'], strTau[0]), formatInformationT)
            sheetResults.write("A5", "L{}eq,{}           dB".format(params['fweighting'], strTau[0]), formatInformationT)
            for i in range(len(strBands)):
                sheetResults.write(1, i+1, strBands[i])
                sheetResults.write(2, i+1, results['L_min_bands'][i])
                sheetResults.write(3, i+1, results['L_max_bands'][i])
                sheetResults.write(4, i+1, results['Leq_bands'][i])

            # Bar graphs
            plotOctave = workbook.add_chart({'type': 'column'})
            plotOctave.add_series({
                'name': ['Results', 2,0],
                'categories': ['Results', 1,1,0,results['bands'].size],
                'values': ['Results', 2,1,2,results['bands'].size]
                })
            plotOctave.add_series({
                'name': ['Results', 3,0],
                'categories': ['Results', 1,1,0,results['bands'].size],
                'values': ['Results', 3,1,3,results['bands'].size]
                })
            plotOctave.add_series({
                'name': ['Results', 4,0],
                'categories': ['Results', 1,1,0,results['bands'].size],
                'values': ['Results', 4,1,4,results['bands'].size]
                })
            plotOctave.set_title({'name': 'Sound pressure level measured in 1/%d octave bands'%params['b']})
            plotOctave.set_x_axis({'name': 'Frequency Hz'})
            plotOctave.set_y_axis({'name': 'Level dB'})
            # plotOctave.set_style(15)
            if params['b'] == 1:
                sheetResults.insert_chart(5,2, plotOctave, {'x_offset': 0, 'y_offset': 0, 'x_scale': 2, 'y_scale': 1.4})
            else:
                sheetResults.insert_chart(5,2, plotOctave, {'x_offset': 0, 'y_offset': 0, 'x_scale': 4, 'y_scale': 1.4})

            # Time Analysis
            # sheetResults.set_column(1,1,15)
            if params['b'] == 1:
                sheetResults.merge_range(27,0,27,2*results['bands'].size, 'Time Analysis', formatSection)
            else:
                sheetResults.merge_range(27,0,27,results['bands'].size, 'Time Analysis', formatSection)
            sheetResults.merge_range(28,0,29,1,"L{},{}".format(params['fweighting'], strTau[0]), formatInformationT)
            sheetResults.write(30, 0, 'Time         s', formatInformationT)
            sheetResults.write_column(31,0, time)
            sheetResults.write(30, 1, 'Level dB', formatInformationT)
            sheetResults.write_column(31,1, results['Lglobal'])
            # Inline graphics
            plotLine = workbook.add_chart({'type': 'line'})
            plotLine.add_series({
                'name': ['Results', 28,0],
                'categories': ['Results', 31, 0, time.size, 0],
                'values': ['Results', 31, 1, time.size, 1]
                })
            plotLine.set_title({'name': 'Sound pressure level measured in {} weighting'.format(strTau)})
            plotLine.set_x_axis({'name': 'Time s'})
            plotLine.set_y_axis({'name': "L{},{} dB".format(params['fweighting'], strTau[0])})
            # plotLine.set_style(10)
            if params['b'] == 1:
                sheetResults.insert_chart(28, 2, plotLine, {'x_offset': 0, 'y_offset': 0, 'x_scale': 2, 'y_scale': 1.5})
            else:
                sheetResults.insert_chart(28, 2, plotLine, {'x_offset': 0, 'y_offset': 0, 'x_scale': 4, 'y_scale': 1.5})
            sheetResults.write("K51", "L{}eq,{}".format(params["fweighting"], strTau[0]), formatInformationT)
            sheetResults.write("K52", "{} dB".format(results["Leq_global"]))
            sheetResults.write("M51", "LAE/SEL", formatInformationT)
            sheetResults.write("M52", "{} dB".format(results["SEL"]))
            sheetResults.write("O51", "Lmax", formatInformationT)
            sheetResults.write("O52", "{} dB".format(results["Lmax"]))
            sheetResults.write("Q51", "Lmin", formatInformationT)
            sheetResults.write("Q52", "{} dB".format(results["Lmin"]))
            sheetResults.write("K54", "Lpeak", formatInformationT)
            sheetResults.write("K55", "{} dB".format(results["Lpeak"]))
            sheetResults.write("M54", "L10", formatInformationT)
            sheetResults.write("M55", "{} dB".format(results["L10"]))
            sheetResults.write("O54", "L50", formatInformationT)
            sheetResults.write("O55", "{} dB".format(results["L50"]))
            sheetResults.write("Q54", "L90", formatInformationT)
            sheetResults.write("Q55", "{} dB".format(results["L90"]))

        else: # Reverberation time
            # Excitement signal labels
            if params['method'] == 'pinkNoise':
                method = "Pink noise"
            elif params['method'] == 'whiteNoise':
                method = "White noise"
            elif params['method'] == 'sweepExponential':
                method = "Sweep exponential"
            else:
                method = "Impulse"
            # Measurement parameters (sheet Setup)
            sheetSetup.set_column(0,0,30,bold)
            sheetSetup.merge_range(0,0,0,10, 'Measurement parameters', formatSection)
            sheetSetup.write("A2", "Version of software", bold);              sheetSetup.write("B2", "Advanced Frequency Analyzer")
            sheetSetup.write("A3", "Template", bold);                         sheetSetup.write("B3", "Reverberation time")
            sheetSetup.write("A4", "Frequency range", bold);                  sheetSetup.write("B4", '%.2f Hz to %.2f kHz'%(params['fstart'], np.round(params['fend']/1000, 2)))
            sheetSetup.write("A5", "Frequency weighting", bold);              sheetSetup.write("B5", params['fweighting'])
            sheetSetup.write("A6", "Integration time", bold);                 sheetSetup.write("B6", strTau)
            sheetSetup.write("A7", "Octave band", bold);                      sheetSetup.write("B7", '1/%d'%params['b'])
            sheetSetup.write("A8", "Excitement signal", bold);                sheetSetup.write("B8", method)
            sheetSetup.write("A9", "Excitation time", bold);                  sheetSetup.write("B9", seconds2MS(params['excitTime']))
            sheetSetup.write("A10", "Decay time", bold);                      sheetSetup.write("B10", "%i s" % params['decayTime'])
            sheetSetup.write("A11", "Number of decays", bold);                sheetSetup.write("B11", "%i" % params['numDecay'])
            sheetSetup.write("A12", "Trigger level", bold);                   sheetSetup.write("B12", "%i dB" % params['numDecay'] if method == 'impulse' else "-- dB")
            sheetSetup.write("A13", "Escape time", bold);                     sheetSetup.write("B14", seconds2MS(params['scapeTime']))
            sheetSetup.write("A14", "Sampling rate", bold);                   sheetSetup.write("B14",'%d Hz'%params['fs'])
            sheetSetup.write("A15", "Microphone sensitivity", bold);          sheetSetup.write("B15", '%.2f mV/Pa'%params['sensitivity'])
            sheetSetup.write("A16", "Correction", bold);                      sheetSetup.write("B16", '%.2f dB'%params['corrFactor'])
            sheetSetup.write("A17", "Adjustment factor", bold);               sheetSetup.write("B17", '%.2f'%params['calibFactor'])
            sheetSetup.write("A18", "Reference pressure", bold);              sheetSetup.write("B18", '%d dB'%params['pCalib'])
            sheetSetup.write("A19", "Reference frequency", bold);             sheetSetup.write("B19", '%d Hz'%params['fCalib'])
            sheetSetup.write("A20", "Raw data", bold);                        sheetSetup.write("B20", str(file_audio))
            sheetSetup.write("A21", "Project", bold);                         sheetSetup.write("B21", params['currentProject'])
            sheetSetup.write("A22", "Project path", bold);                    sheetSetup.write("B22", os.path.join(params['pathProject'], params['currentProject']))
            sheetSetup.write("A23", "Microphone correction file", bold);      sheetSetup.write("B23", params['micCorrFile'])
            sheetSetup.write("A24", "Was the mic correction applied?", bold); sheetSetup.write("B24", 'Yes' if params['applyMicCorr']==True else 'No')
            sheetSetup.write("A25", "ADC correction file", bold);             sheetSetup.write("B25", params['adcCorrFile'])
            sheetSetup.write("A26", "Was the ADC correction applied?", bold); sheetSetup.write("B26", 'Yes' if params['applyAdcCorr']==True else 'No')
            sheetSetup.write("A27", "Start of measurement", bold);            sheetSetup.write("B27", timestamp['play'])
            sheetSetup.write("A28", "End of measurement", bold);              sheetSetup.write("B28", timestamp['stop'])

            # Results (sheet Results)
            # Reverberation time (EDT, T15, T20, T30)
            if params['b'] == 1:
                sheetResults.merge_range(0,0,0,2*results['bands'].size, 'Reverberation time (EDT, T15, T20, T30)', formatSection)
            else:
                sheetResults.merge_range(0,0,0,results['bands'].size, 'Reverberation time (EDT, T15, T20, T30)', formatSection)
            sheetResults.set_column(0,0,15,bold)
            sheetResults.set_row(0,15,bold)
            sheetResults.write("A2", "Frequency Hz", formatInformationT)
            sheetResults.write("A3", "EDT   s", formatInformationT)
            sheetResults.write("A4", "T15   s", formatInformationT)
            sheetResults.write("A5", "T20   s", formatInformationT)
            sheetResults.write("A6", "T30   s", formatInformationT)
            for i in range(len(strBands)):
                sheetResults.write(1, i+1, strBands[i])
                sheetResults.write(2, i+1, results['EDT'][i])
                sheetResults.write(3, i+1, results['RT15'][i])
                sheetResults.write(4, i+1, results['RT20'][i])
                sheetResults.write(5, i+1, results['RT30'][i])

            # Bar graphs
            plotOctave_RT = workbook.add_chart({'type': 'column'})
            plotOctave_RT.add_series({
                'name': ['Results', 2,0],
                'categories': ['Results', 1,1,0,results['bands'].size],
                'values': ['Results', 2,1,2,results['bands'].size]
                })
            plotOctave_RT.add_series({
                'name': ['Results', 3,0],
                'categories': ['Results', 1,1,0,results['bands'].size],
                'values': ['Results', 3,1,3,results['bands'].size]
                })
            plotOctave_RT.add_series({
                'name': ['Results', 4,0],
                'categories': ['Results', 1,1,0,results['bands'].size],
                'values': ['Results', 4,1,4,results['bands'].size]
                })
            plotOctave_RT.add_series({
                'name': ['Results', 5,0],
                'categories': ['Results', 1,1,0,results['bands'].size],
                'values': ['Results', 5,1,5,results['bands'].size]
                })
            plotOctave_RT.set_title({'name': 'Reverberation time measured in 1/%d octave bands'%params['b']})
            plotOctave_RT.set_x_axis({'name': 'Frequency Hz'})
            plotOctave_RT.set_y_axis({'name': 'Time s'})

            # Definition (D50, D80)
            if params['b'] == 1:
                sheetResults.merge_range(27,0,27,2*results['bands'].size, 'Definition (D50, D80)', formatSection)
            else:
                sheetResults.merge_range(27,0,27,results['bands'].size, 'Definition (D50, D80)', formatSection)
            sheetResults.write("A29", "Frequency Hz", formatInformationT)
            sheetResults.write("A30", "D50   %", formatInformationT)
            sheetResults.write("A31", "D80   %", formatInformationT)
            for i in range(len(strBands)):
                sheetResults.write(28, i+1, strBands[i])
                sheetResults.write(29, i+1, results['D50'][i])
                sheetResults.write(30, i+1, results['D80'][i])

            # Bar graphs
            plotOctave_D = workbook.add_chart({'type': 'column'})
            plotOctave_D.add_series({
                'name': ['Results', 29,0],
                'categories': ['Results', 28,1,28,results['bands'].size],
                'values': ['Results', 29,1,29,results['bands'].size]
                })
            plotOctave_D.add_series({
                'name': ['Results', 30,0],
                'categories': ['Results', 28,1,28,results['bands'].size],
                'values': ['Results', 30,1,30,results['bands'].size]
                })
            plotOctave_D.set_title({'name': 'Definition measured in 1/%d octave bands'%params['b']})
            plotOctave_D.set_x_axis({'name': 'Frequency Hz'})
            plotOctave_D.set_y_axis({'name': 'Definition %'})

            # Clarity (C50, C80)
            if params['b'] == 1:
                sheetResults.merge_range(54,0,54,2*results['bands'].size, 'Clarity (C50, C80)', formatSection)
            else:
                sheetResults.merge_range(54,0,54,results['bands'].size, 'Clarity (C50, C80)', formatSection)
            sheetResults.write("A56", "Frequency Hz", formatInformationT)
            sheetResults.write("A57", "C50   dB", formatInformationT)
            sheetResults.write("A58", "C80   dB", formatInformationT)
            for i in range(len(strBands)):
                sheetResults.write(55, i+1, strBands[i])
                sheetResults.write(56, i+1, results['C50'][i])
                sheetResults.write(57, i+1, results['C80'][i])

            # Bar graphs
            plotOctave_C = workbook.add_chart({'type': 'column'})
            plotOctave_C.add_series({
                'name': ['Results', 56,0],
                'categories': ['Results', 55,1,55,results['bands'].size],
                'values': ['Results', 56,1,56,results['bands'].size]
                })
            plotOctave_C.add_series({
                'name': ['Results', 57,0],
                'categories': ['Results', 55,1,55,results['bands'].size],
                'values': ['Results', 57,1,57,results['bands'].size]
                })
            plotOctave_C.set_title({'name': 'Clarity measured in 1/%d octave bands'%params['b']})
            plotOctave_C.set_x_axis({'name': 'Frequency Hz'})
            plotOctave_C.set_y_axis({'name': 'Clarity dB'})
            if params['b'] == 1:
                sheetResults.insert_chart(6,2, plotOctave_RT, {'x_offset': 0, 'y_offset': 0, 'x_scale': 2, 'y_scale': 1.4})
                sheetResults.insert_chart(31,2, plotOctave_D, {'x_offset': 0, 'y_offset': 0, 'x_scale': 2, 'y_scale': 1.4})
                sheetResults.insert_chart(58,2, plotOctave_C, {'x_offset': 0, 'y_offset': 0, 'x_scale': 2, 'y_scale': 1.4})
            else:
                sheetResults.insert_chart(6,2, plotOctave_RT, {'x_offset': 0, 'y_offset': 0, 'x_scale': 4, 'y_scale': 1.4})
                sheetResults.insert_chart(31,2, plotOctave_D, {'x_offset': 0, 'y_offset': 0, 'x_scale': 4, 'y_scale': 1.4})
                sheetResults.insert_chart(58,2, plotOctave_C, {'x_offset': 0, 'y_offset': 0, 'x_scale': 4, 'y_scale': 1.4})
    else: # DataLogger
        # Time vector
        time = np.arange(results['Lglobal'].size) * params['tau']

        # Time weighting labels
        if params['tau'] == 0.035:
            strTau = 'Impulse'
        elif params['tau'] == 0.125:
            strTau = 'Fast'
        else:
            strTau = 'Slow'

        # Creating the spreadsheet
        workbook = xlsxwriter.Workbook(file_name)
        bold = workbook.add_format({'bold': True})
        sheetResults = workbook.add_worksheet(name="Results")
        sheetSetup      = workbook.add_worksheet(name="Settings")

        # Formatting templates
        formatSection = workbook.add_format({'bold': True, 'border': 4, 'align': 'center', 'valign': 'vcenter', 'fg_color': '#7030a0'})
        formatInformationT = workbook.add_format({'bold': True, 'border': 4, 'align': 'center', 'valign': 'vcenter', 'fg_color': '#CCCCCC'})

        # Measurement parameters (sheet Setup)
        sheetSetup.set_column(0,0,30,bold)
        sheetSetup.merge_range(0,0,0,10, 'Measurement parameters', formatSection)
        sheetSetup.write("A2", "Version of software", bold);              sheetSetup.write("B2", "Data Logger")
        sheetSetup.write("A3", "Template", bold);                         sheetSetup.write("B3", "Sounud Pressure Level")
        sheetSetup.write("A4", "Duration", bold);                         sheetSetup.write("B4", seconds2HMS(params['duration']))
        sheetSetup.write("A5", "Frequency weighting", bold);              sheetSetup.write("B5", params['fweighting'])
        sheetSetup.write("A6", "Integration time", bold);                 sheetSetup.write("B6", strTau)
        sheetSetup.write("A7", "Sampling rate", bold);                    sheetSetup.write("B7",'%d Hz'%params['fs'])
        sheetSetup.write("A8", "Microphone sensitivity", bold);           sheetSetup.write("B8", '%.2f mV/Pa'%params['sensitivity'])
        sheetSetup.write("A9", "Correction", bold);                       sheetSetup.write("B9", '%.2f dB'%params['corrFactor'])
        sheetSetup.write("A10", "Adjustment factor", bold);               sheetSetup.write("B10", '%.2f'%params['calibFactor'])
        sheetSetup.write("A11", "Reference pressure", bold);              sheetSetup.write("B11", '%d dB'%params['pCalib'])
        sheetSetup.write("A12", "Reference frequency", bold);             sheetSetup.write("B12", '%d Hz'%params['fCalib'])
        sheetSetup.write("A13", "Raw data", bold);                        sheetSetup.write("B13", str(file_audio))
        sheetSetup.write("A14", "Project", bold);                         sheetSetup.write("B14", params['currentProject'])
        sheetSetup.write("A15", "Project path", bold);                    sheetSetup.write("B15", os.path.join(params['pathProject'], params['currentProject']))
        sheetSetup.write("A16", "Microphone correction file", bold);      sheetSetup.write("B16", params['micCorrFile'] if params['micCorrFile'] is not None else 'None')
        sheetSetup.write("A17", "Was the mic correction applied?", bold); sheetSetup.write("B17", 'Yes' if params['applyMicCorr']==True else 'No')
        sheetSetup.write("A18", "ADC correction file", bold);             sheetSetup.write("B18", params['adcCorrFile'] if params['adcCorrFile'] is not None else 'None')
        sheetSetup.write("A19", "Was the ADC correction applied?", bold); sheetSetup.write("B19", 'Yes' if params['applyAdcCorr']==True else 'No')
        sheetSetup.write("A20", "Start of measurement", bold);            sheetSetup.write("B20", timestamp['play'])
        sheetSetup.write("A21", "End of measurement", bold);              sheetSetup.write("B21", timestamp['stop'])

        # Time Analysis
        sheetResults.set_column(0,0,15)
        sheetResults.merge_range(0,0,0,31, 'Time Analysis', formatSection)
        sheetResults.merge_range(1,0,2,1,"L{},{}".format(params['fweighting'], strTau[0]), formatInformationT)
        sheetResults.write(3, 0, 'Time         s', formatInformationT)
        sheetResults.write_column(4,0, time)
        sheetResults.write(3, 1, 'Level dB', formatInformationT)
        sheetResults.write_column(4,1, results['Lglobal'])

        # Inline graphics
        plotLine = workbook.add_chart({'type': 'line'})
        plotLine.add_series({
            'name': ['Results', 1,0],
            'categories': ['Results', 4, 0, time.size, 0],
            'values': ['Results', 4, 1, time.size, 1]
            })
        plotLine.set_title({'name': 'Sound pressure level measured in {} weighting'.format(strTau)})
        plotLine.set_x_axis({'name': 'Time s'})
        plotLine.set_y_axis({'name': "L{},{} dB".format(params['fweighting'], strTau[0])})
        # plotLine.set_style(10)
        sheetResults.insert_chart(1, 2, plotLine, {'x_offset': 0, 'y_offset': 0, 'x_scale': 4, 'y_scale': 1.5})
        sheetResults.write("N25", "L{}eq,{}".format(params["fweighting"], strTau[0]), formatInformationT)
        sheetResults.write("N26", "{} dB".format(results["Leq_global"]))
        sheetResults.write("P25", "LAE/SEL", formatInformationT)
        sheetResults.write("P26", "{} dB".format(results["SEL"]))
        sheetResults.write("R25", "Lmax", formatInformationT)
        sheetResults.write("R26", "{} dB".format(results["Lmax"]))
        sheetResults.write("T25", "Lmin", formatInformationT)
        sheetResults.write("T26", "{} dB".format(results["Lmin"]))
        sheetResults.write("N28", "Lpeak", formatInformationT)
        sheetResults.write("N29", "{} dB".format(results["Lpeak"]))
        sheetResults.write("P28", "L10", formatInformationT)
        sheetResults.write("P29", "{} dB".format(results["L10"]))
        sheetResults.write("R28", "L50", formatInformationT)
        sheetResults.write("R29", "{} dB".format(results["L50"]))
        sheetResults.write("T28", "L90", formatInformationT)
        sheetResults.write("T29", "{} dB".format(results["L90"]))
    workbook.close()
    return

def seconds2MS(seconds):
    M, S = divmod(seconds, 60)
    _, M = divmod(M, 60)
    return "%02dm%02ds" % (M, S)

def seconds2HMS(seconds):
    M, S = divmod(seconds, 60)
    H, M = divmod(M, 60)
    return "%02dh%02dm%02ds" % (H, M, S)


import numpy as np
from envs.babyai.oracle.teacher import Teacher

class PhysicalCorrections(Teacher):

    def empty_feedback(self):
        """
        Return a tensor corresponding to no feedback.
        """
        return np.array([])

    def random_feedback(self):
        """
        Return a tensor corresponding to no feedback.
        """
        hrand = np.random.randint(self.env.grid.height)
        wrand = np.random.randint(self.env.grid.width)
        self.env.agent_pos = np.array([hrand, wrand])
        return np.array([])

    def compute_feedback(self):
        """
        Return the expert action from the previous timestep.
        """
        # TODO: Unhardocde this
        # Hardcoded 1 time-step away
        # TODO: Check for off by one errors here.
        # Put in a signal that it has been moved
        if self.path is None:
            raise Exception('Path not found, blah')
        else:
            if len(self.path) > 1:
                feedback = self.path[1]
                self.env.agent_pos = np.array(feedback)
        return np.array([])

    def feedback_condition(self):
        """
        Returns true when we should give feedback.
        Currently returns true when the agent's past action did not match the oracle's action.
        """
        # For now, we're being lazy and correcting the agent any time it strays from the agent's optimal set of actions.
        # This is kind of sketchy since multiple paths can be optimal.

        return len(self.agent_actions) > 0 and (not self.agent_actions[-1] == self.oracle_actions[-1])


#! python3
# -*- coding: utf-8 -*-
from pylots.temixins import CompInterface, TEM
from wxpyJemacs import Layer
import numpy as np

class Plugin(CompInterface, Layer):
    """Plugin of compensation
    Adjust iscomp1-shift [alpha]
    """
    caption = "ISSHIFT"
    conf_key = 'compisshift'

    ## PYJEM には ISCOMP がないため legacy を直接使う
    ## ISCOMP には取得コマンドがないので，ここでは内部変数で覚えておく
    ## Notify が存在しないので，この値が装置の実際の値と一致するとは限らない
    __index = [0x8000, 0x8000]

    @property
    def index(self):
        return np.array(self.__index)

    @index.setter
    def index(self, v):
        self.__index = v
        self.tem.dsys.Write('IS1SHIFTX', v[0])
        self.tem.dsys.Write('IS1SHIFTY', v[1])

    wobbler = TEM.IS1

    spot = property(lambda self: self.parent.require('beam_spot'))
    deflector = property(lambda self: self.parent.require('beam_tilt'))
    ## deflector = property(lambda self: self.parent.require('align_pla'))

    def Init(self):
        CompInterface.Init(self)

        if self.config: # the config might be missing when plugging in
            self.__index = self.conf_table

    @property
    def conf_table(self):
        return self.config[self.conf_key][0]

    def cal(self):
        with self.thread:
            with self.save_excursion(mmode='DIFF'):
                ## self.index = [0x8000, 0x8000] # neutralize :reload
                self.spot.focus()
                return CompInterface.cal(self)

    def execute(self):
        with self.thread:
            with self.save_excursion(mmode='DIFF', mag=2000):
                return self.cal()


import os

import astropy.io.fits as fits
from astropy.convolution import Gaussian2DKernel, convolve_fft, convolve
import matplotlib.colors as colors
from matplotlib.patches import Ellipse
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np

try:
    import progressbar
except ImportError:
    print('WARNING: progressbar is not present')
from scipy import interpolate

from .parameters import Params, find_parameter_file
from .utils import FWHM_to_sigma, default_cmap, Wm2_to_Tb, Jy_to_Tb

class Line:

    _line_file = "lines.fits.gz"

    def __init__(self, dir=None, **kwargs):

        # Correct path if needed
        dir = os.path.normpath(os.path.expanduser(dir))
        self.dir = dir

        # Search for parameter file
        para_file = find_parameter_file(dir)

        # Read parameter file
        self.P = Params(para_file)

        # Read model results
        self._read(**kwargs)

    def _read(self):
        # Read ray-traced image
        try:
            hdu = fits.open(self.dir + "/" + self._line_file)
            self.lines = hdu[0].data
            # Read a few keywords in header
            self.pixelscale = hdu[0].header['CDELT2'] * 3600.0  # arcsec
            self.unit = hdu[0].header['BUNIT']
            self.cx = hdu[0].header['CRPIX1']
            self.cy = hdu[0].header['CRPIX2']
            self.nx = hdu[0].header['NAXIS1']
            self.ny = hdu[0].header['NAXIS2']
            self.nv = hdu[0].header['NAXIS3']

            if self.unit == "JY/PIXEL":
                self.is_casa = True
                self.restfreq = hdu[0].header['RESTFREQ']
                self.freq = [self.restfreq]
                self.velocity_type = hdu[0].header['CTYPE3']
                if self.velocity_type == "VELO-LSR":
                    self.CRPIX3 = hdu[0].header['CRPIX3']
                    self.CRVAL3 = hdu[0].header['CRVAL3']
                    self.CDELT3 = hdu[0].header['CDELT3']
                    self.velocity = self.CRVAL3 + self.CDELT3 * (
                        np.arange(1, self.nv + 1) - self.CRPIX3
                    )  # km/s

                else:
                    raise ValueError("Velocity type is not recognised")
            else:
                self.is_casa = False
                self.cont = hdu[1].data

                self.ifreq = hdu[2].data
                self.freq = hdu[3].data  # frequencies of the transition
                self.velocity = hdu[4].data / 1000  # km/s

            self.dv = self.velocity[1] - self.velocity[0]

            hdu.close()
        except OSError:
            print('cannot open', self._line_file)

    def plot_map(
        self,
        i=0,
        iaz=0,
        iTrans=0,
        v=None,
        iv=None,
        insert=False,
        substract_cont=False,
        moment=None,
        psf_FWHM=None,
        bmaj=None,
        bmin=None,
        bpa=None,
        plot_beam=None,
        axes_unit="arcsec",
        conv_method=None,
        fmax=None,
        fmin=None,
        fpeak=None,
        dynamic_range=1e3,
        color_scale=None,
        colorbar=True,
        cmap=None,
        ax=None,
        no_xlabel=False,
        no_ylabel=False,
        no_xticks=False,
        no_yticks=False,
        title=None,
        limit=None,
        limits=None,
        Tb=False,
        Delta_v=None,
    ):
        # Todo:
        # - allow user to change brightness unit : W.m-1, Jy, Tb
        # - print molecular info (eg CO J=3-2)
        # - add continnum subtraction
        # bmin and bamj in arcsec

        if ax is None:
            ax = plt.gca()

        # -- Selecting channel corresponding to a given velocity
        if v is not None:
            iv = np.abs(self.velocity - v).argmin()
            print("Selecting channel #", iv)

        # --- Compute pixel scale and extent of image
        if axes_unit.lower() == 'arcsec':
            pix_scale = self.pixelscale
            xlabel = r'$\Delta$ Ra ["]'
            ylabel = r'$\Delta$ Dec ["]'
        elif axes_unit.lower() == 'au':
            pix_scale = self.pixelscale * self.P.map.distance
            xlabel = 'Distance from star [au]'
            ylabel = 'Distance from star [au]'
        elif axes_unit.lower() == 'pixels' or axes_unit.lower() == 'pixel':
            pix_scale = 1
            xlabel = r'$\Delta$ x [pix]'
            ylabel = r'$\Delta$ y [pix]'
        else:
            raise ValueError("Unknown unit for axes_units: " + axes_unit)
        halfsize = np.asarray(self.lines.shape[-2:]) / 2 * pix_scale
        extent = [-halfsize[0], halfsize[0], -halfsize[1], halfsize[1]]

        # -- set color map
        if cmap is None:
            if moment == 1:
                cmap = "RdBu"
            else:
                cmap = default_cmap

        # -- beam or psf : psf_FWHM and bmaj and bmin are in arcsec, bpa in deg
        i_convolve = False
        beam = None
        if psf_FWHM is not None:
            sigma = (
                psf_FWHM / self.pixelscale * (2.0 * np.sqrt(2.0 * np.log(2)))
            )  # in pixels
            beam = Gaussian2DKernel(sigma)
            i_convolve = True
            bmin = psf_FWHM
            bmaj = psf_FWHM
            bpa = 0
            if plot_beam is None:
                plot_beam = True

        if bmaj is not None:
            sigma_x = bmin / self.pixelscale * FWHM_to_sigma  # in pixels
            sigma_y = bmaj / self.pixelscale * FWHM_to_sigma  # in pixels
            beam = Gaussian2DKernel(sigma_x, sigma_y, bpa * np.pi / 180)
            i_convolve = True
            if plot_beam is None:
                plot_beam = True

        # -- Selecting convolution function
        if conv_method is None:
            conv_method = convolve_fft

        # -- Selection of image to plot
        if moment is not None:
            im = self.get_moment_map(
                i=i,
                iaz=iaz,
                iTrans=iTrans,
                moment=moment,
                beam=beam,
                conv_method=conv_method,
            )
        else:
            # individual channel
            if self.is_casa:
                cube = self.lines[:, :, :]
                # im = self.lines[iv+1,:,:])
            else:
                cube = self.lines[i, iaz, iTrans, :, :, :]
                # im = self.lines[i,iaz,iTrans,iv,:,:]

                # -- continuum substraction
                if substract_cont:
                    cube = np.maximum(
                        cube - self.cont[i, iaz, iTrans, np.newaxis, :, :], 0.0
                    )

            # Convolve spectrally
            if Delta_v is not None:
                print("Spectral convolution at ", Delta_v, "km/s")
                # Creating a Hanning function with 101 points
                n_window = 101
                w = np.hanning(n_window)

                # For each pixel, resampling the spectrum between -FWHM to FWHM
                # then integrating over convolution window
                v_new = self.velocity[iv] + np.linspace(-1, 1, n_window) * Delta_v

                iv_min = int(iv - Delta_v / self.dv - 1)
                iv_max = int(iv + Delta_v / self.dv + 2)

                print(iv_min, iv_max, iv_max - iv_min)

                print(self.velocity[iv_min:iv_max])

                print(v_new)

                im = np.zeros([self.nx, self.ny])
                for j in range(self.ny):
                    for i in range(self.nx):
                        f = interpolate.interp1d(
                            self.velocity[iv_min:iv_max], cube[iv_min:iv_max, i, j]
                        )
                        im[i, j] = np.average(f(v_new))
            else:
                im = cube[iv, :, :]

            # -- Convolve image
            if i_convolve:
                im = conv_method(im, beam)
                if plot_beam is None:
                    plot_beam = True

            # -- Conversion to brightness temperature
            if Tb:
                if self.is_casa:
                    im = Jy_to_Tb(im, self.freq[iTrans], self.pixelscale)
                else:
                    im = Wm2_to_Tb(im, self.freq[iTrans], self.pixelscale)
                    im = np.nan_to_num(im)
                print("Max Tb=", np.max(im), "K")

        # --- Plot range and color map`
        _color_scale = 'lin'
        if fmax is None:
            fmax = im.max()
        if fpeak is not None:
            fmax = im.max() * fpeak
        if fmin is None:
            fmin = im.min()

        if color_scale is None:
            color_scale = _color_scale
        if color_scale == 'log':
            if fmin <= 0.0:
                fmin = fmax / dynamic_range
            norm = colors.LogNorm(vmin=fmin, vmax=fmax, clip=True)
        elif color_scale == 'lin':
            norm = colors.Normalize(vmin=fmin, vmax=fmax, clip=True)
        else:
            raise ValueError("Unknown color scale: " + color_scale)

        # -- Make the plot
        ax.cla()
        image = ax.imshow(im, norm=norm, extent=extent, origin='lower', cmap=cmap)

        if limit is not None:
            limits = [-limit, limit, -limit, limit]

        if limits is not None:
            ax.set_xlim(limits[0], limits[1])
            ax.set_ylim(limits[2], limits[3])

        if not no_xlabel:
            ax.set_xlabel(xlabel)
        if not no_ylabel:
            ax.set_ylabel(ylabel)

        if no_xticks:
            ax.get_xaxis().set_visible(False)
        if no_yticks:
            ax.get_yaxis().set_visible(False)

        if title is not None:
            ax.set_title(title)

        # -- Color bar
        unit = self.unit
        if colorbar:
            divider = make_axes_locatable(ax)
            cax = divider.append_axes("right", size="5%", pad=0.05)
            cb = plt.colorbar(image, cax=cax)
            formatted_unit = unit.replace("-1", "$^{-1}$").replace("-2", "$^{-2}$")

            if moment == 0:
                cb.set_label("Flux [" + formatted_unit + "km.s$^{-1}$]")
            elif moment == 1:
                cb.set_label("Velocity [km.s$^{-1}]$")
            elif moment == 2:
                cb.set_label("Velocity dispersion [km.s$^{-1}$]")
            else:
                if Tb:
                    cb.set_label("T$_\mathrm{b}$ [K]")
                else:
                    cb.set_label("Flux [" + formatted_unit + "]")

        # -- Adding velocity
        if moment is None:
            ax.text(
                0.5,
                0.1,
                f"$\Delta$v={self.velocity[iv]:<4.2f}$\,$km/s",
                horizontalalignment='center',
                color="white",
                transform=ax.transAxes,
            )

        # --- Adding beam
        if plot_beam:
            dx = 0.125
            dy = 0.125
            beam = Ellipse(
                ax.transLimits.inverted().transform((dx, dy)),
                width=bmin,
                height=bmaj,
                angle=bpa,
                fill=True,
                color="grey",
            )
            ax.add_patch(beam)

        return image

    def plot_line(
        self,
        i=0,
        iaz=0,
        iTrans=0,
        psf_FWHM=None,
        bmaj=None,
        bmin=None,
        bpa=None,
        plot_beam=False,
        plot_cont=True,
    ):

        if self.is_casa:
            line = np.sum(self.lines[:, :, :], axis=(1, 2))
            ylabel = "Flux [Jy]"
        else:
            line = np.sum(self.lines[i, iaz, iTrans, :, :, :], axis=(1, 2))
            ylabel = "Flux [W.m$^{-2}$]"

        plt.plot(self.velocity, line)

        if plot_cont:
            if self.is_casa:
                Fcont = 0.5 * (line[0] + line[-1])  # approx the continuum
            else:
                Fcont = np.sum(self.cont[i, iaz, iTrans, :, :])
            plt.plot([self.velocity[0], self.velocity[-1]], [Fcont, Fcont])

        xlabel = "v [m.s$^{-1}$]"

        plt.xlabel(xlabel)
        plt.ylabel(ylabel)

    def get_moment_map(
        self, i=0, iaz=0, iTrans=0, moment=0, beam=None, conv_method=None
    ):
        """
        This returns the moment maps in physical units, ie:
         - M1 is the average velocity [km/s]
         - M2 is the velocity dispersion [km/s]
        """
        if self.is_casa:
            cube = np.copy(self.lines[:, :, :])
        else:
            cube = np.copy(self.lines[i, iaz, iTrans, :, :, :])

        dv = self.velocity[1] - self.velocity[0]

        if beam is None:
            M0 = np.sum(cube, axis=0) * dv
        else:
            if moment == 0:
                M0 = np.sum(cube, axis=0) * dv
                M0 = conv_method(M0, beam)
            else:  # We need to convolve each channel indidually
                print(
                    "Convolving individual channel maps, this may take a bit of time ...."
                )
                try:
                    bar = progressbar.ProgressBar(
                        maxval=self.nv,
                        widgets=[
                            progressbar.Bar('=', '[', ']'),
                            ' ',
                            progressbar.Percentage(),
                        ],
                    )
                    bar.start()
                except:
                    pass
                for iv in range(self.nv):
                    try:
                        bar.update(iv + 1)
                    except:
                        pass
                    channel = np.copy(cube[iv, :, :])
                    cube[iv, :, :] = conv_method(channel, beam)
                    M0 = np.sum(cube, axis=0) * dv
                try:
                    bar.finish()
                except:
                    pass

        if moment >= 1:
            M1 = (
                np.sum(cube[:, :, :] * self.velocity[:, np.newaxis, np.newaxis], axis=0)
                * dv
                / M0
            )

        if moment == 2:
            M2 = np.sqrt(
                np.sum(
                    cube[:, :, :]
                    * (self.velocity[:, np.newaxis, np.newaxis] - M1[np.newaxis, :, :])
                    ** 2,
                    axis=0,
                )
                * dv
                / M0
            )

        if moment == 0:
            return M0
        elif moment == 1:
            return M1
        elif moment == 2:
            return M2

import numpy,from scipy,import astropy,from astropy


import numpy as np
import requests
import json
import re # re.findall() finds the required data in string, we will find the digits in strings

class MultiSimCheck:

    @staticmethod
    def validate_imeis_capacity(core_base_url, api_version, imeis_list):
        # api_version validation
        api_version_small = api_version.lower()
        if not api_version_small == 'api/v2':
            return {"msg": "API Version V2 is required"}
        api_response = []
        api_response.append(True)
        # each device IMEIs list and operations over it
        for device_imeis_rec in imeis_list:
            device_imeis = device_imeis_rec[0].split(',')

            # take imeis count to check for match in imeiquantitysupport, simslot and (removable_euicc,
            # nonremovable_euicc, removable_euicc, removable_uicc)
            imeis_count = str(len(device_imeis))
            ind_device_imeis = []

            # consider device imeis, fetch the tacs and make them unique
            for single_imei in device_imeis:
                ind_device_imeis.append(single_imei[0:8])

            # make the TACs unique
            device_unique_list = np.unique(np.array(ind_device_imeis))
            device_having_multiple_tacs = len(device_unique_list)
            same_model = []  # initiate same model list

            # loop through the TACs for each device. scenario-> a single device can have multiple TACs
            for tac in device_unique_list:
                str_imeis = " "

                # get GSMA record against each TAC and match them with IMEIs count given in list
                gsma_response = requests.get(core_base_url + api_version_small + '/tac/' + str(tac)).json()

                if gsma_response["gsma"] is None:
                    same_model.append("Nill")
                    api_response.append({"gsma_tac_response": "No record found for TAC:" + str(tac)})
                    api_response[0] = "False"
                else:
                    # make model list for similar model in case of multiple TACs for a single device instance
                    same_model.append(gsma_response["gsma"]["model_name"])

                    # check against simslots
                    sim_slots = gsma_response["gsma"]["simslot"].lower()
                    if not sim_slots == "not known":
                        # check if simslot matches with imeis_count
                        if not str(sim_slots) == imeis_count:
                            api_response.append({"simslot_mismatch": "GSMA record for TAC: " + str(
                                tac) + " shows: " + str(sim_slots) + " count, Provided IMEIs: " + str_imeis.join(
                                device_imeis) + " are: " + str(imeis_count)})
                            api_response[0] = "False"
                    else:  # if simslot is Not Known
                        imeiquantitysupport = gsma_response["gsma"]["imeiquantitysupport"].lower()
                        if not imeiquantitysupport == "not known":
                            # check if simslot matches with imeis_count
                            if not sim_slots == imeis_count:
                                api_response.append({"imeiquantitysupport_mismatch": "GSMA record for TAC: " + str(
                                    tac) + " shows: " + str(
                                    imeiquantitysupport) + " count, Provided IMEIs: " + str_imeis.join(
                                    device_imeis) + " are: " + str(imeis_count)})
                                api_response[0] = "False"
                        else:  # if simslot and imeiquantitysupport are Not Known
                            nonremovable_euicc = gsma_response["gsma"]["nonremovable_euicc"]
                            nonremovable_uicc = gsma_response["gsma"]["nonremovable_uicc"]
                            removable_euicc = gsma_response["gsma"]["removable_euicc"]
                            removable_uicc = gsma_response["gsma"]["removable_uicc"]
                            # get the digits part out of the string
                            nonremovable_euicc = list(map(int, re.findall(r'\d+', nonremovable_euicc)))
                            nonremovable_uicc = list(map(int, re.findall(r'\d+', nonremovable_uicc)))
                            removable_euicc = list(map(int, re.findall(r'\d+', removable_euicc)))
                            removable_uicc = list(map(int, re.findall(r'\d+', removable_uicc)))
                            if nonremovable_euicc:
                                int_nonremovable_euicc = nonremovable_euicc[0]
                            else:
                                int_nonremovable_euicc = 0
                            if nonremovable_uicc:
                                int_nonremovable_uicc = nonremovable_uicc[0]
                            else:
                                int_nonremovable_uicc = 0
                            if removable_euicc:
                                int_removable_euicc = removable_euicc[0]
                            else:
                                int_removable_euicc = 0
                            if removable_uicc:
                                int_removable_uicc = removable_uicc[0]
                            else:
                                int_removable_uicc = 0
                            total_sim_capacity = sum((
                                                     int_nonremovable_euicc, int_nonremovable_uicc, int_removable_euicc,
                                                     int_removable_uicc))
                            # combo_euicc_uicc = [None]
                            combo_euicc_uicc = str(total_sim_capacity)
                            # get the total sim capacity and check it with the number of device_imeis and
                            if total_sim_capacity > 0:
                                if combo_euicc_uicc == imeis_count:
                                    pass  # condition met
                                else:
                                    if not int(imeis_count) == int(combo_euicc_uicc):
                                        api_response.append({"combo_euicc_uicc_mismatch": "GSMA record for TAC: " + str(
                                            tac) + " shows: " + str(
                                            combo_euicc_uicc) + " count, Provided IMEIs: " + str_imeis.join(
                                            device_imeis) + " are: " + str(imeis_count)})
                                        api_response[0] = "False"
                                    pass
                            else:
                                if int(imeis_count) > 2:
                                    api_response.append({"combo_euicc_uicc_mismatch": "GSMA record for TAC: " + str(
                                        tac) + " shows: " + str(
                                        combo_euicc_uicc) + " count, Provided IMEIs: " + str_imeis.join(
                                        device_imeis) + " are: " + str(imeis_count)})
                                    api_response[0] = "False"
                                else:
                                    pass

            unique_model_list = np.unique(np.array(same_model))
            unique_model_list_len = len(device_unique_list)

            if int(unique_model_list_len) > 1:
                string_model_list = " & "
                api_response.append({"same_model_mismatch": "Provided IMEIs: " + str_imeis.join(
                    device_imeis) + " are not of the same model. GSMA models for provided IMEIs are: (" + string_model_list.join(
                    unique_model_list) + ")"})
                api_response[0] = "False"

        return (api_response)