import torch
import colorsys
import numpy as np
import argparse
import yaml
from utils.utils import  update_values, letterbox_image
from model import yolo_layer
import time
import cv2

class yolo_detect(object):
    def __init__(self, args):
        self.args = args
        self.model_image_size = (args.height, args.width)
        self.class_names = self.args.classes_names
        self.model = self.creat_model()
        self.model.eval()

    def creat_model(self):
        model_path = self.args.inference_path
        model = yolo_layer.yolov3layer(self.args)
        model.load_state_dict(torch.load(model_path))
        hsv_tuples = [(x / len(self.class_names), 1., 1.)
                      for x in range(len(self.class_names))]
        self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
        self.colors = list(
            map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)),
                self.colors))
        if self.args.use_cuda:
            if self.args.mGPUs:
                model = torch.nn.DataParallel(model).cuda()
            else:
                model = model.cuda()
        return model

    def detect(self, image):
        time_crop_img_s = time.time()
        if self.model_image_size != (None, None):
            assert self.model_image_size[0]%32 == 0, 'Multiples of 32 required'
            assert self.model_image_size[1]%32 == 0, 'Multiples of 32 required'
            boxed_image = letterbox_image(image, tuple(reversed(self.model_image_size)))
        else:
            new_image_size = (image.width - (image.width % 32),
                              image.height - (image.height % 32))
        time_crop_img_e = time.time()
        print('Time consuming of image crop:', time_crop_img_e - time_crop_img_s)
        time_inference_s = time.time()
        image_data = np.array(boxed_image, dtype='float32') / 255.0
        img = torch.from_numpy(image_data).float().cuda()
        img = img.unsqueeze(0)
        img = img.view(img.shape[0], img.shape[1], img.shape[2], img.shape[3]).permute(0, 3, 1, 2).contiguous()
        img_ori_shape = torch.Tensor([image.shape[0], image.shape[1]])
        dets, images, classes = self.model.detect(img, img_ori_shape)
        time_inference_e = time.time()
        print('Time consuming of inference:', time_inference_e - time_inference_s)
        dets_arr = dets.cpu().numpy()
        classes_arr = classes.cpu().numpy()
        time_draw_s = time.time()
        # font = ImageFont.truetype(font='font/FiraMono-Medium.otf',
        #             size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32'))
        for i, c in enumerate(classes_arr):
            c = int(c)
            predicted_class = self.class_names[c]
            box = dets_arr[i]

            top, left, bottom, right, score = box
            label = '{} {:.2f}'.format(predicted_class, score)

            top = max(0, np.floor(top + 0.5).astype('int32'))
            left = max(0, np.floor(left + 0.5).astype('int32'))
            bottom = min(image.shape[0], np.floor(bottom + 0.5).astype('int32'))
            right = min(image.shape[1], np.floor(right + 0.5).astype('int32'))
            print(label, (left, top), (right, bottom))

            cv2.rectangle(image, (left, top), (right, bottom), self.colors[c], 2)
        time_draw_e = time.time()
        print('Time consuming of Drawing image:', time_draw_e - time_draw_s)
        return image

def detect_img_test(yolo):
    while 1:
        image = input("input image:")
        try:
            time_load_start = time.time()
            image = cv2.imread(image)
            time_load_end = time.time()
            print('Time consuming of image loading:', time_load_end - time_load_start)
        except:
            print('Open Error! Try again!')
            continue
        else:
            r_image = yolo.detect(image)
            time_end = time.time()
            print('Time consuming of object detection based on yolo3', time_end - time_load_start)
            cv2.namedWindow("result", cv2.WINDOW_NORMAL)
            cv2.imshow('result', r_image)
            cv2.waitKey(0)

def main():
    args = make_args()
    with open(args.cfg_path, 'r') as handle:
        options_yaml = yaml.load(handle)
    update_values(options_yaml, vars(args))
    detect_img_test(yolo_detect(args))

def make_args():
    # load the optional parameters and update new arguments
    parser = argparse.ArgumentParser()
    # # Data input settings
    parser.add_argument('--cfg_path', type=str, default='cfgs/Yolo_detect.yml', help='load config')
    parser.add_argument('--use_cuda', type=bool, default=True, help='whether use gpu.')
    parser.add_argument('--mGPUs', type=bool, default=False, help='whether use mgpu.')
    return parser.parse_args()

if __name__ == '__main__':
    main()


from vsbrnn.utils import Rect
import numpy as np

class RegionModel:
    def __init__(self):
        self.model_TGH = {}
        self.model_TWH = {}
        self.ignore_fixations_outside = False
        self.segmentation = None
        self.region_type = None

    @classmethod
    def from_segmentation(cls, segmentation):
        cls = RegionModel()
        cls.region_type = "segmentation"
        cls.segmentation = segmentation
        return cls

    def add_region_TGH(self, name, x, y, w, h):
        self.model_TGH[name] = Rect(x, y, w, h)

    def add_region_TWH(self, name, x, y, w, h):
        self.model_TWH[name] = Rect(x, y, w, h)

    def fix_in_segmentation(self, fix):
        x = np.floor(fix.x * self.segmentation.shape[0])
        y = np.floor(fix.y * self.segmentation.shape[1])
        if x < 0 or x >= self.segmentation.shape[0] or y < 0 or y >= self.segmentation.shape[1]:
            return np.max(self.segmentation) + 2
        else:
            return self.segmentation[int(x), int(y)] + 1

    def fix_in_bounding_box(self, test, fix):
        if "TWH" in test:
            models = self.model_TWH
        else:
            models = self.model_TGH
        for key in models:
            model = models[key]
            if model.isPointInRect(fix.x, fix.y):
                return key
        return "N"

    def fix_in_region(self, test, fix):
        if self.region_type == "segmentation":
            return self.fix_in_segmentation(fix)
        elif self.region_type == "bounding_box":
            return self.fix_in_bounding_box(test, fix)

class FaceRegionModel_semantic5(RegionModel):
    def __init__(self):
        RegionModel.__init__(self)
        self.region_type = "bounding_box"
        self.add_region_TGH("LE", x=0.0, y=0.25, w=0.45, h=0.2)
        self.add_region_TGH("RE", x=0.55, y=0.25, w=0.45, h=0.2)
        self.add_region_TGH("NO", x=0.275, y=0.45, w=0.45, h=0.2)
        self.add_region_TGH("M", x=0.15, y=0.65, w=0.7, h=0.2)

        self.add_region_TWH("LE", x=0.0, y=0.25, w=0.45, h=0.20)
        self.add_region_TWH("RE", x=0.55, y=0.25, w=0.45, h=0.20)
        self.add_region_TWH("NO", x=0.275, y=0.45, w=0.45, h=0.20)
        self.add_region_TWH("M", x=0.15, y=0.65, w=0.7, h=0.2)

class FaceRegionModel_semantic8(RegionModel):
    def __init__(self):
        RegionModel.__init__(self)
        self.region_type = "bounding_box"
        self.add_region_TGH("FH", x=0.0, y=0.0, w=1, h=0.2)
        self.add_region_TGH("LE", x=0.0, y=0.25, w=0.45, h=0.2)
        self.add_region_TGH("RE", x=0.55, y=0.25, w=0.45, h=0.2)
        self.add_region_TGH("NO", x=0.275, y=0.45, w=0.45, h=0.2)
        self.add_region_TGH("LC", x=0.0, y=0.45, w=0.25, h=0.2)
        self.add_region_TGH("RC", x=0.75, y=0.45, w=0.25, h=0.2)
        self.add_region_TGH("M", x=0.15, y=0.65, w=0.7, h=0.2)

        self.add_region_TWH("FH", x=0.0, y=0.0, w=1, h=0.2)
        self.add_region_TWH("LE", x=0.0, y=0.25, w=0.45, h=0.20)
        self.add_region_TWH("RE", x=0.55, y=0.25, w=0.45, h=0.20)
        self.add_region_TWH("NO", x=0.275, y=0.45, w=0.45, h=0.20)
        self.add_region_TWH("LC", x=0.0, y=0.45, w=0.25, h=0.2)
        self.add_region_TWH("RC", x=0.75, y=0.45, w=0.25, h=0.2)
        self.add_region_TWH("M", x=0.15, y=0.65, w=0.7, h=0.2)

class FaceRegionModel4(RegionModel):
    def __init__(self):
        RegionModel.__init__(self)
        self.region_type = "bounding_box"
        self.ignore_fixations_outside = True
        self.add_region_TGH("FH1", x=0.0, y=0.0, w=0.38, h=0.25)
        self.add_region_TGH("FH2", x=0.38, y=0.0, w=0.24, h=0.25)
        self.add_region_TGH("FH3", x=0.62, y=0.0, w=0.38, h=0.25)

        self.add_region_TGH("LE", x=0.0, y=0.25, w=0.38, h=0.21)
        self.add_region_TGH("NT", x=0.38, y=0.25, w=0.24, h=0.21)
        self.add_region_TGH("RE", x=0.62, y=0.25, w=0.38, h=0.21)

        self.add_region_TGH("LC", x=0.0, y=0.46, w=0.38, h=0.21)
        self.add_region_TGH("NO", x=0.38, y=0.46, w=0.24, h=0.21)
        self.add_region_TGH("RC", x=0.62, y=0.46, w=0.38, h=0.21)

        self.add_region_TGH("LM", x=0.0, y=0.67, w=0.38, h=0.33)
        self.add_region_TGH("M", x=0.38, y=0.67, w=0.24, h=0.33)
        self.add_region_TGH("RM", x=0.62, y=0.67, w=0.38, h=0.33)

        self.add_region_TWH("FH1", x=0.0, y=0.0, w=0.38, h=0.25)
        self.add_region_TWH("FH2", x=0.38, y=0.0, w=0.24, h=0.25)
        self.add_region_TWH("FH3", x=0.62, y=0.0, w=0.38, h=0.25)

        self.add_region_TWH("LE", x=0.0, y=0.25, w=0.38, h=0.21)
        self.add_region_TWH("NT", x=0.38, y=0.25, w=0.24, h=0.21)
        self.add_region_TWH("RE", x=0.62, y=0.25, w=0.38, h=0.21)

        self.add_region_TWH("LC", x=0.0, y=0.46, w=0.38, h=0.21)
        self.add_region_TWH("NO", x=0.38, y=0.46, w=0.24, h=0.21)
        self.add_region_TWH("RC", x=0.62, y=0.46, w=0.38, h=0.21)

        self.add_region_TWH("LM", x=0.0, y=0.67, w=0.38, h=0.33)
        self.add_region_TWH("M", x=0.38, y=0.67, w=0.24, h=0.33)
        self.add_region_TWH("RM", x=0.62, y=0.67, w=0.38, h=0.33)

class FaceRegionModel_grid9(RegionModel):
    def __init__(self):
        RegionModel.__init__(self)
        self.region_type = "bounding_box"
        self.ignore_fixations_outside = True
        self.add_region_TGH("FH1", x=0.0, y=0.0, w=0.33, h=0.33)
        self.add_region_TGH("FH2", x=0.33, y=0.0, w=0.33, h=0.33)
        self.add_region_TGH("FH3", x=0.66, y=0.0, w=0.33, h=0.33)

        self.add_region_TGH("LE", x=0.0, y=0.33, w=0.33, h=0.33)
        self.add_region_TGH("NT", x=0.33, y=0.33, w=0.33, h=0.33)
        self.add_region_TGH("RE", x=0.66, y=0.33, w=0.33, h=0.33)

        self.add_region_TGH("LC", x=0.0, y=0.66, w=0.33, h=0.33)
        self.add_region_TGH("NO", x=0.33, y=0.66, w=0.33, h=0.33)
        self.add_region_TGH("RC", x=0.66, y=0.66, w=0.33, h=0.33)

        self.add_region_TWH("FH1", x=0.0, y=0.0, w=0.33, h=0.33)
        self.add_region_TWH("FH2", x=0.33, y=0.0, w=0.33, h=0.33)
        self.add_region_TWH("FH3", x=0.66, y=0.0, w=0.33, h=0.33)

        self.add_region_TWH("LE", x=0.0, y=0.33, w=0.33, h=0.33)
        self.add_region_TWH("NT", x=0.33, y=0.33, w=0.33, h=0.33)
        self.add_region_TWH("RE", x=0.66, y=0.33, w=0.33, h=0.33)

        self.add_region_TWH("LC", x=0.0, y=0.66, w=0.33, h=0.33)
        self.add_region_TWH("NO", x=0.33, y=0.66, w=0.33, h=0.33)
        self.add_region_TWH("RC", x=0.66, y=0.66, w=0.33, h=0.33)

class FaceRegionModel_grid16(RegionModel):
    def __init__(self):
        RegionModel.__init__(self)
        self.region_type = "bounding_box"
        self.ignore_fixations_outside = True
        self.add_region_TGH("FH1", x=0.0, y=0.0, w=0.25, h=0.25)
        self.add_region_TGH("FH2", x=0.25, y=0.0, w=0.25, h=0.25)
        self.add_region_TGH("FH3", x=0.50, y=0.0, w=0.25, h=0.25)
        self.add_region_TGH("FH4", x=0.75, y=0.0, w=0.25, h=0.25)

        self.add_region_TGH("LE1", x=0.0, y=0.25, w=0.25, h=0.25)
        self.add_region_TGH("LE2", x=0.25, y=0.25, w=0.25, h=0.25)
        self.add_region_TGH("RE1", x=0.50, y=0.25, w=0.25, h=0.25)
        self.add_region_TGH("RE2", x=0.75, y=0.25, w=0.25, h=0.25)

        self.add_region_TGH("LC", x=0.0, y=0.50, w=0.25, h=0.25)
        self.add_region_TGH("NO1", x=0.25, y=0.50, w=0.25, h=0.25)
        self.add_region_TGH("NO2", x=0.50, y=0.50, w=0.25, h=0.25)
        self.add_region_TGH("RC", x=0.75, y=0.50, w=0.25, h=0.25)

        self.add_region_TGH("M1", x=0.0, y=0.75, w=0.25, h=0.25)
        self.add_region_TGH("M2", x=0.25, y=0.75, w=0.25, h=0.25)
        self.add_region_TGH("M3", x=0.50, y=0.75, w=0.25, h=0.25)
        self.add_region_TGH("M4", x=0.75, y=0.75, w=0.25, h=0.25)

        self.add_region_TWH("FH1", x=0.0, y=0.0, w=0.25, h=0.25)
        self.add_region_TWH("FH2", x=0.25, y=0.0, w=0.25, h=0.25)
        self.add_region_TWH("FH3", x=0.50, y=0.0, w=0.25, h=0.25)
        self.add_region_TWH("FH4", x=0.75, y=0.0, w=0.25, h=0.25)

        self.add_region_TWH("LE1", x=0.0, y=0.25, w=0.25, h=0.25)
        self.add_region_TWH("LE2", x=0.25, y=0.25, w=0.25, h=0.25)
        self.add_region_TWH("RE1", x=0.50, y=0.25, w=0.25, h=0.25)
        self.add_region_TWH("RE2", x=0.75, y=0.25, w=0.25, h=0.25)

        self.add_region_TWH("LC", x=0.0, y=0.50, w=0.25, h=0.25)
        self.add_region_TWH("NO1", x=0.25, y=0.50, w=0.25, h=0.25)
        self.add_region_TWH("NO2", x=0.50, y=0.50, w=0.25, h=0.25)
        self.add_region_TWH("RC", x=0.75, y=0.50, w=0.25, h=0.25)

        self.add_region_TWH("M1", x=0.0, y=0.75, w=0.25, h=0.25)
        self.add_region_TWH("M2", x=0.25, y=0.75, w=0.25, h=0.25)
        self.add_region_TWH("M3", x=0.50, y=0.75, w=0.25, h=0.25)
        self.add_region_TWH("M4", x=0.75, y=0.75, w=0.25, h=0.25)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@file          project_parser.py
@brief         parse an USDX note file and the corresponding audio file
               for further processing
@author        paradigm
"""
import os
import sys
import wave
import subprocess
import numpy as np

class ProjectParser:
    """ parse an usdx note file and the corresponding audio file  """

    def __init__(self):
        """ init state variables """
        self.meta = {}
        # list of dictionarys with singable notes
        self.singable = []
        # buffer for note_file content
        self.file_buffer = []
        # derived project path from note file
        self.proj_dir = 0
        # change the ffmpeg path depending on using script or executable
        if getattr(sys, "frozen", False):
            self.ffmpeg = os.path.join(sys._MEIPASS, "ffmpeg.exe")
        else:
            self.ffmpeg = "ffmpeg"

    @property
    def meta(self) -> dict:
        """ getter for metadata """
        return self._meta

    @meta.setter
    def meta(self, meta: dict):
        """ setter for metadata """
        self._meta = meta

    def load_note_file(self, note_file: str):
        """ load and parse usdx project file """
        self.meta.clear()
        self.singable.clear()
        self.file_buffer.clear()
        self.proj_dir = os.path.dirname(note_file)
        notes = open(note_file, "r", encoding="utf-8-sig")
        # buffer file for later reuse
        try:
            self.file_buffer = notes.read().splitlines(True)
        except UnicodeDecodeError:
            # if the file doesn't use utf-8, try ansi
            notes = open(note_file, "r", encoding="iso-8859-1")
            self.file_buffer = notes.read().splitlines(True)
        for line in self.file_buffer:
            # parse header
            if line.startswith("#") and not self.singable:
                # remove trailing whitespaces
                line = line.rstrip("\r").rstrip("\n")
                key, value = line.split(":", 1)
                if key in ("#BPM", "#GAP"):
                    self.meta[key] = float(value.replace(",", "."))
                else:
                    self.meta[key] = value
            # parse singable notes
            elif line.startswith((":", "*")):
                line = line.split(" ")
                pitch = int(line[3]) % 12
                # start = gap + start_beat * (15000 / bpm)
                # the 15000 indicates an 1/16 note, while a beat is usually a 1/4 note
                t_start = self.meta["#GAP"] + float(line[1]) * (
                    15000 / self.meta["#BPM"]
                )
                # end = gap + (start_beat + end_beat) * (15000 / bpm)
                t_end = self.meta["#GAP"] + (
                    float(line[1]) + float(line[2])
                ) * (15000 / self.meta["#BPM"])
                # append line data to singable list
                self.singable.append(
                    {"t_start": t_start, "t_end": t_end, "pitch": pitch}
                )

    def dump_pitches(self) -> list:
        """ returns singable pitches """
        return [singable["pitch"] for singable in self.singable]

    def update_pitches(self, new_pitches: list):
        """ replace old singable pitches by newly calculated ones """
        assert len(new_pitches) == len(
            self.singable
        ), "pitches can't be updated, array size doesn't match!"
        for singable, new_pitch in zip(self.singable, new_pitches):
            singable["pitch"] = new_pitch

    def save_note_file(self, note_file: str):
        """ save updated note file under a new name """
        note_file = open(note_file, "w+", encoding="utf-8")
        singable = iter(self.singable)
        # go through old file and update it when needed
        for line in self.file_buffer:
            if line.startswith((":", "*")):
                line = line.split(" ")
                line[3] = str(int(next(singable)["pitch"]))
                line = " ".join(line)
            note_file.write(line)

    def process_audio(self, sample_rate: int = 16000) -> np.ndarray:
        """ convert, resample and divide audio file into audio segments """
        err_str1 = "{0} has to be an positive integer value"
        assert (
            isinstance(sample_rate, int) and sample_rate > 0
        ), err_str1.format("sample_rate")
        # convert mp3 to temporary mono wav file
        audio_path = os.path.join(self.proj_dir, self.meta["#MP3"])
        wav_path = os.path.join(self.proj_dir, "tmp.wav")
        subprocess._cleanup()
        subprocess.run(
            [
                self.ffmpeg,
                "-i",
                audio_path,
                "-y",
                "-ac",
                "1",
                "-ar",
                str(sample_rate),
                wav_path,
            ],
            stdin=subprocess.DEVNULL,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            check=True,
            shell=False,
        )
        # load wav into numpy array for processing and discard file
        wav_file = wave.open(wav_path)
        samples_mono = np.frombuffer(
            wav_file.readframes(wav_file.getnframes()), dtype="int16"
        )
        wav_file.close()
        os.remove(wav_path)
        for segment in self.singable:
            start_sample = int(round((segment["t_start"] * sample_rate) / 1000))
            end_sample = int(round((segment["t_end"] * sample_rate) / 1000))
            yield samples_mono[start_sample:end_sample]


import os
import numpy as np
from tqdm import tqdm
from sklearn.metrics import confusion_matrix
import sys
sys.path.append('.')

from core.configures import NAME_MAP, evaluating_config
from core.utils.data_utils.image_io_utils import load_image
from core.utils.metric_utils import compute_global_metrics, compute_metrics_per_image

def evaluating_main():
    preds_fnames = os.listdir(evaluating_config.preds_dir)
    label_fnames = os.listdir(evaluating_config.label_dir)
    n_class = len(NAME_MAP[evaluating_config.dataset_name])

    if evaluating_config.mode == "global":
        mat = np.zeros((n_class, n_class))

        for preds_fname, label_fname in tqdm(zip(preds_fnames, label_fnames)):
            print(preds_fname, label_fname)
            preds = load_image(os.path.join(evaluating_config.preds_dir, preds_fname), is_gray=True)
            h, w, _ = preds.shape
            label = load_image(os.path.join(evaluating_config.label_dir, label_fname), is_gray=True, target_size=(h, w))
            _mat = confusion_matrix(label.reshape(-1), preds.reshape(-1), labels=np.arange(n_class))
            mat = mat + _mat
        if evaluating_config.ignore_0:
            mat = mat[1:, 1:]
        avg_metric = compute_global_metrics(mat)

    elif evaluating_config.mode == "per_image":
        avg_metric = {"accuracies_per_class": np.zeros(n_class), "macro_accuracy": 0., "micro_accuracy": 0.,
         "precisions_per_class": np.zeros(n_class), "precision": 0.,
         "recalls_per_class": np.zeros(n_class), "recall": 0.,
         "f1s_pre_class": np.zeros(n_class), "f1": 0.,
         "ious_per_class": np.zeros(n_class), "miou": 0.}
        count = {"accuracies_per_class": np.zeros(n_class), "macro_accuracy": 0, "micro_accuracy": 0,
                 "precisions_per_class": np.zeros(n_class), "precision": 0,
                 "recalls_per_class": np.zeros(n_class), "recall": 0,
                 "f1s_pre_class": np.zeros(n_class), "f1": 0,
                 "ious_per_class": np.zeros(n_class), "miou": 0}
        for preds_fname, label_fname in zip(preds_fnames, label_fnames):
            preds = load_image(os.path.join(evaluating_config.preds_dir, preds_fname), is_gray=True)
            h, w, _ = preds.shape
            label = load_image(os.path.join(evaluating_config.label_dir, label_fname), is_gray=True, target_size=(h, w))

            metric = compute_metrics_per_image(label, preds, n_class)
            for key in metric:
                if not np.isscalar(metric[key]):
                    for i in range(len(metric[key])):
                        if not np.isnan(metric[key][i]):
                            avg_metric[key][i] = avg_metric[key][i] + metric[key][i]
                            count[key][i] += 1
                else:
                    avg_metric[key] = avg_metric[key] + metric[key]
                    count[key] += 1

        for key in avg_metric:
            if not np.isscalar(avg_metric[key]):
                for i in range(len(avg_metric[key])):
                    if not np.isnan(avg_metric[key][i]):
                        avg_metric[key][i] = avg_metric[key][i] / count[key][i]
            else:
                avg_metric[key] = avg_metric[key] / count[key]
    else:
        raise ValueError("Invalid 'mode': %s. Expected to be 'global' or 'per_image'!" % evaluating_config.mode)

    for key in avg_metric:
        print('{:^20s}{}'.format(key, avg_metric[key]))
    return avg_metric

if __name__ == "__main__":
    print(">>>> Evaluating...")
    evaluating_main()


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
try:
    import itertools.izip as zip
except ImportError:
    pass
import numpy as np
from . import _libqpoint as lib
from ._libqpoint import libqp as qp
from ._libqpoint import check_input, check_inputs, check_output

__all__ = ['QPoint', 'check_input', 'check_inputs', 'check_output']

class QPoint(object):

    def __init__(self, **kwargs):
        """
        Initialize a `QPoint` memory instance for keeping track of pointing
        corrections over time.

        Any keyword arguments are passed to
        :meth:`qpoint.qpoint_class.QPoint.set` to update memory.
        """

        # initialize memory
        self._memory = qp.qp_init_memory()

        # collect all parameter functions
        self._funcs = lib.qp_funcs
        self._all_funcs = dict()
        for k in self._funcs:
            self._all_funcs.update(**self._funcs[k])

        # set any requested parameters
        self.set(**kwargs)

    def print_memory(self):
        """
        Print current memory state in C.
        """
        qp.qp_print_memory(self._memory)

    def __del__(self):
        """
        Free memory before deleting the object
        """
        qp.qp_free_memory(self._memory)

    def _set(self, key, val):
        """
        Set a single parameter to the given value.  See
        :meth:`qpoint.qpoint_class.QPoint.set` for a list of parameter names.
        """
        if key not in self._all_funcs:
            raise KeyError('Unknown parameter {}'.format(key))
        val = self._all_funcs[key]['check_set'](val)
        self._all_funcs[key]['set'](self._memory, val)

    def _get(self, key):
        """
        Get the value for a single parameter.  See
        :meth:`qpoint.qpoint_class.QPoint.set` for a list of parameter names.
        """
        if key not in self._all_funcs:
            raise KeyError('Unknown parameter {}'.format(key))
        val = self._all_funcs[key]['get'](self._memory)
        return self._all_funcs[key]['check_get'](val)

    def set(self, **kwargs):
        """
        Set computation options.

        Arguments
        ---------
        rate_daber : {'never', 'once', 'always'}, or float
            Rate at which the diurnal aberration correction is applied in seconds
            (NB: this can only be applied always or never)
        rate_lonlat : {'never', 'once', 'always'}, or float
            Rate at which observer's lon and lat are updated
        rate_wobble : {'never', 'once', 'always'}, or float
            Rate at which the polar motion correction is updated
            (NB: these are not estimated for dates beyond a year from now)
        rate_dut1 : {'never', 'once', 'always'}, or float
            Rate at which the ut1-utc correction is updated
            (NB: this is not estimated for dates beyond a year from now)
        rate_erot : {'never', 'once', 'always'}, or float
            Rate at which the earth's rotation angle is updated
        rate_npb : {'never', 'once', 'always'}, or float
            Rate at which the nutation/precession/frame-bias terms are updated
        rate_aaber : {'never', 'once', 'always'}, or float
            Rate at which the annual aberration correction
            (due to the earth's orbital velocity) is updated
        rate_ref : {'never', 'once', 'always'}, or float
            Rate at which the refaction correction is updated
            (NB: this correction can also be updated manually -- see `refraction`)
        accuracy : 'low' or 'high'
            If 'low', use a truncated form (2000b) for the NPB correction,
            which is much faster but less accurate. If 'high' (default), use
            the full 2006/2000a form.
        mean_aber : bool
            If True, apply the aberration correction as an average for the
            entire field of view.  This is gives a 1-2 arcsec deviation
            at the edges of the SPIDER field of view.
        fast_math : bool
            If True, use polynomial approximations for trig functions
        polconv : 'cosmo' or 'iau'
            Specify the 'cosmo' or 'iau' polarization convention
        pix_order : 'nest' or 'ring'
            HEALPix pixel ordering
        interp_pix : bool
            If True, interpolate between pixels in scanning the source map.
        fast_pix : bool
            If True, use `vec2pix` to get pixel number directly from the
            quaternion instead of `ang2pix` from ra/dec.
        error_missing : bool
            If True, raise an error if reading/writing missing pixels.
        nan_missing : bool
            If True, fill samples from missing pixels with NaN.
            Only used if `error_missing` is False.
        interp_missing : bool
            If True and `interp_pix` is True, drop missing neighbors
            and reweight remaining neighbors.  Overrides `nan_missing`.
        num_threads : bool
             Number of openMP threads to use for mapmaking.
        temperature : float
            Ambient temperature, Celcius. For computing refraction corrections.
        pressure : float
            Ambient pressure, mbar. For computing refraction corrections.
        humidity : float
            Relative humidity, fraction. For computing refraction corrections.
        frequency : float
            Observer frequency, GHz. For computing refraction corrections.
        dut1 : float
            UT1 correction
        ref_delta : float
            Refraction correction
        """

        for k in kwargs:
            try:
                self._set(k, kwargs[k])
            except KeyError:
                continue

    def get(self, *args):
        """
        Returns a dictionary of the requested state parameters.  If no
        parameters are supplied, then all are returned.  If a single parameter
        is supplied, then just that value is returned.  See
        :meth:`qpoint.qpoint_class.QPoint.set` for a list of parameter names.

        Can also select 'options', 'rates', 'weather', or 'params' to return
        all of that subset of parameters.
        """
        state = dict()
        if not len(args):
            for k in self._funcs:
                state[k] = dict()
                for kk in self._funcs[k]:
                    state[k][kk] = self._get(kk)
            return state

        for arg in args:
            if arg in self._funcs:
                state[arg] = dict()
                for k in self._funcs[arg]:
                    state[arg][k] = self._get(k)
            else:
                state[arg] = self._get(arg)
        if len(args) == 1:
            return state[args[0]]
        return state

    def reset_rates(self):
        """
        Reset update counters for each state.  Useful to force an updated
        correction term at the beginning of each chunk.
        """
        qp.qp_reset_rates(self._memory)

    def reset_inv_rates(self):
        """
        Reset update counters for each inverse state.  Useful to force an updated
        correction term at the beginning of each chunk.
        """
        qp.qp_reset_inv_rates(self._memory)

    def refraction(self, *args, **kwargs):
        """refraction(q, **kwargs)
        refraction(delta)

        Update refraction parameters

        Arguments
        ---------
        q : quaternion or array of quaternions
            Observer orientation in horizon coordinates
        temperature : float
            Ambient temperature, Celcius
        pressure : float
            Ambient pressure, mbar
        humidity : float
            Ambient relative humidity, fraction
        frequency : float
            Observing frequency, GHz
        delta : float
            The refraction correction itself, in degrees

        Returns
        -------
        delta : array_like
            Refraction correction computed at each input orientation

        Notes
        -----
        If `q` is given, then the refraction correction in degrees
        is calculated, stored and returned after updating any other given
        parameters. Otherwise, the correction is returned w/out recalculating.

        Alternatively, if a single numerical argument, or the `delta` keyword
        argument is given, then the correction is stored with this value
        instead of being recalculated.

        Numpy-vectorized for the `q` argument.  Note that this is not
        an efficient vectorization, and only the last calculated value is
        stored for use in the coordinate conversion functions.
        """

        if len(args) == 1 and len(kwargs) == 0:
            v = args[0]
            if np.isscalar(v):
                self._set('ref_delta', v)
                return v

        if 'delta' in kwargs:
            v = kwargs.get('delta')
            self._set('ref_delta', v)
            return v

        arg_names = ['q'] + list(self._funcs['weather'])
        for idx, a in enumerate(args):
            kwargs[arg_names[idx]] = a

        for w in self._funcs['weather']:
            if w in kwargs:
                self._set(w, kwargs.get(w))

        q = kwargs.get('q', None)
        if q is not None:
            def func(x0, x1, x2, x3):
                q = np.ascontiguousarray([x0, x1, x2, x3])
                return qp.qp_update_ref(self._memory, q)
            fvec = np.vectorize(func, [np.double])
            if q.size // 4 > 1:
                q = q.transpose()
            delta = fvec(q[0], q[1], q[2], q[3])
            if delta.shape == ():
                return delta[()]
            return delta
        return self._get('ref_delta')

    def gmst(self, ctime, **kwargs):
        """
        Return Greenwich mean sidereal time for given ctimes.
        Vectorized.

        Arguments
        ---------
        ctime : array_like
            Unix time in seconds UTC

        Returns
        -------
        gmst : array_like
            Greenwich mean sidereal time of the observer

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        ctime = check_input('ctime', np.atleast_1d(ctime))
        n = ctime.size

        gmst = check_output('gmst', shape=ctime.shape)
        qp.qp_gmstn(self._memory, ctime, gmst, n)

        if n == 1:
            return gmst[0]
        return gmst

    def lmst(self, ctime, lon, **kwargs):
        """
        Return local mean sidereal time for given ctimes and longitudes.
        Vectorized, input arguments must be broadcastable to the same shape.

        Arguments
        ---------
        ctime : array_like
            Unix time in seconds UTC
        lon : array_like
            Observer longitude (degrees)

        Returns
        -------
        lmst : array_like
            Local mean sidereal time of the observer

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        ctime, lon = check_inputs(ctime, lon)
        n = ctime.size

        lmst = check_output('lmst', shape=ctime.shape)
        qp.qp_lmstn(self._memory, ctime, lon, lmst, n)

        if n == 1:
            return lmst[0]
        return lmst

    def dipole(self, ctime, ra, dec, **kwargs):
        """
        Return dipole amplitude in the given equatorial direction.
        Vectorized, input arguments must be broadcastable to the same shape.

        Arguments
        ---------
        ctime : array_like
            Unix time in seconds UTC
        ra : array_like
            Right ascension on the sky, in degrees.
        dec : array_like
            Declination on the sky, in degrees

        Returns
        -------
        dipole : array_like
            Dipole amplitude in K

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        ctime, ra, dec = check_inputs(ctime, ra, dec)
        n = ctime.size

        dipole = check_output('dipole', shape=ctime.shape, **kwargs)
        qp.qp_dipolen(self._memory, ctime, ra, dec, dipole, n)

        if n == 1:
            return dipole[0]
        return dipole

    def bore2dipole(self, q_off, ctime, q_bore, **kwargs):
        """
        Calculate dipole timestream for given offset and boresight pointing.

        Arguments
        ---------
        q_off : quaternion
            Detector offset quaternion for a single detector, calculated using
            `det_offset`
        ctime : array_like
            Array of unix times in seconds UTC
        q_bore : quaternion or array of quaternions
            Array of quaternions encoding the boresight orientation
            on the sky (as output by `azel2radec` or similar).
            Broadcastable to the same length as `ctime`.

        Returns
        -------
        dipole : array_like
            Dipole amplitude in K

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        q_off = check_input('q_off', q_off, shape=(4,), quat=True)
        ctime = check_input('ctime', ctime)
        n = ctime.size
        q_bore = check_input('q_bore', q_bore, shape=(n, 4), quat=True)

        dipole = check_output('dipole', shape=ctime.shape, **kwargs)
        qp.qp_bore2dipole(self._memory, q_off, ctime, q_bore, dipole, n)

        if n == 1:
            return dipole[0]
        return dipole

    def det_offset(self, delta_az, delta_el, delta_psi):
        """
        Return quaternion corresponding to the requested detector centroid
        offset from boresight.  Vectorized, input arguments must be
        broadcastable to the same shape.

        Arguments
        ---------
        delta_az : array_like
            Azimuthal centroid offset of the detector in degrees
        delta_el : array_like
            Elevation centroid offset of the detector in degrees
        delta_psi : array_like
            Polarization offset of the detector from vertical in degrees

        Returns
        -------
        q : array_like
            Detector centroid offset quaternion for each detector
        """

        delta_az, delta_el, delta_psi = \
            check_inputs(delta_az, delta_el, delta_psi)
        ndet = delta_az.size

        quat = check_output('quat', shape=(ndet,4))
        qp.qp_det_offsetn(delta_az, delta_el, delta_psi, quat, ndet)

        if ndet == 1:
            return quat[0]
        return quat

    def bore_offset(self, q_bore, ang1=None, ang2=None, ang3=None,
                    post=False, inplace=False):
        """
        Apply a fixed or variable offset to the boresight quaternion.
        Input arguments must be broadcastable to the same shape.

        Arguments
        ---------
        q_bore : array_like
            boresight pointing quaternion
        ang1 : array_like, optional
            Azimuthal or ra offset in degrees
        ang2 : array_like, optional
            Elevation or dec offset in degrees
        ang3 : array_like, optional
            Position angle offset in degrees
        post : bool, optional
            If False, apply offset as an az/el/pa pre-rotation
            If True, apply offset as an ra/dec/pa post-rotation
        inplace : bool, optional
            If True, apply the rotation in-place in memory.

        Returns
        -------
        q_bore : array_like
            Offset boresight quaternion
        """
        q_bore = check_input('q_bore', np.atleast_2d(q_bore), quat=True,
                             inplace=inplace, output=True)
        n = q_bore.shape[0]
        if all([a is None for a in [ang1, ang2, ang3]]):
            raise ValueError('One of ang1, ang2, ang3 is required')
        ang1, ang2, ang3 = \
            check_inputs(ang1, ang2, ang3, shape=(n,))
        qp.qp_bore_offset(self._memory, q_bore, ang1, ang2, ang3, n, int(post))
        return q_bore

    def hwp_quat(self, theta):
        """
        Return quaternion corresponding to rotation by 2 * theta, where theta is
        the physical waveplate angle. Vectorized.

        Arguments
        ---------
        theta : array_like
            HWP physical angle in degrees

        Returns
        -------
        q : array_like
            Quaternion for each hwp angle
        """
        theta = check_input('theta', np.atleast_1d(theta))
        n = theta.size

        quat = check_output('quat', shape=(n,4))
        qp.qp_hwp_quatn(theta, quat, n)

        if n == 1:
            return quat[0]
        return quat

    def azel2bore(self, az, el, pitch, roll, lon, lat, ctime, q=None,
                  **kwargs):
        """
        Estimate the quaternion for the boresight orientation on the sky given
        the attitude (az/el/pitch/roll), location on the earth (lon/lat) and
        ctime. Input vectors must be numpy-array-like and broadcastable to the
        same shape.

        Arguments
        ---------
        az : array_like
            Boresight azimuth in degrees
        el : array_like
            Boresight elevation in degrees
        pitch : array_like
            Boresight pitch in degrees.  If `None`, this term is ignored.
        roll : array_like
            Boresight roll in degrees.  If `None`, this term is ignored.
        lon : array_like
            Observer longitude in degrees
        lat : array_like
            Observer latitude in degrees
        ctime : array_like
            Unix time in seconds UTC
        q : array_like, optional
            Output quaternion array initialized by user.  Supply this
            for in-place computation.

        Returns
        -------
        q : array_like
            Nx4 numpy array of quaternions for each supplied timestamp.

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        az, el, pitch, roll, lon, lat, ctime = \
            check_inputs(az, el, pitch, roll, lon, lat, ctime)
        n = az.size

        # identity quaternion
        q = check_output('q', q, shape=(n,4), fill=[1,0,0,0])

        qp.qp_azel2bore(self._memory, az, el, pitch, roll, lon, lat,
                        ctime, q, n)

        return q

    def bore2radec(self, q_off, ctime, q_bore, q_hwp=None, sindec=False,
                   return_pa=False, ra=None, dec=None, pa=None,
                   sin2psi=None, cos2psi=None, **kwargs):
        """
        Calculate the orientation on the sky for a detector offset from the
        boresight.  Detector offsets are defined assuming the boresight is
        pointed toward the horizon, and that the boresight polarization axis is
        along the vertical.

        Arguments
        ---------
        q_off : quaternion
            Detector offset quaternion for a single detector, calculated using
            `det_offset`.
        ctime : array_like
            Unix time in seconds UTC, broadcastable to shape (N,),
            the long dimension of `q_bore`.
        q_bore : quaternion or array of quaternions
            Nx4 array of quaternions encoding the boresight orientation
            on the sky (as output by `azel2radec` or equivalent)
        q_hwp : quaternion or array of quaternions, optional
            HWP angle quaternions calculated using `hwp_quat`.
            Must be broadcastable to the same shape as `q_bore`.
        sindec : bool, optional
            If `True`, return sin(dec) instead of dec in degrees
            (default False).
        return_pa : bool, optional
            If `True`, return pa instead of sin2psi / cos2psi

        Returns
        -------
        ra : array_like
            Detector right ascension in degrees
        dec/sindec : array_like
            Detector declination in degrees or sin(dec) if `sindec` is `True`.
        pa/sin2psi : array_like
            Detector polarization orientation if `return_pa` is `True`, or
            sin(2*pa) if `return_pa` is `False`.
        cos2psi : array_like
            detector polarization orientation cos(2*pa), if `return_pa` is `False`.

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.

        Pre-allocated output arguments can also be supplied as input keywords
        for in-place operation.
        """

        self.set(**kwargs)

        q_off  = check_input('q_off', q_off, quat=True)
        q_bore = check_input('q_bore', np.atleast_2d(q_bore), quat=True)
        shape = (q_bore.size // 4,)
        if ctime is None:
            if not self.get('mean_aber'):
                raise ValueError('ctime required if mean_aber is False')
            ctime = np.zeros(shape, dtype=q_bore.dtype)
        ctime  = check_input('ctime', ctime, shape=shape)
        pars = dict(shape=ctime.shape, dtype=np.double)
        ra = check_output('ra', ra, **pars)
        dec = check_output('dec', dec, **pars)
        if return_pa:
            if sindec:
                raise ValueError('Cannot use sindec with return_pa=True')
            pa = check_output('pa', pa, **pars)
        else:
            sin2psi = check_output('sin2psi', sin2psi, **pars)
            cos2psi = check_output('cos2psi', cos2psi, **pars)
        n = ctime.size

        if q_hwp is None:
            if return_pa:
                qp.qp_bore2radecpa(self._memory, q_off, ctime, q_bore,
                                   ra, dec, pa, n)
            elif sindec:
                qp.qp_bore2rasindec(self._memory, q_off, ctime, q_bore,
                                    ra, dec, sin2psi, cos2psi, n)
            else:
                qp.qp_bore2radec(self._memory, q_off, ctime, q_bore,
                                 ra, dec, sin2psi, cos2psi, n)
        else:
            q_hwp = check_input('q_hwp', q_hwp, shape=q_bore.shape)
            if return_pa:
                qp.qp_bore2radecpa_hwp(self._memory, q_off, ctime, q_bore,
                                       q_hwp, ra, dec, pa, n)
            elif sindec:
                qp.qp_bore2rasindec_hwp(self._memory, q_off, ctime, q_bore,
                                        q_hwp, ra, dec, sin2psi, cos2psi, n)
            else:
                qp.qp_bore2radec_hwp(self._memory, q_off, ctime, q_bore,
                                     q_hwp, ra, dec, sin2psi, cos2psi, n)

        if return_pa:
            if n == 1:
                return ra[0], dec[0], pa[0]
            return ra, dec, pa

        if n == 1:
            return ra[0], dec[0], sin2psi[0], cos2psi[0]
        return ra, dec, sin2psi, cos2psi

    def azel2radec(self, delta_az, delta_el, delta_psi,
                   az, el, pitch, roll, lon, lat, ctime,
                   hwp=None, sindec=False, return_pa=False,
                   ra=None, dec=None, pa=None, sin2psi=None,
                   cos2psi=None, **kwargs):
        """
        Estimate the orientation on the sky for a detector offset from
        boresight, given the boresight attitude (az/el/pitch/roll), location on
        the earth (lon/lat) and UTC time.  Input vectors must be
        numpy-array-like and broadcastable to the same shape. Detector offsets
        are defined assuming the boresight is pointed toward the horizon, and
        that the boresight polarization axis is along the horizontal.

        Arguments
        ---------
        delta_az : float
            Azimuthal offset of the detector in degrees
        delta_el : float
            Elevation offset of the detector in degrees
        delta_psi : float
            Polarization offset of the detector in degrees
        az : array_like
            Boresight azimuth in degrees
        el : array_like
            Boresight elevation in degrees
        pitch : array_like
            Boresight pitch in degrees.  If None, this term is ignored.
        roll : array_like
            Boresight roll in degrees.  If None, this term is ignored.
        lon : array_like
            Observer longitude in degrees.
        lat : array_like
            Observer latitude in degrees.
        ctime : array_like
            Unix time in seconds UTC
        hwp : array_like, optional
            HWP angles in degrees
        sindec : bool, optional
            If `True`, return sin(dec) instead of dec in degrees (default False)
        return_pa : bool, optional
            If `True`, return pa instead of sin2psi/cos2psi

        Returns
        -------
        ra : array_like
            Detector right ascension in degrees
        dec/sindec : array_like
            Detector declination in degrees
        pa : array_like
            Detector position angle, if `return_pa` is True
        sin2psi : array_like
            Detector polarization orientation, if `return_pa` is False
        cos2psi : array_like
            Detector polarization orientation, if `return_pa` is False

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        az, el, pitch, roll, lon, lat, ctime = \
            check_inputs(az, el, pitch, roll, lon, lat, ctime)

        ra = check_output('ra', ra, shape=az.shape, dtype=np.double)
        dec = check_output('dec', dec, shape=az.shape, dtype=np.double)
        if return_pa:
            pa = check_output('pa', pa, shape=az.shape, dtype=np.double)
        else:
            sin2psi = check_output('sin2psi', sin2psi, shape=az.shape,
                                   dtype=np.double)
            cos2psi = check_output('cos2psi', cos2psi, shape=az.shape,
                                   dtype=np.double)
        n = az.size

        if hwp is None:
            if return_pa:
                qp.qp_azel2radecpa(self._memory, delta_az, delta_el, delta_psi,
                                   az, el, pitch, roll, lon, lat, ctime,
                                   ra, dec, pa, n)
            elif sindec:
                qp.qp_azel2rasindec(self._memory, delta_az, delta_el, delta_psi,
                                    az, el, pitch, roll, lon, lat, ctime,
                                    ra, dec, sin2psi, cos2psi, n)
            else:
                qp.qp_azel2radec(self._memory, delta_az, delta_el, delta_psi,
                                 az, el, pitch, roll, lon, lat, ctime,
                                 ra, dec, sin2psi, cos2psi, n)
        else:
            hwp = check_input('hwp', hwp, shape=az.shape)

            if return_pa:
                qp.qp_azel2radec_hwp(self._memory, delta_az, delta_el, delta_psi,
                                     az, el, pitch, roll, lon, lat, ctime, hwp,
                                     ra, dec, pa, n)
            elif sindec:
                qp.qp_azel2rasindec_hwp(self._memory, delta_az, delta_el, delta_psi,
                                        az, el, pitch, roll, lon, lat, ctime, hwp,
                                        ra, dec, sin2psi, cos2psi, n)
            else:
                qp.qp_azel2radec_hwp(self._memory, delta_az, delta_el, delta_psi,
                                     az, el, pitch, roll, lon, lat, ctime, hwp,
                                     ra, dec, sin2psi, cos2psi, n)

        if return_pa:
            return ra, dec, pa
        return ra, dec, sin2psi, cos2psi

    def radec2azel(self, ra, dec, pa, lon, lat, ctime, az=None, el=None, hpa=None,
                   **kwargs):
        """
        Estimate the horizon coordinates for a given set of equatorial coordinates
        (ra/dec/psi), location on the earth (lon/lat) and UTC time.  Input vectors
        must be numpy-array-like and broadcastable to the same shape.

        Arguments
        ---------
        ra : array_like
            Right ascension angle
        dec : array_like
            Declination angle
        pa : array_like
            Position angle in equatorial coordinates
        lon : array_like
            Observer longitude in degrees.
        lat : array_like
            Observer latitude in degrees.
        ctime : array_like
            Unix time in seconds UTC

        Returns
        -------
        az : array_like
            Azimuth in degrees
        el : array_like
            Elevation in degrees
        hpa : array_like
            Position angle in horizon coordinates

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        ra, dec, pa, lon, lat, ctime = \
            check_inputs(ra, dec, pa, lon, lat, ctime)

        az = check_output('az', az, shape=ra.shape, dtype=np.double)
        el = check_output('el', el, shape=ra.shape, dtype=np.double)
        hpa = check_output('hpa', hpa, shape=ra.shape, dtype=np.double)
        n = ra.size

        qp.qp_radec2azel(self._memory, ra, dec, pa, lon, lat, ctime,
                         az, el, hpa, n)

        return az, el, hpa

    def radecpa2quat(self, ra, dec, pa, **kwargs):
        """
        Calculate quaternion for input ra/dec/pa. Vectorized, input arguments
        must be broadcastable to the same shape.

        Arguments
        ---------
        ra : array_like
            Right ascension angle
        dec : array_like
            Declination angle
        pa : array_like
            Position angle

        Returns
        -------
        q : array_like
            Quaternion constructed from the input angles.

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """
        self.set(**kwargs)

        ra, dec, pa = check_inputs(ra, dec, pa)
        n = ra.size
        quat = check_output('quat', shape=(n,4), dtype=np.double, **kwargs)
        qp.qp_radecpa2quatn(self._memory, ra, dec, pa, quat, n)

        if n == 1:
            return quat[0]
        return quat

    def quat2radecpa(self, quat, **kwargs):
        """
        Calculate ra/dec/pa for input quaternion(s).

        Arguments
        ---------
        q : array_like
            Pointing quaternion

        Returns
        -------
        ra : array_like
            Right ascension angle
        dec : array_like
            Declination angle
        pa : array_like
            Position angle

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """
        self.set(**kwargs)

        quat = check_input('quat', np.atleast_2d(quat), quat=True)
        n = quat.shape[0]
        ra = check_output('ra', shape=(n,), dtype=np.double, **kwargs)
        dec = check_output('dec', shape=(n,), dtype=np.double, **kwargs)
        pa = check_output('pa', shape=(n,), dtype=np.double, **kwargs)

        qp.qp_quat2radecpan(self._memory, quat, ra, dec, pa, n)
        if n == 1:
            return ra[0], dec[0], pa[0]
        return ra, dec, pa

    def quat2pixpa(self, quat, nside=256, **kwargs):
        """
        Calculate ra/dec/pa for input quaternion(s).

        Arguments
        ---------
        q : array_like
            Pointing quaternion

        Returns
        -------
        pix : array_like
            Pixel number(s) corresponding to the input positions(s).
        pa : array_like
            Position angle

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """
        self.set(**kwargs)

        quat = check_input('quat', np.atleast_2d(quat), quat=True)
        n = quat.shape[0]
        pix = check_output('pix', shape=(n,), dtype=np.int, **kwargs)
        pa = check_output('pa', shape=(n,), dtype=np.double, **kwargs)

        qp.qp_quat2pixpan(self._memory, quat, pix, pa, n)
        if n == 1:
            return pix[0], pa[0]
        return pix, pa

    def radec2pix(self, ra, dec, nside=256, **kwargs):
        """
        Calculate HEALpix pixel number for given ra/dec and nside.  Vectorized,
        input arguments must be broadcastable to the same shape.

        Arguments
        ---------
        ra : array_like
            Right ascension angle
        dec : array_like
            Declination angle
        nside : int
            HEALpix resolution parameter

        Returns
        -------
        pix : array_like
            Pixel number(s) corresponding to the input positions(s).

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        ra, dec = check_inputs(ra, dec)
        n = ra.size

        pix = check_output('pix', shape=ra.shape, dtype=np.int, **kwargs)
        qp.qp_radec2pixn(self._memory, ra, dec, nside, pix, n)

        if n == 1:
            return pix[0]
        return pix

    def rotate_quat(self, quat, coord=['C', 'G'], inplace=True, **kwargs):
        """
        Rotate a quaternion from one coordinate system to another.
        Supported coordinates:

        C: celestial (equatorial) coordinates
        G: galactic coordinates

        Arguments
        ---------
        quat : array_like
            array of quaternions, of shape (n, 4)
        coord : list, optional
            2-element list of input and output coordinates
        inplace : bool, optional
            If True, apply the rotation in-place on the input quaternion.
            Otherwise, return a copy of the input array.  Default: True.

        Returns
        -------
        quat : array_like
            rotated quaternion array

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        quat = check_input('quat', np.atleast_2d(quat), quat=True,
                           inplace=inplace, output=True)
        n = quat.size // 4

        if coord[0] == 'C' and coord[1] == 'G':
            qp.qp_radec2gal_quatn(self._memory, quat, n)
        elif coord[0] == 'G' and coord[1] == 'C':
            qp.qp_gal2radec_quatn(self._memory, quat, n)
        else:
            raise ValueError('unsupported coord {}'.format(coord))

        return quat.squeeze()

    def rotate_coord(self, ra, dec, pa=None, sin2psi=None, cos2psi=None,
                     coord=['C', 'G'], inplace=True, **kwargs):
        """
        Rotate coordinates from one coordinate system to another. Vectorized,
        input arguments must be broadcastable to the same shape.
        Supported coordinates:

        C: celestial (equatorial) coordinates
        G: galactic coordinates

        Arguments
        ---------
        ra, dec, pa : array_like
          -- or --
        ra, dec, sin2psi, cos2psi : array_like
            arrays of coordinates, of shape (n,)
            If none of pa, sin2psi or cos2psi are supplied,
            pa = 0 is assumed.
        coord : list, optional
            2-element list of input and output coordinates.
            Supported systems: C, G.
        inplace : bool, optional
            If True, apply the rotation in-place on the input coordinates.
            Otherwise, return a copy of the input array.  Default: True.

        Returns
        -------
        ra, dec, pa : array_like
          -- or --
        ra, dec, sin2psi, cos2psi : array_like
            rotated coordinate arrays, same form as input

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """
        self.set(**kwargs)

        do_pa = True
        if pa is None:
            if sin2psi is None and cos2psi is None:
                pass
            elif sin2psi is None or cos2psi is None:
                raise KeyError('both sin2psi and cos2psi arguments required')
            else:
                do_pa = False
        else:
            if sin2psi is not None or cos2psi is not None:
                raise KeyError('ambiguous pol arguments, supply either pa '
                               'or sin2psi/cos2psi only')

        ra, dec, pa, sin2psi, cos2psi = \
            check_inputs(ra, dec, pa, sin2psi, cos2psi, inplace=inplace, output=True)
        n = ra.size

        if coord[0] == 'C' and coord[1] == 'G':
            if do_pa:
                qp.qp_radecpa2galn(self._memory, ra, dec, pa, n)
            else:
                qp.qp_radec2galn(self._memory, ra, dec, sin2psi, cos2psi, n)
        elif coord[0] == 'G' and coord[1] == 'C':
            if do_pa:
                qp.qp_gal2radecpan(self._memory, ra, dec, pa, n)
            else:
                qp.qp_gal2radecn(self._memory, ra, dec, sin2psi, cos2psi, n)

        if n == 1:
            if do_pa:
                return ra[0], dec[0], pa[0]
            return ra[0], dec[0], sin2psi[0], cos2psi[0]
        if do_pa:
            return ra, dec, pa
        return ra, dec, sin2psi, cos2psi

    def radec2gal(self, ra, dec, pa=None, sin2psi=None, cos2psi=None,
                  inplace=True, **kwargs):
        """
        Rotate celestial (equatorial) coordinates to galactic coordinates.
        This is equivalent to calling `rotate_coord(..., coord=['C', 'G'])`.
        Vectorized, input arguments must be broadcastable to the same shape.

        Arguments
        ---------
        ra, dec, pa : array_like
          -- or --
        ra, dec, sin2psi, cos2psi : array_like
            arrays of coordinates, of shape (n,)
            If none of pa, sin2psi or cos2psi are supplied,
            pa = 0 is assumed.
        inplace : bool, optional
            If True, apply the rotation in-place on the input quaternion.
            Otherwise, return a copy of the input array.  Default: True.

        Returns
        -------
        l, b, pa : array_like
          -- or --
        l, b, sin2psi, cos2psi : array_like
            rotated coordinate arrays, same form as input

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """
        return self.rotate_coord(ra, dec, pa, sin2psi, cos2psi,
                                 coord=['C','G'], inplace=inplace, **kwargs)

    def gal2radec(self, ra, dec, pa=None, sin2psi=None, cos2psi=None,
                  inplace=True, **kwargs):
        """
        Rotate galactic coordinates to celestial (equatorial) coordinates.
        This is equivalent to calling `rotate_coord(..., coord=['G', 'C'])`.
        Vectorized, input arguments must be broadcastable to the same shape.

        Arguments
        ---------
        l, b, pa : array_like
          -- or --
        l, b, sin2psi, cos2psi : array_like
            arrays of coordinates, of shape (n,)
            If none of pa, sin2psi or cos2psi are supplied,
            pa = 0 is assumed.
        inplace : bool, optional
            If True, apply the rotation in-place on the input quaternion.
            Otherwise, return a copy of the input array.  Default: True.

        Returns
        -------
        ra, dec, pa : array_like
          -- or --
        ra, dec, sin2psi, cos2psi : array_like
            rotated coordinate arrays, same form as input

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """
        return self.rotate_coord(ra, dec, pa, sin2psi, cos2psi,
                                 coord=['G','C'], inplace=inplace, **kwargs)

    def rotate_map(self, map_in, coord=['C','G'], map_out=None,
                   interp_pix=True, **kwargs):
        """
        Rotate a polarized 3-x-npix map from one coordinate system to another.
        Supported coordinates:

        C = celestial (equatorial J2000)
        G = galactic

        Arguments
        ---------
        map_in : array_like
            Input map, of shape (3, N)
        coord : list, optional
            2-element list of input and output coordinates.
            Supported systems: C, G.
        map_out : array_like, optional
            Rotated output map, for inplace operation.  Same shape as `map_in`.
        interp_pix : bool, optional
            If True, interpolate the rotated map.

        Returns
        -------
        map_out : array_like
            Rotated output map.

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.

        Only full-sky maps are currently supported.
        """

        from warnings import warn
        warn('This code is buggy, use at your own risk', UserWarning)

        interp_orig = self.get('interp_pix')
        self.set(interp_pix=interp_pix, **kwargs)

        from .qmap_class import check_map
        map_in, nside = check_map(map_in)
        map_out = check_output(
            'map_out', map_out, shape=map_in.shape, dtype=map_in.dtype, fill=0)

        try:
            coord_in = coord[0]
            coord_out = coord[1]
        except:
            raise ValueError('unable to parse coord')

        map_in_p = lib.pointer_2d(map_in)
        map_out_p = lib.pointer_2d(map_out)

        qp.qp_rotate_map(self._memory, nside, map_in_p, coord_in,
                         map_out_p, coord_out)

        self.set(interp_pix=interp_orig)
        return map_out

    def quat2pix(self, quat, nside=256, pol=True, **kwargs):
        """
        Calculate HEALpix pixel number and optional polarization angle
        for a given orientation.

        Arguments
        ---------
        quat : quaternion or array of quaternions
            Pointing orientation(s)
        nside : int, optional
            HEALpix resolution parameter
        pol : bool, optional
            If True, return sin2psi and cos2psi along with the pixel number(s)

        Returns
        -------
        pix : array_like
            Pixel number(s) for the given input quaternion(s)
        sin2psi : array_like
        cos2psi : array_like
            Polarization coefficients, if `pol` is `True`.

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        quat = check_input('quat', np.atleast_2d(quat), quat=True)

        n = quat.shape[0]
        shape = (n,)
        pix = check_output('pix', shape=shape, dtype=np.int, **kwargs)
        sin2psi = check_output('sin2psi', shape=shape, **kwargs)
        cos2psi = check_output('cos2psi', shape=shape, **kwargs)
        qp.qp_quat2pixn(self._memory, quat, nside, pix, sin2psi, cos2psi, n)

        if n == 1:
            pix, sin2psi, cos2psi = pix[0], sin2psi[0], cos2psi[0]
        if pol:
            return pix, sin2psi, cos2psi
        return pix

    def bore2pix(self, q_off, ctime, q_bore, q_hwp=None, nside=256, pol=True,
                 return_pa=False, **kwargs):
        """
        Calculate the orientation on the sky for a detector offset from the
        boresight.  Detector offsets are defined assuming the boresight is
        pointed toward the horizon, and that the boresight polarization axis is
        along the vertical.

        Arguments
        ---------
        q_off : quaternion
            Detector offset quaternion for a single detector,
            calculated using `det_offset`.
        ctime : array_like
            Unix times in seconds UTC, broadcastable to shape (N,),
            the long dimenions of `q_bore`.
        q_bore : quaternion or array of quaternions
            Nx4 array of quaternions encoding the boresight orientation
            on the sky (as output by `azel2radec` or equivalent)
        q_hwp : quaternion or array of quaternions, optional
            HWP angle quaternions calculated using `hwp_quat`.
            Must be broadcastable to the same shape as `q_bore`.
        nside : int, optional
            HEALpix map dimension.  Default: 256.
        pol : bool, optional
            If `False`, return only the pixel timestream
        return_pa : bool, optional
            If `True`, return pa instead of sin2psi / cos2psi

        Returns
        -------
        pix : array_like
            Detector pixel number
        pa/sin2psi : array_like
            Detector polarization orientation if `return_pa` is `True`, or
            sin(2*pa) if `return_pa` is `False`.
        cos2psi : array_like
            detector polarization orientation cos(2*pa), if `return_pa` is `False`.

        Notes
        -----
        Any keywords accepted by the :meth:`qpoint.qpoint_class.QPoint.set`
        method can also be passed here, and will be processed prior to
        calculation.
        """

        self.set(**kwargs)

        q_off  = check_input('q_off', q_off, quat=True)
        q_bore = check_input('q_bore', q_bore, quat=True)
        if ctime is None:
            if not self.get('mean_aber'):
                raise ValueError('ctime required if mean_aber is False')
            ctime = np.zeros((q_bore.size // 4,), dtype=q_bore.dtype)
        ctime  = check_input('ctime', ctime)
        pix  = check_output('pix', shape=ctime.shape,
                                dtype=np.int, **kwargs)
        if return_pa:
            pa = check_output('pa', shape=ctime.shape, **kwargs)
        else:
            sin2psi = check_output('sin2psi', shape=ctime.shape,
                                   **kwargs)
            cos2psi = check_output('cos2psi', shape=ctime.shape,
                                   **kwargs)
        n = ctime.size

        if q_hwp is None:
            if return_pa:
                qp.qp_bore2pixpa(self._memory, q_off, ctime, q_bore,
                                 nside, pix, pa, n)
            else:
                qp.qp_bore2pix(self._memory, q_off, ctime, q_bore,
                               nside, pix, sin2psi, cos2psi, n)
        else:
            q_hwp = check_input('q_hwp', q_hwp, shape=q_bore.shape)

            if return_pa:
                qp.qp_bore2pixpa_hwp(self._memory, q_off, ctime, q_bore,
                                     q_hwp, nside, pix, pa, n)
            else:
                qp.qp_bore2pix_hwp(self._memory, q_off, ctime, q_bore,
                                   q_hwp, nside, pix, sin2psi, cos2psi, n)

        if pol is True:
            if return_pa:
                return pix, pa
            return pix, sin2psi, cos2psi
        return pix

    def get_interp_val(self, map_in, ra, dec, nest=False):
        """
        Interpolate map pixels to these coordinates.  Uses a C implementation
        of the bilinear interpolation method `get_interpol()` as implemented
        in the equivalent healpix_cxx / healpy function.

        Arguments
        ---------
        map_in : array_like
            A single healpix map or list of maps which to interpolate from.
        ra, dec: array_like
            Timestreams of coordinates to interpolate to, in degrees,
            broadcastable to a common shape (nsample,)
        nest: bool, optional
            If True, input map is in the nested pixel ordering scheme.
            Otherwise, ring ordering is assumed.
            Default: False.

        Returns
        -------
        values : array_like
            Array of interpolated map values, of shape (nmap, nsample).
        """

        pix_order = self.get('pix_order')
        if nest:
            self.set(pix_order='nest')
        else:
            self.set(pix_order='ring')

        ra, dec = check_inputs(ra, dec)
        n = ra.size

        from .qmap_class import check_map
        map_in, nside = check_map(map_in)

        val = check_output('value', shape=(len(map_in), n))

        for m, v in zip(map_in, val):
            qp.qp_get_interp_valn(self._memory, nside, m, ra, dec, v, n)

        self.set(pix_order=pix_order)

        v = v.squeeze()
        if not v.shape:
            return v[()]
        return v

    def update_bulletin_a(self, start_year=2000):
        """
        Update the IERS Bulletin A database using astropy, and return the stored
        entries.  Issues an ImportWarning if astropy version 1.2 or newer is not
        found.

        Arguments
        ---------
        start_year : int, optional
            Oldest year for which data should be stored.

        Returns
        -------
        mjd : array_like
            Modified Julian date
        dut1 : array_like
            UT1-UTC time correction
        x : array_like
        y : array_like
            Polar motion (wobble) corrections
        """
        try:
            from astropy.utils.iers import IERS_Auto
        except ImportError:
            from warnings import warn
            warn('Compatible Astropy not found.  Install astropy v1.2 or newer '
                 'for accurate polar motion and UT1 corrections', ImportWarning)
            return

        columns = ['MJD', 'UT1_UTC', 'PM_x', 'PM_y', 'year']
        iers_table = IERS_Auto.open()[columns].as_array()

        # check year
        year = iers_table['year'] + 1900
        wraps, = np.where(np.ediff1d(year) < 0)
        for idx in wraps:
            year[idx + 1:] += 100
        iers_table['year'] = year
        iers_table = iers_table[year >= start_year]

        # check MJD
        mjds = iers_table['MJD']
        mjd_min = int(mjds.min())
        mjd_max = int(mjds.max())

        # update table
        dut1 = np.array(iers_table['UT1_UTC'])
        x = np.array(iers_table['PM_x'])
        y = np.array(iers_table['PM_y'])
        qp.qp_set_iers_bulletin_a(self._memory, mjd_min, mjd_max, dut1, x, y)

        return mjds, dut1, x, y

    def load_bulletin_a(self, filename, columns=['mjd','dut1','x','y'], **kwargs):
        """
        Load IERS Bulletin A from file and store in memory.  The file must be
        readable using `numpy.loadtxt` with `unpack=True`, and is assumed to be
        sorted by mjd.

        Arguments
        ---------
        filename : string
            Name of the text file containing IERS Bulletin A parameters.
        columns : list of strings
            list of columns as they appear in the file.
            A KeyError is raise if the list does not contain
            each of ['mjd', 'dut1', 'x', 'y'].

        Any other keyword arguments are passed to the `numpy.loadtxt` function

        Returns
        -------
        mjd : array_like
            Modified Julian date
        dut1 : array_like
            UT1-UTC time correction
        x : array_like
        y : array_like
            Polar motion corrections
        """

        req_columns = ['mjd','dut1','x','y']
        if not set(req_columns) <= set(columns):
            raise KeyError(
                'Missing columns {}'.format(list(set(req_columns)-set(columns))))
        kwargs['unpack'] = True
        data = np.loadtxt(filename, **kwargs)
        mjd, x, y, dut1 = (data[columns.index(x)] for x in req_columns)
        mjd_min, mjd_max = int(mjd[0]), int(mjd[-1])

        try:
            qp.qp_set_iers_bulletin_a(self._memory, mjd_min, mjd_max, dut1, x, y)
        except:
            raise RuntimeError(
                'Error loading Bulletin A data from file {}'.format(filename))

        return mjd, dut1, x, y

    def get_bulletin_a(self, mjd):
        """
        Return dut1/x/y for given mjd. Numpy-vectorized.
        """

        def func(x):
            return lib.qp_get_bulletin_a(self._memory, x)
        fvec = np.vectorize(func, [np.double]*3)

        out = fvec(mjd)
        if out[0].shape == ():
            return tuple(x[()] for x in out)
        return out


# import the necessary pages
from imutils.object_detection import non_max_suppression
import numpy as np
import argparse
import cv2
import pytesseract
from util.ToBlackAndWhiteConvert import makeGray

heroes = ["Veigar","Kayle","Orianna","Volibear","Teemo","Corki","LeBlanc","Swain","Karthus","Quinn","Home"]
heroesLen = len(heroes)
cvChampList = []
for hero in heroes:
	cvChampList.append(cv2.imread(f'name_label/{hero}_name.jpg'))

def getResults(mm):
	global cvChampList,heroesLen
	for cvChampIndex in range(heroesLen):
		cvChamp = cvChampList[cvChampIndex]
		result = cv2.matchTemplate(cvChamp, mm, cv2.TM_SQDIFF_NORMED)
		mn, _, mnLoc, _ = cv2.minMaxLoc(result)
		confidence = 1-mn
		if confidence>args["threshold"]:
			return heroes[cvChampIndex]
	return None

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", type=str, required=True,
	help="path to input image where we'll apply template matching")
ap.add_argument("-t", "--template", type=str, required=True,
	help="path to template image")
ap.add_argument("-b", "--threshold", type=float, default=0.8,
	help="threshold for multi-template matching")
args = vars(ap.parse_args())

# load the input image and template image from disk, then grab the
# template image spatial dimensions
print("[INFO] loading images...")
image = cv2.imread(args["image"])
template = cv2.imread(args["template"])
(tH, tW) = template.shape[:2]
# display the  image and template to our screen
cv2.imshow("Image", image)
cv2.imshow("Template", template)

# convert both the image and template to grayscale
imageGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
templateGray = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
# perform template matching
print("[INFO] performing template matching...")
result = cv2.matchTemplate(imageGray, templateGray,
	cv2.TM_CCOEFF_NORMED)

# find all locations in the result map where the matched value is
# greater than the threshold, then clone our original image so we
# can draw on it
(yCoords, xCoords) = np.where(result >= args["threshold"])
clone = image.copy()
print("[INFO] {} matched locations *before* NMS".format(len(yCoords)))
# loop over our starting (x, y)-coordinates
for (x, y) in zip(xCoords, yCoords):
	# draw the bounding box on the image
	cv2.rectangle(clone, (x, y), (x + tW, y + tH),
		(255, 0, 0), 3)
# show our output image *before* applying non-maxima suppression
cv2.imshow("Before NMS", clone)
# cv2.waitKey(0)

# initialize our list of rectangles
rects = []
# loop over the starting (x, y)-coordinates again
for (x, y) in zip(xCoords, yCoords):
	# update our list of rectangles
	rects.append((x, y, x + tW, y + tH))
# apply non-maxima suppression to the rectangles
pick = non_max_suppression(np.array(rects))
print("[INFO] {} matched locations *after* NMS".format(len(pick)))
# loop over the final bounding boxes
for (startX, startY, endX, endY) in pick:
	# draw the bounding box on the image
	centerColor = image[int((startY+endY)/2),int((startX+endX)/2)]
	# print(centerColor)
	HP_Length = 59
	if centerColor[0] >100 and  centerColor[1] > 85 and centerColor[2] <90:
		for i in range(startX+1,endX+59):
			_color = image[int((startY+endY)/2),i]
			# print(_color)
			if _color[0] >100 and  _color[1] > 85 and _color[2] <90:
				pass
			else:
				HP_Length = i - startX
				break
		print("friend",HP_Length)
	elif centerColor[0] < 100 and centerColor[0] > 50 and centerColor[1] < 95 and centerColor[1] > 50 and centerColor[2] > 90:
		for i in range(startX+1,endX+59):
			_color = image[int((startY+endY)/2),i]
			# print(_color)
			if _color[0] <100 and  _color[1] < 95 and _color[2] >90:
				pass
			else:
				HP_Length = i - startX
				break
		print("enemy",HP_Length)
	else:
		continue
		# break
	# if startY-35 < 0: startY=35
	# if endY-28 < 0: endY=28
	# _image = image[startY-35:endY-28,startX+25:endX+85].copy()
	# makeGray(_image)
	# heroName = getResults(_image)
	# if heroName:
		# cv2.imwrite(heroName+str(startX)+".jpg", _image)
	# else:
		# cv2.imwrite('heroName'+str(startX)+".jpg", _image)
	cv2.rectangle(image, (startX, startY), (startX+HP_Length, endY),
		(255, 0, 0), 1)

# show the output image
cv2.imshow("After NMS", image)
cv2.waitKey(0)

# python multi_template_matching_minion.py --image images/g0/3a0z2sVYl4.jpg --template images/minion.jpg --threshold 0.986


#!/usr/bin/env python3
"""Extract acoustic features using ``librosa``.

To extract features using ``librosa``, run:

    python gen_librosa_feats.py feats_dir/ fn1.wav fn2.wav fn3.wav ...

which will extract mel filterbank features for the audio files ``fn1.wav,
``fn2.wav``, ``fn3.wav``, ..., and for each recording output frame-level
features to a corresponding ``.npy`` file located under the directory
``feats_dir``.

For each audio file, this script outputs a NumPy ``.npy`` file containing an
``num_frames`` x ``num_features`` array of frame-level features. These
correspond to frames sampled every 10 ms starting from an offset of 0; that is,
the ``i``-th frame of features corresponds to an offset of ``i*0.010`` seconds.

"""
import argparse
from functools import partial
from multiprocessing import Pool
from pathlib import Path
import sys
import warnings
import yaml

import librosa
from librosa.feature import melspectrogram, mfcc
import numpy as np
from tqdm import tqdm

warnings.simplefilter('ignore', FutureWarning)

def sec_to_samples(n_sec, sr):
    """Return number of samples required to cover duration in seconds."""
    return int(n_sec*sr)

def db_mel_spectrogram(x, sr, **kwargs):
    """Return DB mel spectrogram."""
    p = melspectrogram(x, sr, **kwargs)
    p = librosa.power_to_db(p, ref=np.max)
    return p

def extract_feats(af, feats_dir, sr, feats_fn, add_deltas=False, **kwargs):
    """Extract mel frequency cepstral coefficient features to file.

    Parameters
    ----------
    af : Path
        Path to audio file.

    feats_dir : Path
        Path to output directory.

    sr : int
       Sample rate (Hz) to resample audio to.

    kwargs
        Keyword arguments to pass to ``librosa.feature.melspectrogram``.
    """
    x, _ = librosa.core.load(af, sr)
    feats = feats_fn(x, sr=sr, **kwargs)
    feats = feats.T  # Convert features to frames x feat_dim.
    if add_deltas:
        delta = librosa.feature.delta(feats, order=1, axis=0)
        delta_delta = librosa.feature.delta(feats, order=2, axis=0)
        feats = np.concatente(
            [feats, delta, delta_delta], axis=1)
    np.save(Path(feats_dir, af.stem + '.npy'), feats)

def main():
    parser = argparse.ArgumentParser(
        'extract librosa spectral features from audio', add_help=True)
    parser.add_argument(
        'feats_dir', nargs=None, type=Path,
        help='output directory for features')
    parser.add_argument(
        'af', nargs='+', type=Path,
        help='audio files to process')
    parser.add_argument(
        '--ftype', nargs=None, default='mfcc', metavar='FEATURE',
        choices=['mfcc', 'mel_fbank'],
        help='type of features to extract (default: %(default)s)')
    parser.add_argument(
        '--step', nargs=None, default=0.01, type=float, metavar='SECONDS',
        help='frame step in seconds (default: %(default)s)')
    parser.add_argument(
        '--wl', nargs=None, default=0.035, type=float, metavar='SECONDS',
        help='window length in seconds (default: %(default)s)')
    parser.add_argument(
        '--sr', nargs=None, default=16000, type=int, metavar='SAMPLES',
        help='resample to SAMPLES Hz before extraction (default: %(default)s)')
    parser.add_argument(
        '--add_deltas', default=False, action='store_true',
        help='compute delta and delta-delta features')
    parser.add_argument(
        '--config', nargs=None, default=None, type=Path, metavar='FILE',
        help='load additional keyword arguments for librosa feature function '
             'from YAML config FILE (default: %(default)s)')
    parser.add_argument(
        '--disable-progress', default=False, action='store_true',
        help='disable progress bar')
    parser.add_argument(
        '--n_jobs', nargs=None, default=1, type=int, metavar='JOBS',
        help='number of parallel jobs (default: %(default)s)')
    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)
    args = parser.parse_args()

    args.feats_dir.mkdir(parents=True, exist_ok=True)

    # Determine parameters for MFCC extraction.
    kwargs = {}
    kwargs['win_length'] = sec_to_samples(args.wl, args.sr)
    kwargs['hop_length'] = sec_to_samples(args.step, args.sr)
    if args.config is not None:
        with open(args.config, 'r') as f:
            kwargs.update(yaml.load(f, Loader=yaml.FullLoader))
    if 'n_fft' in kwargs:
        # Make sure n_fft is at least as large as the window size.
        kwargs['n_fft'] = int(2**np.ceil(np.log2(kwargs['win_length'])))

    # Extract features in parallel.
    if args.ftype == 'mfcc':
        feats_fn = mfcc
    elif args.ftype == 'mel_fbank':
        feats_fn = db_mel_spectrogram
    audio_paths = sorted(args.af)
    pool = Pool(args.n_jobs)
    f = partial(
        extract_feats, feats_dir=args.feats_dir, sr=args.sr, feats_fn=feats_fn,
        **kwargs)
    with tqdm(total=len(audio_paths), disable=args.disable_progress) as pbar:
        for _ in pool.imap(f, audio_paths):
            pbar.update(1)

if __name__ == '__main__':
    main()


import numpy as np
import pytest
import tensorflow as tf

from finetuner.tuner.keras import KerasTuner

@pytest.mark.parametrize('num_workers,expected_batch_time', [(1, 4)])
def test_multiple_workers(
    num_workers,
    expected_batch_time,
    generate_random_data,
    multi_workers_preprocess_fn,
    multi_workers_callback,
):
    data = generate_random_data(20, 2, 2)
    model = tf.keras.Sequential(
        [tf.keras.layers.Flatten(), tf.keras.layers.Dense(2, activation='relu')]
    )
    tuner = KerasTuner(model, callbacks=[multi_workers_callback])

    tuner.fit(
        data,
        epochs=1,
        batch_size=4,
        num_items_per_class=2,
        num_workers=num_workers,
        preprocess_fn=multi_workers_preprocess_fn,
    )

    np.testing.assert_almost_equal(
        np.mean(multi_workers_callback.batch_times), expected_batch_time, decimal=1
    )

@pytest.mark.xfail
@pytest.mark.parametrize('num_workers,expected_batch_time', [(2, 2), (4, 1)])
def test_multiple_workers_fail(
    num_workers,
    expected_batch_time,
    generate_random_data,
    multi_workers_preprocess_fn,
    multi_workers_callback,
):
    """Multi-processing of workers does not work with keras"""
    data = generate_random_data(20, 2, 2)
    model = tf.keras.Sequential(
        [tf.keras.layers.Flatten(), tf.keras.layers.Dense(2, activation='relu')]
    )
    tuner = KerasTuner(model, callbacks=[multi_workers_callback])

    tuner.fit(
        data,
        epochs=1,
        batch_size=4,
        num_items_per_class=2,
        num_workers=num_workers,
        preprocess_fn=multi_workers_preprocess_fn,
    )

    np.testing.assert_almost_equal(
        np.mean(multi_workers_callback.batch_times), expected_batch_time, decimal=1
    )


from .._utilities import helper_functions
import copy
import numpy as np
import pandas as pd
import vispy.color as vpcolor

class DIVETableRow:
    """
    This class stores information for a table row that can be displayed by the table in DIVE.
    """
    def get_row_data(self, data_objs, change_idx, current_time, timezone, change_duration):
        """
        Return the text and color to use for this row.

        Parameters
        ----------
        data_objs : dict
            The data objects currently in DIVE.
        change_idx : dict
            The valid data indices for the current time value.
            It is used to avoid recalculating the indices when updating
            table rows in a loop.
        current_time : None, numeric, pandas.Timestamp with tz
            The current time value in DIVE.
        timezone : str
            The current timezone in DIVE.
        change_duration : numeric, pandas.Timedelta
            The number of seconds prior to the current time that should be examined
            for color criteria that use "change".

        Returns
        -------
        str
            The text to use for this row.
        array
            The color to use for this row\'s background color.
        array
            The color to use for this row\'s text color.
        """
        data_obj = data_objs[self.data_name]
        if self.data_name not in change_idx:
            change_idx[self.data_name] = [None, data_obj.get_valid_idx(current_time)] # [(change_start_idx, change_end_idx), valid_idx]
        valid_idx = change_idx[self.data_name][1]
        has_data = np.any(valid_idx)
        if not has_data:
            val = ''
        elif self.operation == 'latest':
            val = data_obj.data.loc[valid_idx, self.field_name].iat[-1]
        else:
            val = data_obj.data.loc[valid_idx, self.field_name].apply(self.operation)
        text = helper_functions.strftime(helper_functions.safe_tz_convert(val, timezone), include_tz=True) if isinstance(val, pd.Timestamp) and val.tzinfo is not None else str(val)
        color_val = []
        if has_data:
            for color_operation, criteria_value, color_str in self.color_criteria:
                if color_operation != 'change':
                    try:
                        valid = helper_functions.apply_operation(color_operation, val, criteria_value)
                    except:
                        valid = helper_functions.apply_operation(color_operation, str(val), str(criteria_value))
                    if valid:
                        color_val.append(vpcolor.Color(color=color_str).RGB)
                elif color_operation == 'change' and current_time is not None:
                    time_vals = data_obj.data.loc[valid_idx, data_obj.time_field]
                    if current_time >= time_vals.iloc[0]:
                        if change_idx[self.data_name][0] is None:
                            change_idx[self.data_name][0] = time_vals.searchsorted(helper_functions.safe_time_math(current_time, change_duration, add=False)), time_vals.searchsorted(current_time, side='right')
                        s, e = change_idx[self.data_name][0]
                        if s != e: # Both values are in range
                            if s == 0: # First value
                                color_val.append(vpcolor.Color(color=color_str).RGB)
                            elif data_obj.data.loc[valid_idx, self.field_name].iloc[s - 1:e].nunique() > 1: # Value has changed
                                color_val.append(vpcolor.Color(color=color_str).RGB)
        criteria_count = len(color_val)
        row_color, text_color = None, None
        if criteria_count > 0:
            row_color = np.array(color_val).sum(axis=0) / criteria_count if self.blend_colors else color_val[-1]
            # Determine text color that is appropriate for the row color
            text_color = (row_color / 255)**2.2
            text_color = [0, 0, 0] if 0.2126 * text_color[0] + 0.7151 * text_color[1] + 0.0721 * text_color[2] > 0.18 else [255, 255, 255]
        return text, row_color, text_color

    def get_state(self, as_copy=True):
        state = {attr: getattr(self, attr, None) for attr in ['data_name', 'field_name', 'label', 'operation', 'color_criteria', 'blend_colors']}
        return copy.deepcopy(state) if as_copy else state

    def set_state(self, data_objs, state):
        attrs = self.get_state(as_copy=False)
        data_changed = field_changed = operation_changed = False

        # Validate parameters
        if 'data_name' in state:
            attrs['data_name'] = state['data_name']
            if not isinstance(attrs['data_name'], str):
                return 'data_name must be of type: str'
            elif attrs['data_name'] not in data_objs:
                return '"{}" is not a valid data name.'.format(attrs['data_name'])
            data_changed = True
        if 'field_name' in state or data_changed:
            if 'field_name' in state:
                attrs['field_name'] = state['field_name']
                field_changed = True
            if not isinstance(attrs['field_name'], str):
                return 'field_name must be of type: str'
            elif attrs['field_name'] not in data_objs[attrs['data_name']].data:
                return '"{}" is not a valid field name for data object "{}".'.format(attrs['field_name'], attrs['data_name'])
        if 'label' in state:
            attrs['label'] = state['label']
            if not isinstance(attrs['label'], str):
                return 'label must be of type: str'
        if 'operation' in state or data_changed or field_changed:
            if 'operation' in state:
                attrs['operation'] = state['operation']
                operation_changed = True
            if not isinstance(attrs['operation'], str):
                return 'operation must be of type: str'
            elif attrs['operation'] != 'latest':
                try:
                    data_objs[attrs['data_name']].data.loc[:, attrs['field_name']].apply(attrs['operation'])
                except:
                    return 'Cannot perform operation "{}" on field "{}" in data object, "{}".'.format(attrs['operation'], attrs['field_name'], attrs['data_name'])
        if 'color_criteria' in state or data_changed or field_changed or operation_changed:
            if 'color_criteria' in state:
                attrs['color_criteria'] = state['color_criteria']
            if isinstance(attrs['color_criteria'], np.ndarray):
                if attrs['color_criteria'].ndim > 1:
                    attrs['color_criteria'] = attrs['color_criteria'].flatten()
            elif isinstance(attrs['color_criteria'], pd.Series):
                attrs['color_criteria'] = attrs['color_criteria'].to_numpy()
            elif not isinstance(attrs['color_criteria'], (list, tuple)):
                return 'color_criteria must be one of the following types: list, tuple, numpy.ndarray, pandas.Series'
            color_criteria = []
            for criteria in attrs['color_criteria']:
                if isinstance(criteria, np.ndarray):
                    if criteria.ndim > 1:
                        criteria = criteria.flatten()
                elif isinstance(criteria, pd.Series):
                    criteria = criteria.to_numpy()
                elif not isinstance(criteria, (list, tuple)):
                    return 'Arrays in color_criteria must be one of the following types: list, tuple, numpy.ndarray, pandas.Series'
                if len(criteria) < 3:
                    return 'Arrays in color_criteria must contain 3 elements.'
                elif not isinstance(criteria[0], str):
                    return 'The first element in a color_criteria array must be of type: str'
                comparison_op = criteria[0].lower()
                if comparison_op not in ['>', '>=', '==', '!=', '<=', '<', 'change']:
                    return 'The first element in a color_criteria array must be one of the following: ">", ">=", "==", "!=", "<=", "<", "change"'
                elif comparison_op == 'change':
                    if data_objs[attrs['data_name']].time_field is None:
                        return 'The first element in a color_criteria array cannot be "change" if the data object doesn\'t have a time field.'
                    elif attrs['operation'] != 'latest':
                        return 'The first element in a color_criteria array cannot be "change" if "operation" isn\'t "latest".'
                elif comparison_op != 'change':
                    data_dtype = data_objs[attrs['data_name']].data.dtypes.at[attrs['field_name']]
                    if pd.api.types.is_numeric_dtype(data_dtype):
                        if not pd.api.types.is_numeric_dtype(type(criteria[1])):
                            return '"{}" cannot be compared with field "{}" in data object "{}" because it isn\'t numeric.'.format(criteria[1], attrs['field_name'], attrs['data_name'])
                    elif pd.api.types.is_datetime64tz_dtype(data_dtype):
                        if not (isinstance(criteria[1], pd.Timestamp) and criteria[1].tzinfo is not None):
                            return '"{}" cannot be compared with field "{}" in data object "{}" because it isn\'t a pandas.Timestamp with tz.'.format(criteria[1], attrs['field_name'], attrs['data_name'])
                if not isinstance(criteria[2], str):
                    return 'The third element in a color_criteria array must be of type: str'
                try:
                    vpcolor.Color(criteria[2])
                except:
                    return '"{}" is not a valid color string.'.format(criteria[2])
                color_criteria.append([comparison_op, copy.deepcopy(criteria[1]), criteria[2]])
            attrs['color_criteria'] = color_criteria
        if 'blend_colors' in state:
            attrs['blend_colors'] = state['blend_colors']
            if not isinstance(attrs['blend_colors'], (bool, np.bool_)):
                return 'blend_colors must be of type: bool'

        for attr in attrs:
            setattr(self, attr, attrs[attr])