import numpy as np
import argparse
import time
import cv2
import os
ap = argparse.ArgumentParser()
ap.add_argument('-i','--image',required=True,help='path to input image')
ap.add_argument('-y','--yolo',required=True,help='base path to YOLO directory')
ap.add_argument('-c','--confidence',type=float,default=0.5,help='minimum probability to filter weak detections')
ap.add_argument('-t','--threashold',type=float,default=0.3,help="threshold when applying non-maxima suppression")

args = vars(ap.parse_args())

labelsPath = os.path.sep.join([args['yolo'],"coco.names"])
LABELS = open(labelsPath).read().strip().split('\n')

np.random.seed(42)
COLORS = np.random.randint(0,255,size=(len(LABELS),3),dtype="uint8")

weightsPath = os.path.sep.join([args['yolo'],'yolov3.weights'])
configPath = os.path.sep.join([args['yolo'],"yolov3.cfg"])

print('[INFO] loading YOLO from disk...')
net = cv2.dnn.readNetFromDarknet(configPath,weightsPath)

image = cv2.imread(args['image'])
(H,W) = image.shape[:2]

ln = net.getLayerNames()
ln = [ln[i[0] - 1] for i in net.getUnconnectedOutLayers()]

blob = cv2.dnn.blobFromImage(image, 1/255.0,(416,416),swapRB=True,crop=False)
net.setInput(blob)
start = time.time()
layerOutputs = net.forward(ln)
end = time.time()

print('[INFO] YOLO took {:.6f} seconds'.format(end - start))

boxes = []
confidences = []
classIDs = []

for output in layerOutputs:
    for detection in output:
        scores = detection[5:]
        classID = np.argmax(scores)
        confidence = scores[classID]

        if confidence > args['confidence']:
            box = detection[0:4] * np.array([W,H,W,H])
            (centerx,centerY,width,height) = box.astype('int')

            x = int(centerx - (width / 2))
            y = int(centerY - (height / 2))

            boxes.append([x,y,int(width),int(height)])
            confidences.append(float(confidence))
            classIDs.append(classID)

idxs = cv2.dnn.NMSBoxes(boxes,confidences,args["confidence"],args['threshold'])

if len(idxs) > 0:
    for i in idxs.flatten():
        (x,y) = (boxes[i][0],boxes[i][1])
        (w,h) = (boxes[i][2],boxes[i][3])

        color = [int(c) for c in COLORS[classIDs[i]]]
        cv2.rectangle(image,(x,y),(x+w,y+h),color,2)
        text = "{}:{:4f}".format(LABELS[classIDs[i]],confidences[i])
        cv2.putText(image,text,(x,y-5),cv2.FONT_HERSHEY_COMPLEX,0.5,color,2)

cv2.imshow("Image",image)
cv2.waitKey(0)


"""
Handles writing process output into a pyramid of NumPy array files.

output configuration parameters
-------------------------------

mandatory
~~~~~~~~~

bands: integer
    number of output bands to be written
path: string
    output directory
dtype: string
    numpy datatype
ndim: integer
    number of dimensions
"""

import os
import numpy as np
import numpy.ma as ma

from mapchete.formats import base
from mapchete.tile import BufferedTile
from mapchete.io.raster import prepare_array, extract_from_tile

class OutputData(base.OutputData):
    """
    Template class handling process output data.

    Parameters
    ----------
    output_params : dictionary
        output parameters from Mapchete file

    Attributes
    ----------
    path : string
        path to output directory
    file_extension : string
        file extension for output files (.tif)
    output_params : dictionary
        output parameters from Mapchete file
    nodata : integer or float
        nodata value used when writing GeoTIFFs
    pixelbuffer : integer
        buffer around output tiles
    pyramid : ``tilematrix.TilePyramid``
        output ``TilePyramid``
    crs : ``rasterio.crs.CRS``
        object describing the process coordinate reference system
    srid : string
        spatial reference ID of CRS (e.g. "{'init': 'epsg:4326'}")
    """

    METADATA = {
        "driver_name": "NumPy",
        "data_type": "raster",
        "mode": "rw"
    }

    def __init__(self, output_params):
        """Initialize."""
        super(OutputData, self).__init__(output_params)
        self.path = output_params["path"]
        self.file_extension = ".np"
        self.output_params = output_params
        try:
            self.nodata = output_params["nodata"]
        except KeyError:
            self.nodata = 0
        try:
            self.single_file = output_params["single_file"]
        except KeyError:
            self.single_file = True
        if self.single_file == False:
            raise NotImplementedError()

    def read(self, output_tile):
        """
        Read existing process output.

        Parameters
        ----------
        output_tile : ``BufferedTile``
            must be member of output ``TilePyramid``

        Returns
        -------
        process output : ``BufferedTile`` with appended data
        """
        try:
            output_tile.data = np.load(self.get_path(output_tile))
        except IOError:
            output_tile.data = self.emtpy(output_tile)
        return output_tile

    def write(self, process_tile):
        """
        Write data from process tiles into NumPy array dumps.

        Parameters
        ----------
        process_tile : ``BufferedTile``
            must be member of process ``TilePyramid``
        """
        if not os.path.exists(self.path):
            os.makedirs(self.path)
        process_tile.data = prepare_array(
            process_tile.data, masked=True, nodata=self.nodata,
            dtype=self.output_params["dtype"]
        )
        # Convert from process_tile to output_tiles
        for tile in self.pyramid.intersecting(process_tile):
            out_tile = BufferedTile(tile, self.pixelbuffer)
            out_data = extract_from_tile(process_tile, out_tile)
            self.prepare_path(tile)
            out_data.dump(self.get_path(out_tile))

    def tiles_exist(self, process_tile):
        """
        Check whether all output tiles of a process tile exist.

        Parameters
        ----------
        process_tile : ``BufferedTile``
            must be member of process ``TilePyramid``

        Returns
        -------
        exists : bool
        """
        return all(
            os.path.exists(self.get_path(tile))
            for tile in self.pyramid.intersecting(process_tile)
        )

    def is_valid_with_config(self, config):
        """
        Check if output format is valid with other process parameters.

        Parameters
        ----------
        config : dictionary
            output configuration parameters

        Returns
        -------
        is_valid : bool
        """
        assert isinstance(config, dict)
        assert "bands" in config
        assert isinstance(config["bands"], int)
        assert "path" in config
        assert isinstance(config["path"], str)
        assert "dtype" in config
        assert isinstance(config["dtype"], str)
        assert "ndim" in config
        assert isinstance(config["ndim"], int)
        return True

    def get_path(self, tile):
        """
        Determine target file path.

        Parameters
        ----------
        tile : ``BufferedTile``
            must be member of output ``TilePyramid``

        Returns
        -------
        path : string
        """
        return os.path.join(*[
            self.path, str(tile.zoom), str(tile.row),
            str(tile.col) + self.file_extension]
        )

    def prepare_path(self, tile):
        """
        Create directory and subdirectory if necessary.

        Parameters
        ----------
        tile : ``BufferedTile``
            must be member of output ``TilePyramid``
        """
        try:
            os.makedirs(os.path.dirname(self.get_path(tile)))
        except OSError:
            pass

    def empty(self, process_tile):
        """
        Return empty data.

        Parameters
        ----------
        process_tile : ``BufferedTile``
            must be member of process ``TilePyramid``

        Returns
        -------
        empty data : array
            empty array with data type provided in output profile
        """
        return ma.masked_array(
            data=np.array(
                np.full(
                    (self.output_params["bands"], ) + process_tile.shape,
                    self.nodata,
                    dtype=self.output_params["dtype"]),
                ndmin=self.output_params["ndim"]
            ),
            mask=True
        )

    def open(self, tile, process, **kwargs):
        """
        Open process output as input for other process.

        Parameters
        ----------
        tile : ``Tile``
        process : ``MapcheteProcess``
        kwargs : keyword arguments
        """
        try:
            resampling = kwargs["resampling"]
        except KeyError:
            resampling = None
        return InputTile(tile, process, resampling)

class InputTile(base.InputTile):
    """
    Target Tile representation of input data.

    Parameters
    ----------
    tile : ``Tile``
    process : ``MapcheteProcess``
    resampling : string
        rasterio resampling method

    Attributes
    ----------
    tile : ``Tile``
    process : ``MapcheteProcess``
    resampling : string
        rasterio resampling method
    pixelbuffer : integer
    """

    def __init__(self, tile, process, resampling):
        """Initialize."""
        self.tile = tile
        self.process = process
        self.pixelbuffer = None
        self.resampling = resampling
        self._np_cache = None

    def read(self):
        """
        Read reprojected & resampled input data.

        Parameters
        ----------
        indexes : integer or list
            band number or list of band numbers

        Returns
        -------
        data : array
        """
        if self._np_cache is None:
            tile = self.process.get_raw_output(self.tile)
            self._np_cache = tile.data
        return self._np_cache

    def is_empty(self):
        """
        Check if there is data within this tile.

        Returns
        -------
        is empty : bool
        """
        # empty if tile does not intersect with file bounding box
        src_bbox = self.process.config.process_area()
        tile_geom = self.tile.bbox
        if not tile_geom.intersects(src_bbox):
            return True

        # empty if source is empty
        return self.read().mask.all()

    def __enter__(self):
        """Enable context manager."""
        return self

    def __exit__(self, t, v, tb):
        """Clear cache on close."""
        del self._np_cache


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright 2020 Project U-Ray Authors
#
# Use of this source code is governed by a ISC-style
# license that can be found in the LICENSE file.
#
# SPDX-License-Identifier: ISC

import csv
import numpy as np
import sys

# LIMITED_STANDARDS is set to use fewer IOSTANDARD's initially to reduce
# required sample counts.
from utils.io_utils import bank_planner2, read_io_pins, \
        choose_iobank_voltage_uniform_by_iostandard, \
        primatives_for_bank, \
        iostandards_for_bank_and_voltage, \
        DRIVES, \
        SINGLE_ENDED_STANDARDS

def print_top(seed, fout=sys.stdout, offset=0):
    np.random.seed(0)

    pins = []
    bank_to_pins = {}
    bank_to_iotype = {}
    site_to_pin = {}
    pin_to_pin_idx = {}
    pin_to_tiles = {}

    with open("../iopins.csv", "r") as iof:
        bank_type_to_tiles, tile_to_pins, pin_to_banks, pin_to_site_offsets, pin_to_tile_types = read_io_pins(
            iof)

    with open("../iopins.csv", "r") as iof:
        for row in csv.DictReader(iof):
            pin = row['pin']
            bank = int(row['bank'])
            func = row['pin_function']
            site_name = row['site_name']
            site_type = row['site_type']
            tile = row['tile']

            if "VREF" in func:
                continue  # conflict

            if "VRP" in func or "VRN" in func:
                continue  # conflict

            pins.append((pin, bank, func, site_name, site_type))
            pin_to_pin_idx[pin] = len(pins) - 1

            if bank not in bank_to_pins:
                bank_to_pins[bank] = []
            bank_to_pins[bank].append(pin)

            if bank not in bank_to_iotype:
                bank_to_iotype[bank] = site_type.split("_")[0]
            site_to_pin[site_name] = pin

            assert pin not in pin_to_tiles
            pin_to_tiles[pin] = tile

    used_pins = []
    io_config = []

    bank_to_vcc = {}
    bank_pod_used = set()
    bank_pod_not_used = set()
    lut_inputs = []
    lut_outputs = []
    bank_iref = {}
    pin_to_bank_type = {}

    def inp():
        sig = "li[%d]" % len(lut_inputs)
        lut_inputs.append(sig)
        return sig

    def outp():
        sig = "lo[%d]" % len(lut_outputs)
        lut_outputs.append(sig)
        return sig

    def maybe_inp(pin):
        return inp() if np.random.choice([True, False]) else ""

    def maybe_outp(pin):
        if pin_to_bank_type[pin] == 'HDIOB' and (offset % 2) == 1:
            return ""
        else:
            return outp() if np.random.choice([True, False]) else ""

    bank_type_plan = {}

    bank_plan_idx = {}

    for bank_type in sorted(bank_type_to_tiles):
        pins_in_bank_type = []
        for tile in bank_type_to_tiles[bank_type]:
            for pin in tile_to_pins[tile]:
                if pin not in pin_to_pin_idx:
                    continue

                pins_in_bank_type.append(pin)

                assert pin not in pin_to_bank_type
                pin_to_bank_type[pin] = bank_type

        for idx, plan in enumerate(
                bank_planner2(
                    random_lib=np.random,
                    bank_type=bank_type,
                    single_ended=True,
                    pins=pins_in_bank_type,
                    pin_to_banks=pin_to_banks,
                    pin_to_site_offsets=pin_to_site_offsets,
                    pin_to_tile_types=pin_to_tile_types,
                    pin_to_tiles=pin_to_tiles,
                )):

            if bank_type == 'HDIOB':
                if idx == (offset // 2):
                    bank_plan_idx[bank_type] = idx
                    bank_type_plan[bank_type] = dict(plan)
            else:
                if idx == offset:
                    bank_plan_idx[bank_type] = idx
                    bank_type_plan[bank_type] = dict(plan)

    np.random.seed(seed)
    bank_to_vcc = choose_iobank_voltage_uniform_by_iostandard(
        bank_to_iotype, SINGLE_ENDED_STANDARDS, DRIVES, np.random)

    for bank_type in sorted(bank_plan_idx):
        print('// {}[{:03d}]'.format(bank_type, bank_plan_idx[bank_type]))

    for (pin, bank, func, sn, st) in pins:
        iot = st.split("_")[0]
        bank_type = iot

        if bank_type in bank_type_plan:
            assert pin in bank_type_plan[bank_type], (
                pin, bank_type_plan[bank_type].keys())
            config = bank_type_plan[bank_type][pin]
            if config is None:
                print('// {} X_OFFSET="{}"'.format(pin,
                                                   pin_to_site_offsets[pin]))
                continue
            _, ios, prim, params = config

            print('// {} X_OFFSET="{}" {} {} {}'.format(
                pin, pin_to_site_offsets[pin], ios, prim, bank))

            params["IOSTANDARD"] = ios
            params["prim"] = prim
        else:
            params = {}
            ios = np.random.choice(
                iostandards_for_bank_and_voltage(
                    iot, bank_to_vcc[bank], single_ended=True))
            if bank in bank_pod_used and ios in ("HSTL_I_12", "HSTL_I_DCI_12",
                                                 "SSTL12", "SSTL12_DCI",
                                                 "HSUL_12", "HSUL_12_DCI"):
                ios = "LVCMOS12"
            if bank in bank_pod_not_used and ios in ("POD12", "POD12_DCI"):
                ios = "LVCMOS12"
            params["IOSTANDARD"] = ios
            prim = np.random.choice(
                primatives_for_bank(bank_type, single_ended=True))
            params["prim"] = prim
            print('//', pin, ios, bank_type, prim)

            if prim in ("OBUF", "OBUFT", "IOBUF", "IOBUFE3"):
                if (iot, ios) in DRIVES:
                    params["DRIVE"] = np.random.choice(DRIVES[iot, ios])
                if iot == "HPIOB":
                    params["SLEW"] = np.random.choice(
                        ["SLOW", "MEDIUM", "FAST"])
                else:
                    params["SLEW"] = np.random.choice(["SLOW", "FAST"])

        if prim in ("IBUF", "IOBUF", "IOBUFE3", "IBUF_IBUFDISABLE",
                    "IBUF_INTERMDISABLE"):
            params["PULLTYPE"] = np.random.choice(
                ["NONE", "PULLUP", "PULLDOWN", "KEEPER"])

        if prim in ("IBUF", "IOBUF", "IOBUFE3", "IBUF_IBUFDISABLE",
                    "IBUF_INTERMDISABLE"):
            if "POD" in ios:
                odt_choices = []
                if "DCI" not in ios:
                    odt_choices.append("RTT_NONE")

                if "OUTPUT_IMPEDANCE" not in params or params[
                        "OUTPUT_IMPEDANCE"] != "RDRV_48_48":
                    odt_choices += ["RTT_40", "RTT_60"]
                else:
                    odt_choices += ["RTT_48"]

                params["ODT"] = np.random.choice(odt_choices)
                if "POD12" in ios:
                    params["EQUALIZATION"] = np.random.choice([
                        "EQ_LEVEL0", "EQ_LEVEL1", "EQ_LEVEL2", "EQ_LEVEL3",
                        "EQ_LEVEL4", "EQ_NONE"
                    ])

        used_pins.append(pin)
        io_config.append(params)
        if iot == "HDIOB":
            if ios in ("HSTL_I_18", "SSTL18_I", "SSTL18_II"):
                bank_iref[bank] = "0.90"
            elif ios in ("HSTL_I", "SSTL15", "SSTL15_II"):
                bank_iref[bank] = "0.75"
            elif ios in ("SSTL135", "SSTL135_II"):
                bank_iref[bank] = "0.675"
            elif ios == "SSTL12":
                bank_iref[bank] = "0.60"
        if ios in ("POD12", "POD12_DCI"):
            bank_pod_used.add(bank)
            bank_iref[bank] = np.random.choice([None, "0.84"])
        if ios in ("HSTL_I_12", "HSTL_I_DCI_12", "SSTL12", "SSTL12_DCI",
                   "HSUL_12", "HSUL_12_DCI"):
            bank_pod_not_used.add(bank)
            bank_iref[bank] = np.random.choice([None, "0.60"])

    print("module top(", file=fout)
    for i, params in enumerate(io_config):
        prim = params["prim"]
        bank = pin_to_banks[used_pins[i]]
        if bank in bank_iref and bank_iref[bank] is not None:
            params["internal_vref"] = bank_iref[bank]
        print(
            "(* %s, %s, %s *)" %
            ('X_PIN="{}"'.format(used_pins[i]), 'X_OFFSET="{}"'.format(
                pin_to_site_offsets[used_pins[i]]), ", ".join(
                    'X_%s="%s"' % (k, v) for k, v in sorted(params.items()))),
            file=fout)
        if prim in ("IBUF", "IBUF_IBUFDISABLE"):
            print(
                "input p%d%s" % (i, "," if i < len(io_config) - 1 else ""),
                file=fout)
        elif prim in ("OBUF", "OBUFT"):
            print(
                "output p%d%s" % (i, "," if i < len(io_config) - 1 else ""),
                file=fout)
        elif prim in ("IOBUF", "IOBUFE3"):
            print(
                "inout p%d%s" % (i, "," if i < len(io_config) - 1 else ""),
                file=fout)
    print(");", file=fout)
    for i, (pin, params) in enumerate(zip(used_pins, io_config)):
        print("(* KEEP, DONT_TOUCH *)", file=fout)
        prim = params["prim"]
        if prim == "IBUF":
            print(
                """
                IBUF buf_{i} (
                    .I(p{i}),
                    .O({sig_o})
                );
            """.format(i=i, sig_o=maybe_inp(pin)),
                file=fout)
        elif prim == "OBUF":
            print(
                """
                OBUF buf_{i} (
                    .O(p{i}),
                    .I({sig_i})
                );
            """.format(i=i, sig_i=maybe_outp(pin)),
                file=fout)
        elif prim == "OBUFT":
            print(
                """
                OBUFT buf_{i} (
                    .O(p{i}),
                    .T({sig_t}),
                    .I({sig_i})
                );
            """.format(i=i, sig_t=outp(), sig_i=maybe_outp(pin)),
                file=fout)
        elif prim == "IOBUF":
            print(
                """
                IOBUF buf_{i} (
                    .IO(p{i}),
                    .T({sig_t}),
                    .I({sig_i}),
                    .O({sig_o})
                );
            """.format(
                    i=i,
                    sig_t=maybe_outp(pin),
                    sig_i=maybe_outp(pin),
                    sig_o=maybe_inp(pin)),
                file=fout)
        elif prim == "IBUF_IBUFDISABLE":
            print(
                """
                IBUF_IBUFDISABLE buf_{i} (
                    .I(p{i}),
                    .O({sig_o}),
                    .IBUFDISABLE({ibuf_dis})
                );
            """.format(i=i, sig_o=maybe_inp(pin), ibuf_dis=maybe_outp(pin)),
                file=fout)
        elif prim == "IOBUFE3":
            if np.random.choice([True, False]):
                print("(* KEEP, DONT_TOUCH *)", file=fout)
            print(
                """
                IOBUFE3 buf_{i} (
                    .IO(p{i}),
                    .T({sig_t}),
                    .I({sig_i}),
                    .O({sig_o}),
                    .DCITERMDISABLE({dci_dis}),
                    .IBUFDISABLE({ibuf_dis})
                );
            """.format(
                    i=i,
                    sig_t=maybe_outp(pin),
                    sig_i=maybe_outp(pin),
                    sig_o=maybe_inp(pin),
                    dci_dis=maybe_outp(pin),
                    ibuf_dis=maybe_outp(pin)),
                file=fout)
        print("", file=fout)
    print("wire [%d:0] li;" % (len(lut_inputs) - 1), file=fout)
    print("wire [%d:0] lo;" % (len(lut_outputs) - 1), file=fout)

    for i in range(max(len(lut_inputs) // 6 + 1, len(lut_outputs))):
        ip = [
            "1'b0" if
            (i * 6 + j) >= len(lut_inputs) else "li[%d]" % (i * 6 + j)
            for j in range(6)
        ]
        op = "lo[%d]" % i if i < len(lut_outputs) else ""
        print(
            """
            (* KEEP, DONT_TOUCH *)
            LUT6 lut{i} (.I0({i0}), .I1({i1}), .I2({i2}), .I3({i3}), .I4({i4}), .I5({i5}), .O({o}));
            """.format(
                i=i,
                i0=ip[0],
                i1=ip[1],
                i2=ip[2],
                i3=ip[3],
                i4=ip[4],
                i5=ip[5],
                o=op,
            ),
            file=fout)
    print("endmodule", file=fout)

    with open('top.xdc', 'w') as f:
        for i, params in enumerate(io_config):
            pin = used_pins[i]
            print(
                "set_property PACKAGE_PIN %s [get_ports {p%d}]" % (pin, i),
                file=f)
            for k, v in sorted(params.items()):
                if k.isupper():
                    print(
                        "set_property %s %s [get_ports {p%d}]" % (k, v, i),
                        file=f)
        for bank, iref in sorted(bank_iref.items()):
            if iref is None:
                continue
            print(
                "set_property INTERNAL_VREF {%s} [get_iobanks %d]" % (iref,
                                                                      bank),
                file=f)

    with open('complete_top.tcl', 'w') as f:
        print(
            "set_property SEVERITY {Warning} [get_drc_checks NSTD-1]", file=f)
        print(
            "set_property SEVERITY {Warning} [get_drc_checks UCIO-1]", file=f)
        print(
            "set_property SEVERITY {Warning} [get_drc_checks AVAL-*]", file=f)
        print(
            "set_property SEVERITY {Warning} [get_drc_checks REQP-*]", file=f)
        print(
            "set_property SEVERITY {Warning} [get_drc_checks BIVR-*]", file=f)
        print(
            "set_property SEVERITY {Warning} [get_drc_checks PLHDIO-1]",
            file=f)
        print("opt_design", file=f)
        print("place_design", file=f)
        print("route_design", file=f)


#!/usr/bin/env python

import os
import argparse
import datetime as dt
import json
import socket
import sys
import time
import traceback

import astropy.units as u
import numpy as np
import requests
from cheta import fetch
from cxotime import CxoTime

from chandratime import (calc_time_to_next_comm, convert_chandra_time,
                         convert_to_doy)
from heartbeat import are_we_in_comm

import psutil
process = psutil.Process(os.getpid())

def audit_telemetry(start, channel=None):

    print(f'({CxoTime.now().strftime("%m/%d/%Y %H:%M:%S")}) TELEMETRY AUDIT', end='\r')

    critical_msidlist = {'2C05PALV': (4.9, 5.1),
                         '2C15PALV': (14.9, 15.1),
                         '2C15NALV': (-14.8, -14.6),
                         '2C24PALV': (24.1, 24.3),
                         }

    values_since_comm_start = fetch.get_telem(
        list(critical_msidlist.keys()), start=start, quiet=True, unit_system='eng')

    for msid in list(critical_msidlist.keys()):
        out_of_limit_mask = (values_since_comm_start[msid].vals < critical_msidlist[msid][0]) or (
            values_since_comm_start[msid].vals > critical_msidlist[msid][1])
        if sum(out_of_limit_mask) > 3:
            send_slack_message(
                f'{msid} shows out-of-green-range values: {values_since_comm_start[out_of_limit_mask][0]}. This is beyond CAUTION/WARN limit of {critical_msidlist[msid]}', channel=channel)

def send_slack_message(message, channel='#comm_passes', blocks=None):

    # I run this on both the HEAD LAN and my home machine
    # Make sure it's readable only to you, i.e. chmod og-rwx slackbot_oauth_token
    if socket.gethostname() == 'han-v.cfa.harvard.edu':
        slackbot_token_path = '/home/tremblay/.slackbot/slackbot_oauth_token'
    elif socket.gethostname() == 'symmetry.local':
        slackbot_token_path = '/Users/grant/.slackbot/slackbot_oauth_token'
    else:
        sys.exit('I do not recognize the hostname {}. Exiting.'.format(
            socket.gethostname()))

    # Never push the token to github!
    with open(slackbot_token_path, 'r') as tokenfile:
        # .splitlines()[0] is needed to strip the \n character from the token file
        slack_token = tokenfile.read().splitlines()[0]

    slack_channel = channel
    slack_icon_url = 'https://avatars.slack-edge.com/2021-01-28/1695804235940_26ef808c676830611f43_512.png'
    slack_user_name = 'HRC CommBot'

    # Populate a JSON to push to the Slack API.
    return requests.post('https://slack.com/api/chat.postMessage', {
        'token': slack_token,
        'channel': slack_channel,
        'text': message,
        'icon_url': slack_icon_url,
        'username': slack_user_name,
        'blocks': json.dumps(blocks) if blocks else None}).json()

def grab_critical_telemetry(start=CxoTime.now() - 60 * u.s):

    critical_msidlist = ['CCSDSTMF', '2SHEV1RT', '2PRBSCR',
                         '2FHTRMZT', '2IMTPAST', '2IMBPAST', '2SPTPAST', '2SPBPAST', '2TLEV1RT', '2VLEV1RT']
    critical_msids = fetch.get_telem(
        critical_msidlist, start=start, quiet=True, unit_system='eng')

    tm_format = critical_msids['CCSDSTMF'].vals[-1]
    shield_rate = critical_msids['2SHEV1RT'].vals[-1]
    if shield_rate > 0:
        shield_state = 'UP'
    elif shield_rate == 0:
        shield_state = 'DOWN'
    bus_current_in_amps = np.round(critical_msids['2PRBSCR'].vals[-1], 2)
    bus_current_in_dn = convert_bus_current_to_dn(bus_current_in_amps)

    fea_temp = np.round(critical_msids['2FHTRMZT'].vals[-1], 2)
    hrc_i_voltage = (
        critical_msids['2IMTPAST'].vals[-1], critical_msids['2IMBPAST'].vals[-1])
    # HALF voltage for HRC-I is 42/53
    # FULL voltage for HRC-S is 79/91
    hrc_s_voltage = (
        critical_msids['2SPTPAST'].vals[-1], critical_msids['2SPBPAST'].vals[-1])
    # HALF voltage for HRC-S is 43/54 (top/bottom)
    # FULL voltage is 95/107 (top/bottom)

    # Set statuses

    if tm_format == 'FMT1':
        hrc_observing_status = 'OBSERVING'
    else:
        hrc_observing_status = 'NOT observing'

    # in order of off, half, full
    expected_hrc_i_states = [(0, 0), (42, 53), (79, 91)]
    # in order of off, half, full
    expected_hrc_s_states = [(0, 0), (43, 54), (95, 107)]

    expected_status = ['OFF', 'at HALF voltage', 'at FULL voltage']

    hrc_telem_status = None
    hrc_i_status = None
    hrc_s_status = None

    try:
        hrc_i_status = expected_status[expected_hrc_i_states.index(
            hrc_i_voltage)]
    except ValueError:
        hrc_i_status = 'in a POTENTIALLY UNEXPECTED state ({}). CHECK THIS!'.format(
            hrc_i_voltage)

    try:
        hrc_s_status = expected_status[expected_hrc_s_states.index(
            hrc_s_voltage)]
    except ValueError:
        hrc_s_status = 'in a POTENTIALLY UNEXPECTED state ({}). CHECK THIS!'.format(
            hrc_s_voltage)

    te_rate = critical_msids['2TLEV1RT'].vals[-1]
    ve_rate = critical_msids['2VLEV1RT'].vals[-1]

    telem = {'HRC observing status': hrc_observing_status, 'Format': tm_format, 'Shield Rate': shield_rate, 'Shield State': shield_state,
             'Bus Current (DN)': bus_current_in_dn, 'Bus Current (A)': bus_current_in_amps, 'FEA Temp': fea_temp, 'HRC-I Voltage Steps': hrc_i_voltage, 'HRC-I Status': hrc_i_status, 'HRC-S Voltage Steps': hrc_s_voltage, 'HRC-S Status': hrc_s_status, 'TE Rate': te_rate, 'VE Rate': ve_rate}

    return telem

def convert_bus_current_to_dn(bus_current_in_amps):
    '''
    The DN-to-Current (Amps) conversion for 2PRBSCR is just a linear
    offset in the form I = (m * DN) + b, from here: https://icxc.cfa.harvard.edu/hrcops/msid/hrc.msid.html
    '''

    # bus_current_in_amps = (m * DN) + b, were m=0.0682 and b=-8.65
    # DN = (bus_current_in_amps - b) / m

    b = -8.65
    m = 0.0682

    # Needs to be a whole integer
    dn = int(round((bus_current_in_amps - b) / m))

    return dn

def get_args():
    '''Fetch command line args, if given'''

    parser = argparse.ArgumentParser(
        description='Monitor the VCDU telemetry stream, and send a message to the HRC Ops Slack with critical HRC telemetry whenever we are in comm.')

    parser.add_argument("--fake_comm", help="Trick the code to think we are in comm. Useful for testing. ",
                        action="store_true")

    parser.add_argument("--report_errors", help="Print MAUDE exceptions (which are common) to the command line",
                        action="store_true")

    args = parser.parse_args()
    return args

def main():

    fetch.data_source.set('maude allow_subset=True')

    args = get_args()
    fake_comm = args.fake_comm
    chatty = args.report_errors  # Will be True if user set --report_errors

    if fake_comm:
        bot_slack_channel = '#bot-testing'
    elif not fake_comm:
        bot_slack_channel = bot_slack_channel = '#comm_passes'

    # Initial settings
    recently_in_comm = False
    in_comm_counter = 0
    telemetry_audit_counter = 0

    # Loop infinitely :)
    while True:

        try:
            in_comm = are_we_in_comm(
                verbose=False, cadence=2, fake_comm=fake_comm)

            if not in_comm:
                if recently_in_comm:
                    # We might have just had a loss in telemetry. Try again after waiting for a minute
                    time.sleep(60)
                    in_comm = are_we_in_comm(verbose=False, cadence=2)
                    if in_comm:
                        continue

                    # Assuming the end of comm is real, then comm has recently ended and we need to report that.
                    try:
                        telem = grab_critical_telemetry(start=CxoTime.now() - 1800 * u.s)
                    except Exception as e:
                        telem = None
                        if chatty:
                            print('MAUDE exception: {}'.format(e))
                        continue
                    if telem is not None:
                        message = f"It appears that COMM has ended as of `{CxoTime.now().strftime('%m/%d/%Y %H:%M:%S')}` \n\n Next COMM is expected in {calc_time_to_next_comm()} \n\n HRC was *{telem['HRC observing status']}* \n Last telemetry was in `{telem['Format']}` \n\n *HRC-I* was {telem['HRC-I Status']} \n *HRC-S* was {telem['HRC-S Status']} \n\n *Shields were {telem['Shield State']}* with a count rate of `{telem['Shield Rate']} cps` \n\n *HRC-I* Voltage Steps were (Top/Bottom) = `{telem['HRC-I Voltage Steps'][0]}/{telem['HRC-I Voltage Steps'][1]}` \n *HRC-S* Voltage Steps were (Top/Bottom) = `{telem['HRC-S Voltage Steps'][0]}/{telem['HRC-S Voltage Steps'][1]}`  \n\n *Bus Current* was `{telem['Bus Current (DN)']} DN` (`{telem['Bus Current (A)']} A`)  \n\n *FEA Temperature* was `{telem['FEA Temp']} C`"
                    elif telem is None:
                        message = f"It appears that COMM has ended as of `{CxoTime.now().strftime('%m/%d/%Y %H:%M:%S')}` \n\n Currently having trouble querying telemetry; wait to see if the problem resolves, otherwise contact Grant!"
                    send_slack_message(message, channel=bot_slack_channel)

                recently_in_comm = False
                in_comm_counter = 0
                print(
                    f'({CxoTime.now().strftime("%m/%d/%Y %H:%M:%S")}) Not in Comm.                                ', end='\r')
            if in_comm:
                if fake_comm:
                    # two days to make sure we grab previous comm
                    start_time = CxoTime.now() - 2 * u.d
                elif not fake_comm:
                    start_time = CxoTime.now() - 300 * u.s  # 300 sec to make the grab really small

                if in_comm_counter == 0:
                    # Then start the clock on the comm pass
                    comm_start_timestamp = CxoTime.now()

                recently_in_comm = True
                in_comm_counter += 1
                telemetry_audit_counter += 1

                time.sleep(5)  # Wait a few seconds for MAUDE to refresh
                latest_vcdu = fetch.Msid(
                    'CVCDUCTR', start=start_time).vals[-1]

                print(
                    f'({CxoTime.now().strftime("%m/%d/%Y %H:%M:%S")} | VCDU {latest_vcdu} | #{in_comm_counter}) In Comm.', end='\r')

                if in_comm_counter == 5:
                    # Now we've waited ~half a minute or so for MAUDE to update
                    telem = grab_critical_telemetry(
                        start=CxoTime.now() - 8 * u.h)

                    # Craft a message string using this latest elemetry
                    message = f"We are now *IN COMM* as of `{CxoTime.now().strftime('%m/%d/%Y %H:%M:%S')}` (_Chandra_ time). \n\n HRC is *{telem['HRC observing status']}*  \n Telemetry Format = `{telem['Format']}` \n\n *HRC-I* is {telem['HRC-I Status']} \n *HRC-S* is {telem['HRC-S Status']} \n\n *Shields are {telem['Shield State']}* with a count rate of `{telem['Shield Rate']} cps` \n\n *HRC-I* Voltage Steps (Top/Bottom) = `{telem['HRC-I Voltage Steps'][0]}/{telem['HRC-I Voltage Steps'][1]}` \n *HRC-S* Voltage Steps (Top/Bottom) = `{telem['HRC-S Voltage Steps'][0]}/{telem['HRC-S Voltage Steps'][1]}`  \n \n *Total Event* Rate = `{telem['TE Rate']} cps`   \n *Valid Event* Rate = `{telem['VE Rate']} cps`  \n \n *Bus Current* is `{telem['Bus Current (DN)']} DN` (`{telem['Bus Current (A)']} A`) \n \n *FEA Temperature* is `{telem['FEA Temp']} C`"
                    # Send the message using our slack bot
                    send_slack_message(message, channel=bot_slack_channel)
                    # do a first audit of the telemetry upon announcement

                if telemetry_audit_counter == 10:
                    # Reset the audit counter
                    telemetry_audit_counter = 0
                    # Now we've waited a minute. Let's audit the telemetry and send amessage.
                    # Check starting from the beginning of comm. We always check an ever-expanding
                    # chunk of telemetry. This is a BIT redundant, but not bad.
                    # audit_telemetry(start=comm_start_timestamp,
                    #                 channel=bot_slack_channel)

        except Exception as e:
            # MAUDE queries fail regularly as TM is streaming in (mismatched array sizes as data is being populated), 404s, etc.
            # The solution is almost always to simply try again. Therefore this script just presses on in the event of an Exception.

            if chatty:
                # Then we want a verbose error message, because we're obviously in testing mode
                print(f'({CxoTime.now().strftime("%m/%d/%Y %H:%M:%S")}) ERROR: {e}')
                print("Heres the traceback:")
                print(traceback.format_exc())
                print(
                    f'({CxoTime.now().strftime("%m/%d/%Y %H:%M:%S")}) Pressing on ...')
            elif not chatty:
                # Then we're likely in operational mode. Ignore the errors on the command line.
                print(
                    f'({CxoTime.now().strftime("%m/%d/%Y %H:%M:%S")}) ERROR encountered! Use --report_errors to display them.                             ', end='\r\r\r')
            if in_comm_counter > 0:
                # Reset the comm counter to make the error "not count"
                in_comm_counter -= 1
            continue

if __name__ == '__main__':
    main()


import os
import sys
from numpy import random
import math
import re
import time
import numpy as np
import cv2
import matplotlib
import matplotlib.pyplot as plt
import json
from PIL import Image, ImageDraw
from tensorflow.python.framework.versions import VERSION as __version__
import tensorflow as tf
from imgaug import augmenters as iaa
import warnings
warnings.filterwarnings(action='ignore')

#Cambiamos el Directorio al propio de MASK_RCNN
ROOT_DIR = 'D:/Cleansea/Mask_RCNN-cleansea'
#ROOT_DIR = '/home/saflex/projecto_cleansea/Mask_RCNN/Mask_RCNN-master'
assert os.path.exists(ROOT_DIR), 'ROOT_DIR does not exist'

# Import mrcnn libraries
sys.path.append(ROOT_DIR)
from mrcnn.config import Config
from mrcnn import utils
import mrcnn.model as modellib
from mrcnn import visualize
from mrcnn.model import log

physical_devices = tf.config.list_physical_devices('GPU')
tf.config.experimental.set_memory_growth(physical_devices[0], True)
# Directorio perteneciente a MASK-RCNN
MODEL_DIR = os.path.join(ROOT_DIR, "logs")

# Ruta al archivo de pesos
COCO_WEIGHTS_PATH = os.path.join(ROOT_DIR, "mask_rcnn_coco.h5")

# Descargamos los Pesos Entrenados de COCO
if not os.path.exists(COCO_WEIGHTS_PATH):
    utils.download_trained_weights(COCO_WEIGHTS_PATH)

############################################################
#  Configuracion
############################################################

class CleanSeaConfig(Config):
    """
    Configuracion para el entrenamiento con CleanSea Dataset.
    """

    # Nombre de la configuracion
    NAME = "debris"

    # We use a GPU with 12GB memory, which can fit two images.
    # Adjust down if you use a smaller GPU.
    IMAGES_PER_GPU = 1

    # Use small images for faster training. Set the limits of the small side
    # the large side, and that determines the image shape.
    IMAGE_MIN_DIM = 512
    IMAGE_MAX_DIM = 512

    # Numero de clases + el background
    NUM_CLASSES = 1 + 19  # Cleansea tiene 19 clases

    # Salta las detecciones con <50% de seguridad
    DETECTION_MIN_CONFIDENCE = 0.5

    #Learning Rate Modificado
    LEARNING_RATE = 0.001

config= CleanSeaConfig()
config.display()

def get_ax(rows=1, cols=1, size=8):
    """Return a Matplotlib Axes array to be used in
    all visualizations in the notebook. Provide a
    central point to control graph sizes.

    Change the default size attribute to control the size
    of rendered images
    """
    _, ax = plt.subplots(rows, cols, figsize=(size*cols, size*rows))
    return ax

############################################################
#  Dataset
############################################################
class CleanSeaDataset(utils.Dataset):
    def load_data(self, dataset_dir, subset):
        # Train or validation dataset?
        assert subset in ["train_coco", "test_coco"]
        dataset_dir = os.path.join(dataset_dir, subset)
        print(dataset_dir)

        # Cargamos el archivo json
        annotation_json = os.path.join(dataset_dir,"annotations.json")
        json_file = open(annotation_json)
        coco_json = json.load(json_file)
        json_file.close()
        print("\nAnotaciones Cargadas\n")

        # Añadimos los nombres de las clases usando el metodo de utils.Dataset
        source_name = "coco_like"
        for category in coco_json['categories']:
            class_id = category['id']
            class_name = category['name']
            if class_id < 1:
                print('Error: Class id for "{}" reserved for the background'.format(class_name))
            else:
                self.add_class(source_name, class_id, class_name)
        print("Nombres Añadidos \n")

        # Almacenamos las anotaciones
        annotations = {}
        for annotation in coco_json['annotations']:
            image_id = annotation['image_id']
            if image_id not in annotations:
                annotations[image_id] = []
            annotations[image_id].append(annotation)
        print("Anotaciones Almacenadas\n")

        # Almacenamos las imagenes y las añadimos al dataset
        seen_images = {}
        for image in coco_json['images']:
            image_id = image['id']
            if image_id in seen_images:
                print("Warning: Skipping duplicate image id: {}".format(image))
            else:
                seen_images[image_id] = image
                try:
                    image_file_name = image['file_name']
                    image_width = image['width']
                    image_height = image['height']
                except KeyError as key:
                    print("Warning: Skipping image (id: {}) with missing key: {}".format(image_id, key))

                image_path = os.path.join(dataset_dir, image_file_name)
                image_annotations = annotations[image_id]

                # Añadimos la imagen usando el metodo de utils.Dataset
                self.add_image(
                    source=source_name,
                    image_id=image_id,
                    path=image_path,
                    width=image_width,
                    height=image_height,
                    annotations=image_annotations
                )
        print("Imagenes añadidas al Dataset\n")

    def load_mask(self, image_id):
        """ Carga la mascara de instancia para la imagen dada
        MaskRCNN espera mascaras en forma de mapa de bits (altura, anchura e instancias)
        Argumentos:
            image_id: El ID de la imagen a la que vamos a cargar la mascara
        Salida:
            masks: Una cadena booleana con estructura (altura, anchya y la cuenta de instancias) con una mascara por instancia
            class_ids: Una cadena de 1 dimension de clase ID de la instancia de la mascara """
        image_info = self.image_info[image_id]
        annotations = image_info['annotations']
        instance_masks = []
        class_ids = []

        for annotation in annotations:
            class_id = annotation['category_id']
            mask = Image.new('1', (image_info['width'], image_info['height']))
            mask_draw = ImageDraw.ImageDraw(mask, '1')
            for segmentation in annotation['segmentation']:
                mask_draw.polygon(segmentation, fill=1)
                bool_array = np.array(mask) > 0
                instance_masks.append(bool_array)
                class_ids.append(class_id)

        mask = np.dstack(instance_masks)
        class_ids = np.array(class_ids, dtype=np.int32)
        return mask, class_ids

    def image_reference(self, image_id):
        """Return the path of the image."""
        info = self.image_info[image_id]
        if info["source"] == "object":
            return info["path"]
        else:
            super(self.__class__, self).image_reference(image_id)

"""Train the model."""
# Training dataset.
dataset_train = CleanSeaDataset()
print("Configuracion para train cargada\n")
dataset_train.load_data("D:/Cleansea/cleansea_dataset/CocoFormatDataset","train_coco")
print("Dataset Inicializado Correctamente\n")
dataset_train.prepare()
print("Preparacion del Dataset Completada\n")

# Validation dataset
dataset_test = CleanSeaDataset()
print("Configuracion para test cargada\n")
dataset_test.load_data("D:/Cleansea/cleansea_dataset/CocoFormatDataset", "test_coco")
print("Dataset Inicializado Correctamente\n")
dataset_test.prepare()
print("Preparacion del Dataset Completada\n")

# Load and display random samples
print("Mostrando Imagenes aleatorias...\n")

image_ids = np.random.choice(dataset_train.image_ids, 4)
for image_id in image_ids:
    image = dataset_train.load_image(image_id)
    mask, class_ids = dataset_train.load_mask(image_id)
    visualize.display_top_masks(image, mask, class_ids, dataset_train.class_names)

print("Inicializing model for training...\n")
model=modellib.MaskRCNN(mode="training", config=config, model_dir=MODEL_DIR)
# Which weights to start with?

init_with = "coco"  # imagenet, coco, or last
if init_with == "imagenet":
    model.load_weights(model.get_imagenet_weights(), by_name=True)
elif init_with == "coco":
    # Load weights trained on MS COCO, but skip layers that
    # are different due to the different number of classes
    # See README for instructions to download the COCO weights
    model.load_weights(COCO_WEIGHTS_PATH, by_name=True,
                    exclude=["mrcnn_class_logits", "mrcnn_bbox_fc",
                                "mrcnn_bbox", "mrcnn_mask"])
elif init_with == "last":
    # Load the last model you trained and continue training
    model.load_weights(model.find_last(), by_name=True)

last_path="D:/Cleansea/Mask_RCNN-cleansea/logs/mask_rcnn_debris_weights1000DA5Heads.h5"
model.load_weights(last_path, by_name=True)

############################################################
#  Training
############################################################
# ===============================================================================
# Aumentado de Datos
# ===============================================================================

seq = iaa.Sequential([
    iaa.Fliplr(0.5), # horizontal flips
    # Small gaussian blur with random sigma between 0 and 0.5.
    # But we only blur about 50% of all images.
    iaa.Sometimes(
        0.5,
        iaa.GaussianBlur(sigma=(0, 0.5))
    ),
    # Strengthen or weaken the contrast in each image.
    iaa.LinearContrast((0.75, 1.5)),
    # Make some images brighter and some darker.
    # In 20% of all cases, we sample the multiplier once per channel,
    # which can end up changing the color of the images.
    iaa.Multiply((0.8, 1.2), per_channel=0.2),
    # Apply affine transformations to each image.
    # Scale/zoom them, translate/move them, rotate them and shear them.
    iaa.Affine(
        scale={"x": (0.8, 1.2), "y": (0.8, 1.2)},
        translate_percent={"x": (-0.2, 0.2), "y": (-0.2, 0.2)},
        rotate=(-25, 25),
        shear=(-8, 8)
    )
], random_order=True) # apply augmenters in random order

"""
seq = iaa.Sequential([
    iaa.Fliplr(0.5), # horizontal flips
])
"""

# ===============================================================================

# Train the head branches
# Passing layers="heads" freezes all layers except the head
# layers. You can also pass a regular expression to select
# which layers to train by name pattern.

print("Entrenando Heads...\n")
model.train(dataset_train, dataset_test, learning_rate=config.LEARNING_RATE, epochs=5, layers='heads',augmentation=seq)

# Fine tune all layers
# Passing layers="all" trains all layers. You can also
# pass a regular expression to select which layers to
# train by name pattern.
print("Entrenando extensivamente...\n")
model.train(dataset_train, dataset_test,
            learning_rate=config.LEARNING_RATE / 10,
            epochs=1000,
            layers="all",augmentation=seq)

# Save weights
# Typically not needed because callbacks save after every epoch
# Uncomment to save manually
print("Guardando Pesos...\n")
model_path = os.path.join(MODEL_DIR, "mask_rcnn_debris_weights.h5")
model.keras_model.save_weights(model_path)
print("Pesos Guardados en mask_rcnn_debris_weights.h5")

############################################################
#  Evaluacion
############################################################
class InferenceConfig(CleanSeaConfig):
    GPU_COUNT = 1
    IMAGES_PER_GPU = 1
    USE_MINI_MASK = False

inference_config = InferenceConfig()

# Recreate the model in inference mode
model = modellib.MaskRCNN(mode="inference",
                          config=inference_config,
                          model_dir=MODEL_DIR)

# Get path to saved weights
# Either set a specific path or find last trained weights
# model_path = os.path.join(ROOT_DIR, ".h5 file name here")
model_path = model.find_last()

# Load trained weights
print("Loading weights from ", model_path)
model.load_weights(model_path, by_name=True)

image_ids = dataset_test.image_ids
APs = []
for image_id in image_ids:
    # Load image and ground truth data
    image, image_meta, gt_class_id, gt_bbox, gt_mask =\
        modellib.load_image_gt(dataset_test, inference_config,
                               image_id)
    molded_images = np.expand_dims(modellib.mold_image(image, inference_config), 0)
    # Run object detection
    results = model.detect([image], verbose=0)
    r = results[0]
    # Compute AP
    AP, precisions, recalls, overlaps =\
        utils.compute_ap(gt_bbox, gt_class_id, gt_mask,
                         r["rois"], r["class_ids"], r["scores"], r['masks'])
    APs.append(AP)

print("mAP: ", np.mean(APs))


"""
Right part of the test windows
"""

from PyQt5 import QtCore, QtWidgets
from PyQt5.QtWidgets import (QMainWindow, QWidget, QPushButton, QAction, QStatusBar, QFormLayout, QLabel, QLineEdit,
    QVBoxLayout, QHBoxLayout, QSpacerItem, QMessageBox, QFrame, QSizePolicy, QInputDialog, QGroupBox, QRadioButton, QButtonGroup)
from PyQt5.QtCore import QSize, Qt, QRegExp, QThread, QThreadPool, QObject, pyqtSignal

from PyQt5.QtGui import QIcon, QFont, QDoubleValidator, QPixmap, QPalette, QColor, QCursor, QRegExpValidator

import numpy as np    #For mathematics
import pyqtgraph as pg    #Library to plot graph with pyqt
import time
import sys
import os
from simple_pid import PID

from tension_inflation.modules.sensors_dialogs import MotorPI    #Program to control the axial motor
from tension_inflation.modules.sensors_dialogs import Arduino    #Program created to connect / read... with the arduino microcontrol
from tension_inflation.modules.sensors_dialogs import Pump_seringe    #Program to control the pump

from tension_inflation.modules.mainwindow_modules.CommandThread import CommandThread

#To attribute path if pyinstaller is used
def resource_path(relative_path):
     if hasattr(sys, '_MEIPASS'):
         return os.path.join(sys._MEIPASS, relative_path)
     return os.path.join(os.path.abspath("."), relative_path)

class Right_panel(object):
    def setup_right_panel(self, parent=None):

        self.PVmode = 'P'  #Control modes (P for pressure / V for volume)
        self.FDmode = 'D'  #Control modes (F for load / D for displacement)

        # Creation of the separetor object
        sepTestH = [QFrame() for i in range(7)]
        for obj in sepTestH:
            obj.setFrameShape(QFrame.HLine)    #Vertical line
            obj.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Expanding)
            obj.setFrameShadow(QFrame.Sunken);
            obj.setLineWidth(1)

        sepTestV = [QFrame() for i in range(7)]
        for obj in sepTestV:
            obj.setFrameShape(QFrame.VLine)    #Vertical line
            obj.setSizePolicy(QSizePolicy.Minimum,QSizePolicy.Expanding)
            obj.setFrameShadow(QFrame.Sunken);
            obj.setLineWidth(1)

        # Button for initialisation
        self.inibutton = QPushButton(self)    #Creation of the widget
        self.inibutton.clicked.connect(self.clickMethodAcquisition)    #Method to connect the click on the button with the function clickMethodAcquisition
        #self.inibutton.resize(100,32)
        self.inibutton.setStatusTip('Run the acquisition')    #When cursor on the button the statue bar show the message
        self.inibutton.setIcon(QIcon(resource_path('tension_inflation/resources/button_start_push.png')))   #Change the button's icon by the resources
        self.inibutton.setIconSize(QSize(30,30))   #Change the size of the icon

        # Add button for stop
        self.stopbutton = QPushButton(self)
        self.stopbutton.clicked.connect(self.clickMethodStop)
        self.stopbutton.setIcon(QIcon(resource_path('tension_inflation/resources/button_stop.png')))
        #self.stopbutton.resize(100,32)
        self.stopbutton.setStatusTip('Stop the acquisition')
        self.stopbutton.setIconSize(QSize(30,30))
        #self.stopbutton.move(10, 30)

        #Create label: running or stopped state
        self.label_state = QLabel()    #Creation of the widget
        self.label_state.setText(" Stopped ")    #Set the text in the label
        self.label_state.setAlignment(Qt.AlignCenter)   #Alignement of the label (here it's the vertical alignement)
        self.label_state.setFont(QFont("Arial",16,QFont.Bold))    #Set the writing font with the size
        self.label_state.setStyleSheet("background-color: red;")

        #Widget with user input for changing load
        self.input_sample = QLineEdit()   #Creation of the widget
        self.input_sample.setMaxLength(30)     #Number of characters accepted
        self.input_sample.setAlignment(Qt.AlignRight)
        self.input_sample.setFont(QFont("Arial",12))
        self.input_sample.setStyleSheet("QLineEdit{background: white;}")    #Background color of the widget

        #hChange the writing color from write to black because we set the background write (I like when it's beautiful)
        self.input_sample.palette = QPalette()
        self.input_sample.palette.setColor(QPalette.Text, Qt.black)
        self.input_sample.setPalette(self.input_sample.palette)

        #Creation of the label attached to the QLineEdit
        label_sample_input = QLabel("Sample Name:", self)
        label_sample_input.setFont(QFont("Arial",12,QFont.Bold))

        #Put the QLineEdit and the label together (like a form)
        form_sample = QFormLayout()     #Creation of the layout form
        form_sample.addRow(label_sample_input, self.input_sample)     #Add things in the layout (by row)

        #Add the button "Send" to the previous layout by putting all that in another layout
        box_sample_input = QHBoxLayout()
        box_sample_input.addLayout(form_sample)
        box_sample_input.setAlignment(Qt.AlignCenter)

        #Layout at top with buttons start and stop
        box_top = QHBoxLayout()
        box_top.addWidget(self.inibutton)
        box_top.addSpacing (0)
        box_top.addWidget(self.stopbutton)
        box_top.addLayout(box_sample_input)
        box_top.addStretch()

        box_top.addWidget(sepTestV[6])
        box_top.addWidget(self.label_state)

        #######################################################
                # Choose of the commande mode
        #######################################################

        #Create label: running or stopped state
        label_control = QLabel()    #Creation of the widget
        label_control.setText(" Control: ")    #Set the text in the label
        label_control.setAlignment(Qt.AlignCenter)   #Alignement of the label (here it's the vertical alignement)
        label_control.setFont(QFont("Arial",16,QFont.Bold))    #Set the writing font with the size

        group_infl = QButtonGroup(self.centralWidget)
        #Add a radio button to select mode of command
        self.Radio_volumeMode = QRadioButton("Mode: Volume")
        self.Radio_volumeMode.clicked.connect(lambda:self.Mode(self.Radio_volumeMode))
        group_infl.addButton(self.Radio_volumeMode)

        self.Radio_pressureMode = QRadioButton("Mode: Pressure")
        self.Radio_pressureMode.setChecked(True)
        self.Radio_pressureMode.clicked.connect(lambda:self.Mode(self.Radio_pressureMode))
        group_infl.addButton(self.Radio_pressureMode)

        group_tension = QButtonGroup(self.centralWidget)
        #Add a radio button to select mode of command
        self.Radio_dispMode = QRadioButton("Mode: Displacement")
        self.Radio_dispMode.setChecked(True)
        self.Radio_dispMode.clicked.connect(lambda:self.Mode(self.Radio_dispMode))
        group_tension.addButton(self.Radio_dispMode)

        self.Radio_loadMode = QRadioButton("Mode: Load")
        self.Radio_loadMode.clicked.connect(lambda:self.Mode(self.Radio_loadMode))
        group_tension.addButton(self.Radio_loadMode)

        box_control = QHBoxLayout()
        box_control.addSpacing (1)
        box_control.addStretch()
        box_control.addWidget(label_control)
        box_control.addSpacing (1)
        box_control.addStretch()
        box_control.addWidget(sepTestV[0])
        box_control.addSpacing (1)
        box_control.addStretch()
        ################################
        box__radio_infl = QVBoxLayout()
        box__radio_infl.addWidget(self.Radio_volumeMode)
        box__radio_infl.addWidget(self.Radio_pressureMode)

        ################################
        box_control.addLayout(box__radio_infl)

        box_control.addSpacing (1)
        box_control.addStretch()
        box_control.addWidget(sepTestV[1])

        ################################
        box__radio_tension = QVBoxLayout()
        box__radio_tension.addWidget(self.Radio_dispMode)
        box__radio_tension.addWidget(self.Radio_loadMode)

        ################################
        box_control.addLayout(box__radio_tension)

        #######################################################
                # Choose the pre-loading
        #######################################################

        label_preloading = QLabel()    #Creation of the widget
        label_preloading.setText(" Pre-conditioning: ")    #Set the text in the label
        label_preloading.setAlignment(Qt.AlignCenter)   #Alignement of the label (here it's the vertical alignement)
        label_preloading.setFont(QFont("Arial",16,QFont.Bold))    #Set the writing font with the size

        #####################
        self.input_cycleF = QLineEdit()
        self.input_cycleF.setValidator(QDoubleValidator())
        self.input_cycleF.setMaxLength(2)
        self.input_cycleF.setAlignment(Qt.AlignRight)
        self.input_cycleF.setFont(QFont("Arial",12))
        #input_vol_pressure.setCursor(QCursor(Qt.ArrowCursor))
        self.input_cycleF.setStyleSheet("QLineEdit{background: white;}")

        self.input_cycleF.palette = QPalette()
        self.input_cycleF.palette.setColor(QPalette.Text, Qt.black)
        self.input_cycleF.setPalette(self.input_cycleF.palette)

        self.label_cyclF = QLabel("Cycle Motor:", self)
        self.label_cyclF.setFont(QFont("Arial",12,QFont.Bold))

        #Layout input label + input number
        form_cycleF = QFormLayout()     #Creation of the layout form
        form_cycleF.addRow(self.label_cyclF, self.input_cycleF)     #Add things in the layout

        #Add the button "Send" to the previous layout by putting all that in another layout
        box_cycle_inputF = QHBoxLayout()
        box_cycle_inputF.addLayout(form_cycleF)

        #####################
        self.input_cycleP = QLineEdit()
        self.input_cycleP.setValidator(QDoubleValidator())
        self.input_cycleP.setMaxLength(2)
        self.input_cycleP.setAlignment(Qt.AlignRight)
        self.input_cycleP.setFont(QFont("Arial",12))
        self.input_cycleP.setStyleSheet("QLineEdit{background: white;}")

        self.input_cycleP.palette = QPalette()
        self.input_cycleP.palette.setColor(QPalette.Text, Qt.black)
        self.input_cycleP.setPalette(self.input_cycleP.palette)

        self.label_cyclP = QLabel("Cycle Pump:", self)
        self.label_cyclP.setFont(QFont("Arial",12,QFont.Bold))

        #Layout input label + input number
        form_cycleP = QFormLayout()     #Creation of the layout form
        form_cycleP.addRow(self.label_cyclP, self.input_cycleP)     #Add things in the layout

        #Add the button "Send" to the previous layout by putting all that in another layout
        box_cycle_inputP = QHBoxLayout()
        box_cycle_inputP.addLayout(form_cycleP)

        box_preloading = QHBoxLayout()
        box_preloading.addSpacing (5)
        box_preloading.addLayout(box_cycle_inputF)
        box_preloading.addStretch()
        box_preloading.addLayout(box_cycle_inputP)
        box_preloading.addSpacing (5)

########################################################################################
########################################################################################
#######################################################
        #Choose command send
#######################################################

        label_commands = QLabel()    #Creation of the widget
        label_commands.setText(" Commands: ")    #Set the text in the label
        label_commands.setAlignment(Qt.AlignCenter)   #Alignement of the label (here it's the vertical alignement)
        label_commands.setFont(QFont("Arial",16,QFont.Bold))    #Set the writing font with the size

        ####################
        #Widget with user input for changing load
        self.input_disp_load = QLineEdit()   #Creation of the widget
        self.input_disp_load.setValidator(QDoubleValidator())   #Only double accepted
        self.input_disp_load.setMaxLength(10)     #Number of characters accepted
        self.input_disp_load.setAlignment(Qt.AlignRight)
        self.input_disp_load.setFont(QFont("Arial",12))
        self.input_disp_load.setStyleSheet("QLineEdit{background: white;}")    #Background color of the widget

        #hChange the writing color from write to black because we set the background write (I like when it's beautiful)
        self.input_disp_load.palette = QPalette()
        self.input_disp_load.palette.setColor(QPalette.Text, Qt.black)
        self.input_disp_load.setPalette(self.input_disp_load.palette)

        #Creation of the label attached to the QLineEdit
        self.label_load_input = QLabel("Disp (mm):", self)
        self.label_load_input.setFont(QFont("Arial",12,QFont.Bold))

        #Put the QLineEdit and the label together (like a form)
        form_load = QFormLayout()     #Creation of the layout form
        form_load.addRow(self.label_load_input, self.input_disp_load)     #Add things in the layout (by row)

        #Add the button "Send" to the previous layout by putting all that in another layout
        box_load_input = QHBoxLayout()
        box_load_input.addLayout(form_load)
        #####################

        #####################
        #Widget with user input for changing pressure
        self.input_vol_pressure = QLineEdit()
        self.input_vol_pressure.setValidator(QDoubleValidator())
        self.input_vol_pressure.setMaxLength(10)
        self.input_vol_pressure.setAlignment(Qt.AlignRight)
        self.input_vol_pressure.setFont(QFont("Arial",12))
        self.input_vol_pressure.setStyleSheet("QLineEdit{background: white;}")

        self.input_vol_pressure.palette = QPalette()
        self.input_vol_pressure.palette.setColor(QPalette.Text, Qt.black)
        self.input_vol_pressure.setPalette(self.input_vol_pressure.palette)

        self.label_pressure_input = QLabel("Pressure (mmHg):", self)
        self.label_pressure_input.setFont(QFont("Arial",12,QFont.Bold))

        #Layout input label + input number
        form_pressure = QFormLayout()     #Creation of the layout form
        form_pressure.addRow(self.label_pressure_input, self.input_vol_pressure)     #Add things in the layout

        #Add the button "Send" to the previous layout by putting all that in another layout
        box_pressure_input = QHBoxLayout()
        box_pressure_input.addLayout(form_pressure)

         #####################

        box_command = QHBoxLayout()
        box_command.addSpacing (5)
        box_command.addLayout(box_load_input)
        box_command.addSpacing (5)
        box_command.addWidget(sepTestV[3])
        box_command.addSpacing (5)
        box_command.addLayout(box_pressure_input)
        box_command.addSpacing (5)
        box_command.addWidget(sepTestV[4])
        box_command.addSpacing (5)

        #######################################################
                #Stop & restart
        #######################################################

        self.button_pause = QPushButton( self)
        self.button_pause.clicked.connect(self.clickMethodPause)
        self.button_pause.setIcon(QIcon(resource_path('tension_inflation/resources/button_pause.png')))
        self.button_pause.setStatusTip('Stop the test')
        self.button_pause.setFixedSize( 60, 50 )
        self.button_pause.setIconSize(QSize(40, 40))

        self.button_restart = QPushButton( self)
        self.button_restart.clicked.connect(self.clickMethodRestart)
        self.button_restart.setIcon(QIcon(resource_path('tension_inflation/resources/button_restart.png')))
        self.button_restart.setStatusTip('Restart the test')
        self.button_restart.setFixedSize( 60, 50 )
        self.button_restart.setIconSize(QSize(40, 40))

        box_break = QHBoxLayout()
        box_break.addSpacing (20)
        box_break.addStretch()
        box_break.addWidget(self.button_pause)
        box_break.addSpacing (20)
        box_break.addStretch()
        box_break.addWidget(self.button_restart)
        box_break.addSpacing (20)
        box_break.addStretch()

        #######################################################
                #Graph definition
        #######################################################

        # Button for clear the graphs
        self.clrGraphButton = QPushButton('Clear Graphs', self)
        self.clrGraphButton.clicked.connect(self.clickMethodClearGraphs)
        self.clrGraphButton.setStatusTip('Clear the graphs')
        self.clrGraphButton.setIconSize(QSize(100,100))

        self.graph1 = pg.PlotWidget(self, title="Graph 1")   #Creation of the graphic widget
        self.graph1.setMinimumSize(300,300)    #Set the minimum size of the graph
        self.graph1.showGrid(x=True, y=True)
        self.graph1.setLabel('left', 'Load (N)')
        self.graph1.setLabel('bottom', 'Pressure (mmHg)')

        self.graphL=self.graph1.plot(pen=None, symbol='+')    #Show on the graph

        self.graph2 = pg.PlotWidget(self, title="Graph 2")   #Creation of the graphic widget
        self.graph2.setMinimumSize(300,300)    #Set the minimum size of the graph
        self.graph2.showGrid(x=True, y=True)
        self.graph2.setLabel('left', 'Pressure (mmHg)')
        self.graph2.setLabel('bottom', 'Time (s)')

        self.graphR=self.graph2.plot(pen=None, symbol='+')    #Show on the graph

        group_graph = QButtonGroup(self.centralWidget)
        #Add a radio button to select mode of command
        self.Radio_graphON = QRadioButton("Graph: On")

        self.Radio_graphON.clicked.connect(lambda:self.graph_state(self.Radio_graphON))
        group_graph.addButton(self.Radio_graphON)

        self.Radio_graphOFF = QRadioButton("Graph: Off")
        self.Radio_graphOFF.setChecked(True)
        self.Radio_graphOFF.clicked.connect(lambda:self.graph_state(self.Radio_graphOFF))
        group_graph.addButton(self.Radio_graphOFF)

        #Creat the layout where to put the graph (and other things)
        graph_layout = QHBoxLayout()
        graph_layout.addWidget(self.graph1)
        graph_layout.addWidget(self.graph2)
        graph_mode = QHBoxLayout()
        graph_mode.addWidget(self.Radio_graphON)
        graph_mode.addWidget(self.Radio_graphOFF)
        graph_mode.addWidget(self.clrGraphButton)

        #######################################################
        #######################################################

        self.panel_test_layout = QVBoxLayout()
        self.panel_test_layout.addLayout(box_top)
        self.panel_test_layout.addWidget(sepTestH[0])
        self.panel_test_layout.addLayout(box_control)
        self.panel_test_layout.addWidget(sepTestH[1])

        self.panel_test_layout.addWidget(label_preloading)
        self.panel_test_layout.addSpacing (20)
        self.panel_test_layout.addLayout(box_preloading)
        self.panel_test_layout.addWidget(sepTestH[2])

        self.panel_test_layout.addWidget(label_commands)
        self.panel_test_layout.addSpacing (20)
        self.panel_test_layout.addLayout(box_command)
        self.panel_test_layout.addWidget(sepTestH[3])
        self.panel_test_layout.addLayout(box_break)
        self.panel_test_layout.addWidget(sepTestH[4])
        self.panel_test_layout.addLayout(graph_mode)
        self.panel_test_layout.addLayout(graph_layout)

    ###################################
    #Functions !!
    ###################################
    #Change the mode of command the pump when push the radio button

    def Mode(self, radio_mode):
        """Change the command mode (volume / pressure)"""
        if radio_mode.text() == "Mode: Volume":
            if radio_mode.isChecked() == True:
                self.label_pressure_input.setText("Volume (mL):")    #Set the text in the label
                self.PVmode = 'V'

                print("Mode volume is selected")

        if radio_mode.text() == "Mode: Pressure":
            if radio_mode.isChecked() == True:
                self.label_pressure_input.setText("Pressure (mmHg):")    #Set the text in the label
                self.PVmode = 'P'

                print("Mode pressure is selected")

        if radio_mode.text() == "Mode: Displacement":
            if radio_mode.isChecked() == True:
                self.label_load_input.setText("Disp (mm):")    #Set the text in the label
                self.FDmode = 'D'

                print("Mode disp is selected")

        if radio_mode.text() == "Mode: Load":
            if radio_mode.isChecked() == True:
                self.label_load_input.setText("Load (N):")    #Set the text in the label
                self.FDmode = 'F'
                print("Mode load is selected")

    def graph_state(self, test):
        if test.text() == "Graph: On":
            self.file_save = 'dumb'
            self.running = True
            self.time_ini_graph = time.time()
        elif test.text() == "Graph: Off":
            self.running = False

    def clickMethodPause(self):
        """pause"""
        self.pause = True
        self.pump_run = False
        self.button_pause.setIcon(QIcon(resource_path('tension_inflation/resources/button_pause_push.png')))
        self.CommandThread.stop()
        self.pause_phase()

        #Write in the savefile
        with open(self.path + '/' + self.file_save + '.txt', 'a') as mon_fichier:
            mon_fichier.write("\nPause\n\n")

    def clickMethodRestart(self):
        """restart"""

        self.button_pause.setIcon(QIcon(resource_path('tension_inflation/resources/button_pause.png')))
        if not self.pause:
            return

        with open(self.path + '/' + self.file_save + '.txt', 'a') as mon_fichier:
            mon_fichier.write("\nEnd Pause\n\n")

        if not self.input_disp_load.text() == '':
            disp_load_target = float(self.input_disp_load.text().replace(',','.'))
        else:
            disp_load_target=''
        if not self.input_vol_pressure.text() == '':
            vol_pressure_target = float(self.input_vol_pressure.text())
        else:
            vol_pressure_target=''
        if not self.input_cycleF.text() == '':
            cycleF = float(self.input_cycleF.text())
        else:
            cycleF = 0
        if not self.input_cycleP.text() == '':
            cycleP = float(self.input_cycleP.text())
        else:
            cycleP = 0

        self.CommandThread = CommandThread(disp_load_target, vol_pressure_target, cycleF, cycleP, self.PVmode, self.FDmode)
        self.CommandThread.signal_end.connect(self.test_end)
        self.CommandThread.update_thread.connect(self.update_value)
        self.CommandThread.start()

        self.pause = False
        self.running_phase()
        # Restart the pump

    def clickMethodClearGraphs(self):

        reply = QMessageBox.question(self, 'Annihilation activated',
                         'Clear them all ?!', QMessageBox.Yes, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.array_F = []
            self.array_P = []
            self.array_d = []
            self.array_t = []
            self.time_ini_graph = time.time()
            self.graphL.clear()
            self.graphR.clear()


from __future__ import absolute_import, division, print_function, unicode_literals

import cv2
import numpy as np
import os

from .model import MODELS

from .data import numDetectionWindowsAlongAxis, squashCoords, MIN_FACE_SCALE, OFFSET, SCALES, CALIB_PATTERNS_ARR, OBJECT_DATABASE_PATHS, NEGATIVE_DATABASE_PATHS
from .preprocess import ImageNormalizer

IOU_THRESH = .5
NET_12_THRESH = .009
NET_24_THRESH = .076
NET_48_THRESH = .3

GOLDEN_RATIO = (1+5**.5)/2

NUM_PYRAMID_LEVELS = 5
PYRAMID_DOWNSCALE = 1.25

def IoU(boxes, box, area=None):
    int_x1, int_y1, int_x2, int_y2 = ((np.maximum if i < 2 else np.minimum)(boxes[:, i], box[i]) for i in np.arange(boxes.shape[1]))
    area = (boxes[:,2]-boxes[:,0])*(boxes[:,3]-boxes[:, 1]) if not area else area
    int_area = (np.maximum(0,int_x2-int_x1))*(np.maximum(0,int_y2-int_y1))
    union_area = area+(box[2]-box[0])*(box[3]-box[1])-int_area
    int_over_union = int_area/union_area
    return int_over_union

def nms(boxes, predictions, iouThresh = IOU_THRESH):
    idxs = np.argsort(predictions)
    picked = []

    while len(idxs) > 0:
        pick = boxes[idxs[-1]]
        picked.append(idxs[-1])

        int_over_union = IoU(boxes[idxs[:-1]], pick)
        idxs = np.delete(idxs, np.concatenate(([len(idxs)-1],np.where(int_over_union > iouThresh)[0])))

    return boxes[picked], picked

def local_nms(boxes, predictions, pyrIdxs, iouThresh = IOU_THRESH):
    suppressedBoxes = np.zeros((0, *boxes.shape[1:]))
    prevIdx = 0
    newPyrIdxs = []
    picked = []

    for curIdx in pyrIdxs:
        localSuppressedBoxes, localPicked = nms(boxes[prevIdx:curIdx], predictions[prevIdx:curIdx], iouThresh)
        suppressedBoxes = np.vstack((suppressedBoxes, localSuppressedBoxes))
        picked.extend(localPicked)
        prevIdx = curIdx
        newPyrIdxs.append(suppressedBoxes.shape[0])

    return suppressedBoxes, picked, newPyrIdxs

def getDetectionWindows(img, scale, minFaceScale = MIN_FACE_SCALE, numPyramidLevels = NUM_PYRAMID_LEVELS, pyramidDownscale = PYRAMID_DOWNSCALE):
    numPyramidLevels = min(int(np.log(min(img.shape[:2])/minFaceScale)/np.log(pyramidDownscale)), numPyramidLevels)
    resized = [cv2.resize(img, None, fx = scale/(minFaceScale*pyramidDownscale**i), fy = scale/(minFaceScale*pyramidDownscale**i)) for i in np.arange(numPyramidLevels)]
    numDetectionWindows = sum((numDetectionWindowsAlongAxis(img.shape[0])*numDetectionWindowsAlongAxis(img.shape[1]) for img in resized))
    yield numDetectionWindows

    for pyrLevel, img in enumerate(resized):
        for yIdx in np.arange(numDetectionWindowsAlongAxis(img.shape[0])):
            for xIdx in np.arange(numDetectionWindowsAlongAxis(img.shape[1])):
                xMin, yMin, xMax, yMax = (xIdx*OFFSET, yIdx*OFFSET, xIdx*OFFSET+scale, yIdx*OFFSET+scale)
                yield (pyrLevel, xMin, yMin, xMax, yMax, img[yMin:yMax, xMin:xMax])

def calibrateCoordinates(coords, calibPredictions):
    calibTransformations = CALIB_PATTERNS_ARR[np.argmax(calibPredictions, axis=1)]
    sn = calibTransformations[:, 0]
    dimensions = (coords[:, 2:]-coords[:, :2])/sn[:, None]
    coords[:, :2] -= dimensions*calibTransformations[:, 1:]
    coords[:, 2:] = coords[:, :2] + dimensions
    return coords

def getNetworkInputs(img, curScale, coords):
    inputs = np.ones((len(coords), curScale, curScale, 3), dtype=np.uint8)

    for i, (xMin, yMin, xMax, yMax) in enumerate(coords.astype(np.int32)):
        xMin, yMin, w, h = squashCoords(img, xMin, yMin, xMax-xMin, yMax-yMin)
        inputs[i] = cv2.resize(img[yMin:yMin+h, xMin:xMin+w], (curScale, curScale))

    return inputs

MODELS = [(MODELS[False][stageIdx], MODELS[True][stageIdx]) for stageIdx in np.arange(len(SCALES))]
PATHS = []
NORMALIZERS = []
THRESHOLDS = (NET_12_THRESH, NET_24_THRESH, NET_48_THRESH)

for stageIdx in np.arange(len(SCALES)):
    if os.path.isfile(OBJECT_DATABASE_PATHS[stageIdx]) and os.path.isfile(NEGATIVE_DATABASE_PATHS[stageIdx]):
        PATHS.append((OBJECT_DATABASE_PATHS[stageIdx], NEGATIVE_DATABASE_PATHS[stageIdx]))
        NORMALIZERS.append(tuple((ImageNormalizer(PATHS[stageIdx][0], PATHS[stageIdx][1], MODELS[stageIdx][isCalib].getNormalizationMethod()) for isCalib in (0, 1))))

def detectMultiscale(img, maxStageIdx=len(SCALES)-1, minFaceScale = MIN_FACE_SCALE):
    classifierInputs = []
    calibratorInputs = []

    for stageIdx in np.arange(0, maxStageIdx + 1):
        curScale = SCALES[stageIdx][0]

        if stageIdx == 0:
            detectionWindowGenerator = getDetectionWindows(img, curScale)
            totalNumDetectionWindows = next(detectionWindowGenerator)
            detectionWindows = np.zeros((totalNumDetectionWindows, curScale, curScale, 3))
            coords = np.zeros((totalNumDetectionWindows, 4))
            pyrIdxs = []
            prevPyrLevel = 0

            for i, (pyrLevel, xMin, yMin, xMax, yMax, detectionWindow) in enumerate(detectionWindowGenerator):
                detectionWindows[i] = detectionWindow
                coords[i] = (xMin, yMin, xMax, yMax)
                coords[i] *= PYRAMID_DOWNSCALE ** pyrLevel

                if pyrLevel != prevPyrLevel:
                    prevPyrLevel = pyrLevel
                    pyrIdxs.append(i)

            pyrIdxs.append(len(detectionWindows))
            coords *= minFaceScale/curScale
        else:
            for i in np.arange(0, stageIdx):
                classifierInputs[i] = classifierInputs[i][posDetectionIndices][picked]

        classifierNormalizer, calibNormalizer = NORMALIZERS[stageIdx]
        classifier, calibrator = MODELS[stageIdx]

        detectionWindows = detectionWindows if stageIdx == 0 else getNetworkInputs(img, curScale, coords).astype(np.float)
        classifierInputs.insert(0 if stageIdx < 2 else stageIdx, classifierNormalizer.preprocess(detectionWindows))
        predictions = classifier.predict(classifierInputs)[:,1]
        posDetectionIndices = np.where(predictions>=THRESHOLDS[stageIdx])

        calibratorInputs = [calibNormalizer.preprocess(detectionWindows[posDetectionIndices])]
        calibPredictions = calibrator.predict(calibratorInputs)
        coords = calibrateCoordinates(coords[posDetectionIndices], calibPredictions)

        if stageIdx == len(SCALES)-1:
            coords, picked = nms(coords, predictions[posDetectionIndices], iouThresh = .3)
        else:
            coords, picked, pyrIdxs = local_nms(coords, predictions[posDetectionIndices], pyrIdxs)

        if stageIdx == maxStageIdx:
            return coords.astype(np.int32, copy=False)


# source : http://dlib.net/face_recognition.py.html
"""
Davis E. King. Dlib-ml: A Machine Learning Toolkit. Journal of Machine Learning Research 10, pp. 1755-1758, 2009

@Article{dlib09,
  author = {Davis E. King},
  title = {Dlib-ml: A Machine Learning Toolkit},
  journal = {Journal of Machine Learning Research},
  year = {2009},
  volume = {10},
  pages = {1755-1758},
}
"""

import dlib
import numpy as np
from functools import partial

def recognize_face(img, face_recognizer, shape_predictor, bounding_box_coordinates, net_jitter=None):
    """
    Converts the cropped face image into 128D Feature vector
    Note: image size should be 128x128x3

    :param img: numpy array
        The image should be cropped image

    :param face_recognizer: dlib.face_recognition_model_v1
        pre-inititlized dlib_face_recognition_resnet_model_v1

    :param shape_predictor: dlib.shape_predictor
        pre-inititlized dlib shape predictor

    :param bounding_box_coordinates: [x1, y1, x2, y2]
        (x1, y1)-----
           |        |
           |        |
           -----(x2, y2)

    :return: numpy array
        128D Feature vector representing the face img provided
    """
    shape = shape_predictor(img, dlib.rectangle(*bounding_box_coordinates))

    # Let's generate the aligned image using get_face_chip
    face_chip = dlib.get_face_chip(img, shape)

    # Now we simply pass this chip (aligned image) to the api
    if net_jitter:
        return np.array(face_recognizer.compute_face_descriptor(face_chip, net_jitter))
    else:
        return np.array(face_recognizer.compute_face_descriptor(face_chip))

def initialize_models(face_recognizer_path, shape_predictor_path):
    """
    initilaizes the models which takes approx 400ms

    :param face_recognizer_path: str
        path to dlib_face_recognition_resnet_model_v1.dat

    :param shape_predictor_path: str
        path to shape_predictor_5_face_landmarks.dat

    :return: dlib.face_recognition_model_v1, dlib.shape_predictor
    """
    # defining the models and initializing them
    return dlib.face_recognition_model_v1(face_recognizer_path), dlib.shape_predictor(shape_predictor_path)

def compare_face(vec1, vec2):
    """
    Returns Euclidean Distance between two feature vectors

    :param vec1: numpy array
        Feature vector 1
    :param vec2: numpy array
        Feature vector 2
    :return: float
        Euclidean distance between the feature vectors
    """
    return np.linalg.norm(vec1 - vec2, axis=0)

def generate_face_compare_fn(saved_feature_vector):
    """
    Returns compare face function with hard-coding already saved feature encoding.

    :param saved_feature_vector: numpy array
    :return: partial functool wrapper for compare face function
    """
    return partial(compare_face, saved_feature_vector)

# todo save face description function with more sampling of the face descriptor function for accuracy