import cv2 as cv
import numpy as np
import tracking
import json
import img_proc as proc
from Lego import Lego

if __name__ == '__main__':

    scale_percent = 100
    MIN_DISTANCE = 50
    LEGO_UNIT_SIZE = 17

    cap = cv.VideoCapture("video_tracking_dataset/video_tracking_04.avi")
    ret, frame1 = cap.read()
    dim = proc.calc_resize(frame1, scale_percent)
    min_roi = int(dim[1]/3)
    max_roi = int(dim[1]*5/6)

    # read file
    with open('colors_video_tracking.json') as json_file:
        data = json_file.read()
    # parse file
    obj = json.loads(data)

    legos_dict = {}
    while cap.isOpened():
        ret, frame = cap.read()
        lego_lst = []
        if ret:
            #print("--------- NEW FRAME -----------")
            all_masks = np.zeros((dim[1], dim[0]), dtype=np.uint8)
            frame_resized = cv.resize(frame, (dim), interpolation=cv.INTER_AREA)
            cv.imshow('frame resized', frame_resized)

            for color in obj['colors']:
                color_name = color['color']
                hsv_low = np.array(color['hsv_low'], np.uint8)
                hsv_upper = np.array(color['hsv_upper'], np.uint8)
                frame_bw = proc.apply_mask(frame_resized, hsv_low, hsv_upper)
                all_masks = cv.bitwise_or(all_masks, frame_bw)
                lego_color_lst = tracking.find_legos(frame_bw, color_name)
                lego_lst.extend(lego_color_lst)

            cv.imshow('all masks', all_masks)

            for lego in lego_lst:
                if lego.ref_point[1] > 15:
                    tracking.update_dict(legos_dict, lego, MIN_DISTANCE)
                    key = tracking.get_key(legos_dict, lego, MIN_DISTANCE)
                    legos_dict[key].append(lego)

            if legos_dict:
                print("dict content:")
                for key, value in legos_dict.items():
                    lego = value[2]
                    if min_roi < lego.ref_point[1] < max_roi and lego.rect is True:
                        Lego.compute_ratio(lego, LEGO_UNIT_SIZE, dim[0], dim[1])
                    print("{} : {}" .format(key, str(lego)))
                    lego.draw_info(frame_resized, key)

                cv.imshow('draw info', frame_resized)

        else:
            print("Can't receive frame (stream end?). Exiting ...")
            break

        k = cv.waitKey(1)
        if k == ord('q'):
            break

    cap.release()
    cv.destroyAllWindows()


from rest_framework.response import Response
from . serializers import CondominiumSerializer, GroupNoticesSerializer, ApartmentSerializer
from . models import Condominium, GroupNotices,  PrivNotices, Apartment
from rest_framework import status, generics
from rest_framework.decorators import api_view
from django.contrib.auth.decorators import login_required
from rest_framework.permissions import IsAuthenticated

@api_view(['GET'])
def get_condominiums(request):
    user = request.user
    condominiums = Condominium.objects.filter(owner=user)
    serializer = CondominiumSerializer(condominiums, many = True)
    return Response(serializer.data)

@api_view(['GET'])
def get_condominiumid(request,pk):
    condominiums = Condominium.objects.get(pk=pk)
    serializer = CondominiumSerializer(condominiums)
    return Response(serializer.data)

@api_view(['POST'])
def create_condominium(request):
    serializer = CondominiumSerializer(data = request.data)
    if serializer.is_valid():
        serializer.save(owner = request.user)
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    print(serializer.errors)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['PUT'])
def edit_condominium(request,pk):
    condominium = Condominium.objects.get(pk = pk)
    if request.user == condominium.owner:
        serializer = CondominiumSerializer(condominium, data = request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)
    else:
        return Response(serializer.data, status=status.HTTP_401_UNAUTHORIZED)

@api_view(['DELETE'])
def delete_condominium(request, pk):
        condominium = Condominium.objects.get(pk = pk)
        if request.user == condominium.owner:
            condominium.delete()
            return Response(status = status.HTTP_204_NO_CONTENT)
        else:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

@api_view(['GET'])
def get_apartments(request,pk):
    condominium = Condominium.objects.get(pk = pk)
    if request.user == condominium.owner:
        apartments = Apartment.objects.filter(condominium = condominium)
        serializer = ApartmentSerializer(apartments, many = True)
        return Response(serializer.data)
    else:
        return Response(status=status.HTTP_401_UNAUTHORIZED)

@api_view(['GET'])
def get_apartment(request,pk):
    apartment = Apartment.objects.get(pk = pk)
    serializer = ApartmentSerializer(apartment)
    return Response(serializer.data)

@api_view(['POST'])
def create_apartment(request,pk):
    request.data['renters'] = request.data.get('renters', None)
    serializer = ApartmentSerializer(data = request.data)
    condominium = Condominium.objects.get(pk = pk)
    if serializer.is_valid():
        serializer.save(condominium = condominium)
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    else:
        print(serializer.errors)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['DELETE'])
def delete_apartment(request, pk):
        if request.user != None:
            apartment = Apartment.objects.filter(pk = pk)
            apartment.delete()
            return Response(status = status.HTTP_204_NO_CONTENT)
        else: print("ERROR")

@api_view(['PUT'])
def edit_apartment(request,pk):
    print(pk)
    apartment = Apartment.objects.get(pk = pk)
    serializer = ApartmentSerializer(apartment, data = request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_200_OK)
    else:
        return Response(serializer.data, status=status.HTTP_400_BAD_REQUEST)


import io
import os
import sys
import traceback
import datetime
from PyPDF2 import PdfFileWriter, PdfFileReader
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter

def retrieve_project_parameters():

    parameters = sys.argv

    parameters_number = parameters.index("-traces") if "-traces" in parameters else None
    if parameters_number is not None:
        parameters_number = parameters_number + 1
        traces = parameters[parameters_number]
    else:
        traces = ""

    parameters_number = parameters.index("-file") if "-file" in parameters else None
    if parameters_number is not None:
        parameters_number = parameters_number + 1
        file = parameters[parameters_number]
    else:
        file = ""

    parameters_number = parameters.index("-saveas") if "-saveas" in parameters else None
    if parameters_number is not None:
        parameters_number = parameters_number + 1
        saveas = parameters[parameters_number]
    else:
        saveas = file.lower().replace(".pdf", "_new.pdf")

    parameters_number = parameters.index("-text") if "-text" in parameters else None
    if parameters_number is not None:
        parameters_number = parameters_number + 1
        text = parameters[parameters_number]
    else:
        text = ""

    parameters_number = parameters.index("-size") if "-size" in parameters else None
    if parameters_number is not None:
        parameters_number = parameters_number + 1
        size = parameters[parameters_number]
    else:
        size = ""

    parameters_number = parameters.index("-position") if "-position" in parameters else None
    if parameters_number is not None:
        parameters_number = parameters_number + 1
        position = str(parameters[parameters_number]).split(",")
    else:
        position = str(",").split(",")

    return {
        "traces": traces,
        "file": file,
        "saveas": saveas,
        "text": text,
        "size": size,
        "position": position,
    }

def validate_project_parameters(parameters):

    traces = parameters["traces"]
    file = parameters["file"]
    saveas = parameters["saveas"]
    text = parameters["text"]
    size = parameters["size"]
    position = parameters["position"]

    if traces == "" or traces.upper() == "FALSE":
        traces = False
    elif traces.upper() == "TRUE":
        traces = True
    else:
        return "ERROR: Invalid traces parameter! Parameter = " + str(traces)

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "=== * Parameters retrieved start * ===")

    if os.path.isfile(file):
        if not file.upper().endswith(".PDF"):
            return "ERROR: The save as file is not PDF!"
    else:
        return "ERROR: The file was not found!"

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tFile = " + str(file))

    saveas_exists = os.path.isdir(os.path.dirname(os.path.abspath(saveas)))
    if saveas_exists:
        if not saveas.upper().endswith(".PDF"):
            return "ERROR: The save as file is not PDF!"
    else:
        return "ERROR: The save as folder directory was not found!"

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tSave as = " + str(saveas))

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tText = " + str(text))

    if size != "":
        if not size.isnumeric():
            return "ERROR: The specified size is not numeric!"

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tSize = " + str(size))

    if isinstance(position, list):
        position = [i.strip() for i in position]

        for i in position:
            if not i.isnumeric():
                return "ERROR: The specified position is invalid!"

        position = {"x": position[0], "y": position[1]}

    else:
        return "ERROR: The specified position is invalid!"

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tPosition = " + str(position))

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "=== * Parameters retrieved end * ===")

    return {
        "traces": traces,
        "file": file,
        "saveas": saveas,
        "text": text,
        "size": size,
        "position": position,
    }

def create_template(traces, pdf_packet, size, position, text):

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "=== * Create template start * ===")

    can = canvas.Canvas(pdf_packet, pagesize=letter)

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tCanvas generated...")

    if size is not "" and size.isnumeric():
        size = int(size)
        can.setFont('Courier', size=size)

        if traces is True:
            print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tText size changed...")

    width, height = letter
    x, y = int(position["x"]), int(position["y"])
    can.drawString(width - x, height - y, text)

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tText drawn...")

    can.showPage()
    can.save()

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tTemplate saved...")

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "=== * Create template end * ===")

    can = None
    return pdf_packet

def generate_pdf(traces, pdf_packet, file, saveas):

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "=== * Generate PDF start * ===")

    pdf_packet.seek(0)
    pdf_reader = PdfFileReader(pdf_packet, strict=False)
    pdf_writer = PdfFileWriter()

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tPDF objects declared...")

    pdf_existing = PdfFileReader(open(file, "rb"), strict=False)
    pdf_existing_pages = pdf_existing.numPages

    for i in range(pdf_existing_pages):
        page = pdf_existing.getPage(i)
        page.mergePage(pdf_reader.getPage(0))
        pdf_writer.addPage(page)

        if traces is True:
            print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + f"\tPage {str(i+1)} of {str(pdf_existing_pages)} handled...")

    with open(saveas, "wb") as pdf_new:
        pdf_writer.write(pdf_new)

    if traces is True:
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "\tOutput PDF generated...")
        print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ": " + "=== * Generate PDF end * ===")

    pdf_packet, pdf_reader, pdf_writer = None, None, None
    return pdf_packet

def execute_command(parameters):

    traces = parameters["traces"]
    file = parameters["file"]
    saveas = parameters["saveas"]
    text = parameters["text"]
    size = parameters["size"]
    position = parameters["position"]

    try:
        pdf_packet = io.BytesIO()
        pdf_packet = create_template(traces, pdf_packet, size, position, text)
        pdf_packet = generate_pdf(traces, pdf_packet, file, saveas)

    except:
        print(traceback.format_exc())
        return "ERROR: Unexpected issue!"

    return True

def main():

    parameters = retrieve_project_parameters()

    parameters = validate_project_parameters(parameters)
    if not isinstance(parameters, dict):
        print(str(parameters))
        return

    valid = execute_command(parameters)
    if not valid is True:
        print(str(valid))
        return

    print("SUCCESS")

if __name__ == "__main__":
    main()


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

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit
from matplotlib.backends.backend_pdf import PdfPages

pp = PdfPages("curvy4.pdf")
print("Setup Complete")

def func(x, a, b, c):
    return a * np.exp(-b * x) + c

xdata = np.linspace(0, 4, 50)
y = func(xdata, 2.5, 1.3, 0.5)
rng = np.random.default_rng()
y_noise = 0.2 * rng.normal(size=xdata.size)
ydata = y + y_noise
plt.plot(xdata, ydata, "bo", label="data points")
plt.plot(xdata, ydata, "y", label="data")

popt, pcov = curve_fit(func, xdata, ydata)
print(popt)
plt.plot(xdata, func(xdata, *popt), "r-", label="fit: a=%5.3f, b=%5.3f, c=%5.3f" % tuple(popt))

popt, pcov = curve_fit(func, xdata, ydata, bounds=(0, [3.0, 1.0, 0.5]))
print(popt)
plt.plot(xdata, func(xdata, *popt), "g--", label="fit: a=%5.3f, b=%5.3f, c=%5.3f" % tuple(popt))
plt.xlabel("x")
plt.ylabel("y")
plt.legend()
pp.savefig()

pp.close()


"""
The event module is intended to contain any handling of pygame events, and provide some interfaces
for working with events in general.

globals:
  KeyPressEvent         An event object which runs all registered objects when
                        a key is pressed.
  KeyReleaseEvent       An event object which runs all registered objects when
                        a key is released.
  KeyToggleEvent        An event object which runs all registered objects when
                        a key is pressed or released (toggled).

functions:
  update                Run through all events currently in the pygame event
                        queue and put them into the real event queue.

"""

from collections import deque
from heapq import merge

import pygame

from util import Event, RealEvent
from game import game

class _KeyEvent(Event):
    """
    A KeyEvent object can be directly registered to for any key (will be passed as first parameter),
    or a specific key can be registered to, which will not pass the key as a parameter.

    """

    def __getitem__(self, key):
        """Get Event for the specified key."""
        if key not in self.__events:
            # None exist, create new one
            self.__events[key] = Event()

        return self.__events[key]

    def __init__(self):
        self.__events = {}
        super().__init__()

# Information about these in the module docstring.
KeyPressEvent = _KeyEvent()
KeyReleaseEvent = _KeyEvent()
KeyToggleEvent = _KeyEvent()

class _KeyPress(RealEvent):
    """
    Event used to indicate a key was pressed.  Key Press/Release events are automatically
    generated when CurrentState does not match the next key state.

    """

    def __call__(self):
        """Run all relevant events."""
        KeyPressEvent[self.key]()
        KeyToggleEvent[self.key](True)

        KeyPressEvent(self.key)
        KeyToggleEvent(True)

    def __init__(self, key, time):
        self.key = key
        self.time = time
        self.invalid = False

class _KeyRelease(RealEvent):
    """
    Event used to indicate a key was released.  Key Press/Release events are automatically
    generated when CurrentState does not match the next key state.

    """

    def __call__(self):
        """Run all relevant events."""
        KeyReleaseEvent[self.key]()
        KeyToggleEvent[self.key](False)

        KeyReleaseEvent(self.key)
        KeyToggleEvent(False)

    def __init__(self, key, time):
        self.key = key
        self.time = time
        self.invalid = False

# Current Key State is not synced with the game's time.
# If you wish to know the current key state, you should
# register a function with KeyPressEvent and KeyReleaseEvent.
_CurrentState = []

def update(current_time):
    """
    Run through all events currently in the pygame event queue and put them
    into the real event queue.

    """

    pygame.event.pump()

    # Handle key press/release events
    global _CurrentState
    next_state = pygame.key.get_pressed()
    key_events = []
    for key, was_pressed in enumerate(_CurrentState):
        if next_state[key] and not was_pressed:
            key_events.append(_KeyPress(key, current_time))

        if was_pressed and not next_state[key]:
            key_events.append(_KeyRelease(key, current_time))
    game.real_events = deque(merge(game.real_events, key_events))
    _CurrentState = next_state


"""
EXECUTION: python script_apply_word_length_filter.py
           --directory <directory>
           [--threshold 20000]

PURPOSE: the script
         - takes all the dataset files in <directory>
         - for each dataset file, checks whether a document has a non-whitespace sequence of length > <threshold>
         - if so, it filters those documents and writes the rest to <directory>_FILTERED
"""
import os
from os.path import join, isdir, isfile
import json
import argparse

from os.path import abspath, dirname
import sys
BASE_DIR = abspath(dirname(dirname(dirname(abspath(__file__)))))
print(f">>> BASE_DIR: {BASE_DIR}")
sys.path.append(BASE_DIR)

def main(args):

    input_directory = join(BASE_DIR, args.directory)
    output_directory = join(BASE_DIR, f"{input_directory}_FILTERED")

    assert isdir(input_directory), f"ERROR! {input_directory} not found."

    input_files = [
        join(input_directory, elem)
        for elem in os.listdir(input_directory)
        if isfile(join(input_directory, elem)) and "_FILTERED.jsonl" not in elem
    ]

    print()
    print(f"> found {len(input_files)} files in {input_directory}")

    for i, input_file in enumerate(input_files):
        print(f"\n=== file #{i+1}: {input_file}")
        output_file = join(output_directory, input_file.split("/")[-1].replace(".jsonl", "_FILTERED.jsonl"))

        # read old data
        with open(input_file, "r", encoding="utf-8") as _file:
            data = [json.loads(line) for line in _file]
        print(f"> loaded {len(data)} documents from {input_file}")

        # new data: apply word length filter
        new_data = list()
        counter = {'kept': 0, 'removed': 0}
        for document in data:
            longest_word = max([len(elem) for elem in document["text"].split(" ")])
            if longest_word <= args.threshold:
                new_data.append(document)
                counter['kept'] += 1
            else:
                counter['removed'] += 1
        print(f"> applied filter with threshold = {args.threshold}")
        print(f"  kept    documents: {counter['kept']}")
        print(f"  removed documents: {counter['removed']}")

        # write new data if needed
        if counter['removed'] > 0:
            os.makedirs(output_directory, exist_ok=True)
            with open(output_file, "w", encoding="utf-8") as _file:
                for line in new_data:
                    _file.write(json.dumps(line, ensure_ascii=False) + "\n")
            print(f"> wrote {len(new_data)} documents to {output_file}")
        else:
            print(f"> no need to write a new file")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--directory", type=str, required=True)
    parser.add_argument("--threshold", type=int, default=20000)
    _args = parser.parse_args()

    main(_args)


import torchfile
import torch
import torch.nn as nn
import PIL
import cv2
import yaml
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import torch.utils.data as data

from torch.autograd import Variable
import os.path
from torch.utils.data import DataLoader
from torchvision import transforms as T

from torch.optim import lr_scheduler
import torch.nn.init as init
import time
import math
import torchvision.utils as vutils

from torchvision import models

from graphviz import Digraph

from imageio import imwrite

from pathlib import Path
from utils.html import HTML

from utils.pytorch_ssim import ssim
from utils.pytorch_lpips import util_of_lpips

import torch.utils.data.distributed as distributed

# normalization image to (0,1):
IMAGENET_MEAN_01 = [0.485, 0.456, 0.406]
IMAGENET_STD_01 = [0.229, 0.224, 0.225]

INV_IMAGENET_MEAN_01 = [-m for m in IMAGENET_MEAN_01]
INV_IMAGENET_STD_01 = [1.0 / s for s in IMAGENET_STD_01]

# normalization image to (-1,1):
IMAGENET_MEAN = [0.5, 0.5, 0.5]
IMAGENET_STD = [0.5, 0.5, 0.5]

INV_IMAGENET_MEAN = [-m for m in IMAGENET_MEAN]
INV_IMAGENET_STD = [1.0 / s for s in IMAGENET_STD]

# loader使用torchvision中自带的transforms函数
loader = T.Compose([T.ToTensor()])
unloader = T.ToPILImage()

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

# 1, PIL读取图片转化为Tensor: # 输入图片地址 # 返回tensor变量
def PIL_image_loader(image_path):
    image = Image.open(image_path).convert('RGB')
    image = loader(image).unsqueeze(0)
    return image.to(device, torch.float)

# 2, 将PIL图片转化为Tensor: # 输入PIL格式图片 # 返回tensor变量
def PIL_to_tensor(image):
    image = loader(image).unsqueeze(0)
    return image.to(device, torch.float)

# 3, Tensor转化为PIL图片: # 输入tensor变量 # 输出PIL格式图片
def tensor_to_PIL(tensor):
    image = tensor.cpu().clone()
    image = image.squeeze(0)
    image = unloader(image)
    return image

def get_all_data_loaders(conf):
    batch_size = conf['batch_size']
    num_workers = conf['num_workers']

    # change the size of dataset
    if 'new_size' in conf:
        new_size_a = new_size_b = conf['new_size']
    else:
        new_size_a = conf['new_size_a']
        new_size_b = conf['new_size_b']
    height = conf['crop_image_height']
    width = conf['crop_image_width']

    # further to get train and test dataset
    if 'data_root' in conf:
        train_loader_a = get_data_loader_folder(os.path.join(conf['data_root'], 'trainA'), batch_size, True,
                                              new_size_a, height, width, num_workers, True)
        test_loader_a = get_data_loader_folder(os.path.join(conf['data_root'], 'testA'), batch_size, False,
                                             new_size_a, new_size_a, new_size_a, num_workers, True)
        train_loader_b = get_data_loader_folder(os.path.join(conf['data_root'], 'trainB'), batch_size, True,
                                              new_size_b, height, width, num_workers, True)
        test_loader_b = get_data_loader_folder(os.path.join(conf['data_root'], 'testB'), batch_size, False,
                                             new_size_b, new_size_b, new_size_b, num_workers, True)

    return train_loader_a, train_loader_b, test_loader_a, test_loader_b

def get_data_loader_folder(input_folder, batch_size, train, new_size=None,
                           height=256, width=256, num_workers=4, crop=True):
    transform_list = [T.ToTensor(),
                      T.Normalize((0.5, 0.5, 0.5),
                                           (0.5, 0.5, 0.5))]
    transform_list = [T.RandomCrop((height, width))] + transform_list if crop else transform_list
    transform_list = [T.Resize(new_size)] + transform_list if new_size is not None else transform_list
    transform_list = [T.RandomHorizontalFlip()] + transform_list if train else transform_list
    transform = T.Compose(transform_list)
    dataset = ImageFolder(input_folder, transform=transform)
    loader = DataLoader(dataset=dataset, batch_size=batch_size, shuffle=train, drop_last=True, num_workers=num_workers)
    return loader

def get_config(config):
    with open(config, 'r') as stream:
        # YAML 5.1版本后弃用了yaml.load(file)这个用法，因为觉得很不安全，5.1版本之后就修改了需要指定Loader，
        # 通过默认加载器（FullLoader）禁止执行任意函数，该load函数也变得更加安全
        return yaml.load(stream, Loader=yaml.FullLoader)

def default_loader(path, image_size):
    img = Image.open(path)
    img1 = img.resize(image_size, Image.ANTIALIAS)
    img1 = img1.convert('RGB')
    return img1

IMG_EXTENSIONS = [
    '.jpg', '.JPG', '.jpeg', '.JPEG',
    '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP',
]

def is_image_file(filename):
    return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)

def make_dataset(dir):
    images = []
    assert os.path.isdir(dir), '%s is not a valid directory' % dir

    for root, _, fnames in sorted(os.walk(dir)):
        for fname in fnames:
            if is_image_file(fname):
                path = os.path.join(root, fname)
                images.append(path)

    return images

class ImageFolder(data.Dataset):

    def __init__(self, root, image_size, transform=None, return_paths=False,
                 loader=default_loader):
        imgs = sorted(make_dataset(root))
        if len(imgs) == 0:
            raise(RuntimeError("Found 0 images in: " + root + "\n"
                               "Supported image extensions are: " +
                               ",".join(IMG_EXTENSIONS)))

        self.root = root
        self.imgs = imgs
        self.image_size = image_size
        self.transform = transform
        self.return_paths = return_paths
        self.loader = loader

    def __getitem__(self, index):
        path = self.imgs[index]
        img = self.loader(path, self.image_size)
        if self.transform is not None:
            img = self.transform(img)
        if self.return_paths:
            return img, path
        else:
            return img

    def __len__(self):
        return len(self.imgs)

def imagenet_preprocess_01():
    return T.Normalize(mean=IMAGENET_MEAN_01, std=IMAGENET_STD_01)

def imagenet_preprocess():
    return T.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD)

def rescale(x):
    lo, hi = x.min(), x.max()
    return x.sub(lo).div(hi - lo)

def imagenet_deprocess_01(rescale_image=True):
    transforms = [
        T.Normalize(mean=[0, 0, 0], std=INV_IMAGENET_STD_01),
        T.Normalize(mean=INV_IMAGENET_MEAN_01, std=[1.0, 1.0, 1.0]),
    ]
    if rescale_image:
        transforms.append(rescale)
    return T.Compose(transforms)

def imagenet_deprocess_batch_01(imgs, rescale=True):
    """
    Input:
    - imgs: FloatTensor of shape (N, C, H, W) giving preprocessed images

    Output:
    - imgs_de: ByteTensor of shape (N, C, H, W) giving deprocessed images
      in the range [0, 255]
    """
    if isinstance(imgs, torch.autograd.Variable):
        imgs = imgs.data
    imgs = imgs.cpu().clone()
    deprocess_fn = imagenet_deprocess_01(rescale_image=rescale)
    imgs_de = []
    for i in range(imgs.size(0)):
        img_de = deprocess_fn(imgs[i])[None]
        img_de = img_de.mul(255).clamp(0, 255).byte()
        imgs_de.append(img_de)
    imgs_de = torch.cat(imgs_de, dim=0)
    return imgs_de

def imagenet_deprocess(rescale_image=True):
    transforms = [
        T.Normalize(mean=[0, 0, 0], std=INV_IMAGENET_STD),
        T.Normalize(mean=INV_IMAGENET_MEAN, std=[1.0, 1.0, 1.0]),
    ]
    if rescale_image:
        transforms.append(rescale)
    return T.Compose(transforms)

def imagenet_deprocess_batch(imgs, rescale=True):
    """
    Input:
    - imgs: FloatTensor of shape (N, C, H, W) giving preprocessed images

    Output:
    - imgs_de: ByteTensor of shape (N, C, H, W) giving deprocessed images
      in the range [0, 255]
    """
    if isinstance(imgs, torch.autograd.Variable):
        imgs = imgs.data
    imgs = imgs.cpu().clone()
    deprocess_fn = imagenet_deprocess(rescale_image=rescale)
    imgs_de = []
    for i in range(imgs.size(0)):
        img_de = deprocess_fn(imgs[i])[None]
        img_de = img_de.mul(255).clamp(0, 255).byte()
        imgs_de.append(img_de)
    imgs_de = torch.cat(imgs_de, dim=0)
    return imgs_de

def img_save(name, img, i, save_dir):
    # save normalized image format to normal image format:
    img = imagenet_deprocess_batch(img)
    img = img[0].numpy().transpose(1, 2, 0)
    img_path = os.path.join(save_dir, 'iter{}_{}.png'.format(i + 1, name))
    print("Generated image: {}".format(img_path))
    imwrite(img_path, img)

def visualise_feature_to_img(feature):
    # to numpy, numpy不能读取CUDA tensor 需要将它转化为 CPU tensor
    feature = feature.data.cpu().numpy()

    # 将tensor转为numpy，然后归一化到[0, 1]
    # use sigmod to [0,1],对于归一化到[0,1]的部分用了sigmod方法:
    feature = 1.0 / (1 + np.exp(-1 * feature))

    # to [0,255]，最后乘255，使得范围为[0, 255]
    image = np.round(feature * 255)

    # 得到灰度图像并保存
    # cv2.imwrite('../output/feature_visualise.jpg', image)
    return image

def show_single_feature(features, text):
    for i in range(features.size(0)):
        feature = features[i, :, :, :]
        feature = feature[0, :, :]  # only extract o dimension
        # visualize the feature map
        image = visualise_feature_to_img(feature)
        if i == 4:
            plt.imshow(image)
            plt.show()
            path = "../images/feature_" + text + "_" + str(i) + "_" + ".jpg"
            cv2.imwrite(path, image)
            break

def image_objects_show(imgs):
    for i in range(imgs.size(0)):
        img = imgs[i, :, :, :].unsqueeze(0)
        # show cropped image clip
        img = imagenet_deprocess_batch(img)
        img = img[0].numpy().transpose(1, 2, 0)
        img = Image.fromarray(img)
        plt.imshow(img)
        plt.show()
        # img.save("../images/image_" + text + "_" + str(i) + "_" + ".jpg")

def image_show(img, i, text):
    img = imagenet_deprocess_batch(img)
    img = img[0].numpy().transpose(1, 2, 0)
    img = Image.fromarray(img)
    plt.imshow(img)
    plt.show()
    # img.save("../images/image_" + text + "_" + str(i) + "_" + ".jpg")

def image_with_bbox_mask_show(img, box, mask, i):
    box = box * img.size(2)  # recover the box value from [0,1] to [0, 255]
    x0, y0, x1, y1 = box  # extract coordinate
    mask = mask.cpu().clone()  # convert cuda to cpu

    # convert img to numpy and then to CV data
    img = imagenet_deprocess_batch(img)
    img = img[0].numpy().transpose(1, 2, 0)
    img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)  # PIL转cv2

    # draw mask on image
    if i == 0:
        color_mask = np.array([255, 129, 0], dtype=np.uint8)  # orange
    elif i == 3:
        color_mask = np.array([84, 130, 53], dtype=np.uint8)  # pale green
    elif i == 4:
        color_mask = np.array([192, 0, 0], dtype=np.uint8)  # crimson
    else:
        color_mask = np.array([0, 255, 0], dtype=np.uint8)  # green
    mask = mask.numpy().astype(np.bool)
    img[mask] = img[mask] * 0.5 + color_mask * 0.5

    # draw bbox on image
    cv2.rectangle(img, (int(x0), int(y0)), (int(x1), int(y1)), (0, 255, 0), 2)  # red color

    # convert whole image to Image and show it
    if i == 4:
        img = Image.fromarray(img)
        plt.imshow(img)
        plt.show()
        img.save("../images/image_bbox_mask_" + str(i) + "_" + ".jpg")

def binary_image_with_bbox_mask_show(img, box, mask, i):
    box = box * img.size(2)  # recover the box value from [0,1] to [0, 255]
    x0, y0, x1, y1 = box  # extract coordinate
    mask = mask.cpu().clone()  # convert cuda to cpu

    # convert img to numpy and then to CV data
    img = imagenet_deprocess_batch(img)
    img = img[0].numpy().transpose(1, 2, 0)
    img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)  # PIL转cv2

    # # draw mask on image
    # if i == 0:
    #     color_mask = np.array([127, 81, 127], dtype=np.uint8)  # orange  road
    # elif i == 1:
    #     color_mask = np.array([113, 135, 63], dtype=np.uint8)  # pale green  building
    # elif i == 2:
    #     color_mask = np.array([113, 137, 63], dtype=np.uint8)  # pale green  tree
    # elif i == 3:
    #     color_mask = np.array([84, 130, 53], dtype=np.uint8)  # pale green sky
    # elif i == 4:
    #     color_mask = np.array([68, 125, 230], dtype=np.uint8)  # crimson car
    # else:
    #     color_mask = np.array([0, 255, 0], dtype=np.uint8)  # green
    # mask = mask.numpy().astype(np.bool)
    # img[mask] = img[mask] * 0.5 + color_mask * 0.5

    # creat a binary image same to raw img
    height = img.shape[0]
    weight = img.shape[1]
    for row in range(height):  # 遍历高
        for col in range(weight):  # 遍历宽
            if not mask[row, col]:  # False
                r = img[row, col, 0] = 0
                g = img[row, col, 1] = 0
                b = img[row, col, 2] = 0

    # draw bbox on image, -1表示的是填充矩形的意思
    cv2.rectangle(img, (int(x0), int(y0)), (int(x1), int(y1)), (127, 81, 127), -1)  # red color

    # convert whole image to Image and show it
    # if i == 0:
    img = Image.fromarray(img)
    plt.imshow(img)
    plt.show()
    img.save("../images/binary_image_bbox_mask_" + str(i) + "_" + ".jpg")

class Resize(object):
    def __init__(self, size, interp=PIL.Image.BILINEAR):
        if isinstance(size, tuple):
            H, W = size
            self.size = (W, H)
        else:
            self.size = (size, size)
        self.interp = interp

    def __call__(self, img):
        return img.resize(self.size, self.interp)

def unpack_var(v):
    if isinstance(v, torch.autograd.Variable):
        return v.data
    return v

def split_graph_batch(triples, obj_data, obj_to_img, triple_to_img):
    triples = unpack_var(triples)
    obj_data = [unpack_var(o) for o in obj_data]
    obj_to_img = unpack_var(obj_to_img)
    triple_to_img = unpack_var(triple_to_img)

    triples_out = []
    obj_data_out = [[] for _ in obj_data]
    obj_offset = 0
    N = obj_to_img.max() + 1
    for i in range(N):
        o_idxs = (obj_to_img == i).nonzero().view(-1)
        t_idxs = (triple_to_img == i).nonzero().view(-1)

        cur_triples = triples[t_idxs].clone()
        cur_triples[:, 0] -= obj_offset
        cur_triples[:, 2] -= obj_offset
        triples_out.append(cur_triples)

        for j, o_data in enumerate(obj_data):
            cur_o_data = None
            if o_data is not None:
                cur_o_data = o_data[o_idxs]
            obj_data_out[j].append(cur_o_data)

        obj_offset += o_idxs.size(0)

    return triples_out, obj_data_out

def write_one_row_html(html_file, iterations, img_filename, all_size, label):
    # html_file.write("
Iteration: %d  (%s)
" % (iterations, img_filename.split('/')[-1]))
    html_file.write("
Iteration: %d  (%s)
" % (iterations, label))
    html_file.write("""

        """ % (img_filename, img_filename, all_size))
    return

def write_html(filename, iterations, image_directory, all_size=512):
    html_file = open(filename, "w")
    html_file.write('''
    <!DOCTYPE html>

      <title>Experiment name = %s</title>
      <meta http-equiv="refresh" content="30">

    ''' % os.path.basename(filename))
    html_file.write("
Comparison
")
    write_one_row_html(html_file, iterations, '%s/img_t_%08d.png' % (image_directory, iterations), all_size, "Real_Thermal")
    write_one_row_html(html_file, iterations, '%s/img_t2v_%08d.png' % (image_directory, iterations), all_size, "Fake_Visible")
    write_one_row_html(html_file, iterations, '%s/img_v_%08d.png' % (image_directory, iterations), all_size, "Real_Visible")
    html_file.write("")
    html_file.close()

# Get model list for resume
def get_model_list(dirname, key):
    if os.path.exists(dirname) is False:
        return None
    gen_models = [os.path.join(dirname, f) for f in os.listdir(dirname) if
                  os.path.isfile(os.path.join(dirname, f)) and key in f and ".pt" in f]
    if gen_models is None:
        return None
    gen_models.sort()
    last_model_name = gen_models[-1]
    return last_model_name

# TV Loss
class TVLoss(nn.Module):
    def __init__(self, tv_loss_weight=1):
        super(TVLoss, self).__init__()
        self.tv_loss_weight = tv_loss_weight

    def forward(self, x):
        batch_size = x.size(0)
        h_x = x.size(2)
        w_x = x.size(3)
        count_h = self.tensor_size(x[:, :, 1:, :])
        count_w = self.tensor_size(x[:, :, :, 1:])
        h_tv = torch.pow((x[:, :, 1:, :] - x[:, :, :h_x - 1, :]), 2).sum()
        w_tv = torch.pow((x[:, :, :, 1:] - x[:, :, :, :w_x - 1]), 2).sum()
        return self.tv_loss_weight * 2 * (h_tv / count_h + w_tv / count_w) / batch_size

    @staticmethod
    def tensor_size(t):
        return t.size()[1] * t.size()[2] * t.size()[3]

# VGG Features matching
class Vgg19(torch.nn.Module):
    def __init__(self, requires_grad=False):
        super(Vgg19, self).__init__()
        vgg_pretrained_features = models.vgg19(pretrained=True).features
        self.slice1 = torch.nn.Sequential()
        self.slice2 = torch.nn.Sequential()
        self.slice3 = torch.nn.Sequential()
        self.slice4 = torch.nn.Sequential()
        self.slice5 = torch.nn.Sequential()
        for x in range(2):
            self.slice1.add_module(str(x), vgg_pretrained_features[x])
        for x in range(2, 7):
            self.slice2.add_module(str(x), vgg_pretrained_features[x])
        for x in range(7, 12):
            self.slice3.add_module(str(x), vgg_pretrained_features[x])
        for x in range(12, 21):
            self.slice4.add_module(str(x), vgg_pretrained_features[x])
        for x in range(21, 30):
            self.slice5.add_module(str(x), vgg_pretrained_features[x])
        if not requires_grad:
            for param in self.parameters():
                param.requires_grad = False

    def forward(self, X):
        h_relu1 = self.slice1(X)
        h_relu2 = self.slice2(h_relu1)
        h_relu3 = self.slice3(h_relu2)
        h_relu4 = self.slice4(h_relu3)
        h_relu5 = self.slice5(h_relu4)
        out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5]
        return out

class VGGLoss(nn.Module):
    def __init__(self):
        super(VGGLoss, self).__init__()
        self.vgg = Vgg19().cuda()
        self.criterion = nn.L1Loss()
        self.weights = [1.0 / 32, 1.0 / 16, 1.0 / 8, 1.0 / 4, 1.0]

    def forward(self, x, y):
        x_vgg, y_vgg = self.vgg(x), self.vgg(y)
        loss = 0
        for i in range(len(x_vgg)):
            loss += self.weights[i] * self.criterion(x_vgg[i], y_vgg[i].detach())
        return loss

def compute_vgg_loss(vgg, img, target):
    img_vgg = vgg_preprocess(img)
    target_vgg = vgg_preprocess(target)
    img_fea = vgg(img_vgg)
    target_fea = vgg(target_vgg)
    # 使用正则化的方式
    instancenorm = nn.InstanceNorm2d(512, affine=False)
    return torch.mean((instancenorm(img_fea) - instancenorm(target_fea)) ** 2)

def vgg_preprocess(batch):
    tensortype = type(batch.data)
    (r, g, b) = torch.chunk(batch, 3, dim = 1)
    batch = torch.cat((b, g, r), dim = 1) # convert RGB to BGR
    batch = (batch + 1) * 255 * 0.5 # [-1, 1] -> [0, 255]
    mean = tensortype(batch.data.size()).cuda()
    mean[:, 0, :, :] = 103.939
    mean[:, 1, :, :] = 116.779
    mean[:, 2, :, :] = 123.680
    batch = batch.sub(Variable(mean))  # subtract mean
    return batch

class Vgg16(nn.Module):
    def __init__(self):
        super(Vgg16, self).__init__()
        features = models.vgg16(pretrained=True).features
        self.to_relu_1_2 = nn.Sequential()
        self.to_relu_2_2 = nn.Sequential()
        self.to_relu_3_3 = nn.Sequential()
        self.to_relu_4_3 = nn.Sequential()
        self.to_relu_5_3 = nn.Sequential()

        for x in range(4):
            self.to_relu_1_2.add_module(str(x), features[x])
        for x in range(4, 9):
            self.to_relu_2_2.add_module(str(x), features[x])
        for x in range(9, 16):
            self.to_relu_3_3.add_module(str(x), features[x])
        for x in range(16, 23):
            self.to_relu_4_3.add_module(str(x), features[x])
        for x in range(23, 30):
            self.to_relu_5_3.add_module(str(x), features[x])

        # don't need the gradients, just want the features
        for param in self.parameters():
            param.requires_grad = False

    def forward(self, x):
        h = self.to_relu_1_2(x)
        h_relu_1_2 = h
        h = self.to_relu_2_2(h)
        h_relu_2_2 = h
        h = self.to_relu_3_3(h)
        h_relu_3_3 = h
        h = self.to_relu_4_3(h)
        h_relu_4_3 = h
        h = self.to_relu_5_3(h)
        h_relu_5_3 = h
        out = (h_relu_1_2, h_relu_2_2, h_relu_3_3, h_relu_4_3, h_relu_5_3)
        return out

def get_scheduler(optimizer, config, iterations=-1):
    if config.lr_policy == 'step':
        scheduler = lr_scheduler.StepLR(optimizer, step_size=config.step_size, gamma=config.gamma, last_epoch=iterations)
    else:
        scheduler = None  # constant scheduler
        return NotImplementedError('learning rate policy [%s] is not implemented', config.lr_policy)
    return scheduler

def weights_init(init_type='gaussian'):
    def init_fun(m):
        classname = m.__class__.__name__
        if (classname.find('Conv') == 0 or classname.find('Linear') == 0) and hasattr(m, 'weight'):
            # print m.__class__.__name__
            if init_type == 'gaussian':
                init.normal_(m.weight.data, 0.0, 0.02)
            elif init_type == 'xavier':
                init.xavier_normal_(m.weight.data, gain=math.sqrt(2))
            elif init_type == 'kaiming':
                init.kaiming_normal_(m.weight.data, a=0, mode='fan_in')
            elif init_type == 'orthogonal':
                init.orthogonal_(m.weight.data, gain=math.sqrt(2))
            elif init_type == 'default':
                pass
            else:
                assert 0, "Unsupported initialization: {}".format(init_type)
            if hasattr(m, 'bias') and m.bias is not None:
                init.constant_(m.bias.data, 0.0)

    return init_fun

class Timer:
    def __init__(self, msg):
        self.msg = msg
        self.start_time = None

    def __enter__(self):
        self.start_time = time.time()

    def __exit__(self, exc_type, exc_value, exc_tb):
        print(self.msg % (time.time() - self.start_time))

# 画pytorch模型图，以及参数计算
def make_dot(var, params=None):
    """ Produces Graphviz representation of PyTorch autograd graph
    Blue nodes are the Variables that require grad, orange are Tensors
    saved for backward in torch.autograd.Function
    Args:
        var: output Variable
        params: dict of (name, Variable) to add names to node that
            require grad (TODO: make optional)
    """
    if params is not None:
        assert isinstance(params.values()[0], Variable)
        param_map = {id(v): k for k, v in params.items()}

    node_attr = dict(style='filled',
                     shape='box',
                     align='left',
                     fontsize='12',
                     ranksep='0.1',
                     height='0.2')
    dot = Digraph(node_attr=node_attr, graph_attr=dict(size="12,12"))
    seen = set()

    def size_to_str(size):
        return '(' + (', ').join(['%d' % v for v in size]) + ')'

    def add_nodes(var):
        if var not in seen:
            if torch.is_tensor(var):
                dot.node(str(id(var)), size_to_str(var.size()), fillcolor='orange')
            elif hasattr(var, 'variable'):
                u = var.variable
                name = param_map[id(u)] if params is not None else ''
                node_name = '%s\n %s' % (name, size_to_str(u.size()))
                dot.node(str(id(var)), node_name, fillcolor='lightblue')
            else:
                dot.node(str(id(var)), str(type(var).__name__))
            seen.add(var)
            if hasattr(var, 'next_functions'):
                for u in var.next_functions:
                    if u[0] is not None:
                        dot.edge(str(id(u[0])), str(id(var)))
                        add_nodes(u[0])
            if hasattr(var, 'saved_tensors'):
                for t in var.saved_tensors:
                    dot.edge(str(id(t)), str(id(var)))
                    add_nodes(t)

    add_nodes(var.grad_fn)
    return dot

def image_save(name, img, save_dir):
    # save normalized image format to normal image format:
    img = imagenet_deprocess_batch(img)
    img = img[0].numpy().transpose(1, 2, 0)
    if not Path(save_dir).exists():
        Path(save_dir).mkdir(parents=True)
    img_path = os.path.join(save_dir, '{}.jpg'.format(name))
    print("Generated image: {}".format(img_path))
    imwrite(img_path, img)

def html_save(n, save_dir, config, fname_list):
    # save images to website
    img_path = os.path.join('/home/liyun/Desktop/Challenge1_experiment/OL-GANs/experiment4/results', 'images')
    html = HTML(save_dir, 'experiment')
    for i in range(n, 0, -config.image_step):
        html.add_header('[%d]' % i)
        ims = []
        ims.append(os.path.join(img_path, 'img_%08d_t_%s.jpg' % (i, fname_list[i-1])))
        ims.append(os.path.join(img_path, 'img_%08d_t2v_%s.jpg' % (i, fname_list[i-1])))
        ims.append(os.path.join(img_path, 'img_%08d_v_%s.jpg' % (i, fname_list[i-1])))
        texts = ['Thermal', 'Fake Visible', 'Visible']
        html.add_images(ims, texts)
    html.save()

def image_save_test(name, img, save_dir):
    # save normalized image format to normal image format:
    img = imagenet_deprocess_batch_01(img)
    img = img[0].numpy().transpose(1, 2, 0)
    if not Path(save_dir).exists():
        Path(save_dir).mkdir(parents=True)
    img_path = os.path.join(save_dir, '{}.png'.format(name))
    print("Generated image: {}".format(img_path))
    imwrite(img_path, img)

def html_save_test(n, save_dir, fname):
    # save images to website
    img_path = os.path.join('/home/liyun/Desktop/Challenge1_experiment/SF-GANs/experiment7/tests', 'images')
    html = HTML(save_dir, 'experiment')
    for n in range(n, 0, -1):
        html.add_header('[%d] %s' % (n, fname))
        ims = []
        ims.append(os.path.join(img_path, 'img%s_t.png' % fname))
        ims.append(os.path.join(img_path, 'img%s_t2v.png' % fname))
        ims.append(os.path.join(img_path, 'img%s_v.png' % fname))
        texts = ['Thermal', 'Fake Visible', 'Visible']
        html.add_images(ims, texts)
    html.save()

def validation_methods(sr, hr):
    batch_mse = ((sr - hr) ** 2).data.mean()
    batch_ssim = ssim(sr, hr).item()
    psnr = 10 * math.log10(1 / (batch_mse / sr.size(0)))

    return batch_mse, batch_ssim, psnr

def calculation_lpips(img1_path, img2_path):
    ## Initializing the model
    loss_fn = util_of_lpips(net='alex', use_gpu=True)
    # Compute distance
    lpips = loss_fn.calc_lpips(img1_path, img2_path)

    return lpips

def convert_normalize(tensor_image):
    # [-1, 1] to [0, 1]
    # step 1: convert it to [0 ,2]
    tensor_image = tensor_image + 1
    # step 2: convert it to [0 ,1]
    tensor_image = tensor_image - tensor_image.min()
    tensor_image_0_1 = tensor_image / (tensor_image.max() - tensor_image.min())

    return tensor_image_0_1

def convert_transform(tensor_image):
    # [-1, 1] to [0, 1]
    img = imagenet_deprocess_batch(tensor_image)
    img = img[0].numpy().transpose(1, 2, 0)
    img = Image.fromarray(img)
    # plt.imshow(img)
    # plt.show()
    image_size = (256, 256)
    transform = [Resize(image_size), T.ToTensor()]
    transform = T.Compose(transform)
    img = transform(img.convert('RGB'))

    return img

def image_show_normal(img):
    img = imagenet_deprocess_batch(img)
    img = img[0].numpy().transpose(1, 2, 0)
    img = Image.fromarray(img)
    plt.imshow(img)
    plt.show()

def image_show_test(img):
    img = imagenet_deprocess_batch_01(img)
    img = img[0].numpy().transpose(1, 2, 0)
    img = Image.fromarray(img)
    plt.imshow(img)
    plt.show()


from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.views.generic.list import ListView
from django.utils import timezone
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.core.urlresolvers import reverse_lazy
from .form import PacienteForm
from .models import Paciente

def poslogin(request):

    if request.user.id:
     return render(request, 'Bem_vindo.html', {})
    else:
        return HttpResponseRedirect("/")

def cadastroPaciente(request):

 if not request.user.id:
    return HttpResponseRedirect("/")

 else:

    if request.method == 'POST':

        form = PacienteForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            form.save()
            # redirect to a new URL:
            return HttpResponseRedirect('/inicio/')

    # if a GET (or any other method) we'll create a blank form
    else:
        form = PacienteForm()

    return render(request, 'CadastroPaciente.html', {'form': form})

class PacienteListVeiw(ListView):

    model = Paciente
    def lista(self, **kwargs):

        list = super (PacienteListVeiw, self)
        return list

class PacienteUpdate(UpdateView):
    model = Paciente
    success_url = '/inicio/lista/'
    fields = fields = ['nome','cpf','data_Nascimento','sexo','estadoCivil','data_Atendimento','queixaPrincipal',]

class PacienteDelete(DeleteView):
    model = Paciente
    success_url = '/inicio/lista/'


'''
Autores:   Manuel López Amo-Ocón
           Alejangro Galván Pérez-Ilzarbe
           Santiago Cebellán
           Alejandro Meza Tudela

'''

#importacion de librerias
import numpy as np #agrega soporte para vectores y matrices, contituye biblioteca de funciones de alto nivel
import random as rand
from numpy import random #random permite la generacion de numeros aleatorios

from random import choices #choices esta dedicado a la representacion de pesos
import os
import seaborn as sns
import matplotlib.pyplot as plt #Dedicado a la representacion grafica
from scipy.stats import rv_discrete
import pandas as pd

#importacion de la clase persona
from PersonaV13 import *
from EdificioV15 import *

#Clase Simulador: contiene lo necesario para llevar a cabo la simulacion
'''
Lista de atributos:
                    dtrEdad: variable que contiene una distribucion de las edades
                    ciudad: ciudad que se crea para la simulacion
                    dia: entero que representa el paso de los dias
                    hora: entero que representa el paso de las horas
                    numpersonasinicial: entero que representa el numero de personas iniciales de la simulacion
                    numpersonas: entero que representa el numero de personas de la simulacion.
                    numedificios: entero que representa el numero de edificios totales.
                    CatalogoPersonas: lista que contiene a las personas de la simulacion
                    cementerio: lista que contiene las personas muertes debido a la enfermedad
                    RegistroSanos: lista que contiene a los individuos que estan sanos
                    RegistroMuertos: lista que contiene los individuos que van muriendo
                    mortalidadEdadesCovid : lista de listas, que contiene la probabilidad de muerte dependiendo de ciertos rangos
                                            de edad
'''

class Simulador:
        def __init__(self, numpersonas,personasEdificio,maxPiso,mortalidadEdadesCovid,aforoMedio):
            self.dtrEdad=self.generador_distribucion()
            self.aforoMedio=aforoMedio
            self.ciudadViviendas=self.crearViviendas(numpersonas,personasEdificio,maxPiso)
            self.ciudadOficinas=self.crearOficinas(numpersonas,personasEdificio,maxPiso,aforoMedio)
            self.dia=0 #se inicializa el dia con un valor de 0
            self.hora=0 #se inicializa las horas con un valor de 0
            self.numpersonasinicial=numpersonas
            self.numViviendas=len(self.ciudadViviendas)
            self.numOficinas=len(self.ciudadOficinas)
            #self.capacidades=np.zeros(self.numedificios)
            self.CatalogoPersonas=self.crearPoblacion()

            self.acomodarGente() #llamada al metodo definido un poco mas abajo

            self.cementerio=[] #lista vacia que simula un cementerio

            #self.distrEdad=self.dtrEdad
            self.RegistroSanos=[numpersonas]
            self.RegistroMuertos=[]
            self.mortalidadEdadesCovid=mortalidadEdadesCovid

        #Metodo que devuelve una lista de personas: una poblacion para nuestro caso particular
        def crearPoblacion(self):
            poblacion=[]
            for i in range(self.numpersonasinicial):
                poblacion.append(Persona(i,self.dtrEdad))
            return poblacion

        #Metodo que crea una lista de listas, que son las oficinas
        def crearOficinas(self,numpersonas,personasEdificio,maxCapacidad,aforoMedio):
            Oficinas=[]
            personasColocadas=0
            edificios=0
            while (numpersonas>personasColocadas): #mientras existan personas por colocar...
                personasEsteEdificio=random.randint(int(personasEdificio/2),int((personasEdificio/2)*3))
                if (numpersonas<personasEsteEdificio+personasColocadas):
                    personasEsteEdificio=numpersonas-personasColocadas
                personasColocadas+=personasEsteEdificio
                EdificioActual=Oficina(numeroEdificio=edificios,capacidadEdificio=personasEsteEdificio,maxCapacidad=maxCapacidad,
                                       aforo=aforoMedio, vestibulo=True)
                Oficinas.append(EdificioActual) #se adiciona una oficina, a la lista de oficinas
                edificios+=1
            return Oficinas

        def acomodarGente(self):
            personasAcomodadas=0
            for i in self.ciudadViviendas:
                capactual=i.capacidadEdificio
                listaActual=self.CatalogoPersonas[personasAcomodadas:personasAcomodadas+capactual]
                i.acomodar(listaActual)
                i.meterInicio(listaActual)
                personasAcomodadas+=capactual
            CatalogoBarajado=self.CatalogoPersonas.copy()
            random.shuffle(CatalogoBarajado)
            #for i in CatalogoBarajado:
            #    print(i)
            personasAcomodadas=0
            for i in self.ciudadOficinas:
                capactual=i.capacidadEdificio
                listaActual=CatalogoBarajado[personasAcomodadas:personasAcomodadas+capactual]
                i.acomodar(listaActual)
                personasAcomodadas+=capactual

        #Metodo que simula el paso del tiempo de la simulacion
        def pasar_tiempo(self,mediaincubacion,desvincubacion,mediaduracion,desvduracion,posibilidadContagio):
            #self.RegistroSanos.append(self.personas_sanas())
            for i in self.ciudadViviendas:
                i.edificioIAS ( self.hora,self)
                i.contagiarEdificio(posibilidadContagio,mediaincubacion,desvincubacion,self.dia)
            for i in self.ciudadOficinas:
                i.edificioIAS ( self.hora,self)
                i.contagiarEdificio(posibilidadContagio,mediaincubacion,desvincubacion,self.dia)

            if (self.hora<23):
                self.hora+=1
            else:#comienza un nuevo dia
                self.hora=0
                self.dia+=1
                for i in self.ciudadViviendas:
                    #Llamar a las transiciones de estados
                    i.transicionEdificio(self.dia, mediaincubacion,desvincubacion,mediaduracion,desvduracion,self.mortalidadEdadesCovid)
                for i in self.ciudadOficinas:
                    #Llamar a las transiciones de estados
                #transicionEstados(self.dia, mediaincubacion,desvincubacion,mediaduracion,desvduracion,self.mortalidadEdadesCovid)
                     i.transicionEdificio(self.dia, mediaincubacion,desvincubacion,mediaduracion,desvduracion,self.mortalidadEdadesCovid)

        #Metodo que permite a una persona irse de su lugar actual
        def irse(self,persona):
            if persona.lugarActual[2]==0:
                self.ciudadViviendas[persona.lugarActual[0]].departamentos[persona.lugarActual[1]].remove(persona)
            elif persona.lugarActual[2]==1:
                self.ciudadOficinas[persona.lugarActual[0]].departamentos[persona.lugarActual[1]].remove(persona)
            elif persona.lugarActual[2]==2:
                self.ciudadOficinas[persona.lugarActual[0]].vestibulo.remove(persona)

         #Metodo que permite a una persona ir a un lugar que es diferente del que estaba
        def meterse(self,persona,lugar,tipolugar):
            if tipolugar==0:
                self.ciudadViviendas[lugar[0]].departamentos[lugar[1]].append(persona)
            elif tipolugar==1:
                self.ciudadOficinas[lugar[0]].departamentos[lugar[1]].append(persona)
            elif tipolugar==2:
                self.ciudadOficinas[lugar[0]].vestibulo.append(persona)

         #Metodo que crea una lista de listas, que son las viviendas
        def crearViviendas(self,numpersonas,personasEdificio,maxCapacidad):
            Viviendas=[]
            personasColocadas=0
            edificios=0
            while (numpersonas>personasColocadas):#mientras existan personas por colocar...
                personasEsteEdificio=random.randint(int(personasEdificio/2),int((personasEdificio/2)*3))
                if (numpersonas<personasEsteEdificio+personasColocadas):
                    personasEsteEdificio=numpersonas-personasColocadas
                personasColocadas+=personasEsteEdificio
                print(edificios,personasEsteEdificio,maxCapacidad)
                EdificioActual=Vivienda(numeroEdificio=edificios,capacidadEdificio=personasEsteEdificio,maxCapacidad=maxCapacidad)
                Viviendas.append(EdificioActual)
                edificios+=1
            return Viviendas

        #Metodo que permite la impresion de los individuos del catalago de personas
        def printearCatalogo(self):
            for i in self.CatalogoPersonas:
                print(i)

        #Metodo que crea a los 'pacientes cero'
        def contagio_fijo(self, cambioEstadoInicial,numpacientescero):
            personas=rand.sample(self.CatalogoPersonas,k=numpacientescero)
            for i in personas:
                i.estado="asintomatico"
                i.cambioEstado= cambioEstadoInicial

        def graficoPersonasEdades(self):
            edades=[self.CatalogoPersonas[i].edad for i in range(len(self.CatalogoPersonas))]
            arr=np.reshape(edades, (len(self.CatalogoPersonas), 1)).T[0]
            plt.hist(arr, bins=50)
            plt.show()

        def  graficoPersonasSanas(self):
            arr=np.reshape(self.RegistroSanos, (len(self.RegistroSanos), 1)).T[0]
            data=pd.DataFrame({'horas':range(0,len(self.RegistroSanos)),'personas_sanas':arr})
            sns.relplot(x="horas",y="personas_sanas", kind="line", data=data)

        def  graficoPersonasMuertas(self):
            arr=np.reshape(self.RegistroMuertos, (len(self.RegistroMuertos), 1)).T[0]
            data=pd.DataFrame({'horas':range(0,len(self.RegistroMuertos)),'personas_muertas':arr})
            sns.relplot(x="horas",y="personas_muertas", kind="line", data=data)

        #Funcion que se encarga de mostrar el cementerio
        def mostrarcementerio(self):
            print()
            print("Se va a mostrar el cementerio: ")
            if(len(self.cementerio) == 0):
                print("No ha habido ningun fallecido")
            else:
                for i in range (len(self.cementerio)):
                    print(f"{i.dni} , {i.edad}")

        def generador_distribucion(self):
            datos_edad = pd.read_csv("distribucion_edad.csv")
            l=list(datos_edad.iloc[:,0]) # cogemos todos los datos de la primera columna, pertenecientes a todas las filas
            l2= [] #definicion de una lista auxiliar
            for i in range(len(l)-1):
                l2.append(int(l[i])/int(l[-1]))
            #Esto es para ajustar porque parece que el numero total de personas y el numero de personas por edad no acaba de coincidir
            #creo que es que corte los mayores de 100 años.
            l2.append(1-sum(l2))

            l3=[i for i in range(len(l2))]
            #Esta es la distribución que obtenemos
            dtrEdad = rv_discrete(values=(l3,l2))

            #Se procede a eliminar, para que no queden variables sueltas
            del l
            del l2
            del l3
            return dtrEdad


import numpy as np
import matplotlib.pyplot as plt
import scipy.misc
import os
import pyclipper
from skimage import io
from PIL import Image
"""
This scripts find the axes of an object
"""

"""
First Method: Covariance matrix and eigens
"""
# 1.Matrix of points:
#   We'll be dealing with 2D points so our matrix is 2xm. The 1st row are the x-coordinates,
#   2nd row are the y-coordinates and m indicates the number of points.
os.chdir("/home/mrobot/Documentos/TFG/code/Skin-Lesion-Analysis-Towards-Melanoma-Detection")
img = scipy.misc.imread('image/train_ground/ISIC_0000000_segmentation.png', flatten=True)
y, x = np.nonzero(img)

# 2.Subtract the mean for each point:
#   Calculate the mean of the row of x-coordinates. For each x-point, subtract the mean from it. Do the same for
#   the y-coordinate. Mean subtraction minimises the mean square error of approximating the data and centers the data.

x = x - np.mean(x)
y = y - np.mean(y)
coords = np.vstack([x,y])

# 3.Covariance matrix calculation:
#   Calculate the 2x2 covariance matrix.

cov = np.cov(coords)

# 4.Eigenvector, eigenvalues of covariance matrix:
#   Find the 2 eigen-pairs of our dataset.

evals, evecs = np.linalg.eig(cov)

# 5.Rearrange the eigen-pairs:
#   Sort by decreasing eigenvalues

sort_indices = np.argsort(evals)[::-1]
evec1, evec2 = evecs[:, sort_indices]
x_v1, y_v1 = evec1
x_v2, y_v2 = evec2

# Plot tumor
plt.plot(x, y, 'k.')

# Rotation of the tumor
theta = np.arctan((x_v1)/(y_v1))
rotation_mat = np.matrix([[np.cos(theta), -np.sin(theta)],
                          [np.sin(theta), np.cos(theta)]])
transformed_mat = rotation_mat * coords
x_transformed, y_transformed = transformed_mat.A

plt.plot(x_transformed, y_transformed, 'g.')

# Obtener el área que no se solapa con su reflexión
"""
t = [[1, 0]]
d = t * transformed_mat
index_over = np.where(d>=0)[1]
x_over = x_transformed[index_over]
y_over = y_transformed[index_over]

index_sub = np.where(d<0)[1]
x_sub = x_transformed[index_sub]
y_sub = y_transformed[index_sub]

plt.plot(x_over*-1, y_over, 'y.')
plt.plot(x_sub*-1, y_sub, 'b.')
"""

# Inversa del tumor en sentido del eje x
x_transformed_reflection = x_transformed*-1
y_transformed_reflection = y_transformed

plt.plot(x_transformed_reflection, y_transformed_reflection, 'y.')

# Encuentra el convex hull
from scipy.spatial import ConvexHull

points_t = np.array([x_transformed, y_transformed]).transpose()
points_tr = np.array([x_transformed_reflection, y_transformed_reflection]).transpose()
hull_t = ConvexHull(points_t)
hull_tr = ConvexHull(points_tr)

plt.plot(points_t[hull_t.vertices,0], points_t[hull_t.vertices,1], 'r--', lw=2)

#for simplex in hull.simplices:
#    plt.plot(points_t[simplex, 0], points_t[simplex, 1], 'k-')
#    plt.plot(points_tr[simplex, 0], points_tr[simplex, 1], 'r-')

#d = (y_v1)*x0 + (-x_v1)*y0 # determines if a certain point (x0,y0) is over(d>0) or sub(d<0) to the straight line
#print(d)
#plt.plot([x0], [y0], 'wo')

# 6.Plot the principal components
scale = 60

plt.plot([x_v1*-scale*2, x_v1*scale*2],
         [y_v1*-scale*2, y_v1*scale*2], color='red')

plt.plot([x_v2*-scale, x_v2*scale],
         [y_v2*-scale, y_v2*scale], color='blue')

plt.plot([0], [0], 'ro')

plt.axis('equal')
plt.gca().invert_yaxis()
plt.show()


from Model_Prediction_Modules.validation_pred_data import rawpreddatavalidation
from Model_Prediction_Modules.pred_preprocessing import pred_Preprocessor
from Model_Prediction_Modules.model_prediction import model_predictor
from Model_Training_Modules.validation_train_data import rawtraindatavalidation
from Model_Training_Modules.train_preprocessing import train_Preprocessor
from Model_Training_Modules.model_training import model_trainer
import pandas as pd
import streamlit as st
import joblib

def main():
    st.title("Wafer Status Prediction")
    html_temp = """

Wafer Status Prediction App

    """
    st.markdown(html_temp,unsafe_allow_html=True)
    if st.button("Training Data Validation"):
        trainvalidator = rawtraindatavalidation(
            tablename = 'sensordata', file_object = "Training_Logs/Training_Main_Log.txt", gooddir = "Good_Training_Data/", baddir = "Bad_Training_Data/")
        folders = ['Good_Training_Data/','Bad_Training_Data/','Archive_Training_Data/','Training_Data_FromDB/','Intermediate_Train_Results/','Caching/','Saved_Models/']
        trainvalidator.initial_data_preparation(
            schemapath = 'schema_training.json', folders = folders,batchfilepath = "Training_Batch_Files", goodfilepath = 'Good_Training_Data/', archivefilepath= 'Archive_Training_Data/',compileddatapath= 'Training_Data_FromDB/Training_Data.csv')
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")
    if st.button("Exploratory Data Analysis"):
        preprocessor = train_Preprocessor(
            file_object= "Training_Logs/Training_Preprocessing_Log.txt",data_path = 'Training_Data_FromDB/Training_Data.csv',result_dir= 'Intermediate_Train_Results/')
        preprocessor.eda(target_col = 'Output')
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")
    if st.button("Training Data Preprocessing"):
        preprocessor = train_Preprocessor(
            file_object= "Training_Logs/Training_Preprocessing_Log.txt", result_dir= 'Intermediate_Train_Results/')
        preprocessor.data_preprocessing(
            col_drop_path= 'Columns_Drop_from_Original.csv', index_col= 'Wafer', target_col = 'Output')
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")
    if st.button("Model Selection"):
        trainer = model_trainer(
            file_object= "Training_Logs/Training_Model_Log.txt")
        X = pd.read_csv('Intermediate_Train_Results/X.csv')
        y = pd.read_csv('Intermediate_Train_Results/y.csv')
        trainer.model_selection(
            input = X, output = y, num_trials = 20, folderpath = 'Intermediate_Train_Results/')
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")
    if st.button("Final Model Training"):
        trainer = model_trainer(
            file_object= "Training_Logs/Training_Model_Log.txt")
        X = pd.read_csv('Intermediate_Train_Results/X.csv')
        y = pd.read_csv('Intermediate_Train_Results/y.csv')
        trainer.final_model_tuning(
            input_data = X, output_data = y, num_trials = 20, folderpath = 'Intermediate_Train_Results/')
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")
    if st.button("Prediction Data Validation"):
        predvalidator = rawpreddatavalidation(
            tablename = 'predsensordata', file_object= "Prediction_Logs/Prediction_Main_Log.txt", gooddir= "Good_Prediction_Data/", baddir= "Bad_Prediction_Data/")
        folders = ['Good_Prediction_Data/','Bad_Prediction_Data/','Archive_Prediction_Data/','Prediction_Data_FromDB/','Intermediate_Pred_Results/']
        predvalidator.initial_data_preparation(
            schemapath = 'schema_prediction.json', folders = folders,batchfilepath= "Prediction_Batch_Files/", goodfilepath= 'Good_Prediction_Data/', archivefilepath= 'Archive_Prediction_Data/',compileddatapath= 'Prediction_Data_FromDB/Prediction_Data.csv')
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")
    if st.button("Prediction Data Preprocessing"):
        preprocessor = pred_Preprocessor(
            file_object= "Prediction_Logs/Prediction_Preprocessing_Log.txt")
        preprocessor.data_preprocessing(
            data_path= 'Prediction_Data_FromDB/Prediction_Data.csv',train_path= 'Intermediate_Train_Results/', index_col= 'Wafer')
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")
    if st.button("Model Prediction"):
        predictor = model_predictor(
            file_object= "Prediction_Logs/Prediction_Model_Log.txt")
        predictor.model_prediction(
            datapath = 'Intermediate_Pred_Results/Prediction_Processed_Data.csv', model = joblib.load('Saved_Models/FinalModel.pkl'), pipeline = joblib.load('Saved_Models/Preprocessing_Pipeline.pkl'))
        st.success(
            "This step of the pipeline has been completed successfully. Check the local files for more details.")

if __name__=='__main__':
    main()


import hashlib
import json
import yaml
import os
import sys
import shutil
import subprocess
import tempfile
import logging
import posixpath
import docker
import platform
from os.path import expanduser
from os.path import sep as separator
import requests
from requests.exceptions import HTTPError
import boto3
from urllib.parse import urlparse
import configparser
import base64
import uuid

from mlflow.projects.backend.abstract_backend import AbstractBackend
import mlflow.tracking as tracking
from mlflow.entities import RunStatus
from mlflow.utils.git_utils import get_git_commit, get_git_repo_url
from mlflow.projects.submitted_run import SubmittedRun
from mlflow.projects.utils import (
    fetch_and_validate_project,
    get_or_create_run,
    load_project,
    get_entry_point_command,
    get_run_env_vars,
    MLFLOW_DOCKER_WORKDIR_PATH
)
import mlflow.projects
import mlflow.projects.docker
from mlflow.utils import process, file_utils
from mlflow.utils.mlflow_tags import (
        MLFLOW_PROJECT_ENV,
        MLFLOW_PROJECT_BACKEND,
        MLFLOW_DOCKER_IMAGE_URI,
        MLFLOW_DOCKER_IMAGE_ID
)
import mlflow.projects.kubernetes
from mlflow.projects.kubernetes import KubernetesSubmittedRun, _get_run_command, _load_kube_context
import kubernetes
from kubernetes.config.config_exception import ConfigException
from parallels_plugin.login import get_conf, get_token, get_token_file_obj, get_env_var
import kubernetes
from kubernetes.config.config_exception import ConfigException

_logger = logging.getLogger(__name__)

verbose = True

'''
For running in k8s, invoke as 'mlflow run . -b infinstor-backend --backend-config kubernetes_config.json'
kubernetes_config.json contains:
{
    "backend-type": "singlevm|eks",
    "kube-context": "minikube",
    "kube-job-template-path": "kubernetes_job_template.yaml",
    "kube-client-location": "local|backend",
    "repository-uri": "public.ecr.aws/l9n7x1v8/mlflow-projects-demo/full-image",
    "kube-namespace": "default",
    "resources.limits.cpu": "500m",
    "resources.limits.memory": "512Mi",
    "resources.limits.hugepages": "2Mi/80Mi",
    "resources.limits.nvidia.com/gpu": "4",
    "resources.requests.cpu": "250m",
    "resources.requests.memory": "256Mi",
    "resources.requests.hugepages": "2Mi/80Mi"
    "resources.requests.nvidia.com/gpu": "1"
}
Notes:
- If backend-type is not present, it is assumed to be singlevm
- repository-uri is the uri where the full container img, i.e. docker env for MLproject plus MLproject files will be pushed. Use only when kube-client-location is local
- docker env for the MLproject is pushed to by: cd to the MLproject and running
$ docker build .
$ docker tag <image_id_printed_from_prev_cmd> public.ecr.aws/l9n7x1v8/mlflow-projects-demo/base-image
$ docker push public.ecr.aws/l9n7x1v8/mlflow-projects-demo/base-image

kubernetes_job_template contains:
apiVersion: batch/v1
kind: Job
metadata:
  name: "{replaced with MLflow Project name}"
  namespace: default
spec:
  ttlSecondsAfterFinished: 100
  backoffLimit: 0
  template:
    spec:
      containers:
      - name: "{replaced with MLflow Project name}"
        image: "{replaced with URI of Docker image created during Project execution}"
        command: ["{replaced with MLflow Project entry point command}"]
        #env: ["{appended with MLFLOW_TRACKING_URI, MLFLOW_RUN_ID and MLFLOW_EXPERIMENT_ID}"]
        env: []
        resources:
          limits:
            memory: 512Mi
          requests:
            memory: 256Mi
      imagePullSecrets:
      - name: ecr-private-key
      restartPolicy: Never

You can specify the kubernetes_job_template.yaml file or the k8s.resources. If you specify the job template file, and if you are using ecr private registry, you must include the imagePullSecrets section.
'''

class ParallelsSubmittedRun(SubmittedRun):
    """
    A run that just does nothing
    """

    def __init__(self, run_id):
        self._run_id = run_id
        self.status = RunStatus.RUNNING

    def wait(self):
        return True

    def get_status(self):
        return self.status;

    def set_status(self, st):
        self.status = st;

    def cancel(self):
        pass

    @property
    def run_id(self):
        return self._run_id

def upload_objects(run_id, bucket_name, path_in_bucket, local_path):
    if (path_in_bucket[0] == '/'):
        path_in_bucket = path_in_bucket[1:]
    if (verbose):
        _logger.info('upload_objects: Entered. bucket=' + bucket_name
                + ', path_in_bucket=' + path_in_bucket + ', local_path=' + local_path)
    try:
        for path, subdirs, files in os.walk(local_path):
            path = path.replace("\\","/")
            directory_name = path.replace(local_path, "")
            if directory_name.startswith('/.git'): # skip .git and subdirs
                continue
            for onefile in files:
                src_path = os.path.join(path, onefile)
                if (path_in_bucket.endswith('/')):
                    path_in_bucket = path_in_bucket[:-1]
                if (directory_name.startswith('/')):
                    directory_name = directory_name[1:]
                if (len(directory_name) > 0):
                    dst_path = path_in_bucket + '/' + directory_name
                else:
                    dst_path = path_in_bucket
                    dst_path = dst_path.rstrip('\n')
                if (verbose):
                    _logger.info('upload_objects: Uploading ' + src_path + ' to ' + dst_path)
                tracking.MlflowClient().log_artifact(run_id, src_path, dst_path)
    except Exception as err:
        _logger.info(str(err))

class PluginParallelsProjectBackend(AbstractBackend):
    def run(self, project_uri, entry_point, params,
            version, backend_config, tracking_store_uri, experiment_id):

        if (verbose):
            _logger.info("PluginParallelsProjectBackend: Entered. project_uri=" + str(project_uri)\
                + ", entry_point=" + str(entry_point)\
                + ", params=" + str(params)\
                + ", version=" + str(version)\
                + ", backend_config=" + str(backend_config)\
                + ", experiment_id=" + str(experiment_id)\
                + ", tracking_store_uri=" + str(tracking_store_uri))

        work_dir = fetch_and_validate_project(project_uri, version, entry_point, params)
        if 'run-id' in backend_config:
            active_run = get_or_create_run(backend_config['run-id'], project_uri,
                    experiment_id, work_dir, version, entry_point, params)
        else:
            active_run = get_or_create_run(None, project_uri, experiment_id, work_dir, version,
                                       entry_point, params)
        if (verbose):
            _logger.info('active_run=' + str(active_run))
            _logger.info('active_run.info=' + str(active_run.info))

        artifact_uri = active_run.info.artifact_uri
        run_id = active_run.info.run_id

        # Note: this line is screen scraped/parsed in clientlib, to extract the run_id from the output of 'mlflow run -b infinstor_backend ...' command.  See clientlib::run_transform_eks(), clientlib::run_transform_singlevm()
        _logger.info('run_id=' + str(run_id))

        tags = active_run.data.tags
        if (tags['mlflow.source.type'] != 'PROJECT'):
            raise ValueError('mlflow.source_type must be PROJECT. Instead it is '\
                    + tags['mlflow.source.type'])

        if ('parent_run_id' in backend_config):
            parent_run_id = backend_config['parent_run_id']
            tracking.MlflowClient().set_tag(active_run.info.run_id,
                    'mlflow.parentRunId', parent_run_id)

        pdst = urlparse(artifact_uri)
        bucket_name = pdst.netloc
        if (pdst.path[0] == '/'):
            path_in_bucket = pdst.path[1:]
        else:
            path_in_bucket = pdst.path

        '''
        localdir = tags['mlflow.source.name']
        if ('mlflow.source.git.repoURL' in tags or
                'mlflow.gitRepoURL' in tags or
                'mlflow.source.git.commit' in tags):
            pl = urlparse(localdir)
            if (not pl.scheme == 'file'):
                raise ValueError('Cannot deal with scheme ' + pl.scheme + ' in source path')
            localdir = pl.path + separator + pl.fragment
        '''

        project = load_project(work_dir)
        tracking.MlflowClient().set_tag(active_run.info.run_id, MLFLOW_PROJECT_BACKEND, "mlflow-parallels")

        backend_type = backend_config.get('backend-type')
        if backend_type == 'eks' or backend_type == 'gke':
            return self.run_eks(run_id, backend_type, bucket_name, path_in_bucket, work_dir, project_uri, entry_point,
                    params, version, backend_config, tracking_store_uri, experiment_id, project, active_run)
        else:
            _logger.info('Error. unknown backend type ' + backend_type)
            self.fail_run(run_id)
            rv = ParallelsSubmittedRun(active_run.info.run_id)
            rv.set_status(RunStatus.FAILED)
            return rv

    def run_eks(self, run_id, backend_type, bucket_name, path_in_bucket, work_dir, project_uri, entry_point, params,
            version, backend_config, tracking_store_uri, experiment_id, project, active_run):
        kube_client_location = backend_config.get('kube-client-location', 'local')
        if kube_client_location == 'local':
            return self.run_eks_on_local(backend_type, project_uri, entry_point, params, version,
                    backend_config, tracking_store_uri, experiment_id, project, active_run, work_dir)
        elif kube_client_location == 'backend':
            return self.run_eks_on_backend(run_id, backend_type, bucket_name, path_in_bucket, work_dir, project_uri,
                    entry_point, params, version, backend_config, tracking_store_uri, experiment_id,
                    project, active_run)
        else:
            raise ValueError('kube_client_location must be either local or backend')

    def run_eks_on_backend(self, run_id, backend_type, bucket_name, path_in_bucket, work_dir, project_uri, entry_point, params,
            version, backend_config, tracking_store_uri, experiment_id, project, active_run):
        upload_objects(run_id, bucket_name, '.mlflow-parallels/project_files', work_dir)
        body = dict()
        body['backend_type'] = backend_type
        body['MLFLOW_TRACKING_URI'] = os.getenv('MLFLOW_TRACKING_URI')
        body['MLFLOW_PARALLELS_URI'] = os.getenv('MLFLOW_PARALLELS_URI')
        body['params'] = params
        body['run_id'] = run_id
        body['experiment_id'] = str(experiment_id)
        body['docker_image'] = project.docker_env.get("image")
        if not body['docker_image']:
            self.fail_run(run_id)
            raise ValueError('Error. docker image not specified in MLproject')

        if 'kube-job-template-path' in backend_config:
            _logger.info('Using kubernetes job template file ' + backend_config['kube-job-template-path'])
            body['kube_job_template_contents'] = base64.b64encode(
                    open(backend_config.get('kube-job-template-path'), "r").read().encode('utf-8')).decode('utf-8')
            with open(backend_config.get('kube-job-template-path'), "r") as yf:
                yml = yaml.safe_load(yf)
            if 'metadata' in yml and 'namespace' in yml['metadata']:
                body['namespace'] = yml['metadata']['namespace']
                _logger.info('namespace obtained from job template: ' + body['namespace'])
                if 'kube-namespace' in backend_config:
                    if body['namespace'] != backend_config['kube-namespace']:
                        _logger.info('Error. mismatch between namespace in backend configuration and job template')
                        self.fail_run(run_id)
                        rv = ParallelsSubmittedRun(active_run.info.run_id)
                        rv.set_status(RunStatus.FAILED)
                        return rv
            else:
                if 'kube-namespace' in backend_config:
                    body['namespace'] = backend_config['kube-namespace']
                    _logger.info('namespace obtained from backend configuration: ' + body['namespace'])
                else:
                    body['namespace'] = 'default'
        else:
            _logger.info('Using parameters provided in backend configuration to generate a kubernetes job template')
            if "resources.limits.cpu" in backend_config:
                body['resources.limits.cpu'] = backend_config['resources.limits.cpu']
            if "resources.limits.memory" in backend_config:
                body['resources.limits.memory'] = backend_config['resources.limits.memory']
            if "resources.limits.hugepages" in backend_config:
                body['resources.limits.hugepages'] = backend_config['resources.limits.hugepages']
            if "resources.limits.nvidia.com/gpu" in backend_config:
                body['resources.limits.nvidia.com/gpu'] = backend_config['resources.limits.nvidia.com/gpu']
            if "resources.requests.cpu" in backend_config:
                body['resources.requests.cpu'] = backend_config['resources.requests.cpu']
            if "resources.requests.memory" in backend_config:
                body['resources.requests.memory'] = backend_config['resources.requests.memory']
            if "resources.requests.hugepages" in backend_config:
                body['resources.requests.hugepages'] = backend_config['resources.requests.hugepages']
            if "resources.requests.nvidia.com/gpu" in backend_config:
                body['resources.requests.nvidia.com/gpu'] = backend_config['resources.requests.nvidia.com/gpu']
            if 'kube-namespace' in backend_config:
                body['namespace'] = backend_config['kube-namespace']
            else:
                body['namespace'] = 'default'
        kube_context = backend_config.get('kube-context')
        if kube_context:
            body['kube_context'] = kube_context
        if ('parent_run_id' in backend_config):
            body['parent_run_id'] = backend_config['parent_run_id']
        if ('last_in_chain_of_xforms' in backend_config):
            body['last_in_chain_of_xforms'] = backend_config['last_in_chain_of_xforms']
        body['docker_repo_name'] = self.create_docker_repo_name(work_dir)
        commit = get_git_commit(work_dir)
        if commit:
            _logger.info('Using git commit ' + commit)
            body['git_commit'] = commit

        cognito_client_id, _, _, _, region = get_conf()
        token = get_token(cognito_client_id, region, True)

        headers = {
                'Content-Type': 'application/x-amz-json-1.1',
                'Authorization' : 'Bearer ' + token
                }
        url = get_env_var().rstrip('/') + '/api/2.0/mlflow/parallels/run-project'

        try:
            response = requests.post(url, data=json.dumps(body), headers=headers)
            response.raise_for_status()
        except HTTPError as http_err:
            _logger.info(f'HTTP error occurred: {http_err}')
            raise
        except Exception as err:
            _logger.info(f'Other error occurred: {err}')
            raise
        else:
            return ParallelsSubmittedRun(active_run.info.run_id)

    def fail_run(self, run_id):
        tracking.MlflowClient().set_terminated(run_id, 'FAILED')

    def create_docker_repo_name(self, work_dir):
        wd = get_git_repo_url(work_dir)
        if not wd:
            _logger.info('Unable to determine git repo. Using working dir ' + os.getcwd() + ' as docker repository name')
            return os.getcwd()
        else:
            _logger.info('Using git repo ' + wd + ' as docker repository name')
            return wd

    def run_eks_on_local(self, backend_type, project_uri, entry_point, params,
            version, backend_config, tracking_store_uri, experiment_id, project, active_run, work_dir):
        kube_context = backend_config.get('kube-context')
        repository_uri = backend_config.get('repository-uri')
        git_commit = backend_config.get('git-commit')
        kube_job_template_path = backend_config.get('kube-job-template-path')
        input_data_spec = backend_config.get('INPUT_DATA_SPEC')
        if (verbose):
            _logger.info("PluginParallelsProjectBackend.run_eks_on_local: kube-context=" + str(kube_context)\
                + ", repository-uri=" + str(repository_uri)\
                + ", git-commit=" + str(git_commit)\
                + ", kube-job-template-path=" + str(kube_job_template_path)\
                + ", input_data_spec=" + str(input_data_spec))

        from mlflow.projects.docker import (
            validate_docker_env,
            validate_docker_installation
        )
        from mlflow.projects import kubernetes as kb

        tracking.MlflowClient().set_tag(active_run.info.run_id, MLFLOW_PROJECT_ENV, "docker")
        validate_docker_env(project)
        validate_docker_installation()

        kube_config = mlflow.projects._parse_kubernetes_config(backend_config)

        #If a local image has already been created/pulled, kube_config should have it
        if 'IMAGE_TAG' in backend_config and 'IMAGE_DIGEST' in backend_config:
            _logger.info('Image already available: {}, {}'
                         .format(backend_config['IMAGE_TAG'], backend_config['IMAGE_DIGEST']))
            submitted_run = self.run_eks_job(
                project.name,
                active_run,
                backend_config['IMAGE_TAG'],
                backend_config['IMAGE_DIGEST'],
                get_entry_point_command(project, entry_point, params, backend_config['STORAGE_DIR']),
                get_run_env_vars(
                    run_id=active_run.info.run_uuid, experiment_id=active_run.info.experiment_id
                ),
                input_data_spec,
                kube_config.get("kube-context", None),
                kube_config["kube-job-template"],
            )
            return submitted_run
        else:
            _logger.info('Image not available, build or pull')

        # First, try pulling the specific tagged version of the image from the repo
        do_build = True
        if git_commit:
            docker_client = docker.from_env()
            try:
                image = docker_client.images.pull(repository_uri, tag=git_commit[:7])
            except docker.errors.ImageNotFound as inf:
                _logger.info("PluginParallelsProjectBackend.run_eks_on_local: Docker img "
                        + repository_uri + ", tag=" + git_commit[:7] + " not found. Building...")
            except docker.errors.APIError as apie:
                _logger.info("PluginParallelsProjectBackend.run_eks_on_local: Error " + str(apie)
                        + " while pulling " + repository_uri + ", tag=" + git_commit[:7])
            else:
                _logger.info("PluginParallelsProjectBackend.run_eks_on_local: image=" + str(image))
                _logger.info("PluginParallelsProjectBackend.run_eks_on_local: Docker img found "
                        + repository_uri + ", tag=" + git_commit[:7] + ". Reusing...")
                image_digest = docker_client.images.get_registry_data(image.tags[0]).id
                _logger.info("PluginParallelsProjectBackend.run_eks_on_local: image_digest=" + image_digest)
                do_build = False
        if do_build:
            _logger.info("PluginParallelsProjectBackend.run_eks_on_local: Building "
                    + repository_uri)
            image = self.build_docker_image(
                work_dir=work_dir,
                repository_uri=kube_config["repository-uri"],
                base_image=project.docker_env.get("image"),
                run_id=active_run.info.run_id,
                git_commit=git_commit
            )
            image_digest = kb.push_image_to_registry(image.tags[0])

        submitted_run = self.run_eks_job(
            project.name,
            active_run,
            image.tags[0],
            image_digest,
            get_entry_point_command(project, entry_point, params, backend_config['STORAGE_DIR']),
            get_run_env_vars(
                run_id=active_run.info.run_uuid, experiment_id=active_run.info.experiment_id
            ),
            input_data_spec,
            kube_config.get("kube-context", None),
            kube_config["kube-job-template"],
        )
        return submitted_run

    def build_docker_image(self, work_dir, repository_uri, base_image, run_id, git_commit):
        """
        Build a docker image containing the project in `work_dir`, using the base image.
        """
        from mlflow.projects.docker import (
            _create_docker_build_ctx,
            _PROJECT_TAR_ARCHIVE_NAME,
            _GENERATED_DOCKERFILE_NAME
        )
        version_string = ":" + git_commit[:7] if git_commit else ""
        image_uri = repository_uri + version_string
        dockerfile = (
            "FROM {imagename}\n COPY {build_context_path}/ {workdir}\n WORKDIR {workdir}\n"
        ).format(
            imagename=base_image,
            build_context_path=_PROJECT_TAR_ARCHIVE_NAME,
            workdir=MLFLOW_DOCKER_WORKDIR_PATH,
        )
        build_ctx_path = _create_docker_build_ctx(work_dir, dockerfile)
        with open(build_ctx_path, "rb") as docker_build_ctx:
            _logger.info("=== Building docker image %s ===", image_uri)
            client = docker.from_env()
            image, _ = client.images.build(
                tag=image_uri,
                forcerm=True,
                dockerfile=posixpath.join(_PROJECT_TAR_ARCHIVE_NAME, _GENERATED_DOCKERFILE_NAME),
                fileobj=docker_build_ctx,
                custom_context=True,
                encoding="gzip",
            )
        try:
            os.remove(build_ctx_path)
        except Exception:
            _logger.info("Temporary docker context file %s was not deleted.", build_ctx_path)
        tracking.MlflowClient().set_tag(run_id, MLFLOW_DOCKER_IMAGE_URI, image_uri)
        tracking.MlflowClient().set_tag(run_id, MLFLOW_DOCKER_IMAGE_ID, image.id)
        return image

    def run_eks_job(
        self,
        project_name,
        active_run,
        image_tag,
        image_digest,
        command,
        env_vars,
        input_data_spec,
        kube_context=None,
        job_template=None
    ):
        _logger.info('run_eks_job: Entered. project_name=' + str(project_name)\
                + ', active_run=' + str(active_run) + ', image_tag=' + str(image_tag)\
                + ', image_digest=' + str(image_digest) + ', command=' + str(command)\
                + ', env_vars=' + str(env_vars) + ', input_data_spec=' + str(input_data_spec)\
                + ', kube_context=' + str(kube_context) + ', job_template=' + str(job_template))
        env_vars['MLFLOW_PARALLELS_URI'] = os.getenv('MLFLOW_PARALLELS_URI')
        job_template = mlflow.projects.kubernetes._get_kubernetes_job_definition(
            project_name, image_tag, image_digest, _get_run_command(command), env_vars, job_template
        )
        job_name = job_template["metadata"]["name"]
        job_namespace = job_template["metadata"]["namespace"]
        _load_kube_context(context=kube_context)

        core_api_instance = kubernetes.client.CoreV1Api()
        tok = base64.b64encode(get_token_file_obj('r').read().encode('utf-8')).decode('utf-8')
        token_secret_name = 'parallelstokenfile-' + str(uuid.uuid4())
        try:
            core_api_instance.delete_namespaced_secret(namespace=job_namespace, name=token_secret_name)
        except:
            pass
        sec = kubernetes.client.V1Secret()
        sec.metadata = kubernetes.client.V1ObjectMeta(name=token_secret_name, namespace=job_namespace)
        sec.type = 'Opaque'
        sec.data = {'token': tok}
        core_api_instance.create_namespaced_secret(namespace=job_namespace, body=sec)
        aws_creds = base64.b64encode(open(os.path.join(expanduser('~'), '.aws', 'credentials'), "r").read().encode('utf-8')).decode('utf-8')
        awscreds_secret_name = 'awscredsfile-' + active_run.info.run_id
        try:
            core_api_instance.delete_namespaced_secret(namespace=job_namespace, name=awscreds_secret_name)
        except:
            pass
        sec1 = kubernetes.client.V1Secret()
        sec1.metadata = kubernetes.client.V1ObjectMeta(name=awscreds_secret_name, namespace=job_namespace)
        sec1.type = 'Opaque'
        sec1.data = {'credentials': aws_creds}
        core_api_instance.create_namespaced_secret(namespace=job_namespace, body=sec1)
        _logger.info('run_eks_job: input_data_spec = ' + str(input_data_spec))
        if input_data_spec:
            input_spec_name = 'inputdataspec-' + active_run.info.run_id
            _logger.info('run_eks_job: input_spec_name = ' + input_spec_name)
            try:
                core_api_instance.delete_namespaced_secret(namespace=job_namespace, name=input_spec_name)
            except:
                pass
            sec2 = kubernetes.client.V1Secret()
            sec2.metadata = kubernetes.client.V1ObjectMeta(name=input_spec_name, namespace=job_namespace)
            sec2.type = 'Opaque'
            sec2.data = {input_spec_name: input_data_spec}
            core_api_instance.create_namespaced_secret(namespace=job_namespace, body=sec2)

        volume_mounts = [
                    kubernetes.client.V1VolumeMount(mount_path='/root/.mlflow-parallels', name='parallels-token-file'),
                    kubernetes.client.V1VolumeMount(mount_path='/root/.aws', name='aws-creds-file')
                ]
        if input_data_spec:
            volume_mounts.append(kubernetes.client.V1VolumeMount(mount_path='/root/.mlflow-parallels-data', name=input_spec_name))
        job_template["spec"]["template"]["spec"]["containers"][0]["volumeMounts"] = volume_mounts

        job_template["spec"]["template"]["spec"]["volumes"] = [
                    kubernetes.client.V1Volume(name="parallels-token-file", secret=kubernetes.client.V1SecretVolumeSource(secret_name=token_secret_name)),
                    kubernetes.client.V1Volume(name="aws-creds-file", secret=kubernetes.client.V1SecretVolumeSource(secret_name=awscreds_secret_name))
                ]
        if input_data_spec:
            job_template["spec"]["template"]["spec"]["volumes"].append(
                    kubernetes.client.V1Volume(name=input_spec_name, secret=kubernetes.client.V1SecretVolumeSource(secret_name=input_spec_name)))

        _logger.info('run_eks_job: job_template=' + str(job_template))
        api_instance = kubernetes.client.BatchV1Api()
        resp = api_instance.create_namespaced_job(namespace=job_namespace, body=job_template, pretty=True)
        _logger.info( resp.kind + " " + resp.metadata.name +" created." )
        tracking.MlflowClient().log_param(active_run.info.run_id, 'kubernetes.job_name', job_name)
        return KubernetesSubmittedRun(active_run.info.run_id, job_name, job_namespace)