from .base import NavigationTestCase
from ...meta import Meta
from ...navigation import NavEntryNotFound

class TestArrange(NavigationTestCase):
    def test(self):
        navigation = self.createAwesomeNavigation(
            [
                self.section(
                    "Section",
                    [
                        self.page("1", "section/1.md"),
                        self.page("2", "section/2.md"),
                        self.page("3", "section/3.md"),
                        Meta(path="section/.pages", arrange=["2.md", "...", "1.md"]),
                    ],
                )
            ]
        )

        self.assertNavigationEqual(
            navigation.items,
            [
                self.section(
                    "Section",
                    [
                        self.page("2", "section/2.md"),
                        self.page("3", "section/3.md"),
                        self.page("1", "section/1.md"),
                    ],
                )
            ],
        )
        self.assertValidNavigation(navigation.to_mkdocs())

    def test_in_root(self):
        navigation = self.createAwesomeNavigation(
            [
                self.page("1", "1.md"),
                self.page("2", "2.md"),
                self.page("3", "3.md"),
                Meta(path=".pages", arrange=["2.md", "...", "1.md"]),
            ]
        )

        self.assertNavigationEqual(
            navigation.items,
            [self.page("2", "2.md"), self.page("3", "3.md"), self.page("1", "1.md")],
        )
        self.assertValidNavigation(navigation.to_mkdocs())

    def test_sections(self):
        navigation = self.createAwesomeNavigation(
            [
                self.page("1", "1.md"),
                self.page("2", "2.md"),
                self.section("A", [self.page("3", "a/3.md"), self.page("4", "a/4.md")], "a"),
                self.section("B", [self.page("5", "b/5.md"), self.page("6", "b/6.md")], "b"),
                Meta(path=".pages", arrange=["a", "...", "2.md"]),
            ]
        )

        self.assertNavigationEqual(
            navigation.items,
            [
                self.section("A", [self.page("3", "a/3.md"), self.page("4", "a/4.md")]),
                self.page("1", "1.md"),
                self.section("B", [self.page("5", "b/5.md"), self.page("6", "b/6.md")]),
                self.page("2", "2.md"),
            ],
        )
        self.assertValidNavigation(navigation.to_mkdocs())

    def test_deep(self):
        navigation = self.createAwesomeNavigation(
            [
                self.section(
                    "A",
                    [
                        self.page("1", "a/1.md"),
                        self.page("2", "a/2.md"),
                        self.page("3", "a/3.md"),
                        Meta(path="a/.pages", arrange=["...", "1.md"]),
                    ],
                ),
                self.section(
                    "B",
                    [
                        self.page("4", "b/4.md"),
                        self.page("5", "b/5.md"),
                        self.page("6", "b/6.md"),
                        Meta(path="b/.pages", arrange=["5.md"]),
                    ],
                ),
                Meta(path=".pages", arrange=["b", "a"]),
            ]
        )

        self.assertNavigationEqual(
            navigation.items,
            [
                self.section(
                    "B",
                    [
                        self.page("5", "b/5.md"),
                        self.page("4", "b/4.md"),
                        self.page("6", "b/6.md"),
                    ],
                ),
                self.section(
                    "A",
                    [
                        self.page("2", "a/2.md"),
                        self.page("3", "a/3.md"),
                        self.page("1", "a/1.md"),
                    ],
                ),
            ],
        )
        self.assertValidNavigation(navigation.to_mkdocs())

    def test_links(self):
        navigation = self.createAwesomeNavigation(
            [
                self.link("Link 1"),
                self.page("Page 1", "1.md"),
                self.link("Link 2"),
                self.page("Page 2", "2.md"),
                Meta(path=".pages", arrange=["2.md"]),
            ]
        )

        self.assertNavigationEqual(
            navigation.items,
            [
                self.page("Page 2", "2.md"),
                self.link("Link 1"),
                self.page("Page 1", "1.md"),
                self.link("Link 2"),
            ],
        )
        self.assertValidNavigation(navigation.to_mkdocs())

    def test_collapsed(self):
        navigation = self.createAwesomeNavigation(
            [
                self.section("A", [self.page("1", "a/1.md"), self.page("2", "a/2.md")], "a"),
                self.section("B", [self.page("3", "b/3.md")], "b"),
                Meta(path=".pages", arrange=["b", "a"]),
            ],
            collapse_single_pages=True,
        )

        self.assertNavigationEqual(
            navigation.items,
            [
                self.page("3", "b/3.md"),
                self.section("A", [self.page("1", "a/1.md"), self.page("2", "a/2.md")]),
            ],
        )
        self.assertValidNavigation(navigation.to_mkdocs())

    def test_duplicates(self):
        navigation = self.createAwesomeNavigation(
            [
                self.page("1", "1.md"),
                self.page("2", "2.md"),
                self.page("3", "3.md"),
                Meta(path=".pages", arrange=["2.md", "2.md", "3.md", "1.md", "3.md"]),
            ]
        )

        self.assertNavigationEqual(
            navigation.items,
            [
                self.page("2", "2.md"),
                self.page("2", "2.md"),
                self.page("3", "3.md"),
                self.page("1", "1.md"),
                self.page("3", "3.md"),
            ],
        )
        mkdocs_navigation = navigation.to_mkdocs()
        self.assertValidNavigation(mkdocs_navigation, assert_previous_next=False)

        # the previous and next links have to be asserted manually
        # because duplicate navigation entries are two references to the same object
        page1 = mkdocs_navigation.pages[3]
        page2 = mkdocs_navigation.pages[0]
        self.assertEqual(mkdocs_navigation.pages[1], page2)
        page3 = mkdocs_navigation.pages[2]
        self.assertEqual(mkdocs_navigation.pages[4], page3)

        self.assertEqual(page2.previous_page, page2)
        self.assertEqual(page2.next_page, page3)

        self.assertEqual(page3.previous_page, page1)
        self.assertEqual(page3.next_page, None)

        self.assertEqual(page1.previous_page, page3)
        self.assertEqual(page1.next_page, page3)

    def test_entry_not_found(self):
        with self.assertRaises(NavEntryNotFound):
            self.createAwesomeNavigation(
                [
                    self.page("1", "1.md"),
                    self.page("2", "2.md"),
                    Meta(path=".pages", arrange=["1.md", "...", "3.md"]),
                ]
            )

    def test_entry_not_found_not_strict(self):
        with self.assertWarns(NavEntryNotFound):
            self.createAwesomeNavigation(
                [
                    self.page("1", "1.md"),
                    self.page("2", "2.md"),
                    Meta(path=".pages", arrange=["1.md", "...", "3.md"]),
                ],
                strict=False,
            )


import csv

data=[]
with open('archieve.csv') as file:
    reader=csv.reader(file)
    for row in reader:
        data.append(row)
headers=data[0]
planetdata=data[1:]
for i in planetdata:
    i[0]=i[0].lower()
planetdata.sort(key=lambda planetdata:planetdata[0])

with open("sorted.csv","w") as x:
    writer=csv.writer(x)
    writer.writerow(headers)
    writer.writerows(planetdata)

with open("sorted.csv") as input,open("finalsorted.csv","w",newline="") as output:
    writer=csv.writer(output)
    reader=csv.reader(input)
    for i in reader:
        if any(x.strip() for x in i):
            writer.writerow(i)


import math
import os
import random
from typing import Dict, List, Optional

from art.attacks.evasion import RobustDPatch
import cv2
import numpy as np
from tqdm.auto import trange

from armory.art_experimental.attacks.carla_obj_det_utils import (
    linear_depth_to_rgb,
    linear_to_log,
    log_to_linear,
    rgb_depth_to_linear,
)
from armory.logs import log
from armory.utils.external_repo import ExternalRepoImport

with ExternalRepoImport(
    repo="colour-science/colour@v0.3.16",
    experiment="carla_obj_det_dpatch_undefended.json",
):
    import colour

def calculate_ccm(im_np, gt_np, gamma=2.2, Vandermonde=True, degree=1):
    """
    Calculates the color transform matrix.

    attributes::
        im_np
            np array of color patch values from image
        gt_np
            np array of known color patch values
        gamma
            default is 2.2, this is most common
        Vandermonde
            boolean indicating whether to use basic ccm method or
            Vandermonde method
        degree
            default is 1, this is only used with Vandermonde method

    returns::
        color correction matrix
    """
    # normalize both arrays
    im_np = im_np / 255
    gt_np = gt_np / 255

    # linearize values by decoding gamma from RGBs
    im_lin = np.power(im_np, gamma)
    gt_lin = np.power(gt_np, gamma)

    # calculate matrix
    if Vandermonde:
        ccm = colour.characterisation.matrix_colour_correction_Vandermonde(
            gt_lin, im_lin
        )
    else:
        ccm = np.linalg.pinv(np.asmatrix(gt_lin)).dot(np.asmatrix(im_lin))

    return ccm

def apply_ccm(patch, ccm, gamma=2.2, Vandermonde=True, degree=1):
    """
    Applies transform to patch.

    attributes::
        patch
            np array of patch to be inserted
        ccm
            color correction matrix
        gamma
            default is still 2.2
        Vandermonde
            boolean indicating whether basic or Vandermonde method is
            being used for calculations
        degree
            default is 1, should only be used when Vandermonde is True

    returns::
        color transformed patch
    """
    # normalize image
    patch = patch / 255

    # linearize image
    patch_lin = np.power(patch, gamma)

    # get shape of patch
    rows, cols, ch = patch_lin.shape

    if Vandermonde:
        # reshape for matrix multiplication
        patch_reshape = np.reshape(patch_lin, (-1, 3))

        # expand patch for transform
        patch_expand = colour.characterisation.polynomial_expansion_Vandermonde(
            patch_reshape, degree
        )

        # multiply and reshape
        corrected_RGB = np.reshape(
            np.transpose(np.dot(ccm, np.transpose(patch_expand))), (rows, cols, ch)
        )
    else:
        # reshape for matrix multiplication
        patch_reshape = np.reshape(patch_lin, (rows * cols, ch))

        # multiply
        corrected_RGB = np.matmul(np.asmatrix(patch_reshape), ccm)

        # reshape back to normal
        corrected_RGB = np.reshape(np.array(corrected_RGB), (rows, cols, ch))

    # clip where necessary
    corrected_RGB = np.array(corrected_RGB)
    corrected_RGB[corrected_RGB > 1.0] = 1.0
    corrected_RGB[corrected_RGB < 0.0] = 0.0

    # reapply gamma
    corrected_RGB = np.power(corrected_RGB, (1 / gamma))

    # compensate for saturated pixels
    corrected_RGB[patch_lin == 1.0] = 1.0

    return corrected_RGB

def insert_transformed_patch(
    patch, image, gs_shape, rgb, patch_coords=[], image_coords=[]
):
    """
    Insert patch to image based on given or selected coordinates

    attributes::
        patch
            patch as numpy array
        image
            image as numpy array
        gs_shape
            green screen shape
        rgb
            true if gs_im is RGB, false otherwise
        patch_coords
            patch coordinates to map to image [numpy array]
        image_coords
            image coordinates patch coordinates will be mapped to [numpy array]
            going in clockwise direction, starting with upper left corner

    returns::
        image with patch inserted
    """

    # if no patch coords are given, just use whole image
    if patch_coords == []:
        h, w, c = patch.shape
        patch_coords = np.array([[0, 0], [w - 1, 0], [w - 1, h - 1], [0, h - 1]])

    # calculate homography
    h, status = cv2.findHomography(patch_coords, image_coords)

    # mask to aid with insertion
    mask = np.ones((patch.shape[0], patch.shape[1], 3))
    mask_out = cv2.warpPerspective(
        mask, h, (image.shape[1], image.shape[0]), cv2.INTER_CUBIC
    )

    # mask patch and warp it to destination coordinates
    patch[mask == 0] = 0
    im_out = cv2.warpPerspective(
        patch, h, (image.shape[1], image.shape[0]), cv2.INTER_CUBIC
    )

    # save image before adding shadows
    im_cp_one = np.copy(image)
    im_cp_one[mask_out != 0] = 0
    im_out_cp = np.copy(im_out)
    before = im_cp_one.astype("float32") + im_out_cp.astype("float32")

    if rgb:
        v_avg = 0.5647  # V value (in HSV) for the green screen, which is #00903a

        # mask image for patch insert
        image_cp = np.copy(image)
        image_cp[mask_out == 0] = 0

        # convert to HSV space for shadow estimation
        target_hsv = cv2.cvtColor(image_cp, cv2.COLOR_BGR2HSV)
        target_hsv = target_hsv.astype("float32")
        target_hsv /= 255.0

        # apply shadows to patch
        ratios = target_hsv[:, :, 2] / v_avg
        im_out = im_out.astype("float32")
        im_out[:, :, 0] = im_out[:, :, 0] * ratios
        im_out[:, :, 1] = im_out[:, :, 1] * ratios
        im_out[:, :, 2] = im_out[:, :, 2] * ratios
        im_out[im_out > 255.0] = 255.0

        im_cp_two = np.copy(image)
        im_cp_two[mask_out != 0] = 0
        final = im_cp_two.astype("float32") + im_out.astype("float32")
    else:
        final = before

    return before, final

def insert_patch(
    gs_coords, gs_im, patch, gs_shape, cc_gt, cc_scene, apply_realistic_effects, rgb
):
    """
    :param gs_coords: green screen coordinates in [(x0,y0),(x1,y1),...] format. Type ndarray.
    :param gs_im: clean image with green screen. Type ndarray.
    :param patch: adversarial patch. Type ndarray
    :param gs_shape: green screen shape. Type str.
    :param cc_gt: colorchecker ground truth values. Type ndarray.
    :param cc_scene: colorchecker values in the scene. Type ndarray.
    :param apply_realistic_effects: apply effects such as color correction, blurring, and shadowing. Type bool.
    :param rgb: true if gs_im is RGB, false otherwise
    """

    if apply_realistic_effects:
        # calculate color matrix
        ccm = calculate_ccm(cc_scene, cc_gt, Vandermonde=True)

        # apply color matrix to patch
        patch = apply_ccm(patch.astype("float32"), ccm)

        # resize patch
        scale = (np.amax(gs_coords[:, 1]) - np.amin(gs_coords[:, 1])) / patch.shape[0]
        patch = cv2.resize(
            patch,
            (int(patch.shape[1] * scale), int(patch.shape[0] * scale)),
            interpolation=cv2.INTER_CUBIC,
        )

        # datatype correction
        patch = patch * 255
    patch = patch.astype("uint8")

    # convert for use with cv2
    if rgb:
        patch = cv2.cvtColor(patch, cv2.COLOR_RGB2BGR)

    enlarged_coords = np.copy(gs_coords)
    pad_amt_x = int(0.03 * (enlarged_coords[2, 0] - enlarged_coords[0, 0]))
    pad_amt_y = int(0.03 * (gs_coords[2, 1] - gs_coords[0, 1]))
    enlarged_coords[0, 0] -= pad_amt_x
    enlarged_coords[0, 1] -= pad_amt_y
    enlarged_coords[1, 0] += pad_amt_x
    enlarged_coords[1, 1] -= pad_amt_y
    enlarged_coords[2, 0] += pad_amt_x
    enlarged_coords[2, 1] += pad_amt_y
    enlarged_coords[3, 0] -= pad_amt_x
    enlarged_coords[3, 1] += pad_amt_y

    # insert transformed patch
    image_digital, image_physical = insert_transformed_patch(
        patch, gs_im, gs_shape, image_coords=enlarged_coords, rgb=rgb
    )

    if apply_realistic_effects:
        return image_physical
    else:
        return image_digital

class CARLADapricotPatch(RobustDPatch):
    def __init__(self, estimator, **kwargs):
        # Maximum depth perturbation from a flat patch
        self.depth_delta_meters = kwargs.pop("depth_delta_meters", 3)
        self.learning_rate_depth = kwargs.pop("learning_rate_depth", 0.0001)
        self.depth_perturbation = None
        self.max_depth = None
        self.min_depth = None
        self.patch_base_image = kwargs.pop("patch_base_image", None)

        # HSV bounds are user-defined to limit perturbation regions
        self.hsv_lower_bound = np.array(
            kwargs.pop("hsv_lower_bound", [0, 0, 0])
        )  # [0, 0, 0] means unbounded below
        self.hsv_upper_bound = np.array(
            kwargs.pop("hsv_upper_bound", [255, 255, 255])
        )  # [255, 255, 255] means unbounded above

        super().__init__(estimator=estimator, **kwargs)

    def create_initial_image(self, size, hsv_lower_bound, hsv_upper_bound):
        """
        Create initial patch based on a user-defined image
        """
        module_path = globals()["__file__"]
        # user-defined image is assumed to reside in the same location as the attack module
        patch_base_image_path = os.path.abspath(
            os.path.join(os.path.join(module_path, "../"), self.patch_base_image)
        )

        im = cv2.imread(patch_base_image_path)
        im = cv2.resize(im, size)
        im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)

        hsv = cv2.cvtColor(im, cv2.COLOR_RGB2HSV)
        # find the colors within the boundaries
        mask = cv2.inRange(hsv, hsv_lower_bound, hsv_upper_bound)
        mask = np.expand_dims(mask, 2)
        # cv2.imwrite(
        #     "mask.png", mask
        # )  # visualize perturbable regions. Comment out if not needed.

        patch_base = im / 255.0
        mask = mask / 255.0
        return patch_base, mask

    def inner_generate(  # type: ignore
        self, x: np.ndarray, y: Optional[List[Dict[str, np.ndarray]]] = None, **kwargs
    ) -> np.ndarray:
        """
        Generate RobustDPatch.
        :param x: Sample images.
        :param y: Target labels for object detector.
        :return: Adversarial patch.
        """
        if y is None and self.targeted:
            raise ValueError(
                "The targeted version of RobustDPatch attack requires target labels provided to `y`."
            )
        if y is not None and not self.targeted:
            raise ValueError("The RobustDPatch attack does not use target labels.")
        if x.ndim != 4:  # pragma: no cover
            raise ValueError("The adversarial patch can only be applied to images.")

        # Check whether patch fits into the cropped images:
        if self.estimator.channels_first:
            image_height, image_width = x.shape[2:4]
        else:
            image_height, image_width = x.shape[1:3]

        if not self.estimator.native_label_is_pytorch_format and y is not None:
            from art.estimators.object_detection.utils import convert_tf_to_pt

            y = convert_tf_to_pt(y=y, height=x.shape[1], width=x.shape[2])

        if y is not None:
            for i_image in range(x.shape[0]):
                y_i = y[i_image]["boxes"]
                for i_box in range(y_i.shape[0]):
                    x_1, y_1, x_2, y_2 = y_i[i_box]
                    if (  # pragma: no cover
                        x_1 < self.crop_range[1]
                        or y_1 < self.crop_range[0]
                        or x_2 > image_width - self.crop_range[1] + 1
                        or y_2 > image_height - self.crop_range[0] + 1
                    ):
                        raise ValueError(
                            "Cropping is intersecting with at least one box, reduce `crop_range`."
                        )

        if (  # pragma: no cover
            self.patch_location[0] + self.patch_shape[0]
            > image_height - self.crop_range[0]
            or self.patch_location[1] + self.patch_shape[1]
            > image_width - self.crop_range[1]
        ):
            raise ValueError("The patch (partially) lies outside the cropped image.")

        for i_step in trange(
            self.max_iter, desc="RobustDPatch iteration", disable=not self.verbose
        ):
            num_batches = math.ceil(x.shape[0] / self.batch_size)
            patch_gradients = np.zeros_like(self._patch)
            depth_gradients = None

            for e_step in range(self.sample_size):

                for i_batch in range(num_batches):
                    i_batch_start = i_batch * self.batch_size
                    i_batch_end = min((i_batch + 1) * self.batch_size, x.shape[0])

                    if y is None:
                        y_batch = y
                    else:
                        y_batch = y[i_batch_start:i_batch_end]

                    (
                        patched_images,
                        patch_target,
                        transforms,
                    ) = self._augment_images_with_patch(
                        x[i_batch_start:i_batch_end],
                        y_batch,
                        self._patch,
                        channels_first=self.estimator.channels_first,
                    )

                    gradients = self.estimator.loss_gradient(
                        x=patched_images,
                        y=patch_target,
                        standardise_output=True,
                    )  # (B,H,W,C)

                    gradients_rgb = gradients[:, :, :, :3]
                    if gradients.shape[-1] == 6:
                        if depth_gradients is None:
                            depth_gradients = gradients[:, :, :, 3:]
                        else:
                            depth_gradients = np.concatenate(
                                (depth_gradients, gradients[:, :, :, 3:]), axis=0
                            )

                    gradients_rgb = self._untransform_gradients(
                        gradients_rgb,
                        transforms,
                        channels_first=self.estimator.channels_first,
                    )
                    patch_gradients = patch_gradients + np.sum(gradients_rgb, axis=0)

            # Write summary
            if self.summary_writer is not None:  # pragma: no cover
                x_patched, y_patched, _ = self._augment_images_with_patch(
                    x, y, self._patch, channels_first=self.estimator.channels_first
                )

                self.summary_writer.update(
                    batch_id=0,
                    global_step=i_step,
                    grad=np.expand_dims(patch_gradients, axis=0),
                    patch=self._patch,
                    estimator=self.estimator,
                    x=x_patched,
                    y=y_patched,
                    targeted=self.targeted,
                )

            self._patch = (
                self._patch
                + np.sign(patch_gradients)
                * (1 - 2 * int(self.targeted))
                * self.learning_rate
                * self.patch_mask
            )

            # Update depth perturbation
            if gradients.shape[-1] == 6:
                images_depth = patched_images[:, :, :, 3:]

                if self.depth_type == "log":
                    depth_log = (
                        self.depth_perturbation
                        + np.sign(depth_gradients)
                        * (1 - 2 * int(self.targeted))
                        * self.learning_rate_depth
                    )
                    perturbed_images = np.clip(
                        images_depth + depth_log, self.min_depth, self.max_depth
                    )
                    self.depth_pertubation = perturbed_images - images_depth
                else:
                    grads_linear = rgb_depth_to_linear(
                        depth_gradients[:, :, :, 0],
                        depth_gradients[:, :, :, 1],
                        depth_gradients[:, :, :, 2],
                    ).astype("float32")
                    depth_linear = rgb_depth_to_linear(
                        self.depth_perturbation[:, :, :, 0],
                        self.depth_perturbation[:, :, :, 1],
                        self.depth_perturbation[:, :, :, 2],
                    ).astype("float32")
                    depth_linear = (
                        depth_linear
                        + np.sign(grads_linear)
                        * (1 - 2 * int(self.targeted))
                        * self.learning_rate_depth
                    )

                    images_depth_linear = rgb_depth_to_linear(
                        images_depth[:, :, :, 0],
                        images_depth[:, :, :, 1],
                        images_depth[:, :, :, 2],
                    ).astype("float32")
                    depth_linear = np.clip(
                        images_depth_linear + depth_linear,
                        self.min_depth,
                        self.max_depth,
                    )
                    depth_r, depth_g, depth_b = linear_depth_to_rgb(depth_linear)
                    perturbed_images = np.stack(
                        [depth_r, depth_g, depth_b], axis=-1
                    ).astype("float32")
                    self.depth_perturbation = perturbed_images - images_depth

            if self.estimator.clip_values is not None:
                self._patch = np.clip(
                    self._patch,
                    self.estimator.clip_values[0],
                    self.estimator.clip_values[1],
                )

        if self.summary_writer is not None:
            self.summary_writer.reset()

        return self._patch

    def _augment_images_with_patch(self, x, y, patch, channels_first):
        """
        Augment images with patch using perspective transform
        instead of inserting patch based on patch_location

        :param x: Sample images.
        :param y: Target labels.
        :param patch: The patch to be applied.
        :param channels_first: Set channels first or last.
        """

        if x.shape[0] != len(self.gs_coords):
            raise ValueError(
                "Number of images should be equal to the number of arrays of green screen coordinates"
            )
        if y is not None and (x.shape[0] != len(y)):
            raise ValueError(
                "Number of images should be equal to the number of targets"
            )

        transformations = dict()
        x_copy = x.copy()
        patch_copy = patch.copy()

        # Apply patch:
        x_patch = []
        for i in range(len(x_copy)):
            xi = x_copy[i]
            gs_coords = self.gs_coords[i]

            if xi.shape[-1] == 3:
                rgb_img = xi.astype("float32")
            else:
                rgb_img = xi[:, :, :3].astype("float32")  # (H,W,3)
                depth_img = xi[:, :, 3:].astype("float32")  # (H,W,3)
                depth_perturbation = self.depth_perturbation[i]  # (H,W,3)
                foreground = self.foreground[i]  # (H,W,1)

            # apply patch using DAPRICOT transform to RGB channels only
            # insert_patch() uses BGR color ordering for input and output
            rgb_img_with_patch = (
                insert_patch(
                    gs_coords,
                    rgb_img[:, :, ::-1] * 255.0,  # input image needs to be BGR
                    patch_copy * 255.0,
                    self.patch_geometric_shape,
                    cc_gt=self.cc_gt,
                    cc_scene=self.cc_scene,
                    apply_realistic_effects=self.apply_realistic_effects,
                    rgb=True,
                )
                / 255.0
            )

            # embed patch into background
            rgb_img_with_patch[
                np.all(self.binarized_patch_mask == 0, axis=-1)
            ] = rgb_img[np.all(self.binarized_patch_mask == 0, axis=-1)][
                :, ::-1
            ]  # (H,W,3)

            if xi.shape[-1] == 3:
                img_with_patch = rgb_img_with_patch.copy()
            else:
                # apply depth perturbation to depth channels
                depth_img = depth_img + depth_perturbation * foreground  # (H,W,3)

                depth_img = np.clip(
                    depth_img,
                    self.estimator.clip_values[0],
                    self.estimator.clip_values[1],
                )

                img_with_patch = np.concatenate(
                    (depth_img[:, :, ::-1], rgb_img_with_patch), axis=-1
                )

            x_patch.append(img_with_patch[:, :, ::-1])  # convert back to RGB
        x_patch = np.asarray(x_patch)  # (B,H,W,3)

        # 1) crop images: not used.
        if self.crop_range[0] != 0 and self.crop_range[1] != 0:
            log.warning("crop_range argument not used.")

        # 2) rotate images
        if sum(self.rotation_weights[1:]) > 0:
            raise ValueError("Non-zero rotations not correctly supported at this time.")

        # 3) adjust brightness:
        brightness = random.uniform(*self.brightness_range)
        x_copy[..., :3] = brightness * x_copy[..., :3]
        x_patch[..., :3] = brightness * x_patch[..., :3]

        transformations.update({"brightness": brightness})

        patch_target = list()

        if not self.targeted:
            y = self.estimator.predict(
                x=x_copy.astype("float32"), standardise_output=True
            )

        for i_image in range(x_copy.shape[0]):
            target_dict = dict()
            target_dict["boxes"] = y[i_image]["boxes"]
            target_dict["labels"] = y[i_image]["labels"]
            target_dict["scores"] = y[i_image]["scores"]

            patch_target.append(target_dict)

        return x_patch, patch_target, transformations

    def _untransform_gradients(
        self,
        gradients,
        transforms,
        channels_first,
    ):
        """
        Revert transformation on gradients using perspective transform

        :param gradients: The gradients to be reverse transformed.
        :param transforms: The transformations in forward direction.
        :param channels_first: Set channels first or last.
        """
        if gradients.shape[0] != len(self.gs_coords):
            raise ValueError(
                "Number of gradient arrays should be equal to the number of arrays of green screen coordinates"
            )

        # Account for brightness adjustment:
        gradients = transforms["brightness"] * gradients

        # Undo perspective transform for gradients
        h, w, c = self.patch_shape
        patch_coords = np.array([[0, 0], [w - 1, 0], [w - 1, h - 1], [0, h - 1]])
        gradients_tmp = []
        for grads, gs_coords in zip(gradients, self.gs_coords):
            h, _ = cv2.findHomography(gs_coords, patch_coords)
            grads_tmp = cv2.warpPerspective(
                grads, h, (self.patch_shape[1], self.patch_shape[0]), cv2.INTER_CUBIC
            )
            gradients_tmp.append(grads_tmp)
        gradients = np.asarray(gradients_tmp)

        return gradients

    def generate(self, x, y=None, y_patch_metadata=None):
        """
        param x: Sample images. For single-modality, shape=(NHW3). For multimodality, shape=(NHW6)
        param y: [Optional] Sample labels. List of dictionaries,
            ith dictionary contains bounding boxes, class labels, and class scores
        param y_patch_metadata: Patch metadata. List of N dictionaries, ith dictionary contains patch metadata for x[i]
        """

        if x.shape[0] > 1:
            log.info("To perform per-example patch attack, batch size must be 1")
        assert x.shape[-1] in [3, 6], "x must have either 3 or 6 color channels"
        if x.shape[-1] == 6 and self.sample_size != 1:
            raise ValueError(
                "Sample size must be 1 for multimodality input because expectation over transformation cannot be applied to depth perturbation"
            )

        num_imgs = x.shape[0]
        attacked_images = []

        for i in range(num_imgs):

            if x.shape[-1] == 3:
                rgb_img = x[i].astype("float32")
            else:
                rgb_img = x[i, :, :, :3].astype("float32")  # (H,W,3)
                depth_img = x[i, :, :, 3:].astype("float32")  # (H,W,3)

            gs_coords = y_patch_metadata[i]["gs_coords"]  # (4,2)
            patch_width = np.max(gs_coords[:, 0]) - np.min(gs_coords[:, 0])
            patch_height = np.max(gs_coords[:, 1]) - np.min(gs_coords[:, 1])
            self.patch_shape = (
                patch_height,
                patch_width,
                3,
            )

            patch_geometric_shape = y_patch_metadata[i].get("shape", "rect")
            self.patch_geometric_shape = str(patch_geometric_shape)

            # this masked to embed patch into the background in the event of occlusion
            self.binarized_patch_mask = y_patch_metadata[i]["mask"]  # (H,W,3)

            # Eval7 contains a mixture of patch locations.
            # Patches that lie flat on the sidewalk or street are constrained to 0.03m depth perturbation, and they are best used to create disappearance errors.
            # Patches located elsewhere (i.e., that do not impede pedestrian/vehicle motion) are constrained to 3m depth perturbation, and they are best used to create hallucinations.
            # Therefore, the depth perturbation bound for each patch is input-dependent.
            if x.shape[-1] == 6:
                if "max_depth_perturb_meters" in y_patch_metadata[i].keys():
                    self.depth_delta_meters = y_patch_metadata[i][
                        "max_depth_perturb_meters"
                    ]
                    log.info(
                        'This dataset contains input-dependent depth perturbation bounds, and the user-defined "depth_delta_meters" has been reset to {} meters'.format(
                            y_patch_metadata[i]["max_depth_perturb_meters"]
                        )
                    )

            # get colorchecker information from ground truth and scene
            self.cc_gt = y_patch_metadata[i].get("cc_ground_truth", None)
            self.cc_scene = y_patch_metadata[i].get("cc_scene", None)

            # Prior to Eval 5 (dev set version 2.0.0), this was set to True. It's now being
            # set False to be more aligned with AdversarialPatch attack. This line is essentially
            # 'if dev set version >= 2.0.0', since these variables are None as of 2.0.0 / Eval 5
            if self.cc_gt is None or self.cc_scene is None:
                self.apply_realistic_effects = False
            else:
                self.apply_realistic_effects = True

            # self._patch needs to be re-initialized with the correct shape
            if self.patch_base_image is not None:
                self._patch, self.patch_mask = self.create_initial_image(
                    (patch_width, patch_height),
                    self.hsv_lower_bound,
                    self.hsv_upper_bound,
                )
            else:
                self._patch = (
                    np.random.randint(0, 255, size=self.patch_shape)
                    / 255
                    * (self.estimator.clip_values[1] - self.estimator.clip_values[0])
                    + self.estimator.clip_values[0]
                )
                self.patch_mask = np.ones_like(self._patch)

            self._patch = np.clip(
                self._patch,
                self.estimator.clip_values[0],
                self.estimator.clip_values[1],
            )

            self.gs_coords = [gs_coords]

            # initialize depth variables
            if x.shape[-1] == 6:

                if np.all(x[i, :, :, 3] == x[i, :, :, 4]) and np.all(
                    x[i, :, :, 3] == x[i, :, :, 5]
                ):
                    self.depth_type = "log"
                    depth_linear = log_to_linear(x[i, :, :, 3:])
                    self.max_depth = np.minimum(
                        1.0, linear_to_log(depth_linear + self.depth_delta_meters)
                    )
                    self.min_depth = np.maximum(
                        0.0, linear_to_log(depth_linear - self.depth_delta_meters)
                    )
                else:
                    self.depth_type = "linear"
                    depth_linear = rgb_depth_to_linear(
                        x[i, :, :, 3], x[i, :, :, 4], x[i, :, :, 5]
                    )
                    self.max_depth = np.minimum(
                        1000.0, depth_linear + self.depth_delta_meters
                    )
                    self.min_depth = np.maximum(
                        0.0, depth_linear - self.depth_delta_meters
                    )

                self.depth_perturbation = np.zeros(
                    (1, *x.shape[1:3], 3), dtype=np.float32
                )  # (1,H,W,3)
                self.foreground = np.all(self.binarized_patch_mask == 255, axis=-1)
                self.foreground = np.expand_dims(self.foreground, (-1, 0))  # (1,H,W,1)
                # ensure area perturbed in depth is consistent with area perturbed in RGB
                h, _ = cv2.findHomography(
                    np.array(
                        [
                            [0, 0],
                            [patch_width - 1, 0],
                            [patch_width - 1, patch_height - 1],
                            [0, patch_height - 1],
                        ]
                    ),
                    gs_coords,
                )
                rgb_mask = np.ones((patch_height, patch_width, 3), dtype=np.float32)
                rgb_mask = cv2.warpPerspective(
                    rgb_mask, h, (x.shape[2], x.shape[1]), cv2.INTER_CUBIC
                )
                self.foreground = self.foreground * rgb_mask[:, :, 0:1]

            if y is None:
                patch = self.inner_generate(
                    np.expand_dims(x[i], axis=0)
                )  # untargeted attack
            else:
                patch = self.inner_generate(
                    np.expand_dims(x[i], axis=0), y=[y[i]]
                )  # targeted attack

            rgb_img_with_patch = (
                insert_patch(
                    self.gs_coords[0],
                    rgb_img[:, :, ::-1] * 255.0,
                    patch * 255.0,
                    self.patch_geometric_shape,
                    cc_gt=self.cc_gt,
                    cc_scene=self.cc_scene,
                    apply_realistic_effects=self.apply_realistic_effects,
                    rgb=True,
                )
                / 255.0
            )

            # embed patch into background
            rgb_img_with_patch[
                np.all(self.binarized_patch_mask == 0, axis=-1)
            ] = rgb_img[np.all(self.binarized_patch_mask == 0, axis=-1)][
                :, ::-1
            ]  # (H,W,3)

            if x.shape[-1] == 3:
                img_with_patch = rgb_img_with_patch.copy()
            else:
                depth_img = (
                    depth_img + self.depth_perturbation[0] * self.foreground[0]
                )  # (H,W,3)

                depth_img = np.clip(
                    depth_img,
                    self.estimator.clip_values[0],
                    self.estimator.clip_values[1],
                )

                img_with_patch = np.concatenate(
                    (depth_img[:, :, ::-1], rgb_img_with_patch), axis=-1
                )
            attacked_images.append(img_with_patch[:, :, ::-1])

        return np.array(attacked_images)  # (B,H,W,3)


import logging
import time
from typing import Any, Iterable

from shared.messages import StatType, GetStat, NotAvailable, ServerMessagesOutput

from . import Phase

RETRY_DELAY = 3

class EndPhase(Phase):
    def send_stations(self, city: str, lines: Iterable[str]) -> Phase:
        raise ValueError("Can't send stations, job is done")

    def send_weather(self, city: str, lines: Iterable[str]) -> Phase:
        raise ValueError("Can't send weather, job is done")

    def send_trips(self, city: str, lines: Iterable[str]) -> Phase:
        raise ValueError("Can't send trips, job is done")

    def get_stat(self, stat_type: StatType) -> tuple[Phase, Any]:
        logging.debug(f"Requesting stat {stat_type}")
        response: ServerMessagesOutput | None = None
        while response is None or isinstance(response, NotAvailable):
            if response is not None:
                time.sleep(RETRY_DELAY)
            self.output.send(GetStat(stat_type))
            response = self.output.recv()
        logging.debug(f"Stat {stat_type} received")
        return self, response.data


from numpy import mean, std, asarray
from pandas import read_excel
import csv

from mpmath import *  # likely inneficient - will fix!
mp.dps = 50

def setup():
    global COL_NAME, COL_PUPIL, COL_EXPENDITURE, COL_START, COL_STOP

    COL_NAME -= 1  # subtract 1, due to nature of arrays
    COL_PUPIL -= 1
    COL_EXPENDITURE -= 1
    COL_START -= 1
    COL_STOP -= 1

def removeZeros(columns):  # removes "problem" columns (eg zero division error)
    global height, sheet
    sheet = sheet.tolist()  # converts sheet object to list - easier to modify

    print('\nZero Pupil/Expenditure Errors:')
    for column in columns:
        for row in range(height):
            try:
                if sheet[row][column] == 0:  # searches for cells equal to 0
                    print(sheet[row][COL_NAME])
                    del sheet[row]
                    height -= 1  # change max row variable to stop index error
            except:
                print('Error on:', row, column)  # catch unknown errors

    print('\nEmpty Column Errors:')
    for column in range(COL_PUPIL, len(sheet[0])):
        try:
            if sum([sheet[row][column] for row in range(height)]) == 0:
                # checks if a column is full of remove
                # it *could* that this will return true for a non-zero column
                # but unlikely and much more efficient that alternative method
                for row in range(height):
                    del sheet[row][column]  # remove all rows in problem column
        except:
            print('Error on:', row, column)

    sheet = asarray(sheet)  # convert back to data type used elsewhere in code

def outliers(column):  # finds schools with anomalous data in a given column
    global height, sheet

    outliers = []
    data = [mpf(sheet[row][column]) for row in range(height)]
    # creates list of rows in a given column
    # mpf is a datatype from mpmath with high decimal precision

    upper_bound = mean(data) + 3*std(data, ddof=1)
    lower_bound = mean(data) - 3*std(data, ddof=1)
    # we claim that data 3 stdevs away from the mean is anomalous
    # ddof=1 used because we need sample variation, not population

    for row in range(height):
        value = mpf(sheet[row][column])
        if value > upper_bound or value < lower_bound:
            outliers.append(row)  # creates a list of outlier schools

    sheet = sheet.tolist()  # converts to a modifiable datatype
    print('\nOutliers:')
    for row in outliers:
        print(sheet[row][COL_NAME])
        del sheet[row]  # removes outlier schools - possible an issue!
        height -= 1
    sheet = asarray(sheet)  # converts back to a original datatype

def weights(start, stop):  # calculates "importance" of each metric
    return [mean([mpf(sheet[row][column]) / mpf(sheet[row][COL_EXPENDITURE])
            for row in range(height)]) for column in range(start, stop)]
    # returns mean of every school's proportion of expendature in each metric

def perPupil(start, stop):  # divides metric columns by the number of pupils
    global sheet

    for column in range(start, stop):
        for row in range(height):
                value = mpf(sheet[row][column]) / mpf(sheet[row][COL_PUPIL])
                # divide each value by the number of pupils
                sheet[row][column] = value

def metrics(start, stop):  # calculates stdev and mean of every metric
    return [[std([mpf(sheet[row][column]) for row in range(height)], ddof=1),
            mean([mpf(sheet[row][column]) for row in range(height)])]
            for column in range(start, stop)]
    # returns an array [stdev, mean] for every column of metrics

def scores(stats, start, stop):  # calculates a raw score for each school
    bands = [[stats[column][1]+stats[column][0],  # mean + stdev
             stats[column][1],  # mean
             stats[column][1]-stats[column][0]]  # mean - stdev
             for column in range(stop-start)]
    # calculates bands of for every metric

    scores = [[4 if mpf(sheet[row][column]) >= bands[column-start][0] else
              3 if bands[column-start][1] <= mpf(sheet[row][column]) < bands[column-start][0] else
              2 if bands[column-start][2] <= mpf(sheet[row][column]) < bands[column-start][1] else
              1 if mpf(sheet[row][column]) < bands[column-start][2] else -1
              for column in range(start, stop)] for row in range(height)]
    # if value above top band, max score
    # if value if between top and second band, next max score
    # etc

    return scores

def grade(scores, weights, start, stop):  # creates a final grade for schools
    final_score = [sum([scores[row][column-start]*weights[column-start]
                   for column in range(start, stop)]) for row in range(height)]
    # school's score equals the sum of metric value * weight of metric

    avg = mean(final_score)
    stdev = std(final_score, ddof=1)
    bands = [avg, avg-stdev, avg-2*stdev]
    # creates 3 bands for grade scores

    grades = ['A' if value >= bands[0] else
              'B' if bands[1] <= value < bands[0] else
              'C' if bands[2] <= value < bands[1] else
              'D' if value <= bands[1] else -1 for value in final_score]
    # if above mean, grade is A
    # if above mean - stdev, grade is B
    # etc

    return grades

def save(grades):  # outputs a file of schools and corresponding grades
    output = zip([sheet[row][COL_NAME] for row in range(height)], grades)
    # creates array of names and grades

    with open(OUTPUT_FILE, 'w') as file:  # write information to a cvs file
        writer = csv.writer(file)
        writer.writerows(list(output))

#
# change variables below as appropriate
#

INPUT_FILE = 'data/input.xlsx'
OUTPUT_FILE = 'data/output.csv'

COL_NAME = 2
COL_PUPIL = 4
COL_EXPENDITURE = 6

COL_START = 14
COL_STOP = 20

if __name__ == "__main__":
    setup()

    sheet = read_excel(INPUT_FILE).as_matrix()
    height = len(sheet)
    height = 1000  # fix

    removeZeros([COL_PUPIL, COL_EXPENDITURE])
    outliers(COL_EXPENDITURE)

    weights = weights(COL_START, COL_STOP+1)

    perPupil(COL_START, COL_STOP+1)
    stats = metrics(COL_START, COL_STOP+1)
    scores = scores(stats, COL_START, COL_STOP+1)

    grades = grade(scores, weights, COL_START, COL_STOP+1)
    save(grades)


__author__ = 'Paultimate'

class Hero():

    def __init__(self, name="Jimmy", prof="Warrior", weapon="Sword"):
        """Constructor for hero"""
        self.name = name
        self.prof = prof
        self.weapon = weapon
        self.herodict = {
            "Name": self.name,
            "Class": self.prof,
            "Weapon": self.weapon
        }
        self.herotext = {
            "Welcome": "Greetings, hero. What is thine name? ",
            "AskClass": "A fine name, {Name}. What is your class? ",
            "AskWeapon": "A {Class}, hmm? What shalt thy weapon be? ",
        }

    def setHeroDicts(self, textkey, herokey):
        n = input(self.herotext[textkey].format(**self.herodict))

        if n == "":
            n = self.herodict[herokey]

        self.herodict[herokey] = n
        # print self.herodict[herokey]

    def heroMake(self):
        self.setHeroDicts("Welcome", "Name")
        self.setHeroDicts("AskClass", "Class")
        self.setHeroDicts("AskWeapon", "Weapon")

# print h.herodict["Name"]
# print h.herodict["Class"]
# print h.herodict["Weapon"]


import os
import shutil
import argparse
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from sklearn.metrics import confusion_matrix, accuracy_score
from torch.utils.tensorboard import SummaryWriter
from tqdm.autonotebook import tqdm
from torchvision.transforms import Compose, Resize, ToTensor, Normalize, ColorJitter
from utils.matrix_helpers import plot_confusion_matrix
from dataset import EmotionDataset
from models import EmotionCNN

def get_args():
    parser = argparse.ArgumentParser(description="Train a EmotionCNN model")
    parser.add_argument("--data_path", "-d", type=str, default="data")
    parser.add_argument("--num_workers", "-n", type=str, default=4)
    parser.add_argument("--lr", type=float, default=1e-3)
    parser.add_argument("--batch_size", "-b", type=int, default=8)
    parser.add_argument("--epochs", "-e", type=int, default=100)
    parser.add_argument("--log_path", "-l", type=str, default="tensorboard")
    parser.add_argument("--save_path", "-s", type=str, default="trained_models")
    parser.add_argument("--checkpoint", "-sc", type=str, default=None)
    args = parser.parse_args()

    return args

if __name__ == "__main__":
    args = get_args()

    train_transform = Compose(
        [
            ToTensor(),
            Resize((96, 96), antialias=False),
            ColorJitter(brightness=0.125, contrast=0.1, saturation=0.1, hue=0.05),
            Normalize(mean=[0.5384, 0.4309, 0.3805], std=[0.2563, 0.2310, 0.2234]),
        ]
    )

    test_transform = Compose(
        [
            ToTensor(),
            Resize((96, 96), antialias=False),
            Normalize(mean=[0.5384, 0.4309, 0.3805], std=[0.2563, 0.2310, 0.2234]),
        ]
    )

    train_set = EmotionDataset(
        root=args.data_path, train=True, transform=train_transform
    )
    test_set = EmotionDataset(
        root=args.data_path, train=False, transform=test_transform
    )
    classes_data = train_set.classes

    train_dataloader = DataLoader(
        dataset=train_set,
        batch_size=args.batch_size,
        num_workers=args.num_workers,
        drop_last=True,
        shuffle=True,
    )
    test_dataloader = DataLoader(
        dataset=test_set,
        batch_size=args.batch_size,
        num_workers=args.num_workers,
        drop_last=True,
        shuffle=False,
    )

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

    model = EmotionCNN(num_classes=8).to(device)

    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)
    # Used to store the lowest loss value for Early stopping
    best_loss = 100000

    if args.checkpoint:
        checkpoint = torch.load(args.checkpoint)
        model.load_state_dict(checkpoint["model"])
        optimizer.load_state_dict(checkpoint["optimizer"])
        start_epoch = checkpoint["epoch"]
        print("Continue training at the epoch: ", start_epoch + 1)
    else:
        start_epoch = 0

    # Create folder save model
    if not os.path.isdir(args.save_path):
        os.mkdir(args.save_path)

    # Create folder save log tensorboard
    if os.path.isdir(args.log_path):
        shutil.rmtree(args.log_path)
    os.mkdir(args.log_path)
    writer = SummaryWriter(args.log_path)

    for epoch in range(start_epoch, args.epochs):
        # Train step
        model.train()
        train_loss = []

        train_progress_bar = tqdm(train_dataloader, colour="green")
        for i, (images, labels) in enumerate(train_progress_bar):
            images = images.to(device)
            labels = labels.to(device)
            outputs = model(images)
            loss_value = criterion(outputs, labels)

            # Backward pass
            optimizer.zero_grad()
            loss_value.backward()
            train_loss.append(loss_value.item())

            # Optimal
            optimizer.step()

            # Show information on train progress bar
            train_progress_bar.set_description(
                "Train epoch {}. Iteration {}/{} Loss {:.4f}: ".format(
                    epoch + 1, i + 1, len(train_dataloader), np.mean(train_loss)
                )
            )

            # Save training loss to tensorboard
            writer.add_scalar(
                "Train/Loss", np.mean(train_loss), i + epoch * len(train_dataloader)
            )

        # Validation step
        all_predictions = []
        all_labels = []
        test_loss = []
        model.eval()

        with torch.inference_mode():
            test_progress_bar = tqdm(test_dataloader, colour="cyan")
            for i, (images, labels) in enumerate(test_progress_bar):
                images = images.to(device)
                labels = labels.to(device)

                # Forward pass
                outputs = model(images)
                loss_value = criterion(outputs, labels)
                test_loss.append(loss_value.item())

                # Get the max predict value of the model
                predictions = torch.argmax(outputs, dim=1)
                all_predictions.extend(predictions.tolist())
                all_labels.extend(labels.tolist())

                # Show information on test progress bar
                test_progress_bar.set_description(
                    "Valid epoch {}. Iteration {}/{} Loss {:.4f}: ".format(
                        epoch + 1, i + 1, len(test_dataloader), np.mean(test_loss)
                    )
                )

            # Save loss, confusion matrix to tensorboard
            conf_matrix = confusion_matrix(all_labels, all_predictions)
            acc_score = accuracy_score(all_labels, all_predictions)
            writer.add_scalar("Valid/Loss", np.mean(test_loss), epoch)
            writer.add_scalar("Valid/Acc", acc_score, epoch)
            plot_confusion_matrix(writer, conf_matrix, [i for i in classes_data], epoch)

        checkpoint = {
            "epoch": epoch + 1,
            "model": model.state_dict(),
            "optimizer": optimizer.state_dict(),
        }
        torch.save(checkpoint, os.path.join(args.save_path, "last.pt"))

        if np.mean(test_loss) < best_loss:
            best_loss = np.mean(test_loss)
            best_epoch = epoch
            torch.save(checkpoint, os.path.join(args.save_path, "best.pt"))

        if epoch - best_epoch == 5:  # Early Stopping
            print("Activate early stopping at epoch: ", epoch + 1)
            break


import numpy as np
from pydub import AudioSegment
import datetime

def save_numpy_to_mp3(audio, filename,
                      sample_rate = 44100, bitrate = '128k'):

    audio = np.int16(audio.flatten() * 2 ** 15).tobytes()

    audio_segment = AudioSegment(audio, frame_rate = sample_rate,
                                 sample_width = 2,
                                 channels = 1)

    audio_segment.export(filename, format = 'mp3', bitrate=bitrate)


import csv
import locale
from io import StringIO
import unittest
from PySide6.QtCore import QItemSelectionModel
from PySide6.QtWidgets import QApplication
from spinedb_api import Map
from spinetoolbox.mvcmodels.map_model import MapModel
from spinetoolbox.widgets.custom_qtableview import MapTableView, system_lc_numeric

class TestMapTableView(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        if not QApplication.instance():
            QApplication()

    def setUp(self):
        self._original_clip = QApplication.clipboard().text()

    def tearDown(self):
        QApplication.clipboard().setText(self._original_clip)

    def test_copy_without_selection_returns_false(self):
        table_view = MapTableView()
        model = MapModel(Map([], [], float), table_view)
        table_view.setModel(model)
        self.assertFalse(table_view.copy())
        table_view.deleteLater()

    def test_copy_selection(self):
        table_view = MapTableView()
        model = MapModel(Map(["A"], [2.3]), table_view)
        table_view.setModel(model)
        for column in (0, 1):
            table_view.selectionModel().select(model.index(0, column), QItemSelectionModel.Select)
        self.assertTrue(table_view.copy())
        clip = StringIO(QApplication.clipboard().text())
        table = [row for row in csv.reader(clip, delimiter="\t")]
        with system_lc_numeric():
            self.assertEqual(table, [["A", locale.str(2.3)]])
        table_view.deleteLater()

    def test_copy_selection_does_not_copy_expanse_row_or_column(self):
        table_view = MapTableView()
        model = MapModel(Map(["A"], [2.3]), table_view)
        table_view.setModel(model)
        for column in range(model.columnCount()):
            for row in range(model.rowCount()):
                table_view.selectionModel().select(model.index(row, column), QItemSelectionModel.Select)
        self.assertTrue(table_view.copy())
        clip = StringIO(QApplication.clipboard().text())
        table = [row for row in csv.reader(clip, delimiter="\t")]
        with system_lc_numeric():
            self.assertEqual(table, [["A", locale.str(2.3)]])
        table_view.deleteLater()

    def test_paste_without_selection_returns_false(self):
        table_view = MapTableView()
        model = MapModel(Map(["A"], [2.3]), table_view)
        table_view.setModel(model)
        self.assertFalse(table_view.paste())
        table_view.deleteLater()

    def test_paste_to_empty_table(self):
        table_view = MapTableView()
        model = MapModel(Map([], [], str), table_view)
        table_view.setModel(model)
        table_view.selectionModel().select(model.index(0, 0), QItemSelectionModel.Select)
        self._write_to_clipboard([["A", 2.3]])
        self.assertTrue(table_view.paste())
        self.assertEqual(model.rowCount(), 2)
        self.assertEqual(model.columnCount(), 3)
        self.assertEqual(model.value(), Map(["A"], [2.3]))
        table_view.deleteLater()

    def test_paste_to_single_cell_pastes_everything(self):
        table_view = MapTableView()
        model = MapModel(Map(["A"], [2.3]), table_view)
        table_view.setModel(model)
        table_view.selectionModel().select(model.index(0, 0), QItemSelectionModel.Select)
        self._write_to_clipboard([["V", -5.5], ["W", -6.6]])
        self.assertTrue(table_view.paste())
        self.assertEqual(model.rowCount(), 3)
        self.assertEqual(model.columnCount(), 3)
        self.assertEqual(model.value(), Map(["V", "W"], [-5.5, -6.6]))
        table_view.deleteLater()

    def test_paste_large_data_to_small_selection_cuts_data(self):
        table_view = MapTableView()
        model = MapModel(Map(["A", "B", "C"], [2.3, 3.2, 4.3]), table_view)
        table_view.setModel(model)
        for row in (0, 1):
            table_view.selectionModel().select(model.index(row, 0), QItemSelectionModel.Select)
        self._write_to_clipboard([["Q", -4.4], ["V", -5.5], ["W", -6.6]])
        self.assertTrue(table_view.paste())
        self.assertEqual(model.rowCount(), 4)
        self.assertEqual(model.columnCount(), 3)
        m = model.value()
        self.assertEqual(model.value(), Map(["Q", "V", "C"], [2.3, 3.2, 4.3]))
        table_view.deleteLater()

    @staticmethod
    def _write_to_clipboard(data):
        with StringIO() as out_string:
            writer = csv.writer(out_string, delimiter="\t")
            writer.writerows(data)
            clip = out_string.getvalue()
        QApplication.clipboard().setText(clip)

if __name__ == "__main__":
    unittest.main()


import requests
import base64
import json

url = "https://api.riset.ai/api_tablet/v1/recognizemask"

image_filename = "/Users/vanneswijaya/Documents/PERSONAL/GALLERY/foto_selfie.jpg"

with open(image_filename, "rb") as image_file:
    image_base64 = base64.b64encode(image_file.read())
    image_base64 = str(image_base64)[2:]
    image_base64 = str(image_base64)[:-1]
    f = open("response.txt", "a")
    f.write(str(image_base64))
    f.close()

    print("converted image to base64", image_base64)

print(type(image_base64))

payload = {"client_id" : "Trial",
            "trx_id" : "jfwoi902klf9sd7",
            "user_image" : str(image_base64),
            "timestamp" : "2020-01-01 00:00:00+07"}

# f = open("response.txt", "a")
# f.write(str(payload))
# f.close()
payload = str(payload)
# payload = json.loads(payload)

headers = {'content-type': 'application/json'}

response = requests.request("POST", url, data=payload, headers=headers)

data = json.loads(response.text)
print(data["return"])


"""Crop the individual sprites from the sprite sheet."""

import argparse
import os
import pathlib
import sys

import PIL
import PIL.Image

def main() -> int:
    """Execute the main routine."""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.parse_args()

    this_path = pathlib.Path(os.path.realpath(__file__))
    images_dir = this_path.parent.parent / "burpeefrog/media/images"
    sheet_path = images_dir / "frog.png"

    image = PIL.Image.open(
        str(this_path.parent.parent / "burpeefrog/media/images/frog.png")
    )
    png_info = dict()
    if image.mode not in ['RGB', 'RGBA']:
        image = image.convert('RGBA')
        png_info = image.info

    image_w, image_h = image.size
    sprite_w = int(image_w / 4)
    sprite_h = image_h

    for i in range(4):
        xmin = i * sprite_w
        xmax = i * sprite_w + sprite_w

        ymin = 0
        ymax = sprite_h

        cropped = image.crop((xmin, ymin, xmax, ymax))
        cropped = cropped.resize((50, 100))

        cropped.save(str(images_dir / f"frog{i}.png"), **png_info)

    return 0

    return 0

if __name__ == "__main__":
    sys.exit(main())


import numpy as np

import sqlalchemy
from sqlalchemy.ext.automap import automap_base
from sqlalchemy.orm import Session
from sqlalchemy import create_engine, func

from flask import Flask, jsonify

#################################################
# Database Setup
#################################################
engine = create_engine("sqlite:///Resources/hawaii.sqlite")

# reflect an existing database into a new model
Base = automap_base()
# reflect the tables
Base.prepare(autoload_with=engine)

# Save references to each table
measurement = Base.classes.measurement
Station = Base.classes.station

app = Flask(__name__)

@app.route("/")
def welcome():
    """List all available api routes."""
    return (
        f"Available Routes:<br/>"
        f"/api/v1.0/precipitation"
        f"/api/v1.0/stations"
        f"/api/v1.0/tobs"
        f"/api/v1.0/<start>"
        f"/api/v1.0/<start>/<end>"

    )

@app.route("/api/v1.0/precipitation")
def precipitation():
    session = Session(engine)

#Convert the query results from your precipitation analysis
#(i.e. retrieve only the last 12 months of data) to a dictionary
#using date as the key and prcp as the value.

# Calculate the date one year from the last date in data set.
    lastdate = dt.date(2017, 8, 23) - dt.timedelta(days=365)

# Perform a query to retrieve the data and precipitation scores

#prep = session.query(Measurement.date, Measurement.prcp).filter(Measurement.date>lastdate).order_by(Measurement.date).all()
    prep = session.query(Measurement.date, Measurement.prcp).filter(Measurement.date >= lastdate).all()

    session.close()

#

    prp = []
    for prcp, date in prep:
        prp_dict = {}
        prp_dict["precipitation"] = prcp
        prp_dict["date"] = date
        prp.append(prp_dict)

    return jsonify(prp)

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

#id INTEGER
#station TEXT
#name TEXT
#latitude FLOAT
#longitude FLOAT
#elevation FLOAT

#active_stations = session.query(Measurement.station,func.count(Measurement.station)).\
                       #group_by(Measurement.station).\
                       #order_by(func.count(Measurement.station).desc()).all()
#active_stations

@app.route("/api/v1.0/stations")
def stations():
    session = Session(engine)

    active_stations = session.query(Station.station, Station.name).all()
    session.close()

    return jsonify(active_stations)

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

#active  = session.query(Measurement.tobs).filter(Measurement.station == 'USC00519281').\
                         #   filter(Measurement.date >= lastdate).all()
#active

@app.route("/api/v1.0/tobs")
def tobs():
    session = Session(engine)

    active = session.query(Measurement.tobs).filter(Measurement.station == 'USC00519281').\
                            filter(Measurement.date >= lastdate).all()

    session.close()

    return jsonify(active)

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

@app.route("/api/v1.0/<start>")

def start():

    session = Session(engine)

    low_high_avg = session.query(func.min(Measurement.tobs), func.avg(Measurement.tobs), func.max(Measurement.tobs)).filter(Measurement.date >= date).all()

    session.close()

    return jsonify(low_high_avg)

  #  prp = []
  #  for prcp, date in prep:
  #      prp_dict = {}
  #      prp_dict["precipitation"] = prcp
   #     prp_dict["date"] = date
   #     prp.append(prp_dict)

  #  return jsonify(prp)

    low_high_avg_list = []
    for min, avg, max in low_high_avg:
        low_high = {}
        low_high["min"] = min
        low_high["avg"] = avg
        low_high["max"] = max
        low_high_avg_list.append(low_high)

    return jsonify(low_high_avg_list)

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

@app.route("/api/v1.0/<start>/<end>")

def startend (startend):
    session = Session(engine)

    low_high_startend = session.query(func.min(Measurement.tobs), func.avg(Measurement.tobs), func.max(Measurement.tobs)).filter(Measurement.date >= start).filter(Measurement.date <= end).all()

    session.close()

    #low_high_avg_list = []
    #for min, avg, max in low_high_avg:
    #    low_high = {}
     #   low_high["min"] = min
     #   low_high["avg"] = avg
     #   low_high["max"] = max
      #  low_high_avg_list.append(low_high)

#    return jsonify(low_high_avg_list)

    low_high_sstartend = []
    for min, avg, max in low_high_startend:
        low_highsst = {}
        low_highsst["min"] = min
        low_highsst["avg"] = avg
        low_highsst["max"] = max
        low_high_sstartend.append(low_highsst)

    return jsonify(low_high_sstartend)

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

if __name__ == '__main__':
    app.run(debug=True)


from model import CreatTitlesUnit5

def main(creat_json=True):

    # # todo 1-5-1-01 看图填一填
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBackMid(
    #     url_name1='pos-sequence-realize-input_1-5', url_name2='1-5-1-01', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='看图填一填'
    # )
    #
    # #todo 1-5-1-02 看图填一填
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack2Mid(
    #     url_name1='pos-sequence-input2_1-5', url_name2='1-5-1-02', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='看图填一填'
    # )
    #
    # #todo 1-5-1-03 看图选一选
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack3Mid(
    #     url_name1='pos-sequence-select_1-5', url_name2='1-5-1-03', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='看图选一选'
    # )
    #
    # # todo 1-5-1-04 看图填一填
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack4Mid(
    #     url_name1='pos-sequence-select_1-5', url_name2='1-5-1-04', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='看图填一填'
    # )
    #
    # #todo 1-5-1-05 看图选一选
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack5Mid(
    #     url_name1='pos-sequence-select_1-5', url_name2='1-5-1-05', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='看图选一选'
    # )
    #
    # #todo  1-5-1-06 看图填一填
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack6Mid(
    #     url_name1='pos-sequence-input3_1-5', url_name2='1-5-1-06', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='看图填一填'
    # )
    #
    # # todo 1-5-1-07  拖一拖，排排序
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack7Mid(
    #     url_name1='pos-sequence-dragSort_1-5', url_name2='1-5-1-07', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='拖一拖，排排序'
    # )
    #
    # # todo 1-5-1-08  拖一拖，排排序
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack8Mid(
    #     url_name1='pos-sequence-dragSort_1-5', url_name2='1-5-1-08', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='拖一拖，排排序'
    # )
    #
    #
    # #todo 1-5-1-09 拖一拖，排排序
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack9Mid(
    #     url_name1='pos-sequence-dragSort2_1-5', url_name2='1-5-1-09', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='拖一拖，排排序'
    # )
    #
    # # todo 1-5-1-10 看图填一填
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack10Mid(
    #     url_name1='pos-sequence-realize-input_1-5', url_name2='1-5-1-10', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='看图填一填'
    # )
    #
    # # todo 1-5-1-11 填一填
    # CreatTitlesUnit5.LoationTitles().CognitionFrontAndBack11Mid(
    #     url_name1='pos-sequence-input4_1-5', url_name2='1-5-1-11', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='填一填'
    # )
    #
    # #todo 1-5-2-01 选一选
    # CreatTitlesUnit5.LoationTitles().ToChoiceAMid(
    #     url_name1='pos-sequence-select3_1-5', url_name2='1-5-2-01', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='选一选'
    # )
    #
    # #todo 1-5-2-02 选一选
    # CreatTitlesUnit5.LoationTitles().ToChoiceA1Mid(
    #     url_name1='pos-sequence-select4_1-5', url_name2='1-5-2-02', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='选一选'
    # )
    #
    # #todo 1-5-2-03 填一填
    # CreatTitlesUnit5.LoationTitles().ToChoiceA2Mid(
    #     url_name1='pos-sequence-input5_1-5', url_name2='1-5-2-03', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='填一填'
    # )
    #
    # #todo 1-5-2-04 填一填
    # CreatTitlesUnit5.LoationTitles().ToChoiceA3Mid(
    #     url_name1='pos-sequence-input6_1-5', url_name2='1-5-2-04', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='填一填'
    # )
    #
    # #todo 1-5-2-05 填一填
    # CreatTitlesUnit5.LoationTitles().ToChoiceA4Mid(
    #     url_name1='pos-sequence-input7_1-5', url_name2='1-5-2-05', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='填一填'
    # )
    #
    # #todo 1-5-2-06 填一填
    # CreatTitlesUnit5.LoationTitles().ToChoiceA5Mid(
    #     url_name1='pos-sequence-input8_1-5', url_name2='1-5-2-06', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='填一填'
    # )
    #
    #
    # #todo 1-5-2-07 填一填
    # CreatTitlesUnit5.LoationTitles().ToChoiceA6Mid(
    #     url_name1='pos-sequence-input10_1-5', url_name2='1-5-2-07', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='填一填'
    # )
    #
    # # todo 1-5-2-10 选一选
    # CreatTitlesUnit5.LoationTitles().ToChoiceA7Mid(
    #     url_name1='pos-sequence-select5_1-5', url_name2='1-5-2-10', json_path='./data/title_json/',
    #     creat_json=creat_json, json_name='选一选'
    # )
    #
    #
    #todo 1-5-2-11 看图填一填
    CreatTitlesUnit5.LoationTitles().ToChoiceA8Mid(
        url_name1='pos-sequence-input9_1-5', url_name2='1-5-2-11', json_path='./data/title_json/',
        creat_json=creat_json, json_name='看图填一填'
    )

    #todo 1-5-3-01 看图选一选
    CreatTitlesUnit5.LoationTitles().LeftAndRightMid(
        url_name1='pos-sequence-select6_1-5', url_name2='1-5-3-01', json_path='./data/title_json/',
        creat_json=creat_json, json_name='看图选一选'
    )

    # todo 1-5-3-02 看图选一选
    CreatTitlesUnit5.LoationTitles().LeftAndRight1Mid(
        url_name1='pos-sequence-select6_1-5', url_name2='1-5-3-02', json_path='./data/title_json/',
        creat_json=creat_json, json_name='看图选一选'
    )

    #todo 1-5-3-03 看图填一填
    CreatTitlesUnit5.LoationTitles().LeftAndRight2Mid(
        url_name1='pos-sequence-input11_1-5', url_name2='1-5-3-03', json_path='./data/title_json/',
        creat_json=creat_json, json_name='看图选一选'
    )

    # todo 1-5-3-06 连一连
    CreatTitlesUnit5.LoationTitles().LeftAndRight3Mid(
        url_name1='pos-sequence-connect_1-5', url_name2='1-5-3-06', json_path='./data/title_json/',
        creat_json=creat_json, json_name='连一连'
    )

    # todo 1-5-3-07 连一连
    CreatTitlesUnit5.LoationTitles().LeftAndRight4Mid(
        url_name1='pos-sequence-connect2_1-5', url_name2='1-5-3-07', json_path='./data/title_json/',
        creat_json=creat_json, json_name='连一连'
    )

    #todo 1-5-3-08 选一选
    CreatTitlesUnit5.LoationTitles().LeftAndRight5Mid(
        url_name1='pos-sequence-mulSelect_1-5', url_name2='1-5-3-08', json_path='./data/title_json/',
        creat_json=creat_json, json_name='选一选'
    )

    #todo 1-5-3-09 拖一拖
    CreatTitlesUnit5.LoationTitles().LeftAndRight6Mid(
        url_name1='pos-sequence-dragInput2_1-5', url_name2='1-5-3-09', json_path='./data/title_json/',
        creat_json=creat_json, json_name='拖一拖'
    )

main()


import torch
import torch.nn as nn
from einops import rearrange

from vidar.arch.networks.layers.define.embeddings.utils.fourier_position_encoding import \
    PerceiverFourierPositionEncoding
from vidar.utils.data import get_from_dict, get_from_list
from vidar.utils.nerf import sample_coarse, sample_fine, sample_depth, \
    apply_idx, get_camera_origin, get_camera_rays
from vidar.utils.types import is_list
from vidar.arch.networks.layers.define.embeddings.base import BaseEmbeddings

def flatten(data):
    if data.dim() == 4:
        return rearrange(data, 'b n k d -> b (n k) d')
    else:
        return data

class VolumetricEmbeddings(BaseEmbeddings):
    """
    Volumetric Embeddings class.

    Parameters
    ----------
    cfg : Config
        Configuration with parameters
    """
    def __init__(self, cfg):
        super().__init__(cfg)

        self.n_samples = cfg.sample.has('num', None)
        self.sample_range = cfg.sample.has('range', None)
        self.sample_mode = cfg.sample.has('mode', None)
        self.sample_type = cfg.sample.has('type', None)

        self.perturb = cfg.sample.has('perturb', True)
        self.depth_noise = cfg.sample.has('depth_noise', None)
        self.depth_guide = cfg.sample.has('depth_guide', None)
        self.use_prev_samples = cfg.sample.has('use_prev_samples', False)
        self.add_background = cfg.sample.has('add_background', None)

    @property
    def channels(self):
        """ Returns the number of channels."""
        return super().channels

    def sample(self, orig, rays, previous):
        """Return samples along rays given an origin."""
        if self.sample_type == 'coarse':
            near, far = self.sample_range
            if is_list(self.n_samples):
                zvals_coarse = sample_coarse(
                    rays, self.n_samples[0], near, far, depth=previous['depth'],
                    mode=self.sample_mode, perturb=self.perturb, after=False)
                zvals = [zvals_coarse]
                if len(self.n_samples) > 1 and self.n_samples[1] > 0:
                    zvals_depth = sample_depth(
                        rays, self.n_samples[1], near, far, depth=previous['depth'], depth_noise=self.depth_noise)
                    zvals.append(zvals_depth)
                if len(self.n_samples) > 2 and self.n_samples[2] > 0:
                    zvals_after = sample_coarse(
                        rays, self.n_samples[2], near, far, depth=previous['depth'],
                        mode=self.sample_mode, perturb=self.perturb, after=True)
                    zvals.append(zvals_after)
                zvals = torch.cat(zvals, -2)

                zvals_far = sample_coarse(
                    rays, sum(self.n_samples), near, far,
                    mode=self.sample_mode, perturb=self.perturb, after=False)
                same = zvals[:, :, -1, 0] == zvals[:, :, -2, 0]
                zvals[same] = zvals_far[same]

            else:
                zvals = sample_coarse(
                    rays, self.n_samples, near, far, depth=previous['depth'],
                    mode=self.sample_mode, perturb=self.perturb)
        elif self.sample_type == 'fine':
            near, far = self.sample_range
            if is_list(self.n_samples):
                zvals_fine = sample_fine(
                    rays, self.n_samples[0], near, far, previous['weights'])
                zvals_depth = sample_depth(
                    rays, self.n_samples[1], near, far, previous['depth'], self.depth_noise)
                zvals = torch.cat([zvals_fine, zvals_depth], -2)
            else:
                zvals = sample_fine(
                    rays, self.n_samples, near, far, previous['weights'])
        elif self.sample_type == 'depth':
            near, far = self.sample_range
            zvals = sample_depth(
                rays, self.n_samples, near, far, previous['depth'], self.depth_noise)
        else:
            zvals = None

        # Sort and clamp sampled depth values
        if self.sample_range is not None:
            zvals = zvals.clamp(min=self.sample_range[0], max=self.sample_range[1])

        if self.add_background is not None:
            last = self.add_background * rays - orig
            last = torch.linalg.norm(last, dim=2, keepdim=True).unsqueeze(2)
            zvals = torch.cat([zvals, last], 2)

        # Use previous samples if requested
        if self.use_prev_samples:
            zvals = previous['zvals'] if zvals is None else \
                torch.cat([zvals, previous['zvals']], -2)

        # Sort samples
        zvals = torch.sort(zvals, -2)[0]

        # Create 3D points from sampled depth values
        xyz = orig.unsqueeze(-2) + rays.unsqueeze(-2) * zvals

        # Return 3D points and sampled depth values
        return xyz, zvals

    def create_embeddings(self, xyz, orig, rays, zvals, expand=True):
        """Create embeddings for 3D points, origin, rays and depths."""

        b, n, k, _ = zvals.shape

        xyz = flatten(xyz)
        orig = flatten(orig)
        rays = flatten(rays)

        embeddings = []

        # Create 3D embeddings
        if self.fourier_encoding_xyz is not None:
            xyz_embeddings = self.fourier_encoding_xyz(
                index_dims=None, pos=xyz, batch_size=xyz.shape[0], device=xyz.device)
            xyz_embeddings = xyz_embeddings.view(b, n, k, -1)
            embeddings.append(xyz_embeddings)

        # Create origin embeddings
        if self.fourier_encoding_origin is not None:
            orig_embeddings = self.fourier_encoding_origin(
                index_dims=None, pos=orig, batch_size=orig.shape[0], device=orig.device)
            orig_embeddings = orig_embeddings.unsqueeze(2).repeat(1, 1, k, 1) if expand else \
                orig_embeddings.view(b, n, k, -1)
            embeddings.append(orig_embeddings)

        # Create rays embeddings
        if self.fourier_encoding_rays is not None:
            rays_embeddings = self.fourier_encoding_rays(
                index_dims=None, pos=rays, batch_size=rays.shape[0], device=rays.device)
            rays_embeddings = rays_embeddings.unsqueeze(2).repeat(1, 1, k, 1) if expand else \
                rays_embeddings.view(b, n, k, -1)
            embeddings.append(rays_embeddings)

        # Create depth embeddings
        if self.fourier_encoding_depth is not None:
            depth = zvals.view(zvals.shape[0], n * k, 1)
            depth_embeddings = self.fourier_encoding_depth(
                index_dims=None, pos=depth, batch_size=depth.shape[0], device=depth.device)
            depth_embeddings = depth_embeddings.view(b, n, k, -1)
            embeddings.append(depth_embeddings)

        return embeddings

    def get_previous(self, key, data, idx, previous):
        """Get previous predictions if requested. """
        weights = get_from_dict(previous, 'weights', key)
        zvals = get_from_dict(previous, 'info', key)
        zvals = get_from_dict(zvals, 'zvals')

        if self.depth_guide == 'gt':
            depth = rearrange(data[key]['gt']['depth'], 'b c h w -> b (h w) c')
            depth = apply_idx(depth, idx)
        elif self.depth_guide == 'prev':
            depth = get_from_list(get_from_dict(previous, 'depth', key))
        else:
            depth = None

        return {
            'depth': depth,
            'weights': weights,
            'zvals': zvals,
        }

    def get_samples(self, origin, rays, key, data, idx, previous):
        """Get samples from data dict."""
        xyz = get_from_dict(data[key], 'xyz')
        zvals = get_from_dict(data[key], 'zvals')
        if xyz is not None and zvals is not None:
            pass
        elif self.sample_mode is None:
            xyz = get_from_dict(previous['info'], key, 'xyz')
            zvals = get_from_dict(previous['info'], key, 'zvals')
        else:
            previous = self.get_previous(key, data, idx, previous)
            xyz, zvals = self.sample(origin, rays, previous=previous)
        return xyz, zvals

    def forward(self, cam, key, data, coords=None, previous=None):
        """Forward pass: create and return embeddings."""
        orig = get_camera_origin(cam, key, data, idx=coords)
        rays = get_camera_rays(cam, key, data, idx=coords, to_world=self.to_world)
        xyz, zvals = self.get_samples(orig, rays, key, data, coords, previous)

        embeddings = self.create_embeddings(xyz, orig, rays, zvals)
        embeddings = torch.cat(embeddings, -1)

        data = torch.cat([orig, rays], -1)
        if coords is None:
            data = data.permute(0, 2, 1).reshape(len(data), 6, *cam.hw)

        return data, embeddings, {'xyz': xyz, 'zvals': zvals}


import cv2
import numpy as np
import sys
import pickle

class StereoCameraPair:
    @classmethod
    def from_pfiles(cls, left_cam_file, right_cam_file, rect_file, sgbm_file, img_size):
        cam_left = pickle.load(open(left_cam_file, "rb"))
        cam_right = pickle.load(open(right_cam_file, "rb"))
        rect_params = pickle.load(open(rect_file, "rb"))
        sgbm_params = pickle.load(open(sgbm_file, "rb"))
        return cls(cam_left['K'], cam_left['dist'], cam_right['K'],
                   cam_right['dist'], rect_params, sgbm_params, img_size)

    def __init__(self, K_left, dist_left, K_right, dist_right, rect_params, sgbm_params, img_size):
        R1, R2, P1, P2, self.Q, self.valid_region_left, self.valid_region_right = cv2.stereoRectify(
            K_left, dist_left, K_right, dist_right, img_size, rect_params['R'], rect_params['T'])
        self.map_left_1, self.map_left_2 = cv2.initUndistortRectifyMap(
            K_left, dist_left, R1, P1, img_size, cv2.CV_16SC2)
        self.map_right_1, self.map_right_2 = cv2.initUndistortRectifyMap(
            K_right, dist_right, R2, P2, img_size, cv2.CV_16SC2)
        self.stereoSGBM = cv2.StereoSGBM_create(
            sgbm_params["minDisparity"], sgbm_params["numDisparities"], sgbm_params["blockSize"],
            sgbm_params["P1"], sgbm_params["P2"], sgbm_params["disp12MaxDiff"], sgbm_params["preFilterCap"],
            # TODO: mode=1 still needed?
            0 and sgbm_params["uniquenessRatio"], sgbm_params["speckleWindowSize"], sgbm_params["speckleRange"], mode=1)
        print("sgbm",
              sgbm_params["minDisparity"], sgbm_params["numDisparities"], sgbm_params["blockSize"],
              sgbm_params["P1"], sgbm_params["P2"], sgbm_params["disp12MaxDiff"], sgbm_params["preFilterCap"],
              0 and sgbm_params["uniquenessRatio"], sgbm_params["speckleWindowSize"], sgbm_params["speckleRange"])

    def undistort_rectify_left(self, img):
        return cv2.remap(img, self.map_left_1, self.map_left_2, cv2.INTER_LINEAR)

    def undistort_rectify_right(self, img):
        return cv2.remap(img, self.map_right_1, self.map_right_2, cv2.INTER_LINEAR)

    def crop_to_valid_region_left(self, img):
        return img[self.valid_region_left[1]: self.valid_region_left[3],
                   self.valid_region_left[0]: self.valid_region_left[2]]

    def crop_to_valid_region_right(self, img):
        return img[self.valid_region_right[1]: self.valid_region_right[3],
                   self.valid_region_right[0]: self.valid_region_right[2]]

    def compute_3d(self, img_left, img_right):
        img_left = self.undistort_rectify_left(cv2.cvtColor(img_left, cv2.COLOR_BGR2GRAY))
        img_right = self.undistort_rectify_right(cv2.cvtColor(img_right, cv2.COLOR_BGR2GRAY))
        disparity = self.stereoSGBM.compute(img_left, img_right).astype(np.float32)/16
        disp_img = (disparity - disparity.min())/(disparity.max()-disparity.min())
        img_3d = cv2.reprojectImageTo3D(disparity, self.Q)
        return (self.crop_to_valid_region_left(img_3d),
                self.crop_to_valid_region_left(disparity),
                self.crop_to_valid_region_left(img_left))

### DEPRICATED ###

# Model of a camera with intrinsic parameters
# Note xi is only for omnidirectional camera model (probably won't use for webcams)
class Camera:
    def __init__(self, fx, fy, cx, cy, dist, xi=None):
        self.fx, self.fy, self.cx, self.cy = fx, fy, cx, cy
        self.dist, self.xi = np.array(dist), np.array(xi)

    def K(self):
        return np.array([[self.fx, 0, self.cx], [0, self.fy, self.cy], [0, 0, 1]])

    # undistort img assuming it was taken with this camera
    # target_cam is a camera with the desired intrinsics (returns image as if it were taken by target_cam)
    def undistort(self, img, target_cam):
        if self.xi is None:
            return img # TODO pinhole camera model undistortion
        else:
            return cv2.omnidir.undistortImage(img, self.K(), self.dist, self.xi,
                                              cv2.omnidir.RECTIFY_PERSPECTIVE, Knew=target_cam.K())

# Intrinsics for cameras used in the TUM VI dataset (not sure about x/y order)
# Omnidirectional Model
TUM_VI_CAM_0 = Camera(533.340727445877, 533.2556495307942, 254.64689387916482, 256.4835490935692,
                     [-0.05972430882700243, 0.17468739202093328, 0.000737218969875311, 0.000574074894976456],
                      1.7921879013)
TUM_VI_CAM_1 = Camera(520.2546241208013, 520.1799003708908, 252.24978846121377, 254.15045097300418,
                      [-0.07693518083211431, 0.12590335598238764, 0.0016421936053305271, 0.0006230553630283544],
                      1.73241756065)
# Pinhole model
TUM_VI_CAM_0_PIN = Camera(190.97847715128717, 190.9733070521226, 254.9317060593547, 256.8974428996504,
                          [0.0034823894022493434, 0.0007150348452162257, -0.0020532361418706202, 0.00020293673591811182])
TUM_VI_CAM_1_PIN = Camera(190.44236969414825, 190.4344384721956, 252.59949716835982, 254.91723064636983,
                          [0.0034003170790442797, 0.001766278153469831, -0.00266312569781606, 0.0003299517423931039])

class ImageProcessingError(Exception):
    def __init__(self, message):
        self.message = message

def undistort_video(original, outname, source_cam, target_cam):
    video = cv2.VideoCapture("../data/extracted/" + original)
    success, frame = video.read()
    if not success:
        sys.exit()
    out = cv2.VideoWriter("../data/intermediates/" + outname, fourcc=cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'),
                          fps=video.get(cv2.CAP_PROP_FPS), frameSize=(frame.shape[1], frame.shape[0]))
    while success:
        out.write(source_cam.undistort(frame, target_cam))
        success, frame = video.read()
    out.release()

# Find the change in rotation and translation of a camera between two consecutive images
# Returns the rotation matrix and translation vector from the camera's pose when im0 was taken
# to the pose when im1 was taken
def find_relative_pose(cam, im0, im1):
    # hard-coding threshold for ambiguous matches for now
    ambiguous_match_thresh = 0.8
    # Detect Features
    orb = cv2.ORB_create()
    im0_kps, im0_desc = orb.detectAndCompute(im0, None)
    im1_kps, im1_desc = orb.detectAndCompute(im1, None)
    # Match Features in im1 (query) to im0 (train)
    matches = cv2.BFMatcher.create(cv2.NORM_HAMMING).knnMatch(im1_desc, im0_desc, k=2)
    matches = [m[0] for m in matches if m[0].distance < ambiguous_match_thresh * m[1].distance]
    if len(matches) < 5:
        raise ImageProcessingError("Not enough feature matches")
    im0_points = np.float32([im0_kps[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
    im1_points = np.float32([im1_kps[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
    # Find Essential Matrix and Rotation and Translation from camera pose 0 to pose 1
    E, _ = cv2.findEssentialMat(im0_points, im1_points, cam.K())
    success, R, t, _ = cv2.recoverPose(E, im0_points, im1_points, cam.K())
    if not success:
        raise ImageProcessingError("Could not recover pose")
    return R, t

# returns rectified images
# Not currently working, so currently just transforming the second image to match the perspective of the first
def rectify(cam0, cam1, im0, im1):
    # Card-coding these parameters for now (see cv2.findFundamentalMat)
    # Maximum distance of an inlier point from an epipolar line, documentation suggests 1-3
    ransacReprojThreshold = 3
    # Desired confidence that estimated matrix is correct. Can try increasing if we get
    # too many wrong values, or decreasing if this runs too slow
    confidence = 0.99

    # Detect Features
    orb = cv2.ORB_create()
    im0_kps, im0_desc = orb.detectAndCompute(im0, None)
    im1_kps, im1_desc = orb.detectAndCompute(im1, None)
    # Match Features (hard-coding 0.8 as ambiguous match threshold)
    matches = cv2.BFMatcher.create(cv2.NORM_HAMMING).knnMatch(im1_desc, im0_desc, k=2)
    matches = [m[0] for m in matches if m[0].distance < 0.8 * m[1].distance]
    if len(matches) < 8:
        raise ImageProcessingError("Not enough feature matches")
    im0_points = np.float32([im0_kps[m.trainIdx].pt for m in matches]).reshape(-1, 1, 2)
    im1_points = np.float32([im1_kps[m.queryIdx].pt for m in matches]).reshape(-1, 1, 2)
    # Find the fundamental matrix and compute rectification transforms
    F, mask = cv2.findFundamentalMat(im0_points, im1_points, cv2.FM_RANSAC, ransacReprojThreshold, confidence)
    # TODO: mask?
    success, H0, H1 = cv2.stereoRectifyUncalibrated(im0_points, im1_points, F, (im0.shape[1], im0.shape[0]))
    if not success:
        raise ImageProcessingError("could not find rectification transforms")
    print(H0)
    print(H1)
    im0_points = np.array([im0_points[i] for i in range(len(mask)) if mask[i]])
    im1_points = np.array([im1_points[i] for i in range(len(mask)) if mask[i]])
    success, H0, H1 = cv2.stereoRectifyUncalibrated(im0_points, im1_points, F, (im0.shape[1], im0.shape[0]))
    if not success:
        raise ImageProcessingError("could not find rectification transforms")
    print(H0)
    print(H1)

# undistort_video("hall0.avi", "hall0_flat.avi", TUM_VI_CAM_0, TUM_VI_CAM_0_PIN)
# undistort_video("hall1.avi", "hall1_flat.avi", TUM_VI_CAM_1, TUM_VI_CAM_1_PIN)


class Node:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# class PhoneDirectory:

#     def __init__(self, maxNumbers: int):
#         self.queue = [i for i in range(maxNumbers)]
#         self.s = set(self.queue)

#     def get(self) -> int:
#         if self.queue:
#             ans = self.queue.pop(0)
#             self.s.remove(ans)
#             return ans
#         else:
#             return -1

#     def check(self, number: int) -> bool:
#         return number in self.s

#     def release(self, number: int) -> None:
#         if number not in self.s:
#             self.s.add(number)
#             self.queue.append(number)

class PhoneDirectory:

    def __init__(self, maxNumbers):
        """
        Initialize your data structure here
        @param maxNumbers - The maximum numbers that can be stored in the phone directory.
        :type maxNumbers: int
        """
        self.head = None
        self.hsh = {val: True for val in range(maxNumbers)}
        for val in reversed(range(maxNumbers)):
            node = Node(val)
            node.next = self.head
            self.head = node

    def get(self):
        """
        Provide a number which is not assigned to anyone.
        @return - Return an available number. Return -1 if none is available.
        :rtype: int
        """
        if not self.head:
            return -1
        number = self.head.val
        self.hsh[number] = False
        self.head = self.head.next
        return number

    def check(self, number):
        """
        Check if a number is available or not.
        :type number: int
        :rtype: bool
        """
        return number in self.hsh and self.hsh[number]

    def release(self, number):
        """
        Recycle or release a number.
        :type number: int
        :rtype: void
        """
        if self.check(number):
            return
        node = Node(number)
        node.next = self.head
        self.head = node
        self.hsh[number] = True

# Your PhoneDirectory object will be instantiated and called as such:
# obj = PhoneDirectory(maxNumbers)
# param_1 = obj.get()
# param_2 = obj.check(number)
# obj.release(number)