import cv2
import numpy as np
import json

image = cv2.imread(r'C:\Users\aminb\Desktop\FIBO\Image\Moduel_image\messageImage_1604660857654.jpg')
# image = cv2.medianBlur(image,9)
# kernel = np.array([[-1,-1,-1],
#                     [-1, 9,-1],
#                     [-1,-1,-1]])
# sharpened = cv2.filter2D(image, -1, kernel)
# image=cv2.fastNlMeansDenoising(sharpened, None, 10, 2, 21)

image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

resolution_X = image.shape[0]
resolution_Y = image.shape[1]

def update(x):
    pass

def add_point_to_list(hie):
    global list_contours_symbol, list_contours_box_symbol, list_contours_path
    for i in hie:  #ADD list_contours
        Parent_check = (i[3] == 0) # check in case
        Child_check_symbol = (i[2] != -1) # Symbol
        Child_check_path = (i[2] == -1) # PATH
        if Parent_check:
            if Child_check_symbol:
                list_contours_box_symbol.append(hie.index(i))
            if Child_check_path:
                list_contours_path.append(hie.index(i))

cv2.namedWindow("image_blur")
Kernel_blur = 1
cv2.createTrackbar("Kernel_blur","image_blur",3,7,update)

cv2.namedWindow("image_edge")
Canny_Thres_1 = 40
cv2.createTrackbar("Canny_Thres_1","image_edge",Canny_Thres_1,255,update)
Canny_Thres_2 = 100
cv2.createTrackbar("Canny_Thres_2","image_edge",Canny_Thres_2,255,update)

cv2.namedWindow("image_morp")
Kernel_morp = 1
cv2.createTrackbar("Kernel_morp","image_morp",Kernel_morp,7,update)
Mode_morp = 1
cv2.createTrackbar("Mode_morp","image_morp",Mode_morp,3,update)
iterations = 1
cv2.createTrackbar("iterations","image_morp",iterations,4,update)

cv2.namedWindow("image_contour")
contour_use = []
maxArea = int(resolution_X*resolution_Y/50)
minArea = 0
cv2.createTrackbar("maxArea","image_contour",6045,20000,update)
cv2.createTrackbar("minArea","image_contour",3325,20000,update)

list_all_point_path = []
list_contours_symbol = []
list_contours_box_symbol = []
list_contours_path = []
list_point_connected = []

while(1):
    image_blur = cv2.GaussianBlur(image_gray , (Kernel_blur,Kernel_blur), 0)
    cv2.imshow("image_blur",image_blur)

    image_edge = cv2.Canny(image_blur,Canny_Thres_1,Canny_Thres_2)
    cv2.imshow("image_edge",image_edge)
    Kernel_morp_use = cv2.getStructuringElement(cv2.MORPH_CROSS, (Kernel_morp,Kernel_morp))
    # Kernel_morp_use  = cv2.GaussianBlur(image_gray , (Kernel_morp,Kernel_morp), 0)
    image_morp = cv2.dilate(image_edge,Kernel_morp_use,iterations = iterations)
    if Mode_morp == 1:
        image_morp = cv2.morphologyEx(image_morp, cv2.MORPH_CLOSE, Kernel_morp_use)
    elif Mode_morp == 2:
        image_morp = cv2.morphologyEx(image_morp, cv2.MORPH_CROSS, Kernel_morp_use)
    elif Mode_morp == 3:
        image_morp = cv2.morphologyEx(image_morp, cv2.MORPH_OPEN, Kernel_morp_use)
    cv2.imshow("image_morp",image_morp)

    contours, hierarchy = cv2.findContours(image_morp , cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    for i in range(0, len(contours)):
        area = int(cv2.contourArea(contours[i]))
        if area >= minArea:
            if area <= maxArea*100:
                contour_use.append(i)
    black_image = np.zeros((resolution_X, resolution_Y, 1), np.uint8)

    for i in contour_use:
        cv2.drawContours(black_image,contours,i,(255,255,255))
    black_image = cv2.dilate(black_image,Kernel_morp_use,iterations = iterations)
    cv2.imshow("image_contour",black_image)

    contour1, hie = cv2.findContours(black_image , cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    black_image1 = np.zeros((resolution_X, resolution_Y, 1), np.uint8)
    cv2.drawContours(black_image1,contour1,-1,(255,255,255))
    blacked = np.zeros((resolution_X, resolution_Y, 1), np.uint8)
    filled_contour = cv2.fillPoly(blacked, [contour1[1]], color=(255,255,255))
    _,filled_path_binary = cv2.threshold(filled_contour,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
    thin_image = cv2.ximgproc.thinning(filled_path_binary,thinningType=cv2.ximgproc.THINNING_ZHANGSUEN)
    cv2.imshow("thin_image",thin_image)
    # black_image1 = cv2.dilate(black_image1,Kernel_morp_use,iterations = iterations)
    cv2.imshow("image_Kuy",black_image1)
    # print(hie)
    add_point_to_list(hie.tolist()[0])
    # print(list_contours_box_symbol)
    # print("Kuy")
    print(len(contour1))

    contour_use = []
    list_contours_path = []
    list_contours_box_symbol = []
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
    Kernel_blur = ((cv2.getTrackbarPos("Kernel_blur","image_blur")+1)*2)-1

    Canny_Thres_1 = cv2.getTrackbarPos("Canny_Thres_1","image_edge")
    Canny_Thres_2 = cv2.getTrackbarPos("Canny_Thres_2","image_edge")

    Kernel_morp = ((cv2.getTrackbarPos("Kernel_morp","image_morp")+1)*2)-1
    Mode_morp = cv2.getTrackbarPos("Mode_morp","image_morp")
    iterations = cv2.getTrackbarPos("iterations","image_morp")

    minArea = cv2.getTrackbarPos("minArea","image_contour")
    maxArea = cv2.getTrackbarPos("maxArea","image_contour")

    data = {}
    data['Parameter'] = []
    data['Parameter'].append({
        'Kernel_blur': (int(((Kernel_blur+1)/2)-1)),
        'Canny_Thres_1': Canny_Thres_1,
        'Canny_Thres_2': Canny_Thres_2,
        'Kernel_morp': (int(((Kernel_morp+1)/2)-1)),
        'Mode_morp': Mode_morp,
        'iterations': iterations,
        'minArea:' : minArea,
        'maxArea': int(maxArea/100)
    })

    # data['blur'].append({
    #     'name': 'Larry',
    #     'website': 'google.com',
    #     'from': 'Michigan'
    # })
    # data['blur'].append({
    #     'name': 'Tim',
    #     'website': 'apple.com',
    #     'from': 'Alabama'
    # })

with open(r'C:\Users\aminb\Desktop\FIBO\Image\Moduel_image\parameter.json', 'w') as outfile:
    json.dump(data, outfile)


# -*- coding: utf-8 -*-
"""
Created on Mon May 29 09:33:06 2017

@author: Jaywan Chung

updated on Mon May 2017
"""

import copy
from functools import lru_cache
import unittest

class HashDict:
    """Create an immutable dict.

    Arithmetic operations (+,-,*,/) are allowed if the items in HashDict allows those operations.
    """

    _dict = {}

    def __hash__(self):
        return hash( frozenset(self._dict.items()) )

    def __eq__(self, other):
        """ self == other """
        if isinstance(other, HashDict):
            return self._dict == other._dict
        elif isinstance(other, dict):
            return self._dict == other
        else:
            return False

    def to_dict(self):
        """ return a dictionary """
        return self._dict.copy()

    def to_string(self):
        return str( self.to_dict() )

    def __repr__(self):
        return 'HashDict: ' + self.to_string()

    def __str__(self):
        return self.to_string()

    def __init__(self, a_dict):
        if isinstance(a_dict, dict):
            self._dict = a_dict.copy()
        elif isinstance(a_dict, HashDict):
            self._dict = a_dict.to_dict()
        else:
            raise TypeError('Cannot create a HashDict.')

    @lru_cache()
    def _add_hashdicts( hashdict1, hashdict2 ):
        """ Add two HashDicts and return a HashDict. """
        dict1 = hashdict1._dict
        dict2 = hashdict2._dict
        all_keys = list(dict1.keys())
        for key in dict2.keys():
            if not(key in all_keys):
                all_keys.append(key)
        added_dict = {}
        for key in all_keys:
            added_dict[key] = (dict1.get(key) or 0) + (dict2.get(key) or 0)
        return HashDict( added_dict )

    def __add__(self, other):
        """ self + other """
        try:
            other = HashDict(other)
            return HashDict._add_hashdicts( self, other )
        except TypeError: # treat as an element
            self_dict = self.to_dict()
            for key in self_dict.keys():
                try:
                    self_dict[key] += other
                except:
                    raise TypeError("Cannot be added to a HashDict.")
            return HashDict( self_dict )

    def __radd__(self, other):
        """ other + self = self + other """
        return self + other   # additive operation is commutative

    @lru_cache()
    def _mul_hashdicts( hashdict1, hashdict2 ):
        """ Multiply two dicts and return a HashDict. """
        dict1 = hashdict1._dict
        dict2 = hashdict2._dict
        mul_dict = {}
        for key in dict1.keys():
            val = dict2.get(key)
            if not(val == None):
                mul_dict[key] = dict1[key] * val
        return HashDict( mul_dict )

    def __mul__(self, other):
        """
        self * other
        if a key is contained only in one HashDict (self or other), treat its value as 0 so omit the key.
        """
        try:
            other = HashDict(other)
            return HashDict._mul_hashdicts( self, other )
        except TypeError:  # treat as an element
            self_dict = self.to_dict()
            for key in self_dict.keys():
                try:
                    self_dict[key] *= other
                except:
                    raise TypeError("Cannot be multiplied to a HashDict.")
            return HashDict( self_dict )

    def __rmul__(self, other):
        """ other * self = self * other """
        return self * other   # multiplicative operation is commutative

    def __neg__(self):
        """ -self = self * (-1) """
        return self * (-1)

    def __sub__(self, other):
        """ self - other = self + (-other) """
        return self + (-other)

    @lru_cache()
    def _div_hashdicts( hashdict1, hashdict2 ):
        """ Divide first dict by second dict and return a HashDict. """
        dict1 = hashdict1._dict
        dict2 = hashdict2._dict
        div_dict = {}
        for key in dict1.keys():
            val = dict2.get(key)
            if not(val == None):
                div_dict[key] = dict1[key] / val
        return HashDict( div_dict )

    def __truediv__(self, other):
        """
        self / other
        if a key is contained only in one HashDict (self or other), omit the key.
        """
        try:
            other = HashDict(other)
            return HashDict._div_hashdicts( self, other )
        except TypeError:  # treat as an element
            self_dict = self.to_dict()
            for key in self_dict.keys():
                try:
                    self_dict[key] /= other
                except:
                    raise TypeError("Cannot divide with a HashDict.")
            return HashDict( self_dict )

    def __rtruediv__(self, other):
        """
        other / self
        if a key is contained only in one HashDict (self or other), omit the key.
        """
        try:
            other = HashDict(other)
            return HashDict._div_hashdicts( other, self )
        except TypeError:  # treat as an element
            self_dict = self.to_dict()
            for key in self_dict.keys():
                try:
                    self_dict[key] = other / self_dict[key]
                except:
                    raise TypeError("Cannot divide with a HashDict.")
            return HashDict( self_dict )

    def __iter__(self):
        return self._dict.__iter__()

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

    def __getitem__(self, key):
        return self._dict[key]

    def items(self):
        return self._dict.items()

    def get(self, key, default=None):
        return self._dict.get(key, default)

    def keys(self):
        return self._dict.keys()

    def values(self):
        return self._dict.values()

    def drop_zeros(self):
        self_dict = self.to_dict()
        for key in self.keys():
            if self.get(key) == 0:
                self_dict.pop(key)
        return HashDict(self_dict)

    def project_to(self, keys_tuple, default_value=0):
        self_dict = self.to_dict()
        arranged_list = []
        for key in keys_tuple:
            arranged_list.append( (key, (self_dict.get(key) or default_value)) )
        return HashDict( dict(arranged_list) )

    @lru_cache()
    def divide_and_project_to( self, hashdict ):
        """
        Divide the exponents of "hashdict1" by the exponents of "hashdict2".
        Only the keys in "hashdict2" is considered. And the missing key in "hashdict1" is assumed to have the zero exponent.
        For example: {'kg':3, 's':2} / {'m':1, 's':2} = {'m':0, 's':1}
        Returns a HashDict.
        """
        usually_divided = self / hashdict
        result = usually_divided.project_to( hashdict.keys() )
        return result

    def pop(self, key, default=None):
        """
        Return a tuple consists of the corresponding item to the given key and a HashDict the key is removed from.
        REMEMBER a HashDict is immutable.
        """
        self_dict = self.to_dict()
        item = self_dict.pop(key, default)
        return (item, HashDict(self_dict))

    def copy(self):
        """ Return a shallow of copy of self. """
        return copy.copy(self)

class TestHashDict(unittest.TestCase):
    """
    A Test case for the HashDict class.
    """

    def setUp(self):
        self.dct = HashDict( {'kg':1, 'm':2, 's':-1} )
    def test__add__(self):
        dct1 = HashDict({'kg':1, 'm':2, 's':-1})
        dct2 = HashDict({'kg':-1, 'm':0, 's':-1})
        self.assertEqual(str(dct1+dct2), "{'kg': 0, 'm': 2, 's': -2}")
    def test__mul__(self):
        self.assertEqual(str(self.dct * -3), "{'kg': -3, 'm': -6, 's': 3}")
        dct1 = HashDict({'kg':1, 'm':2, 's':-1})
        dct2 = HashDict({'kg':-1, 'm':0, 's':-1})
        self.assertEqual(str(dct1 * dct2), "{'kg': -1, 'm': 0, 's': 1}")
    def test__sub__(self):
        dct1 = HashDict( {'kg':3, 's':2} )
        dct2 = HashDict( {'m':1, 's':2} )
        result = dct1 - dct2
        self.assertEqual( str(result), "{'kg': 3, 's': 0, 'm': -1}" )
    def test_drop_zeros(self):
        dct = HashDict({'kg':1, 'm':2, 's':0})
        self.assertEqual(str(dct.drop_zeros()), "{'kg': 1, 'm': 2}")
    def test_divide_and_project_to(self):
        dct1 = HashDict({'kg':3, 's':2})
        dct2 = HashDict({'m':1, 's':2})
        result = dct1.divide_and_project_to( dct2 )
        self.assertEqual( str(result), "{'m': 0, 's': 1.0}" )
    def test_pop(self):
        dct = HashDict({'kg':3, 's':2})
        (item, dic) = dct.pop('s')
        self.assertEqual( item, 2 )
        self.assertEqual( str(dic), "{'kg': 3}" )

if __name__=='__main__':
    # unit test
    suite = unittest.defaultTestLoader.loadTestsFromTestCase( TestHashDict )
    unittest.TextTestRunner().run(suite)


# time o(nlogn)
class Solution:
    def avoidFlood(self, rains):
        import heapq
        from collections import defaultdict
        h = []
        res = []
        heapq.heapify(h)
        dic = defaultdict(list)
        to_empty = []
        heapq.heapify(to_empty)
        res = [-1] * len(rains)

        for idx, rain in enumerate(rains):
            dic[rain].append(idx)

        for i , rain in enumerate(rains):
            if rain != 0:
                if dic[rain] and dic[rain][0] < i: # full
                    return []
                if dic[rain] and len(dic[rain]) > 1: # can put into waiting list
                    next_rain = dic[rain][1]
                    heapq.heappush(to_empty, next_rain)
            else: # needs to pop
                if to_empty: # can pop
                    res[i] = rains[heapq.heappop(to_empty)]
                    dic[res[i]].pop(0)
                else: # nothing to pop
                    res[i] = 1
        return res

if __name__ == "__main__":
    rains = [1,2,0,2,3,0,1]
    output = [-1,-1,2,-1,-1,1,-1]
    sol = Solution()
    print(sol.avoidFlood(rains))

    # Input:
    rains = [1,2,3,4]
    #Output: [-1,-1,-1,-1]
    sol = Solution()
    #print(sol.avoidFlood(rains))

    # Input:
    rains = [1,2,0,0,2,1]
    # Output: [-1,-1,2,1,-1,-1]
    sol = Solution()
    #print(sol.avoidFlood(rains))

    # Input:
    rains = [1,2,0,1,2]
    # Output: []
    sol = Solution()
    #print(sol.avoidFlood(rains))

    # Input:
    rains = [69,0,0,0,69]
    # Output: [-1,69,1,1,-1]
    sol = Solution()
    #print(sol.avoidFlood(rains))

    # Input:
    rains = [10,20,20]
    # Output: []
    sol = Solution()
    #print(sol.avoidFlood(rains))

Python/Contest /1488. Avoid Flood in The City.py

{'heapq': 'heapq', 'defaultdict': 'collections.defaultdict'}


#pylint: disable=C0103, C0301, E1101
"""
The utility functions related to images
"""
__author__ = "Noupin, KeeKee"

#Third Party Imports
import io
import os
import cv2
import base64
import piexif
import numpy as np
from PIL import Image
from colorama import Fore
import matplotlib.pyplot as plt
from moviepy import editor as mediaEditor
from typing import Any, Dict, List, Tuple, Generator, Union

#First Party Imports
from ..exceptions.CVToPIL import CVToPILError
from ..exceptions.IncompatibleShapes import IncompatibleShapeError

def PILToCV(image: Image.Image) -> np.ndarray:
    """
    Given a PIL image it will be converted to a CV image

    Args:
        image (PIL.Image.Image): The PIL Image to be converted to CV

    Returns:
        numpy.ndarray: The CV image
    """

    converted = np.asarray(image)

    return converted

def CVToPIL(image: np.ndarray) -> Image.Image:
    """
    Given a CV image it will be converted to a PIL image

    Args:
        image (numpy.ndarray): The CV image to be converted to PIL

    Returns:
        PIL.Image.Image: The PIL image
    """

    if isinstance(image, Image.Image):
        try:
            raise CVToPILError(image)
        except CVToPILError as error:
            print(Fore.RED + error + Fore.RESET)
            print(Fore.GREEN + "Conversion skipped." + Fore.RESET)
            return image

    converted = Image.fromarray(image)

    return converted

def loadImage(path: str) -> np.ndarray:
    """
    Given a path the image is loaded and converted to the RGB color space

    Args:
        path (str): The path to the image to be loaded

    Returns:
        numpy.ndarray: The RGB CV image
    """

    image = cv2.imread(path)
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    return image

def loadImageFolder(path: str, maximum=-1) -> Generator[np.ndarray, None, None]:
    """
    Loads in a folder of images.

    Args:
        path (str): The path to the folder with the images.
        maximum (int): The maximum number of images to grab from the folder. Defaults to -1.

    Yields:
        Generator of np.ndarray: The images from the folder path given
    """

    for index, imageName in enumerate(os.listdir(path)):
        if index == maximum:
            break

        image = loadImage(os.path.join(path, imageName))

        yield image

def saveImage(image: Image.Image, path: str) -> None:
    """
    Saves the image to the given path

    Args:
        image (PIL.Image.Image): The image to be saved
        path (str): The path to where the file is saved
    """

    _, extenstion = os.path.splitext(path)
    if extenstion.find('.') != -1:
        image.save(path)
    else:
        image.save(f"{path}.png")
        os.rename(f"{path}.png", path)

def resizeImage(image: np.ndarray, size: Tuple[int], keepAR=False, maxDim: int=None,
                interpolation=cv2.INTER_AREA) -> np.ndarray:
    """
    Given a cv2 image it is resized to the given size

    Args:
        image (numpy.ndarray): The image object to be resized
        size (tuple of int): A tuple of the desired x(width) and y(height) dimension
        keepAR (bool, optional): Whether or not to keep the aspect ratio of the
                       image. Defaults to False.
        maxDim (int, optional): The maximum size of either dimension to resize. Defaults to None.
        interpolation (int, optional): The type of image interpolation to downscale the image.
                                       Defaults to cv2.INTER_AREA.

    Returns:
        numpy.ndarray: The resized CV image
    """

    resized = PILToCV(image)

    if keepAR:
        height, width = image.shape[:2]

        if not maxDim and size[0] is None:
            ratio = size[1] / float(height)
            dimensions = (int(width * ratio), size[1])
        elif not maxDim and size[1] is None:
            ratio = size[0] / float(width)
            dimensions = (size[0], int(height * ratio))
        elif maxDim and height >= width and height > maxDim:
            ratio = maxDim / float(height)
            dimensions = (int(width * ratio), maxDim)
        elif maxDim and width >= height and width > maxDim:
            ratio = maxDim / float(width)
            dimensions = (maxDim, int(height * ratio))
        else:
            dimensions = (width, height)

        resized = cv2.resize(resized, dimensions, interpolation=interpolation)
    else:
        resized = cv2.resize(resized, size, interpolation=interpolation)

    return resized

def cropImage(image: np.ndarray, cropArea: Tuple[int]) -> np.ndarray:
    """
    Crops the crop area out of image

    Args:
        image (numpy.ndarray): The image to be cropped
        cropArea (tuple of int): The x, y, width and height values of
                                 the rectange to be cropped from image

    Returns:
        numpy.ndarray: The cropped portion of the image
    """

    x, y, width, height = cropArea
    cropped = image[int(y):int(y+height), int(x):int(x+width)]

    return cropped

def replaceAreaOfImage(fullImage: np.ndarray, replaceArea: Tuple[int], replaceImage: np.ndarray) -> np.ndarray:
    """
    Replaces a certain area of an image with another image. Assuming that
    the size of the replace image fits the dimensions of replaceArea.

    Args:
        fullImage (np.ndarray): The image to have an area replaced within
        replaceArea (Tuple[int]): The x, y, width and height values of the
                                  area to replace fullImage with replaceImage
        replaceImage (np.ndarray): The image to replace the replaceArea of fullImage

    Returns:
        np.ndarray: The image with replace area replaced by replaceImage
    """

    x, y, width, height = replaceArea
    fullImage[int(y):int(y+height), int(x):int(x+width)] = replaceImage

    return fullImage

def viewImage(image, **kwargs) -> None:
    """
    Given a CV image or a PIL image it will be previewed using matplotlib

    Args:
        image (PIL.Image.Image or numpy.ndarray): The image to be displayed
        **kwargs: Arguments to be applied to matplotlib.pyplot.imshow
    """

    plt.imshow(image, **kwargs)
    plt.show()

def encodeImage(image: Image.Image) -> str:
    """
    Takes in an image array then encodes it as a bytestring to be streamed through JSON to JavaScript.

    Args:
        image (numpy.ndarray): The image to be converted to binary string

    Returns:
        str: The binary encoding of the image
    """

    buffered = io.BytesIO()
    image.save(buffered, format="PNG")
    imageString = base64.b64encode(buffered.getvalue())
    encodedImage = imageString.decode('utf-8')

    return encodedImage

def decodeImage(encodedImage: str) -> np.ndarray:
    """
    Given an encoded image as a binary string it will be decoded into a CV image

    Args:
        encodedImage (str): The encoded image binary string

    Returns:
        numpy.ndarray: The decoded image
    """

    decodedImage = base64.b64decode(encodedImage)
    image = Image.open(io.BytesIO(decodedImage))

    return image

def blendImageAndColor(image, colorCode: int) -> np.ndarray:
    """
    Given an input image an augmented image is returned.

    Args:
        image (PIL.Image.Image or numpy.ndarray): The image to be masked by a given color
        colorCode (int): The corresponding integer to the colorMasks values

    Returns:
        numpy.ndarray: The color masked image
    """

    colorMasks = {0: (255, 0, 0), #Red
                  1: (0, 255, 0), #Green
                  2: (0, 0, 255), #Blue
                  3: (0, 0, 0), #Black
                  4: (255, 255, 255)} # White
    alphaLevels = {0: 0.225, #Red alpha level
                   1: 0.225, #Green alpha level
                   2: 0.2, #Blue alpha level
                   3: 0.4, #Black alpha level
                   4: 0.4} #White alpha level

    image = CVToPIL(image)
    colorImage = Image.new("RGB", image.size,
                           colorMasks[colorCode])
    augmenetedImage = Image.blend(image, colorImage, alphaLevels[colorCode])

    image = PILToCV(augmenetedImage)

    return image

def flipImage(image, flipCode: str) -> np.ndarray:
    """
    The image will be flipped by the corresponding flip code.

    Args:
        image (PIL.Image.Image or numpy.ndarray): [description]
        flipCode (str): The corresponding string to the flip values

    Returns:
        numpy.ndarray: The flipped image
    """

    flipMap = {"x": 0,
               "y": 1,
               "xy": -1,
               "yx": -1}
    image = PILToCV(image)

    return cv2.flip(image, flipMap[flipCode])

def imagesToVideo(images: Generator[np.ndarray, None, None], shape: Tuple[int], outPath: str, fps: float) -> mediaEditor.VideoFileClip:
    """
    Given a path with all of the image arrays a .mp4 video will be exported

    Args:
        images (generator of np.ndarray): An array of CV images
        shape (tuple of int): The height, width and color dimension of the frames to converted to a video.
        outPath (str): The path to save the video to
        fps (float): The fps at which to combine the video at
    """

    height, width, _ = shape

    codec = cv2.VideoWriter_fourcc(*'H264') #H264, X264, MP42, MP4A
    videoWrite = cv2.VideoWriter(outPath,
                                 codec,
                                 fps,
                                 (width, height))

    for image in images:
        if isinstance(image, np.ndarray) and image.shape != shape:
            raise IncompatibleShapeError(image.shape, shape)

        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        videoWrite.write(image)

    videoWrite.release()
    cv2.destroyAllWindows()

    video = mediaEditor.VideoFileClip(outPath)

    return video

def createMask(image: np.ndarray, **kwargs) -> np.ndarray:
    """
    Given an image a black and white mask will be generated

    Args:
        image (np.ndarray): [description]
        kwargs: [desc]

    Returns:
        np.ndarray: [description]
    """

    try:
        kwargs["gray"]
    except KeyError:
        kwargs["gray"] = False

    if kwargs["gray"]:
        del kwargs["gray"]
        grayImage = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    else:
        grayImage = image.copy()

    _, thresh = cv2.threshold(grayImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

    kernel = np.ones((3, 3), np.uint8)
    closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)

    distTransform = cv2.distanceTransform(closing, cv2.DIST_L2, 0)
    _, foreground = cv2.threshold(distTransform, 0.2 * distTransform.max(), 255, 0)

    foreground = foreground.astype(np.uint8)
    background = cv2.bitwise_not(foreground)

    mask = cv2.bitwise_and(image, image, mask=foreground)

    return mask

def maskImage(baseImage: np.ndarray, maskImage: np.ndarray, **kwargs) -> np.ndarray:
    """
    Given a base image a mask area will be generated and the portion of the mask image will be

    Args:
        baseImage (np.ndarray): [description]
        maskImage (np.ndarray): [description]

    Returns:
        np.ndarray: [description]
    """

    try:
        kwargs["gray"]
    except KeyError:
        kwargs["gray"] = False

    if kwargs["gray"]:
        del kwargs["gray"]
        grayBaseImage = cv2.cvtColor(baseImage, cv2.COLOR_RGB2GRAY)

    _, thresh = cv2.threshold(grayBaseImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

    kernel = np.ones((3, 3), np.uint8)
    closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)

    distTransform = cv2.distanceTransform(closing, cv2.DIST_L2, 0)
    _, foreground = cv2.threshold(distTransform, 0.2 * distTransform.max(), 255, 0)

    foreground = foreground.astype(np.uint8)
    background = cv2.bitwise_not(foreground)

    baseImage = cv2.bitwise_and(baseImage, baseImage, mask=foreground)
    maskImage = cv2.bitwise_and(maskImage, maskImage, mask=background)

    maskedImage = cv2.add(maskImage, baseImage)

    return maskedImage

def drawPolygon(image: np.ndarray, points: List[List[int]], color=(255, 255, 255), mask=False) -> np.ndarray:
    """
    Connects the dots and fills the newly made polygon with color. If needed
    the background can be set to black and a masking image can be returned.

    Args:
        image (np.ndarray): The image to draw the polygon ontop of.
        points (List[List[int]]): The points to connect together.
        color (tuple, optional): The color to fill the polygon. Defaults to (255, 255, 255).
        mask (bool, optional): Whether the image should be converted to a mask. Defaults to False.

    Returns:
        np.ndarray: The polygon drawn ontop of image or the drawn mask.
    """

    polyImage = image.copy()
    if mask:
        polyImage = np.zeros(image.shape[0:2], dtype=np.uint8)

    cv2.fillPoly(polyImage, pts=[points], color=color)

    return polyImage

def applyMask(baseImage: np.ndarray, maskImage: np.ndarray, mask: np.ndarray, dtype=cv2.CV_8U) -> np.ndarray:
    """
    Applies mask to baseImage and overlays maskImage in the correspodning area.

    Args:
        baseImage (np.ndarray): The image to have the mask applied to.
        maskImage (np.ndarray): The image to apply as a mask to the base image.
        mask (np.ndarray): The black and white bitwise mask to be applied.
        dtype (int, optional): The datatype of baseImage and maskImage. Defaults to cv2.CV_8U.

    Returns:
        np.ndarray: An output of maskImage overlayed on baseImage in using the area from mask
    """

    inverseMask = cv2.bitwise_not(mask)

    base = cv2.bitwise_and(baseImage, baseImage, mask=inverseMask)
    mask = cv2.bitwise_and(maskImage, maskImage, mask=mask)

    maskedImage = cv2.add(base, mask, dtype=dtype)

    return maskedImage

def compressImage(image: Image.Image, quality=65) -> Image.Image:
    """
    Compresses the fiven image to preserve fidelity and decrease file size.

    Args:
        image (PIL.Image.Image): The image to be compressed
        quality (int, optional): The quality of the compression from 0-100. Defaults to 65.

    Returns:
        Image.Image: The compressed image.
    """

    compressed = image.convert("RGB")

    buffered = io.BytesIO()
    compressed.save(buffered, format="JPEG", optimize=True, quality=quality)
    buffered.seek(0)

    return Image.open(buffered)

def imageFilesize(image: Image.Image) -> int:
    """
    Gets the filesize of the image in bytes.

    Args:
        image (PIL.Image.Image): The image to be measured.

    Returns:
        int: The size of the image in bytes.
    """

    buffered = io.BytesIO()
    image.save(buffered, format="PNG")

    return buffered.tell()

def editImageMetadata(image: Image.Image, key: Union[str, int],
                      value: Dict[str, Any]) -> Image.Image:
    """
    Edits the metadata of a PIL image.

    Args:
        image (PIL.Image.Image): The image to edit the metadata of.
        key (Union[str, int]): The key to add to the metadata.
        value (Union[str, int]): The value to associate with the key in the metadata.

    Returns:
        PIL.Image.Image: The edited metadata.
    """

    exifDict = image.getexif()

    if exifDict is None:
        exifDict = {}

    exifDict[key] = value
    exifBytes = piexif.dump(exifDict)

    buffered = io.BytesIO()
    image.save(buffered, format="PNG", exif=exifBytes)
    buffered.seek(0)

    return Image.open(buffered)


import errno
import os
import tempfile
import traceback
import zmq

from circus import logger
from circus.exc import AlreadyExist, MessageError
from circus.sighandler import SysHandler
from circus.show import Show

class Controller(object):
    def __init__(self, context, endpoint, trainer, timeout=1.0):
        self.context = context
        self.skt = self.context.socket(zmq.ROUTER)
        self.skt.bind(endpoint)
        self.poller = zmq.Poller()
        self.poller.register(self.skt, zmq.POLLIN)

        self.trainer = trainer
        self.timeout = timeout * 1000

        # start the sys handler
        self.sys_hdl = SysHandler(trainer)

    def send(self, msg):
        self.skt.send(msg)

    def poll(self):
        while True:
            try:
                events = dict(self.poller.poll(self.timeout))
            except zmq.ZMQError as e:
                if e.errno == errno.EINTR:
                    continue
                else:
                    return
            else:
                break

        for client in events:
            _id = client.recv()
            msg = client.recv() or ""
            msg = msg.strip()
            if not msg:
                self.send_response(_id, client, msg,
                        "error: empty command")
                continue

            msg_parts = msg.split(" ")
            resp = ""
            handler = None
            try:
                cmd, inst, args = self.parse_message(msg_parts)
                handler = getattr(inst, "handle_%s" % cmd)
            except MessageError as e:
                resp = "error: %s" % str(e)
            except AttributeError as e:
                resp = "error: message %r" % msg

            if handler is not None:
                ## hacky part we should be abble to handlle the response
                ## before the controller stop
                if cmd in ('stop', 'quit', 'terminate') and \
                        inst == self.trainer:
                    self.send_response(_id, client, msg, "ok")

                try:
                    resp = handler(*args)
                except OSError as e:
                    resp = "error: %s" % e

                except Exception as e:
                    tb = traceback.format_exc()
                    resp = "error: command %r: %s [%s]" % (msg, str(e), tb)

            if resp is None:
                continue

            if not isinstance(resp, (str, buffer,)):
                msg = "msg %r tried to send a non-string: %s" % (msg,
                        str(resp))
                raise ValueError(msg)

            self.send_response(_id, client, msg, resp)

    def send_response(self, client_id, sock, msg, resp):
        try:
            sock.send(client_id, zmq.SNDMORE)
            sock.send(resp)
        except zmq.ZMQError as e:
            logger.error("Received %r - Could not send back %r - %s" %
                                (msg, resp, str(e)))

    def _get_show(self, show_name):
        try:
            return self.trainer.get_show(show_name)
        except KeyError:
            raise MessageError("program %s not found" % show_name)

    def parse_message(self, msg_parts):
        cmd = msg_parts[0].lower()
        args = []
        inst = self.trainer

        if len(msg_parts) > 1 and msg_parts[1]:
            show_name = msg_parts[1].lower()
            if cmd == "add_show":
                if len(msg_parts) < 3:
                    raise MessageError("invalid number of parameters")

                show_cmd = " ".join(msg_parts[2:])
                args = [show_name, show_cmd]
            elif cmd == "del_show":
                self._get_show(show_name)
                args = [show_name]
            else:
                inst = self._get_show(show_name)
                if len(msg_parts) > 2:
                    args = msg_parts[2:]

        return cmd, inst, args


import wikipedia
from nltk import sent_tokenize
import json

def clean_paragraph(para):
    para = para.replace("\\'", "'")
    para = re.sub(r'\[[^]]*\]|\([^)]*\)', '', para)

    return para

def get_random_title():
    return wikipedia.random()

def get_page_summary(title):
    print(title)
    try:
        summ = wikipedia.summary(title)
        if "\\u" in summ:
            return ""
        return summ
    except:
        print("Disambiguation error in {}".format(title))
        return ""

def main():
    pages = []
    for i in range(5000):
        summary = get_page_summary(get_random_title())
        sentences = sent_tokenize(summary)
        pages.append(sentences)

    out = json.dumps(pages)
    with open("sentence_dump.txt", "a") as f:
        f.write(out)

# with open("sentence_dump.txt", "r") as f:
#     pages = json.loads(f.read())

main()


import CanvasAPI
import logging

def get_course(course_id):
    course = CanvasAPI.courses.get(course_id)
    CanvasAPI.util.printer.print_odict(course)

if __name__ == "__main__":
    for row in CanvasAPI.util.file.get_data(["course_id"]):
        try:
            get_course(row[0])
        except Exception as e:
            logging.warning("Error with '{}': {}".format(", ".join(row), e))
            continue
    input("Press enter to close...")


# -*- coding: utf-8 -*-
from setuptools import setup, find_packages

with open('requirements.txt') as f:
	install_requires = f.read().strip().split('\n')

# get version from __version__ variable in token_system/__init__.py
from token_system import __version__ as version

setup(
	name='token_system',
	version=version,
	description='Generating Tokens.',
	author='Monish Lalchandani',
	author_email='lmonish7108@gmail.com',
	packages=find_packages(),
	zip_safe=False,
	include_package_data=True,
	install_requires=install_requires
)


from neurolight.tensorflow.add_loss import create_custom_loss
from neurolight.gunpowder.nodes import (
    TopologicalMatcher,
    RasterizeSkeleton,
    GetNeuronPair,
    FusionAugment,
    GrowLabels,
)
import gunpowder as gp
import numpy as np

import math
from pathlib import Path
import copy
import logging

logger = logging.getLogger(__file__)

class BinarizeGt(gp.BatchFilter):
    def __init__(self, gt, gt_binary):

        self.gt = gt
        self.gt_binary = gt_binary

    def setup(self):

        spec = self.spec[self.gt].copy()
        spec.dtype = np.uint8
        self.provides(self.gt_binary, spec)

    def prepare(self, request):
        pass

    def process(self, batch, request):

        spec = batch[self.gt].spec.copy()
        spec.dtype = np.int32

        binarized = gp.Array(data=(batch[self.gt].data > 0).astype(np.int32), spec=spec)

        batch[self.gt_binary] = binarized

class Crop(gp.BatchFilter):
    def __init__(self, input_array: gp.ArrayKey, output_array: gp.ArrayKey):

        self.input_array = input_array
        self.output_array = output_array

    def setup(self):

        spec = self.spec[self.input_array].copy()
        self.provides(self.output_array, spec)

    def prepare(self, request):
        pass

    def process(self, batch, request):
        input_data = batch[self.input_array].data
        input_spec = batch[self.input_array].spec
        input_roi = input_spec.roi
        output_roi_shape = request[self.output_array].roi.get_shape()
        shift = (input_roi.get_shape() - output_roi_shape) / 2
        output_roi = gp.Roi(shift, output_roi_shape)
        output_data = input_data[
            tuple(
                map(
                    slice,
                    output_roi.get_begin() / input_spec.voxel_size,
                    output_roi.get_end() / input_spec.voxel_size,
                )
            )
        ]
        output_spec = copy.deepcopy(input_spec)
        output_spec.roi = output_roi

        output_array = gp.Array(output_data, output_spec)

        batch[self.output_array] = output_array

class RejectIfEmpty(gp.BatchFilter):
    def __init__(self, ensure_nonempty: gp.ArrayKey, request_limit: int = 100):
        self.ensure_nonempty = ensure_nonempty
        self.request_limit = request_limit

    def setup(self):
        self.enable_autoskip()
        self.updates(
            self.ensure_nonempty, copy.deepcopy(self.spec[self.ensure_nonempty])
        )

    def prepare(self, request):
        return copy.deepcopy(request)

    def process(self, batch, request):

        k = 0
        found_valid = False
        while (
            not found_valid
            and k < self.request_limit
            and self.ensure_nonempty in request
        ):
            batch = self.get_upstream_provider().request_batch(request)
            if len(batch[self.ensure_nonempty].graph.nodes) > 0:
                found_valid = True
            k += 1

        return batch

def train_fusion_pipeline(
    n_iterations,
    setup_config,
    emb_mknet_tensor_names,
    emb_loss_tensor_names,
    fg_mknet_tensor_names,
):
    input_shape = gp.Coordinate(setup_config["INPUT_SHAPE"])
    output_shape = gp.Coordinate(setup_config["OUTPUT_SHAPE"])
    voxel_size = gp.Coordinate(setup_config["VOXEL_SIZE"])
    shift_attempts = setup_config["SHIFT_ATTEMPTS"]
    request_attempts = setup_config["REQUEST_ATTEMPTS"]
    num_iterations = setup_config["NUM_ITERATIONS"]
    cache_size = setup_config["CACHE_SIZE"]
    num_workers = setup_config["NUM_WORKERS"]
    snapshot_every = setup_config["SNAPSHOT_EVERY"]
    checkpoint_every = setup_config["CHECKPOINT_EVERY"]
    profile_every = setup_config["PROFILE_EVERY"]
    seperate_by = setup_config["SEPERATE_BY"]
    gap_crossing_dist = setup_config["GAP_CROSSING_DIST"]
    match_distance_threshold = setup_config["MATCH_DISTANCE_THRESHOLD"]
    point_balance_radius = setup_config["POINT_BALANCE_RADIUS"]
    neuron_radius = setup_config["NEURON_RADIUS"]
    blend_smoothness = setup_config["BLEND_SMOOTHNESS"]
    foreground_net_checkpoint = setup_config["FOREGROUND_NET_CHECKPOINT"]
    raw_n5 = setup_config["RAW_N5"]

    samples_path = Path(setup_config["SAMPLES_PATH"])
    mongo_url = setup_config["MONGO_URL"]

    input_size = input_shape * voxel_size
    output_size = output_shape * voxel_size
    # voxels have size ~= 1 micron on z axis
    # use this value to scale anything that depends on world unit distance
    micron_scale = voxel_size[0]
    seperate_distance = (np.array(seperate_by)).tolist()

    # array keys for data sources
    raw = gp.ArrayKey("RAW")
    consensus = gp.PointsKey("CONSENSUS")
    skeletonization = gp.PointsKey("SKELETONIZATION")
    matched = gp.PointsKey("MATCHED")
    nonempty_placeholder = gp.PointsKey("NONEMPTY")
    labels = gp.ArrayKey("LABELS")

    # array keys for base volume
    raw_base = gp.ArrayKey("RAW_BASE")
    labels_base = gp.ArrayKey("LABELS_BASE")
    matched_base = gp.PointsKey("MATCHED_BASE")
    skeletonization_base = gp.PointsKey("SKELETONIZATION_BASE")
    consensus_base = gp.PointsKey("CONSENSUS_BASE")

    # array keys for add volume
    raw_add = gp.ArrayKey("RAW_ADD")
    labels_add = gp.ArrayKey("LABELS_ADD")
    matched_add = gp.PointsKey("MATCHED_ADD")
    skeletonization_add = gp.PointsKey("SKELETONIZATION_ADD")
    consensus_add = gp.PointsKey("CONSENSUS_ADD")

    # array keys for fused volume
    raw_fused = gp.ArrayKey("RAW_FUSED")
    labels_fused = gp.ArrayKey("LABELS_FUSED")
    matched_fused = gp.PointsKey("MATCHED_FUSED")
    labels_fg = gp.ArrayKey("LABELS_FG")
    labels_fg_bin = gp.ArrayKey("LABELS_FG_BIN")
    loss_weights = gp.ArrayKey("LOSS_WEIGHTS")

    # debug array keys
    soft_mask = gp.ArrayKey("SOFT_MASK")
    masked_base = gp.ArrayKey("MASKED_BASE")
    masked_add = gp.ArrayKey("MASKED_ADD")
    mask_maxed = gp.ArrayKey("MASK_MAXED")

    # tensorflow tensors
    gt_fg = gp.ArrayKey("GT_FG")
    fg_pred = gp.ArrayKey("FG_PRED")
    embedding = gp.ArrayKey("EMBEDDING")
    fg = gp.ArrayKey("FG")
    maxima = gp.ArrayKey("MAXIMA")
    gradient_embedding = gp.ArrayKey("GRADIENT_EMBEDDING")
    gradient_fg = gp.ArrayKey("GRADIENT_FG")
    emst = gp.ArrayKey("EMST")
    edges_u = gp.ArrayKey("EDGES_U")
    edges_v = gp.ArrayKey("EDGES_V")
    ratio_pos = gp.ArrayKey("RATIO_POS")
    ratio_neg = gp.ArrayKey("RATIO_NEG")
    dist = gp.ArrayKey("DIST")
    num_pos_pairs = gp.ArrayKey("NUM_POS")
    num_neg_pairs = gp.ArrayKey("NUM_NEG")

    # add request
    request = gp.BatchRequest()
    request.add(raw_fused, input_size)
    request.add(labels_fused, input_size)
    request.add(matched_fused, input_size)
    request.add(labels_fg, output_size)
    request.add(gt_fg, output_size)
    request.add(loss_weights, output_size)

    # add snapshot request
    snapshot_request = gp.BatchRequest()
    request.add(labels_fg, output_size)
    request.add(raw_base, input_size)
    request.add(raw_add, input_size)
    request.add(labels_base, input_size)
    request.add(labels_add, input_size)
    request.add(matched_base, input_size)
    request.add(matched_add, input_size)
    request.add(skeletonization_base, input_size)
    request.add(skeletonization_add, input_size)
    request.add(consensus_base, input_size)
    request.add(consensus_add, input_size)

    # add debug requests
    snapshot_request.add(soft_mask, input_size, voxel_size=voxel_size)
    snapshot_request.add(masked_base, input_size, voxel_size=voxel_size)
    snapshot_request.add(masked_add, input_size, voxel_size=voxel_size)
    snapshot_request.add(mask_maxed, input_size, voxel_size=voxel_size)

    # tensorflow requests
    snapshot_request.add(raw_fused, input_size)  # input_size request for positioning
    snapshot_request.add(embedding, output_size, voxel_size=voxel_size)
    snapshot_request.add(fg, output_size, voxel_size=voxel_size)
    snapshot_request.add(gt_fg, output_size, voxel_size=voxel_size)
    snapshot_request.add(fg_pred, output_size, voxel_size=voxel_size)
    snapshot_request.add(maxima, output_size, voxel_size=voxel_size)
    snapshot_request.add(gradient_embedding, output_size, voxel_size=voxel_size)
    snapshot_request.add(gradient_fg, output_size, voxel_size=voxel_size)
    snapshot_request[emst] = gp.ArraySpec()
    snapshot_request[edges_u] = gp.ArraySpec()
    snapshot_request[edges_v] = gp.ArraySpec()
    snapshot_request[ratio_pos] = gp.ArraySpec()
    snapshot_request[ratio_neg] = gp.ArraySpec()
    snapshot_request[dist] = gp.ArraySpec()
    snapshot_request[num_pos_pairs] = gp.ArraySpec()
    snapshot_request[num_neg_pairs] = gp.ArraySpec()

    data_sources = tuple(
        (
            gp.N5Source(
                filename=str((sample / raw_n5).absolute()),
                datasets={raw: "volume"},
                array_specs={
                    raw: gp.ArraySpec(
                        interpolatable=True, voxel_size=voxel_size, dtype=np.uint16
                    )
                },
            ),
            gp.DaisyGraphProvider(
                f"mouselight-{sample.name}-consensus",
                mongo_url,
                points=[consensus, nonempty_placeholder],
                directed=True,
                node_attrs=[],
                edge_attrs=[],
            ),
            gp.DaisyGraphProvider(
                f"mouselight-{sample.name}-skeletonization",
                mongo_url,
                points=[skeletonization],
                directed=False,
                node_attrs=[],
                edge_attrs=[],
            ),
        )
        + gp.MergeProvider()
        + gp.RandomLocation(
            ensure_nonempty=nonempty_placeholder,
            ensure_centered=True,
            point_balance_radius=point_balance_radius * micron_scale,
        )
        + TopologicalMatcher(
            skeletonization,
            consensus,
            matched,
            failures=Path("matching_failures_slow"),
            match_distance_threshold=match_distance_threshold * micron_scale,
            max_gap_crossing=gap_crossing_dist * micron_scale,
            try_complete=False,
            use_gurobi=True,
        )
        + RasterizeSkeleton(
            points=matched,
            array=labels,
            array_spec=gp.ArraySpec(
                interpolatable=False, voxel_size=voxel_size, dtype=np.uint32
            ),
        )
        + GrowLabels(labels, radii=[neuron_radius * micron_scale])
        # TODO: Do these need to be scaled by world units?
        + gp.ElasticAugment(
            [40, 10, 10],
            [0.25, 1, 1],
            [0, math.pi / 2.0],
            subsample=4,
            use_fast_points_transform=True,
            recompute_missing_points=False,
        )
        # + gp.SimpleAugment(mirror_only=[1, 2], transpose_only=[1, 2])
        + gp.Normalize(raw) + gp.IntensityAugment(raw, 0.9, 1.1, -0.001, 0.001)
        for sample in samples_path.iterdir()
        if sample.name in ("2018-07-02", "2018-08-01")
    )

    pipeline = (
        data_sources
        + gp.RandomProvider()
        + GetNeuronPair(
            matched,
            raw,
            labels,
            (matched_base, matched_add),
            (raw_base, raw_add),
            (labels_base, labels_add),
            output_shape=output_size,
            seperate_by=seperate_distance,
            shift_attempts=shift_attempts,
            request_attempts=request_attempts,
            nonempty_placeholder=nonempty_placeholder,
            extra_keys={
                skeletonization: (skeletonization_base, skeletonization_add),
                consensus: (consensus_base, consensus_add),
            },
        )
        + FusionAugment(
            raw_base,
            raw_add,
            labels_base,
            labels_add,
            matched_base,
            matched_add,
            raw_fused,
            labels_fused,
            matched_fused,
            soft_mask=soft_mask,
            masked_base=masked_base,
            masked_add=masked_add,
            mask_maxed=mask_maxed,
            blend_mode="labels_mask",
            blend_smoothness=blend_smoothness * micron_scale,
            gaussian_smooth_mode="mirror",
            num_blended_objects=0,
        )
        + Crop(labels_fused, labels_fg)
        + BinarizeGt(labels_fg, gt_fg)
        + gp.BalanceLabels(gt_fg, loss_weights)
        + gp.PreCache(cache_size=cache_size, num_workers=num_workers)
        + gp.tensorflow.Predict(
            foreground_net_checkpoint,
            graph=f"{foreground_net_checkpoint}.meta",
            inputs={fg_mknet_tensor_names["raw"]: raw_fused},
            outputs={fg_mknet_tensor_names["fg_pred"]: fg_pred},
        )
        + gp.tensorflow.Train(
            "embedding_net",
            optimizer=create_custom_loss(emb_mknet_tensor_names, setup_config),
            loss=None,
            inputs={
                emb_mknet_tensor_names["loss_weights"]: loss_weights,
                emb_mknet_tensor_names["raw"]: raw_fused,
                emb_mknet_tensor_names["gt_labels"]: labels_fg,
                emb_mknet_tensor_names["fg_pred"]: fg_pred,
            },
            outputs={
                emb_mknet_tensor_names["embedding"]: embedding,
                emb_loss_tensor_names["maxima"]: maxima,
                emb_loss_tensor_names["emst"]: emst,
                emb_loss_tensor_names["edges_u"]: edges_u,
                emb_loss_tensor_names["edges_v"]: edges_v,
                emb_loss_tensor_names["ratio_pos"]: ratio_pos,
                emb_loss_tensor_names["ratio_neg"]: ratio_neg,
                emb_loss_tensor_names["dist"]: dist,
                emb_loss_tensor_names["num_pos_pairs"]: num_pos_pairs,
                emb_loss_tensor_names["num_neg_pairs"]: num_neg_pairs,
            },
            gradients={
                emb_mknet_tensor_names["embedding"]: gradient_embedding,
            },
            save_every=checkpoint_every,
            summary="Merge/MergeSummary:0",
            log_dir="tensorflow_logs",
        )
        + gp.PrintProfilingStats(every=profile_every)
        + gp.Snapshot(
            additional_request=snapshot_request,
            output_filename="snapshot_{}_{}.hdf".format(
                int(np.min(seperate_distance)), "{id}"
            ),
            dataset_names={
                # raw data
                raw_fused: "volumes/raw_fused",
                raw_base: "volumes/raw_base",
                raw_add: "volumes/raw_add",
                # labeled data
                labels_fused: "volumes/labels_fused",
                labels_base: "volumes/labels_base",
                labels_add: "volumes/labels_add",
                # trees
                skeletonization_base: "points/skeletonization_base",
                skeletonization_add: "points/skeletonization_add",
                consensus_base: "points/consensus_base",
                consensus_add: "points/consensus_add",
                matched_base: "points/matched_base",
                matched_add: "points/matched_add",
                matched_fused: "points/matched_fused",
                # output volumes
                embedding: "volumes/embedding",
                fg: "volumes/fg",
                maxima: "volumes/maxima",
                gt_fg: "volumes/gt_fg",
                fg_pred: "volumes/fg_pred",
                gradient_embedding: "volumes/gradient_embedding",
                # output trees
                emst: "emst",
                edges_u: "edges_u",
                edges_v: "edges_v",
                # output debug data
                ratio_pos: "ratio_pos",
                ratio_neg: "ratio_neg",
                dist: "dist",
                num_pos_pairs: "num_pos_pairs",
                num_neg_pairs: "num_neg_pairs",
                # debug volumes
                soft_mask: "volumes/soft_mask",
                masked_base: "volumes/masked_base",
                masked_add: "volumes/masked_add",
                mask_maxed: "volumes/mask_maxed",
                loss_weights: "volumes/loss_weights",
            },
            every=snapshot_every,
        )
    )

    with gp.build(pipeline):
        for _ in range(num_iterations):
            pipeline.request_batch(request)

def train_simple_pipeline(
    n_iterations, setup_config, mknet_tensor_names, loss_tensor_names
):
    input_shape = gp.Coordinate(setup_config["INPUT_SHAPE"])
    output_shape = gp.Coordinate(setup_config["OUTPUT_SHAPE"])
    voxel_size = gp.Coordinate(setup_config["VOXEL_SIZE"])
    num_iterations = setup_config["NUM_ITERATIONS"]
    cache_size = setup_config["CACHE_SIZE"]
    num_workers = setup_config["NUM_WORKERS"]
    snapshot_every = setup_config["SNAPSHOT_EVERY"]
    checkpoint_every = setup_config["CHECKPOINT_EVERY"]
    profile_every = setup_config["PROFILE_EVERY"]
    seperate_by = setup_config["SEPERATE_BY"]
    gap_crossing_dist = setup_config["GAP_CROSSING_DIST"]
    match_distance_threshold = setup_config["MATCH_DISTANCE_THRESHOLD"]
    point_balance_radius = setup_config["POINT_BALANCE_RADIUS"]
    neuron_radius = setup_config["NEURON_RADIUS"]

    samples_path = Path(setup_config["SAMPLES_PATH"])
    mongo_url = setup_config["MONGO_URL"]

    input_size = input_shape * voxel_size
    output_size = output_shape * voxel_size
    # voxels have size ~= 1 micron on z axis
    # use this value to scale anything that depends on world unit distance
    micron_scale = voxel_size[0]
    seperate_distance = (np.array(seperate_by)).tolist()

    # array keys for data sources
    raw = gp.ArrayKey("RAW")
    consensus = gp.PointsKey("CONSENSUS")
    skeletonization = gp.PointsKey("SKELETONIZATION")
    matched = gp.PointsKey("MATCHED")
    labels = gp.ArrayKey("LABELS")

    labels_fg = gp.ArrayKey("LABELS_FG")
    labels_fg_bin = gp.ArrayKey("LABELS_FG_BIN")
    loss_weights = gp.ArrayKey("LOSS_WEIGHTS")

    # tensorflow tensors
    gt_fg = gp.ArrayKey("GT_FG")
    fg_pred = gp.ArrayKey("FG_PRED")
    embedding = gp.ArrayKey("EMBEDDING")
    fg = gp.ArrayKey("FG")
    maxima = gp.ArrayKey("MAXIMA")
    gradient_embedding = gp.ArrayKey("GRADIENT_EMBEDDING")
    gradient_fg = gp.ArrayKey("GRADIENT_FG")
    emst = gp.ArrayKey("EMST")
    edges_u = gp.ArrayKey("EDGES_U")
    edges_v = gp.ArrayKey("EDGES_V")
    ratio_pos = gp.ArrayKey("RATIO_POS")
    ratio_neg = gp.ArrayKey("RATIO_NEG")
    dist = gp.ArrayKey("DIST")
    num_pos_pairs = gp.ArrayKey("NUM_POS")
    num_neg_pairs = gp.ArrayKey("NUM_NEG")

    # add request
    request = gp.BatchRequest()
    request.add(labels_fg, output_size)
    request.add(labels_fg_bin, output_size)
    request.add(loss_weights, output_size)
    request.add(raw, input_size)
    request.add(labels, input_size)
    request.add(matched, input_size)
    request.add(skeletonization, input_size)
    request.add(consensus, input_size)

    # add snapshot request
    snapshot_request = gp.BatchRequest()
    request.add(labels_fg, output_size)

    # tensorflow requests
    # snapshot_request.add(raw, input_size)  # input_size request for positioning
    # snapshot_request.add(embedding, output_size, voxel_size=voxel_size)
    # snapshot_request.add(fg, output_size, voxel_size=voxel_size)
    # snapshot_request.add(gt_fg, output_size, voxel_size=voxel_size)
    # snapshot_request.add(fg_pred, output_size, voxel_size=voxel_size)
    # snapshot_request.add(maxima, output_size, voxel_size=voxel_size)
    # snapshot_request.add(gradient_embedding, output_size, voxel_size=voxel_size)
    # snapshot_request.add(gradient_fg, output_size, voxel_size=voxel_size)
    # snapshot_request[emst] = gp.ArraySpec()
    # snapshot_request[edges_u] = gp.ArraySpec()
    # snapshot_request[edges_v] = gp.ArraySpec()
    # snapshot_request[ratio_pos] = gp.ArraySpec()
    # snapshot_request[ratio_neg] = gp.ArraySpec()
    # snapshot_request[dist] = gp.ArraySpec()
    # snapshot_request[num_pos_pairs] = gp.ArraySpec()
    # snapshot_request[num_neg_pairs] = gp.ArraySpec()

    data_sources = tuple(
        (
            gp.N5Source(
                filename=str((sample / "fluorescence-near-consensus.n5").absolute()),
                datasets={raw: "volume"},
                array_specs={
                    raw: gp.ArraySpec(
                        interpolatable=True, voxel_size=voxel_size, dtype=np.uint16
                    )
                },
            ),
            gp.DaisyGraphProvider(
                f"mouselight-{sample.name}-consensus",
                mongo_url,
                points=[consensus],
                directed=True,
                node_attrs=[],
                edge_attrs=[],
            ),
            gp.DaisyGraphProvider(
                f"mouselight-{sample.name}-skeletonization",
                mongo_url,
                points=[skeletonization],
                directed=False,
                node_attrs=[],
                edge_attrs=[],
            ),
        )
        + gp.MergeProvider()
        + gp.RandomLocation(
            ensure_nonempty=consensus,
            ensure_centered=True,
            point_balance_radius=point_balance_radius * micron_scale,
        )
        + TopologicalMatcher(
            skeletonization,
            consensus,
            matched,
            failures=Path("matching_failures_slow"),
            match_distance_threshold=match_distance_threshold * micron_scale,
            max_gap_crossing=gap_crossing_dist * micron_scale,
            try_complete=False,
            use_gurobi=True,
        )
        + RejectIfEmpty(matched)
        + RasterizeSkeleton(
            points=matched,
            array=labels,
            array_spec=gp.ArraySpec(
                interpolatable=False, voxel_size=voxel_size, dtype=np.uint32
            ),
        )
        + GrowLabels(labels, radii=[neuron_radius * micron_scale])
        # TODO: Do these need to be scaled by world units?
        + gp.ElasticAugment(
            [40, 10, 10],
            [0.25, 1, 1],
            [0, math.pi / 2.0],
            subsample=4,
            use_fast_points_transform=True,
            recompute_missing_points=False,
        )
        # + gp.SimpleAugment(mirror_only=[1, 2], transpose_only=[1, 2])
        + gp.Normalize(raw) + gp.IntensityAugment(raw, 0.9, 1.1, -0.001, 0.001)
        for sample in samples_path.iterdir()
        if sample.name in ("2018-07-02", "2018-08-01")
    )

    pipeline = (
        data_sources
        + gp.RandomProvider()
        + Crop(labels, labels_fg)
        + BinarizeGt(labels_fg, labels_fg_bin)
        + gp.BalanceLabels(labels_fg_bin, loss_weights)
        + gp.PreCache(cache_size=cache_size, num_workers=num_workers)
        + gp.tensorflow.Train(
            "train_net",
            optimizer=create_custom_loss(mknet_tensor_names, setup_config),
            loss=None,
            inputs={
                mknet_tensor_names["loss_weights"]: loss_weights,
                mknet_tensor_names["raw"]: raw,
                mknet_tensor_names["gt_labels"]: labels_fg,
            },
            outputs={
                mknet_tensor_names["embedding"]: embedding,
                mknet_tensor_names["fg"]: fg,
                loss_tensor_names["fg_pred"]: fg_pred,
                loss_tensor_names["maxima"]: maxima,
                loss_tensor_names["gt_fg"]: gt_fg,
                loss_tensor_names["emst"]: emst,
                loss_tensor_names["edges_u"]: edges_u,
                loss_tensor_names["edges_v"]: edges_v,
                loss_tensor_names["ratio_pos"]: ratio_pos,
                loss_tensor_names["ratio_neg"]: ratio_neg,
                loss_tensor_names["dist"]: dist,
                loss_tensor_names["num_pos_pairs"]: num_pos_pairs,
                loss_tensor_names["num_neg_pairs"]: num_neg_pairs,
            },
            gradients={
                mknet_tensor_names["embedding"]: gradient_embedding,
                mknet_tensor_names["fg"]: gradient_fg,
            },
            save_every=checkpoint_every,
            summary="Merge/MergeSummary:0",
            log_dir="tensorflow_logs",
        )
        + gp.PrintProfilingStats(every=profile_every)
        + gp.Snapshot(
            additional_request=snapshot_request,
            output_filename="snapshot_{}_{}.hdf".format(
                int(np.min(seperate_distance)), "{id}"
            ),
            dataset_names={
                # raw data
                raw: "volumes/raw",
                # labeled data
                labels: "volumes/labels",
                # trees
                skeletonization: "points/skeletonization",
                consensus: "points/consensus",
                matched: "points/matched",
                # output volumes
                embedding: "volumes/embedding",
                fg: "volumes/fg",
                maxima: "volumes/maxima",
                gt_fg: "volumes/gt_fg",
                fg_pred: "volumes/fg_pred",
                gradient_embedding: "volumes/gradient_embedding",
                gradient_fg: "volumes/gradient_fg",
                # output trees
                emst: "emst",
                edges_u: "edges_u",
                edges_v: "edges_v",
                # output debug data
                ratio_pos: "ratio_pos",
                ratio_neg: "ratio_neg",
                dist: "dist",
                num_pos_pairs: "num_pos_pairs",
                num_neg_pairs: "num_neg_pairs",
                loss_weights: "volumes/loss_weights",
            },
            every=snapshot_every,
        )
    )

    with gp.build(pipeline):
        for _ in range(num_iterations):
            pipeline.request_batch(request)

def train(
    n_iterations,
    setup_config,
    emb_mknet_tensor_names,
    emb_loss_tensor_names,
    fg_mknet_tensor_names,
):
    if setup_config["FUSION_PIPELINE"]:
        train_fusion_pipeline(
            n_iterations,
            setup_config,
            emb_mknet_tensor_names,
            emb_loss_tensor_names,
            fg_mknet_tensor_names,
        )
    else:
        train_simple_pipeline(
            n_iterations,
            setup_config,
            emb_mknet_tensor_names,
            emb_loss_tensor_names,
            fg_mknet_tensor_names,
        )


import abc
from flask import jsonify

class Model(object):

	__metaclass__ = abc.ABCMeta

	def __init__(self):
		self.response = {}

	def json(self):
		self.build_response()
		return jsonify(self.response)

	'''Abstract methods'''
	@abc.abstractmethod
	def build_response(self):
		return

	def get_response(self):
		self.build_response()
		return self.response

class SmallEntity(Model):

	def __init__(self):
		super(SmallEntity, self).__init__()
		self.name = None
		self.score = None
		self.url = None
		self.release_date = None

	def build_response(self):
		super(SmallEntity, self).build_response()
		self.response['name'] = self.name
		self.response['score'] = self.score
		self.response['url'] = self.url
		self.response['release_date'] = self.release_date

class Notable(Model):

	def __init__(self):
		super(Notable, self).__init__()
		self.name = None
		self.score = None
		self.url = None
		self.thumbnail = None

	def build_response(self):
		super(Notable, self).build_response()
		self.response['name'] = self.name
		self.response['score'] = self.score
		self.response['url'] = self.url
		self.response['thumbnail'] = self.thumbnail

class Product(SmallEntity):

	def __init__(self,):
		super(Product, self).__init__()
		self.userscore = None

	def build_response(self):
		super(Product, self).build_response()
		self.response['userscore'] = self.userscore

class Error(Model):

	__metaclass__ = abc.ABCMeta

	def __init__(self, arg):
		super(Error, self).__init__()

		self.code = None
		self.message = None

	def build_response(self):
		super(Error, self).build_response()
		self.response['code'] = self.code
		self.response['message'] = self.message

class Entity(Model):

	__metaclass__ = abc.ABCMeta

	def __init__(self, arg):
		super(Entity, self).__init__()

		self.code = 660
		self.name = None
		self.score = None
		self.genre = []
		self.thumbnail = None
		self.userscore = None
		self.summary = None
		self.rating = None
		self.release_date = None
		self.url = None
		self.userscore_distribution = None
		self.metascore_distribution = None

	def build_response(self):
		super(Entity, self).build_response()
		self.response['code'] = self.code
		self.response['name'] = self.name
		self.response['score'] = self.score
		self.response['genre'] = self.genre
		self.response['thumbnail'] = self.thumbnail
		self.response['userscore'] = self.userscore
		self.response['summary'] = self.summary
		self.response['release_date'] = self.release_date
		self.response['url'] = self.url
		self.response['rating'] = self.rating
		self.response['userscore_distribution'] = self.userscore_distribution
		self.response['metascore_distribution'] = self.metascore_distribution

class ListEntity(Model):

	__metaclass__ = abc.ABCMeta

	def __init__(self):
		super(ListEntity, self).__init__()

		self.build_type = True
		self.build_thumbnail = True
		self.build_summary = True
		self.build_rating = True
		self.build_userscore = True
		self.build_genre = True

		self.type = None
		self.name = None
		self.release_date = None
		self.genre = []
		self.summary = None
		self.score = None
		self.userscore = None
		self.url = None
		self.thumbnail = None
		self.rating = None

	def build_response(self):
		super(ListEntity, self).build_response()
		if self.build_type:
			self.response['type'] = self.type
		self.response['name'] = self.name
		self.response['score'] = self.score
		if self.build_genre:
			self.response['genre'] = self.genre
		if self.build_userscore:
			self.response['userscore'] = self.userscore
		if self.build_summary:
			self.response['summary'] = self.summary
		self.response['release_date'] = self.release_date
		self.response['url'] = self.url
		if self.build_thumbnail:
			self.response['thumbnail'] = self.thumbnail
		if self.build_rating:
			self.response['rating'] = self.rating

	def disable_type(self):
		self.build_type = False

	def disable_thumbnail(self):
		self.build_thumbnail = False

	def disable_summary(self):
		self.build_summary = False

	def disable_rating(self):
		self.build_rating = False

	def disable_userscore(self):
		self.build_userscore = False

	def disable_genre(self):
		self.build_genre = False


import discord
from discord.ext import commands
from .utils.chat_formatting import *
from .utils.dataIO import fileIO
from .utils import checks
from __main__ import send_cmd_help
from urllib import parse
import aiohttp
import os
import asyncio

try:   # check if pony cog is installed
    from .special_pony import Pony
    ponyAvailable = True
except ImportError:
    ponyAvailable = False

try:   # check if derpisafe cog is installed
    from .imagesafe import create_derpi_embed
    derpiAvailable = True
except ImportError:
    derpiAvailable = False

class Ponysub:
    def __init__(self, bot):
        self.bot = bot
        self.data = fileIO("data/ponysub/data.json", "load")
        self.sub_task = bot.loop.create_task(self.ponysub_service())

    @commands.group(name='ponysub',no_pm=True, pass_context=True)
    @checks.admin_or_permissions(manage_server=True)
    async def ponysub(self, ctx):
        """Manages subscription to a search string on derpibooru"""
        if ctx.invoked_subcommand is None:
            await send_cmd_help(ctx)

    @ponysub.command(name='add', pass_context=True)
    @checks.admin_or_permissions(manage_server=True)
    async def _add(self, ctx, sub_name : str, channel : discord.Channel, * , search_string : str=""):
        """Adds a new search string to check for updates:

        [p]ponysub add <name> #<channel> <search string>"""
        server = ctx.message.server
        sub_name = sub_name.lower().capitalize()
        new_sub = { 'name' : sub_name, 'channel' : channel.id, 'search string' : search_string, 'enabled' : False, 'latest image' : "1980" }
        if server.id not in self.data:
            self.data[server.id] = []
        else:
            if find_sub(self, server, sub_name):
                await self.bot.say("Name %s is already used!" % ( sub_name ))
                return
        self.data[server.id].append( new_sub )
        fileIO("data/ponysub/data.json", "save", self.data)
        await self.bot.say("Added %s to subscriptions! Don't forget to enable it!" % ( sub_name ))

    @ponysub.command(name='list', pass_context=True)
    async def _list(self, ctx, channel : discord.Channel=False):
        """Lists all subscriptions:

        [p]ponysub list"""
        server = ctx.message.server
        msg = "Listing:\n"
        if server.id not in self.data:
            msg += "No subscriptions in server!\n"
        else:
            for a in range(len(self.data[server.id])):
                if channel:
                    if channel.id != self.data[server.id][a]['channel']:
                        continue
                msg += "```Markdown\n"
                if self.data[server.id][a]['enabled']:
                    msg += "# %s: Enabled\n" % (self.data[server.id][a]['name'])
                else:
                    msg += "# %s: Disabled\n" % (self.data[server.id][a]['name'])
                msg += "Channel: #%s\n" % ( find_channel( self, server, self.data[server.id][a]['channel'] ).name )
                msg += "Search String: %s\n" % (self.data[server.id][a]['search string'])
                msg += "```\n"
        await self.bot.say(msg)

    @ponysub.command(name='toggle', pass_context=True)
    @checks.admin_or_permissions(manage_server=True)
    async def _toggle(self, ctx, sub_name):
        """Enable or disable a subscription

        [p]ponysub toggle <name>"""
        server = ctx.message.server
        sub_number = find_sub(self, server, sub_name)
        if type(sub_number) is bool:
            await self.bot.say("Can't find %s!" % (sub_name))
            return
        if self.data[server.id][sub_number]['enabled']:
            self.data[server.id][sub_number]['enabled'] = False
            fileIO("data/ponysub/data.json", "save", self.data)
            await self.bot.say( "Subscription %s now disabled!" % (self.data[server.id][sub_number]['name']))
            return
        else:
            search = "https://derpibooru.org/search.json?q="
            search += parse.quote_plus(self.data[server.id][sub_number]['search string'])
            search += "&filter_id=56027"
            try:
                async with aiohttp.get(search) as r:
                    website = await r.json()
            except:
                return await self.bot.say("Error.")
            if website['total'] == 0:
                await self.bot.say("There are no images matching this subscription!")
                return
            self.data[server.id][sub_number]['latest image'] = website['search'][0]['created_at']
            self.data[server.id][sub_number]['enabled'] = True
            fileIO("data/ponysub/data.json", "save", self.data)
            await self.bot.say( "Subscription %s now enabled!" % (self.data[server.id][sub_number]['name']))

    @ponysub.command(name='refresh', pass_context=True)
    @checks.admin_or_permissions(manage_server=True)
    async def _refresh(self, ctx, refresh_time : int):
        """Set the refresh time (how often to check the server in minutes)

        [p]ponysub refresh <time>"""
        server = ctx.message.server
        if refresh_time < 5:
            await self.bot.say("You can only enter a time over 5 minutes.")
            return
        self.data['refresh'] = refresh_time
        await self.bot.say( "Refresh time set to %i minutes." % (refresh_time))
        fileIO("data/ponysub/data.json", "save", self.data)

    @ponysub.command(name='del', pass_context=True)
    @checks.admin_or_permissions(manage_server=True)
    async def _delete(self, ctx, sub_name):
        """Deletes a subscription

        [p]ponysub del <name>"""
        server = ctx.message.server
        sub_number = find_sub(self, server, sub_name)
        if type(sub_number) is bool:
            await self.bot.say("Can't find %s!" % (sub_name))
            return
        if server.id not in self.data:
            await self.bot.say("No subscriptions for this server!")
            return
        await self.bot.say('Type **delete** to actually delete the subscription.')
        message = await self.bot.wait_for_message(timeout=60, author=ctx.message.author)
        if not message:
            return await self.bot.say("Aborted.")
        if message.content.lower() != 'delete':
            return await self.bot.say("Aborted.")
        self.data[server.id].remove( self.data[server.id][sub_number] )
        if not self.data[server.id]:
            del self.data[server.id]
        fileIO("data/ponysub/data.json", "save", self.data)
        await self.bot.say( "Deleted %s subscription." % (sub_name))

    async def ponysub_service(self):
        """Trigger loop"""
        await self.bot.wait_until_ready()
        try:
            while True:
                try:
                    await self.spam_looper()
                except asyncio.CancelledError:
                    raise
                except:
                    pass
                if type(self.data['refresh']) is int:
                    if self.data['refresh'] < 5:
                        self.data['refresh'] = 5
                        fileIO("data/ponysub/data.json", "save", self.data)
                else:
                    self.data['refresh'] = 5
                    fileIO("data/ponysub/data.json", "save", self.data)
                await asyncio.sleep(self.data['refresh'] * 60)
                print("Ponysub service tick.")
        except asyncio.CancelledError:
            pass

    def __unload(self):
        self.sub_task.cancel()

    async def spam_looper(self):
        self.filters = fileIO("data/pony/filters.json", "load")
        if not self.data:
            return
        for server, sub_data in self.data.items():
            if server == 'refresh':
                continue
    #        if type(sub_data) is not 'list':
    #            continue
            server = find_server(self, server)
            for sub_number in range(len(sub_data)):
                if not self.data[server.id][sub_number]['enabled']:
                    continue
                image_list = []
                search = "https://derpibooru.org/search.json?q="
                tagSearch = ""
                tagSearch += self.data[server.id][sub_number]['search string']
                tagSearch += ",created_at.gt:%s" % (self.data[server.id][sub_number]['latest image'])
                if server.id in self.filters:
                    if self.filters[server.id] != []:
                        tagSearch += ", "
                    tagSearch += ", ".join(self.filters[server.id])
                else:
                    tagSearch += ", "
                    tagSearch += ", ".join(self.filters["default"])
                search += parse.quote_plus(tagSearch)
                search += "&filter_id=56027"
                page_counter = 1
                while True:
                    try:
                        async with aiohttp.get(search + "&page=%i" % (page_counter)) as r:
                            website = await r.json()
                    except:
                        return await self.bot.say("Error.")
                    if not website['search']:
                        break
                    for key in range(len(website['search'])):
                        image_list.append(website['search'][key]['id'])
                    page_counter += 1
                if 'duplicate_reports' in website:
                    dupe_found = False
                    for reports in website['duplicate_reports']:
                        if 'state' in reports and reports['state'] == 'open' and 'reason' in reports and reports['reason'].lower() == 'automated perceptual dedupe match':
                            dupe_found = True
                            break
                    if dupe_found:
                        continue
                for key in range(len(image_list)):
                    await self.fetch_image(ctx=False, randomize=False, tags=[ "ID:%s" % (image_list[key]) ], server=server.id, channel=self.data[server.id][sub_number]['channel'])
                search = "https://derpibooru.org/search.json?q="
                search += parse.quote_plus(self.data[server.id][sub_number]['search string'])
                search += "&filter_id=56027"
                try:
                    async with aiohttp.get(search) as r:
                        website = await r.json()
                except:
                    return
                if website['total'] == 0:
                    return
                self.data[server.id][sub_number]['latest image'] = website['search'][0]['created_at']
                self.data[server.id][sub_number]['enabled'] = True
                fileIO("data/ponysub/data.json", "save", self.data)

    async def fetch_image(self, ctx, randomize, tags, server : str="", channel : str=""):

        if server != "" and channel != "":
            server = find_server(self, server)
            channel = find_channel(self, server, channel)
        else:
            server = ctx.message.server
        self.filters = fileIO("data/pony/filters.json", "load")
        self.settings = fileIO("data/pony/settings.json", "load")

        # Initialize base URL
        search = "https://derpibooru.org/search.json?q="
        tagSearch = ""

        # Apply tags to URL
        if tags:
            tagSearch += "{} ".format(" ".join(tags))
        if server.id in self.filters:
            if self.filters[server.id] != [] and tags:
                tagSearch += ", "
            tagSearch += ", ".join(self.filters[server.id])
        else:
            if tags:
                tagSearch += ", "
            tagSearch += ", ".join(self.filters["default"])
        search += parse.quote_plus(tagSearch)

        # Apply Derpibooru's "Everything" filter

        search += "&filter_id=56027"

        # Inform users about image retrieving
        if channel:
            message = await self.bot.send_message(channel, "Fetching pony image...")
        else:
            message = await self.bot.say("Fetching pony image...")

        # Fetch and display the image or an error
        try:
            async with aiohttp.get(search) as r:
                website = await r.json()
            if website["search"] != []:
                website = website["search"][0]
                imageURL = "https:{}".format(website["image"])
            else:
                return await self.bot.edit_message(message, "Your search terms gave no results.")
        except:
            return await self.bot.edit_message(message, "Error.")

        # Edits the pending message with the results
        if derpiAvailable:
            return await self.bot.edit_message(message, "Image found: <https://derpibooru.org/{}>".format(website["id"]), embed=await create_derpi_embed(website))
        else:
            return await self.bot.edit_message(message, imageURL)

def find_sub(self, server, sub_name):
    sub_name = sub_name.lower().capitalize()
    if server.id not in self.data:
        return False
    for a in range(len(self.data[server.id])):
        if self.data[server.id][a]['name'] == sub_name:
            return a
    return False

def find_channel(self, server, channel_id):
    return discord.utils.get(self.bot.get_all_channels(), server__name=server.name, id=channel_id)

def find_server(self, server):
    return self.bot.get_server(server)

def check_folder():
    if not os.path.exists("data/ponysub"):
        print("Creating data/pony folder...")
        os.makedirs("data/ponysub")

def check_files():
    data_default = {"refresh":10}

    if not fileIO("data/ponysub/data.json", "check"):
        print("Creating default ponysub data.json...")
        fileIO("data/ponysub/data.json", "save", data_default)
    else:
        data = fileIO("data/ponysub/data.json", "load")

def setup(bot):
    if ponyAvailable:
        check_folder()
        check_files()
        bot.add_cog(Ponysub(bot))
    else:
        raise RuntimeError("You need to install the pony cog from https://github.com/Alzarath/Booru-Cogs")