
# # SPADE 估计子
# def midpoint_mean(array, midpoint):
#     temp = []
#     y = midpoint[0]
#     x = midpoint[1]
#     for i in (-1, 0, 1):
#         temp.append(array[x+i][y-1:y+2])
#     return np.array(temp, dtype=int).sum() / 9


# def spade_estimator(img_list, sigma=None, midpoint_1=None, midpoint_2=None):
#     data = []
#     for img in img_list:
#         array = tif_to_np(img)
#         k = np.sqrt(midpoint_mean(array, midpoint_1) / midpoint_mean(array, midpoint_2))
#         data.append(2 * sigma * (k-1)/(k+1))
#     return np.array(data)


# # -----------------------------
# # DI 估计子
# def get_zero_point(point_list):
#     temp = []
#     criterion = (point_list.max() + point_list.min()) / 2
#     for i in range(np.size(point_list)):
#         if point_list[i] < criterion:
#             temp.append(point_list[i])
#     temp = np.array(temp)
#     return temp.sum() / np.size(temp)


# def get_point_list(img_list):
#     point_list = []
#     for img in img_list:
#         array = tif_to_np(img)
#         index = np.arange(np.sqrt(np.size(array)))
#         new_array = array / array.sum()
#         point_list.append((index @ new_array).sum())
#     return np.array(point_list)


# def di_estimator(img_list, mmpp=None):
#     point_list = get_point_list(img_list)
#     zero_point = get_zero_point(point_list)
#     return (point_list - zero_point) * mmpp


# # -----------------------------
# # 批量生成tif路径
# def gen_img_list(fixes=None, raw_path=None, frames=None, fix_rule=None):
#     cmd = 'raw_path.format'+fix_rule
#     img_list = []
#     for fix in fixes:
#         for frame in range(1, frames+1):
#             img_list.append(eval(cmd))
#     return np.array(img_list).reshape(np.size(fixes), frames)


# # -----------------------------
# # 读取全部数据, 保存在data数组中并返回
# def read_all(fixes=None, raw_path=None, frames=None, fix_rule=None,
#              SPADE=True, sigma=None, midpoint_1=None, midpoint_2=None, 
#              mmpp=None):

#     img_list = gen_img_list(fixes=fixes, raw_path=raw_path, frames=frames, fix_rule=fix_rule)
#     data = []
#     if SPADE:
#         for index in range(np.size(fixes)):
#             data.append(spade_estimator(img_list[index], sigma=sigma, midpoint_1=midpoint_1, midpoint_2=midpoint_2))
#     elif not SPADE:
#         for index in range(np.size(fixes)):
#             data.append(classical_estimator(img_list[index], mmpp=mmpp))
#     else:
#         pass

#     return np.array(data)



# def delete_zeros(point_list):
#     temp = []
#     criterion = (point_list.max() + point_list.min()) / 2
#     for i in range(np.size(point_list)):
#         if point_list[i] > criterion:
#             temp.append(point_list[i])
#     temp = np.array(temp)
#     return temp
# # -----------------------------------------------




# def tif_to_np(path):
#     img = Image.open(path)
#     Matrix = np.array(img.getdata()).reshape(img.size[1], img.size[0])
#     return Matrix



# def get_zero_point(point_list):
#     temp = []
#     criterion = (point_list.max() + point_list.min()) / 2
#     for i in range(np.size(point_list)):
#         if point_list[i] < criterion:
#             temp.append(point_list[i])
#     temp = np.array(temp)
#     return temp.sum() / np.size(temp)


# def get_point_list(img_list):
#     point_list = []
#     for img in img_list:
#         array = imread(img)
#         index = np.arange(np.sqrt(np.size(array)))
#         new_array = array / array.sum()
#         point_list.append((index @ new_array).sum())
#     return np.array(point_list)


# def di_estimator(img_list, mmpp=None):
#     point_list = get_point_list(img_list)
#     zero_point = get_zero_point(point_list)
#     return (point_list - zero_point) * mmpp

    # index = np.arange(np.shape(img)[0])
    # normalized_img = img / img.sum()
    # return ((index @ normalized_img).sum() - zero_point) * pixel_size


# def imshow(x, cmap=None, via_opencv=False,
#            title=None, colorbar=True, axis=False, font=None, 
#            save=None):
    
#     if via_opencv:
#         if title is None:
#             cv.imshow('Untitled', x)
#             cv.waitKey(0)
#             cv.destroyAllWindows()
#         else:
#             cv.imshow(title, x)
#             cv.waitKey(0)
#             cv.destroyAllWindows()

#     else:
#         if font is not None:
#             if font == 'default':
#                 plt.rcdefaults()
#             else:
#                 plt.rc('font', family=font)

#         if not axis:
#             plt.xticks(())
#             plt.yticks(())

#         if title is not None:
#             plt.title(title)

#         if cmap is None:
#             plt.imshow(x)
#         else:
#             plt.imshow(x, cmap=cmap)

#         if colorbar:
#             plt.colorbar()

#         if save is not None:
#             plt.savefig(save)

#         plt.show()


# def signal_to_noise_ratio(signal=None, noise=None):
#     from math import log10
#     return 10 * log10(sum(photon_number(signal)) / sum(photon_number(noise)))


# def copy_ipynb():
#     from os import path, rename
#     from shutil import copy
#     if not path.exists('./sample.ipynb') and not path.exists('./main.ipynb'):
#         copy(src=path.join(path.dirname(__file__), 'sample.ipynb'), dst='./')
#         rename(src='./sample.ipynb', dst='./main.ipynb')
#     del path, copy


# def phase_map(self, x, y, z):
#     n = self.mode.n
#     m = self.mode.m
#     w = self.beam_size(z)
#     return np.sign(hermite(n)(sqrt(2)*x/w)*hermite(m)(sqrt(2)*y/w))



# def amplitude(self, x, y, z):
#     rho = np.square(x) + np.square(y)

#     # w0 = self.beam_waist
#     w = self.beam_size(z)

#     if isinstance(self.mode, hermite_gauss):
#         n = self.mode.n
#         m = self.mode.m
#         hx = hermite(n)(sqrt(2)*x/w)
#         hy = hermite(m)(sqrt(2)*y/w)
#         return sqrt(2**(1-n-m)/(pi*factorial(n)*factorial(m)))*hx*hy*np.exp(-rho/(w**2)) / w
#     elif isinstance(self.mode, laguerre_gauss):
#         pass # LG 模式的振幅, 待补充



# def intensity(self, x, y, z):
#     return np.square(np.abs(self.complex_amplitude(x, y, z))) 

# def phase_shift(self, x, y, z):
#     return np.angle(self.complex_amplitude(x, y, z))

# def normalized_amplitude(self, x, y, z):
#     amplitude = np.abs(self.amplitude(x, y, z))
#     return amplitude / amplitude.max()

# def normalized_complex_amplitude(self, x, y, z):
#     return self.normalized_amplitude(x, y, z)*np.exp(1j*self.phase_shift(x, y, z))

# def normalized_intensity(self, x, y, z):
#     return np.square(np.abs(self.normalized_complex_amplitude(x, y, z)))



# def plot(x, y, fmts='-', num=300,
#          title=None, label=None,
#          xlabel=None, ylabel=None, xlim=None, ylim=None,
#          legend=True, grid=True, figsize=None, font=None,
#          show=True, save=None):

#     set_font(family=font)

#     if isinstance(x, (list, tuple)) and len(x) == 2 :
#         if callable(y):
#             x = np.linspace(x[0], x[1], num)
#         else:
#             x = np.linspace(x[0], x[1], len(y))

#     if callable(y):
#         try: 
#             y = y(x)
#         except TypeError:
#             y_ = []
#             for x_ in x:
#                 y_.append(y(x_))
#             y = np.array(y_)
#             del x_, y_

#     if title is not None:
#         plt.title(title)

#     if xlabel is not None:
#         plt.xlabel(xlabel)

#     if ylabel is not None:
#         plt.ylabel(ylabel)

#     if xlim is not None:
#         plt.xlim(xlim)

#     if ylim is not None:
#         plt.ylim(ylim)

#     if label is None:
#         plt.plot(x, y, fmts)
#     else:
#         plt.plot(x, y, fmts, label=label)

#     if legend and label is not None:
#         plt.legend()

#     if grid:
#         plt.grid(True)
#     else:
#         plt.grid(False)

#     if save is not None:
#         plt.savefig(save)

#     if show:
#         plt.show()

    

# def hist(x, bins=300, histtype='step', density=True,
#          title=None, label=None,
#          xlabel=None, ylabel=None, xlim=None, ylim=None,
#          legend=True, grid=True, figsize=None, font=None,
#          show=True, save=None):

#     set_font(family=font)

#     if title is not None:
#         plt.title(title)

#     if xlabel is not None:
#         plt.xlabel(xlabel)

#     if ylabel is not None:
#         plt.ylabel(ylabel)

#     if xlim is not None:
#         plt.xlim(xlim)

#     if ylim is not None:
#         plt.ylim(ylim)

#     if label is None:
#         plt.hist(x, bins=bins, histtype=histtype, density=density)
#     else:
#         plt.hist(x, bins=bins, histtype=histtype, density=density, label=label)

#     if legend and label is not None:
#         plt.legend()

#     if grid:
#         plt.grid(True)
#     else:
#         plt.grid(False)

#     if save is not None:
#         plt.savefig(save)

#     if show:
#         plt.show()

    

# def imshow(x, cmap=None, pillow=False,
#            title=None, colorbar=True, axis=False, 
#            figsize=None, font=None,
#            show=True, save=None):

#     set_font(family=font)

#     if pillow:
#         from PIL import Image
#         Image.fromarray(x).show()
#     else:
#         if not axis:
#             plt.xticks(())
#             plt.yticks(())

#         if title is not None:
#             plt.title(title)

#         if cmap is None:
#             plt.imshow(x)
#         else:
#             plt.imshow(x, cmap=cmap)

#         if colorbar:
#             plt.colorbar()

#         if save is not None:
#             plt.savefig(save)

#         if show:
#             plt.show()



# def scatter(x, y, s=None, c=None, alpha=None, 
#             title=None, label=None, colorbar=None,
#             xlabel=None, ylabel=None, xlim=None, ylim=None,
#             legend=True, grid=True, figsize=None, font=None,
#             show=True, save=None):

#     set_font(family=font)

#     if title is not None:
#         plt.title(title)

#     if xlabel is not None:
#         plt.xlabel(xlabel)

#     if ylabel is not None:
#         plt.ylabel(ylabel)

#     if xlim is not None:
#         plt.xlim(xlim)

#     if ylim is not None:
#         plt.ylim(ylim)

#     if label is None:
#         plt.scatter(x, y, s=s, c=c, alpha=alpha)
#     else:
#         plt.scatter(x, y, s=s, c=c, alpha=alpha, label=label)

#     if legend and label is not None:
#         plt.legend()

#     if grid:
#         plt.grid(True)
#     else:
#         plt.grid(False)

#     if colorbar:
#         plt.colorbar()
#     elif (colorbar is not False) and (c is not None):
#         plt.colorbar()

#     if save is not None:
#         plt.savefig(save)

#     if show:
#         plt.show()

# def ip():
#     from urllib import request
#     ip = request.urlopen('https://gitee.com/vxyi/quantum-ws-ip-address/raw/master/ip')
#     print(ip.read().decode('utf-8'))


# import os

# def name_dict(method, path='./'):
#     name_dict = {'m(7)':-7,
#                  'm(6)':-6,
#                  'm(5)':-5,
#                  'm(4)':-4,
#                  'm(3)':-3,
#                  'm(2)':-2,
#                  'm(1)':-1,
#                  'p(0)': 0,
#                  'p(1)': 1,
#                  'p(2)': 2,
#                  'p(3)': 3,
#                  'p(4)': 4,
#                  'p(5)': 5,
#                  'p(6)': 6,
#                  'p(7)': 7}
#     for name in name_dict:
#         os.rename(src = os.path.join(path, method + '_' + name + '.tif'), 
#                   dst = os.path.join(path, method + str(name_dict[name])) + '.tif')



# def imread(img_path, pillow=False):
#     if os.path.exists(img_path):
#         if pillow:
#             return np.array(PIL.Image.open(img_path)).astype(float)
#         else:
#             return cv.imread(img_path, cv.IMREAD_GRAYSCALE | cv.IMREAD_ANYDEPTH).astype(float)
#     else:
#         raise ValueError(f'{img_path} is not exists')


# def imreadmutli(img_path, pillow=True):
#     if os.path.exists(img_path):
#         if pillow:
#             img = PIL.Image.open(img_path)
#             mptiff = []
#             for i in range(img.n_frames):
#                 img.seek(i)
#                 mptiff.append(np.array(img))
#             return np.array(mptiff).astype(float)
#         else:
#             raise ValueError('cv2 is not available yet')
#     else:
#         raise ValueError(f'{img_path} is not exists')


# def imwrite(array=None, save=None, pillow=False):
#     if array is not None:
#         if array.dtype == np.uint8:
#             if pillow:
#                 PIL.Image.fromarray(array).save(save)
#             else:
#                 cv.imwrite(filename=save, img=array)
#         else:
#             raise TypeError('array.dtype must be np.uint8')
#     else:
#         raise TypeError('array is None')

# def plotter_decorator(**kwargs):
#     default_params = {
#           'figsize': None, 
#              'axis': True, 
#              'grid': True, 
#            'xlabel': None, 
#            'ylabel': None, 
#             'title': None,
#              'xlim': None, 
#              'ylim': None, 
#            'legend': True, 
#             'label': None, 
#              'save': None, 
#          'override': False, 
#              'show': True,
#     }
#     params = {**default_params, **kwargs}
#     def decorator(plotter_function):
#         from functools import wraps
#         @wraps(plotter_function)
#         def wrapper(*args, **kwargs):    
#             if params['figsize'] is not None:
#                 old_figsize = plt.rcParams['figure.figsize']
#                 plt.rcParams['figure.figsize'] = params['figsize']
#             if not params['axis']:
#                 plt.xticks([])
#                 plt.yticks([])
#             if params['grid'] is not None:    
#                 plt.grid(params['grid'])
#             if params['xlabel'] is not None:
#                 plt.xlabel(params['xlabel'])
#             if params['ylabel'] is not None:
#                 plt.ylabel(params['ylabel'])
#             if params['title'] is not None:
#                 plt.title(params['title'])

#             plotter_function(*args, **kwargs)
            
#             if params['xlim'] is not None:
#                 plt.xlim(params['xlim'])
#             if params['ylim'] is not None:
#                 plt.ylim(params['ylim'])
#             if params['legend'] and params['label'] is not None:
#                 plt.legend()
#             save = params['save']
#             if save is not None:
#                 if type(save) is bool or type(save) is str:
#                     if save == True:
#                         while True:
#                             temp_name = f'{np.random.randint(0, 1e8)}.svg'
#                             if not os.path.exists(temp_name):
#                                 break
#                         plt.savefig(temp_name)
#                         save = sha1(temp_name)
#                         os.rename(src=temp_name, dst=f'{save}.svg')
#                     elif type(save) is str:
#                         if os.path.exists(save) or os.path.exists(f'{save}.svg'):
#                             if params['override']:
#                                 plt.savefig(save)
#                             else:
#                                 raise FileExistsError('file already exists')
#                         else:
#                             plt.savefig(save)
#                 else:
#                     raise TypeError('type(save) must be bool or str')
#             if params['show']:
#                 plt.show()
#             if params['figsize'] is not None:
#                 plt.rcParams['figure.figsize'] = old_figsize
#         return wrapper
#     return decorator


# def plotter_decorator(**kwargs):
#     default_params = {
#           'figsize': None, 
#              'axis': True, 
#              'grid': True, 
#            'xlabel': None, 
#            'ylabel': None, 
#             'title': None,
#              'xlim': None, 
#              'ylim': None, 
#            'legend': True, 
#             'label': None, 
#              'save': None, 
#          'override': False, 
#              'show': True,
#     }
#     params = {**default_params, **kwargs}
#     def decorator(plotter_function):
#         from functools import wraps
#         @wraps(plotter_function)
#         def wrapper(
#                 *args, 
#                 figsize=params['figsize'], 
#                 axis=params['axis'], 
#                 grid=params['grid'], 
#                 xlabel=params['xlabel'], 
#                 ylabel=params['ylabel'], 
#                 title=params['title'],
#                 xlim=params['xlim'], 
#                 ylim=params['ylim'], 
#                 legend=params['legend'], 
#                 label=params['label'], 
#                 save=params['save'], 
#                 override=params['override'], 
#                 show=params['show'],
#                 **kwargs
#         ):    
#             if figsize is not None:
#                 old_figsize = plt.rcParams['figure.figsize']
#                 plt.rcParams['figure.figsize'] = figsize
#             if not axis:
#                 plt.xticks([])
#                 plt.yticks([])
#             if grid is not None:    
#                 plt.grid(grid)
#             if xlabel is not None:
#                 plt.xlabel(xlabel)
#             if ylabel is not None:
#                 plt.ylabel(ylabel)
#             if title is not None:
#                 plt.title(title)

#             plotter_function(*args, **kwargs)
            
#             if xlim is not None:
#                 plt.xlim(xlim)
#             if ylim is not None:
#                 plt.ylim(ylim)
#             if legend and label is not None:
#                 plt.legend()
#             if save is not None:
#                 if type(save) is bool or type(save) is str:
#                     if save == True:
#                         while True:
#                             temp_name = f'{np.random.randint(0, 1e8)}.svg'
#                             if not os.path.exists(temp_name):
#                                 break
#                         plt.savefig(temp_name)
#                         save = sha1(temp_name)
#                         os.rename(src=temp_name, dst=f'{save}.svg')
#                     elif type(save) is str:
#                         if os.path.exists(save) or os.path.exists(f'{save}.svg'):
#                             if override:
#                                 plt.savefig(save)
#                             else:
#                                 raise FileExistsError('file already exists')
#                         else:
#                             plt.savefig(save)
#                 else:
#                     raise TypeError('type(save) must be bool or str')
#             if show:
#                 plt.show()
#             if figsize is not None:
#                 plt.rcParams['figure.figsize'] = old_figsize
#         return wrapper
#     return decorator

# import numpy as np
# import matplotlib.pyplot as plt

# import os
# import PIL

# from .macro import sha1

# from matplotlib_inline import backend_inline
# backend_inline.set_matplotlib_formats('svg')
# del backend_inline

# from matplotlib.font_manager import fontManager
# fontManager.addfont(os.path.join(os.path.dirname(__file__), 'font/SourceHanSans.otf'))
# del fontManager

# plt.rcParams['font.family'] = ['Source Han Sans SC']
# plt.rcParams['figure.figsize'] = (6, 4)
# plt.rcParams['savefig.format'] = 'svg'

# __all__ = [
#     'show_all_fonts', 
#     'set_font',
#     'figsize_fixed',
#     'imread',
#     'imwrite',
#     'plot',
#     'hist',
#     'scatter',
#     'imshow'
# ]


# shared_params = {
#     'figsize': None, 
#     'axis': True, 
#     'grid': True, 
#     'xlabel': None, 
#     'ylabel': None, 
#     'title': None,
#     'xlim': None, 
#     'ylim': None, 
#     'legend': True, 
#     'label': None, 
#     'save': None, 
#     'override': False, 
#     'show': True
# }


# def plotter_decorator(**kwargs):
#     params = {**shared_params, **kwargs}
#     def decorator(plotter_function):
#         from functools import wraps
#         @wraps(plotter_function)
#         def wrapper(*args, **kwargs):    
#             if params['figsize'] is not None:
#                 old_figsize = plt.rcParams['figure.figsize']
#                 plt.rcParams['figure.figsize'] = params['figsize']
#             if not params['axis']:
#                 plt.xticks([])
#                 plt.yticks([])
#             if params['grid'] is not None:    
#                 plt.grid(params['grid'])
#             if params['xlabel'] is not None:
#                 plt.xlabel(params['xlabel'])
#             if params['ylabel'] is not None:
#                 plt.ylabel(params['ylabel'])
#             if params['title'] is not None:
#                 plt.title(params['title'])

#             plotter_function(*args, **kwargs)
            
#             if params['xlim'] is not None:
#                 plt.xlim(params['xlim'])
#             if params['ylim'] is not None:
#                 plt.ylim(params['ylim'])
#             if params['legend'] and params['label'] is not None:
#                 plt.legend()
#             save = params['save']
#             if save is not None:
#                 if type(save) is bool or type(save) is str:
#                     if save == True:
#                         while True:
#                             temp_name = f'{np.random.randint(0, 1e8)}.svg'
#                             if not os.path.exists(temp_name):
#                                 break
#                         plt.savefig(temp_name)
#                         save = sha1(temp_name)
#                         os.rename(src=temp_name, dst=f'{save}.svg')
#                     elif type(save) is str:
#                         if os.path.exists(save) or os.path.exists(f'{save}.svg'):
#                             if params['override']:
#                                 plt.savefig(save)
#                             else:
#                                 raise FileExistsError('file already exists')
#                         else:
#                             plt.savefig(save)
#                 else:
#                     raise TypeError('type(save) must be bool or str')
#             if params['show']:
#                 plt.show()
#             if params['figsize'] is not None:
#                 plt.rcParams['figure.figsize'] = old_figsize
#         return wrapper
#     return decorator


# def show_all_fonts():
#     from matplotlib.font_manager import get_font_names
#     all_fonts = get_font_names()
#     print('font list got from matplotlib.font_manager:')
#     for font in sorted(all_fonts):
#         print('\t' + font)


# def set_font(family=None, weight=None):
#     if family is None:
#         plt.rcParams['font.family'] = ['Source Han Sans SC']
#     else:
#         plt.rc('font', family=family, weight=weight)


# def figsize_fixed(x_figsize=None, y_figsize=None):
#     if isinstance(x_figsize, (tuple, list)) and (y_figsize is None):
#         plt.rcParams['figure.figsize'] = x_figsize
#     elif isinstance(y_figsize, (tuple, list)) and (x_figsize is None):
#         plt.rcParams['figure.figsize'] = y_figsize
#     elif x_figsize is None and y_figsize is None:
#         plt.rcParams['figure.figsize'] = (6, 4)
#         print('warning: x_figsize and y_figsize is None, setting figsize to default.')
#     elif isinstance(x_figsize, (int, float)) and isinstance(x_figsize, (int, float)):
#         plt.rcParams['figure.figsize'] = (x_figsize, y_figsize)
#     else:
#         raise ValueError('invalid figsize parameters')


# def imread(img_path):
#     if os.path.exists(img_path):
#         img = PIL.Image.open(img_path)
#         array = []
#         for i in range(img.n_frames):
#             img.seek(i)
#             array.append(np.array(img))
#         array = np.array(array, dtype=float)
#         if np.shape(array)[0] == 1:
#             return array[0]
#         else:
#             return array
#     else:
#         raise FileNotFoundError(f'{img_path} is not exists')


# def imwrite(array=None, save=None):
#     if array is not None:
#         if array.dtype == np.uint8:
#             PIL.Image.fromarray(array).save(save)
#         else:
#             raise TypeError('array.dtype must be np.uint8')
#     else:
#         raise TypeError('array is None')


# @plotter_decorator()
# def plot(
#     x: np.ndarray or tuple = [], 
#     y: np.ndarray or function = [], 
#     fmt: str = '-', 
#     dots: int = 300, 
#     alpha: float = None, 
#     xerr: np.ndarray = None, 
#     yerr: np.ndarray = None, 
#     capsize: float = 3,

#     figsize: tuple = shared_params['figsize'], 
#     axis: bool = shared_params['axis'], 
#     grid: bool = shared_params['grid'], 
#     xlabel: str = shared_params['xlabel'], 
#     ylabel: str = shared_params['ylabel'], 
#     title: str = shared_params['title'],
#     xlim: tuple = shared_params['xlim'], 
#     ylim: tuple = shared_params['ylim'], 
#     legend: bool = shared_params['legend'], 
#     label: str = shared_params['label'], 
#     save: bool or str = shared_params['save'], 
#     override: bool = shared_params['override'], 
#     show: bool = shared_params['show']
# ):
#     if len(x) == 0:
#         x = (0, 1)
#         if len(y) != 0:
#             print('warning: x is a empty, x is treated as (0, 1)')
#     if isinstance(x, tuple):
#         if len(x) == 2:
#             if callable(y):
#                 x = np.linspace(x[0], x[1], dots)
#             else:
#                 x = np.linspace(x[0], x[1], len(y))
#         else:
#             raise TypeError('when x is a tuple, ' +
#                             'it is treated as the domain of y, ' +
#                             'so len(x) must be 2')

#     if callable(y):
#         try: 
#             y = y(x)
#         except TypeError:
#             y = [y(x_) for x_ in x]

#     if np.shape(x) == np.shape(y):
#         if xerr is None and yerr is None:
#             plt.plot(x, y, fmt, label=label, alpha=alpha)
#         else:
#             c = None if fmt=='-' or fmt=='' else fmt
#             plt.errorbar(x, y, c=c, xerr=xerr, yerr=yerr, 
#                          label=label, alpha=alpha, capsize=capsize,
#                          marker='o', linestyle='--')
#     else:
#         raise ValueError( 'x and y must have same shape, ' +
#                          f'but have shapes {np.shape(x)} and {np.shape(y)}')


# @plotter_decorator()
# def scatter(
#     x: np.ndarray = [], 
#     y: np.ndarray = [], 
#     s: np.ndarray = None, 
#     c: str = None, 
#     marker: str = None, 
#     alpha: float = None, 
#     xerr: np.ndarray = None, 
#     yerr: np.ndarray = None, 
#     capsize: float = 3,

#     figsize: tuple = shared_params['figsize'], 
#     axis: bool = shared_params['axis'], 
#     grid: bool = shared_params['grid'], 
#     xlabel: str = shared_params['xlabel'], 
#     ylabel: str = shared_params['ylabel'], 
#     title: str = shared_params['title'],
#     xlim: tuple = shared_params['xlim'], 
#     ylim: tuple = shared_params['ylim'], 
#     legend: bool = shared_params['legend'], 
#     label: str = shared_params['label'], 
#     save: bool or str = shared_params['save'], 
#     override: bool = shared_params['override'], 
#     show: bool = shared_params['show']
# ):
#     if np.shape(x) == np.shape(y):
#         plt.scatter(x, y, s=s, c=c, alpha=alpha, label=label, marker=marker)
#         if xerr is not None or yerr is not None:
#             plt.errorbar(x, y, xerr=xerr, yerr=yerr, alpha=alpha, ecolor=c,
#                          marker='none', linestyle='none', capsize=capsize)
#     else:
#         raise ValueError( 'x and y must have same shape, ' +
#                          f'but have shapes {np.shape(x)} and {np.shape(y)}')


# @plotter_decorator()
# def hist(
#     x: np.ndarray = [], 
#     bins: int = 300, 
#     histtype: str = 'step', 
#     density: bool = True,

#     figsize: tuple = shared_params['figsize'], 
#     axis: bool = shared_params['axis'], 
#     grid: bool = shared_params['grid'], 
#     xlabel: str = shared_params['xlabel'], 
#     ylabel: str = shared_params['ylabel'], 
#     title: str = shared_params['title'],
#     xlim: tuple = shared_params['xlim'], 
#     ylim: tuple = shared_params['ylim'], 
#     legend: bool = shared_params['legend'], 
#     label: str = shared_params['label'], 
#     save: bool or str = shared_params['save'], 
#     override: bool = shared_params['override'], 
#     show: bool = shared_params['show']
# ):
#     plt.hist(x, bins=bins, histtype=histtype, density=density, label=label)


# @plotter_decorator(axis=False)
# def imshow(
#     x: np.ndarray = [], 
#     cmap: str = None, 
#     pillow: bool = False, 
#     colorbar:bool = True,
           
#     figsize: tuple = shared_params['figsize'], 
#     axis: bool = shared_params['axis'], 
#     grid: bool = shared_params['grid'], 
#     xlabel: str = shared_params['xlabel'], 
#     ylabel: str = shared_params['ylabel'], 
#     title: str = shared_params['title'],
#     xlim: tuple = shared_params['xlim'], 
#     ylim: tuple = shared_params['ylim'], 
#     legend: bool = shared_params['legend'], 
#     label: str = shared_params['label'], 
#     save: bool or str = shared_params['save'], 
#     override: bool = shared_params['override'], 
#     show: bool = shared_params['show']
# ):
#     if pillow:
#         PIL.Image.fromarray(x).show()
#     else:
#         plt.imshow(x, cmap=cmap)
#     if colorbar:
#         plt.colorbar()


# from math import sqrt

# point_1 = None
# point_2 = None
# characteristic_width = 1

# mode = '+-'

# def photon_number(img):
#     return img[point_1[1]][point_1[0]], img[point_2[1]][point_2[0]]


# def estimator(img, mode = mode):
#     if img[point_2[1]][point_2[0]] == 0:
#         return - 2 * characteristic_width
#     else:
#         if mode == '+-':
#             k = sqrt(img[point_1[1]][point_1[0]] / img[point_2[1]][point_2[0]])
#             return 2 * characteristic_width * (1-k)/(1+k)
#         elif mode == '0001':
#             return 2 * characteristic_width * sqrt(img[point_1[1]][point_1[0]] / img[point_2[1]][point_2[0]])



# import numpy as np

# zero_point = 0
# pixel_size = 1

# def photon_number(img):
#     return img.sum()

# def estimator(img, axis = 'y'):
#     if axis == 'x':
#         img = np.sum(img, axis=0)
#         img = img / img.sum()
#         index = np.arange(len(img))
#         return (zero_point - index @ img) * pixel_size
#     elif axis == 'y':
#         img = np.sum(img, axis=1)
#         img = img / img.sum()
#         index = np.arange(len(img))
#         return (zero_point - index @ img) * pixel_size

# def estimator_1d(img):
#     if len(np.shape(img)) != 1:
#         raise ValueError('img must be 1d')
#     else:
#         img = img / img.sum()
#         index = np.arange(len(img))
#         return (zero_point - index @ img) * pixel_size


# import numpy as np
# from math import sqrt, exp
# import os 
# from ..plotter import imread
# from .__init__ import sigma

# point_1 = (91, 110)
# point_2 = (91, 404)
# characteristic_width = sigma

# def photon_number(img):
#     if type(img) == str and os.path.exists(img):
#         img = imread(img)
#     return img[:, point_1[1], point_1[0]] + img[:, point_2[1], point_2[0]]


# def estimator(img, output_photon_number=True):
#     if type(img) == str and os.path.exists(img):
#         img = imread(img)

#     k = np.sqrt(img[:, point_1[1], point_1[0]] / img[:, point_2[1], point_2[0]])
#     result = 2 * characteristic_width * (1-k)/(1+k)
#     result[np.logical_not(np.isfinite(k))] = -2 * characteristic_width

#     if output_photon_number:
#         return result, photon_number(img)
#     else:
#         return result


# def p1(s, sigma):
#     return (s+2*sigma)**2*exp(-s**2/(4*sigma**2))/(8*sigma**2)

# def p2(s, sigma):
#     return (s-2*sigma)**2*exp(-s**2/(4*sigma**2))/(8*sigma**2)

# def gen_signal(s, resource):
#     random_data = np.random.uniform(0, 1, size = resource)
#     return np.histogram(random_data, bins=[0, p1(s), p1(s)+p2(s)])[0]


# import numpy as np
# import os 
# from ..plotter import imread
# from ..equipments import qcmos, dmd
# from . import sigma

# zero_point = 110.3
# pixel_size = 4.6
# axis_of_interest = 81
# bad_points = -2


# # def photon_number(img):
# #     if type(img) == str and os.path.exists(img):
# #         img = imread(img)
# #     img = img[:, :bad_points, axis_of_interest]
# #     return img.sum(axis=1)


# # def estimator(img, output_photon_number=True):
# #     if type(img) == str and os.path.exists(img):
# #         img = imread(img)
# #     img = img[:, :bad_points, axis_of_interest]
# #     normalized_img = img / img.sum()
# #     index = np.arange(len(normalized_img))
# #     if output_photon_number:
# #         return (zero_point - index @ normalized_img) * pixel_size, photon_number(img)
# #     else:
# #         return (zero_point - index @ normalized_img) * pixel_size


# # def gen_signal(s, resource, roi=218):
# #     s_pixels = zero_point - s / qcmos.pixel_size
# #     sigma_pixels = sigma / qcmos.pixel_size
# #     random_data = np.random.normal(s_pixels, sigma_pixels, size=resource)
# #     return np.histogram(random_data, bins=np.arange(roi+1))[0]



# # %%
# from mypy import *
# import cv2

# # 读取图像并转换为灰度图
# image = cv2.imread('/Users/me/Desktop/1.png')
# gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# imshow(gray)

# # %%
# gray = cv2.flip(gray, 0)
# imshow(gray)

# # %%
# # 二值化处理，找到曲线的轮廓
# _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# imshow(thresh)

# # %%
# # 寻找轮廓
# contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# # plot(np.arange(len(contours)), contours)
# scatter(contours[1].T[0], contours[1].T[1])

# # %%
# cnt = contours[1]
# # 获取轮廓的所有点
# x, y = max_min_normalization(cnt[:, 0, 0])*850+250, max_min_normalization(cnt[:, 0, 1])*90

# plot(x, y, label='QE')

# # %%
# qe = sp.interpolate.interp1d(x, y)

# # %%
# wavelength_range = np.linspace(250, 1100, 8501)

# # %%
# qe_line = qe(wavelength_range)

# # %%
# # np.save(arr=qe_line, file='/Users/me/Desktop/qe.npy')

# # %%
# # scatter(max_min_normalization(contours[1].T[0])*1100, max_min_normalization(contours[1].T[1])*90, show=False, marker='.')
# # plot((1,1100), qe, fmt='k')

# # %%
# qe(770)

# # %%
# qcmos.quantum_efficiency

# # %%
# open_mypy()

# # %%



# import numpy as np
# import matplotlib.pyplot as plt

# import os
# import PIL

# from .macro import sha1

# from matplotlib_inline import backend_inline
# backend_inline.set_matplotlib_formats('svg')
# del backend_inline

# from matplotlib.font_manager import fontManager
# fontManager.addfont(os.path.join(os.path.dirname(__file__), 'font/SourceHanSans.otf'))
# del fontManager

# plt.rcParams['font.family'] = ['Source Han Sans SC']
# plt.rcParams['figure.figsize'] = (6, 4)
# plt.rcParams['savefig.format'] = 'svg'

# __all__ = ['show_all_fonts', 
#            'set_font',
#            'figsize_fixed',
#            'imread',
#            'imwrite',
#            'plot',
#            'hist',
#            'scatter',
#            'imshow']


# def show_all_fonts():
#     from matplotlib.font_manager import get_font_names
#     all_fonts = get_font_names()
#     print('font list got from matplotlib.font_manager:')
#     for font in sorted(all_fonts):
#         print('\t' + font)


# def set_font(family=None, weight=None):
#     if family is None:
#         plt.rcParams['font.family'] = ['Source Han Sans SC']
#     else:
#         plt.rc('font', family=family, weight=weight)


# def figsize_fixed(x_figsize=None, y_figsize=None):
#     if isinstance(x_figsize, (tuple, list)) and (y_figsize is None):
#         plt.rcParams['figure.figsize'] = x_figsize
#     elif isinstance(y_figsize, (tuple, list)) and (x_figsize is None):
#         plt.rcParams['figure.figsize'] = y_figsize
#     elif x_figsize is None and y_figsize is None:
#         plt.rcParams['figure.figsize'] = (6, 4)
#         print('warning: x_figsize and y_figsize is None, setting figsize to default.')
#     elif isinstance(x_figsize, (int, float)) and isinstance(x_figsize, (int, float)):
#         plt.rcParams['figure.figsize'] = (x_figsize, y_figsize)
#     else:
#         raise ValueError('invalid figsize parameters')


# def imread(img_path):
#     if os.path.exists(img_path):
#         img = PIL.Image.open(img_path)
#         array = []
#         for i in range(img.n_frames):
#             img.seek(i)
#             array.append(np.array(img))
#         array = np.array(array, dtype=float)
#         if np.shape(array)[0] == 1:
#             return array[0]
#         else:
#             return array
#     else:
#         raise FileNotFoundError(f'{img_path} is not exists')


# def imwrite(array=None, save=None):
#     if array is not None:
#         if array.dtype == np.uint8:
#             PIL.Image.fromarray(array).save(save)
#         else:
#             raise TypeError('array.dtype must be np.uint8')
#     else:
#         raise TypeError('array is None')


# def save_util(save=None, override=False):
#     if type(save) is bool or type(save) is str:
#         if save == True:
#             while True:
#                 temp_name = f'{np.random.randint(0, 1e8)}.svg'
#                 if not os.path.exists(temp_name):
#                     break
#             plt.savefig(temp_name)
#             save = sha1(temp_name)
#             os.rename(src=temp_name, dst=f'{save}.svg')
#         elif type(save) is str:
#             if os.path.exists(save) or os.path.exists(f'{save}.svg'):
#                 if override:
#                     plt.savefig(save)
#                 else:
#                     raise FileExistsError('file already exists')
#             else:
#                 plt.savefig(save)
#     else:
#         raise TypeError('type(save) must be bool or str')
    

# pre_params = ['figsize', 'axis', 'grid', 'title', 'xlabel', 'ylabel']

# post_params = ['xlim', 'ylim', 'legend', 'label', 'save', 'override', 'show']


# def pre_process(**kwargs):
#     if kwargs['figsize'] is not None:
#         old_figsize = plt.rcParams['figure.figsize']
#         plt.rcParams['figure.figsize'] = kwargs['figsize']
#     if not kwargs['axis']:
#         plt.xticks([])
#         plt.yticks([])
#     if kwargs['title'] is not None:
#         plt.title(kwargs['title'])
#     if kwargs['grid'] is not None:    
#         plt.grid(kwargs['grid'])
#     if kwargs['xlabel'] is not None:
#         plt.xlabel(kwargs['xlabel'])
#     if kwargs['ylabel'] is not None:
#         plt.ylabel(kwargs['ylabel'])
#     return old_figsize



# def post_process(old_figsize, **kwargs):
#     if kwargs['xlim'] is not None:
#         plt.xlim(kwargs['xlim'])
#     if kwargs['ylim'] is not None:
#         plt.ylim(['ylim'])
#     if kwargs['legend'] and kwargs['label'] is not None:
#         plt.legend()
#     if kwargs['save'] is not None:
#         if (kwargs['save'] == True) and (kwargs['title'] is not None):
#             save_util(save=kwargs['title'], override=kwargs['override'])
#         else:
#             save_util(save=kwargs['save'], override=kwargs['override'])
#     if kwargs['show']:
#         plt.show()
#     if kwargs['figsize'] is not None:
#         plt.rcParams['figure.figsize'] = old_figsize


# def plot(x, y, fmt='-', dots=300, figsize=None,
#          alpha=None, xerr=None, yerr=None, capsize=3, 
#          axis=True, title=None, label=None, legend=True, 
#          xlabel=None, ylabel=None, xlim=None, ylim=None,
#          grid=True, show=True, save=None, override=False):

#     if isinstance(x, tuple):
#         if len(x) == 2:
#             if callable(y):
#                 x = np.linspace(x[0], x[1], dots)
#             else:
#                 x = np.linspace(x[0], x[1], len(y))
#         else:
#             raise TypeError('when x is a tuple, ' +
#                             'it is treated as the domain of y, ' +
#                             'so the len(x) must be 2')

#     if callable(y):
#         try: 
#             y = y(x)
#         except TypeError:
#             y = [y(x_) for x_ in x]

#     if figsize is not None:
#         old_figsize = plt.rcParams['figure.figsize']
#         plt.rcParams['figure.figsize'] = figsize
#     if not axis:
#         plt.xticks([])
#         plt.yticks([])
#     if title is not None:
#         plt.title(title)
#     if grid is not None:    
#         plt.grid(grid)
#     if xlabel is not None:
#         plt.xlabel(xlabel)
#     if ylabel is not None:
#         plt.ylabel(ylabel)

#     if np.shape(x) == np.shape(y):
#         if xerr is None and yerr is None:
#             plt.plot(x, y, fmt, label=label, alpha=alpha)
#         else:
#             c = None if fmt=='-' or fmt=='' else fmt
#             plt.errorbar(x, y, c=c, xerr=xerr, yerr=yerr, 
#                          label=label, alpha=alpha, capsize=capsize,
#                          marker='o', linestyle='--')
#     else:
#         raise ValueError( 'x and y must have same shape, ' +
#                          f'but have shapes {np.shape(x)} and {np.shape(y)}')

#     if xlim is not None:
#         plt.xlim(xlim)
#     if ylim is not None:
#         plt.ylim(ylim)
#     if legend and label is not None:
#         plt.legend()
#     if save is not None:
#         if (save == True) and (title is not None):
#             save_util(save=title, override=override)
#         else:
#             save_util(save=save, override=override)
#     if show:
#         plt.show()
#     if figsize is not None:
#         plt.rcParams['figure.figsize'] = old_figsize
#         del old_figsize


# def hist(x, bins=300, histtype='step', density=True, figsize=None,
#          axis=True, title=None, label=None, legend=True, 
#          xlabel=None, ylabel=None, xlim=None, ylim=None,
#          grid=True, show=True, save=None, override=False):

#     if figsize is not None:
#         old_figsize = plt.rcParams['figure.figsize']
#         plt.rcParams['figure.figsize'] = figsize
#     if not axis:
#         plt.xticks([])
#         plt.yticks([])
#     if title is not None:
#         plt.title(title)
#     if grid is not None:    
#         plt.grid(grid)
#     if xlabel is not None:
#         plt.xlabel(xlabel)
#     if ylabel is not None:
#         plt.ylabel(ylabel)

#     plt.hist(x, bins=bins, histtype=histtype, density=density, label=label)

#     if xlim is not None:
#         plt.xlim(xlim)
#     if ylim is not None:
#         plt.ylim(ylim)
#     if legend and label is not None:
#         plt.legend()
#     if save is not None:
#         if (save == True) and (title is not None):
#             save_util(save=title, override=override)
#         else:
#             save_util(save=save, override=override)
#     if show:
#         plt.show()
#     if figsize is not None:
#         plt.rcParams['figure.figsize'] = old_figsize
#         del old_figsize


# def scatter(x, y, s=None, c=None, marker=None, colorbar=False, figsize=None,
#             alpha=None, xerr=None, yerr=None, capsize=3,
#             axis=True, title=None, label=None, legend=True, 
#             xlabel=None, ylabel=None, xlim=None, ylim=None,
#             grid=True, show=True, save=None, override=False):

#     if figsize is not None:
#         old_figsize = plt.rcParams['figure.figsize']
#         plt.rcParams['figure.figsize'] = figsize
#     if not axis:
#         plt.xticks([])
#         plt.yticks([])
#     if title is not None:
#         plt.title(title)
#     if grid is not None:    
#         plt.grid(grid)
#     if xlabel is not None:
#         plt.xlabel(xlabel)
#     if ylabel is not None:
#         plt.ylabel(ylabel)

#     if np.shape(x) == np.shape(y):
#         plt.scatter(x, y, s=s, c=c, alpha=alpha, label=label, marker=marker)
#         if xerr is not None or yerr is not None:
#             plt.errorbar(x, y, xerr=xerr, yerr=yerr, alpha=alpha, ecolor=c,
#                          marker='none', linestyle='none', capsize=capsize)
#     else:
#         raise ValueError( 'x and y must have same shape, ' +
#                          f'but have shapes {np.shape(x)} and {np.shape(y)}')

#     if xlim is not None:
#         plt.xlim(xlim)
#     if ylim is not None:
#         plt.ylim(ylim)
#     if legend and label is not None:
#         plt.legend()
#     if colorbar and (c is not None):
#         plt.colorbar()
#     if save is not None:
#         if (save == True) and (title is not None):
#             save_util(save=title, override=override)
#         else:
#             save_util(save=save, override=override)
#     if show:
#         plt.show()
#     if figsize is not None:
#         plt.rcParams['figure.figsize'] = old_figsize
#         del old_figsize


# def imshow(x, cmap=None, pillow=False, figsize=None, 
#            axis=False, title=None, colorbar=True,
#            xlabel=None, ylabel=None, xlim=None, ylim=None,
#            grid=True, show=True, save=None, override=False):


#     if pillow:
#         PIL.Image.fromarray(x).show()
#     else:
#         if figsize is not None:
#             old_figsize = plt.rcParams['figure.figsize']
#             plt.rcParams['figure.figsize'] = figsize
#         if not axis:
#             plt.xticks([])
#             plt.yticks([])
#         if title is not None:
#             plt.title(title)
#         if grid is not None:    
#             plt.grid(grid)
#         if xlabel is not None:
#             plt.xlabel(xlabel)
#         if ylabel is not None:
#             plt.ylabel(ylabel)

#         plt.imshow(x, cmap=cmap)

#         if xlim is not None:
#             plt.xlim(xlim)
#         if ylim is not None:
#             plt.ylim(ylim)
#         if colorbar:
#             plt.colorbar()
#         if save is not None:
#             if (save == True) and (title is not None):
#                 save_util(save=title, override=override)
#             else:
#                 save_util(save=save, override=override)
#         if show:
#             plt.show()
#         if figsize is not None:
#             plt.rcParams['figure.figsize'] = old_figsize
#             del old_figsize


# def name_dict(i):
#     if type(i) is int:
#         if i < 0:
#             return(f'm({abs(i)})')
#         else:
#             return(f'p({abs(i)})')
#     else:
#         raise TypeError


# import numpy as np
# from math import *
# from .equipments import *
# from .experiments import *
# from .macro import *

# sigma = 103 
# samples = 1000
# sample_rate = 20 
# dmd_frequency = 1

# spade_total_n = 100 
# di_total_n = 500
# psf_type = 'gauss'

# noise_type = 'poisson'
# spade_noise_intensity = 100
# spade_noise_std = 4.12
# di_noise_intensity = 100
# di_noise_std = 4.12

# di_roi = 218
# zero_point = 107
# pixel_size = 4.6

# p1 = lambda s: (s+2*sigma)**2*exp(-s**2/(4*sigma**2))/(8*sigma**2)
# p2 = lambda s: (s-2*sigma)**2*exp(-s**2/(4*sigma**2))/(8*sigma**2)

# def gen_signal(method: str, s):
#     if method.lower() == 'spade':
#         random_data = np.random.uniform(0, 1, size = spade_total_n)
#         return np.histogram(random_data, bins=[0, p1(s), p1(s)+p2(s)])[0]

#     if method.lower() == 'di':
#         s_ = zero_point - s / qcmos.pixel_size
#         sigma_ = sigma / qcmos.pixel_size
#         random_data = np.random.normal(s_, sigma_, size=di_total_n)
#         return np.histogram(random_data, bins=np.arange(di_roi+1))[0]


# def gen_static(s):
#     spade_signal, di_signal = [], []
#     for _ in range(samples):
#         spade_signal.append(gen_signal('spade', s))
#         di_signal.append(gen_signal('di', s))
#     return np.array(spade_signal), np.array(di_signal)


# def gen_dynamic(s):
#     spade_signal, di_signal, count, dmd_flip = [], [], 0, True
#     for _ in range(samples):
#         if dmd_flip:
#             spade_signal.append(gen_signal('spade', 0))
#             di_signal.append(gen_signal('di', 0))
#         else:
#             spade_signal.append(gen_signal('spade', s))
#             di_signal.append(gen_signal('di', s))
#         count = count + 1
#         if count == int(sample_rate / dmd_frequency):
#             count, dmd_flip = 0, not dmd_flip
#     return np.array(spade_signal), np.array(di_signal)


# def add_noise(signal, noise_intensity, noise_std):
#     if noise_type.lower() == 'poisson':
#         noise = np.random.poisson(noise_intensity, size=np.shape(signal))
#     if noise_type.lower() == 'gauss':
#         def relu_and_round(arr):
#             arr[arr < 0] = 0
#             return np.round(arr, 0)
#         noise = relu_and_round(np.random.normal(noise_intensity, noise_std, size=np.shape(signal)))
#     return signal + noise


# def estimator(method: str, data: np.ndarray):
#     if method.lower() == 'spade':
#         k = np.sqrt(data[:, 1] / data[:, 0])
#         result = 2 * sigma * (1-k)/(1+k)
#         result[np.logical_not(np.isfinite(k))] = -2 * sigma
#         return result, data.sum(axis=1)

#     if method.lower() == 'di':
#         data_sum = data.sum(axis=1)
#         index = np.arange(data.shape[1])
#         normalized_data = data / data_sum.reshape(samples, 1)
#         return (zero_point - normalized_data @ index) * pixel_size, data_sum



# from inspect import signature
# 由于自己写的算法效率低, 计算速度慢, 弃用

# def variables_of(func: Callable):
#     return len(signature(func).parameters)

# 
# def gradient(func: Callable, variable=0, epsilon=1e-4):
#     def wrapper(*args):
#         # if len(args) != variables_of(func):
#         #     raise ValueError('specific point to calculate gradient must be provided')
        
#         args_1 = list(args[:])
#         args_1[variable] = args_1[variable] + epsilon / 2

#         args_2 = list(args[:])
#         args_2[variable] = args_2[variable] - epsilon / 2

#         return (func(*args_1) - func(*args_2)) / epsilon
#     return np.vectorize(wrapper)


# def pdv(order: int):
#     def wrapper(func: Callable, variable=0, epsilon=1e-4):
#         for _ in range(order):
#             func = gradient(func, variable=variable, epsilon=epsilon)
#         return func
#     return wrapper


# def gradient_descent(func: Callable, 
#                      init: ArrayLike = None, 
#                      eta: ArrayLike = None,
#                      accuracy: float = None,
#                      max_loops:float = inf,
#                      epsilon = 1e-4):
    
#     variables = variables_of(func)
#     if len(init) != variables:
#         raise ValueError('specific initial value must be provided')

#     gd_result = list(init[:])
#     gd_process = list(init[:])

#     converged = [False for _ in range(variables)]
#     loop = 1

#     while(True):
#         converged = [False for _ in range(variables)]
#         for variable in range(variables):
#             update = eta[variable] * gradient(func, variable, epsilon)(*gd_result)
#             gd_result[variable] = gd_result[variable] - update
#             if (abs(update) <= accuracy):
#                 converged[variable] = True
#             gd_process.append(gd_result[variable])
#         loop = loop + 1
#         if np.array(converged).all() or (loop >= max_loops):
#             break

#     if loop >= max_loops:
#         print('warning: max_loops has reached, the algorithm might not converged')
#     if variables == 1:
#         return gd_result, np.array(gd_process)
#     else:
#         return gd_result, np.array(gd_process).reshape(int(len(gd_process)/variables), variables).T


    # def _img(self):
    #     img = Image.open(self.path)
    #     try:
    #         n_frames = img.n_frames
    #     except AttributeError:
    #         n_frames = 1
    #     arr = []
    #     for i in range(n_frames):
    #         if n_frames > 1:
    #             img.seek(i)
    #         arr.append(np.array(img))

    #     arr = np.array(arr).astype(self.dtype)
    #     if arr.shape[0] == 1:
    #         return arr[0]
    #     return arr



    # def _avi(self):
    #     avi = cv2.VideoCapture(self.path)
    #     arr = []
    #     while True:
    #         ret, frame = avi.read()
    #         if not ret:
    #             break
    #         arr.append(cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY))
    #     avi.release()
    #     return np.array(arr).astype(self.dtype)

