import cv2
import os
import yaml
from pathlib import Path
import numpy as np
from enum import IntEnum
import numexpr as ne

from modelhub import DFLive
from modelhub import onnx as onnx_models
from modelhub import cv as cv_models

from xlib import os as lib_os
from xlib.facemeta import FaceMark, FaceURect
from xlib.facemeta import FaceULandmarks, FacePose
from xlib.facemeta import FaceAlign
from xlib.facemeta import FaceMask, FaceSwap

from xlib.image import ImageProcessor
from xlib.mp import csw as lib_csw
from xlib.python import all_is_not_None

from xlib.onnxruntime.device import ORTDeviceInfo, get_available_devices_info, get_cpu_device

def read_image(filepath):
    with open(filepath, "rb") as stream:
        bytes = bytearray(stream.read())
    numpyarray = np.asarray(bytes, dtype=np.uint8)
    img = cv2.imdecode(numpyarray, flags=cv2.IMREAD_UNCHANGED)
    return img

class DetectorType(IntEnum):
    CENTER_FACE = 0
    S3FD = 1
    YOLOV5 = 2
DetectorTypeNames = ['CenterFace', 'S3FD', 'YoloV5']

class FaceSortBy(IntEnum):
    LARGEST = 0
    DIST_FROM_CENTER = 1
FaceSortByNames = ['@FaceDetector.largest', '@FaceDetector.dist_from_center']

class MarkerType(IntEnum):
    OPENCV_LBF = 0
    GOOGLE_FACEMESH = 1
MarkerTypeNames = ['OpenCV LBF','Google FaceMesh']

class FaceMaskType(IntEnum):
    SRC = 0
    CELEB = 1
    SRC_M_CELEB = 2
FaceMaskTypeNames = ['@FaceMerger.FaceMaskType.SRC','@FaceMerger.FaceMaskType.CELEB','@FaceMerger.FaceMaskType.SRC_M_CELEB']

class MyFaceDetector():
    '''
    Face Detector
    '''
    def __init__(self, cfg, device):
        self.cfg = cfg
        self.detector_type = cfg['detector_type']
        if self.detector_type == DetectorType.CENTER_FACE:
            self.CenterFace = onnx_models.CenterFace(device)
        elif self.detector_type == DetectorType.S3FD:
            self.S3FD = onnx_models.S3FD(device)
        elif self.detector_type == DetectorType.YOLOV5:
            self.YoloV5Face = onnx_models.YoloV5Face(device)

    def run(self, frame_name, frame_image):
        detector_type = self.detector_type
        if (detector_type == DetectorType.CENTER_FACE and self.CenterFace is not None) or \
                (detector_type == DetectorType.S3FD and self.S3FD is not None) or \
                (detector_type == DetectorType.YOLOV5 and self.YoloV5Face is not None):

            _, H, W, _ = ImageProcessor(frame_image).get_dims()

            rects = []
            if detector_type == DetectorType.CENTER_FACE:
                rects = self.CenterFace.extract(frame_image, threshold=self.cfg['threshold'],
                                                fixed_window=self.cfg['fixed_window_size'])[0]
            elif detector_type == DetectorType.S3FD:
                rects = self.S3FD.extract(frame_image, threshold=self.cfg['threshold'],
                                          fixed_window=self.cfg['fixed_window_size'])[0]
            elif detector_type == DetectorType.YOLOV5:
                rects = self.YoloV5Face.extract(frame_image, threshold=self.cfg['threshold'],
                                                fixed_window=self.cfg['fixed_window_size'])[0]

            # to list of FaceURect
            rects = [FaceURect.from_ltrb((l / W, t / H, r / W, b / H)) for l, t, r, b in rects]

            # sort
            if eval(self.cfg['sort_by']) == FaceSortBy.LARGEST:
                rects = FaceURect.sort_by_area_size(rects)
            elif eval(self.cfg['sort_by']) == FaceSortBy.DIST_FROM_CENTER:
                rects = FaceURect.sort_by_dist_from_center(rects)

            if len(rects) != 0:
                max_faces = self.cfg['max_faces']
                if max_faces != 0 and len(rects) > max_faces:
                    rects = rects[:max_faces]

                if self.cfg['temporal_smoothing'] != 1:
                    if len(self.temporal_rects) != len(rects):
                        self.temporal_rects = [[] for _ in range(len(rects))]

                _face_mark_list = []
                for face_id, face_rect in enumerate(rects):
                    if self.cfg['temporal_smoothing'] != 1:
                        if len(self.temporal_rects[face_id]) == 0:
                            self.temporal_rects[face_id].append(face_rect.as_4pts())

                        self.temporal_rects[face_id] = self.temporal_rects[face_id][
                                                       -self.cfg['temporal_smoothing']:]

                        face_rect = FaceURect.from_4pts(np.mean(self.temporal_rects[face_id], 0))

                    if face_rect.get_area() != 0:
                        face_mark = FaceMark()
                        face_mark.set_image_name(frame_name)
                        face_mark.set_face_urect(face_rect)
                        _face_mark_list.append(face_mark)

                return _face_mark_list

class MyFaceMarker():
    '''
    Face Marker
    '''
    def __init__(self, cfg, device):
        self.cfg = cfg
        self.marker_type = cfg['marker_type']

        if self.marker_type == MarkerType.OPENCV_LBF:
            self.opencv_lbf = cv_models.FaceMarkerLBF()
        elif self.marker_type == MarkerType.GOOGLE_FACEMESH:
            self.google_facemesh = onnx_models.FaceMesh(device)
        self.temporal_lmrks = []

    def run(self, frame_image, face_mark_list):
        marker_type = self.marker_type

        is_opencv_lbf = marker_type == MarkerType.OPENCV_LBF and self.opencv_lbf is not None
        is_google_facemesh = marker_type == MarkerType.GOOGLE_FACEMESH and self.google_facemesh is not None

        if marker_type is not None:
            if all_is_not_None(frame_image) and (is_opencv_lbf or is_google_facemesh):
                if self.cfg['temporal_smoothing'] != 1 and len(self.temporal_lmrks) != len(face_mark_list):
                    self.temporal_lmrks = [ [] for _ in range(len(face_mark_list)) ]

                for face_id, face_mark in enumerate(face_mark_list):
                    face_mark_rect = face_mark.get_face_urect()
                    if face_mark_rect is not None:
                        # Cut the face to feed to the face marker
                        face_image, face_uni_mat = face_mark_rect.cut(frame_image,
                            self.cfg['marker_coverage'], 256 if is_opencv_lbf else 192 if is_google_facemesh else 0 )
                        _,H,W,_ = ImageProcessor(face_image).get_dims()

                        if is_opencv_lbf:
                            lmrks = self.opencv_lbf.extract(face_image)[0]
                        elif is_google_facemesh:
                            lmrks = self.google_facemesh.extract(face_image)[0]

                        if self.cfg['temporal_smoothing'] != 1:
                            if len(self.temporal_lmrks[face_id]) == 0:
                                self.temporal_lmrks[face_id].append(lmrks)
                            self.temporal_lmrks[face_id] = self.temporal_lmrks[face_id][-self.cfg['temporal_smoothing']:]

                            lmrks = np.mean(self.temporal_lmrks[face_id],0 )

                        if is_google_facemesh:
                            face_mark.set_face_pose(FacePose.from_3D_468_landmarks(lmrks))

                        if is_opencv_lbf:
                            lmrks /= (W,H)
                        elif is_google_facemesh:
                            lmrks = lmrks[...,0:2] / (W,H)

                        face_ulmrks = FaceULandmarks.create (FaceULandmarks.Type.LANDMARKS_68 if is_opencv_lbf else \
                                                             FaceULandmarks.Type.LANDMARKS_468 if is_google_facemesh else None, lmrks)

                        face_ulmrks = face_ulmrks.transform(face_uni_mat, invert=True)
                        face_mark.add_face_ulandmarks (face_ulmrks)

class MyFrameAdjuster():
    '''
    Frame Adjuster
    '''
    def __init__(self, cfg):
        self.cfg = cfg

    def run(self, frame_image):
        if frame_image is not None:
            frame_image_ip = ImageProcessor(frame_image)
            frame_image_ip.median_blur(5, self.cfg['median_blur_per'] / 100.0)
            frame_image_ip.degrade_resize(self.cfg['degrade_bicubic_per'] / 100.0,
                                          interpolation=ImageProcessor.Interpolation.CUBIC)

            frame_image = frame_image_ip.get_image('HWC')
            return frame_image

class MyFaceAligner():
    '''
    Face Aligner
    '''
    def __init__(self, cfg):
        self.cfg = cfg

    def run(self, frame_name, frame_image, face_mark_list):
        face_align_images = {}
        for face_id, face_mark in enumerate(face_mark_list):
            face_ulmrks = face_mark.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_468)
            if face_ulmrks is None:
                face_ulmrks = face_mark.get_face_ulandmarks_by_type(FaceULandmarks.Type.LANDMARKS_68)

            head_yaw = None
            if self.cfg['head_mode']:
                face_pose = face_mark.get_face_pose()
                if face_pose is not None:
                    head_yaw = face_pose.as_radians()[1]

            if face_ulmrks is not None:
                face_image, uni_mat = face_ulmrks.cut(frame_image, self.cfg['face_coverage'], self.cfg['resolution'],
                                                      exclude_moving_parts=self.cfg['exclude_moving_parts'],
                                                      head_yaw=head_yaw,
                                                      x_offset=self.cfg['x_offset'],
                                                      y_offset=self.cfg['y_offset'])

                face_align_image_name = f'{frame_name}_{face_id}_aligned'

                face_align = FaceAlign()
                face_align.set_image_name(face_align_image_name)
                face_align.set_coverage(self.cfg['face_coverage'])
                face_align.set_source_face_ulandmarks_type(face_ulmrks.get_type())
                face_align.set_source_to_aligned_uni_mat(uni_mat)

                for face_ulmrks in face_mark.get_face_ulandmarks_list():
                    face_align.add_face_ulandmarks(face_ulmrks.transform(uni_mat))
                face_mark.set_face_align(face_align)
                face_align_images[face_align_image_name] = face_image
        return face_align_images

class MyFaceSwapper():
    '''
    Face Swapper
    '''
    def __init__(self, cfg, device):
        self.cfg = cfg
        # dfm_models = DFLive.get_available_models_info(cfg['dfm_models_path'])
        dfm_path_stem = os.path.splitext(os.path.basename(cfg['dfs_model_path']))[0]
        model = DFLive.DFMModel.DFMModelInfo(dfm_path_stem, model_path=Path(cfg['dfs_model_path']), )
        self.dfm_model_initializer = DFLive.DFMModel_from_info(model, device)

    def run(self, face_align_images, face_mark_list):
        if self.dfm_model_initializer is not None:
            events = self.dfm_model_initializer.process_events()
            events = self.dfm_model_initializer.process_events()

            self.dfm_model = events.dfm_model

            model_width, model_height = self.dfm_model.get_input_res()
            dfm_model = self.dfm_model

            face_align_masks, face_swaps, face_swap_masks = {}, {}, {}
            for i, face_mark in enumerate(face_mark_list):
                face_align = face_mark.get_face_align()
                if face_align is not None:
                    face_align_image_name = face_align.get_image_name()
                    face_align_image = face_align_images[face_align_image_name]
                    if face_align_image is not None:

                        pre_gamma_red = self.cfg['pre_gamma_red']
                        pre_gamma_green = self.cfg['pre_gamma_green']
                        pre_gamma_blue = self.cfg['pre_gamma_blue']

                        fai_ip = ImageProcessor(face_align_image)
                        if self.cfg['presharpen_amount'] != 0:
                            fai_ip.sharpen(factor=self.cfg['presharpen_amount'])

                        if pre_gamma_red != 1.0 or pre_gamma_green != 1.0 or pre_gamma_blue != 1.0:
                            fai_ip.adjust_gamma(pre_gamma_red, pre_gamma_green, pre_gamma_blue)
                        face_align_image = fai_ip.get_image('HWC')

                        celeb_face, celeb_face_mask_img, face_align_mask_img = dfm_model.convert(
                            face_align_image, morph_factor=self.cfg['morph_factor'])
                        celeb_face, celeb_face_mask_img, face_align_mask_img = celeb_face[0], \
                                                                               celeb_face_mask_img[0], \
                                                                               face_align_mask_img[0]

                        if self.cfg['two_pass']:
                            celeb_face, celeb_face_mask_img, _ = dfm_model.convert(celeb_face,
                                                                                morph_factor=self.cfg['morph_factor'])
                            celeb_face, celeb_face_mask_img = celeb_face[0], celeb_face_mask_img[0]

                        face_align_mask = FaceMask()
                        face_align_mask.set_image_name(f'{face_align_image_name}_mask')
                        face_align.set_face_mask(face_align_mask)
                        face_align_masks[face_align_mask.get_image_name()] = face_align_mask_img

                        face_swap = FaceSwap()
                        face_swap.set_image_name(f"{face_align_image_name}_swapped")
                        face_align.set_face_swap(face_swap)
                        face_swaps[face_swap.get_image_name()] = celeb_face

                        face_swap_mask = FaceMask()
                        face_swap_mask.set_image_name(f'{face_swap.get_image_name()}_mask')
                        face_swap.set_face_mask(face_swap_mask)
                        face_swap_masks[face_swap_mask.get_image_name()] = celeb_face_mask_img

                return face_align_masks, face_swaps, face_swap_masks

class MyFaceMerger():
    '''
    Face Merger
    '''
    def __init__(self, cfg, device):
        self.cfg = cfg
        self.is_gpu = False

        if not device.is_cpu():
            self.is_gpu = True

            global cp
            import cupy as cp  # BUG eats 1.8Gb paging file per process, so import on demand
            cp.cuda.Device(device.get_index()).use()

            self.cp_mask_clip_kernel = cp.ElementwiseKernel('T x', 'T z', 'z = x < 0.004 ? 0 : x > 1.0 ? 1.0 : x',
                                                            'mask_clip_kernel')
            self.cp_merge_kernel = cp.ElementwiseKernel('T bg, T face, T mask', 'T z', 'z = bg*(1.0-mask) + face*mask',
                                                        'merge_kernel')
            self.cp_merge_kernel_opacity = cp.ElementwiseKernel('T bg, T face, T mask, T opacity', 'T z',
                                                                'z = bg*(1.0-mask) + bg*mask*(1.0-opacity) + face*mask*opacity',
                                                                'merge_kernel_opacity')

    def run(self, frame_name, frame_image, face_mark_list, face_aligns, face_swaps, face_align_masks, face_swap_masks):
        if frame_image is not None:
            frame_finals = {}
            for face_mark in face_mark_list:
                face_align = face_mark.get_face_align()
                if face_align is not None:
                    face_swap = face_align.get_face_swap()
                    face_align_mask = face_align.get_face_mask()

                    if face_swap is not None:
                        face_swap_mask = face_swap.get_face_mask()
                        if face_swap_mask is not None:

                            face_align_img = face_aligns[face_align.get_image_name()]
                            face_swap_img = face_swaps[face_swap.get_image_name()]

                            face_align_mask_img = face_align_masks[face_align_mask.get_image_name()]
                            face_swap_mask_img = face_swap_masks[face_swap_mask.get_image_name()]
                            source_to_aligned_uni_mat = face_align.get_source_to_aligned_uni_mat()

                            face_mask_type = self.cfg['face_mask_type']

                            if all_is_not_None(face_align_img, face_align_mask_img, face_swap_img, face_swap_mask_img, face_mask_type):
                                face_height, face_width = face_align_img.shape[:2]

                                if self.is_gpu:
                                    frame_image = cp.asarray(frame_image)
                                    face_align_mask_img = cp.asarray(face_align_mask_img)
                                    face_swap_mask_img = cp.asarray(face_swap_mask_img)
                                    face_swap_img = cp.asarray(face_swap_img)

                                frame_image_ip = ImageProcessor(frame_image).to_ufloat32()
                                frame_image, (_, frame_height, frame_width, _) = frame_image_ip.get_image('HWC'), frame_image_ip.get_dims()
                                face_align_mask_img = ImageProcessor(face_align_mask_img).to_ufloat32().get_image('HW')
                                face_swap_mask_img = ImageProcessor(face_swap_mask_img).to_ufloat32().get_image('HW')

                                aligned_to_source_uni_mat = source_to_aligned_uni_mat.invert()
                                aligned_to_source_uni_mat = aligned_to_source_uni_mat.source_translated(-self.cfg['face_x_offset'], -self.cfg['face_y_offset'])
                                aligned_to_source_uni_mat = aligned_to_source_uni_mat.source_scaled_around_center(self.cfg['face_scale'],self.cfg['face_scale'])
                                aligned_to_source_uni_mat = aligned_to_source_uni_mat.to_exact_mat (face_width, face_height, frame_width, frame_height)

                                if face_mask_type == FaceMaskType.SRC:
                                    face_mask = face_align_mask_img
                                elif face_mask_type == FaceMaskType.CELEB:
                                    face_mask = face_swap_mask_img
                                elif face_mask_type == FaceMaskType.SRC_M_CELEB:
                                    face_mask = face_align_mask_img*face_swap_mask_img

                                # Combine face mask
                                face_mask_ip = ImageProcessor(face_mask).erode_blur(self.cfg['face_mask_erode'], self.cfg['face_mask_blur'], fade_to_border=True) \
                                                                        .warpAffine(aligned_to_source_uni_mat, frame_width, frame_height)
                                if self.is_gpu:
                                    face_mask_ip.apply( lambda img: self.cp_mask_clip_kernel(img) )
                                else:
                                    face_mask_ip.clip2( (1.0/255.0), 0.0, 1.0, 1.0)
                                frame_face_mask = face_mask_ip.get_image('HWC')

                                frame_face_swap_img = ImageProcessor(face_swap_img) \
                                                      .to_ufloat32().warpAffine(aligned_to_source_uni_mat, frame_width, frame_height).get_image('HWC')

                                # Combine final frame
                                opacity = self.cfg['face_opacity']
                                if self.is_gpu:
                                    if opacity == 1.0:
                                        frame_final = self.cp_merge_kernel(frame_image, frame_face_swap_img, frame_face_mask)
                                    else:
                                        frame_final = self.cp_merge_kernel_opacity(frame_image, frame_face_swap_img, frame_face_mask, opacity)
                                    frame_final = cp.asnumpy(frame_final)
                                else:
                                    if opacity == 1.0:
                                        frame_final = ne.evaluate('frame_image*(1.0-frame_face_mask) + frame_face_swap_img*frame_face_mask')
                                    else:
                                        frame_final = ne.evaluate('frame_image*(1.0-frame_face_mask) + frame_image*frame_face_mask*(1.0-opacity) + frame_face_swap_img*frame_face_mask*opacity')

                                # keep image in float32 in order not to extra load FaceMerger

                                merged_frame_name = f'{frame_name}_merged'
                                frame_finals[merged_frame_name] = frame_final
            return frame_finals

def inference(img, onnx_device: ORTDeviceInfo):
    '''
    Inference Pipeline
    '''
    ## run face detector
    face_detector = MyFaceDetector(cfg=config['FaceDetector'], device=onnx_device)
    _face_mark_list = face_detector.run(frame_name=filename, frame_image=img)
    print(len(_face_mark_list), _face_mark_list)

    ## run face marker
    face_marker = MyFaceMarker(cfg=config['FaceMarker'], device=onnx_device)
    face_marker.run(frame_image=img, face_mark_list=_face_mark_list)

    ## run face aligner
    face_aligner = MyFaceAligner(cfg=config['FaceAligner'])
    face_aligns = face_aligner.run(frame_name=filename, frame_image=img, face_mark_list=_face_mark_list)

    ## run face swapper
    face_swapper = MyFaceSwapper(cfg=config['FaceSwapper'], device=onnx_device)
    face_align_masks, face_swaps, face_swap_masks = face_swapper.run(face_align_images=face_aligns,
                                                                     face_mark_list=_face_mark_list)

    ## run frame adjuster
    frame_adjuster = MyFrameAdjuster(cfg=config['FrameAdjuster'])
    img = frame_adjuster.run(frame_image=img)

    ## run face merger
    face_merger = MyFaceMerger(cfg=config['FaceMerger'], device=onnx_device)
    frame_finals = face_merger.run(filename, img, _face_mark_list, face_aligns, face_swaps, face_align_masks,
                                   face_swap_masks)

    return list(frame_finals.values())[0]

if __name__ == '__main__':
    ## get onnx devices info
    devices = get_available_devices_info()
    print('devices: ', devices)

    USE_GPU = False
    if USE_GPU:
        # onnx_device = list(filter(lambda x: 'CUDA' in x.get_execution_provider(), devices))[0]
        onnx_device = list(filter(lambda x: not x.is_cpu(), devices))[0]
    else:
        onnx_device = get_cpu_device()
    print('device: ', onnx_device)
    ## read default config
    config = yaml.load(open('config.yml', 'rb'), Loader=yaml.Loader)

    ## read image
    filepath = 'F:/Projects/DeepFaceLive_NVIDIA/userdata/samples/photos/000007.jpg'
    filename = os.path.splitext(os.path.basename(filepath))[0]
    img = read_image(filepath)

    ## inference
    res_img = inference(img, onnx_device)
    print('result: ', res_img.shape)

    ## visualize
    cv2.imshow('img_color', res_img)
    cv2.waitKey(0)
    cv2.imwrite(f'result_{filename}.jpg', np.asarray(res_img * 255, dtype=np.uint8))
    cv2.destroyAllWindows()


import json
import matplotlib.pyplot as plt
from numpy import array, zeros
from cv2 import imread
from glob import glob
import scipy.misc
import os

path="C:\\Users\\nihal\\PycharmProjects\\datascp\\data\\"
# load the images
dno=0
with open("test.txt", "a") as myfile:
    for d in os.listdir(path):
        print("Doing "+str(d))
        imgs=imread(path+"\\"+d+"\\"+'images\\image00000.tiff')
        files = sorted(glob(path+"\\"+d+"\\"+'images\\*.tiff'))
        file_count=0
        for f in files:
            print("Processing Image and its Regions :"+f,end="\r")
            imgs+=imread(f)
            file_count+=1
        rgb_min = 0
        rgb_max = 1
        normal_min = 0
        normal_max = 255
        #print(imgs[345])
        imgs= rgb_min+(((imgs-normal_min)*(rgb_max-rgb_min))/(normal_max-normal_min))
        #print(imgs[345])
        fname='outfile_'+str(dno)+'.jpg'
        scipy.misc.imsave('outfile_'+str(dno)+'.jpg', imgs)
        dno += 1

        with open(path + "\\" + d + "\\" + "regions\\regions.json") as f:
            regions = json.load(f)
            i = 0
            squareregions = {}
            for rr in regions:
                maxx = 0
                maxy = 0
                minx = 512
                miny = 512
                # print("id: "+str(regions[i]["id"]))
                for r in regions[i]["coordinates"]:
                    if maxx < r[0]:
                        maxx = r[0]
                    if maxy < r[1]:
                        maxy = r[1]
                    if minx > r[0]:
                        minx = r[0]
                    if miny > r[1]:
                        miny = r[1]
                arry = []
                top = [minx, miny]
                bottom = [maxx, maxy]
                arry.append(top)
                arry.append(bottom)
                # print(arry)
                # squareregions[regions[i]["id"]]=arry
                i += 1
                myfile.write(path +fname+ "," + str(minx) + "," + str(miny) + "," + str(maxx) + "," + str(maxy) + "," + "neuron\n")

# show the outputs
plt.imshow(imgs)
plt.show()


#! /usr/bin/env python
# coding=utf-8
# ================================================================
#   Copyright (C) 2018 * Ltd. All rights reserved.
#
#   Editor      : VIM
#   File name   : process_video_to_images.py
#   Author      : karsil
#   Created date: 2019-12-17 17:55:23
#   Description :
#
# ================================================================

import cv2
import time
import numpy as np
import core.utils as utils
import tensorflow as tf
from PIL import Image
import os
import sys
import argparse
from datetime import datetime
import srt
from tqdm import tqdm

return_elements  = ["input/input_data:0", "pred_sbbox/concat_2:0", "pred_mbbox/concat_2:0", "pred_lbbox/concat_2:0"]
pb_file          = "./yolov3_fish.pb"
num_classes      = 2
input_size       = 416
score_threshold  = 0.3
graph            = tf.Graph()
return_tensors   = utils.read_pb_return_tensors(graph, pb_file, return_elements)

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('file_path', help='Provide the path to a file with data')
    args = parser.parse_args()

    inputFile = args.file_path

    if not os.path.exists(inputFile):
        print(f"The video file {inputFile} does not exist. Quitting...")
        sys.exit()

    base = os.path.splitext(inputFile)[0]
    srtFile = base + '.srt'
    print(srtFile)
    if not os.path.exists(srtFile):
        print(f"The srt file {inputFile} does not exist. Quitting...")
        sys.exit()

    # Reading srt file
    with open(srtFile, 'r') as f:
        data = f.read()
    srt_generator = srt.parse(data)
    srtData = list(srt_generator)
    print(f"Constructed generator with {len(srtData)} entries")

    targetFolder = createTargetFolder(inputFile)

    run(inputFile, srtData, targetFolder)

    print(f"Done! files have been saved to folder ", targetFolder)

def run(inputFile, srtData, targetFolder):
    with tf.Session(graph=graph) as sess:
        print("Reading video at " + inputFile)
        vid = cv2.VideoCapture(inputFile)

        maxFrames = int(vid.get(cv2.CAP_PROP_FRAME_COUNT))

        print(f"Begin processing of video with {maxFrames} frames...")

        # frames
        currFrame = 0

        # progressbar
        pbar = tqdm(total=maxFrames)

        while (vid.isOpened()):
            ret, frame = vid.read()
            if ret:
                frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
                image = Image.fromarray(frame)
            else:
                if (currFrame > 0):
                    # something has been processed earlier
                    print("Done. Quitting...")
                    break
                else:
                    raise ValueError("Error while reading!", frame)

            frame_size = frame.shape[:2]
            image_data = utils.image_preporcess(np.copy(frame), [input_size, input_size])
            image_data = image_data[np.newaxis, ...]
            prev_time = time.time()

            pred_sbbox, pred_mbbox, pred_lbbox = sess.run(
                [return_tensors[1], return_tensors[2], return_tensors[3]],
                feed_dict={return_tensors[0]: image_data})

            pred_bbox = np.concatenate([np.reshape(pred_sbbox, (-1, 5 + num_classes)),
                                        np.reshape(pred_mbbox, (-1, 5 + num_classes)),
                                        np.reshape(pred_lbbox, (-1, 5 + num_classes))], axis=0)

            bboxes = utils.postprocess_boxes(pred_bbox, frame_size, input_size, score_treshold)
            bboxes = utils.nms(bboxes, 0.45, method='nms')

            #  When detection has been observed, save image
            if (len(bboxes) > 0):
                image = utils.draw_bbox(frame, bboxes)

                # saving as image
                image = Image.fromarray(image)
                exportName = srtData[currFrame].content
                filepath = targetFolder + "/" + exportName + ".jpg"

                # Save logfile for image
                # Format: TopleftX, TopleftY, BottomRightX, BottomRightY, Class ID
                filepathLog = targetFolder + "/" + exportName + ".txt"
                with open(filepathLog, "w") as logfile:
                    for i, bbox in enumerate(bboxes):
                        coor = np.array(bbox[:4], dtype=np.int32)
                        class_ind = int(bbox[5])
                        logfile.write(str(coor[0]) + " " + str(coor[1]) + " " + str(coor[2]) + " " + str(coor[3]) + " " + str(class_ind) + "\n")

                image.save(filepath)

            pbar.update(1)
            currFrame = currFrame + 1

        vid.release()

# returns absolute path of newly created target folder
def createTargetFolder(inputFile):
    now = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')

    # Saving files into folder 'inputFilename_%Y-%m-%d_%H:%M:%S'
    sourcePathAbs = os.path.abspath(inputFile)
    sourceFileHead, sourceFileTail = os.path.split(sourcePathAbs)
    outputPath = sourceFileTail + "_" + now
    targetFolder = sourceFileHead + "/" + outputPath

    try:
        os.mkdir(targetFolder)
        print("Target directory ", targetFolder, " created")
    except FileExistsError:
        print("Target directory ", targetFolder, " already exists...")

    return targetFolder

if __name__ == "__main__":
    main()


## attack.py -- generate audio adversarial examples
##
## Copyright (C) 2017, Nicholas Carlini <nicholas@carlini.com>.
##
## This program is licenced under the BSD 2-Clause licence,
## contained in the LICENCE file in this directory.

import numpy as np
import tensorflow as tf
from shutil import copyfile
import scipy.io.wavfile as wav
import struct
import pandas as pd
import time
import sys
from collections import namedtuple
sys.path.append("DeepSpeech")
import DeepSpeech
try:
    import pydub
except:
    print("pydub was not loaded, MP3 compression will not work")
from tensorflow.python.keras.backend import ctc_label_dense_to_sparse
from tf_logits import get_logits
from ds_ctcdecoder import ctc_beam_search_decoder, Scorer
from deepspeech_training.util.flags import create_flags, FLAGS
from deepspeech_training.util.config import Config, initialize_globals
import absl.flags

# Define arguments to be parsed
f = absl.flags
f.DEFINE_multi_string('input', None, 'Input audio .wav file(s), at 16KHz (separated by spaces)')
f.DEFINE_multi_string('output', None, 'Path for the adversarial example(s)')
f.DEFINE_string('outprefix', None, 'Prefix of path for adversarial examples')
f.DEFINE_string('target', None, 'Target transcription')
f.DEFINE_multi_string('finetune', None, 'Initial .wav file(s) to use as a starting point')
f.DEFINE_integer('lr', 100, 'Learning rate for optimization')
f.DEFINE_integer('iterations', 1000, 'Maximum number of iterations of gradient descent')
f.DEFINE_float('l2penalty', float('inf'), 'Weight for l2 penalty on loss function')
f.DEFINE_boolean('mp3', False, 'Generate MP3 compression resistant adversarial examples')
f.DEFINE_string('restore_path', None, 'Path to the DeepSpeech checkpoint (ending in best_dev-1466475)')
f.DEFINE_string('lang', "en", 'Language of the input audio (English: en, German: de)')

# Define which arguments are required
f.mark_flag_as_required('input')
f.mark_flag_as_required('target')
f.mark_flag_as_required('restore_path')

def convert_mp3(new, lengths):
    import pydub
    wav.write("/tmp/load.wav", 16000,
              np.array(np.clip(np.round(new[0][:lengths[0]]),
                               -2**15, 2**15-1),dtype=np.int16))
    pydub.AudioSegment.from_wav("/tmp/load.wav").export("/tmp/saved.mp3")
    raw = pydub.AudioSegment.from_mp3("/tmp/saved.mp3")
    mp3ed = np.array([struct.unpack("<h", raw.raw_data[i:i+2])[0] for i in range(0,len(raw.raw_data),2)])[np.newaxis,:lengths[0]]
    return mp3ed

class Attack:
    def __init__(self, sess, loss_fn, phrase_length, max_audio_len,
                 learning_rate=10, num_iterations=5000, batch_size=1,
                 mp3=False, l2penalty=float('inf'), restore_path=None):
        """
        Set up the attack procedure.
        Here we create the TF graph that we're going to use to
        actually generate the adversarial examples.
        """
        print("\nInitializing attack..\n")
        self.sess = sess
        self.learning_rate = learning_rate
        self.num_iterations = num_iterations
        self.batch_size = batch_size
        self.phrase_length = phrase_length
        self.max_audio_len = max_audio_len
        self.mp3 = mp3

        # Create all the variables necessary
        # they are prefixed with qq_ just so that we know which
        # ones are ours so when we restore the session we don't
        # clobber them.
        self.delta = delta = tf.Variable(np.zeros((batch_size, max_audio_len), dtype=np.float32), name='qq_delta')
        self.mask = mask = tf.Variable(np.zeros((batch_size, max_audio_len), dtype=np.float32), name='qq_mask')
        self.cwmask = cwmask = tf.Variable(np.zeros((batch_size, phrase_length), dtype=np.float32), name='qq_cwmask')
        self.original = original = tf.Variable(np.zeros((batch_size, max_audio_len), dtype=np.float32), name='qq_original')
        self.lengths = lengths = tf.Variable(np.zeros(batch_size, dtype=np.int32), name='qq_lengths')
        self.importance = tf.Variable(np.zeros((batch_size, phrase_length), dtype=np.float32), name='qq_importance')
        self.target_phrase = tf.Variable(np.zeros((batch_size, phrase_length), dtype=np.int32), name='qq_phrase')
        self.target_phrase_lengths = tf.Variable(np.zeros((batch_size), dtype=np.int32), name='qq_phrase_lengths')
        self.rescale = tf.Variable(np.zeros((batch_size,1), dtype=np.float32), name='qq_phrase_lengths')

        # Initially we bound the l_infty norm by 2000, increase this
        # constant if it's not big enough of a distortion for your dataset.
        self.apply_delta = tf.clip_by_value(delta, -2000, 2000)*self.rescale

        # We set the new input to the model to be the abve delta
        # plus a mask, which allows us to enforce that certain
        # values remain constant 0 for length padding sequences.
        self.new_input = new_input = self.apply_delta*mask + original

        # We add a tiny bit of noise to help make sure that we can
        # clip our values to 16-bit integers and not break things.
        noise = tf.random_normal(new_input.shape,
                                 stddev=2)
        pass_in = tf.clip_by_value(new_input+noise, -2**15, 2**15-1)

        # Feed this final value to get the logits.
        self.logits = logits = get_logits(pass_in, lengths)

        # And finally restore the graph to make the classifier
        # actually do something interesting.
        saver = tf.train.Saver([x for x in tf.global_variables() if 'qq' not in x.name])
        saver.restore(sess, restore_path)

        # Choose the loss function we want -- either CTC or CW
        self.loss_fn = loss_fn
        if loss_fn == "CTC":
            target = ctc_label_dense_to_sparse(self.target_phrase, self.target_phrase_lengths)

            ctcloss = tf.nn.ctc_loss(labels=tf.cast(target, tf.int32),
                                     inputs=logits, sequence_length=lengths)

            # Slight hack: an infinite l2 penalty means that we don't penalize l2 distortion
            # The code runs faster at a slight cost of distortion, and also leaves one less
            # paramaeter that requires tuning.
            if not np.isinf(l2penalty):
                loss = tf.reduce_mean((self.new_input-self.original)**2,axis=1) + l2penalty*ctcloss
            else:
                loss = ctcloss
            self.expanded_loss = tf.constant(0)

        elif loss_fn == "CW":
            raise NotImplemented("The current version of this project does not include the CW loss function implementation.")
        else:
            raise NotImplemented

        self.loss = loss
        self.ctcloss = ctcloss

        # Set up the Adam optimizer to perform gradient descent for us
        start_vars = set(x.name for x in tf.global_variables())
        optimizer = tf.train.AdamOptimizer(learning_rate)

        grad,var = optimizer.compute_gradients(self.loss, [delta])[0]
        self.train = optimizer.apply_gradients([(tf.sign(grad),var)])

        end_vars = tf.global_variables()
        new_vars = [x for x in end_vars if x.name not in start_vars]

        sess.run(tf.variables_initializer(new_vars+[delta]))

        # Convert logits to probs for CTC decoder using softmax
        self.probs = tf.squeeze(tf.nn.softmax(self.logits, name='logits'))

        # Initialize scorer for CTC decoder
        if FLAGS.scorer_path:
            self.scorer = Scorer(FLAGS.lm_alpha, FLAGS.lm_beta,
                            FLAGS.scorer_path, Config.alphabet)
        else:
            self.scorer = None
        print("Initialization done.\n")

    def attack(self, audio, lengths, target, toks, finetune=None):
        print("Start attack..\n")
        sess = self.sess

        # Initialize all of the variables
        sess.run(tf.variables_initializer([self.delta]))
        sess.run(self.original.assign(np.array(audio)))
        sess.run(self.lengths.assign((np.array(lengths)-(2*Config.audio_step_samples/3))//320))
        sess.run(self.mask.assign(np.array([[1 if i < l else 0 for i in range(self.max_audio_len)] for l in lengths])))
        sess.run(self.cwmask.assign(np.array([[1 if i < l else 0 for i in range(self.phrase_length)] for l in (np.array(lengths)-1)//320])))
        sess.run(self.target_phrase_lengths.assign(np.array([len(x) for x in target])))
        sess.run(self.target_phrase.assign(np.array([list(t)+[0]*(self.phrase_length-len(t)) for t in target])))
        c = np.ones((self.batch_size, self.phrase_length))
        sess.run(self.importance.assign(c))
        sess.run(self.rescale.assign(np.ones((self.batch_size,1))))

        # Here we'll keep track of the best solution we've found so far
        final_deltas = [None]*self.batch_size

        if finetune is not None and len(finetune) > 0:
            sess.run(self.delta.assign(finetune-audio))

        # We'll make a bunch of iterations of gradient descent here
        #now = time.time()
        MAX = self.num_iterations
        first_hits = np.zeros((self.batch_size,))
        best_hits = np.zeros((self.batch_size,))
        for i in range(MAX):
            # Print out some debug information every 10 iterations.
            if i%10 == 0:
                new, delta, probs_out, r_logits = sess.run((self.new_input, self.delta, self.probs, self.logits))

                lst = [(probs_out, r_logits)]
                if self.mp3:
                    # TODO: Implement mp3 support
                    raise NotImplemented("The current version does not support mp3 conversion.")
                    mp3ed = convert_mp3(new, lengths)
                    mp3_probs, mp3_logits = sess.run((self.probs, self.logits),
                                                   {self.new_input: mp3ed})
                    mp3_out = ctc_beam_search_decoder(mp3_probs, Config.alphabet, FLAGS.beam_width,
                                                scorer=self.scorer, cutoff_prob=FLAGS.cutoff_prob,
                                                cutoff_top_n=FLAGS.cutoff_top_n)
                    lst.append((mp3_out, mp3_logits))

                batch_size = r_logits.shape[1]
                for out, logits in lst:
                    out_list = []
                    for ii in range(batch_size):
                        if batch_size == 1:
                            probs = probs_out
                        else:
                            probs = probs_out[:,ii,:]
                        decoded = ctc_beam_search_decoder(probs, Config.alphabet, FLAGS.beam_width,
                                                        scorer=self.scorer, cutoff_prob=FLAGS.cutoff_prob,
                                                        cutoff_top_n=FLAGS.cutoff_top_n)
                        # Here we print the strings that are recognized.
                        print(decoded[0][1])
                        out_list.append(decoded)
                    # And here we print the argmax of the alignment.
                    res2 = np.argmax(logits,axis=2).T
                    res2 = ["".join(toks[int(x)] for x in y[:(l-int(2*Config.audio_step_samples/3))//320]) for y,l in zip(res2,lengths)]
                    print("\n".join(res2))

            if self.mp3:
                new = sess.run(self.new_input)
                mp3ed = convert_mp3(new, lengths)
                feed_dict = {self.new_input: mp3ed}
            else:
                feed_dict = {}

            # Actually do the optimization step
            d, el, cl, l, logits, new_input, _ = sess.run((self.delta, self.expanded_loss,
                                                           self.ctcloss, self.loss,
                                                           self.logits, self.new_input,
                                                           self.train),
                                                          feed_dict)

            # Report progress
            print("%.3f"%np.mean(cl), "\t", "\t".join("%.3f"%x for x in cl))

            logits = np.argmax(logits,axis=2).T
            for ii in range(self.batch_size):
                # Every 100 iterations, check if we've succeeded
                # if we have (or if it's the final epoch) then we
                # should record our progress and decrease the
                # rescale constant.
                if (self.loss_fn == "CTC" and i%10 == 0 and out_list[ii][0][1] == "".join([toks[x] for x in target[ii]])) \
                   or (i == MAX-1 and final_deltas[ii] is None):
                    # Get the current constant
                    rescale = sess.run(self.rescale)
                    if rescale[ii]*2000 > np.max(np.abs(d)):
                        # If we're already below the threshold, then
                        # just reduce the threshold to the current
                        # point and save some time.
                        print("It's way over", np.max(np.abs(d[ii]))/2000.0)
                        rescale[ii] = np.max(np.abs(d[ii]))/2000.0

                    # Otherwise reduce it by some constant. The closer
                    # this number is to 1, the better quality the result
                    # will be. The smaller, the quicker we'll converge
                    # on a result but it will be lower quality.
                    rescale[ii] *= .8

                    # Adjust the best solution found so far
                    final_deltas[ii] = new_input[ii]

                    print("Worked i=%d ctcloss=%f bound=%f"%(ii, cl[ii], 2000*rescale[ii][0]))

                    if (first_hits[ii] == 0):
                        print("First hit for audio {} at iteration {}".format(ii, i))
                        first_hits[ii]=i
                    else:
                        best_hits[ii]=i

                    sess.run(self.rescale.assign(rescale))

                    # Just for debugging, save the adversarial example
                    # to /tmp so we can see it if we want
                    wav.write("tmp/adv.wav", 16000,
                              np.array(np.clip(np.round(new_input[ii]),
                                               -2**15, 2**15-1),dtype=np.int16))

        return final_deltas, first_hits, best_hits

def main(_):
    initialize_globals()
    # These are the tokens that we're allowed to use.
    # The - token is special and corresponds to the epsilon
    # value in CTC decoding, and can not occur in the phrase.
    toks = " abcdefghijklmnopqrstuvwxyz'-"

    with tf.Session() as sess:
        finetune = []
        audios = []
        lengths = []
        names = []
        source_dBs = []
        distortions = []
        high_pertub_bounds = []
        low_pertub_bounds = []

        if FLAGS.output is None:
            assert FLAGS.outprefix is not None
        else:
            assert FLAGS.outprefix is None
            assert len(FLAGS.input) == len(FLAGS.output)
        if FLAGS.finetune is not None and len(FLAGS.finetune):
            assert len(FLAGS.input) == len(FLAGS.finetune)

        # Load the inputs that we're given
        # TODO: [FINDBUG] loading multiple inputs is possible,
        #       but there are some weird things going on at the end of every transcription
        for i in range(len(FLAGS.input)):
            fs, audio = wav.read(FLAGS.input[i])
            names.append(FLAGS.input[i])
            assert fs == 16000
            assert audio.dtype == np.int16
            if (audio.shape[-1] == 2):
                audio = np.squeeze(audio[:,1])
                print(audio.shape)
            source_dB = 20 * np.log10(np.max(np.abs(audio)))
            print('source dB', source_dB)
            source_dBs.append(source_dB)
            audios.append(list(audio))
            lengths.append(len(audio))

            if FLAGS.finetune is not None:
                finetune.append(list(wav.read(FLAGS.finetune[i])[1]))

        maxlen = max(map(len,audios))
        audios = np.array([x+[0]*(maxlen-len(x)) for x in audios])
        finetune = np.array([x+[0]*(maxlen-len(x)) for x in finetune])

        phrase = FLAGS.target
        print("\nAttack phrase: ", phrase)

        attack = Attack(sess, 'CTC', len(phrase), maxlen,
                        batch_size=len(audios),
                        mp3=FLAGS.mp3,
                        learning_rate=FLAGS.lr,
                        num_iterations=FLAGS.iterations,
                        l2penalty=FLAGS.l2penalty,
                        restore_path=FLAGS.restore_path)

        start_time = time.time()
        deltas, first_hits, best_hits = attack.attack(audios,
                               lengths,
                               [[toks.index(x) for x in phrase]]*len(audios),
                               toks,
                               finetune)
        runtime = time.time() - start_time

        print("Finished in {}s.".format(runtime))
        # And now save it to the desired output
        if FLAGS.mp3:
            convert_mp3(deltas, lengths)
            copyfile("/tmp/saved.mp3", FLAGS.output[0])
            print("Final distortion", np.max(np.abs(deltas[0][:lengths[0]]-audios[0][:lengths[0]])))
        else:
            for i in range(len(FLAGS.input)):
                if FLAGS.output is not None:
                    path = FLAGS.output[i]
                else:
                    path = FLAGS.outprefix+str(i)+".wav"
                wav.write(path, 16000,
                          np.array(np.clip(np.round(deltas[i][:lengths[i]]),
                                           -2**15, 2**15-1),dtype=np.int16))

                # Define metrics for evaluation
                diff = deltas[i][:lengths[i]]-audios[i][:lengths[i]]
                high_pertub_bound = np.max(np.abs(diff))
                low_pertub_bound = np.min(np.abs(diff[diff!=0]))
                distortion = 20 * np.log10(np.max(np.abs(diff))) - source_dBs[i]
                high_pertub_bounds.append(high_pertub_bound)
                low_pertub_bounds.append(low_pertub_bound)
                distortions.append(distortion)
                print("Final noise loudness: ", distortion)

    # Create data_dict to store values for csv file
    data_dict = {
        'filename': names,
        'length' : lengths,
        'attack_runtime': [runtime]*len(names),
        'source_dB': source_dBs,
        'noise_loudness': distortions,
        'high_pertubation_bound' : high_pertub_bounds,
        'low_pertubation_bound' : low_pertub_bounds,
        'first_hit' : first_hits,
        'best_hit' : best_hits
    }
    df = pd.DataFrame(data_dict, columns=['filename', 'length', 'attack_runtime', 'source_dB', 'noise_loudness', 'high_pertubation_bound', 'low_pertubation_bound', 'first_hit', 'best_hit'])
    csv_filename = "tmp/attack-{}.csv".format(FLAGS.lang, time.strftime("%Y%m%d-%H%M%S"))
    df.to_csv(csv_filename, index=False, header=True)

def run_script():
    create_flags()
    absl.app.run(main)

if __name__ == "__main__":
    run_script()


import numpy as np
from cosmic import _evolvebin

def evolve(mass1, mass2, orbital_period, ecc,
           v_kick1, theta_kick1, phi_kick1,
           v_kick2, theta_kick2, phi_kick2,
           tphysf, metallicity,
           verbose=False,
           xi=1.0, bhflag=1, neta=0.5, windflag=3, wdflag=1, alpha1=1.0,
           pts1=0.001, pts3=0.02, pts2=0.01, epsnov=0.001,
           hewind=0.5, ck=-1000, bwind=0.0, lambdaf=0.5, mxns=2.5,
           beta=0.125, tflag=1, acc2=1.5, nsflag=3, ceflag=0, eddfac=1.0,
           ifflag=0, bconst=-3000, sigma=265.0, gamma=-1.0, pisn=45.0,
           natal_kick_array=[-100.0,-100.0,-100.0,-100.0,-100.0,-100.0],
           bhsigmafrac=1.0, polar_kick_angle=90,
           qcrit_array=[0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],
           cekickflag=2, cehestarflag=0, cemergeflag=0,
           ecsn=2.5, ecsn_mlow=1.4, aic=1, ussn=0,
           sigmadiv=-20.0, qcflag=2, eddlimflag=0,
           idum1=None,
           fprimc_array=[2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0,2.0/21.0],
           bhspinflag=0, bhspinmag=0.0, rejuv_fac=1.0, rejuvflag=0,
           htpmb=1, ST_cr=1, ST_tide=0, bdecayfac=1):

    natal_kick_array = [v_kick1, v_kick2, theta_kick1, theta_kick2, phi_kick1, phi_kick2]

    if idum1 is None:
        idum1 = -np.random.randint(np.iinfo(np.int32).min, np.iinfo(np.int32).max, size=1)

    # Set input variables
    _evolvebin.windvars.neta = neta
    _evolvebin.windvars.bwind = bwind
    _evolvebin.windvars.hewind = hewind
    _evolvebin.cevars.alpha1 = alpha1
    _evolvebin.cevars.lambdaf = lambdaf
    _evolvebin.ceflags.ceflag = ceflag
    _evolvebin.flags.tflag = tflag
    _evolvebin.flags.ifflag = ifflag
    _evolvebin.flags.wdflag = wdflag
    _evolvebin.snvars.pisn = pisn
    _evolvebin.flags.bhflag = bhflag
    _evolvebin.flags.nsflag = nsflag
    _evolvebin.ceflags.cekickflag = cekickflag
    _evolvebin.ceflags.cemergeflag = cemergeflag
    _evolvebin.ceflags.cehestarflag = cehestarflag
    _evolvebin.windvars.mxns = mxns
    _evolvebin.points.pts1 = pts1
    _evolvebin.points.pts2 = pts2
    _evolvebin.points.pts3 = pts3
    _evolvebin.snvars.ecsn = ecsn
    _evolvebin.snvars.ecsn_mlow = ecsn_mlow
    _evolvebin.flags.aic = aic
    _evolvebin.ceflags.ussn = ussn
    _evolvebin.snvars.sigma = sigma
    _evolvebin.snvars.sigmadiv = sigmadiv
    _evolvebin.snvars.bhsigmafrac = bhsigmafrac
    _evolvebin.snvars.polar_kick_angle = polar_kick_angle
    _evolvebin.snvars.natal_kick_array = natal_kick_array
    _evolvebin.cevars.qcrit_array = qcrit_array
    _evolvebin.windvars.beta = beta
    _evolvebin.windvars.xi = xi
    _evolvebin.windvars.acc2 = acc2
    _evolvebin.windvars.epsnov = epsnov
    _evolvebin.windvars.eddfac = eddfac
    _evolvebin.windvars.gamma = gamma
    _evolvebin.flags.bdecayfac = bdecayfac
    _evolvebin.magvars.bconst = bconst
    _evolvebin.magvars.ck = ck
    _evolvebin.flags.windflag = windflag
    _evolvebin.flags.qcflag = qcflag
    _evolvebin.windvars.eddlimflag = eddlimflag
    _evolvebin.tidalvars.fprimc_array = fprimc_array
    _evolvebin.rand1.idum1 = idum1
    _evolvebin.flags.bhspinflag = bhspinflag
    _evolvebin.snvars.bhspinmag = bhspinmag
    _evolvebin.mixvars.rejuv_fac = rejuv_fac
    _evolvebin.flags.rejuvflag = rejuvflag
    _evolvebin.flags.htpmb = htpmb
    _evolvebin.flags.st_cr = ST_cr
    _evolvebin.flags.st_tide = ST_tide
    _evolvebin.cmcpass.using_cmc = 0

# 0 k1,
# 1 k2,
# 2 m1,
# 3 m2,
# 4 porb,
# 5 ecc,
# 6 metallicity,
# 7 tphysf,
# 8 mass0_1,
# 9 mass0_2,
# 10 rad1,
# 11 rad2,
# 12 lumin1,
# 13 lumin2,
# 14 massc1,
# 15 massc2,
# 16 radc1,
# 17 radc2,
# 18 menv1,
# 19 menv2,
# 20 renv1,
# 21 renv2,
# 22 ospin1,
# 23 ospin2,
# 24 b_0_1,
# 25 b_0_2,
# 26 bacc1,
# 27 bacc2,
# 28 tacc1,
# 29 tacc2,
# 30 epoch1,
# 31 epoch2,
# 32 tms1,
# 33 tms2,
# 34 bhspin1,
# 35 bhspin2,
# 36 tphys,
# 37 binfrac

    print(_evolvebin.windvars.neta,
            _evolvebin.windvars.bwind,
            _evolvebin.windvars.hewind,
            _evolvebin.cevars.alpha1,
            _evolvebin.cevars.lambdaf,
            _evolvebin.ceflags.ceflag,
            _evolvebin.flags.tflag,
            _evolvebin.flags.ifflag,
            _evolvebin.flags.wdflag,
            _evolvebin.snvars.pisn,
            _evolvebin.flags.bhflag,
            _evolvebin.flags.nsflag,
            _evolvebin.ceflags.cekickflag,
            _evolvebin.ceflags.cemergeflag,
            _evolvebin.ceflags.cehestarflag,
            _evolvebin.windvars.mxns,
            _evolvebin.points.pts1,
            _evolvebin.points.pts2,
            _evolvebin.points.pts3,
            _evolvebin.snvars.ecsn,
            _evolvebin.snvars.ecsn_mlow,
            _evolvebin.flags.aic,
            _evolvebin.ceflags.ussn,
            _evolvebin.snvars.sigma,
            _evolvebin.snvars.sigmadiv,
            _evolvebin.snvars.bhsigmafrac,
            _evolvebin.snvars.polar_kick_angle,
            _evolvebin.snvars.natal_kick_array,
            _evolvebin.cevars.qcrit_array,
            _evolvebin.windvars.beta,
            _evolvebin.windvars.xi,
            _evolvebin.windvars.acc2,
            _evolvebin.windvars.epsnov,
            _evolvebin.windvars.eddfac,
            _evolvebin.windvars.gamma,
            _evolvebin.flags.bdecayfac,
            _evolvebin.magvars.bconst,
            _evolvebin.magvars.ck,
            _evolvebin.flags.windflag,
            _evolvebin.flags.qcflag,
            _evolvebin.windvars.eddlimflag,
            _evolvebin.tidalvars.fprimc_array,
            _evolvebin.rand1.idum1,
            _evolvebin.flags.bhspinflag,
            _evolvebin.snvars.bhspinmag,
            _evolvebin.mixvars.rejuv_fac,
            _evolvebin.flags.rejuvflag,
            _evolvebin.flags.htpmb,
            _evolvebin.flags.st_cr,
            _evolvebin.flags.st_tide,
            _evolvebin.cmcpass.using_cmc
            )

    [bpp, bcm] = _evolvebin.evolv2([1, 1],
                                   [mass1, mass2],
                                   orbital_period,
                                   ecc,
                                   metallicity,
                                   tphysf,
                                   tphysf,  # dtp,
                                   [mass1, mass2],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   [0, 0],
                                   # [mass0_1, mass0_2],
                                   # [rad1, rad2],
                                   # [lumin1, lumin2],
                                   # [massc1, massc2],
                                   # [radc1, radc2],
                                   # [menv1, menv2],
                                   # [renv1, renv2],
                                   # [ospin1, ospin2],
                                   # [b_0_1, b_0_2],
                                   # [bacc1, bacc2],
                                   # [tacc1, tacc2],
                                   # [epoch1, epoch2],
                                   # [tms1, tms2],
                                   # [bhspin1, bhspin2],
                                   0.0,
                                   np.zeros(20),
                                   np.zeros(20))

    print([1, 1],
           [mass1, mass2],
           orbital_period,
           ecc,
           metallicity,
           tphysf,
           tphysf,  # dtp,
           [mass1, mass2],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           [0, 0],
           # [mass0_1, mass0_2],
           # [rad1, rad2],
           # [lumin1, lumin2],
           # [massc1, massc2],
           # [radc1, radc2],
           # [menv1, menv2],
           # [renv1, renv2],
           # [ospin1, ospin2],
           # [b_0_1, b_0_2],
           # [bacc1, bacc2],
           # [tacc1, tacc2],
           # [epoch1, epoch2],
           # [tms1, tms2],
           # [bhspin1, bhspin2],
           0.0,
           np.zeros(20),
           np.zeros(20))

    return bcm, bpp


from tensorflow.keras.models import load_model
from time import sleep
from keras.preprocessing.image import img_to_array
from keras.preprocessing import image
import cv2
import numpy as np
import os
from mtcnn import MTCNN

# Importing the MTCNN detector to detect faces
detector = MTCNN()

# Path to the emotion detection model
model_path = os.path.join("model","accuracy_80.h5")
classifier =load_model(model_path)

class_labels = ['Angry','Happy','Neutral','Sad','Surprise'] # Remember to keep in alphabetical order

def face_emot_detect(vid_path,filename,output_path):
    """
    Take video path and find emotion and tag in video
    :param vid_path: complete path of input video
    :param filename: name of the video file
    :result: bool, dictionary of detected emotions
    """
    label_info = [0,0,0,0,0]
    cap = cv2.VideoCapture(vid_path)
    if cap.isOpened() == False:
        print('No video found')
        return False
    # for saving
    fourcc = cv2.VideoWriter_fourcc(*'DIVX')
    # output file name, fourcc code, frame/sec, size tuple
    if output_path == '':
        out = cv2.VideoWriter(filename, fourcc, int(cap.get(5)), (int(cap.get(3)),int(cap.get(4))))
    else:
        out = cv2.VideoWriter(output_path+'//'+filename, fourcc, int(cap.get(5)), (int(cap.get(3)),int(cap.get(4))))
    while(True):
    # Read one frame at a time
        ret, frame = cap.read()
        labels = []
        # If a frame is returned
        if ret == True:
            # Get a dictionary of all faces
            gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
            faces = detector.detect_faces(frame)
            # For every face in the faces detected in the current frame
            for face in faces:
                # Get the confidence value of the 'f' being a face
                if face.get('confidence')>=0.9:
                    # Get the co-ordinates of the cropped area wherein face lies
                    x,y,w,h = face.get('box')
                    # Draw a Rectangle
                    frame = cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,255),2)
                    roi_gray = gray[y:y+h,x:x+w]
                    try:
                        roi_gray = cv2.resize(roi_gray,(48,48),interpolation=cv2.INTER_AREA)
                    except:
                        print(f"error in {filename}")

                    if np.sum([roi_gray])!=0:
                        roi = roi_gray.astype('float')/255.0
                        roi = img_to_array(roi)
                        roi = np.expand_dims(roi,axis=0)
                    # make a prediction on the ROI, then lookup the class
                        preds = classifier.predict(roi)[0]
                        label_info[preds.argmax()] += 1
                        label = class_labels[preds.argmax()]
                        label_position = (x,y-5)
                        cv2.putText(frame,label,label_position,cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)
                    else:
                        cv2.putText(frame,'No Face Found',(20,60),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)
            out.write(frame)
        else:
            break
    # Freeing all resources
    out.release()
    cap.release()
    return dict(zip(class_labels,label_info))