docs and new UI

This commit is contained in:
Ali Abid 2020-07-16 12:56:36 -07:00
parent 066b16fa42
commit 32f2d96c63
27 changed files with 877 additions and 673 deletions

1
.gitignore vendored
View File

@ -19,3 +19,4 @@ __pycache__/
demo/models/*
dist/*
*.h5
docs.json

View File

@ -0,0 +1,68 @@
import json
from gradio.inputs import AbstractInput
from gradio.outputs import AbstractOutput
from gradio.interface import Interface
import inspect
def get_params(func):
params_str = inspect.getdoc(func)
params_doc = []
documented_params = {"self"}
for param_line in params_str.split("\n")[1:]:
space_index = param_line.index(" ")
colon_index = param_line.index(":")
name = param_line[:space_index]
documented_params.add(name)
params_doc.append((name, param_line[space_index+2:colon_index-1], param_line[colon_index+2:]))
params = inspect.getfullargspec(func)
param_set = []
for i in range(len(params.args)):
neg_index = -1 - i
if params.args[neg_index] not in documented_params:
continue
if i < len(params.defaults):
default = params.defaults[neg_index]
if type(default) == str:
default = '"' + default + '"'
else:
default = str(default)
param_set.insert(0, (params.args[neg_index], default))
else:
param_set.insert(0, (params.args[neg_index],))
return param_set, params_doc
def document(cls_set):
docset = []
for cls in cls_set:
inp = {}
inp["name"] = cls.__name__
doc = inspect.getdoc(cls)
inp["doc"] = "\n".join(doc.split("\n")[:-1])
inp["type"] = doc.split("\n")[-1].split("type: ")[-1]
inp["params"], inp["params_doc"] = get_params(cls.__init__)
inp["shortcuts"] = list(cls.get_shortcut_implementations().items())
docset.append(inp)
return docset
inputs = document(AbstractInput.__subclasses__())
outputs = document(AbstractOutput.__subclasses__())
interface_params = get_params(Interface.__init__)
interface = {
"doc": inspect.getdoc(Interface),
"params": interface_params[0],
"params_doc": interface_params[1],
}
launch_params = get_params(Interface.launch)
launch = {
"params": launch_params[0],
"params_doc": launch_params[1],
}
with open("docs.json", "w") as docs:
json.dump({
"inputs": inputs,
"outputs": outputs,
"interface": interface,
"launch": launch
}, docs)

View File

@ -4,15 +4,18 @@ This module defines various classes that can serve as the `input` to an interfac
automatically added to a registry, which allows them to be easily referenced in other parts of the code.
"""
from abc import ABC, abstractmethod
from gradio import preprocessing_utils, validation_data
import numpy as np
import PIL.Image, PIL.ImageOps
import datetime
import json
import os
import time
import warnings
import json
import datetime
import os
from abc import ABC, abstractmethod
import numpy as np
import PIL.Image
import PIL.ImageOps
import scipy.io.wavfile
from gradio import preprocessing_utils, validation_data
# Where to find the static resources associated with each template.
# BASE_INPUT_INTERFACE_TEMPLATE_PATH = 'static/js/interfaces/input/{}.js'
@ -24,6 +27,7 @@ class AbstractInput(ABC):
An abstract class for defining the methods that all gradio inputs should have.
When this is subclassed, it is automatically added to the registry
"""
def __init__(self, label):
self.label = label
@ -60,79 +64,27 @@ class AbstractInput(ABC):
return {}
class Sketchpad(AbstractInput):
def __init__(self, shape=(28, 28), invert_colors=True,
flatten=False, label=None):
self.image_width = shape[0]
self.image_height = shape[1]
self.invert_colors = invert_colors
self.flatten = flatten
super().__init__(label)
@classmethod
def get_shortcut_implementations(cls):
return {
"sketchpad": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for the SketchPad is to convert the sketch to black and white and resize 28x28
"""
im_transparent = preprocessing_utils.decode_base64_to_image(inp)
im = PIL.Image.new("RGBA", im_transparent.size, "WHITE") # Create a white background for the alpha channel
im.paste(im_transparent, (0, 0), im_transparent)
im = im.convert('L')
if self.invert_colors:
im = PIL.ImageOps.invert(im)
im = im.resize((self.image_width, self.image_height))
if self.flatten:
array = np.array(im).flatten().reshape(1, self.image_width * self.image_height)
else:
array = np.array(im).flatten().reshape(1, self.image_width, self.image_height)
return array
def process_example(self, example):
return preprocessing_utils.convert_file_to_base64(example)
class Webcam(AbstractInput):
def __init__(self, shape=(224, 224), label=None):
self.image_width = shape[0]
self.image_height = shape[1]
self.num_channels = 3
super().__init__(label)
def get_validation_inputs(self):
return validation_data.BASE64_COLOR_IMAGES
@classmethod
def get_shortcut_implementations(cls):
return {
"webcam": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for is to convert the picture to black and white and resize to be 48x48
"""
im = preprocessing_utils.decode_base64_to_image(inp)
im = im.convert('RGB')
im = preprocessing_utils.resize_and_crop(im, (self.image_width, self.image_height))
return np.array(im)
class Textbox(AbstractInput):
"""
Component creates a textbox for user to enter input. Provides a string (or number is `is_numeric` is true) as an argument to the wrapped function.
Input type: str
"""
def __init__(self, lines=1, placeholder=None, default=None, numeric=False, label=None):
'''
Parameters:
lines (int): number of line rows to provide in textarea.
placeholder (str): placeholder hint to provide behind textarea.
default (str): default text to provide in textarea.
numeric (bool): whether the input should be parsed as a number instead of a string.
label (str): component name in interface.
'''
self.lines = lines
self.placeholder = placeholder
self.default = default
self.numeric = numeric
super().__init__(label)
def get_validation_inputs(self):
return validation_data.ENGLISH_TEXTS
def get_template_context(self):
return {
"lines": self.lines,
@ -159,44 +111,21 @@ class Textbox(AbstractInput):
return inp
class Radio(AbstractInput):
def __init__(self, choices, label=None):
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Dropdown(AbstractInput):
def __init__(self, choices, label=None):
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class CheckboxGroup(AbstractInput):
def __init__(self, choices, label=None):
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Slider(AbstractInput):
def __init__(self, minimum=0, maximum=100, default=None, label=None):
"""
Component creates a slider that ranges from `minimum` to `maximum`. Provides a number as an argument to the wrapped function.
Input type: float
"""
def __init__(self, minimum=0, maximum=100, step=None, default=None, label=None):
'''
Parameters:
minimum (float): minimum value for slider.
maximum (float): maximum value for slider.
step (float): increment between slider values.
default (float): default value.
label (str): component name in interface.
'''
self.minimum = minimum
self.maximum = maximum
self.default = minimum if default is None else default
@ -218,7 +147,16 @@ class Slider(AbstractInput):
class Checkbox(AbstractInput):
"""
Component creates a checkbox that can be set to `True` or `False`. Provides a boolean as an argument to the wrapped function.
Input type: bool
"""
def __init__(self, label=None):
'''
Parameters:
label (str): component name in interface.
'''
super().__init__(label)
@classmethod
@ -228,8 +166,85 @@ class Checkbox(AbstractInput):
}
class CheckboxGroup(AbstractInput):
"""
Component creates a set of checkboxes of which a subset can be selected. Provides a list of strings representing the selected choices as an argument to the wrapped function.
Input type: List[str]
"""
def __init__(self, choices, label=None):
'''
Parameters:
choices (List[str]): list of options to select from.
label (str): component name in interface.
'''
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Radio(AbstractInput):
"""
Component creates a set of radio buttons of which only one can be selected. Provides string representing selected choice as an argument to the wrapped function.
Input type: str
"""
def __init__(self, choices, label=None):
'''
Parameters:
choices (List[str]): list of options to select from.
label (str): component name in interface.
'''
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Dropdown(AbstractInput):
"""
Component creates a dropdown of which only one can be selected. Provides string representing selected choice as an argument to the wrapped function.
Input type: str
"""
def __init__(self, choices, label=None):
'''
Parameters:
choices (List[str]): list of options to select from.
label (str): component name in interface.
'''
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Image(AbstractInput):
"""
Component creates an image upload box with editing capabilities. Provides numpy array of shape `(width, height, 3)` if `image_mode` is "RGB" as an argument to the wrapped function. Provides numpy array of shape `(width, height)` if `image_mode` is "L" as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, shape=(224, 224), image_mode='RGB', label=None):
'''
Parameters:
shape (Tuple[int, int]): shape to crop and resize image to.
image_mode (str): "RGB" if color, or "L" if black and white.
label (str): component name in interface.
'''
self.image_width = shape[0]
self.image_height = shape[1]
self.image_mode = image_mode
@ -258,7 +273,8 @@ class Image(AbstractInput):
warnings.simplefilter("ignore")
im = im.convert(self.image_mode)
im = preprocessing_utils.resize_and_crop(im, (self.image_width, self.image_height))
im = preprocessing_utils.resize_and_crop(
im, (self.image_width, self.image_height))
return np.array(im)
def process_example(self, example):
@ -268,21 +284,132 @@ class Image(AbstractInput):
return example
class Microphone(AbstractInput):
def __init__(self, label=None):
class Sketchpad(AbstractInput):
"""
Component creates a sketchpad for black and white illustration. Provides numpy array of shape `(width, height)` as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, shape=(28, 28), invert_colors=True,
flatten=False, label=None):
'''
Parameters:
shape (Tuple[int, int]): shape to crop and resize image to.
invert_colors (bool): whether to represent black as 1 and white as 0 in the numpy array.
flatten (bool): whether to reshape the numpy array to a single dimension.
label (str): component name in interface.
'''
self.image_width = shape[0]
self.image_height = shape[1]
self.invert_colors = invert_colors
self.flatten = flatten
super().__init__(label)
@classmethod
def get_shortcut_implementations(cls):
return {
"sketchpad": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for the SketchPad is to convert the sketch to black and white and resize 28x28
"""
im_transparent = preprocessing_utils.decode_base64_to_image(inp)
# Create a white background for the alpha channel
im = PIL.Image.new("RGBA", im_transparent.size, "WHITE")
im.paste(im_transparent, (0, 0), im_transparent)
im = im.convert('L')
if self.invert_colors:
im = PIL.ImageOps.invert(im)
im = im.resize((self.image_width, self.image_height))
if self.flatten:
array = np.array(im).flatten().reshape(
1, self.image_width * self.image_height)
else:
array = np.array(im).flatten().reshape(
1, self.image_width, self.image_height)
return array
def process_example(self, example):
return preprocessing_utils.convert_file_to_base64(example)
class Webcam(AbstractInput):
"""
Component creates a webcam for captured image input. Provides numpy array of shape `(width, height, 3)` as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, shape=(224, 224), label=None):
'''
Parameters:
shape (Tuple[int, int]): shape to crop and resize image to.
label (str): component name in interface.
'''
self.image_width = shape[0]
self.image_height = shape[1]
self.num_channels = 3
super().__init__(label)
def get_validation_inputs(self):
return validation_data.BASE64_COLOR_IMAGES
@classmethod
def get_shortcut_implementations(cls):
return {
"webcam": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for is to convert the picture to black and white and resize to be 48x48
"""
im = preprocessing_utils.decode_base64_to_image(inp)
im = im.convert('RGB')
im = preprocessing_utils.resize_and_crop(
im, (self.image_width, self.image_height))
return np.array(im)
class Microphone(AbstractInput):
"""
Component creates a microphone element for audio inputs. Provides numpy array of shape `(samples, 2)` as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, preprocessing=None, label=None):
'''
Parameters:
preprocessing (Union[str, Callable]): preprocessing to apply to input
label (str): component name in interface.
'''
super().__init__(label)
if preprocessing is None or preprocessing == "mfcc":
self.preprocessing = preprocessing
else:
raise ValueError(
"unexpected value for preprocessing", preprocessing)
@classmethod
def get_shortcut_implementations(cls):
return {
"microphone": {},
}
def preprocess(self, inp):
"""
By default, no pre-processing is applied to a microphone input file
"""
file_obj = preprocessing_utils.decode_base64_to_wav_file(inp)
mfcc_array = preprocessing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
return mfcc_array
if self.preprocessing == "mfcc":
return preprocessing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
_, signal = scipy.io.wavfile.read(file_obj.name)
return signal
# Automatically adds all shortcut implementations in AbstractInput into a dictionary.
shortcuts = {}
for cls in AbstractInput.__subclasses__():
for shortcut, parameters in cls.get_shortcut_implementations().items():
shortcuts[shortcut] = cls(**parameters)
shortcuts[shortcut] = cls(**parameters)

View File

@ -33,8 +33,7 @@ except requests.ConnectionError:
class Interface:
"""
The Interface class represents a general input/output interface for a machine learning model. During construction,
the appropriate inputs and outputs
Interfaces are created with Gradio using the `gradio.Interface()` function.
"""
instances = weakref.WeakSet()
@ -43,9 +42,15 @@ class Interface:
capture_session=False, title=None, description=None,
thumbnail=None, server_name=networking.LOCALHOST_NAME):
"""
:param fn: a function that will process the input panel data from the interface and return the output panel data.
:param inputs: a string or `AbstractInput` representing the input interface.
:param outputs: a string or `AbstractOutput` representing the output interface.
Parameters:
fn (Callable): the function to wrap an interface around.
inputs (Union[str, List[Union[str, AbstractInput]]]): a single Gradio input component, or list of Gradio input components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of input components should match the number of parameters in fn.
outputs (Union[str, List[Union[str, AbstractOutput]]]): a single Gradio output component, or list of Gradio output components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of output components should match the number of values returned by fn.
live (bool): whether the interface should automatically reload on change.
capture_session (bool): if True, captures the default graph and session (needed for Tensorflow 1.x)
title (str): a title for the interface; if provided, appears above the input and output components.
description (str): a description for the interface; if provided, appears above the input and output components.
examples (List[List[Any]]): sample inputs for the function; if provided, appears below the UI components and can be used to populate the interface. Should be nested list, in which the outer list consists of samples and each inner list consists of an input corresponding to each input component.
"""
def get_input_instance(iface):
if isinstance(iface, str):
@ -257,11 +262,8 @@ class Interface:
def launch(self, inline=None, inbrowser=None, share=False, validate=True, debug=False):
"""
Standard method shared by interfaces that creates the interface and sets up a websocket to communicate with it.
:param inline: boolean. If True, then a gradio interface is created inline (e.g. in jupyter or colab notebook)
:param inbrowser: boolean. If True, then a new browser window opens with the gradio interface.
:param share: boolean. If True, then a share link is generated using ngrok is displayed to the user.
:param validate: boolean. If True, then the validation is run if the interface has not already been validated.
Parameters
share (bool): whether to create a publicly shareable link from your computer for the interface.
"""
# if validate and not self.validate_flag:
# self.validate()

View File

@ -116,6 +116,17 @@ def get_first_available_port(initial, final):
)
)
def send_prediction_analytics(interface):
data = {'input_interface': interface.input_interfaces,
'output_interface': interface.output_interfaces,
}
try:
requests.post(
analytics_url + 'gradio-prediction-analytics/',
data=data)
except requests.ConnectionError:
pass # do not push analytics if no network
def serve_files_in_background(interface, port, directory_to_serve=None, server_name=LOCALHOST_NAME):
class HTTPHandler(SimpleHTTPRequestHandler):
@ -160,17 +171,11 @@ def serve_files_in_background(interface, port, directory_to_serve=None, server_n
# f.write(json.dumps(output_flag))
# f.write("\n")
# Prepare return json dictionary.
self.wfile.write(json.dumps(output).encode())
data = {'input_interface': interface.input_interfaces,
'output_interface': interface.output_interfaces,
}
try:
requests.post(
analytics_url + 'gradio-prediction-analytics/',
data=data)
except requests.ConnectionError:
pass # do not push analytics if no network
analytics_thread = threading.Thread(
target=send_prediction_analytics, args=[interface])
analytics_thread.start()
elif self.path == "/api/flag/":
self._set_headers()

View File

@ -21,6 +21,7 @@ class AbstractOutput(ABC):
An abstract class for defining the methods that all gradio inputs should have.
When this is subclassed, it is automatically added to the registry
"""
def __init__(self, label):
self.label = label
@ -44,12 +45,56 @@ class AbstractOutput(ABC):
return {}
class Textbox(AbstractOutput):
'''
Component creates a textbox to render output text or number.
Output type: str
'''
def __init__(self, label=None):
'''
Parameters:
label (str): component name in interface.
'''
super().__init__(label)
def get_template_context(self):
return {
**super().get_template_context()
}
@classmethod
def get_shortcut_implementations(cls):
return {
"text": {},
"textbox": {},
"number": {},
}
def postprocess(self, prediction):
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
return str(prediction)
else:
raise ValueError("The `Textbox` output interface expects an output that is one of: a string, or"
"an int/float that can be converted to a string.")
class Label(AbstractOutput):
'''
Component outputs a classification label, along with confidence scores of top categories if provided. Confidence scores are represented as a dictionary mapping labels to scores between 0 and 1.
Output type: Union[Dict[str, float], str, int, float]
'''
LABEL_KEY = "label"
CONFIDENCE_KEY = "confidence"
CONFIDENCES_KEY = "confidences"
def __init__(self, num_top_classes=None, label=None):
'''
Parameters:
num_top_classes (int): number of most confident classes to show.
label (str): component name in interface.
'''
self.num_top_classes = num_top_classes
super().__init__(label)
@ -58,7 +103,7 @@ class Label(AbstractOutput):
return {"label": str(prediction)}
elif isinstance(prediction, dict):
sorted_pred = sorted(
prediction.items(),
prediction.items(),
key=operator.itemgetter(1),
reverse=True
)
@ -86,51 +131,18 @@ class Label(AbstractOutput):
}
class KeyValues(AbstractOutput):
def __init__(self, label=None):
super().__init__(label)
def postprocess(self, prediction):
if isinstance(prediction, dict):
return prediction
else:
raise ValueError("The `KeyValues` output interface expects an output that is a dictionary whose keys are "
"labels and values are corresponding values.")
@classmethod
def get_shortcut_implementations(cls):
return {
"key_values": {},
}
class Textbox(AbstractOutput):
def __init__(self, label=None):
super().__init__(label)
def get_template_context(self):
return {
**super().get_template_context()
}
@classmethod
def get_shortcut_implementations(cls):
return {
"text": {},
"textbox": {},
"number": {},
}
def postprocess(self, prediction):
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
return str(prediction)
else:
raise ValueError("The `Textbox` output interface expects an output that is one of: a string, or"
"an int/float that can be converted to a string.")
class Image(AbstractOutput):
'''
Component displays an image. Expects a numpy array of shape `(width, height, 3)` to be returned by the function, or a `matplotlib.pyplot` if `plot = True`.
Output type: numpy.array
'''
def __init__(self, plot=False, label=None):
'''
Parameters:
plot (bool): whether to expect a plot to be returned by the function.
label (str): component name in interface.
'''
self.plot = plot
super().__init__(label)
@ -154,7 +166,8 @@ class Image(AbstractOutput):
try:
return preprocessing_utils.encode_array_to_base64(prediction)
except:
raise ValueError("The `Image` output interface (with plt=False) expects a numpy array.")
raise ValueError(
"The `Image` output interface (with plt=False) expects a numpy array.")
def rebuild_flagged(self, dir, msg):
"""
@ -168,6 +181,33 @@ class Image(AbstractOutput):
return filename
class KeyValues(AbstractOutput):
'''
Component displays a table representing values for multiple fields.
Output type: List[Tuple[str, value]]
'''
def __init__(self, label=None):
'''
Parameters:
label (str): component name in interface.
'''
super().__init__(label)
def postprocess(self, prediction):
if isinstance(prediction, dict):
return prediction
else:
raise ValueError("The `KeyValues` output interface expects an output that is a dictionary whose keys are "
"labels and values are corresponding values.")
@classmethod
def get_shortcut_implementations(cls):
return {
"key_values": {},
}
# Automatically adds all shortcut implementations in AbstractInput into a dictionary.
shortcuts = {}
for cls in AbstractOutput.__subclasses__():

View File

@ -60,11 +60,9 @@ input.panel_button {
flex-grow: 1;
padding: 12px;
box-sizing: border-box;
text-transform: uppercase;
font-weight: bold;
border: 0 none;
border-radius: 4px;
transition: background-color 0.2s ease;
margin-left: 16px;
flex-grow: 1;
}
@ -78,11 +76,6 @@ input.submit {
input.submit:hover {
background-color: #f39c12;
}
label {
transition: background-color 0.2s ease;
padding: 2px 4px;
border-radius: 2px;
}
/* label:hover {
background-color: lightgray;
} */

View File

@ -13,30 +13,8 @@
margin-top: 12px;
margin-left: 16px;
}
.ui-icon-background, .ui-state-active .ui-icon-background {
border: #f39c12;
background-color: #ffffff;
.checkbox_solo label {
width: 27px !important;
padding-left: 11px;
padding-right: 3px;
}
.ui-visual-focus {
box-shadow: 0 0 3px 1px rgb(241, 171, 41);
}
.ui-button {
padding: 0.25em 0.6em;
}
.ui-widget {
font-family: inherit;
}
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, .ui-button, html .ui-button.ui-state-disabled:hover, html .ui-button.ui-state-disabled:active {
border: 1px solid lightgray;
background: lightgray;
}
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
border: 1px solid #e67e22 !important;
background: #e67e22 !important;
font-weight: normal;
color: #ffffff;
}
.ui-button {
outline: none;
}

View File

@ -1,3 +0,0 @@
.key_values {
width: 100%;
}

View File

@ -1,7 +1,7 @@
/*! jQuery UI - v1.12.1 - 2016-09-14
* http://jqueryui.com
* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=3px&bgColorHeader=e9e9e9&bgTextureHeader=flat&borderColorHeader=dddddd&fcHeader=333333&iconColorHeader=444444&bgColorContent=ffffff&bgTextureContent=flat&borderColorContent=dddddd&fcContent=333333&iconColorContent=444444&bgColorDefault=f6f6f6&bgTextureDefault=flat&borderColorDefault=c5c5c5&fcDefault=454545&iconColorDefault=777777&bgColorHover=ededed&bgTextureHover=flat&borderColorHover=cccccc&fcHover=2b2b2b&iconColorHover=555555&bgColorActive=007fff&bgTextureActive=flat&borderColorActive=003eff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=fffa90&bgTextureHighlight=flat&borderColorHighlight=dad55e&fcHighlight=777620&iconColorHighlight=777620&bgColorError=fddfdf&bgTextureError=flat&borderColorError=f1a899&fcError=5f3f3f&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
* Copyright jQuery Foundation and other contributors; Licensed MIT */
/* Layout helpers
@ -164,7 +164,7 @@
right: 0;
}
.ui-button {
padding: .4em 1em;
padding: 0.25em 0.6em;
display: inline-block;
position: relative;
line-height: normal;
@ -290,9 +290,7 @@ button.ui-button::-moz-focus-inner {
}
.ui-checkboxradio-label .ui-icon-background {
box-shadow: inset 1px 1px 1px #ccc;
border-radius: .12em;
border: none;
}
.ui-checkboxradio-radio-label .ui-icon-background {
width: 16px;
@ -301,12 +299,15 @@ button.ui-button::-moz-focus-inner {
overflow: visible;
border: none;
}
.ui-checkboxradio-label .ui-icon-background {
border: solid 1px lightgray;
}
.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,
.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon {
background-image: none;
width: 8px;
height: 8px;
border-width: 4px;
border-width: 5px;
border-style: solid;
}
.ui-checkboxradio-disabled {
@ -880,10 +881,11 @@ button.ui-button::-moz-focus-inner {
body .ui-tooltip {
border-width: 2px;
}
/* Component containers
----------------------------------*/
.ui-widget {
font-family: Arial,Helvetica,sans-serif;
font-family: inherit;
font-size: 1em;
}
.ui-widget .ui-widget {
@ -928,8 +930,8 @@ body .ui-tooltip {
works properly when clicked or hovered */
html .ui-button.ui-state-disabled:hover,
html .ui-button.ui-state-disabled:active {
border: 1px solid #c5c5c5;
background: #f6f6f6;
border: 1px solid lightgray;
background: white;
font-weight: normal;
color: #454545;
}
@ -970,23 +972,25 @@ a.ui-button:focus {
text-decoration: none;
}
.ui-visual-focus {
box-shadow: 0 0 3px 1px rgb(94, 158, 214);
.ui-checkboxradio-label {
border: none !important;
background: none;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active,
a.ui-button:active,
.ui-button:active,
.ui-button.ui-state-active:hover {
border: 1px solid #003eff;
background: #007fff;
border: 1px solid #e67e22;
background: #e67e22;
font-weight: normal;
color: #ffffff;
}
.ui-icon-background,
.ui-state-active .ui-icon-background {
border: #003eff;
border: solid 1px #f39c12;
background-color: #ffffff;
}
.ui-state-active a,
@ -1302,8 +1306,8 @@ a.ui-button:active,
/* Overlays */
.ui-widget-overlay {
background: #aaaaaa;
opacity: .3;
filter: Alpha(Opacity=30); /* support: IE8 */
opacity: .003;
filter: Alpha(Opacity=.3); /* support: IE8 */
}
.ui-widget-shadow {
-webkit-box-shadow: 0px 0px 5px #666666;

View File

@ -6,8 +6,8 @@ function gradio(config, fn, target) {
<div class="input_interfaces">
</div>
<div class="panel_buttons">
<input class="submit panel_button" type="submit" value="submit"/>
<input class="clear panel_button" type="reset" value="clear">
<input class="clear panel_button" type="reset" value="CLEAR">
<input class="submit panel_button" type="submit" value="SUBMIT"/>
</div>
</div>
<div class="panel output_panel">
@ -18,8 +18,8 @@ function gradio(config, fn, target) {
<div class="output_interfaces">
</div>
<div class="panel_buttons">
<input class="screenshot panel_button" type="button" value="screenshot"/>
<input class="flag panel_button" type="button" value="flag"/>
<input class="screenshot panel_button" type="button" value="SCREENSHOT" style="visibility: hidden"/>
<input class="flag panel_button" type="button" value="FLAG" style="visibility: hidden"/>
</div>
</div>
</div>`);

View File

@ -1,5 +1,7 @@
const checkbox = {
html: `<label><input class="checkbox" type="checkbox">&nbsp;</label>`,
html: `<div class="checkbox_solo">
<label><input class="checkbox" type="checkbox">&nbsp;</label>
</div>`,
init: function(opts) {
this.target.css("height", "auto");
this.target.find("input").checkboxradio();
@ -11,6 +13,7 @@ const checkbox = {
},
clear: function() {
this.target.find("input").prop("checked", false);
this.target.find("input").button("refresh");
},
load_example: function(data) {
if (data) {
@ -18,5 +21,6 @@ const checkbox = {
} else {
this.target.find("input").prop("checked", false);
}
this.target.find("input").button("refresh");
}
}

View File

@ -1,59 +0,0 @@
// var MAX_PREVIEW_ROWS = 100
//
// $('body').on('click', ".input_csv.drop_mode", function (e) {
// $(this).parent().find(".hidden_upload").click();
// })
//
// $('body').on('drag dragstart dragend dragover dragenter dragleave drop', ".input_csv.drop_mode", function(e) {
// e.preventDefault();
// e.stopPropagation();
// })
//
// function loadTableFromFiles(files) {
// Papa.parse(files[0], {
// complete: function(results) {
// $(".input_csv").hide()
// $(".input_csv").removeClass("drop_mode")
// var data_array = results.data
// var table_html = ""
// for (var i = 0; i < data_array.length && i <= MAX_PREVIEW_ROWS; i++) {
// row = data_array[i]
// if (i == 0) {
// table_html += "<tr class='header'>"
// } else {
// table_html += "<tr>"
// }
// for (var c = 0; c < row.length; c++) {
// table_html += "<td>" + row[c] + "</td>"
// }
// table_html += "</tr>"
// }
// table_html += ""
// $(".csv_preview").html(table_html)
// $(".table_holder").show()
// }
// })
// }
//
// $(".input_csv").on('drop', function(e) {
// files = e.originalEvent.dataTransfer.files;
// loadTableFromFiles(files)
// });
//
// $(".hidden_upload").on("change", function() {
// var files = !!this.files ? this.files : []
// if (!files.length || !window.FileReader) {
// return
// }
// loadTableFromFiles(files)
// })
//
// $('body').on('click', '.clear', function(e) {
// $(".hidden_upload").prop("value", "")
// $(".input_csv").show()
// $(".input_csv").addClass("drop_mode")
// $(".table_holder").hide()
// })
// $('body').on('click', '.submit', function(e) {
// loadStart();
// })

View File

@ -1,150 +0,0 @@
const image_input = {
html: `
<div class="upload_zone drop_zone">
<div class="input_caption">Drop Image Here<br>- or -<br>Click to Upload</div>
</div>
<div class="image_display hide">
<div class="edit_holder">
<button class="edit_image interface_button primary">Edit</button>
</div>
<div class="view_holders">
<div class="image_preview_holder">
<img class="image_preview" />
</div>
<div class="saliency_holder hide">
<canvas class="saliency"></canvas>
</div>
</div>
</div>
<input class="hidden_upload" type="file" accept="image/x-png,image/gif,image/jpeg" />`
,
overlay_html: `
<div class="overlay interface_extension image_editor_overlay hide" interface_id="{0}">
<div class="image_editor_holder">
<div class="image_editor"></div>
</div>
</div>
`,
init: function() {
var io = this;
$('body').append(this.overlay_html.format(this.id));
this.overlay_target = $(`.overlay[interface_id=${this.id}]`);
this.target.find(".upload_zone").click(function (e) {
io.target.find(".hidden_upload").click();
});
this.target.on('drag dragstart dragend dragover dragenter dragleave drop',
".drop_zone", function(e) {
e.preventDefault();
e.stopPropagation();
})
this.target.on('drop', '.drop_zone', function(e) {
files = e.originalEvent.dataTransfer.files;
io.load_preview_from_files(files)
});
this.target.find('.hidden_upload').on('change', function (e) {
if (this.files) {
io.load_preview_from_files(this.files);
}
})
this.target.find('.edit_image').click(function (e) {
io.overlay_target.removeClass("hide");
io.target.find(".saliency_holder").addClass("hide");
})
this.tui_editor = new tui.ImageEditor(this.overlay_target.
find(".image_editor")[0], {
includeUI: {
menuBarPosition: 'left',
menu: ['crop', 'flip', 'rotate', 'draw', 'filter']
},
cssMaxWidth: 700,
cssMaxHeight: 500,
selectionStyle: {
cornerSize: 20,
rotatingPointOffset: 70
}
})
this.overlay_target.find(".tui-image-editor-header-buttons").html(`
<button class="tui_save tui_close interface_button primary">Save</button>
<button class="tui_cancel tui_close interface_button secondary">Cancel</button>
`)
this.overlay_target.find('.tui_close').click(function (e) {
io.overlay_target.addClass("hide");
if ($(e.target).hasClass('tui_save')) {
// if (io.tui_editor.ui.submenu == "crop") {
// io.tui_editor._cropAction().crop());
// }
io.set_image_data(io.tui_editor.toDataURL(), /*update_editor=*/false);
}
});
$(".tests").html(this.test_html);
$(".rotate_test").click(function () {
if (io.image_data) {
io.io_master.test("rotation", io.image_data);
}
})
$(".light_test").click(function () {
if (io.image_data) {
io.io_master.test("lighting", io.image_data);
}
})
},
submit: function() {
if (this.state == "IMAGE_LOADED") {
resizeImage.call(this, this.image_data, 300, 300, function(image_data) {
this.io_master.input(this.id, image_data);
})
}
},
clear: function() {
this.target.find(".upload_zone").show();
this.target.find(".image_preview").attr('src', '');
this.target.find(".image_display").addClass("hide");
this.target.find(".hidden_upload").prop("value", "")
this.state = "NO_IMAGE";
this.image_data = null;
this.target.find(".saliency_holder").addClass("hide");
},
output: function(data) {
if (this.target.find(".image_preview").attr("src")) {
var image = this.target.find(".image_preview");
var width = image.width();
var height = image.height();
this.target.find(".saliency_holder").removeClass("hide").html(`
<canvas class="saliency" width=${width} height=${height}></canvas>`);
var ctx = this.target.find(".saliency")[0].getContext('2d');
paintSaliency(ctx, width, height);
}
},
state: "NO_IMAGE",
image_data: null,
set_image_data: function(data, update_editor) {
let io = this;
resizeImage.call(this, data, 600, 600, function(image_data) {
io.image_data = image_data
io.target.find(".image_preview").attr('src', image_data);
if (update_editor) {
io.tui_editor.loadImageFromURL(io.image_data, 'input').then(function (sizeValue) {
io.tui_editor.clearUndoStack();
io.tui_editor.ui.activeMenuEvent();
io.tui_editor.ui.resizeEditor({ imageSize: sizeValue });
});
}
})
},
load_preview_from_files: function(files) {
if (!files.length || !window.FileReader || !/^image/.test(files[0].type)) {
return
}
var ReaderObj = new FileReader()
ReaderObj.readAsDataURL(files[0])
ReaderObj.io = this;
this.state = "IMAGE_LOADING"
ReaderObj.onloadend = function() {
let io = this.io;
io.target.find(".upload_zone").hide();
io.target.find(".image_display").removeClass("hide");
io.set_image_data(this.result, /*update_editor=*/true);
io.state = "IMAGE_LOADED"
}
}
}

View File

@ -34,7 +34,6 @@
<link rel="stylesheet" href="../static/css/interfaces/input/dropdown.css">
<link rel="stylesheet" href="../static/css/interfaces/input/checkbox_group.css">
<link rel="stylesheet" href="../static/css/interfaces/input/slider.css">
<link rel="stylesheet" href="../static/css/interfaces/input/csv.css">
<link rel="stylesheet" href="../static/css/interfaces/input/webcam.css">
<link rel="stylesheet" href="../static/css/interfaces/input/microphone.css">
<link rel="stylesheet" href="../static/css/interfaces/output/image.css">
@ -61,7 +60,9 @@
</div>
<div id="credit">Built with <a href="http://gradio.app" target="_blank">Gradio</a>.</div>
<script src="../static/js/vendor/jquery.min.js"></script>
<!-- TUI EDITOR -->
<!-- VENDOR -->
<script src="../static/js/vendor/html2canvas.min.js"></script>
<script src="../static/js/vendor/jquery-ui.min.js"></script>
<script src="../static/js/vendor/fabric.js"></script>
<script src="../static/js/vendor/tui-code-snippet.min.js"></script>
<script src="../static/js/vendor/FileSaver.min.js"></script>
@ -70,9 +71,6 @@
<script src="../static/js/vendor/white-theme.js"></script>
<script src="../static/js/vendor/black-theme.js"></script>
<script src="../static/js/vendor/html2canvas.min.js"></script>
<script src="../static/js/vendor/jquery-ui.min.js"></script>
<script src="../static/js/utils.js"></script>
<script src="../static/js/all_io.js"></script>
<script src="../static/js/interfaces/input/csv.js"></script>

View File

@ -5,20 +5,17 @@ from time import time
def flip(image):
start = time()
return image, {
"1": 0.2,
"2": 0.8
}
return image
def flip2(image):
start = time()
return np.fliplr(image), "stuff"
return np.fliplr(image)
gr.Interface([flip, flip2],
gr.inputs.Image(shape=(50, 50, 3)),
["image", "label"],
["image"],
examples=[
["images/cheetah1.jpg"],
["images/cheetah2.jpg"],

View File

@ -2,6 +2,7 @@ MANIFEST.in
README.md
setup.py
gradio/__init__.py
gradio/generate_docs.py
gradio/inputs.py
gradio/interface.py
gradio/networking.py

68
gradio/generate_docs.py Normal file
View File

@ -0,0 +1,68 @@
import json
from gradio.inputs import AbstractInput
from gradio.outputs import AbstractOutput
from gradio.interface import Interface
import inspect
def get_params(func):
params_str = inspect.getdoc(func)
params_doc = []
documented_params = {"self"}
for param_line in params_str.split("\n")[1:]:
space_index = param_line.index(" ")
colon_index = param_line.index(":")
name = param_line[:space_index]
documented_params.add(name)
params_doc.append((name, param_line[space_index+2:colon_index-1], param_line[colon_index+2:]))
params = inspect.getfullargspec(func)
param_set = []
for i in range(len(params.args)):
neg_index = -1 - i
if params.args[neg_index] not in documented_params:
continue
if i < len(params.defaults):
default = params.defaults[neg_index]
if type(default) == str:
default = '"' + default + '"'
else:
default = str(default)
param_set.insert(0, (params.args[neg_index], default))
else:
param_set.insert(0, (params.args[neg_index],))
return param_set, params_doc
def document(cls_set):
docset = []
for cls in cls_set:
inp = {}
inp["name"] = cls.__name__
doc = inspect.getdoc(cls)
inp["doc"] = "\n".join(doc.split("\n")[:-1])
inp["type"] = doc.split("\n")[-1].split("type: ")[-1]
inp["params"], inp["params_doc"] = get_params(cls.__init__)
inp["shortcuts"] = list(cls.get_shortcut_implementations().items())
docset.append(inp)
return docset
inputs = document(AbstractInput.__subclasses__())
outputs = document(AbstractOutput.__subclasses__())
interface_params = get_params(Interface.__init__)
interface = {
"doc": inspect.getdoc(Interface),
"params": interface_params[0],
"params_doc": interface_params[1],
}
launch_params = get_params(Interface.launch)
launch = {
"params": launch_params[0],
"params_doc": launch_params[1],
}
with open("docs.json", "w") as docs:
json.dump({
"inputs": inputs,
"outputs": outputs,
"interface": interface,
"launch": launch
}, docs)

View File

@ -27,6 +27,7 @@ class AbstractInput(ABC):
An abstract class for defining the methods that all gradio inputs should have.
When this is subclassed, it is automatically added to the registry
"""
def __init__(self, label):
self.label = label
@ -63,79 +64,27 @@ class AbstractInput(ABC):
return {}
class Sketchpad(AbstractInput):
def __init__(self, shape=(28, 28), invert_colors=True,
flatten=False, label=None):
self.image_width = shape[0]
self.image_height = shape[1]
self.invert_colors = invert_colors
self.flatten = flatten
super().__init__(label)
@classmethod
def get_shortcut_implementations(cls):
return {
"sketchpad": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for the SketchPad is to convert the sketch to black and white and resize 28x28
"""
im_transparent = preprocessing_utils.decode_base64_to_image(inp)
im = PIL.Image.new("RGBA", im_transparent.size, "WHITE") # Create a white background for the alpha channel
im.paste(im_transparent, (0, 0), im_transparent)
im = im.convert('L')
if self.invert_colors:
im = PIL.ImageOps.invert(im)
im = im.resize((self.image_width, self.image_height))
if self.flatten:
array = np.array(im).flatten().reshape(1, self.image_width * self.image_height)
else:
array = np.array(im).flatten().reshape(1, self.image_width, self.image_height)
return array
def process_example(self, example):
return preprocessing_utils.convert_file_to_base64(example)
class Webcam(AbstractInput):
def __init__(self, shape=(224, 224), label=None):
self.image_width = shape[0]
self.image_height = shape[1]
self.num_channels = 3
super().__init__(label)
def get_validation_inputs(self):
return validation_data.BASE64_COLOR_IMAGES
@classmethod
def get_shortcut_implementations(cls):
return {
"webcam": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for is to convert the picture to black and white and resize to be 48x48
"""
im = preprocessing_utils.decode_base64_to_image(inp)
im = im.convert('RGB')
im = preprocessing_utils.resize_and_crop(im, (self.image_width, self.image_height))
return np.array(im)
class Textbox(AbstractInput):
"""
Component creates a textbox for user to enter input. Provides a string (or number is `is_numeric` is true) as an argument to the wrapped function.
Input type: str
"""
def __init__(self, lines=1, placeholder=None, default=None, numeric=False, label=None):
'''
Parameters:
lines (int): number of line rows to provide in textarea.
placeholder (str): placeholder hint to provide behind textarea.
default (str): default text to provide in textarea.
numeric (bool): whether the input should be parsed as a number instead of a string.
label (str): component name in interface.
'''
self.lines = lines
self.placeholder = placeholder
self.default = default
self.numeric = numeric
super().__init__(label)
def get_validation_inputs(self):
return validation_data.ENGLISH_TEXTS
def get_template_context(self):
return {
"lines": self.lines,
@ -162,44 +111,21 @@ class Textbox(AbstractInput):
return inp
class Radio(AbstractInput):
def __init__(self, choices, label=None):
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Dropdown(AbstractInput):
def __init__(self, choices, label=None):
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class CheckboxGroup(AbstractInput):
def __init__(self, choices, label=None):
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Slider(AbstractInput):
def __init__(self, minimum=0, maximum=100, default=None, label=None):
"""
Component creates a slider that ranges from `minimum` to `maximum`. Provides a number as an argument to the wrapped function.
Input type: float
"""
def __init__(self, minimum=0, maximum=100, step=None, default=None, label=None):
'''
Parameters:
minimum (float): minimum value for slider.
maximum (float): maximum value for slider.
step (float): increment between slider values.
default (float): default value.
label (str): component name in interface.
'''
self.minimum = minimum
self.maximum = maximum
self.default = minimum if default is None else default
@ -221,7 +147,16 @@ class Slider(AbstractInput):
class Checkbox(AbstractInput):
"""
Component creates a checkbox that can be set to `True` or `False`. Provides a boolean as an argument to the wrapped function.
Input type: bool
"""
def __init__(self, label=None):
'''
Parameters:
label (str): component name in interface.
'''
super().__init__(label)
@classmethod
@ -231,8 +166,85 @@ class Checkbox(AbstractInput):
}
class CheckboxGroup(AbstractInput):
"""
Component creates a set of checkboxes of which a subset can be selected. Provides a list of strings representing the selected choices as an argument to the wrapped function.
Input type: List[str]
"""
def __init__(self, choices, label=None):
'''
Parameters:
choices (List[str]): list of options to select from.
label (str): component name in interface.
'''
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Radio(AbstractInput):
"""
Component creates a set of radio buttons of which only one can be selected. Provides string representing selected choice as an argument to the wrapped function.
Input type: str
"""
def __init__(self, choices, label=None):
'''
Parameters:
choices (List[str]): list of options to select from.
label (str): component name in interface.
'''
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Dropdown(AbstractInput):
"""
Component creates a dropdown of which only one can be selected. Provides string representing selected choice as an argument to the wrapped function.
Input type: str
"""
def __init__(self, choices, label=None):
'''
Parameters:
choices (List[str]): list of options to select from.
label (str): component name in interface.
'''
self.choices = choices
super().__init__(label)
def get_template_context(self):
return {
"choices": self.choices,
**super().get_template_context()
}
class Image(AbstractInput):
"""
Component creates an image upload box with editing capabilities. Provides numpy array of shape `(width, height, 3)` if `image_mode` is "RGB" as an argument to the wrapped function. Provides numpy array of shape `(width, height)` if `image_mode` is "L" as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, shape=(224, 224), image_mode='RGB', label=None):
'''
Parameters:
shape (Tuple[int, int]): shape to crop and resize image to.
image_mode (str): "RGB" if color, or "L" if black and white.
label (str): component name in interface.
'''
self.image_width = shape[0]
self.image_height = shape[1]
self.image_mode = image_mode
@ -261,7 +273,8 @@ class Image(AbstractInput):
warnings.simplefilter("ignore")
im = im.convert(self.image_mode)
im = preprocessing_utils.resize_and_crop(im, (self.image_width, self.image_height))
im = preprocessing_utils.resize_and_crop(
im, (self.image_width, self.image_height))
return np.array(im)
def process_example(self, example):
@ -271,13 +284,112 @@ class Image(AbstractInput):
return example
class Sketchpad(AbstractInput):
"""
Component creates a sketchpad for black and white illustration. Provides numpy array of shape `(width, height)` as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, shape=(28, 28), invert_colors=True,
flatten=False, label=None):
'''
Parameters:
shape (Tuple[int, int]): shape to crop and resize image to.
invert_colors (bool): whether to represent black as 1 and white as 0 in the numpy array.
flatten (bool): whether to reshape the numpy array to a single dimension.
label (str): component name in interface.
'''
self.image_width = shape[0]
self.image_height = shape[1]
self.invert_colors = invert_colors
self.flatten = flatten
super().__init__(label)
@classmethod
def get_shortcut_implementations(cls):
return {
"sketchpad": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for the SketchPad is to convert the sketch to black and white and resize 28x28
"""
im_transparent = preprocessing_utils.decode_base64_to_image(inp)
# Create a white background for the alpha channel
im = PIL.Image.new("RGBA", im_transparent.size, "WHITE")
im.paste(im_transparent, (0, 0), im_transparent)
im = im.convert('L')
if self.invert_colors:
im = PIL.ImageOps.invert(im)
im = im.resize((self.image_width, self.image_height))
if self.flatten:
array = np.array(im).flatten().reshape(
1, self.image_width * self.image_height)
else:
array = np.array(im).flatten().reshape(
1, self.image_width, self.image_height)
return array
def process_example(self, example):
return preprocessing_utils.convert_file_to_base64(example)
class Webcam(AbstractInput):
"""
Component creates a webcam for captured image input. Provides numpy array of shape `(width, height, 3)` as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, shape=(224, 224), label=None):
'''
Parameters:
shape (Tuple[int, int]): shape to crop and resize image to.
label (str): component name in interface.
'''
self.image_width = shape[0]
self.image_height = shape[1]
self.num_channels = 3
super().__init__(label)
def get_validation_inputs(self):
return validation_data.BASE64_COLOR_IMAGES
@classmethod
def get_shortcut_implementations(cls):
return {
"webcam": {},
}
def preprocess(self, inp):
"""
Default preprocessing method for is to convert the picture to black and white and resize to be 48x48
"""
im = preprocessing_utils.decode_base64_to_image(inp)
im = im.convert('RGB')
im = preprocessing_utils.resize_and_crop(
im, (self.image_width, self.image_height))
return np.array(im)
class Microphone(AbstractInput):
"""
Component creates a microphone element for audio inputs. Provides numpy array of shape `(samples, 2)` as an argument to the wrapped function.
Input type: numpy.array
"""
def __init__(self, preprocessing=None, label=None):
'''
Parameters:
preprocessing (Union[str, Callable]): preprocessing to apply to input
label (str): component name in interface.
'''
super().__init__(label)
if preprocessing is None or preprocessing == "mfcc":
self.preprocessing = preprocessing
else:
raise ValueError("unexpected value for preprocessing", preprocessing)
raise ValueError(
"unexpected value for preprocessing", preprocessing)
@classmethod
def get_shortcut_implementations(cls):

View File

@ -33,8 +33,7 @@ except requests.ConnectionError:
class Interface:
"""
The Interface class represents a general input/output interface for a machine learning model. During construction,
the appropriate inputs and outputs
Interfaces are created with Gradio using the `gradio.Interface()` function.
"""
instances = weakref.WeakSet()
@ -43,9 +42,15 @@ class Interface:
capture_session=False, title=None, description=None,
thumbnail=None, server_name=networking.LOCALHOST_NAME):
"""
:param fn: a function that will process the input panel data from the interface and return the output panel data.
:param inputs: a string or `AbstractInput` representing the input interface.
:param outputs: a string or `AbstractOutput` representing the output interface.
Parameters:
fn (Callable): the function to wrap an interface around.
inputs (Union[str, List[Union[str, AbstractInput]]]): a single Gradio input component, or list of Gradio input components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of input components should match the number of parameters in fn.
outputs (Union[str, List[Union[str, AbstractOutput]]]): a single Gradio output component, or list of Gradio output components. Components can either be passed as instantiated objects, or referred to by their string shortcuts. The number of output components should match the number of values returned by fn.
live (bool): whether the interface should automatically reload on change.
capture_session (bool): if True, captures the default graph and session (needed for Tensorflow 1.x)
title (str): a title for the interface; if provided, appears above the input and output components.
description (str): a description for the interface; if provided, appears above the input and output components.
examples (List[List[Any]]): sample inputs for the function; if provided, appears below the UI components and can be used to populate the interface. Should be nested list, in which the outer list consists of samples and each inner list consists of an input corresponding to each input component.
"""
def get_input_instance(iface):
if isinstance(iface, str):
@ -257,11 +262,8 @@ class Interface:
def launch(self, inline=None, inbrowser=None, share=False, validate=True, debug=False):
"""
Standard method shared by interfaces that creates the interface and sets up a websocket to communicate with it.
:param inline: boolean. If True, then a gradio interface is created inline (e.g. in jupyter or colab notebook)
:param inbrowser: boolean. If True, then a new browser window opens with the gradio interface.
:param share: boolean. If True, then a share link is generated using ngrok is displayed to the user.
:param validate: boolean. If True, then the validation is run if the interface has not already been validated.
Parameters
share (bool): whether to create a publicly shareable link from your computer for the interface.
"""
# if validate and not self.validate_flag:
# self.validate()

View File

@ -21,6 +21,7 @@ class AbstractOutput(ABC):
An abstract class for defining the methods that all gradio inputs should have.
When this is subclassed, it is automatically added to the registry
"""
def __init__(self, label):
self.label = label
@ -44,12 +45,56 @@ class AbstractOutput(ABC):
return {}
class Textbox(AbstractOutput):
'''
Component creates a textbox to render output text or number.
Output type: str
'''
def __init__(self, label=None):
'''
Parameters:
label (str): component name in interface.
'''
super().__init__(label)
def get_template_context(self):
return {
**super().get_template_context()
}
@classmethod
def get_shortcut_implementations(cls):
return {
"text": {},
"textbox": {},
"number": {},
}
def postprocess(self, prediction):
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
return str(prediction)
else:
raise ValueError("The `Textbox` output interface expects an output that is one of: a string, or"
"an int/float that can be converted to a string.")
class Label(AbstractOutput):
'''
Component outputs a classification label, along with confidence scores of top categories if provided. Confidence scores are represented as a dictionary mapping labels to scores between 0 and 1.
Output type: Union[Dict[str, float], str, int, float]
'''
LABEL_KEY = "label"
CONFIDENCE_KEY = "confidence"
CONFIDENCES_KEY = "confidences"
def __init__(self, num_top_classes=None, label=None):
'''
Parameters:
num_top_classes (int): number of most confident classes to show.
label (str): component name in interface.
'''
self.num_top_classes = num_top_classes
super().__init__(label)
@ -58,7 +103,7 @@ class Label(AbstractOutput):
return {"label": str(prediction)}
elif isinstance(prediction, dict):
sorted_pred = sorted(
prediction.items(),
prediction.items(),
key=operator.itemgetter(1),
reverse=True
)
@ -86,51 +131,18 @@ class Label(AbstractOutput):
}
class KeyValues(AbstractOutput):
def __init__(self, label=None):
super().__init__(label)
def postprocess(self, prediction):
if isinstance(prediction, dict):
return prediction
else:
raise ValueError("The `KeyValues` output interface expects an output that is a dictionary whose keys are "
"labels and values are corresponding values.")
@classmethod
def get_shortcut_implementations(cls):
return {
"key_values": {},
}
class Textbox(AbstractOutput):
def __init__(self, label=None):
super().__init__(label)
def get_template_context(self):
return {
**super().get_template_context()
}
@classmethod
def get_shortcut_implementations(cls):
return {
"text": {},
"textbox": {},
"number": {},
}
def postprocess(self, prediction):
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
return str(prediction)
else:
raise ValueError("The `Textbox` output interface expects an output that is one of: a string, or"
"an int/float that can be converted to a string.")
class Image(AbstractOutput):
'''
Component displays an image. Expects a numpy array of shape `(width, height, 3)` to be returned by the function, or a `matplotlib.pyplot` if `plot = True`.
Output type: numpy.array
'''
def __init__(self, plot=False, label=None):
'''
Parameters:
plot (bool): whether to expect a plot to be returned by the function.
label (str): component name in interface.
'''
self.plot = plot
super().__init__(label)
@ -154,7 +166,8 @@ class Image(AbstractOutput):
try:
return preprocessing_utils.encode_array_to_base64(prediction)
except:
raise ValueError("The `Image` output interface (with plt=False) expects a numpy array.")
raise ValueError(
"The `Image` output interface (with plt=False) expects a numpy array.")
def rebuild_flagged(self, dir, msg):
"""
@ -168,6 +181,33 @@ class Image(AbstractOutput):
return filename
class KeyValues(AbstractOutput):
'''
Component displays a table representing values for multiple fields.
Output type: List[Tuple[str, value]]
'''
def __init__(self, label=None):
'''
Parameters:
label (str): component name in interface.
'''
super().__init__(label)
def postprocess(self, prediction):
if isinstance(prediction, dict):
return prediction
else:
raise ValueError("The `KeyValues` output interface expects an output that is a dictionary whose keys are "
"labels and values are corresponding values.")
@classmethod
def get_shortcut_implementations(cls):
return {
"key_values": {},
}
# Automatically adds all shortcut implementations in AbstractInput into a dictionary.
shortcuts = {}
for cls in AbstractOutput.__subclasses__():

View File

@ -60,11 +60,9 @@ input.panel_button {
flex-grow: 1;
padding: 12px;
box-sizing: border-box;
text-transform: uppercase;
font-weight: bold;
border: 0 none;
border-radius: 4px;
transition: background-color 0.2s ease;
margin-left: 16px;
flex-grow: 1;
}
@ -78,11 +76,6 @@ input.submit {
input.submit:hover {
background-color: #f39c12;
}
label {
transition: background-color 0.2s ease;
padding: 2px 4px;
border-radius: 2px;
}
/* label:hover {
background-color: lightgray;
} */

View File

@ -13,30 +13,8 @@
margin-top: 12px;
margin-left: 16px;
}
.ui-icon-background, .ui-state-active .ui-icon-background {
border: #f39c12;
background-color: #ffffff;
.checkbox_solo label {
width: 27px !important;
padding-left: 11px;
padding-right: 3px;
}
.ui-visual-focus {
box-shadow: 0 0 3px 1px rgb(241, 171, 41);
}
.ui-button {
padding: 0.25em 0.6em;
}
.ui-widget {
font-family: inherit;
}
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, .ui-button, html .ui-button.ui-state-disabled:hover, html .ui-button.ui-state-disabled:active {
border: 1px solid lightgray;
background: lightgray;
}
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
border: 1px solid #e67e22 !important;
background: #e67e22 !important;
font-weight: normal;
color: #ffffff;
}
.ui-button {
outline: none;
}

View File

@ -1,7 +1,7 @@
/*! jQuery UI - v1.12.1 - 2016-09-14
* http://jqueryui.com
* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=3px&bgColorHeader=e9e9e9&bgTextureHeader=flat&borderColorHeader=dddddd&fcHeader=333333&iconColorHeader=444444&bgColorContent=ffffff&bgTextureContent=flat&borderColorContent=dddddd&fcContent=333333&iconColorContent=444444&bgColorDefault=f6f6f6&bgTextureDefault=flat&borderColorDefault=c5c5c5&fcDefault=454545&iconColorDefault=777777&bgColorHover=ededed&bgTextureHover=flat&borderColorHover=cccccc&fcHover=2b2b2b&iconColorHover=555555&bgColorActive=007fff&bgTextureActive=flat&borderColorActive=003eff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=fffa90&bgTextureHighlight=flat&borderColorHighlight=dad55e&fcHighlight=777620&iconColorHighlight=777620&bgColorError=fddfdf&bgTextureError=flat&borderColorError=f1a899&fcError=5f3f3f&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
* Copyright jQuery Foundation and other contributors; Licensed MIT */
/* Layout helpers
@ -164,7 +164,7 @@
right: 0;
}
.ui-button {
padding: .4em 1em;
padding: 0.25em 0.6em;
display: inline-block;
position: relative;
line-height: normal;
@ -290,9 +290,7 @@ button.ui-button::-moz-focus-inner {
}
.ui-checkboxradio-label .ui-icon-background {
box-shadow: inset 1px 1px 1px #ccc;
border-radius: .12em;
border: none;
}
.ui-checkboxradio-radio-label .ui-icon-background {
width: 16px;
@ -301,12 +299,15 @@ button.ui-button::-moz-focus-inner {
overflow: visible;
border: none;
}
.ui-checkboxradio-label .ui-icon-background {
border: solid 1px lightgray;
}
.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,
.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon {
background-image: none;
width: 8px;
height: 8px;
border-width: 4px;
border-width: 5px;
border-style: solid;
}
.ui-checkboxradio-disabled {
@ -880,10 +881,11 @@ button.ui-button::-moz-focus-inner {
body .ui-tooltip {
border-width: 2px;
}
/* Component containers
----------------------------------*/
.ui-widget {
font-family: Arial,Helvetica,sans-serif;
font-family: inherit;
font-size: 1em;
}
.ui-widget .ui-widget {
@ -928,8 +930,8 @@ body .ui-tooltip {
works properly when clicked or hovered */
html .ui-button.ui-state-disabled:hover,
html .ui-button.ui-state-disabled:active {
border: 1px solid #c5c5c5;
background: #f6f6f6;
border: 1px solid lightgray;
background: white;
font-weight: normal;
color: #454545;
}
@ -970,23 +972,25 @@ a.ui-button:focus {
text-decoration: none;
}
.ui-visual-focus {
box-shadow: 0 0 3px 1px rgb(94, 158, 214);
.ui-checkboxradio-label {
border: none !important;
background: none;
}
.ui-state-active,
.ui-widget-content .ui-state-active,
.ui-widget-header .ui-state-active,
a.ui-button:active,
.ui-button:active,
.ui-button.ui-state-active:hover {
border: 1px solid #003eff;
background: #007fff;
border: 1px solid #e67e22;
background: #e67e22;
font-weight: normal;
color: #ffffff;
}
.ui-icon-background,
.ui-state-active .ui-icon-background {
border: #003eff;
border: solid 1px #f39c12;
background-color: #ffffff;
}
.ui-state-active a,
@ -1302,8 +1306,8 @@ a.ui-button:active,
/* Overlays */
.ui-widget-overlay {
background: #aaaaaa;
opacity: .3;
filter: Alpha(Opacity=30); /* support: IE8 */
opacity: .003;
filter: Alpha(Opacity=.3); /* support: IE8 */
}
.ui-widget-shadow {
-webkit-box-shadow: 0px 0px 5px #666666;

View File

@ -6,8 +6,8 @@ function gradio(config, fn, target) {
<div class="input_interfaces">
</div>
<div class="panel_buttons">
<input class="submit panel_button" type="submit" value="submit"/>
<input class="clear panel_button" type="reset" value="clear">
<input class="clear panel_button" type="reset" value="CLEAR">
<input class="submit panel_button" type="submit" value="SUBMIT"/>
</div>
</div>
<div class="panel output_panel">
@ -18,8 +18,7 @@ function gradio(config, fn, target) {
<div class="output_interfaces">
</div>
<div class="panel_buttons">
<input class="screenshot panel_button" type="button" value="screenshot"/>
<input class="flag panel_button" type="button" value="flag"/>
<input class="screenshot panel_button" type="button" value="SCREENSHOT" style="visibility: hidden"/>
</div>
</div>
</div>`);

View File

@ -1,5 +1,7 @@
const checkbox = {
html: `<label><input class="checkbox" type="checkbox">&nbsp;</label>`,
html: `<div class="checkbox_solo">
<label><input class="checkbox" type="checkbox">&nbsp;</label>
</div>`,
init: function(opts) {
this.target.css("height", "auto");
this.target.find("input").checkboxradio();
@ -11,6 +13,7 @@ const checkbox = {
},
clear: function() {
this.target.find("input").prop("checked", false);
this.target.find("input").button("refresh");
},
load_example: function(data) {
if (data) {
@ -18,5 +21,6 @@ const checkbox = {
} else {
this.target.find("input").prop("checked", false);
}
this.target.find("input").button("refresh");
}
}

View File

@ -34,7 +34,6 @@
<link rel="stylesheet" href="../static/css/interfaces/input/dropdown.css">
<link rel="stylesheet" href="../static/css/interfaces/input/checkbox_group.css">
<link rel="stylesheet" href="../static/css/interfaces/input/slider.css">
<link rel="stylesheet" href="../static/css/interfaces/input/csv.css">
<link rel="stylesheet" href="../static/css/interfaces/input/webcam.css">
<link rel="stylesheet" href="../static/css/interfaces/input/microphone.css">
<link rel="stylesheet" href="../static/css/interfaces/output/image.css">
@ -61,7 +60,9 @@
</div>
<div id="credit">Built with <a href="http://gradio.app" target="_blank">Gradio</a>.</div>
<script src="../static/js/vendor/jquery.min.js"></script>
<!-- TUI EDITOR -->
<!-- VENDOR -->
<script src="../static/js/vendor/html2canvas.min.js"></script>
<script src="../static/js/vendor/jquery-ui.min.js"></script>
<script src="../static/js/vendor/fabric.js"></script>
<script src="../static/js/vendor/tui-code-snippet.min.js"></script>
<script src="../static/js/vendor/FileSaver.min.js"></script>
@ -70,9 +71,6 @@
<script src="../static/js/vendor/white-theme.js"></script>
<script src="../static/js/vendor/black-theme.js"></script>
<script src="../static/js/vendor/html2canvas.min.js"></script>
<script src="../static/js/vendor/jquery-ui.min.js"></script>
<script src="../static/js/utils.js"></script>
<script src="../static/js/all_io.js"></script>
<script src="../static/js/interfaces/input/csv.js"></script>