mirror of
https://github.com/gradio-app/gradio.git
synced 2025-01-06 10:25:17 +08:00
merge
This commit is contained in:
commit
11c728764c
52
build/lib/gradio/component.py
Normal file
52
build/lib/gradio/component.py
Normal file
@ -0,0 +1,52 @@
|
||||
class Component():
|
||||
"""
|
||||
A class for defining the methods that all gradio input and output components should have.
|
||||
"""
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
def get_template_context(self):
|
||||
"""
|
||||
:return: a dictionary with context variables for the javascript file associated with the context
|
||||
"""
|
||||
return {"label": self.label}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Any preprocessing needed to be performed on function input.
|
||||
"""
|
||||
return x
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Any postprocessing needed to be performed on function output.
|
||||
"""
|
||||
return y
|
||||
|
||||
def process_example(self, example):
|
||||
"""
|
||||
Proprocess example for UI
|
||||
"""
|
||||
return example
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
"""
|
||||
Return dictionary of shortcut implementations
|
||||
"""
|
||||
return {}
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
All interfaces should define a method that rebuilds the flagged input when it's passed back (i.e. rebuilds image from base64)
|
||||
"""
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def get_all_shortcut_implementations(cls):
|
||||
shortcuts = {}
|
||||
for sub_cls in cls.__subclasses__():
|
||||
for shortcut, parameters in sub_cls.get_shortcut_implementations().items():
|
||||
shortcuts[shortcut] = (sub_cls, parameters)
|
||||
return shortcuts
|
@ -1,6 +1,6 @@
|
||||
import json
|
||||
from gradio.inputs import AbstractInput
|
||||
from gradio.outputs import AbstractOutput
|
||||
from gradio.inputs import InputComponent
|
||||
from gradio.outputs import OutputComponent
|
||||
from gradio.interface import Interface
|
||||
import inspect
|
||||
|
||||
@ -44,8 +44,8 @@ def document(cls_set):
|
||||
docset.append(inp)
|
||||
return docset
|
||||
|
||||
inputs = document(AbstractInput.__subclasses__())
|
||||
outputs = document(AbstractOutput.__subclasses__())
|
||||
inputs = document(InputComponent.__subclasses__())
|
||||
outputs = document(OutputComponent.__subclasses__())
|
||||
interface_params = get_params(Interface.__init__)
|
||||
interface = {
|
||||
"doc": inspect.getdoc(Interface),
|
||||
|
@ -1,19 +0,0 @@
|
||||
"""
|
||||
This file is used by launch models on a hosted service, like `GradioHub`
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
import traceback
|
||||
import webbrowser
|
||||
|
||||
import gradio.inputs
|
||||
import gradio.outputs
|
||||
from gradio import networking, strings
|
||||
from distutils.version import StrictVersion
|
||||
import pkg_resources
|
||||
import requests
|
||||
import random
|
||||
import time
|
||||
|
||||
def launch_from_config(path):
|
||||
pass
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
"""
|
||||
This module defines various classes that can serve as the `input` to an interface. Each class must inherit from
|
||||
`AbstractInput`, and each class must define a path to its template. All of the subclasses of `AbstractInput` are
|
||||
`InputComponent`, and each class must define a path to its template. All of the subclasses of `InputComponent` are
|
||||
automatically added to a registry, which allows them to be easily referenced in other parts of the code.
|
||||
"""
|
||||
|
||||
@ -9,79 +9,48 @@ import json
|
||||
import os
|
||||
import time
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from gradio.component import Component
|
||||
|
||||
import base64
|
||||
import numpy as np
|
||||
import PIL.Image
|
||||
import PIL.ImageOps
|
||||
import scipy.io.wavfile
|
||||
from gradio import preprocessing_utils
|
||||
from gradio import processing_utils
|
||||
import pandas as pd
|
||||
import math
|
||||
import tempfile
|
||||
|
||||
# Where to find the static resources associated with each template.
|
||||
# BASE_INPUT_INTERFACE_TEMPLATE_PATH = 'static/js/interfaces/input/{}.js'
|
||||
BASE_INPUT_INTERFACE_JS_PATH = 'static/js/interfaces/input/{}.js'
|
||||
|
||||
|
||||
class AbstractInput(ABC):
|
||||
class InputComponent(Component):
|
||||
"""
|
||||
An abstract class for defining the methods that all gradio inputs should have.
|
||||
When this is subclassed, it is automatically added to the registry
|
||||
Input Component. All input components subclass this.
|
||||
"""
|
||||
pass
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
def get_template_context(self):
|
||||
"""
|
||||
:return: a dictionary with context variables for the javascript file associated with the context
|
||||
"""
|
||||
return {"label": self.label}
|
||||
|
||||
def preprocess(self, inp):
|
||||
"""
|
||||
By default, no pre-processing is applied to text.
|
||||
"""
|
||||
return inp
|
||||
|
||||
def process_example(self, example):
|
||||
"""
|
||||
Proprocess example for UI
|
||||
"""
|
||||
return example
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
"""
|
||||
Return dictionary of shortcut implementations
|
||||
"""
|
||||
return {}
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
All interfaces should define a method that rebuilds the flagged input when it's passed back (i.e. rebuilds image from base64)
|
||||
"""
|
||||
return data
|
||||
|
||||
class Textbox(AbstractInput):
|
||||
class Textbox(InputComponent):
|
||||
"""
|
||||
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.
|
||||
Component creates a textbox for user to enter input. Provides a string (or number is `type` is "float") as an argument to the wrapped function.
|
||||
Input type: str
|
||||
"""
|
||||
|
||||
def __init__(self, lines=1, placeholder=None, default=None, numeric=False, label=None):
|
||||
def __init__(self, lines=1, placeholder=None, default=None, numeric=False, type="str", 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.
|
||||
numeric (bool): DEPRECATED. Whether the input should be parsed as a number instead of a string.
|
||||
type (str): Type of value to be returned by component. "str" returns a string, "number" returns a float value.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.lines = lines
|
||||
self.placeholder = placeholder
|
||||
self.default = default
|
||||
self.numeric = numeric
|
||||
if numeric:
|
||||
warnings.warn("The 'numeric' parameter has been deprecated. Set parameter 'type' to 'number' instead.", DeprecationWarning)
|
||||
self.type = "number"
|
||||
else:
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -97,20 +66,20 @@ class Textbox(AbstractInput):
|
||||
return {
|
||||
"text": {},
|
||||
"textbox": {"lines": 7},
|
||||
"number": {"numeric": True}
|
||||
"number": {"type": "number"}
|
||||
}
|
||||
|
||||
def preprocess(self, inp):
|
||||
"""
|
||||
Cast type of input
|
||||
"""
|
||||
if self.numeric:
|
||||
return float(inp)
|
||||
def preprocess(self, x):
|
||||
if self.type == "str":
|
||||
return x
|
||||
elif self.type == "number":
|
||||
return float(x)
|
||||
else:
|
||||
return inp
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'str', 'number'.")
|
||||
|
||||
|
||||
class Slider(AbstractInput):
|
||||
|
||||
class Slider(InputComponent):
|
||||
"""
|
||||
Component creates a slider that ranges from `minimum` to `maximum`. Provides a number as an argument to the wrapped function.
|
||||
Input type: float
|
||||
@ -127,6 +96,11 @@ class Slider(AbstractInput):
|
||||
'''
|
||||
self.minimum = minimum
|
||||
self.maximum = maximum
|
||||
if step is None:
|
||||
difference = maximum - minimum
|
||||
power = math.floor(math.log10(difference) - 1)
|
||||
step = 10 ** power
|
||||
self.step = step
|
||||
self.default = minimum if default is None else default
|
||||
super().__init__(label)
|
||||
|
||||
@ -134,6 +108,7 @@ class Slider(AbstractInput):
|
||||
return {
|
||||
"minimum": self.minimum,
|
||||
"maximum": self.maximum,
|
||||
"step": self.step,
|
||||
"default": self.default,
|
||||
**super().get_template_context()
|
||||
}
|
||||
@ -145,7 +120,7 @@ class Slider(AbstractInput):
|
||||
}
|
||||
|
||||
|
||||
class Checkbox(AbstractInput):
|
||||
class Checkbox(InputComponent):
|
||||
"""
|
||||
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
|
||||
@ -165,19 +140,21 @@ class Checkbox(AbstractInput):
|
||||
}
|
||||
|
||||
|
||||
class CheckboxGroup(AbstractInput):
|
||||
class CheckboxGroup(InputComponent):
|
||||
"""
|
||||
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]
|
||||
Input type: Union[List[str], List[int]]
|
||||
"""
|
||||
|
||||
def __init__(self, choices, label=None):
|
||||
def __init__(self, choices, type="value", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
choices (List[str]): list of options to select from.
|
||||
type (str): Type of value to be returned by component. "value" returns the list of strings of the choices selected, "index" returns the list of indicies of the choices selected.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.choices = choices
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -186,20 +163,30 @@ class CheckboxGroup(AbstractInput):
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
return [self.choices.index(choice) for choice in x]
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'value', 'index'.")
|
||||
|
||||
class Radio(AbstractInput):
|
||||
|
||||
class Radio(InputComponent):
|
||||
"""
|
||||
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
|
||||
Input type: Union[str, int]
|
||||
"""
|
||||
|
||||
def __init__(self, choices, label=None):
|
||||
def __init__(self, choices, type="value", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
choices (List[str]): list of options to select from.
|
||||
type (str): Type of value to be returned by component. "value" returns the string of the choice selected, "index" returns the index of the choice selected.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.choices = choices
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -208,20 +195,29 @@ class Radio(AbstractInput):
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
return self.choices.index(x)
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'value', 'index'.")
|
||||
|
||||
class Dropdown(AbstractInput):
|
||||
class Dropdown(InputComponent):
|
||||
"""
|
||||
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):
|
||||
def __init__(self, choices, type="value", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
choices (List[str]): list of options to select from.
|
||||
type (str): Type of value to be returned by component. "value" returns the string of the choice selected, "index" returns the index of the choice selected.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.choices = choices
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -230,59 +226,71 @@ class Dropdown(AbstractInput):
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
return self.choices.index(x)
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'value', 'index'.")
|
||||
|
||||
class Image(AbstractInput):
|
||||
|
||||
class Image(InputComponent):
|
||||
"""
|
||||
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
|
||||
Component creates an image upload box with editing capabilities.
|
||||
Input type: Union[numpy.array, PIL.Image, str]
|
||||
"""
|
||||
|
||||
def __init__(self, shape=None, image_mode='RGB', label=None):
|
||||
def __init__(self, shape=None, image_mode='RGB', source="upload", type="numpy", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
shape (Tuple[int, int]): shape to crop and resize image to; if None, matches input image size.
|
||||
image_mode (str): "RGB" if color, or "L" if black and white.
|
||||
source (str): Source of image. "upload" creates a box where user can drop an image file, "webcam" allows user to take snapshot from their webcam, "canvas" defaults to a white image that can be edited and drawn upon with tools.
|
||||
type (str): Type of value to be returned by component. "numpy" returns a numpy array with shape (width, height, 3), "pil" returns a PIL image object, "file" returns a temporary file object whose path can be retrieved by file_obj.name.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
if shape is None:
|
||||
self.image_width, self.image_height = None, None
|
||||
else:
|
||||
self.image_width = shape[0]
|
||||
self.image_height = shape[1]
|
||||
self.shape = shape
|
||||
self.image_mode = image_mode
|
||||
self.source = source
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"image": {},
|
||||
"webcam": {"source": "webcam"},
|
||||
"sketchpad": {"image_mode": "L", "source": "canvas"},
|
||||
}
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"image_mode": self.image_mode,
|
||||
"source": self.source,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
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)
|
||||
def preprocess(self, x):
|
||||
im = processing_utils.decode_base64_to_image(x)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
im = im.convert(self.image_mode)
|
||||
image_width, image_height = self.image_width, self.image_height
|
||||
if image_width is None:
|
||||
image_width = im.size[0]
|
||||
if image_height is None:
|
||||
image_height = im.size[1]
|
||||
im = preprocessing_utils.resize_and_crop(
|
||||
im, (image_width, image_height))
|
||||
return np.array(im)
|
||||
if self.shape is not None:
|
||||
im = processing_utils.resize_and_crop(
|
||||
im, (self.shape[0], self.shape[1]))
|
||||
if self.type == "pil":
|
||||
return im
|
||||
elif self.type == "numpy":
|
||||
return np.array(im)
|
||||
elif self.type == "file":
|
||||
file_obj = tempfile.NamedTemporaryFile()
|
||||
im.save(file_obj.name)
|
||||
return file_obj
|
||||
|
||||
def process_example(self, example):
|
||||
if os.path.exists(example):
|
||||
return preprocessing_utils.convert_file_to_base64(example)
|
||||
return processing_utils.encode_file_to_base64(example)
|
||||
else:
|
||||
return example
|
||||
|
||||
@ -290,16 +298,150 @@ class Image(AbstractInput):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.png'
|
||||
im.save(f'{dir}/{filename}', 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class Sketchpad(AbstractInput):
|
||||
class Audio(InputComponent):
|
||||
"""
|
||||
Component creates a sketchpad for black and white illustration. Provides numpy array of shape `(width, height)` as an argument to the wrapped function.
|
||||
Component accepts audio input files.
|
||||
Input type: Union[Tuple[int, numpy.array], str, numpy.array]
|
||||
"""
|
||||
|
||||
def __init__(self, source="upload", type="numpy", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
source (str): Source of audio. "upload" creates a box where user can drop an audio file, "microphone" creates a microphone input.
|
||||
type (str): Type of value to be returned by component. "numpy" returns a 2-set tuple with an integer sample_rate and the data numpy.array of shape (samples, 2), "file" returns a temporary file object whose path can be retrieved by file_obj.name, "mfcc" returns the mfcc coefficients of the input audio.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.source = source
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"source": self.source,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"audio": {},
|
||||
"microphone": {"source": "microphone"}
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
By default, no pre-processing is applied to a microphone input file
|
||||
"""
|
||||
file_obj = processing_utils.decode_base64_to_file(x)
|
||||
if self.type == "file":
|
||||
return file_obj
|
||||
elif self.type == "numpy":
|
||||
return scipy.io.wavfile.read(file_obj.name)
|
||||
elif self.type == "mfcc":
|
||||
return processing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
|
||||
|
||||
|
||||
class File(InputComponent):
|
||||
"""
|
||||
Component accepts generic file uploads.
|
||||
Input type: Union[str, bytes]
|
||||
"""
|
||||
|
||||
def __init__(self, type="file", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
type (str): Type of value to be returned by component. "file" returns a temporary file object whose path can be retrieved by file_obj.name, "binary" returns an bytes object.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"file": {},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "file":
|
||||
return processing_utils.decode_base64_to_file(x)
|
||||
elif self.type == "bytes":
|
||||
return processing_utils.decode_base64_to_binary(x)
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'file', 'bytes'.")
|
||||
|
||||
|
||||
class Dataframe(InputComponent):
|
||||
"""
|
||||
Component accepts 2D input through a spreadsheet interface.
|
||||
Input type: Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]]
|
||||
"""
|
||||
|
||||
def __init__(self, headers=None, row_count=3, col_count=3, datatype="str", type="pandas", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
headers (List[str]): Header names to dataframe.
|
||||
row_count (int): Limit number of rows for input.
|
||||
col_count (int): Limit number of columns for input. If equal to 1, return data will be one-dimensional. Ignored if `headers` is provided.
|
||||
datatype (Union[str, List[str]]): Datatype of values in sheet. Can be provided per column as a list of strings, or for the entire sheet as a single string. Valid datatypes are "str", "number", "bool", and "date".
|
||||
type (str): Type of value to be returned by component. "pandas" for pandas dataframe, "numpy" for numpy array, or "array" for a Python array.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.headers = headers
|
||||
self.datatype = datatype
|
||||
self.row_count = row_count
|
||||
self.col_count = len(headers) if headers else col_count
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"headers": self.headers,
|
||||
"datatype": self.datatype,
|
||||
"row_count": self.row_count,
|
||||
"col_count": self.col_count,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"dataframe": {"type": "pandas"},
|
||||
"numpy": {"type": "numpy"},
|
||||
"matrix": {"type": "array"},
|
||||
"list": {"type": "array", "col_count": 1},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "pandas":
|
||||
if self.headers:
|
||||
return pd.DataFrame(x, columns=self.headers)
|
||||
else:
|
||||
return pd.DataFrame(x)
|
||||
if self.col_count == 1:
|
||||
x = x[0]
|
||||
if self.type == "numpy":
|
||||
return np.array(x)
|
||||
elif self.type == "array":
|
||||
return x
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'pandas', 'numpy', 'array'.")
|
||||
|
||||
#######################
|
||||
# DEPRECATED COMPONENTS
|
||||
#######################
|
||||
|
||||
class Sketchpad(InputComponent):
|
||||
"""
|
||||
DEPRECATED. 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
|
||||
"""
|
||||
|
||||
@ -312,23 +454,18 @@ class Sketchpad(AbstractInput):
|
||||
flatten (bool): whether to reshape the numpy array to a single dimension.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
warnings.warn("Sketchpad has been deprecated. Please use 'Image' component to generate a sketchpad. The string shorcut 'sketchpad' has been moved to the Image component.", DeprecationWarning)
|
||||
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):
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
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_transparent = processing_utils.decode_base64_to_image(x)
|
||||
# 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)
|
||||
@ -345,22 +482,22 @@ class Sketchpad(AbstractInput):
|
||||
return array
|
||||
|
||||
def process_example(self, example):
|
||||
return preprocessing_utils.convert_file_to_base64(example)
|
||||
return processing_utils.encode_file_to_base64(example)
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.png'
|
||||
im.save(f'{dir}/{filename}', 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class Webcam(AbstractInput):
|
||||
class Webcam(InputComponent):
|
||||
"""
|
||||
Component creates a webcam for captured image input. Provides numpy array of shape `(width, height, 3)` as an argument to the wrapped function.
|
||||
DEPRECATED. 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
|
||||
"""
|
||||
|
||||
@ -370,24 +507,19 @@ class Webcam(AbstractInput):
|
||||
shape (Tuple[int, int]): shape to crop and resize image to.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
warnings.warn("Webcam has been deprecated. Please use 'Image' component to generate a webcam. The string shorcut 'webcam' has been moved to the Image component.", DeprecationWarning)
|
||||
self.image_width = shape[0]
|
||||
self.image_height = shape[1]
|
||||
self.num_channels = 3
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"webcam": {},
|
||||
}
|
||||
|
||||
def preprocess(self, inp):
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
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 = processing_utils.decode_base64_to_image(x)
|
||||
im = im.convert('RGB')
|
||||
im = preprocessing_utils.resize_and_crop(
|
||||
im = processing_utils.resize_and_crop(
|
||||
im, (self.image_width, self.image_height))
|
||||
return np.array(im)
|
||||
|
||||
@ -395,16 +527,16 @@ class Webcam(AbstractInput):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.png'
|
||||
im.save('{}/{}'.format(dir, filename), 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class Microphone(AbstractInput):
|
||||
class Microphone(InputComponent):
|
||||
"""
|
||||
Component creates a microphone element for audio inputs. Provides numpy array of shape `(samples, 2)` as an argument to the wrapped function.
|
||||
DEPRECATED. Component creates a microphone element for audio inputs.
|
||||
Input type: numpy.array
|
||||
"""
|
||||
|
||||
@ -414,6 +546,7 @@ class Microphone(AbstractInput):
|
||||
preprocessing (Union[str, Callable]): preprocessing to apply to input
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
warnings.warn("Microphone has been deprecated. Please use 'Audio' component to generate a microphone. The string shorcut 'microphone' has been moved to the Audio component.", DeprecationWarning)
|
||||
super().__init__(label)
|
||||
if preprocessing is None or preprocessing == "mfcc":
|
||||
self.preprocessing = preprocessing
|
||||
@ -421,22 +554,17 @@ class Microphone(AbstractInput):
|
||||
raise ValueError(
|
||||
"unexpected value for preprocessing", preprocessing)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"microphone": {},
|
||||
}
|
||||
|
||||
def preprocess(self, inp):
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
By default, no pre-processing is applied to a microphone input file
|
||||
"""
|
||||
file_obj = preprocessing_utils.decode_base64_to_wav_file(inp)
|
||||
file_obj = processing_utils.decode_base64_to_file(x)
|
||||
if self.preprocessing == "mfcc":
|
||||
return preprocessing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
|
||||
return processing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
|
||||
_, signal = scipy.io.wavfile.read(file_obj.name)
|
||||
return signal
|
||||
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
inp = data.split(';')[1].split(',')[1]
|
||||
wav_obj = base64.b64decode(inp)
|
||||
@ -444,11 +572,4 @@ class Microphone(AbstractInput):
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.wav'
|
||||
with open("{}/{}".format(dir, filename), "wb+") as f:
|
||||
f.write(wav_obj)
|
||||
return filename
|
||||
|
||||
|
||||
# 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)
|
||||
return filename
|
@ -6,8 +6,8 @@ interface using the input and output types.
|
||||
import tempfile
|
||||
import webbrowser
|
||||
|
||||
import gradio.inputs
|
||||
import gradio.outputs
|
||||
from gradio.inputs import InputComponent
|
||||
from gradio.outputs import OutputComponent
|
||||
from gradio import networking, strings, utils
|
||||
from distutils.version import StrictVersion
|
||||
import pkg_resources
|
||||
@ -21,7 +21,6 @@ import weakref
|
||||
import analytics
|
||||
import os
|
||||
|
||||
|
||||
PKG_VERSION_URL = "https://gradio.app/api/pkg-version"
|
||||
analytics.write_key = "uxIFddIEuuUcFLf9VgH2teTEtPlWdkNy"
|
||||
analytics_url = 'https://api.gradio.app/'
|
||||
@ -48,51 +47,50 @@ class Interface:
|
||||
def __init__(self, fn, inputs, outputs, verbose=False, examples=None,
|
||||
live=False, show_input=True, show_output=True,
|
||||
capture_session=False, title=None, description=None,
|
||||
thumbnail=None, server_port=None, server_name=networking.LOCALHOST_NAME,
|
||||
thumbnail=None, server_port=None, server_name=networking.LOCALHOST_NAME,
|
||||
allow_screenshot=True, allow_flagging=True,
|
||||
flagging_dir="flagged"):
|
||||
"""
|
||||
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.
|
||||
inputs (Union[str, List[Union[str, InputComponent]]]): 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, OutputComponent]]]): 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.
|
||||
verbose (bool): whether to print detailed information during launch.
|
||||
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.
|
||||
live (bool): whether the interface should automatically reload on change.
|
||||
show_input (bool): if False, removes the input from the interface
|
||||
and underlays it in the output.
|
||||
show_output (bool): if False, removes the output from the interface
|
||||
and overlays it in the input.
|
||||
show_input (bool): if False, removes the input from the interface and underlays it in the output.
|
||||
show_output (bool): if False, removes the output from the interface and overlays it in the input.
|
||||
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.
|
||||
thumbnail (str): path to image or src to use as display picture for
|
||||
models listed in gradio.app/hub
|
||||
allow_screenshot (bool): if False, users will not see a button to
|
||||
take a screenshot of the interface.
|
||||
allow_flagging (bool): if False, users will not see a button to flag an
|
||||
input and output.
|
||||
thumbnail (str): path to image or src to use as display picture for models listed in gradio.app/hub
|
||||
allow_screenshot (bool): if False, users will not see a button to take a screenshot of the interface.
|
||||
allow_flagging (bool): if False, users will not see a button to flag an input and output.
|
||||
flagging_dir (str): what to name the dir where flagged data is stored.
|
||||
"""
|
||||
|
||||
def get_input_instance(iface):
|
||||
if isinstance(iface, str):
|
||||
return gradio.inputs.shortcuts[iface.lower()]
|
||||
elif isinstance(iface, gradio.inputs.AbstractInput):
|
||||
shortcut = InputComponent.get_all_shortcut_implementations()[iface]
|
||||
return shortcut[0](**shortcut[1])
|
||||
elif isinstance(iface, InputComponent):
|
||||
return iface
|
||||
else:
|
||||
raise ValueError("Input interface must be of type `str` or "
|
||||
"`AbstractInput`")
|
||||
"`InputComponent`")
|
||||
|
||||
def get_output_instance(iface):
|
||||
if isinstance(iface, str):
|
||||
return gradio.outputs.shortcuts[iface.lower()]
|
||||
elif isinstance(iface, gradio.outputs.AbstractOutput):
|
||||
shortcut = OutputComponent.get_all_shortcut_implementations()[iface]
|
||||
return shortcut[0](**shortcut[1])
|
||||
elif isinstance(iface, OutputComponent):
|
||||
return iface
|
||||
else:
|
||||
raise ValueError(
|
||||
"Output interface must be of type `str` or "
|
||||
"`AbstractOutput`"
|
||||
"`OutputComponent`"
|
||||
)
|
||||
|
||||
if isinstance(inputs, list):
|
||||
self.input_interfaces = [get_input_instance(i) for i in inputs]
|
||||
else:
|
||||
@ -138,7 +136,7 @@ class Interface:
|
||||
try:
|
||||
import tensorflow as tf
|
||||
self.session = tf.get_default_graph(), \
|
||||
tf.keras.backend.get_session()
|
||||
tf.keras.backend.get_session()
|
||||
except (ImportError, AttributeError):
|
||||
# If they are using TF >= 2.0 or don't have TF,
|
||||
# just ignore this.
|
||||
@ -154,7 +152,7 @@ class Interface:
|
||||
"_{}".format(index)):
|
||||
index += 1
|
||||
self.flagging_dir = self.flagging_dir + "/" + dir_name + \
|
||||
"_{}".format(index)
|
||||
"_{}".format(index)
|
||||
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-initiated-analytics/',
|
||||
@ -191,8 +189,8 @@ class Interface:
|
||||
iface[1]["label"] = ret_name
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return config
|
||||
|
||||
return config
|
||||
|
||||
def process(self, raw_input):
|
||||
"""
|
||||
@ -211,7 +209,7 @@ class Interface:
|
||||
durations = []
|
||||
for predict_fn in self.predict:
|
||||
start = time.time()
|
||||
if self.capture_session and not(self.session is None):
|
||||
if self.capture_session and not (self.session is None):
|
||||
graph, sess = self.session
|
||||
with graph.as_default():
|
||||
with sess.as_default():
|
||||
@ -241,10 +239,19 @@ class Interface:
|
||||
return processed_output, durations
|
||||
|
||||
def close(self):
|
||||
if self.simple_server and not(self.simple_server.fileno() == -1): # checks to see if server is running
|
||||
if self.simple_server and not (self.simple_server.fileno() == -1): # checks to see if server is running
|
||||
print("Closing Gradio server on port {}...".format(self.server_port))
|
||||
networking.close_server(self.simple_server)
|
||||
|
||||
def run_until_interrupted(self, thread, path_to_local_server):
|
||||
try:
|
||||
while 1:
|
||||
pass
|
||||
except (KeyboardInterrupt, OSError):
|
||||
print("Keyboard interruption in main thread... closing server.")
|
||||
thread.keep_running = False
|
||||
networking.url_ok(path_to_local_server)
|
||||
|
||||
def launch(self, inline=None, inbrowser=None, share=False, debug=False):
|
||||
"""
|
||||
Parameters
|
||||
@ -261,11 +268,10 @@ class Interface:
|
||||
path_to_local_server (str): Locally accessible link
|
||||
share_url (str): Publicly accessible link (if share=True)
|
||||
"""
|
||||
|
||||
output_directory = tempfile.mkdtemp()
|
||||
# Set up a port to serve the directory containing the static files with interface.
|
||||
server_port, httpd = networking.start_simple_server(self, output_directory, self.server_name,
|
||||
server_port=self.server_port)
|
||||
server_port, httpd, thread = networking.start_simple_server(
|
||||
self, output_directory, self.server_name, server_port=self.server_port)
|
||||
path_to_local_server = "http://{}:{}/".format(self.server_name, server_port)
|
||||
networking.build_template(output_directory)
|
||||
|
||||
@ -280,7 +286,7 @@ class Interface:
|
||||
print("IMPORTANT: You are using gradio version {}, "
|
||||
"however version {} "
|
||||
"is available, please upgrade.".format(
|
||||
current_pkg_version, latest_pkg_version))
|
||||
current_pkg_version, latest_pkg_version))
|
||||
print('--------')
|
||||
except: # TODO(abidlabs): don't catch all exceptions
|
||||
pass
|
||||
@ -373,6 +379,11 @@ class Interface:
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
|
||||
is_in_interactive_mode = bool(getattr(sys, 'ps1', sys.flags.interactive))
|
||||
if not is_in_interactive_mode:
|
||||
self.run_until_interrupted(thread, path_to_local_server)
|
||||
|
||||
return httpd, path_to_local_server, share_url
|
||||
|
||||
|
||||
|
@ -9,6 +9,7 @@ from http.server import HTTPServer as BaseHTTPServer, SimpleHTTPRequestHandler
|
||||
import pkg_resources
|
||||
from distutils import dir_util
|
||||
from gradio import inputs, outputs
|
||||
import time
|
||||
import json
|
||||
from gradio.tunneling import create_tunnel
|
||||
import urllib.request
|
||||
@ -16,6 +17,8 @@ from shutil import copyfile
|
||||
import requests
|
||||
import sys
|
||||
import analytics
|
||||
import csv
|
||||
|
||||
|
||||
INITIAL_PORT_VALUE = int(os.getenv(
|
||||
'GRADIO_SERVER_PORT', "7860")) # The http server will try to open on port 7860. If not available, 7861, 7862, etc.
|
||||
@ -183,18 +186,32 @@ def serve_files_in_background(interface, port, directory_to_serve=None, server_n
|
||||
os.makedirs(interface.flagging_dir, exist_ok=True)
|
||||
output = {'inputs': [interface.input_interfaces[
|
||||
i].rebuild(
|
||||
interface.flagging_dir, msg['data']['input_data']) for i
|
||||
interface.flagging_dir, msg['data']['input_data'][i]) for i
|
||||
in range(len(interface.input_interfaces))],
|
||||
'outputs': [interface.output_interfaces[
|
||||
i].rebuild(
|
||||
interface.flagging_dir, msg['data']['output_data']) for i
|
||||
interface.flagging_dir, msg['data']['output_data'][i])
|
||||
for i
|
||||
in range(len(interface.output_interfaces))]}
|
||||
|
||||
with open("{}/log.txt".format(interface.flagging_dir),
|
||||
'a+') as f:
|
||||
f.write(json.dumps(output))
|
||||
f.write("\n")
|
||||
log_fp = "{}/log.csv".format(interface.flagging_dir)
|
||||
|
||||
is_new = not os.path.exists(log_fp)
|
||||
|
||||
with open(log_fp, "a") as csvfile:
|
||||
headers = ["input_{}".format(i) for i in range(len(
|
||||
output["inputs"]))] + ["output_{}".format(i) for i in
|
||||
range(len(output["outputs"]))]
|
||||
writer = csv.DictWriter(csvfile, delimiter=',',
|
||||
lineterminator='\n',
|
||||
fieldnames=headers)
|
||||
if is_new:
|
||||
writer.writeheader()
|
||||
|
||||
writer.writerow(
|
||||
dict(zip(headers, output["inputs"] +
|
||||
output["outputs"]))
|
||||
)
|
||||
else:
|
||||
self.send_error(404, 'Path not found: {}'.format(self.path))
|
||||
|
||||
@ -205,22 +222,21 @@ def serve_files_in_background(interface, port, directory_to_serve=None, server_n
|
||||
self.base_path = base_path
|
||||
BaseHTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
|
||||
class QuittableHTTPThread(threading.Thread):
|
||||
def __init__(self, httpd):
|
||||
super().__init__(daemon=False)
|
||||
self.httpd = httpd
|
||||
self.keep_running =True
|
||||
|
||||
def run(self):
|
||||
while self.keep_running:
|
||||
self.httpd.handle_request()
|
||||
|
||||
httpd = HTTPServer(directory_to_serve, (server_name, port))
|
||||
|
||||
# Now loop forever
|
||||
def serve_forever():
|
||||
try:
|
||||
while True:
|
||||
sys.stdout.flush()
|
||||
httpd.serve_forever()
|
||||
except (KeyboardInterrupt, OSError):
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
thread = threading.Thread(target=serve_forever, daemon=False)
|
||||
thread = QuittableHTTPThread(httpd=httpd)
|
||||
thread.start()
|
||||
|
||||
return httpd
|
||||
return httpd, thread
|
||||
|
||||
|
||||
def start_simple_server(interface, directory_to_serve=None, server_name=None, server_port=None):
|
||||
@ -229,8 +245,8 @@ def start_simple_server(interface, directory_to_serve=None, server_name=None, se
|
||||
port = get_first_available_port(
|
||||
server_port, server_port + TRY_NUM_PORTS
|
||||
)
|
||||
httpd = serve_files_in_background(interface, port, directory_to_serve, server_name)
|
||||
return port, httpd
|
||||
httpd, thread = serve_files_in_background(interface, port, directory_to_serve, server_name)
|
||||
return port, httpd, thread
|
||||
|
||||
|
||||
def close_server(server):
|
||||
|
@ -1,67 +1,40 @@
|
||||
"""
|
||||
This module defines various classes that can serve as the `output` to an interface. Each class must inherit from
|
||||
`AbstractOutput`, and each class must define a path to its template. All of the subclasses of `AbstractOutput` are
|
||||
`OutputComponent`, and each class must define a path to its template. All of the subclasses of `OutputComponent` are
|
||||
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.component import Component
|
||||
import numpy as np
|
||||
import json
|
||||
from gradio import preprocessing_utils
|
||||
from gradio import processing_utils
|
||||
import datetime
|
||||
import operator
|
||||
from numbers import Number
|
||||
import warnings
|
||||
import tempfile
|
||||
import scipy
|
||||
import os
|
||||
|
||||
# Where to find the static resources associated with each template.
|
||||
BASE_OUTPUT_INTERFACE_JS_PATH = 'static/js/interfaces/output/{}.js'
|
||||
|
||||
|
||||
class AbstractOutput(ABC):
|
||||
class OutputComponent(Component):
|
||||
"""
|
||||
An abstract class for defining the methods that all gradio inputs should have.
|
||||
When this is subclassed, it is automatically added to the registry
|
||||
Output Component. All output components subclass this.
|
||||
"""
|
||||
pass
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
def get_template_context(self):
|
||||
"""
|
||||
:return: a dictionary with context variables for the javascript file associated with the context
|
||||
"""
|
||||
return {"label": self.label}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
"""
|
||||
Any postprocessing needed to be performed on function output.
|
||||
"""
|
||||
return prediction
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
"""
|
||||
Return dictionary of shortcut implementations
|
||||
"""
|
||||
return {}
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
All interfaces should define a method that rebuilds the flagged input when it's passed back (i.e. rebuilds image from base64)
|
||||
"""
|
||||
return data
|
||||
|
||||
|
||||
class Textbox(AbstractOutput):
|
||||
class Textbox(OutputComponent):
|
||||
'''
|
||||
Component creates a textbox to render output text or number.
|
||||
Output type: str
|
||||
Output type: Union[str, float, int]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
def __init__(self, type="str", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
type (str): Type of value to be passed to component. "str" expects a string, "number" expects a float value.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -72,20 +45,21 @@ class Textbox(AbstractOutput):
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"text": {},
|
||||
"textbox": {},
|
||||
"number": {},
|
||||
"text": {"type": "str"},
|
||||
"textbox": {"type": "str"},
|
||||
"number": {"type": "number"},
|
||||
}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
|
||||
return str(prediction)
|
||||
def postprocess(self, y):
|
||||
if self.type == "str":
|
||||
return y
|
||||
elif self.type == "number":
|
||||
return str(y)
|
||||
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.")
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'str', 'number'")
|
||||
|
||||
|
||||
class Label(AbstractOutput):
|
||||
class Label(OutputComponent):
|
||||
'''
|
||||
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]
|
||||
@ -104,12 +78,12 @@ class Label(AbstractOutput):
|
||||
self.num_top_classes = num_top_classes
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str) or isinstance(prediction, Number):
|
||||
return {"label": str(prediction)}
|
||||
elif isinstance(prediction, dict):
|
||||
def postprocess(self, y):
|
||||
if isinstance(y, str) or isinstance(y, Number):
|
||||
return {"label": str(y)}
|
||||
elif isinstance(y, dict):
|
||||
sorted_pred = sorted(
|
||||
prediction.items(),
|
||||
y.items(),
|
||||
key=operator.itemgetter(1),
|
||||
reverse=True
|
||||
)
|
||||
@ -124,8 +98,8 @@ class Label(AbstractOutput):
|
||||
} for pred in sorted_pred
|
||||
]
|
||||
}
|
||||
elif isinstance(prediction, int) or isinstance(prediction, float):
|
||||
return {self.LABEL_KEY: str(prediction)}
|
||||
elif isinstance(y, int) or isinstance(y, float):
|
||||
return {self.LABEL_KEY: str(y)}
|
||||
else:
|
||||
raise ValueError("The `Label` output interface expects one of: a string label, or an int label, a "
|
||||
"float label, or a dictionary whose keys are labels and values are confidences.")
|
||||
@ -140,62 +114,64 @@ class Label(AbstractOutput):
|
||||
"""
|
||||
Default rebuild method for label
|
||||
"""
|
||||
return json.loads(data)
|
||||
# return json.loads(data)
|
||||
return data
|
||||
|
||||
class Image(AbstractOutput):
|
||||
class Image(OutputComponent):
|
||||
'''
|
||||
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
|
||||
Component displays an output image.
|
||||
Output type: Union[numpy.array, PIL.Image, str, matplotlib.pyplot]
|
||||
'''
|
||||
|
||||
def __init__(self, plot=False, label=None):
|
||||
def __init__(self, type="numpy", plot=False, label=None):
|
||||
'''
|
||||
Parameters:
|
||||
plot (bool): whether to expect a plot to be returned by the function.
|
||||
type (str): Type of value to be passed to component. "numpy" expects a numpy array with shape (width, height, 3), "pil" expects a PIL image object, "file" expects a file path to the saved image, "plot" expects a matplotlib.pyplot object.
|
||||
plot (bool): DEPRECATED. Whether to expect a plot to be returned by the function.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.plot = plot
|
||||
if plot:
|
||||
warnings.warn("The 'plot' parameter has been deprecated. Set parameter 'type' to 'plot' instead.", DeprecationWarning)
|
||||
self.type = "plot"
|
||||
else:
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"image": {},
|
||||
"plot": {"plot": True}
|
||||
"plot": {"type": "plot"},
|
||||
"pil": {"type": "pil"}
|
||||
}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
"""
|
||||
"""
|
||||
if self.plot:
|
||||
try:
|
||||
return preprocessing_utils.encode_plot_to_base64(prediction)
|
||||
except:
|
||||
raise ValueError("The `Image` output interface expects a `matplotlib.pyplot` object"
|
||||
"if plt=True.")
|
||||
def postprocess(self, y):
|
||||
if self.type in ["numpy", "pil"]:
|
||||
if self.type == "pil":
|
||||
y = np.array(y)
|
||||
return processing_utils.encode_array_to_base64(y)
|
||||
elif self.type == "file":
|
||||
return processing_utils.encode_file_to_base64(y)
|
||||
elif self.type == "plot":
|
||||
return processing_utils.encode_plot_to_base64(y)
|
||||
else:
|
||||
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("Unknown type: " + self.type + ". Please choose from: 'numpy', 'pil', 'file', 'plot'.")
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = 'output_{}.png'.format(timestamp.
|
||||
strftime("%Y-%m-%d-%H-%M-%S"))
|
||||
im.save('{}/{}'.format(dir, filename), 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class KeyValues(AbstractOutput):
|
||||
class KeyValues(OutputComponent):
|
||||
'''
|
||||
Component displays a table representing values for multiple fields.
|
||||
Output type: Dict[str, value]
|
||||
Output type: Union[Dict, List[Tuple[str, Union[str, int, float]]]]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
@ -205,9 +181,11 @@ class KeyValues(AbstractOutput):
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, dict):
|
||||
return prediction
|
||||
def postprocess(self, y):
|
||||
if isinstance(y, dict):
|
||||
return list(y.items())
|
||||
elif isinstance(y, list):
|
||||
return y
|
||||
else:
|
||||
raise ValueError("The `KeyValues` output interface expects an output that is a dictionary whose keys are "
|
||||
"labels and values are corresponding values.")
|
||||
@ -219,19 +197,51 @@ class KeyValues(AbstractOutput):
|
||||
}
|
||||
|
||||
|
||||
class HighlightedText(AbstractOutput):
|
||||
class HighlightedText(OutputComponent):
|
||||
'''
|
||||
Component creates text that contains spans that are highlighted by category or numerical value.
|
||||
Output is represent as a list of Tuple pairs, where the first element represents the span of text represented by the tuple, and the second element represents the category or value of the text.
|
||||
Output type: List[Tuple[str, Union[float, str]]]
|
||||
'''
|
||||
|
||||
def __init__(self, category_colors=None, label=None):
|
||||
def __init__(self, color_map=None, label=None):
|
||||
'''
|
||||
Parameters:
|
||||
category_colors (Dict[str, float]):
|
||||
color_map (Dict[str, str]): Map between category and respective colors
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.color_map = color_map
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"color_map": self.color_map,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"highlight": {},
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
return y
|
||||
|
||||
|
||||
class Audio(OutputComponent):
|
||||
'''
|
||||
Creates an audio player that plays the output audio.
|
||||
Output type: Union[Tuple[int, numpy.array], str]
|
||||
'''
|
||||
|
||||
def __init__(self, type="numpy", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
type (str): Type of value to be passed to component. "numpy" returns a 2-set tuple with an integer sample_rate and the data numpy.array of shape (samples, 2), "file" returns a temporary file path to the saved wav audio file.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -242,21 +252,24 @@ class HighlightedText(AbstractOutput):
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"highlight": {},
|
||||
"audio": {},
|
||||
}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
|
||||
return str(prediction)
|
||||
def postprocess(self, y):
|
||||
if self.type in ["numpy", "file"]:
|
||||
if self.type == "numpy":
|
||||
file = tempfile.NamedTemporaryFile()
|
||||
scipy.io.wavfile.write(file, y[0], y[1])
|
||||
y = file.name
|
||||
return processing_utils.encode_file_to_base64(y, type="audio", ext="wav")
|
||||
else:
|
||||
raise ValueError("The `HighlightedText` output interface expects an output that is one of: a string, or"
|
||||
"an int/float that can be converted to a string.")
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'numpy', 'file'.")
|
||||
|
||||
|
||||
class JSON(AbstractOutput):
|
||||
class JSON(OutputComponent):
|
||||
'''
|
||||
Used for JSON output. Expects a JSON string or a Python dictionary or list that can be converted to JSON.
|
||||
Output type: Union[str, Dict[str, Any], List[Any]]
|
||||
Used for JSON output. Expects a JSON string or a Python object that is JSON serializable.
|
||||
Output type: Union[str, Any]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
@ -266,14 +279,12 @@ class JSON(AbstractOutput):
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, dict) or isinstance(prediction, list):
|
||||
return json.dumps(prediction)
|
||||
elif isinstance(prediction, str):
|
||||
return prediction
|
||||
def postprocess(self, y):
|
||||
if isinstance(y, str):
|
||||
return json.dumps(y)
|
||||
else:
|
||||
raise ValueError("The `JSON` output interface expects an output that is a dictionary or list "
|
||||
"or a preformatted JSON string.")
|
||||
return y
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
@ -282,9 +293,9 @@ class JSON(AbstractOutput):
|
||||
}
|
||||
|
||||
|
||||
class HTML(AbstractOutput):
|
||||
class HTML(OutputComponent):
|
||||
'''
|
||||
Used for HTML output. Expects a JSON string or a Python dictionary or list that can be converted to JSON.
|
||||
Used for HTML output. Expects an HTML valid string.
|
||||
Output type: str
|
||||
'''
|
||||
|
||||
@ -295,11 +306,6 @@ class HTML(AbstractOutput):
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str):
|
||||
return prediction
|
||||
else:
|
||||
raise ValueError("The `HTML` output interface expects an output that is a str.")
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
@ -308,8 +314,75 @@ class HTML(AbstractOutput):
|
||||
}
|
||||
|
||||
|
||||
# Automatically adds all shortcut implementations in AbstractInput into a dictionary.
|
||||
shortcuts = {}
|
||||
for cls in AbstractOutput.__subclasses__():
|
||||
for shortcut, parameters in cls.get_shortcut_implementations().items():
|
||||
shortcuts[shortcut] = cls(**parameters)
|
||||
class File(OutputComponent):
|
||||
'''
|
||||
Used for file output.
|
||||
Output type: Union[file-like, str]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
'''
|
||||
Parameters:
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"file": {},
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
return {
|
||||
"name": os.path.basename(y),
|
||||
"size": os.path.getsize(y),
|
||||
"data": processing_utils.encode_file_to_base64(y, header=False)
|
||||
}
|
||||
|
||||
|
||||
class Dataframe(OutputComponent):
|
||||
"""
|
||||
Component displays 2D output through a spreadsheet interface.
|
||||
Output type: Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]]
|
||||
"""
|
||||
|
||||
def __init__(self, headers=None, type="pandas", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
headers (List[str]): Header names to dataframe.
|
||||
type (str): Type of value to be passed to component. "pandas" for pandas dataframe, "numpy" for numpy array, or "array" for Python array.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
self.headers = headers
|
||||
super().__init__(label)
|
||||
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"headers": self.headers,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"dataframe": {"type": "pandas"},
|
||||
"numpy": {"type": "numpy"},
|
||||
"matrix": {"type": "array"},
|
||||
"list": {"type": "array"},
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
if self.type == "pandas":
|
||||
return {"headers": list(y.columns), "data": y.values.tolist()}
|
||||
elif self.type in ("numpy", "array"):
|
||||
if self.type == "numpy":
|
||||
y = y.tolist()
|
||||
if len(y) == 0 or not isinstance(y[0], list):
|
||||
y = [y]
|
||||
return {"data": y}
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'pandas', 'numpy', 'array'.")
|
||||
|
@ -17,12 +17,16 @@ def decode_base64_to_image(encoding):
|
||||
return Image.open(BytesIO(base64.b64decode(image_encoded)))
|
||||
|
||||
|
||||
def convert_file_to_base64(img):
|
||||
with open(img, "rb") as image_file:
|
||||
encoded_string = base64.b64encode(image_file.read())
|
||||
def encode_file_to_base64(f, type="image", ext=None, header=True):
|
||||
with open(f, "rb") as file:
|
||||
encoded_string = base64.b64encode(file.read())
|
||||
base64_str = str(encoded_string, 'utf-8')
|
||||
type = img.split(".")[-1]
|
||||
return "data:image/" + type + ";base64," + base64_str
|
||||
if not header:
|
||||
return base64_str
|
||||
if ext is None:
|
||||
ext = f.split(".")[-1]
|
||||
return "data:" + type + "/" + ext + ";base64," + base64_str
|
||||
|
||||
|
||||
def encode_plot_to_base64(plt):
|
||||
with BytesIO() as output_bytes:
|
||||
@ -61,19 +65,24 @@ def resize_and_crop(img, size, crop_type='center'):
|
||||
return ImageOps.fit(img, size, centering=center)
|
||||
|
||||
##################
|
||||
# AUDIO FILES
|
||||
# OUTPUT
|
||||
##################
|
||||
|
||||
def decode_base64_to_wav_file(encoding):
|
||||
def decode_base64_to_binary(encoding):
|
||||
inp = encoding.split(';')[1].split(',')[1]
|
||||
wav_obj = base64.b64decode(inp)
|
||||
return base64.b64decode(inp)
|
||||
|
||||
|
||||
def decode_base64_to_file(encoding):
|
||||
file_obj = tempfile.NamedTemporaryFile()
|
||||
file_obj.close()
|
||||
with open(file_obj.name, 'wb') as f:
|
||||
f.write(wav_obj)
|
||||
file_obj.write(decode_base64_to_binary(encoding))
|
||||
return file_obj
|
||||
|
||||
|
||||
##################
|
||||
# AUDIO FILES
|
||||
##################
|
||||
|
||||
def generate_mfcc_features_from_audio_file(wav_filename,
|
||||
pre_emphasis=0.95,
|
||||
frame_size= 0.025,
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"input_interface_type": "{{input_interface_type}}",
|
||||
"output_interface_type": "{{output_interface_type}}",
|
||||
"live": {{live}}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
body {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
}
|
||||
nav {
|
||||
text-align: center;
|
||||
padding: 16px 0 4px;
|
||||
}
|
||||
nav img {
|
||||
margin-right: auto;
|
||||
height: 32px;
|
||||
}
|
||||
#bulk_rows td {
|
||||
text-align: center;
|
||||
height: 60px;
|
||||
}
|
||||
#bulk_rows tr:nth-child(even) {
|
||||
background: #DDD
|
||||
}
|
||||
#bulk_rows img {
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
}
|
||||
#bulk_rows {
|
||||
margin: 0 auto;
|
||||
width: 70%;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -27,10 +27,19 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
.interface {
|
||||
height: 360px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
.interface_box {
|
||||
height: 360px;
|
||||
}
|
||||
.interface_mini_box {
|
||||
height: 180px;
|
||||
}
|
||||
.interface_max_box {
|
||||
overflow: auto;
|
||||
max-height: 360px;
|
||||
}
|
||||
.interface:not(*:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
.table_holder {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
overflow: scroll;
|
||||
display: none;
|
||||
}
|
||||
.csv_preview {
|
||||
background-color: white;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.csv_preview tr {
|
||||
border-bottom: solid 1px black;
|
||||
}
|
||||
.csv_preview tr.header td {
|
||||
background-color: #e67e22;
|
||||
font-weight: bold;
|
||||
}
|
||||
.csv_preview td {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.csv_preview td:nth-child(even) {
|
||||
background-color: whitesmoke;
|
||||
}
|
15
build/lib/gradio/static/css/interfaces/input/file.css
Normal file
15
build/lib/gradio/static/css/interfaces/input/file.css
Normal file
@ -0,0 +1,15 @@
|
||||
.file_display {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.file_name {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.file_size {
|
||||
font-size: 18px;
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
.image_display {
|
||||
height: 100%;
|
||||
}
|
||||
.view_holders {
|
||||
flex-grow: 1;
|
||||
height: calc(100% - 36px);
|
||||
background-color: #CCCCCC;
|
||||
position: relative;
|
||||
}
|
||||
.image_preview_holder, .saliency_holder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.saliency_holder {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
.saliency {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: none;
|
||||
opacity: 1;
|
||||
}
|
||||
.saliency > div {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.saliency > div > div {
|
||||
flex-grow: 1;
|
||||
background-color: #EEA45D;
|
||||
}
|
||||
.image_preview {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
.hidden_upload {
|
||||
display: none;
|
||||
}
|
||||
.image_editor_overlay {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.image_editor_holder {
|
||||
height: 85%;
|
||||
width: 85%;
|
||||
}
|
||||
.image_editor {
|
||||
background-color: black;
|
||||
}
|
||||
#tie-btn-reset, #tie-btn-delete, #tie-btn-delete-all {
|
||||
display: none !important;
|
||||
}
|
||||
.tui-image-editor-icpartition {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.tui_close {
|
||||
border-radius: 0 !important;
|
||||
border: none !important;
|
||||
margin-left: 10px !important;
|
||||
font-family: 'Open Sans', sans-serif !important;
|
||||
}
|
@ -37,10 +37,10 @@
|
||||
.canvas_holder canvas {
|
||||
background-color: white;
|
||||
}
|
||||
.canvas_holder, .saliency_holder {
|
||||
.canvas_holder {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: calc(100% - 36px);
|
||||
}
|
||||
.saliency_holder {
|
||||
position: absolute;
|
||||
|
15
build/lib/gradio/static/css/interfaces/output/audio.css
Normal file
15
build/lib/gradio/static/css/interfaces/output/audio.css
Normal file
@ -0,0 +1,15 @@
|
||||
.output_text {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
outline: none;
|
||||
background-color: white;
|
||||
border: solid 1px lightgray;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
padding: 4px;
|
||||
min-height: 30px;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap; /* CSS3 */
|
||||
white-space: -moz-pre-wrap; /* Firefox */
|
||||
word-wrap: break-word; /* IE */
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
.highlight_legend {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.color_legend {
|
||||
font-family: monospace;
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(90deg, rgba(58,241,255,1) 0%, rgba(58,241,255,0) 49%, rgba(230,126,34,0) 50%, rgba(230,126,34,1) 100%);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.category-label {
|
||||
display: inline-flex;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.category-label div {
|
||||
width: 24px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
15
build/lib/gradio/static/css/interfaces/output/html.css
Normal file
15
build/lib/gradio/static/css/interfaces/output/html.css
Normal file
@ -0,0 +1,15 @@
|
||||
.output_text {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
outline: none;
|
||||
background-color: white;
|
||||
border: solid 1px lightgray;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
padding: 4px;
|
||||
min-height: 30px;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap; /* CSS3 */
|
||||
white-space: -moz-pre-wrap; /* Firefox */
|
||||
word-wrap: break-word; /* IE */
|
||||
}
|
@ -40,6 +40,7 @@
|
||||
.output_class {
|
||||
font-weight: bold;
|
||||
font-size: 36px;
|
||||
padding: 32px 16px;;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
1
build/lib/gradio/static/css/vendor/jexcel.min.css
vendored
Normal file
1
build/lib/gradio/static/css/vendor/jexcel.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
build/lib/gradio/static/css/vendor/jsonTree.css
vendored
12
build/lib/gradio/static/css/vendor/jsonTree.css
vendored
@ -13,15 +13,23 @@
|
||||
|
||||
/* Styles for the container of the tree (e.g. fonts, margins etc.) */
|
||||
.jsontree_tree {
|
||||
margin-left: 30px;
|
||||
font-family: 'PT Mono', monospace;
|
||||
font-size: 14px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.jsontree_tree ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.jsontree_tree li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
/* Styles for a list of child nodes */
|
||||
.jsontree_child-nodes {
|
||||
display: none;
|
||||
margin-left: 35px;
|
||||
margin-bottom: 5px;
|
||||
line-height: 2;
|
||||
}
|
||||
|
8
build/lib/gradio/static/css/vendor/jsuites.min.css
vendored
Normal file
8
build/lib/gradio/static/css/vendor/jsuites.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -59,8 +59,8 @@ var io_master_template = {
|
||||
flag: function() {
|
||||
var post_data = {
|
||||
'data': {
|
||||
'input_data' : toStringIfObject(this.last_input) ,
|
||||
'output_data' : toStringIfObject(this.last_output)
|
||||
'input_data' : this.last_input ,
|
||||
'output_data' : this.last_output
|
||||
}
|
||||
}
|
||||
$.ajax({type: "POST",
|
||||
|
@ -1,73 +0,0 @@
|
||||
var io_master_template = {
|
||||
gather: function() {
|
||||
this.clear();
|
||||
for (let iface of this.input_interfaces) {
|
||||
iface.submit();
|
||||
}
|
||||
},
|
||||
clear: function() {
|
||||
this.last_input = new Array(this.input_interfaces.length);
|
||||
this.input_count = 0;
|
||||
},
|
||||
input: function(interface_id, data) {
|
||||
this.last_input[interface_id] = data;
|
||||
this.input_count += 1;
|
||||
if (this.input_count == this.input_interfaces.length) {
|
||||
this.submit();
|
||||
}
|
||||
},
|
||||
submit: function() {
|
||||
let io = this;
|
||||
if (!this.config.live) {
|
||||
this.target.find(".loading").removeClass("invisible");
|
||||
this.target.find(".loading_in_progress").show();
|
||||
this.target.find(".loading_failed").hide();
|
||||
this.target.find(".output_interface").addClass("invisible");
|
||||
this.target.find(".output_interfaces .panel_header").addClass("invisible");
|
||||
}
|
||||
this.fn(this.last_input).then((output) => {
|
||||
io.output(output);
|
||||
}).catch((error) => {
|
||||
console.error(error);
|
||||
this.target.find(".loading_in_progress").hide();
|
||||
this.target.find(".loading_failed").show();
|
||||
})
|
||||
},
|
||||
output: function(data) {
|
||||
this.last_output = data["data"];
|
||||
|
||||
for (let i = 0; i < this.output_interfaces.length; i++) {
|
||||
this.output_interfaces[i].output(data["data"][i]);
|
||||
}
|
||||
if (data["durations"]) {
|
||||
let ratio = this.output_interfaces.length / data["durations"].length;
|
||||
for (let i = 0; i < this.output_interfaces.length; i = i + ratio) {
|
||||
this.output_interfaces[i].target.parent().find(`.loading_time[interface="${i + ratio - 1}"]`).text("Latency: " + ((data["durations"][i / ratio])).toFixed(2) + "s");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.config.live) {
|
||||
this.gather();
|
||||
} else {
|
||||
this.target.find(".loading").addClass("invisible");
|
||||
this.target.find(".output_interface").removeClass("invisible");
|
||||
this.target.find(".output_interfaces .panel_header").removeClass("invisible");
|
||||
}
|
||||
},
|
||||
flag: function(message) {
|
||||
var post_data = {
|
||||
'data': {
|
||||
'input_data' : toStringIfObject(this.last_input) ,
|
||||
'output_data' : toStringIfObject(this.last_output),
|
||||
'message' : message
|
||||
}
|
||||
}
|
||||
$.ajax({type: "POST",
|
||||
url: "/api/flag/",
|
||||
data: JSON.stringify(post_data),
|
||||
success: function(output){
|
||||
console.log("Flagging successful")
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
@ -42,13 +42,22 @@ function gradio(config, fn, target) {
|
||||
"checkboxgroup" : checkbox_group,
|
||||
"slider" : slider,
|
||||
"dropdown" : dropdown,
|
||||
"audio" : audio_input,
|
||||
"file" : file_input,
|
||||
"dataframe" : dataframe_input,
|
||||
}
|
||||
let output_to_object_map = {
|
||||
"csv" : {},
|
||||
"image" : image_output,
|
||||
"label" : label_output,
|
||||
"keyvalues" : key_values,
|
||||
"textbox" : textbox_output
|
||||
"textbox" : textbox_output,
|
||||
"highlightedtext": highlighted_text,
|
||||
"audio": audio_output,
|
||||
"json": json_output,
|
||||
"html": html_output,
|
||||
"file" : file_output,
|
||||
"dataframe" : dataframe_output,
|
||||
}
|
||||
let id_to_interface_map = {}
|
||||
|
||||
|
153
build/lib/gradio/static/js/interfaces/input/audio.js
Normal file
153
build/lib/gradio/static/js/interfaces/input/audio.js
Normal file
@ -0,0 +1,153 @@
|
||||
const audio_input = {
|
||||
html: `
|
||||
<div class="interface_box">
|
||||
<div class="file_zone hidden">
|
||||
<div class="upload_zone drop_zone">
|
||||
<div class="input_caption">Drop Audio Here<br>- or -<br>Click to Upload</div>
|
||||
</div>
|
||||
<div class="file_display hide">
|
||||
<div class="file_name"></div>
|
||||
<div class="file_size"></div>
|
||||
</div>
|
||||
<input class="hidden_upload" type="file" accept="audio/*" />
|
||||
</div>
|
||||
<div class="upload_zone mic_zone hidden">
|
||||
<img class="not_recording" src="/static/img/mic.png" />
|
||||
<div class="recording hidden volume_display">
|
||||
<div class="volume volume_left">
|
||||
<div class="volume_bar"></div>
|
||||
</div>
|
||||
<img src="/static/img/mic_recording.png" />
|
||||
<div class="volume volume_right">
|
||||
<div class="volume_bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="not_recording input_caption">Click to Record from Microphone</div>
|
||||
<div class="recording hidden input_caption">Click to Stop Recording</div>
|
||||
</div>
|
||||
<div class="player hidden">
|
||||
<div class="waveform"></div>
|
||||
<button class="playpause primary">Play / Pause</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
state: "NO_AUDIO",
|
||||
init: function(opts) {
|
||||
var io = this;
|
||||
this.source = opts.source;
|
||||
this.wavesurfer = WaveSurfer.create({
|
||||
container: io.target.find('.waveform')[0],
|
||||
waveColor: '#888888',
|
||||
progressColor: '#e67e22',
|
||||
barWidth: 3,
|
||||
hideScrollbar: true
|
||||
});
|
||||
if (this.source == "microphone") {
|
||||
this.target.find(".mic_zone").removeClass("hidden");
|
||||
this.target.find(".mic_zone").click(function() {
|
||||
if (io.state == "NO_AUDIO") {
|
||||
if (!has_audio_loaded) {
|
||||
loadAudio();
|
||||
io.mic = new p5.AudioIn();
|
||||
}
|
||||
io.recorder = new p5.SoundRecorder();
|
||||
io.soundFile = new p5.SoundFile();
|
||||
io.recorder.setInput(io.mic);
|
||||
io.target.find(".recording").removeClass("hidden");
|
||||
io.target.find(".not_recording").hide();
|
||||
io.state = "RECORDING";
|
||||
io.mic.start();
|
||||
io.recorder.record(io.soundFile);
|
||||
|
||||
io.interval_id = window.setInterval(function () {
|
||||
var volume = Math.floor(100 * io.mic.getLevel());
|
||||
io.target.find(".volume_bar").width(`${(volume > 0 ? 10 : 0) + Math.round(2 * Math.sqrt(10 * volume))}px`)
|
||||
}, 100)
|
||||
}
|
||||
});
|
||||
|
||||
this.target.find(".mic_zone").mousedown(function() {
|
||||
if (io.state == "RECORDING" || io.state == "STOP_RECORDING") {
|
||||
io.target.find(".upload_zone").hide();
|
||||
io.recorder.stop();
|
||||
var blob = io.soundFile.getBlob();
|
||||
var reader = new window.FileReader();
|
||||
reader.readAsDataURL(blob);
|
||||
reader.onloadend = function() {
|
||||
io.load_preview_from_audio(reader.result);
|
||||
}
|
||||
if (io.interval_id) {
|
||||
window.clearInterval(io.interval_id);
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (this.source == "upload") {
|
||||
this.target.find(".file_zone").removeClass("hidden");
|
||||
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(".playpause").click(function () {
|
||||
io.wavesurfer.playPause();
|
||||
})
|
||||
},
|
||||
load_preview_from_audio: function(audio) {
|
||||
var io = this;
|
||||
io.audio_data = audio;
|
||||
io.target.find(".player").removeClass("hidden");
|
||||
io.wavesurfer.load(io.audio_data);
|
||||
if (io.state == "STOP_RECORDING") {
|
||||
io.state = "RECORDED";
|
||||
io.submit();
|
||||
}
|
||||
io.state = "RECORDED";
|
||||
},
|
||||
load_preview_from_files: function(files) {
|
||||
if (!files.length || !window.FileReader) {
|
||||
return
|
||||
}
|
||||
var ReaderObj = new FileReader()
|
||||
ReaderObj.readAsDataURL(files[0])
|
||||
ReaderObj.io = this;
|
||||
this.state = "AUDIO_LOADING"
|
||||
ReaderObj.onloadend = function() {
|
||||
let io = this.io;
|
||||
io.target.find(".upload_zone").hide();
|
||||
io.load_preview_from_audio(this.result);
|
||||
}
|
||||
},
|
||||
submit: function() {
|
||||
if (this.state == "RECORDED") {
|
||||
this.io_master.input(this.id, this.audio_data);
|
||||
} else if (this.state == "RECORDING") {
|
||||
this.state = "STOP_RECORDING";
|
||||
this.target.find(".upload_zone").mousedown();
|
||||
}
|
||||
},
|
||||
clear: function() {
|
||||
this.audio_data = null;
|
||||
this.state = "NO_AUDIO";
|
||||
this.target.find(".not_recording").show();
|
||||
this.target.find(".recording").addClass("hidden");
|
||||
this.target.find(".player").addClass("hidden");
|
||||
this.target.find(".upload_zone").show();
|
||||
this.target.find(".hidden_upload").prop("value", "")
|
||||
if (this.wavesurfer) {
|
||||
this.wavesurfer.stop();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ const checkbox = {
|
||||
<label><input class="checkbox" type="checkbox"> </label>
|
||||
</div>`,
|
||||
init: function(opts) {
|
||||
this.target.css("height", "auto");
|
||||
this.target.find("input").checkboxradio();
|
||||
},
|
||||
submit: function() {
|
||||
|
@ -1,7 +1,6 @@
|
||||
const checkbox_group = {
|
||||
html: ``,
|
||||
init: function(opts) {
|
||||
this.target.css("height", "auto");
|
||||
this.choices = opts.choices;
|
||||
html = "<div class='checkbox_group'>"
|
||||
for ([index, choice] of opts.choices.entries()) {
|
||||
|
56
build/lib/gradio/static/js/interfaces/input/dataframe.js
Normal file
56
build/lib/gradio/static/js/interfaces/input/dataframe.js
Normal file
@ -0,0 +1,56 @@
|
||||
const dataframe_input = {
|
||||
html: `
|
||||
<div class="interface_max_box">
|
||||
<div class="dataframe">
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
init: function(opts) {
|
||||
let row_count = opts.row_count;
|
||||
let col_count = opts.col_count;
|
||||
this.datatype = opts.datatype;
|
||||
let data = [];
|
||||
for (let i = 0; i < row_count; i++) {
|
||||
let row = []
|
||||
for (let j = 0; j < col_count; j++) {
|
||||
row.push(null);
|
||||
}
|
||||
data.push(row);
|
||||
}
|
||||
let config = {data: data};
|
||||
if (opts.headers || opts.datatype) {
|
||||
let column_config = [];
|
||||
for (let i = 0; i < col_count; i++) {
|
||||
let column = {};
|
||||
if (opts.datatype) {
|
||||
let datatype = typeof opts.datatype === "string" ? opts.datatype : opts.datatype[i];
|
||||
let datatype_map = {"str": "text", "bool": "checkbox", "number": "numeric", "date": "calendar"}
|
||||
column.type = datatype_map[datatype];
|
||||
}
|
||||
if (opts.headers) {
|
||||
column.title = opts.headers[i];
|
||||
}
|
||||
column_config.push(column);
|
||||
}
|
||||
config.columns = column_config;
|
||||
}
|
||||
this.config = config;
|
||||
this.table = this.target.find(".dataframe").jexcel(config);
|
||||
},
|
||||
submit: function() {
|
||||
let data = this.table.getData();
|
||||
if (this.datatype) {
|
||||
for (let i = 0; i < data[0].length; i++) {
|
||||
if (this.datatype == "number" || (i < this.datatype.length && this.datatype[i].type == "number")) {
|
||||
for (let j = 0; j < data.length; j++) {
|
||||
let val = data[j][i];
|
||||
data[j][i] = val == "" ? 0 : parseFloat(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.io_master.input(this.id, data);
|
||||
},
|
||||
clear: function() {
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
const dropdown = {
|
||||
html: ``,
|
||||
init: function(opts) {
|
||||
this.target.css("height", "auto");
|
||||
this.choices = opts.choices;
|
||||
html = "<select class='dropdown'>"
|
||||
for ([index, choice] of opts.choices.entries()) {
|
||||
|
63
build/lib/gradio/static/js/interfaces/input/file.js
Normal file
63
build/lib/gradio/static/js/interfaces/input/file.js
Normal file
@ -0,0 +1,63 @@
|
||||
const file_input = {
|
||||
html: `
|
||||
<div class="interface_mini_box">
|
||||
<div class="upload_zone drop_zone">
|
||||
<div class="input_caption">Drop File Here<br>- or -<br>Click to Upload</div>
|
||||
</div>
|
||||
<div class="file_display hide">
|
||||
<div class="file_name"></div>
|
||||
<div class="file_size"></div>
|
||||
</div>
|
||||
<input class="hidden_upload" type="file" />
|
||||
</div>
|
||||
`
|
||||
,
|
||||
init: function(opts) {
|
||||
var io = this;
|
||||
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);
|
||||
}
|
||||
})
|
||||
},
|
||||
submit: function() {
|
||||
if (this.file_data) {
|
||||
this.io_master.input(this.id, this.file_data);
|
||||
}
|
||||
},
|
||||
load_preview_from_files: function(files) {
|
||||
if (!files.length || !window.FileReader) {
|
||||
return
|
||||
}
|
||||
var ReaderObj = new FileReader()
|
||||
ReaderObj.readAsDataURL(files[0])
|
||||
ReaderObj.io = this;
|
||||
ReaderObj.onloadend = function() {
|
||||
let io = this.io;
|
||||
io.target.find(".upload_zone").hide();
|
||||
io.target.find(".file_display").removeClass("hide");
|
||||
io.target.find(".file_name").text(files[0].name);
|
||||
io.target.find(".file_size").text(prettyBytes(files[0].size));
|
||||
io.file_data = this.result;
|
||||
}
|
||||
},
|
||||
clear: function() {
|
||||
this.target.find(".upload_zone").show();
|
||||
this.target.find(".file_display").addClass("hide");
|
||||
this.target.find(".hidden_upload").prop("value", "")
|
||||
this.file_data = null;
|
||||
},
|
||||
file_data: null,
|
||||
}
|
@ -1,22 +1,39 @@
|
||||
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 class="interface_box">
|
||||
<div class="upload_zone drop_zone hide">
|
||||
<div class="input_caption">Drop Image Here<br>- or -<br>Click to Upload</div>
|
||||
</div>
|
||||
<div class="view_holders">
|
||||
<div class="image_preview_holder">
|
||||
<img class="image_preview" />
|
||||
<div class="webcam upload_zone hide">
|
||||
<div class="webcam_box">
|
||||
</div>
|
||||
<div class="saliency_holder hide">
|
||||
<canvas class="saliency"></canvas>
|
||||
<span>Click to Snap!</span>
|
||||
</div>
|
||||
<div class="sketchpad hide">
|
||||
<div class="sketch_tools">
|
||||
<div id="brush_1" size="8" class="brush"></div>
|
||||
<div id="brush_2" size="16" class="brush selected"></div>
|
||||
<div id="brush_3" size="24" class="brush"></div>
|
||||
</div>
|
||||
<div class="view_holders">
|
||||
<div class="canvas_holder">
|
||||
<canvas class="sketch"></canvas>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
<input class="hidden_upload" type="file" accept="image/x-png,image/gif,image/jpeg" />
|
||||
</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}">
|
||||
@ -27,28 +44,72 @@ const image_input = {
|
||||
`,
|
||||
init: function(opts) {
|
||||
var io = this;
|
||||
this.source = opts.source;
|
||||
$('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);
|
||||
}
|
||||
})
|
||||
if (this.source == "upload") {
|
||||
io.target.find(".drop_zone").removeClass("hide");
|
||||
this.target.find(".drop_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);
|
||||
}
|
||||
})
|
||||
} else if (this.source == "webcam") {
|
||||
io.target.find(".webcam").removeClass("hide");
|
||||
let w = this.target.find(".webcam_box").width();
|
||||
let h = this.target.find(".webcam_box").height();
|
||||
let RATIO = 4/3;
|
||||
let dim = Math.min(h, w / RATIO);
|
||||
Webcam.set({
|
||||
image_format: 'jpeg',
|
||||
width: dim * RATIO,
|
||||
height: dim,
|
||||
dest_width: dim * RATIO,
|
||||
dest_height: dim,
|
||||
})
|
||||
Webcam.attach(this.target.find(".webcam_box")[0]);
|
||||
io.target.find(".webcam").click(function() {
|
||||
Webcam.snap(function(image_data) {
|
||||
io.target.find(".webcam").hide();
|
||||
io.target.find(".image_display").removeClass("hide");
|
||||
io.set_image_data(image_data, /*update_editor=*/true);
|
||||
io.state = "IMAGE_LOADED";
|
||||
});
|
||||
})
|
||||
} else if (this.source == "canvas") {
|
||||
io.target.find(".sketchpad").removeClass("hide");
|
||||
var dimension = Math.min(this.target.find(".canvas_holder").width(),
|
||||
this.target.find(".canvas_holder").height()) - 2 // dimension - border
|
||||
var id = this.id;
|
||||
this.sketchpad = new Sketchpad({
|
||||
element: '.interface[interface_id=' + id + '] .sketch',
|
||||
width: dimension,
|
||||
height: dimension
|
||||
});
|
||||
this.sketchpad.penSize = this.target.find(".brush.selected").attr("size");
|
||||
this.canvas = this.target.find('.canvas_holder canvas')[0];
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.target.find(".brush").click(function (e) {
|
||||
io.target.find(".brush").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
io.sketchpad.penSize = $(this).attr("size");
|
||||
})
|
||||
this.clear();
|
||||
}
|
||||
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], {
|
||||
@ -70,45 +131,47 @@ const image_input = {
|
||||
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);
|
||||
}
|
||||
});
|
||||
},
|
||||
submit: function() {
|
||||
var io = this;
|
||||
if (this.state == "IMAGE_LOADED") {
|
||||
if (this.source == "canvas") {
|
||||
var dataURL = this.canvas.toDataURL("image/png");
|
||||
this.io_master.input(this.id, dataURL);
|
||||
} else if (this.state == "IMAGE_LOADED") {
|
||||
io.io_master.input(io.id, this.image_data);
|
||||
} else if (this.source == "webcam") {
|
||||
Webcam.snap(function(image_data) {
|
||||
io.target.find(".webcam").hide();
|
||||
io.target.find(".image_display").removeClass("hide");
|
||||
io.set_image_data(image_data, /*update_editor=*/true);
|
||||
io.state = "IMAGE_LOADED";
|
||||
io.io_master.input(io.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);
|
||||
}
|
||||
if (this.source == "canvas") {
|
||||
this.context.fillStyle = "#FFFFFF";
|
||||
this.context.fillRect(0, 0, this.context.canvas.width, this.context.
|
||||
canvas.height);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
},
|
||||
state: "NO_IMAGE",
|
||||
image_data: null,
|
||||
set_image_data: function(data, update_editor) {
|
||||
set_image_data: function(image_data, update_editor) {
|
||||
let io = this;
|
||||
io.image_data = data
|
||||
io.target.find(".image_preview").attr('src', 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();
|
||||
@ -138,9 +201,21 @@ const image_input = {
|
||||
},
|
||||
load_example: function(data) {
|
||||
let io = this;
|
||||
io.target.find(".upload_zone").hide();
|
||||
io.target.find(".image_display").removeClass("hide");
|
||||
io.set_image_data(data, /*update_editor=*/true);
|
||||
io.state = "IMAGE_LOADED"
|
||||
if (this.source == "canvas") {
|
||||
this.clear();
|
||||
let ctx = this.context;
|
||||
var img = new Image;
|
||||
let dimension = this.target.find(".canvas_holder canvas").width();
|
||||
img.onload = function(){
|
||||
ctx.clearRect(0,0,dimension,dimension);
|
||||
ctx.drawImage(img,0,0,dimension,dimension);
|
||||
};
|
||||
img.src = data;
|
||||
} else {
|
||||
io.target.find(".upload_zone").hide();
|
||||
io.target.find(".image_display").removeClass("hide");
|
||||
io.set_image_data(data, /*update_editor=*/true);
|
||||
io.state = "IMAGE_LOADED";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,24 @@
|
||||
const microphone = {
|
||||
html: `
|
||||
<div class="upload_zone">
|
||||
<img class="not_recording" src="/static/img/mic.png" />
|
||||
<div class="recording hidden volume_display">
|
||||
<div class="volume volume_left">
|
||||
<div class="volume_bar"></div>
|
||||
</div>
|
||||
<img src="/static/img/mic_recording.png" />
|
||||
<div class="volume volume_right">
|
||||
<div class="volume_bar"></div>
|
||||
<div class="interface_box">
|
||||
<div class="upload_zone">
|
||||
<img class="not_recording" src="/static/img/mic.png" />
|
||||
<div class="recording hidden volume_display">
|
||||
<div class="volume volume_left">
|
||||
<div class="volume_bar"></div>
|
||||
</div>
|
||||
<img src="/static/img/mic_recording.png" />
|
||||
<div class="volume volume_right">
|
||||
<div class="volume_bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="not_recording input_caption">Click to Record from Microphone</div>
|
||||
<div class="recording hidden input_caption">Click to Stop Recording</div>
|
||||
</div>
|
||||
<div class="player hidden">
|
||||
<div class="waveform"></div>
|
||||
<button class="playpause primary">Play / Pause</button>
|
||||
</div>
|
||||
<div class="not_recording input_caption">Click to Record from Microphone</div>
|
||||
<div class="recording hidden input_caption">Click to Stop Recording</div>
|
||||
</div>
|
||||
<div class="player hidden">
|
||||
<div class="waveform"></div>
|
||||
<button class="playpause primary">Play / Pause</button>
|
||||
</div>
|
||||
`,
|
||||
state: "NO_AUDIO",
|
||||
@ -78,6 +80,7 @@ const microphone = {
|
||||
if (this.state == "RECORDED") {
|
||||
this.io_master.input(this.id, this.audio_data);
|
||||
} else if (this.state == "RECORDING") {
|
||||
this.state = "STOP_RECORDING";
|
||||
this.target.find(".upload_zone").mousedown();
|
||||
}
|
||||
},
|
||||
|
@ -1,7 +1,6 @@
|
||||
const radio = {
|
||||
html: ``,
|
||||
init: function(opts) {
|
||||
this.target.css("height", "auto");
|
||||
this.choices = opts.choices;
|
||||
html = "<div class='radio_group'>"
|
||||
for ([index, choice] of opts.choices.entries()) {
|
||||
|
@ -1,15 +1,18 @@
|
||||
const sketchpad_input = {
|
||||
html: `
|
||||
<div class="sketch_tools">
|
||||
<div id="brush_1" size="8" class="brush"></div>
|
||||
<div id="brush_2" size="16" class="brush selected"></div>
|
||||
<div id="brush_3" size="24" class="brush"></div>
|
||||
</div>
|
||||
<div class="view_holders">
|
||||
<div class="canvas_holder">
|
||||
<canvas class="sketch"></canvas>
|
||||
<div class="interface_box">
|
||||
<div class="sketch_tools">
|
||||
<div id="brush_1" size="8" class="brush"></div>
|
||||
<div id="brush_2" size="16" class="brush selected"></div>
|
||||
<div id="brush_3" size="24" class="brush"></div>
|
||||
</div>
|
||||
</div>`,
|
||||
<div class="view_holders">
|
||||
<div class="canvas_holder">
|
||||
<canvas class="sketch"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
init: function() {
|
||||
var io = this;
|
||||
var dimension = Math.min(this.target.find(".canvas_holder").width(),
|
||||
|
@ -8,15 +8,6 @@ const slider = {
|
||||
init: function(opts) {
|
||||
let io = this;
|
||||
this.minimum = opts.minimum;
|
||||
this.target.css("height", "auto");
|
||||
let difference = opts.maximum - opts.minimum;
|
||||
if (difference <= 1) {
|
||||
step = 0.01;
|
||||
} else if (difference <= 10) {
|
||||
step = 0.1;
|
||||
} else {
|
||||
step = 1;
|
||||
}
|
||||
var handle = this.target.find(".ui-slider-handle");
|
||||
this.slider = this.target.find(".slider").slider({
|
||||
create: function() {
|
||||
@ -27,7 +18,7 @@ const slider = {
|
||||
},
|
||||
min: opts.minimum,
|
||||
max: opts.maximum,
|
||||
step: step
|
||||
step: opts.step
|
||||
});
|
||||
},
|
||||
submit: function() {
|
||||
|
@ -1,12 +1,9 @@
|
||||
const textbox_input = {
|
||||
html: `<textarea class="input_text"></textarea>
|
||||
<div class='input_text_saliency'></div>`,
|
||||
disabled_html: `<textarea class="input_text" disabled></textarea>
|
||||
<div class='input_text_saliency'></div>`,
|
||||
init: function(opts) {
|
||||
if (opts.lines) {
|
||||
this.target.find(".input_text").attr("rows", opts.lines).css("height", "auto");
|
||||
this.target.css("height", "auto");
|
||||
}
|
||||
if (opts.placeholder) {
|
||||
this.target.find(".input_text").attr("placeholder", opts.placeholder)
|
||||
|
@ -1,10 +1,11 @@
|
||||
const webcam = {
|
||||
html: `
|
||||
<div class="webcam_box"></div>
|
||||
<div class="interface_box">
|
||||
<div class="webcam_box"></div>
|
||||
</div>
|
||||
`,
|
||||
init: function(opts) {
|
||||
var io = this;
|
||||
// this.target.find(".webcam_box").width(this.target.find(".webcam_box").width);
|
||||
let w = this.target.find(".webcam_box").width();
|
||||
let h = this.target.find(".webcam_box").height();
|
||||
let RATIO = 4/3;
|
||||
|
34
build/lib/gradio/static/js/interfaces/output/audio.js
Normal file
34
build/lib/gradio/static/js/interfaces/output/audio.js
Normal file
@ -0,0 +1,34 @@
|
||||
const audio_output = {
|
||||
html: `
|
||||
<div class="interface_box">
|
||||
<div class="player hidden">
|
||||
<div class="waveform"></div>
|
||||
<button class="playpause primary">Play / Pause</button>
|
||||
</div>
|
||||
</div
|
||||
`,
|
||||
state: "NO_AUDIO",
|
||||
init: function(opts) {
|
||||
var io = this;
|
||||
this.wavesurfer = WaveSurfer.create({
|
||||
container: io.target.find('.waveform')[0],
|
||||
waveColor: '#888888',
|
||||
progressColor: '#e67e22',
|
||||
barWidth: 3,
|
||||
hideScrollbar: true
|
||||
});
|
||||
this.target.find(".playpause").click(function () {
|
||||
io.wavesurfer.playPause();
|
||||
})
|
||||
},
|
||||
output: function(data) {
|
||||
io.target.find(".player").removeClass("hidden");
|
||||
this.wavesurfer.load(data);
|
||||
},
|
||||
clear: function() {
|
||||
this.target.find(".player").addClass("hidden");
|
||||
if (this.wavesurfer) {
|
||||
this.wavesurfer.stop();
|
||||
}
|
||||
}
|
||||
}
|
27
build/lib/gradio/static/js/interfaces/output/dataframe.js
Normal file
27
build/lib/gradio/static/js/interfaces/output/dataframe.js
Normal file
@ -0,0 +1,27 @@
|
||||
const dataframe_output = {
|
||||
html: `
|
||||
<div class="interface_max_box">
|
||||
<div class="dataframe"></div>
|
||||
</div>
|
||||
`,
|
||||
init: function(opts) {
|
||||
},
|
||||
output: function(data) {
|
||||
let config = {data: data.data};
|
||||
if (data.headers) {
|
||||
let column_config = [];
|
||||
for (let header of data.headers) {
|
||||
column_config.push({title: header});
|
||||
}
|
||||
config.columns = column_config;
|
||||
}
|
||||
if (this.table) {
|
||||
this.clear();
|
||||
}
|
||||
this.table = this.target.find(".dataframe").jexcel(config);
|
||||
},
|
||||
clear: function() {
|
||||
jexcel.destroy(this.target.find(".dataframe")[0]);
|
||||
this.table = null;
|
||||
}
|
||||
}
|
28
build/lib/gradio/static/js/interfaces/output/file.js
Normal file
28
build/lib/gradio/static/js/interfaces/output/file.js
Normal file
@ -0,0 +1,28 @@
|
||||
const file_output = {
|
||||
html: `
|
||||
<a class="interface_mini_box">
|
||||
<div class="file_display file_download">
|
||||
<div class="file_name"></div>
|
||||
<div class="file_size"></div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
init: function(opts) {
|
||||
},
|
||||
output: function(data) {
|
||||
this.target.find(".file_name").text(data.name);
|
||||
this.target.find(".file_size").text(prettyBytes(data.size));
|
||||
this.target.find(".interface_mini_box")
|
||||
.attr("href", "data:;base64," + data.data)
|
||||
.attr("download", data.name);
|
||||
},
|
||||
submit: function() {
|
||||
},
|
||||
clear: function() {
|
||||
this.target.find(".file_name").empty();
|
||||
this.target.find(".file_size").empty();
|
||||
this.target.find(".interface_mini_box")
|
||||
.removeAttr("href")
|
||||
.removeAttr("download");
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
const highlighted_text = {
|
||||
html: `
|
||||
<div class="highlight_legend">
|
||||
<div class="color_legend invisible">
|
||||
<span>-1</span>
|
||||
<span>0</span>
|
||||
<span>+1</span>
|
||||
</div>
|
||||
<div class="category_legend invisible"></div>
|
||||
</div>
|
||||
<div class="output_text"></div>
|
||||
`,
|
||||
init: function(opts) {
|
||||
this.color_map = {};
|
||||
if (opts.color_map) {
|
||||
this.generate_category_legend(opts.color_map);
|
||||
}
|
||||
},
|
||||
new_category_index: 0,
|
||||
generate_category_legend: function(map) {
|
||||
let default_colors = ["pink", "lightblue", "gold", "plum", "lightskyblue", "greenyellow", "khaki", "cyan", "moccasin", "lightgray"]
|
||||
for (let category in map) {
|
||||
if (category in this.color_map) {
|
||||
continue;
|
||||
}
|
||||
let color = map[category];
|
||||
if (!color) {
|
||||
if (this.new_category_index < default_colors.length) {
|
||||
color = default_colors[this.new_category_index];
|
||||
this.new_category_index++;
|
||||
} else {
|
||||
function randInt(min, max) {
|
||||
return Math.floor(Math.random() * (max- min) + min);
|
||||
}
|
||||
color = "rgb(" + randInt(128, 240) + ", " + randInt(128, 240) + ", " + randInt(128, 240) + ")"
|
||||
}
|
||||
}
|
||||
this.color_map[category] = color;
|
||||
this.target.find(".category_legend").append(`
|
||||
<div class="category-label">
|
||||
<div style="background-color:${color}"> </div>
|
||||
${category}
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
},
|
||||
output: function(data) {
|
||||
if (data.length == 0) {
|
||||
return;
|
||||
} else if (typeof(data[0][1]) == "string") {
|
||||
this.target.find(".category_legend").removeClass("invisible");
|
||||
let new_color_map = {};
|
||||
for (let span of data) {
|
||||
let category = span[1];
|
||||
if (category != null) {
|
||||
new_color_map[category] = null;
|
||||
}
|
||||
}
|
||||
this.generate_category_legend(new_color_map);
|
||||
let html = "";
|
||||
for (let span of data) {
|
||||
let category = span[1];
|
||||
let color = category == null ? "white" : this.color_map[category];
|
||||
html += `<span title="${category}" style="background-color: ${color}">${span[0]}</span>`
|
||||
}
|
||||
this.target.find(".output_text").html(html);
|
||||
|
||||
} else {
|
||||
this.target.find(".color_legend").removeClass("invisible");
|
||||
let html = "";
|
||||
for (let span of data) {
|
||||
let value = span[1];
|
||||
let color = "";
|
||||
if (value < 0) {
|
||||
color = "8,241,255," + (-value);
|
||||
} else {
|
||||
color = "230,126,34," + value;
|
||||
}
|
||||
html += `<span title="${value}" style="background-color: rgba(${color})">${span[0]}</span>`
|
||||
}
|
||||
this.target.find(".output_text").html(html);
|
||||
}
|
||||
},
|
||||
submit: function() {
|
||||
},
|
||||
clear: function() {
|
||||
this.target.find(".output_text").empty();
|
||||
this.target.find(".highlight_legend div").addClass("invisible");
|
||||
}
|
||||
}
|
11
build/lib/gradio/static/js/interfaces/output/html.js
Normal file
11
build/lib/gradio/static/js/interfaces/output/html.js
Normal file
@ -0,0 +1,11 @@
|
||||
const html_output = {
|
||||
html: ``,
|
||||
init: function(opts) {
|
||||
},
|
||||
output: function(data) {
|
||||
this.target.html(data);
|
||||
},
|
||||
clear: function() {
|
||||
this.target.empty();
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
const image_output = {
|
||||
html: `
|
||||
<div class="interface_box">
|
||||
<div class="output_image_holder">
|
||||
<img class="output_image" />
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
init: function(opts) {},
|
||||
output: function(data) {
|
||||
|
13
build/lib/gradio/static/js/interfaces/output/json.js
Normal file
13
build/lib/gradio/static/js/interfaces/output/json.js
Normal file
@ -0,0 +1,13 @@
|
||||
const json_output = {
|
||||
html: `
|
||||
`,
|
||||
init: function(opts) {
|
||||
},
|
||||
output: function(data) {
|
||||
this.clear();
|
||||
jsonTree.create(data, this.target[0]);
|
||||
},
|
||||
clear: function() {
|
||||
this.target.empty();
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
const textbox_output = {
|
||||
html: `<div class="output_text"></div>`,
|
||||
init: function(opts) {
|
||||
this.target.css("height", "auto");
|
||||
},
|
||||
output: function(data) {
|
||||
this.target.find(".output_text").text(data);
|
||||
|
@ -1,20 +0,0 @@
|
||||
history_count = 0;
|
||||
entry_history = [];
|
||||
|
||||
function add_history(entry) {
|
||||
$("#featured_table").append(`
|
||||
<div entry=${history_count}>${io_master.input_interface.renderFeatured(entry)}</div>
|
||||
`);
|
||||
entry_history.push(entry);
|
||||
history_count++;
|
||||
}
|
||||
|
||||
function load_history(data) {
|
||||
data.forEach(add_history)
|
||||
}
|
||||
|
||||
$('body').on('click', "#featured_table div", function() {
|
||||
let entry = entry_history[$(this).attr("entry")];
|
||||
io_master.input_interface.loadFeatured(entry);
|
||||
io_master.output_interface.clear();
|
||||
})
|
@ -1,119 +0,0 @@
|
||||
input_to_object_map = {
|
||||
"csv" : {},
|
||||
"imagein" : image_input,
|
||||
"sketchpad" : sketchpad_input,
|
||||
"textbox" : textbox_input,
|
||||
"webcam" : webcam,
|
||||
"microphone" : microphone,
|
||||
"radio" : radio,
|
||||
"checkbox" : checkbox,
|
||||
"checkboxgroup" : checkbox_group,
|
||||
"slider" : slider,
|
||||
"dropdown" : dropdown,
|
||||
}
|
||||
output_to_object_map = {
|
||||
"csv" : {},
|
||||
"image" : image_output,
|
||||
"label" : label_output,
|
||||
"textbox" : textbox_output
|
||||
}
|
||||
id_to_interface_map = {}
|
||||
|
||||
function set_interface_id(interface, id) {
|
||||
interface.id = id;
|
||||
id_to_interface_map[id] = interface;
|
||||
}
|
||||
|
||||
var config;
|
||||
$.getJSON("static/config.json", function(data) {
|
||||
config = data;
|
||||
_id = 0;
|
||||
let input_interfaces = [];
|
||||
let output_interfaces = [];
|
||||
for (let i = 0; i < config["input_interfaces"].length; i++) {
|
||||
input_interface_data = config["input_interfaces"][i];
|
||||
input_interface = Object.create(input_to_object_map[input_interface_data[0]]);
|
||||
if (input_interface_data[1]["label"]) {
|
||||
$(".input_interfaces").append(`
|
||||
<div class="panel_header">${input_interface_data[1]["label"]}</strong>
|
||||
`);
|
||||
}
|
||||
$(".input_interfaces").append(`
|
||||
<div class="input_interface interface" interface_id=${_id}>
|
||||
${input_interface.html}
|
||||
</div>
|
||||
`);
|
||||
input_interface.target = $(`.input_interface[interface_id=${_id}]`);
|
||||
set_interface_id(input_interface, _id);
|
||||
input_interface.init(input_interface_data[1]);
|
||||
input_interfaces.push(input_interface);
|
||||
input_interface.io_master = io_master;
|
||||
_id++;
|
||||
}
|
||||
for (let i = 0; i < config["output_interfaces"].length; i++) {
|
||||
if (i != 0 && i % (config["output_interfaces"].length / config.function_count) == 0) {
|
||||
$(".output_interfaces").append("<hr>");
|
||||
}
|
||||
output_interface_data = config["output_interfaces"][i];
|
||||
output_interface = Object.create(output_to_object_map[
|
||||
output_interface_data[0]]);
|
||||
if (output_interface_data[1]["label"]) {
|
||||
$(".output_interfaces").append(`
|
||||
<div class="panel_header">${output_interface_data[1]["label"]}</strong>
|
||||
`);
|
||||
}
|
||||
$(".output_interfaces").append(`
|
||||
<div class="output_interface interface" interface_id=${_id}>
|
||||
${output_interface.html}
|
||||
</div>
|
||||
`);
|
||||
output_interface.target = $(`.output_interface[interface_id=${_id}]`);
|
||||
set_interface_id(output_interface, _id);
|
||||
output_interface.init(output_interface_data[1]);
|
||||
output_interfaces.push(output_interface);
|
||||
output_interface.io_master = io_master;
|
||||
_id++;
|
||||
}
|
||||
io_master.input_interfaces = input_interfaces;
|
||||
io_master.output_interfaces = output_interfaces;
|
||||
$(".clear").click(function() {
|
||||
for (let input_interface of input_interfaces) {
|
||||
input_interface.clear();
|
||||
}
|
||||
for (let output_interface of output_interfaces) {
|
||||
output_interface.clear();
|
||||
}
|
||||
$(".flag").removeClass("flagged");
|
||||
$(".flag_message").empty();
|
||||
$("#loading").addClass("invisible");
|
||||
$(".output_interface").removeClass("invisible");
|
||||
io_master.last_input = null;
|
||||
io_master.last_output = null;
|
||||
})
|
||||
if (config["share_url"] != "None") {
|
||||
$("#share_row").css('display', 'flex');
|
||||
}
|
||||
load_history(config["sample_inputs"] || []);
|
||||
if (!config["sample_inputs"]) {
|
||||
$("#featured_history").hide();
|
||||
}
|
||||
if (config.live) {
|
||||
io_master.gather();
|
||||
} else {
|
||||
$(".submit").show();
|
||||
$(".submit").click(function() {
|
||||
io_master.gather();
|
||||
$(".flag").removeClass("flagged");
|
||||
})
|
||||
}
|
||||
if (!config.show_input) {
|
||||
$(".input_panel").hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('click', '.flag', function(e) {
|
||||
if (io_master.last_output) {
|
||||
$(".flag").addClass("flagged");
|
||||
io_master.flag($(".flag_message").val());
|
||||
}
|
||||
})
|
@ -1,41 +0,0 @@
|
||||
$("#share").click(function() {
|
||||
$("#share").hide()
|
||||
$("#share_form").css('display', 'flex')
|
||||
})
|
||||
|
||||
$("#send_link").click(function(evt) {
|
||||
let name = $("#share_name").val()
|
||||
let email = $("#share_email").val()
|
||||
if (name && email) {
|
||||
$("#send_link").attr('disabled', true);
|
||||
$.ajax({
|
||||
"url" : "https://gradio.app/api/send-email/",
|
||||
"type": "POST",
|
||||
"crossDomain": true,
|
||||
"data": {
|
||||
"url": config["share_url"],
|
||||
"name": name,
|
||||
"email": email
|
||||
},
|
||||
"success": function() {
|
||||
$("#share_message").text("Shared successfully.");
|
||||
$("#share_more").text("Share more");
|
||||
},
|
||||
"error": function() {
|
||||
$("#share_message").text("Failed to share.");
|
||||
$("#share_more").text("Try again");
|
||||
},
|
||||
"complete": function() {
|
||||
$("#share_form").hide();
|
||||
$("#share_complete").show();
|
||||
$("#send_link").attr('disabled', false);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
$("#share_more").click(function (evt) {
|
||||
$("#share_email").val("");
|
||||
$("#share_form").show();
|
||||
$("#share_complete").hide();
|
||||
})
|
@ -78,3 +78,14 @@ function interpolate(val, rgb1, rgb2) {
|
||||
function colorToString(rgb) {
|
||||
return "rgb(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ")";
|
||||
}
|
||||
|
||||
function prettyBytes(bytes) {
|
||||
let units = ["B", "KB", "MB", "GB", "PB"];
|
||||
let i = 0;
|
||||
while (bytes > 1024) {
|
||||
bytes /= 1024;
|
||||
i++;
|
||||
}
|
||||
let unit = units[i];
|
||||
return bytes.toFixed(1) + " " + unit;
|
||||
}
|
@ -6266,9 +6266,6 @@
|
||||
var source = container.bounds;
|
||||
var destination = box;
|
||||
source = {top: 0, left: 0, width: container.intrinsicWidth, height: container.intrinsicHeight}
|
||||
console.log(image)
|
||||
console.log(container)
|
||||
console.log(box)
|
||||
var newWidth = 30;
|
||||
var newHeight = 30;
|
||||
var newX = destination.left;
|
||||
@ -6283,9 +6280,6 @@
|
||||
newHeight = destination.height;
|
||||
newX = destination.left + (destination.width - newWidth) / 2;
|
||||
}
|
||||
console.log(destination.left, destination.top, destination.width, destination.height);
|
||||
console.log(newX, newY, newWidth, newHeight);
|
||||
console.log("---");
|
||||
this.ctx.drawImage(image, 0, 0, container.intrinsicWidth, container.intrinsicHeight, newX, newY, newWidth, newHeight);
|
||||
this.ctx.restore();
|
||||
}
|
||||
|
1
build/lib/gradio/static/js/vendor/jexcel.min.js
vendored
Normal file
1
build/lib/gradio/static/js/vendor/jexcel.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,238 +0,0 @@
|
||||
/*!
|
||||
* jQuery UI Touch Punch 1.0.7 as modified by RWAP Software
|
||||
* based on original touchpunch v0.2.3 which has not been updated since 2014
|
||||
*
|
||||
* Updates by RWAP Software to take account of various suggested changes on the original code issues
|
||||
*
|
||||
* Original: https://github.com/furf/jquery-ui-touch-punch
|
||||
* Copyright 2011–2014, Dave Furfero
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Fork: https://github.com/RWAP/jquery-ui-touch-punch
|
||||
*
|
||||
* Depends:
|
||||
* jquery.ui.widget.js
|
||||
* jquery.ui.mouse.js
|
||||
*/
|
||||
|
||||
(function( factory ) {
|
||||
if ( typeof define === "function" && define.amd ) {
|
||||
|
||||
// AMD. Register as an anonymous module.
|
||||
define([ "jquery", "jquery.ui" ], factory );
|
||||
} else {
|
||||
|
||||
// Browser globals
|
||||
factory( jQuery );
|
||||
}
|
||||
}(function ($) {
|
||||
|
||||
// Detect touch support
|
||||
$.support.touch = ( 'ontouchstart' in document
|
||||
|| 'ontouchstart' in window
|
||||
|| window.TouchEvent
|
||||
|| (window.DocumentTouch && document instanceof DocumentTouch)
|
||||
|| navigator.maxTouchPoints > 0
|
||||
|| navigator.msMaxTouchPoints > 0
|
||||
);
|
||||
|
||||
// Ignore browsers without touch or mouse support
|
||||
if (!$.support.touch || !$.ui.mouse) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mouseProto = $.ui.mouse.prototype,
|
||||
_mouseInit = mouseProto._mouseInit,
|
||||
_mouseDestroy = mouseProto._mouseDestroy,
|
||||
touchHandled;
|
||||
|
||||
/**
|
||||
* Get the x,y position of a touch event
|
||||
* @param {Object} event A touch event
|
||||
*/
|
||||
function getTouchCoords (event) {
|
||||
return {
|
||||
x: event.originalEvent.changedTouches[0].pageX,
|
||||
y: event.originalEvent.changedTouches[0].pageY
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a mouse event based on a corresponding touch event
|
||||
* @param {Object} event A touch event
|
||||
* @param {String} simulatedType The corresponding mouse event
|
||||
*/
|
||||
function simulateMouseEvent (event, simulatedType) {
|
||||
|
||||
// Ignore multi-touch events
|
||||
if (event.originalEvent.touches.length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent "Ignored attempt to cancel a touchmove event with cancelable=false" errors
|
||||
if (event.cancelable) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
var touch = event.originalEvent.changedTouches[0],
|
||||
simulatedEvent = document.createEvent('MouseEvents');
|
||||
|
||||
// Initialize the simulated mouse event using the touch event's coordinates
|
||||
simulatedEvent.initMouseEvent(
|
||||
simulatedType, // type
|
||||
true, // bubbles
|
||||
true, // cancelable
|
||||
window, // view
|
||||
1, // detail
|
||||
touch.screenX, // screenX
|
||||
touch.screenY, // screenY
|
||||
touch.clientX, // clientX
|
||||
touch.clientY, // clientY
|
||||
false, // ctrlKey
|
||||
false, // altKey
|
||||
false, // shiftKey
|
||||
false, // metaKey
|
||||
0, // button
|
||||
null // relatedTarget
|
||||
);
|
||||
|
||||
// Dispatch the simulated event to the target element
|
||||
event.target.dispatchEvent(simulatedEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the jQuery UI widget's touchstart events
|
||||
* @param {Object} event The widget element's touchstart event
|
||||
*/
|
||||
mouseProto._touchStart = function (event) {
|
||||
|
||||
var self = this;
|
||||
|
||||
// Interaction time
|
||||
this._startedMove = event.timeStamp;
|
||||
|
||||
// Track movement to determine if interaction was a click
|
||||
self._startPos = getTouchCoords(event);
|
||||
|
||||
// Ignore the event if another widget is already being handled
|
||||
if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the flag to prevent other widgets from inheriting the touch event
|
||||
touchHandled = true;
|
||||
|
||||
// Track movement to determine if interaction was a click
|
||||
self._touchMoved = false;
|
||||
|
||||
// Simulate the mouseover event
|
||||
simulateMouseEvent(event, 'mouseover');
|
||||
|
||||
// Simulate the mousemove event
|
||||
simulateMouseEvent(event, 'mousemove');
|
||||
|
||||
// Simulate the mousedown event
|
||||
simulateMouseEvent(event, 'mousedown');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the jQuery UI widget's touchmove events
|
||||
* @param {Object} event The document's touchmove event
|
||||
*/
|
||||
mouseProto._touchMove = function (event) {
|
||||
|
||||
// Ignore event if not handled
|
||||
if (!touchHandled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Interaction was moved
|
||||
this._touchMoved = true;
|
||||
|
||||
// Simulate the mousemove event
|
||||
simulateMouseEvent(event, 'mousemove');
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the jQuery UI widget's touchend events
|
||||
* @param {Object} event The document's touchend event
|
||||
*/
|
||||
mouseProto._touchEnd = function (event) {
|
||||
|
||||
// Ignore event if not handled
|
||||
if (!touchHandled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Simulate the mouseup event
|
||||
simulateMouseEvent(event, 'mouseup');
|
||||
|
||||
// Simulate the mouseout event
|
||||
simulateMouseEvent(event, 'mouseout');
|
||||
|
||||
// If the touch interaction did not move, it should trigger a click
|
||||
// Check for this in two ways - length of time of simulation and distance moved
|
||||
// Allow for Apple Stylus to be used also
|
||||
var timeMoving = event.timeStamp - this._startedMove;
|
||||
if (!this._touchMoved || timeMoving < 500) {
|
||||
// Simulate the click event
|
||||
simulateMouseEvent(event, 'click');
|
||||
} else {
|
||||
var endPos = getTouchCoords(event);
|
||||
if ((Math.abs(endPos.x - this._startPos.x) < 10) && (Math.abs(endPos.y - this._startPos.y) < 10)) {
|
||||
|
||||
// If the touch interaction did not move, it should trigger a click
|
||||
if (!this._touchMoved || event.originalEvent.changedTouches[0].touchType === 'stylus') {
|
||||
// Simulate the click event
|
||||
simulateMouseEvent(event, 'click');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unset the flag to determine the touch movement stopped
|
||||
this._touchMoved = false;
|
||||
|
||||
// Unset the flag to allow other widgets to inherit the touch event
|
||||
touchHandled = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* A duck punch of the $.ui.mouse _mouseInit method to support touch events.
|
||||
* This method extends the widget with bound touch event handlers that
|
||||
* translate touch events to mouse events and pass them to the widget's
|
||||
* original mouse event handling methods.
|
||||
*/
|
||||
mouseProto._mouseInit = function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
// Delegate the touch handlers to the widget's element
|
||||
self.element.on({
|
||||
touchstart: $.proxy(self, '_touchStart'),
|
||||
touchmove: $.proxy(self, '_touchMove'),
|
||||
touchend: $.proxy(self, '_touchEnd')
|
||||
});
|
||||
|
||||
// Call the original $.ui.mouse init method
|
||||
_mouseInit.call(self);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the touch event handlers
|
||||
*/
|
||||
mouseProto._mouseDestroy = function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
// Delegate the touch handlers to the widget's element
|
||||
self.element.off({
|
||||
touchstart: $.proxy(self, '_touchStart'),
|
||||
touchmove: $.proxy(self, '_touchMove'),
|
||||
touchend: $.proxy(self, '_touchEnd')
|
||||
});
|
||||
|
||||
// Call the original $.ui.mouse destroy method
|
||||
_mouseDestroy.call(self);
|
||||
};
|
||||
|
||||
}));
|
8
build/lib/gradio/static/js/vendor/jsuites.min.js
vendored
Normal file
8
build/lib/gradio/static/js/vendor/jsuites.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,33 +0,0 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Gradio</title>
|
||||
<link rel="stylesheet" href="../static/css/bulk_style.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<a href="https://gradio.app"><img src="../static/img/logo_inline.png" /></a>
|
||||
</nav>
|
||||
<table id="bulk_rows">
|
||||
<thead>
|
||||
<th>Image</th>
|
||||
<th>Label</th>
|
||||
</thead>
|
||||
</table>
|
||||
<script src="../static/js/vendor/jquery.min.js"></script>
|
||||
<script>
|
||||
$.get("/static/flagged/data.txt", function(data) {
|
||||
let lines = data.split("\n");
|
||||
lines.forEach((line) => {
|
||||
let row_data = JSON.parse(line);
|
||||
let output = row_data["output"];
|
||||
$("#bulk_rows").append(`
|
||||
<tr class="bulk_row">
|
||||
<td><img src="/static/flagged/${row_data["input"]}" /></td>
|
||||
<td class="label">${output["label"] + (output["confidences"] ? ": " + Math.round(100 * output["confidences"][0]["confidence"]) + "%" : "")}</td>
|
||||
</tr>
|
||||
`)
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -33,6 +33,9 @@
|
||||
<link type="text/css" href="../static/css/vendor/tui-color-picker.css" rel="stylesheet">
|
||||
<link type="text/css" href="../static/css/vendor/tui-image-editor.css" rel="stylesheet">
|
||||
<link type="text/css" href="../static/css/vendor/jquery-ui.css" rel="stylesheet">
|
||||
<link type="text/css" href="../static/css/vendor/jexcel.min.css" rel="stylesheet">
|
||||
<link type="text/css" href="../static/css/vendor/jsuites.min.css" rel="stylesheet">
|
||||
<link type="text/css" href="../static/css/vendor/jsonTree.css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
|
||||
|
||||
<link rel="stylesheet" href="../static/css/style.css">
|
||||
@ -47,10 +50,15 @@
|
||||
<link rel="stylesheet" href="../static/css/interfaces/input/slider.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/input/file.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/image.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/label.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/key_values.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/textbox.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/highlighted_text.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/audio.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/json.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/output/html.css">
|
||||
<link rel="stylesheet" href="../static/css/loading.css"/>
|
||||
</head>
|
||||
|
||||
@ -77,6 +85,7 @@
|
||||
<script src="../static/js/vendor/html2canvas.min.js"></script>
|
||||
<script src="../static/js/vendor/jquery-ui.min.js"></script>
|
||||
<script src="../static/js/vendor/jquery.ui.touch-punch.js"></script>
|
||||
<script src="../static/js/vendor/jsonTree.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>
|
||||
@ -84,6 +93,12 @@
|
||||
<script src="../static/js/vendor/tui-image-editor.js"></script>
|
||||
<script src="../static/js/vendor/white-theme.js"></script>
|
||||
<script src="../static/js/vendor/black-theme.js"></script>
|
||||
<script src="../static/js/vendor/wavesurfer.min.js"></script>
|
||||
<script src="../static/js/vendor/p5.min.js"></script>
|
||||
<script src="../static/js/vendor/p5.sound.min.js"></script>
|
||||
<script src="../static/js/vendor/p5.dom.min.js"></script>
|
||||
<script src="../static/js/vendor/jexcel.min.js"></script>
|
||||
<script src="../static/js/vendor/jsuites.min.js"></script>
|
||||
|
||||
<script src="../static/js/utils.js"></script>
|
||||
<script src="../static/js/all_io.js"></script>
|
||||
@ -97,18 +112,22 @@
|
||||
<script src="../static/js/interfaces/input/checkbox.js"></script>
|
||||
<script src="../static/js/interfaces/input/dropdown.js"></script>
|
||||
<script src="../static/js/interfaces/input/slider.js"></script>
|
||||
<script src="../static/js/interfaces/input/csv.js"></script>
|
||||
<script src="../static/js/interfaces/input/dataframe.js"></script>
|
||||
<script src="../static/js/interfaces/input/audio.js"></script>
|
||||
<script src="../static/js/interfaces/input/file.js"></script>
|
||||
<script src="../static/js/vendor/webcam.min.js"></script>
|
||||
<script src="../static/js/interfaces/input/webcam.js"></script>
|
||||
<script src="../static/js/interfaces/input/microphone.js"></script>
|
||||
<script src="../static/js/vendor/wavesurfer.min.js"></script>
|
||||
<script src="../static/js/vendor/p5.min.js"></script>
|
||||
<script src="../static/js/vendor/p5.sound.min.js"></script>
|
||||
<script src="../static/js/vendor/p5.dom.min.js"></script>
|
||||
<script src="../static/js/interfaces/output/image.js"></script>
|
||||
<script src="../static/js/interfaces/output/label.js"></script>
|
||||
<script src="../static/js/interfaces/output/key_values.js"></script>
|
||||
<script src="../static/js/interfaces/output/textbox.js"></script>
|
||||
<script src="../static/js/interfaces/output/highlighted_text.js"></script>
|
||||
<script src="../static/js/interfaces/output/audio.js"></script>
|
||||
<script src="../static/js/interfaces/output/json.js"></script>
|
||||
<script src="../static/js/interfaces/output/html.js"></script>
|
||||
<script src="../static/js/interfaces/output/dataframe.js"></script>
|
||||
<script src="../static/js/interfaces/output/file.js"></script>
|
||||
<script src="../static/js/gradio.js"></script>
|
||||
<script>
|
||||
$.getJSON("static/config.json", function(config) {
|
||||
|
@ -24,7 +24,8 @@ def colab_check():
|
||||
is_colab = False
|
||||
try: # Check if running interactively using ipython.
|
||||
from_ipynb = get_ipython()
|
||||
if "google.colab" in str(from_ipynb):
|
||||
if "google.colab" in str(from_ipynb) or "ipykernel" in str(
|
||||
from_ipynb):
|
||||
is_colab = True
|
||||
except NameError:
|
||||
error_analytics("NameError", analytics_url)
|
||||
|
File diff suppressed because one or more lines are too long
8
demo/files.py
Normal file
8
demo/files.py
Normal file
@ -0,0 +1,8 @@
|
||||
import gradio as gr
|
||||
import random
|
||||
|
||||
def upload(file):
|
||||
print(file.name)
|
||||
return "/mnt/c/Users/aliab/projects/gradio/gradio/static/js/interfaces/output/file.js"
|
||||
|
||||
gr.Interface(upload, "file", "file").launch()
|
@ -16,11 +16,11 @@ def flip10(i1, i2, i3, i4, i5):
|
||||
return i1 + i2
|
||||
|
||||
gr.Interface(flip2,
|
||||
["image"],
|
||||
["webcam"],
|
||||
["image"],
|
||||
examples=[
|
||||
["images/cheetah1.jpg"],
|
||||
["images/cheetah2.jpg"],
|
||||
["images/lion.jpg"],
|
||||
]
|
||||
).launch()
|
||||
).launch(share=True)
|
||||
|
12
demo/matrix.py
Normal file
12
demo/matrix.py
Normal file
@ -0,0 +1,12 @@
|
||||
import gradio as gr
|
||||
import numpy as np
|
||||
import random
|
||||
|
||||
def transpose(matrix):
|
||||
print(matrix)
|
||||
return matrix.T
|
||||
|
||||
gr.Interface(transpose,
|
||||
gr.inputs.Dataframe(type="numpy", datatype="number", row_count=5, col_count=3),
|
||||
"numpy"
|
||||
).launch()
|
32
demo/multi1.py
Normal file
32
demo/multi1.py
Normal file
@ -0,0 +1,32 @@
|
||||
import gradio as gr
|
||||
import numpy as np
|
||||
import random
|
||||
|
||||
def answer_question(text, audio):
|
||||
return [
|
||||
("The movie was ", "good"),
|
||||
("unexpectedly, ", "great"),
|
||||
("a fantastic experience ", "neutral"),
|
||||
], {
|
||||
"address": "1 Main St.",
|
||||
"bedrooms": 5,
|
||||
"is_apt": False,
|
||||
"residents": [
|
||||
{"name": "Farhan", "age": 13},
|
||||
{"name": "Aziz", "age": 18},
|
||||
{"name": "Fozan", "age": None},
|
||||
]
|
||||
}, "<div style='background-color: pink; padding: 2px;'>" + str(audio[1].shape) + "</div>", audio
|
||||
|
||||
gr.Interface(answer_question,
|
||||
[
|
||||
gr.inputs.Dropdown(["cat", "dog", "bird"]),
|
||||
"microphone",
|
||||
],
|
||||
[
|
||||
gr.outputs.HighlightedText(color_map={"good": "lightgreen", "bad": "pink"}),
|
||||
gr.outputs.JSON(),
|
||||
gr.outputs.HTML(),
|
||||
gr.outputs.Audio(),
|
||||
],
|
||||
).launch()
|
14
demo/records.py
Normal file
14
demo/records.py
Normal file
@ -0,0 +1,14 @@
|
||||
import gradio as gr
|
||||
import numpy as np
|
||||
import random
|
||||
|
||||
def filter_records(records, gender):
|
||||
return records[records['gender'] == gender]
|
||||
|
||||
gr.Interface(filter_records,
|
||||
[
|
||||
gr.inputs.Dataframe(headers=["name", "age", "gender"], datatype=["str", "number", "str"], row_count=5),
|
||||
gr.inputs.Dropdown(["M", "F", "O"])
|
||||
],
|
||||
"dataframe"
|
||||
).launch()
|
9
demo/reverse_audio.py
Normal file
9
demo/reverse_audio.py
Normal file
@ -0,0 +1,9 @@
|
||||
import gradio as gr
|
||||
import numpy as np
|
||||
import random
|
||||
|
||||
def reverse_audio(audio):
|
||||
sr, data = audio
|
||||
return (sr, np.flipud(data))
|
||||
|
||||
gr.Interface(reverse_audio, "microphone", "audio").launch()
|
@ -4,4 +4,4 @@ import numpy as np
|
||||
def snap(image):
|
||||
return image
|
||||
|
||||
gr.Interface(snap, gr.inputs.Webcam(shape=(50,100)), "image").launch()
|
||||
gr.Interface(snap, gr.inputs.Image(shape=(100,100), image_mode="L", source="webcam"), "image").launch()
|
||||
|
@ -1,2 +0,0 @@
|
||||
recursive-include gradio/templates *
|
||||
recursive-include gradio/static *
|
@ -1,170 +0,0 @@
|
||||
[![CircleCI](https://circleci.com/gh/gradio-app/gradio.svg?style=svg)](https://circleci.com/gh/gradio-app/gradio) [![PyPI version](https://badge.fury.io/py/gradio.svg)](https://badge.fury.io/py/gradio)
|
||||
|
||||
# Welcome to `gradio` :rocket:
|
||||
|
||||
Quickly create customizable UI components around your TensorFlow or PyTorch models, or even arbitrary Python functions. Mix and match components to support any combination of inputs and outputs. Gradio makes it easy for you to "play around" with your model in your browser by dragging-and-dropping in your own images (or pasting your own text, recording your own voice, etc.) and seeing what the model outputs. You can also generate a share link which allows anyone, anywhere to use the interface as the model continues to run on your machine. Our core library is free and open-source! Take a look:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://i.ibb.co/m0skD0j/bert.gif" alt="drawing"/>
|
||||
</p>
|
||||
|
||||
Gradio is useful for:
|
||||
* Creating demos of your machine learning code for clients / collaborators / users
|
||||
* Getting feedback on model performance from users
|
||||
* Debugging your model interactively during development
|
||||
|
||||
To get a sense of `gradio`, take a look at a few of these examples, and find more on our website: www.gradio.app.
|
||||
|
||||
## Installation
|
||||
```
|
||||
pip install gradio
|
||||
```
|
||||
(you may need to replace `pip` with `pip3` if you're running `python3`).
|
||||
|
||||
## Usage
|
||||
|
||||
Gradio is very easy to use with your existing code. Here are a few working examples:
|
||||
|
||||
### 0. Hello World [![alt text](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/18ODkJvyxHutTN0P5APWyGFO_xwNcgHDZ?usp=sharing)
|
||||
|
||||
Let's start with a basic function (no machine learning yet!) that greets an input name. We'll wrap the function with a `Text` to `Text` interface.
|
||||
|
||||
```python
|
||||
import gradio as gr
|
||||
|
||||
def greet(name):
|
||||
return "Hello " + name + "!"
|
||||
|
||||
gr.Interface(fn=greet, inputs="text", outputs="text").launch()
|
||||
```
|
||||
|
||||
The core Interface class is initialized with three parameters:
|
||||
|
||||
- `fn`: the function to wrap
|
||||
- `inputs`: the name of the input interface
|
||||
- `outputs`: the name of the output interface
|
||||
|
||||
Calling the `launch()` function of the `Interface` object produces the interface shown in image below. Click on the gif to go the live interface in our getting started page.
|
||||
|
||||
<a href="https://gradio.app/getting_started#interface_4">
|
||||
<p align="center">
|
||||
<img src="https://i.ibb.co/T4Rqs5y/hello-name.gif" alt="drawing"/>
|
||||
</p>
|
||||
</a>
|
||||
|
||||
### 1. Inception Net [![alt text](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1c6gQiW88wKBwWq96nqEwuQ1Kyt5LejiU?usp=sharing)
|
||||
|
||||
Now, let's do a machine learning example. We're going to wrap an
|
||||
interface around the InceptionV3 image classifier, which we'll load
|
||||
using Tensorflow! Since this is an image classification model, we will use the `Image` input interface.
|
||||
We'll output a dictionary of labels and their corresponding confidence scores with the `Label` output
|
||||
interface. (The original Inception Net architecture [can be found here](https://arxiv.org/abs/1409.4842))
|
||||
|
||||
```python
|
||||
import gradio as gr
|
||||
import tensorflow as tf
|
||||
import numpy as np
|
||||
import requests
|
||||
|
||||
inception_net = tf.keras.applications.InceptionV3() # load the model
|
||||
|
||||
# Download human-readable labels for ImageNet.
|
||||
response = requests.get("https://git.io/JJkYN")
|
||||
labels = response.text.split("\n")
|
||||
|
||||
def classify_image(inp):
|
||||
inp = inp.reshape((-1, 299, 299, 3))
|
||||
inp = tf.keras.applications.inception_v3.preprocess_input(inp)
|
||||
prediction = inception_net.predict(inp).flatten()
|
||||
return {labels[i]: float(prediction[i]) for i in range(1000)}
|
||||
|
||||
image = gr.inputs.Image(shape=(299, 299, 3))
|
||||
label = gr.outputs.Label(num_top_classes=3)
|
||||
|
||||
gr.Interface(fn=classify_image, inputs=image, outputs=label).launch()
|
||||
```
|
||||
This code will produce the interface below. The interface gives you a way to test
|
||||
Inception Net by dragging and dropping images, and also allows you to use naturally modify the input image using image editing tools that
|
||||
appear when you click EDIT. Notice here we provided actual `gradio.inputs` and `gradio.outputs` objects to the Interface
|
||||
function instead of using string shortcuts. This lets us use built-in preprocessing (e.g. image resizing)
|
||||
and postprocessing (e.g. choosing the number of labels to display) provided by these
|
||||
interfaces.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://i.ibb.co/X8KGJqB/inception-net-2.gif" alt="drawing"/>
|
||||
</p>
|
||||
|
||||
You can supply your own model instead of the pretrained model above, as well as use different kinds of models or functions. Here's a list of the interfaces we currently support, along with their preprocessing / postprocessing parameters:
|
||||
|
||||
**Input Interfaces**:
|
||||
- `Sketchpad(shape=(28, 28), invert_colors=True, flatten=False, scale=1/255, shift=0, dtype='float64')`
|
||||
- `Webcam(image_width=224, image_height=224, num_channels=3, label=None)`
|
||||
- `Textbox(lines=1, placeholder=None, label=None, numeric=False)`
|
||||
- `Radio(choices, label=None)`
|
||||
- `Dropdown(choices, label=None)`
|
||||
- `CheckboxGroup(choices, label=None)`
|
||||
- `Slider(minimum=0, maximum=100, default=None, label=None)`
|
||||
- `Image(shape=(224, 224, 3), image_mode='RGB', scale=1/127.5, shift=-1, label=None)`
|
||||
- `Microphone()`
|
||||
|
||||
**Output Interfaces**:
|
||||
- `Label(num_top_classes=None, label=None)`
|
||||
- `KeyValues(label=None)`
|
||||
- `Textbox(lines=1, placeholder=None, label=None)`
|
||||
- `Image(label=None, plot=False)`
|
||||
|
||||
Interfaces can also be combined together, for multiple-input or multiple-output models.
|
||||
|
||||
### 2. Real-Time MNIST [![alt text](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1LXJqwdkZNkt1J_yfLWQ3FLxbG2cAF8p4?usp=sharing)
|
||||
|
||||
Let's wrap a fun `Sketchpad`-to-`Label` UI around MNIST. For this example, we'll take advantage of the `live`
|
||||
feature in the library. Set `live=True` inside `Interface()`> to have it run continuous predictions.
|
||||
We've abstracted the model training from the code below, but you can see the full code on the colab link.
|
||||
|
||||
```python
|
||||
import tensorflow as tf
|
||||
import gradio as gr
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
urlretrieve("https://gr-models.s3-us-west-2.amazonaws.com/mnist-model.h5","mnist-model.h5")
|
||||
model = tf.keras.models.load_model("mnist-model.h5")
|
||||
|
||||
def recognize_digit(inp):
|
||||
prediction = model.predict(inp.reshape(1, 28, 28, 1)).tolist()[0]
|
||||
return {str(i): prediction[i] for i in range(10)}
|
||||
|
||||
sketchpad = gr.inputs.Sketchpad()
|
||||
label = gr.outputs.Label(num_top_classes=3)
|
||||
|
||||
gr.Interface(fn=recognize_digit, inputs=sketchpad,
|
||||
outputs=label, live=True).launch()
|
||||
```
|
||||
|
||||
This code will produce the interface below.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://i.ibb.co/vkgZLcH/gif6.gif" alt="drawing"/>
|
||||
</p>
|
||||
|
||||
## Contributing:
|
||||
If you would like to contribute and your contribution is small, you can directly open a pull request (PR). If you would like to contribute a larger feature, we recommend first creating an issue with a proposed design for discussion. Please see our contributing guidelines for more info.
|
||||
|
||||
## License:
|
||||
Gradio is licensed under the Apache License 2.0
|
||||
|
||||
## See more:
|
||||
|
||||
You can find many more examples (like GPT-2, model comparison, multiple inputs, and numerical interfaces) as well as more info on usage on our website: www.gradio.app
|
||||
|
||||
See, also, the accompanying paper: ["Gradio: Hassle-Free Sharing and Testing of ML Models in the Wild"](https://arxiv.org/pdf/1906.02569.pdf), *ICML HILL 2019*, and please use the citation below.
|
||||
|
||||
```
|
||||
@article{abid2019gradio,
|
||||
title={Gradio: Hassle-Free Sharing and Testing of ML Models in the Wild},
|
||||
author={Abid, Abubakar and Abdalla, Ali and Abid, Ali and Khan, Dawood and Alfozan, Abdulrahman and Zou, James},
|
||||
journal={arXiv preprint arXiv:1906.02569},
|
||||
year={2019}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
from gradio.interface import * # This makes it possible to import `Interface` as `gradio.Interface`.
|
@ -1,68 +0,0 @@
|
||||
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)
|
||||
|
@ -1,422 +0,0 @@
|
||||
"""
|
||||
This module defines various classes that can serve as the `input` to an interface. Each class must inherit from
|
||||
`AbstractInput`, and each class must define a path to its template. All of the subclasses of `AbstractInput` are
|
||||
automatically added to a registry, which allows them to be easily referenced in other parts of the code.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import warnings
|
||||
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'
|
||||
BASE_INPUT_INTERFACE_JS_PATH = 'static/js/interfaces/input/{}.js'
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_validation_inputs(self):
|
||||
"""
|
||||
An interface can optionally implement a method that returns a list of examples inputs that it should be able to
|
||||
accept and preprocess for validation purposes.
|
||||
"""
|
||||
return []
|
||||
|
||||
def get_template_context(self):
|
||||
"""
|
||||
:return: a dictionary with context variables for the javascript file associated with the context
|
||||
"""
|
||||
return {"label": self.label}
|
||||
|
||||
def preprocess(self, inp):
|
||||
"""
|
||||
By default, no pre-processing is applied to text.
|
||||
"""
|
||||
return inp
|
||||
|
||||
def process_example(self, example):
|
||||
"""
|
||||
Proprocess example for UI
|
||||
"""
|
||||
return example
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
"""
|
||||
Return dictionary of shortcut implementations
|
||||
"""
|
||||
return {}
|
||||
|
||||
|
||||
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_template_context(self):
|
||||
return {
|
||||
"lines": self.lines,
|
||||
"placeholder": self.placeholder,
|
||||
"default": self.default,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"text": {},
|
||||
"textbox": {"lines": 7},
|
||||
"number": {"numeric": True}
|
||||
}
|
||||
|
||||
def preprocess(self, inp):
|
||||
"""
|
||||
Cast type of input
|
||||
"""
|
||||
if self.numeric:
|
||||
return float(inp)
|
||||
else:
|
||||
return inp
|
||||
|
||||
|
||||
class Slider(AbstractInput):
|
||||
"""
|
||||
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
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"minimum": self.minimum,
|
||||
"maximum": self.maximum,
|
||||
"default": self.default,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"slider": {},
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"checkbox": {},
|
||||
}
|
||||
|
||||
|
||||
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=None, image_mode='RGB', label=None):
|
||||
'''
|
||||
Parameters:
|
||||
shape (Tuple[int, int]): shape to crop and resize image to; if None, matches input image size.
|
||||
image_mode (str): "RGB" if color, or "L" if black and white.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
if shape is None:
|
||||
self.image_width, self.image_height = None, None
|
||||
else:
|
||||
self.image_width = shape[0]
|
||||
self.image_height = shape[1]
|
||||
self.image_mode = image_mode
|
||||
super().__init__(label)
|
||||
|
||||
def get_validation_inputs(self):
|
||||
return validation_data.BASE64_COLOR_IMAGES
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"image": {},
|
||||
}
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
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)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
im = im.convert(self.image_mode)
|
||||
image_width, image_height = self.image_width, self.image_height
|
||||
if image_width is None:
|
||||
image_width = im.size[0]
|
||||
if image_height is None:
|
||||
image_height = im.size[1]
|
||||
im = preprocessing_utils.resize_and_crop(
|
||||
im, (image_width, image_height))
|
||||
return np.array(im)
|
||||
|
||||
def process_example(self, example):
|
||||
if os.path.exists(example):
|
||||
return preprocessing_utils.convert_file_to_base64(example)
|
||||
else:
|
||||
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)
|
||||
|
||||
@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)
|
||||
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)
|
@ -1,420 +0,0 @@
|
||||
"""
|
||||
This is the core file in the `gradio` package, and defines the Interface class, including methods for constructing the
|
||||
interface using the input and output types.
|
||||
"""
|
||||
|
||||
import tempfile
|
||||
import traceback
|
||||
import webbrowser
|
||||
|
||||
import gradio.inputs
|
||||
import gradio.outputs
|
||||
from gradio import networking, strings
|
||||
from distutils.version import StrictVersion
|
||||
import pkg_resources
|
||||
import requests
|
||||
import random
|
||||
import time
|
||||
import inspect
|
||||
from IPython import get_ipython
|
||||
import sys
|
||||
import weakref
|
||||
import analytics
|
||||
|
||||
|
||||
PKG_VERSION_URL = "https://gradio.app/api/pkg-version"
|
||||
analytics.write_key = "uxIFddIEuuUcFLf9VgH2teTEtPlWdkNy"
|
||||
analytics_url = 'https://api.gradio.app/'
|
||||
try:
|
||||
ip_address = requests.get('https://api.ipify.org').text
|
||||
except requests.ConnectionError:
|
||||
ip_address = "No internet connection"
|
||||
|
||||
|
||||
class Interface:
|
||||
"""
|
||||
Interfaces are created with Gradio using the `gradio.Interface()` function.
|
||||
"""
|
||||
instances = weakref.WeakSet()
|
||||
|
||||
def __init__(self, fn, inputs, outputs, saliency=None, verbose=False, examples=None,
|
||||
live=False, show_input=True, show_output=True,
|
||||
capture_session=False, title=None, description=None,
|
||||
thumbnail=None, server_port=None, server_name=networking.LOCALHOST_NAME,
|
||||
allow_screenshot=True):
|
||||
"""
|
||||
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):
|
||||
return gradio.inputs.shortcuts[iface.lower()]
|
||||
elif isinstance(iface, gradio.inputs.AbstractInput):
|
||||
return iface
|
||||
else:
|
||||
raise ValueError("Input interface must be of type `str` or "
|
||||
"`AbstractInput`")
|
||||
|
||||
def get_output_instance(iface):
|
||||
if isinstance(iface, str):
|
||||
return gradio.outputs.shortcuts[iface.lower()]
|
||||
elif isinstance(iface, gradio.outputs.AbstractOutput):
|
||||
return iface
|
||||
else:
|
||||
raise ValueError(
|
||||
"Output interface must be of type `str` or "
|
||||
"`AbstractOutput`"
|
||||
)
|
||||
if isinstance(inputs, list):
|
||||
self.input_interfaces = [get_input_instance(i) for i in inputs]
|
||||
else:
|
||||
self.input_interfaces = [get_input_instance(inputs)]
|
||||
if isinstance(outputs, list):
|
||||
self.output_interfaces = [get_output_instance(i) for i in outputs]
|
||||
else:
|
||||
self.output_interfaces = [get_output_instance(outputs)]
|
||||
if not isinstance(fn, list):
|
||||
fn = [fn]
|
||||
self.output_interfaces *= len(fn)
|
||||
self.predict = fn
|
||||
self.verbose = verbose
|
||||
self.status = "OFF"
|
||||
self.saliency = saliency
|
||||
self.live = live
|
||||
self.show_input = show_input
|
||||
self.show_output = show_output
|
||||
self.flag_hash = random.getrandbits(32)
|
||||
self.capture_session = capture_session
|
||||
self.session = None
|
||||
self.server_name = server_name
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.thumbnail = thumbnail
|
||||
self.examples = examples
|
||||
self.server_port = server_port
|
||||
self.simple_server = None
|
||||
self.allow_screenshot = allow_screenshot
|
||||
Interface.instances.add(self)
|
||||
|
||||
data = {'fn': fn,
|
||||
'inputs': inputs,
|
||||
'outputs': outputs,
|
||||
'saliency': saliency,
|
||||
'live': live,
|
||||
'capture_session': capture_session,
|
||||
'ip_address': ip_address
|
||||
}
|
||||
|
||||
if self.capture_session:
|
||||
try:
|
||||
import tensorflow as tf
|
||||
self.session = tf.get_default_graph(), \
|
||||
tf.keras.backend.get_session()
|
||||
except (ImportError, AttributeError): # If they are using TF >= 2.0 or don't have TF, just ignore this.
|
||||
pass
|
||||
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-initiated-analytics/',
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
|
||||
def get_config_file(self):
|
||||
config = {
|
||||
"input_interfaces": [
|
||||
(iface.__class__.__name__.lower(), iface.get_template_context())
|
||||
for iface in self.input_interfaces],
|
||||
"output_interfaces": [
|
||||
(iface.__class__.__name__.lower(), iface.get_template_context())
|
||||
for iface in self.output_interfaces],
|
||||
"function_count": len(self.predict),
|
||||
"live": self.live,
|
||||
"show_input": self.show_input,
|
||||
"show_output": self.show_output,
|
||||
"title": self.title,
|
||||
"description": self.description,
|
||||
"thumbnail": self.thumbnail,
|
||||
"allow_screenshot": self.allow_screenshot
|
||||
}
|
||||
try:
|
||||
param_names = inspect.getfullargspec(self.predict[0])[0]
|
||||
for iface, param in zip(config["input_interfaces"], param_names):
|
||||
if not iface[1]["label"]:
|
||||
iface[1]["label"] = param.replace("_", " ")
|
||||
for i, iface in enumerate(config["output_interfaces"]):
|
||||
ret_name = "Output " + str(i + 1) if len(config["output_interfaces"]) > 1 else "Output"
|
||||
if not iface[1]["label"]:
|
||||
iface[1]["label"] = ret_name
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return config
|
||||
|
||||
def process(self, raw_input):
|
||||
processed_input = [input_interface.preprocess(
|
||||
raw_input[i]) for i, input_interface in
|
||||
enumerate(self.input_interfaces)]
|
||||
predictions = []
|
||||
durations = []
|
||||
for predict_fn in self.predict:
|
||||
start = time.time()
|
||||
if self.capture_session and not(self.session is None):
|
||||
graph, sess = self.session
|
||||
with graph.as_default():
|
||||
with sess.as_default():
|
||||
prediction = predict_fn(*processed_input)
|
||||
else:
|
||||
try:
|
||||
prediction = predict_fn(*processed_input)
|
||||
except ValueError as exception:
|
||||
if str(exception).endswith("is not an element of this "
|
||||
"graph."):
|
||||
raise ValueError("It looks like you might be using "
|
||||
"tensorflow < 2.0. Please "
|
||||
"pass capture_session=True in "
|
||||
"Interface to avoid the 'Tensor is "
|
||||
"not an element of this graph.' "
|
||||
"error.")
|
||||
else:
|
||||
raise exception
|
||||
duration = time.time() - start
|
||||
|
||||
if len(self.output_interfaces) == len(self.predict):
|
||||
prediction = [prediction]
|
||||
durations.append(duration)
|
||||
predictions.extend(prediction)
|
||||
processed_output = [output_interface.postprocess(
|
||||
predictions[i]) for i, output_interface in enumerate(self.output_interfaces)]
|
||||
return processed_output, durations
|
||||
|
||||
def validate(self):
|
||||
if self.validate_flag:
|
||||
if self.verbose:
|
||||
print("Interface already validated")
|
||||
return
|
||||
validation_inputs = self.input_interface.get_validation_inputs()
|
||||
n = len(validation_inputs)
|
||||
if n == 0:
|
||||
self.validate_flag = True
|
||||
if self.verbose:
|
||||
print(
|
||||
"No validation samples for this interface... skipping validation."
|
||||
)
|
||||
return
|
||||
for m, msg in enumerate(validation_inputs):
|
||||
if self.verbose:
|
||||
print(
|
||||
"Validating samples: {}/{} [".format(m+1, n)
|
||||
+ "=" * (m + 1)
|
||||
+ "." * (n - m - 1)
|
||||
+ "]",
|
||||
end="\r",
|
||||
)
|
||||
try:
|
||||
processed_input = self.input_interface.preprocess(msg)
|
||||
prediction = self.predict(processed_input)
|
||||
except Exception as e:
|
||||
data = {'error': e}
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-error-analytics/',
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
if self.verbose:
|
||||
print("\n----------")
|
||||
print(
|
||||
"Validation failed, likely due to incompatible pre-processing and model input. See below:\n"
|
||||
)
|
||||
print(traceback.format_exc())
|
||||
break
|
||||
try:
|
||||
_ = self.output_interface.postprocess(prediction)
|
||||
except Exception as e:
|
||||
data = {'error': e}
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-error-analytics/',
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
if self.verbose:
|
||||
print("\n----------")
|
||||
print(
|
||||
"Validation failed, likely due to incompatible model output and post-processing."
|
||||
"See below:\n"
|
||||
)
|
||||
print(traceback.format_exc())
|
||||
break
|
||||
else: # This means if a break was not explicitly called
|
||||
self.validate_flag = True
|
||||
if self.verbose:
|
||||
print("\n\nValidation passed successfully!")
|
||||
return
|
||||
raise RuntimeError("Validation did not pass")
|
||||
|
||||
def close(self):
|
||||
if self.simple_server and not(self.simple_server.fileno() == -1): # checks to see if server is running
|
||||
print("Closing Gradio server on port {}...".format(self.server_port))
|
||||
networking.close_server(self.simple_server)
|
||||
|
||||
def launch(self, inline=None, inbrowser=None, share=False, validate=True, debug=False):
|
||||
"""
|
||||
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()
|
||||
|
||||
output_directory = tempfile.mkdtemp()
|
||||
# Set up a port to serve the directory containing the static files with interface.
|
||||
server_port, httpd = networking.start_simple_server(self, output_directory, self.server_name,
|
||||
server_port=self.server_port)
|
||||
path_to_local_server = "http://{}:{}/".format(self.server_name, server_port)
|
||||
networking.build_template(output_directory)
|
||||
|
||||
self.server_port = server_port
|
||||
self.status = "RUNNING"
|
||||
self.simple_server = httpd
|
||||
|
||||
is_colab = False
|
||||
try: # Check if running interactively using ipython.
|
||||
from_ipynb = get_ipython()
|
||||
if "google.colab" in str(from_ipynb):
|
||||
is_colab = True
|
||||
except NameError:
|
||||
data = {'error': 'NameError in launch method'}
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-error-analytics/',
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
pass
|
||||
|
||||
try:
|
||||
current_pkg_version = pkg_resources.require("gradio")[0].version
|
||||
latest_pkg_version = requests.get(url=PKG_VERSION_URL).json()["version"]
|
||||
if StrictVersion(latest_pkg_version) > StrictVersion(current_pkg_version):
|
||||
print("IMPORTANT: You are using gradio version {}, "
|
||||
"however version {} "
|
||||
"is available, please upgrade.".format(
|
||||
current_pkg_version, latest_pkg_version))
|
||||
print('--------')
|
||||
except: # TODO(abidlabs): don't catch all exceptions
|
||||
pass
|
||||
|
||||
if not is_colab:
|
||||
print(strings.en["RUNNING_LOCALLY"].format(path_to_local_server))
|
||||
else:
|
||||
if debug:
|
||||
print("Colab notebook detected. This cell will run indefinitely so that you can see errors and logs. "
|
||||
"To turn off, set debug=False in launch().")
|
||||
else:
|
||||
print("Colab notebook detected. To show errors in colab notebook, set debug=True in launch()")
|
||||
|
||||
if share:
|
||||
try:
|
||||
share_url = networking.setup_tunnel(server_port)
|
||||
print("Running on External URL:", share_url)
|
||||
except RuntimeError:
|
||||
data = {'error': 'RuntimeError in launch method'}
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-error-analytics/',
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
share_url = None
|
||||
if self.verbose:
|
||||
print(strings.en["NGROK_NO_INTERNET"])
|
||||
else:
|
||||
if (
|
||||
is_colab
|
||||
): # For a colab notebook, create a public link even if share is False.
|
||||
share_url = networking.setup_tunnel(server_port)
|
||||
print("Running on External URL:", share_url)
|
||||
if self.verbose:
|
||||
print(strings.en["COLAB_NO_LOCAL"])
|
||||
else: # If it's not a colab notebook and share=False, print a message telling them about the share option.
|
||||
if self.verbose:
|
||||
print(strings.en["PUBLIC_SHARE_TRUE"])
|
||||
share_url = None
|
||||
|
||||
if inline is None:
|
||||
try: # Check if running interactively using ipython.
|
||||
get_ipython()
|
||||
inline = True
|
||||
if inbrowser is None:
|
||||
inbrowser = False
|
||||
except NameError:
|
||||
inline = False
|
||||
if inbrowser is None:
|
||||
inbrowser = True
|
||||
else:
|
||||
if inbrowser is None:
|
||||
inbrowser = False
|
||||
|
||||
if inbrowser and not is_colab:
|
||||
webbrowser.open(
|
||||
path_to_local_server
|
||||
) # Open a browser tab with the interface.
|
||||
if inline:
|
||||
from IPython.display import IFrame, display
|
||||
|
||||
if (
|
||||
is_colab
|
||||
): # Embed the remote interface page if on google colab;
|
||||
# otherwise, embed the local page.
|
||||
print("Interface loading below...")
|
||||
while not networking.url_ok(share_url):
|
||||
time.sleep(1)
|
||||
display(IFrame(share_url, width=1000, height=500))
|
||||
else:
|
||||
display(IFrame(path_to_local_server, width=1000, height=500))
|
||||
|
||||
config = self.get_config_file()
|
||||
config["share_url"] = share_url
|
||||
|
||||
processed_examples = []
|
||||
if self.examples is not None:
|
||||
for example_set in self.examples:
|
||||
processed_set = []
|
||||
for iface, example in zip(self.input_interfaces, example_set):
|
||||
processed_set.append(iface.process_example(example))
|
||||
processed_examples.append(processed_set)
|
||||
config["examples"] = processed_examples
|
||||
|
||||
networking.set_config(config, output_directory)
|
||||
networking.set_meta_tags(output_directory, self.title, self.description, self.thumbnail)
|
||||
|
||||
if debug:
|
||||
while True:
|
||||
sys.stdout.flush()
|
||||
time.sleep(0.1)
|
||||
|
||||
launch_method = 'browser' if inbrowser else 'inline'
|
||||
data = {'launch_method': launch_method,
|
||||
'is_google_colab': is_colab,
|
||||
'is_sharing_on': share,
|
||||
'share_url': share_url,
|
||||
'ip_address': ip_address
|
||||
}
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-launched-analytics/',
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
return httpd, path_to_local_server, share_url
|
||||
|
||||
@classmethod
|
||||
def get_instances(cls):
|
||||
return list(Interface.instances) # Returns list of all current instances.
|
||||
|
||||
|
||||
def reset_all():
|
||||
for io in Interface.get_instances():
|
||||
io.close()
|
@ -1,287 +0,0 @@
|
||||
"""
|
||||
Defines helper methods useful for setting up ports, launching servers, and handling `ngrok`
|
||||
"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import threading
|
||||
from http.server import HTTPServer as BaseHTTPServer, SimpleHTTPRequestHandler
|
||||
import pkg_resources
|
||||
from distutils import dir_util
|
||||
from gradio import inputs, outputs
|
||||
import json
|
||||
from gradio.tunneling import create_tunnel
|
||||
import urllib.request
|
||||
from shutil import copyfile
|
||||
import requests
|
||||
import sys
|
||||
import analytics
|
||||
|
||||
|
||||
INITIAL_PORT_VALUE = int(os.getenv(
|
||||
'GRADIO_SERVER_PORT', "7860")) # The http server will try to open on port 7860. If not available, 7861, 7862, etc.
|
||||
TRY_NUM_PORTS = int(os.getenv(
|
||||
'GRADIO_NUM_PORTS', "100")) # Number of ports to try before giving up and throwing an exception.
|
||||
LOCALHOST_NAME = os.getenv(
|
||||
'GRADIO_SERVER_NAME', "127.0.0.1")
|
||||
GRADIO_API_SERVER = "https://api.gradio.app/v1/tunnel-request"
|
||||
|
||||
STATIC_TEMPLATE_LIB = pkg_resources.resource_filename("gradio", "templates/")
|
||||
STATIC_PATH_LIB = pkg_resources.resource_filename("gradio", "static/")
|
||||
STATIC_PATH_TEMP = "static/"
|
||||
TEMPLATE_TEMP = "index.html"
|
||||
BASE_JS_FILE = "static/js/all_io.js"
|
||||
CONFIG_FILE = "static/config.json"
|
||||
|
||||
ASSOCIATION_PATH_IN_STATIC = "static/apple-app-site-association"
|
||||
ASSOCIATION_PATH_IN_ROOT = "apple-app-site-association"
|
||||
|
||||
FLAGGING_DIRECTORY = 'static/flagged/'
|
||||
FLAGGING_FILENAME = 'data.txt'
|
||||
analytics.write_key = "uxIFddIEuuUcFLf9VgH2teTEtPlWdkNy"
|
||||
analytics_url = 'https://api.gradio.app/'
|
||||
|
||||
|
||||
def build_template(temp_dir):
|
||||
"""
|
||||
Create HTML file with supporting JS and CSS files in a given directory.
|
||||
:param temp_dir: string with path to temp directory in which the html file should be built
|
||||
"""
|
||||
dir_util.copy_tree(STATIC_TEMPLATE_LIB, temp_dir)
|
||||
dir_util.copy_tree(STATIC_PATH_LIB, os.path.join(
|
||||
temp_dir, STATIC_PATH_TEMP))
|
||||
|
||||
# Move association file to root of temporary directory.
|
||||
copyfile(os.path.join(temp_dir, ASSOCIATION_PATH_IN_STATIC),
|
||||
os.path.join(temp_dir, ASSOCIATION_PATH_IN_ROOT))
|
||||
|
||||
|
||||
def render_template_with_tags(template_path, context):
|
||||
"""
|
||||
Combines the given template with a given context dictionary by replacing all of the occurrences of tags (enclosed
|
||||
in double curly braces) with corresponding values.
|
||||
:param template_path: a string with the path to the template file
|
||||
:param context: a dictionary whose string keys are the tags to replace and whose string values are the replacements.
|
||||
"""
|
||||
with open(template_path) as fin:
|
||||
old_lines = fin.readlines()
|
||||
new_lines = render_string_or_list_with_tags(old_lines, context)
|
||||
with open(template_path, "w") as fout:
|
||||
for line in new_lines:
|
||||
fout.write(line)
|
||||
|
||||
|
||||
def render_string_or_list_with_tags(old_lines, context):
|
||||
# Handle string case
|
||||
if isinstance(old_lines, str):
|
||||
for key, value in context.items():
|
||||
old_lines = old_lines.replace(r"{{" + key + r"}}", str(value))
|
||||
return old_lines
|
||||
|
||||
# Handle list case
|
||||
new_lines = []
|
||||
for line in old_lines:
|
||||
for key, value in context.items():
|
||||
line = line.replace(r"{{" + key + r"}}", str(value))
|
||||
new_lines.append(line)
|
||||
return new_lines
|
||||
|
||||
|
||||
def set_meta_tags(temp_dir, title, description, thumbnail):
|
||||
title = "Gradio" if title is None else title
|
||||
description = "Easy-to-use UI for your machine learning model" if description is None else description
|
||||
thumbnail = "https://gradio.app/static/img/logo_only.png" if thumbnail is None else thumbnail
|
||||
|
||||
index_file = os.path.join(temp_dir, TEMPLATE_TEMP)
|
||||
render_template_with_tags(index_file, {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"thumbnail": thumbnail
|
||||
})
|
||||
|
||||
|
||||
def set_config(config, temp_dir):
|
||||
config_file = os.path.join(temp_dir, CONFIG_FILE)
|
||||
with open(config_file, "w") as output:
|
||||
json.dump(config, output)
|
||||
|
||||
|
||||
def get_first_available_port(initial, final):
|
||||
"""
|
||||
Gets the first open port in a specified range of port numbers
|
||||
:param initial: the initial value in the range of port numbers
|
||||
:param final: final (exclusive) value in the range of port numbers, should be greater than `initial`
|
||||
:return:
|
||||
"""
|
||||
for port in range(initial, final):
|
||||
try:
|
||||
s = socket.socket() # create a socket object
|
||||
s.bind((LOCALHOST_NAME, port)) # Bind to the port
|
||||
s.close()
|
||||
return port
|
||||
except OSError:
|
||||
pass
|
||||
raise OSError(
|
||||
"All ports from {} to {} are in use. Please close a port.".format(
|
||||
initial, final
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def send_prediction_analytics(interface):
|
||||
data = {'title': interface.title,
|
||||
'description': interface.description,
|
||||
'thumbnail': interface.thumbnail,
|
||||
'input_interface': interface.input_interfaces,
|
||||
'output_interface': interface.output_interfaces,
|
||||
}
|
||||
print(data)
|
||||
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):
|
||||
"""This handler uses server.base_path instead of always using os.getcwd()"""
|
||||
def _set_headers(self):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "application/json")
|
||||
self.end_headers()
|
||||
|
||||
def translate_path(self, path):
|
||||
path = SimpleHTTPRequestHandler.translate_path(self, path)
|
||||
relpath = os.path.relpath(path, os.getcwd())
|
||||
fullpath = os.path.join(self.server.base_path, relpath)
|
||||
return fullpath
|
||||
|
||||
def log_message(self, format, *args):
|
||||
return
|
||||
|
||||
def do_POST(self):
|
||||
# Read body of the request.
|
||||
if self.path == "/api/predict/":
|
||||
# Make the prediction.
|
||||
self._set_headers()
|
||||
data_string = self.rfile.read(
|
||||
int(self.headers["Content-Length"]))
|
||||
msg = json.loads(data_string)
|
||||
raw_input = msg["data"]
|
||||
prediction, durations = interface.process(raw_input)
|
||||
|
||||
output = {"data": prediction, "durations": durations}
|
||||
if interface.saliency is not None:
|
||||
saliency = interface.saliency(raw_input, prediction)
|
||||
output['saliency'] = saliency.tolist()
|
||||
# if interface.always_flag:
|
||||
# msg = json.loads(data_string)
|
||||
# flag_dir = os.path.join(FLAGGING_DIRECTORY, str(interface.hash))
|
||||
# os.makedirs(flag_dir, exist_ok=True)
|
||||
# output_flag = {'input': interface.input_interface.rebuild_flagged(flag_dir, msg['data']),
|
||||
# 'output': interface.output_interface.rebuild_flagged(flag_dir, processed_output),
|
||||
# }
|
||||
# with open(os.path.join(flag_dir, FLAGGING_FILENAME), 'a+') as f:
|
||||
# f.write(json.dumps(output_flag))
|
||||
# f.write("\n")
|
||||
|
||||
self.wfile.write(json.dumps(output).encode())
|
||||
|
||||
analytics_thread = threading.Thread(
|
||||
target=send_prediction_analytics, args=[interface])
|
||||
analytics_thread.start()
|
||||
|
||||
elif self.path == "/api/flag/":
|
||||
self._set_headers()
|
||||
data_string = self.rfile.read(
|
||||
int(self.headers["Content-Length"]))
|
||||
msg = json.loads(data_string)
|
||||
flag_dir = os.path.join(FLAGGING_DIRECTORY,
|
||||
str(interface.flag_hash))
|
||||
os.makedirs(flag_dir, exist_ok=True)
|
||||
output = {'inputs': [interface.input_interfaces[
|
||||
i].rebuild_flagged(
|
||||
flag_dir, msg['data']['input_data']) for i
|
||||
in range(len(interface.input_interfaces))],
|
||||
'outputs': [interface.output_interfaces[
|
||||
i].rebuild_flagged(
|
||||
flag_dir, msg['data']['output_data']) for i
|
||||
in range(len(interface.output_interfaces))],
|
||||
'message': msg['data']['message']}
|
||||
|
||||
with open(os.path.join(flag_dir, FLAGGING_FILENAME), 'a+') as f:
|
||||
f.write(json.dumps(output))
|
||||
f.write("\n")
|
||||
|
||||
else:
|
||||
self.send_error(404, 'Path not found: {}'.format(self.path))
|
||||
|
||||
class HTTPServer(BaseHTTPServer):
|
||||
"""The main server, you pass in base_path which is the path you want to serve requests from"""
|
||||
|
||||
def __init__(self, base_path, server_address, RequestHandlerClass=HTTPHandler):
|
||||
self.base_path = base_path
|
||||
BaseHTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
|
||||
httpd = HTTPServer(directory_to_serve, (server_name, port))
|
||||
|
||||
# Now loop forever
|
||||
def serve_forever():
|
||||
try:
|
||||
while True:
|
||||
sys.stdout.flush()
|
||||
httpd.serve_forever()
|
||||
except (KeyboardInterrupt, OSError):
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
thread = threading.Thread(target=serve_forever, daemon=False)
|
||||
thread.start()
|
||||
|
||||
return httpd
|
||||
|
||||
|
||||
def start_simple_server(interface, directory_to_serve=None, server_name=None, server_port=None):
|
||||
if server_port is None:
|
||||
server_port = INITIAL_PORT_VALUE
|
||||
port = get_first_available_port(
|
||||
server_port, server_port + TRY_NUM_PORTS
|
||||
)
|
||||
httpd = serve_files_in_background(interface, port, directory_to_serve, server_name)
|
||||
return port, httpd
|
||||
|
||||
|
||||
def close_server(server):
|
||||
server.server_close()
|
||||
|
||||
|
||||
def url_request(url):
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
url=url, headers={"content-type": "application/json"}
|
||||
)
|
||||
res = urllib.request.urlopen(req, timeout=10)
|
||||
return res
|
||||
except Exception as e:
|
||||
raise RuntimeError(str(e))
|
||||
|
||||
|
||||
def setup_tunnel(local_server_port):
|
||||
response = url_request(GRADIO_API_SERVER)
|
||||
if response and response.code == 200:
|
||||
try:
|
||||
payload = json.loads(response.read().decode("utf-8"))[0]
|
||||
return create_tunnel(payload, LOCALHOST_NAME, local_server_port)
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(str(e))
|
||||
|
||||
|
||||
def url_ok(url):
|
||||
try:
|
||||
r = requests.head(url)
|
||||
return r.status_code == 200
|
||||
except ConnectionError:
|
||||
return False
|
@ -1,25 +0,0 @@
|
||||
try:
|
||||
from setuptools import setup
|
||||
except ImportError:
|
||||
from distutils.core import setup
|
||||
|
||||
setup(
|
||||
name='gradio',
|
||||
version='1.0.2',
|
||||
include_package_data=True,
|
||||
description='Python library for easily interacting with trained machine learning models',
|
||||
author='Abubakar Abid',
|
||||
author_email='a12d@stanford.edu',
|
||||
url='https://github.com/gradio-app/gradio-UI',
|
||||
packages=['gradio'],
|
||||
keywords=['machine learning', 'visualization', 'reproducibility'],
|
||||
install_requires=[
|
||||
'numpy',
|
||||
'requests',
|
||||
'paramiko',
|
||||
'scipy',
|
||||
'IPython',
|
||||
'scikit-image',
|
||||
'analytics-python',
|
||||
],
|
||||
)
|
@ -1,6 +1,6 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: gradio
|
||||
Version: 1.0.7
|
||||
Version: 1.1.4
|
||||
Summary: Python library for easily interacting with trained machine learning models
|
||||
Home-page: https://github.com/gradio-app/gradio-UI
|
||||
Author: Abubakar Abid
|
||||
|
@ -2,12 +2,13 @@ MANIFEST.in
|
||||
README.md
|
||||
setup.py
|
||||
gradio/__init__.py
|
||||
gradio/component.py
|
||||
gradio/generate_docs.py
|
||||
gradio/inputs.py
|
||||
gradio/interface.py
|
||||
gradio/networking.py
|
||||
gradio/outputs.py
|
||||
gradio/preprocessing_utils.py
|
||||
gradio/processing_utils.py
|
||||
gradio/strings.py
|
||||
gradio/tunneling.py
|
||||
gradio/utils.py
|
||||
@ -22,6 +23,7 @@ gradio/static/css/loading.css
|
||||
gradio/static/css/style.css
|
||||
gradio/static/css/interfaces/input/checkbox_group.css
|
||||
gradio/static/css/interfaces/input/dropdown.css
|
||||
gradio/static/css/interfaces/input/file.css
|
||||
gradio/static/css/interfaces/input/image.css
|
||||
gradio/static/css/interfaces/input/microphone.css
|
||||
gradio/static/css/interfaces/input/radio.css
|
||||
@ -29,13 +31,19 @@ gradio/static/css/interfaces/input/sketchpad.css
|
||||
gradio/static/css/interfaces/input/slider.css
|
||||
gradio/static/css/interfaces/input/textbox.css
|
||||
gradio/static/css/interfaces/input/webcam.css
|
||||
gradio/static/css/interfaces/output/audio.css
|
||||
gradio/static/css/interfaces/output/highlighted_text.css
|
||||
gradio/static/css/interfaces/output/html.css
|
||||
gradio/static/css/interfaces/output/image.css
|
||||
gradio/static/css/interfaces/output/json.css
|
||||
gradio/static/css/interfaces/output/key_values.css
|
||||
gradio/static/css/interfaces/output/label.css
|
||||
gradio/static/css/interfaces/output/textbox.css
|
||||
gradio/static/css/vendor/icons.svg
|
||||
gradio/static/css/vendor/jexcel.min.css
|
||||
gradio/static/css/vendor/jquery-ui.css
|
||||
gradio/static/css/vendor/jsonTree.css
|
||||
gradio/static/css/vendor/jsuites.min.css
|
||||
gradio/static/css/vendor/tui-color-picker.css
|
||||
gradio/static/css/vendor/tui-image-editor.css
|
||||
gradio/static/css/vendor/images/ui-bg_flat_0_aaaaaa_40x100.png
|
||||
@ -62,9 +70,12 @@ gradio/static/img/vendor/icon-d.svg
|
||||
gradio/static/js/all_io.js
|
||||
gradio/static/js/gradio.js
|
||||
gradio/static/js/utils.js
|
||||
gradio/static/js/interfaces/input/audio.js
|
||||
gradio/static/js/interfaces/input/checkbox.js
|
||||
gradio/static/js/interfaces/input/checkbox_group.js
|
||||
gradio/static/js/interfaces/input/dataframe.js
|
||||
gradio/static/js/interfaces/input/dropdown.js
|
||||
gradio/static/js/interfaces/input/file.js
|
||||
gradio/static/js/interfaces/input/image.js
|
||||
gradio/static/js/interfaces/input/microphone.js
|
||||
gradio/static/js/interfaces/input/radio.js
|
||||
@ -72,7 +83,13 @@ gradio/static/js/interfaces/input/sketchpad.js
|
||||
gradio/static/js/interfaces/input/slider.js
|
||||
gradio/static/js/interfaces/input/textbox.js
|
||||
gradio/static/js/interfaces/input/webcam.js
|
||||
gradio/static/js/interfaces/output/audio.js
|
||||
gradio/static/js/interfaces/output/dataframe.js
|
||||
gradio/static/js/interfaces/output/file.js
|
||||
gradio/static/js/interfaces/output/highlighted_text.js
|
||||
gradio/static/js/interfaces/output/html.js
|
||||
gradio/static/js/interfaces/output/image.js
|
||||
gradio/static/js/interfaces/output/json.js
|
||||
gradio/static/js/interfaces/output/key_values.js
|
||||
gradio/static/js/interfaces/output/label.js
|
||||
gradio/static/js/interfaces/output/textbox.js
|
||||
@ -80,10 +97,12 @@ gradio/static/js/vendor/FileSaver.min.js
|
||||
gradio/static/js/vendor/black-theme.js
|
||||
gradio/static/js/vendor/fabric.js
|
||||
gradio/static/js/vendor/html2canvas.min.js
|
||||
gradio/static/js/vendor/jexcel.min.js
|
||||
gradio/static/js/vendor/jquery-ui.min.js
|
||||
gradio/static/js/vendor/jquery.min.js
|
||||
gradio/static/js/vendor/jquery.ui.touch-punch.js
|
||||
gradio/static/js/vendor/jsonTree.js
|
||||
gradio/static/js/vendor/jsuites.min.js
|
||||
gradio/static/js/vendor/p5.dom.min.js
|
||||
gradio/static/js/vendor/p5.min.js
|
||||
gradio/static/js/vendor/p5.sound.min.js
|
||||
|
@ -5,3 +5,4 @@ scipy
|
||||
IPython
|
||||
scikit-image
|
||||
analytics-python
|
||||
pandas
|
||||
|
52
gradio/component.py
Normal file
52
gradio/component.py
Normal file
@ -0,0 +1,52 @@
|
||||
class Component():
|
||||
"""
|
||||
A class for defining the methods that all gradio input and output components should have.
|
||||
"""
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
def get_template_context(self):
|
||||
"""
|
||||
:return: a dictionary with context variables for the javascript file associated with the context
|
||||
"""
|
||||
return {"label": self.label}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
Any preprocessing needed to be performed on function input.
|
||||
"""
|
||||
return x
|
||||
|
||||
def postprocess(self, y):
|
||||
"""
|
||||
Any postprocessing needed to be performed on function output.
|
||||
"""
|
||||
return y
|
||||
|
||||
def process_example(self, example):
|
||||
"""
|
||||
Proprocess example for UI
|
||||
"""
|
||||
return example
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
"""
|
||||
Return dictionary of shortcut implementations
|
||||
"""
|
||||
return {}
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
All interfaces should define a method that rebuilds the flagged input when it's passed back (i.e. rebuilds image from base64)
|
||||
"""
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def get_all_shortcut_implementations(cls):
|
||||
shortcuts = {}
|
||||
for sub_cls in cls.__subclasses__():
|
||||
for shortcut, parameters in sub_cls.get_shortcut_implementations().items():
|
||||
shortcuts[shortcut] = (sub_cls, parameters)
|
||||
return shortcuts
|
@ -1,6 +1,6 @@
|
||||
import json
|
||||
from gradio.inputs import AbstractInput
|
||||
from gradio.outputs import AbstractOutput
|
||||
from gradio.inputs import InputComponent
|
||||
from gradio.outputs import OutputComponent
|
||||
from gradio.interface import Interface
|
||||
import inspect
|
||||
|
||||
@ -44,8 +44,8 @@ def document(cls_set):
|
||||
docset.append(inp)
|
||||
return docset
|
||||
|
||||
inputs = document(AbstractInput.__subclasses__())
|
||||
outputs = document(AbstractOutput.__subclasses__())
|
||||
inputs = document(InputComponent.__subclasses__())
|
||||
outputs = document(OutputComponent.__subclasses__())
|
||||
interface_params = get_params(Interface.__init__)
|
||||
interface = {
|
||||
"doc": inspect.getdoc(Interface),
|
||||
|
391
gradio/inputs.py
391
gradio/inputs.py
@ -1,6 +1,6 @@
|
||||
"""
|
||||
This module defines various classes that can serve as the `input` to an interface. Each class must inherit from
|
||||
`AbstractInput`, and each class must define a path to its template. All of the subclasses of `AbstractInput` are
|
||||
`InputComponent`, and each class must define a path to its template. All of the subclasses of `InputComponent` are
|
||||
automatically added to a registry, which allows them to be easily referenced in other parts of the code.
|
||||
"""
|
||||
|
||||
@ -9,79 +9,48 @@ import json
|
||||
import os
|
||||
import time
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from gradio.component import Component
|
||||
|
||||
import base64
|
||||
import numpy as np
|
||||
import PIL.Image
|
||||
import PIL.ImageOps
|
||||
import scipy.io.wavfile
|
||||
from gradio import preprocessing_utils
|
||||
from gradio import processing_utils
|
||||
import pandas as pd
|
||||
import math
|
||||
import tempfile
|
||||
|
||||
# Where to find the static resources associated with each template.
|
||||
# BASE_INPUT_INTERFACE_TEMPLATE_PATH = 'static/js/interfaces/input/{}.js'
|
||||
BASE_INPUT_INTERFACE_JS_PATH = 'static/js/interfaces/input/{}.js'
|
||||
|
||||
|
||||
class AbstractInput(ABC):
|
||||
class InputComponent(Component):
|
||||
"""
|
||||
An abstract class for defining the methods that all gradio inputs should have.
|
||||
When this is subclassed, it is automatically added to the registry
|
||||
Input Component. All input components subclass this.
|
||||
"""
|
||||
pass
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
def get_template_context(self):
|
||||
"""
|
||||
:return: a dictionary with context variables for the javascript file associated with the context
|
||||
"""
|
||||
return {"label": self.label}
|
||||
|
||||
def preprocess(self, inp):
|
||||
"""
|
||||
By default, no pre-processing is applied to text.
|
||||
"""
|
||||
return inp
|
||||
|
||||
def process_example(self, example):
|
||||
"""
|
||||
Proprocess example for UI
|
||||
"""
|
||||
return example
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
"""
|
||||
Return dictionary of shortcut implementations
|
||||
"""
|
||||
return {}
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
All interfaces should define a method that rebuilds the flagged input when it's passed back (i.e. rebuilds image from base64)
|
||||
"""
|
||||
return data
|
||||
|
||||
class Textbox(AbstractInput):
|
||||
class Textbox(InputComponent):
|
||||
"""
|
||||
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.
|
||||
Component creates a textbox for user to enter input. Provides a string (or number is `type` is "float") as an argument to the wrapped function.
|
||||
Input type: str
|
||||
"""
|
||||
|
||||
def __init__(self, lines=1, placeholder=None, default=None, numeric=False, label=None):
|
||||
def __init__(self, lines=1, placeholder=None, default=None, numeric=False, type="str", 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.
|
||||
numeric (bool): DEPRECATED. Whether the input should be parsed as a number instead of a string.
|
||||
type (str): Type of value to be returned by component. "str" returns a string, "number" returns a float value.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.lines = lines
|
||||
self.placeholder = placeholder
|
||||
self.default = default
|
||||
self.numeric = numeric
|
||||
if numeric:
|
||||
warnings.warn("The 'numeric' parameter has been deprecated. Set parameter 'type' to 'number' instead.", DeprecationWarning)
|
||||
self.type = "number"
|
||||
else:
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -97,20 +66,20 @@ class Textbox(AbstractInput):
|
||||
return {
|
||||
"text": {},
|
||||
"textbox": {"lines": 7},
|
||||
"number": {"numeric": True}
|
||||
"number": {"type": "number"}
|
||||
}
|
||||
|
||||
def preprocess(self, inp):
|
||||
"""
|
||||
Cast type of input
|
||||
"""
|
||||
if self.numeric:
|
||||
return float(inp)
|
||||
def preprocess(self, x):
|
||||
if self.type == "str":
|
||||
return x
|
||||
elif self.type == "number":
|
||||
return float(x)
|
||||
else:
|
||||
return inp
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'str', 'number'.")
|
||||
|
||||
|
||||
class Slider(AbstractInput):
|
||||
|
||||
class Slider(InputComponent):
|
||||
"""
|
||||
Component creates a slider that ranges from `minimum` to `maximum`. Provides a number as an argument to the wrapped function.
|
||||
Input type: float
|
||||
@ -127,6 +96,11 @@ class Slider(AbstractInput):
|
||||
'''
|
||||
self.minimum = minimum
|
||||
self.maximum = maximum
|
||||
if step is None:
|
||||
difference = maximum - minimum
|
||||
power = math.floor(math.log10(difference) - 1)
|
||||
step = 10 ** power
|
||||
self.step = step
|
||||
self.default = minimum if default is None else default
|
||||
super().__init__(label)
|
||||
|
||||
@ -134,6 +108,7 @@ class Slider(AbstractInput):
|
||||
return {
|
||||
"minimum": self.minimum,
|
||||
"maximum": self.maximum,
|
||||
"step": self.step,
|
||||
"default": self.default,
|
||||
**super().get_template_context()
|
||||
}
|
||||
@ -145,7 +120,7 @@ class Slider(AbstractInput):
|
||||
}
|
||||
|
||||
|
||||
class Checkbox(AbstractInput):
|
||||
class Checkbox(InputComponent):
|
||||
"""
|
||||
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
|
||||
@ -165,19 +140,21 @@ class Checkbox(AbstractInput):
|
||||
}
|
||||
|
||||
|
||||
class CheckboxGroup(AbstractInput):
|
||||
class CheckboxGroup(InputComponent):
|
||||
"""
|
||||
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]
|
||||
Input type: Union[List[str], List[int]]
|
||||
"""
|
||||
|
||||
def __init__(self, choices, label=None):
|
||||
def __init__(self, choices, type="value", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
choices (List[str]): list of options to select from.
|
||||
type (str): Type of value to be returned by component. "value" returns the list of strings of the choices selected, "index" returns the list of indicies of the choices selected.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.choices = choices
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -186,20 +163,30 @@ class CheckboxGroup(AbstractInput):
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
return [self.choices.index(choice) for choice in x]
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'value', 'index'.")
|
||||
|
||||
class Radio(AbstractInput):
|
||||
|
||||
class Radio(InputComponent):
|
||||
"""
|
||||
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
|
||||
Input type: Union[str, int]
|
||||
"""
|
||||
|
||||
def __init__(self, choices, label=None):
|
||||
def __init__(self, choices, type="value", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
choices (List[str]): list of options to select from.
|
||||
type (str): Type of value to be returned by component. "value" returns the string of the choice selected, "index" returns the index of the choice selected.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.choices = choices
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -208,20 +195,29 @@ class Radio(AbstractInput):
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
return self.choices.index(x)
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'value', 'index'.")
|
||||
|
||||
class Dropdown(AbstractInput):
|
||||
class Dropdown(InputComponent):
|
||||
"""
|
||||
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):
|
||||
def __init__(self, choices, type="value", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
choices (List[str]): list of options to select from.
|
||||
type (str): Type of value to be returned by component. "value" returns the string of the choice selected, "index" returns the index of the choice selected.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.choices = choices
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -230,59 +226,71 @@ class Dropdown(AbstractInput):
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "value":
|
||||
return x
|
||||
elif self.type == "index":
|
||||
return self.choices.index(x)
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'value', 'index'.")
|
||||
|
||||
class Image(AbstractInput):
|
||||
|
||||
class Image(InputComponent):
|
||||
"""
|
||||
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
|
||||
Component creates an image upload box with editing capabilities.
|
||||
Input type: Union[numpy.array, PIL.Image, str]
|
||||
"""
|
||||
|
||||
def __init__(self, shape=None, image_mode='RGB', label=None):
|
||||
def __init__(self, shape=None, image_mode='RGB', source="upload", type="numpy", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
shape (Tuple[int, int]): shape to crop and resize image to; if None, matches input image size.
|
||||
image_mode (str): "RGB" if color, or "L" if black and white.
|
||||
source (str): Source of image. "upload" creates a box where user can drop an image file, "webcam" allows user to take snapshot from their webcam, "canvas" defaults to a white image that can be edited and drawn upon with tools.
|
||||
type (str): Type of value to be returned by component. "numpy" returns a numpy array with shape (width, height, 3), "pil" returns a PIL image object, "file" returns a temporary file object whose path can be retrieved by file_obj.name.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
if shape is None:
|
||||
self.image_width, self.image_height = None, None
|
||||
else:
|
||||
self.image_width = shape[0]
|
||||
self.image_height = shape[1]
|
||||
self.shape = shape
|
||||
self.image_mode = image_mode
|
||||
self.source = source
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"image": {},
|
||||
"webcam": {"source": "webcam"},
|
||||
"sketchpad": {"image_mode": "L", "source": "canvas"},
|
||||
}
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"image_mode": self.image_mode,
|
||||
"source": self.source,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
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)
|
||||
def preprocess(self, x):
|
||||
im = processing_utils.decode_base64_to_image(x)
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
im = im.convert(self.image_mode)
|
||||
image_width, image_height = self.image_width, self.image_height
|
||||
if image_width is None:
|
||||
image_width = im.size[0]
|
||||
if image_height is None:
|
||||
image_height = im.size[1]
|
||||
im = preprocessing_utils.resize_and_crop(
|
||||
im, (image_width, image_height))
|
||||
return np.array(im)
|
||||
if self.shape is not None:
|
||||
im = processing_utils.resize_and_crop(
|
||||
im, (self.shape[0], self.shape[1]))
|
||||
if self.type == "pil":
|
||||
return im
|
||||
elif self.type == "numpy":
|
||||
return np.array(im)
|
||||
elif self.type == "file":
|
||||
file_obj = tempfile.NamedTemporaryFile()
|
||||
im.save(file_obj.name)
|
||||
return file_obj
|
||||
|
||||
def process_example(self, example):
|
||||
if os.path.exists(example):
|
||||
return preprocessing_utils.convert_file_to_base64(example)
|
||||
return processing_utils.encode_file_to_base64(example)
|
||||
else:
|
||||
return example
|
||||
|
||||
@ -290,16 +298,150 @@ class Image(AbstractInput):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.png'
|
||||
im.save(f'{dir}/{filename}', 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class Sketchpad(AbstractInput):
|
||||
class Audio(InputComponent):
|
||||
"""
|
||||
Component creates a sketchpad for black and white illustration. Provides numpy array of shape `(width, height)` as an argument to the wrapped function.
|
||||
Component accepts audio input files.
|
||||
Input type: Union[Tuple[int, numpy.array], str, numpy.array]
|
||||
"""
|
||||
|
||||
def __init__(self, source="upload", type="numpy", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
source (str): Source of audio. "upload" creates a box where user can drop an audio file, "microphone" creates a microphone input.
|
||||
type (str): Type of value to be returned by component. "numpy" returns a 2-set tuple with an integer sample_rate and the data numpy.array of shape (samples, 2), "file" returns a temporary file object whose path can be retrieved by file_obj.name, "mfcc" returns the mfcc coefficients of the input audio.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.source = source
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"source": self.source,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"audio": {},
|
||||
"microphone": {"source": "microphone"}
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
By default, no pre-processing is applied to a microphone input file
|
||||
"""
|
||||
file_obj = processing_utils.decode_base64_to_file(x)
|
||||
if self.type == "file":
|
||||
return file_obj
|
||||
elif self.type == "numpy":
|
||||
return scipy.io.wavfile.read(file_obj.name)
|
||||
elif self.type == "mfcc":
|
||||
return processing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
|
||||
|
||||
|
||||
class File(InputComponent):
|
||||
"""
|
||||
Component accepts generic file uploads.
|
||||
Input type: Union[str, bytes]
|
||||
"""
|
||||
|
||||
def __init__(self, type="file", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
type (str): Type of value to be returned by component. "file" returns a temporary file object whose path can be retrieved by file_obj.name, "binary" returns an bytes object.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"file": {},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "file":
|
||||
return processing_utils.decode_base64_to_file(x)
|
||||
elif self.type == "bytes":
|
||||
return processing_utils.decode_base64_to_binary(x)
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'file', 'bytes'.")
|
||||
|
||||
|
||||
class Dataframe(InputComponent):
|
||||
"""
|
||||
Component accepts 2D input through a spreadsheet interface.
|
||||
Input type: Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]]
|
||||
"""
|
||||
|
||||
def __init__(self, headers=None, row_count=3, col_count=3, datatype="str", type="pandas", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
headers (List[str]): Header names to dataframe.
|
||||
row_count (int): Limit number of rows for input.
|
||||
col_count (int): Limit number of columns for input. If equal to 1, return data will be one-dimensional. Ignored if `headers` is provided.
|
||||
datatype (Union[str, List[str]]): Datatype of values in sheet. Can be provided per column as a list of strings, or for the entire sheet as a single string. Valid datatypes are "str", "number", "bool", and "date".
|
||||
type (str): Type of value to be returned by component. "pandas" for pandas dataframe, "numpy" for numpy array, or "array" for a Python array.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.headers = headers
|
||||
self.datatype = datatype
|
||||
self.row_count = row_count
|
||||
self.col_count = len(headers) if headers else col_count
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"headers": self.headers,
|
||||
"datatype": self.datatype,
|
||||
"row_count": self.row_count,
|
||||
"col_count": self.col_count,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"dataframe": {"type": "pandas"},
|
||||
"numpy": {"type": "numpy"},
|
||||
"matrix": {"type": "array"},
|
||||
"list": {"type": "array", "col_count": 1},
|
||||
}
|
||||
|
||||
def preprocess(self, x):
|
||||
if self.type == "pandas":
|
||||
if self.headers:
|
||||
return pd.DataFrame(x, columns=self.headers)
|
||||
else:
|
||||
return pd.DataFrame(x)
|
||||
if self.col_count == 1:
|
||||
x = x[0]
|
||||
if self.type == "numpy":
|
||||
return np.array(x)
|
||||
elif self.type == "array":
|
||||
return x
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'pandas', 'numpy', 'array'.")
|
||||
|
||||
#######################
|
||||
# DEPRECATED COMPONENTS
|
||||
#######################
|
||||
|
||||
class Sketchpad(InputComponent):
|
||||
"""
|
||||
DEPRECATED. 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
|
||||
"""
|
||||
|
||||
@ -312,23 +454,18 @@ class Sketchpad(AbstractInput):
|
||||
flatten (bool): whether to reshape the numpy array to a single dimension.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
warnings.warn("Sketchpad has been deprecated. Please use 'Image' component to generate a sketchpad. The string shorcut 'sketchpad' has been moved to the Image component.", DeprecationWarning)
|
||||
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):
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
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_transparent = processing_utils.decode_base64_to_image(x)
|
||||
# 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)
|
||||
@ -345,22 +482,22 @@ class Sketchpad(AbstractInput):
|
||||
return array
|
||||
|
||||
def process_example(self, example):
|
||||
return preprocessing_utils.convert_file_to_base64(example)
|
||||
return processing_utils.encode_file_to_base64(example)
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.png'
|
||||
im.save(f'{dir}/{filename}', 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class Webcam(AbstractInput):
|
||||
class Webcam(InputComponent):
|
||||
"""
|
||||
Component creates a webcam for captured image input. Provides numpy array of shape `(width, height, 3)` as an argument to the wrapped function.
|
||||
DEPRECATED. 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
|
||||
"""
|
||||
|
||||
@ -370,24 +507,19 @@ class Webcam(AbstractInput):
|
||||
shape (Tuple[int, int]): shape to crop and resize image to.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
warnings.warn("Webcam has been deprecated. Please use 'Image' component to generate a webcam. The string shorcut 'webcam' has been moved to the Image component.", DeprecationWarning)
|
||||
self.image_width = shape[0]
|
||||
self.image_height = shape[1]
|
||||
self.num_channels = 3
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"webcam": {},
|
||||
}
|
||||
|
||||
def preprocess(self, inp):
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
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 = processing_utils.decode_base64_to_image(x)
|
||||
im = im.convert('RGB')
|
||||
im = preprocessing_utils.resize_and_crop(
|
||||
im = processing_utils.resize_and_crop(
|
||||
im, (self.image_width, self.image_height))
|
||||
return np.array(im)
|
||||
|
||||
@ -395,16 +527,16 @@ class Webcam(AbstractInput):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.png'
|
||||
im.save('{}/{}'.format(dir, filename), 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class Microphone(AbstractInput):
|
||||
class Microphone(InputComponent):
|
||||
"""
|
||||
Component creates a microphone element for audio inputs. Provides numpy array of shape `(samples, 2)` as an argument to the wrapped function.
|
||||
DEPRECATED. Component creates a microphone element for audio inputs.
|
||||
Input type: numpy.array
|
||||
"""
|
||||
|
||||
@ -414,6 +546,7 @@ class Microphone(AbstractInput):
|
||||
preprocessing (Union[str, Callable]): preprocessing to apply to input
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
warnings.warn("Microphone has been deprecated. Please use 'Audio' component to generate a microphone. The string shorcut 'microphone' has been moved to the Audio component.", DeprecationWarning)
|
||||
super().__init__(label)
|
||||
if preprocessing is None or preprocessing == "mfcc":
|
||||
self.preprocessing = preprocessing
|
||||
@ -421,22 +554,17 @@ class Microphone(AbstractInput):
|
||||
raise ValueError(
|
||||
"unexpected value for preprocessing", preprocessing)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"microphone": {},
|
||||
}
|
||||
|
||||
def preprocess(self, inp):
|
||||
def preprocess(self, x):
|
||||
"""
|
||||
By default, no pre-processing is applied to a microphone input file
|
||||
"""
|
||||
file_obj = preprocessing_utils.decode_base64_to_wav_file(inp)
|
||||
file_obj = processing_utils.decode_base64_to_file(x)
|
||||
if self.preprocessing == "mfcc":
|
||||
return preprocessing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
|
||||
return processing_utils.generate_mfcc_features_from_audio_file(file_obj.name)
|
||||
_, signal = scipy.io.wavfile.read(file_obj.name)
|
||||
return signal
|
||||
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
inp = data.split(';')[1].split(',')[1]
|
||||
wav_obj = base64.b64decode(inp)
|
||||
@ -444,11 +572,4 @@ class Microphone(AbstractInput):
|
||||
filename = f'input_{timestamp.strftime("%Y-%m-%d-%H-%M-%S")}.wav'
|
||||
with open("{}/{}".format(dir, filename), "wb+") as f:
|
||||
f.write(wav_obj)
|
||||
return filename
|
||||
|
||||
|
||||
# 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)
|
||||
return filename
|
@ -6,8 +6,8 @@ interface using the input and output types.
|
||||
import tempfile
|
||||
import webbrowser
|
||||
|
||||
import gradio.inputs
|
||||
import gradio.outputs
|
||||
from gradio.inputs import InputComponent
|
||||
from gradio.outputs import OutputComponent
|
||||
from gradio import networking, strings, utils
|
||||
from distutils.version import StrictVersion
|
||||
import pkg_resources
|
||||
@ -21,7 +21,6 @@ import weakref
|
||||
import analytics
|
||||
import os
|
||||
|
||||
|
||||
PKG_VERSION_URL = "https://gradio.app/api/pkg-version"
|
||||
analytics.write_key = "uxIFddIEuuUcFLf9VgH2teTEtPlWdkNy"
|
||||
analytics_url = 'https://api.gradio.app/'
|
||||
@ -48,52 +47,51 @@ class Interface:
|
||||
def __init__(self, fn, inputs, outputs, verbose=False, examples=None,
|
||||
live=False, show_input=True, show_output=True,
|
||||
capture_session=False, title=None, description=None,
|
||||
thumbnail=None, server_port=None, server_name=networking.LOCALHOST_NAME,
|
||||
thumbnail=None, server_port=None, server_name=networking.LOCALHOST_NAME,
|
||||
allow_screenshot=True, allow_flagging=True,
|
||||
flagging_dir="flagged", analytics_enabled=True):
|
||||
|
||||
"""
|
||||
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.
|
||||
inputs (Union[str, List[Union[str, InputComponent]]]): 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, OutputComponent]]]): 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.
|
||||
verbose (bool): whether to print detailed information during launch.
|
||||
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.
|
||||
live (bool): whether the interface should automatically reload on change.
|
||||
show_input (bool): if False, removes the input from the interface
|
||||
and underlays it in the output.
|
||||
show_output (bool): if False, removes the output from the interface
|
||||
and overlays it in the input.
|
||||
show_input (bool): if False, removes the input from the interface and underlays it in the output.
|
||||
show_output (bool): if False, removes the output from the interface and overlays it in the input.
|
||||
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.
|
||||
thumbnail (str): path to image or src to use as display picture for
|
||||
models listed in gradio.app/hub
|
||||
allow_screenshot (bool): if False, users will not see a button to
|
||||
take a screenshot of the interface.
|
||||
allow_flagging (bool): if False, users will not see a button to flag an
|
||||
input and output.
|
||||
thumbnail (str): path to image or src to use as display picture for models listed in gradio.app/hub
|
||||
allow_screenshot (bool): if False, users will not see a button to take a screenshot of the interface.
|
||||
allow_flagging (bool): if False, users will not see a button to flag an input and output.
|
||||
flagging_dir (str): what to name the dir where flagged data is stored.
|
||||
"""
|
||||
|
||||
def get_input_instance(iface):
|
||||
if isinstance(iface, str):
|
||||
return gradio.inputs.shortcuts[iface.lower()]
|
||||
elif isinstance(iface, gradio.inputs.AbstractInput):
|
||||
shortcut = InputComponent.get_all_shortcut_implementations()[iface]
|
||||
return shortcut[0](**shortcut[1])
|
||||
elif isinstance(iface, InputComponent):
|
||||
return iface
|
||||
else:
|
||||
raise ValueError("Input interface must be of type `str` or "
|
||||
"`AbstractInput`")
|
||||
"`InputComponent`")
|
||||
|
||||
def get_output_instance(iface):
|
||||
if isinstance(iface, str):
|
||||
return gradio.outputs.shortcuts[iface.lower()]
|
||||
elif isinstance(iface, gradio.outputs.AbstractOutput):
|
||||
shortcut = OutputComponent.get_all_shortcut_implementations()[iface]
|
||||
return shortcut[0](**shortcut[1])
|
||||
elif isinstance(iface, OutputComponent):
|
||||
return iface
|
||||
else:
|
||||
raise ValueError(
|
||||
"Output interface must be of type `str` or "
|
||||
"`AbstractOutput`"
|
||||
"`OutputComponent`"
|
||||
)
|
||||
|
||||
if isinstance(inputs, list):
|
||||
self.input_interfaces = [get_input_instance(i) for i in inputs]
|
||||
else:
|
||||
@ -140,7 +138,7 @@ class Interface:
|
||||
try:
|
||||
import tensorflow as tf
|
||||
self.session = tf.get_default_graph(), \
|
||||
tf.keras.backend.get_session()
|
||||
tf.keras.backend.get_session()
|
||||
except (ImportError, AttributeError):
|
||||
# If they are using TF >= 2.0 or don't have TF,
|
||||
# just ignore this.
|
||||
@ -156,7 +154,7 @@ class Interface:
|
||||
"_{}".format(index)):
|
||||
index += 1
|
||||
self.flagging_dir = self.flagging_dir + "/" + dir_name + \
|
||||
"_{}".format(index)
|
||||
"_{}".format(index)
|
||||
|
||||
if self.analytics_enabled:
|
||||
try:
|
||||
@ -194,8 +192,8 @@ class Interface:
|
||||
iface[1]["label"] = ret_name
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
return config
|
||||
|
||||
return config
|
||||
|
||||
def process(self, raw_input):
|
||||
"""
|
||||
@ -214,7 +212,7 @@ class Interface:
|
||||
durations = []
|
||||
for predict_fn in self.predict:
|
||||
start = time.time()
|
||||
if self.capture_session and not(self.session is None):
|
||||
if self.capture_session and not (self.session is None):
|
||||
graph, sess = self.session
|
||||
with graph.as_default():
|
||||
with sess.as_default():
|
||||
@ -244,10 +242,19 @@ class Interface:
|
||||
return processed_output, durations
|
||||
|
||||
def close(self):
|
||||
if self.simple_server and not(self.simple_server.fileno() == -1): # checks to see if server is running
|
||||
if self.simple_server and not (self.simple_server.fileno() == -1): # checks to see if server is running
|
||||
print("Closing Gradio server on port {}...".format(self.server_port))
|
||||
networking.close_server(self.simple_server)
|
||||
|
||||
def run_until_interrupted(self, thread, path_to_local_server):
|
||||
try:
|
||||
while 1:
|
||||
pass
|
||||
except (KeyboardInterrupt, OSError):
|
||||
print("Keyboard interruption in main thread... closing server.")
|
||||
thread.keep_running = False
|
||||
networking.url_ok(path_to_local_server)
|
||||
|
||||
def launch(self, inline=None, inbrowser=None, share=False, debug=False):
|
||||
"""
|
||||
Parameters
|
||||
@ -264,11 +271,10 @@ class Interface:
|
||||
path_to_local_server (str): Locally accessible link
|
||||
share_url (str): Publicly accessible link (if share=True)
|
||||
"""
|
||||
|
||||
output_directory = tempfile.mkdtemp()
|
||||
# Set up a port to serve the directory containing the static files with interface.
|
||||
server_port, httpd = networking.start_simple_server(self, output_directory, self.server_name,
|
||||
server_port=self.server_port)
|
||||
server_port, httpd, thread = networking.start_simple_server(
|
||||
self, output_directory, self.server_name, server_port=self.server_port)
|
||||
path_to_local_server = "http://{}:{}/".format(self.server_name, server_port)
|
||||
networking.build_template(output_directory)
|
||||
|
||||
@ -283,7 +289,7 @@ class Interface:
|
||||
print("IMPORTANT: You are using gradio version {}, "
|
||||
"however version {} "
|
||||
"is available, please upgrade.".format(
|
||||
current_pkg_version, latest_pkg_version))
|
||||
current_pkg_version, latest_pkg_version))
|
||||
print('--------')
|
||||
except: # TODO(abidlabs): don't catch all exceptions
|
||||
pass
|
||||
@ -379,12 +385,18 @@ class Interface:
|
||||
'share_url': share_url,
|
||||
'ip_address': ip_address
|
||||
}
|
||||
|
||||
if self.analytics_enabled:
|
||||
try:
|
||||
requests.post(analytics_url + 'gradio-launched-analytics/',
|
||||
data=data)
|
||||
except requests.ConnectionError:
|
||||
pass # do not push analytics if no network
|
||||
|
||||
is_in_interactive_mode = bool(getattr(sys, 'ps1', sys.flags.interactive))
|
||||
if not is_in_interactive_mode:
|
||||
self.run_until_interrupted(thread, path_to_local_server)
|
||||
|
||||
return httpd, path_to_local_server, share_url
|
||||
|
||||
|
||||
|
@ -9,6 +9,7 @@ from http.server import HTTPServer as BaseHTTPServer, SimpleHTTPRequestHandler
|
||||
import pkg_resources
|
||||
from distutils import dir_util
|
||||
from gradio import inputs, outputs
|
||||
import time
|
||||
import json
|
||||
from gradio.tunneling import create_tunnel
|
||||
import urllib.request
|
||||
@ -198,22 +199,21 @@ def serve_files_in_background(interface, port, directory_to_serve=None, server_n
|
||||
self.base_path = base_path
|
||||
BaseHTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
|
||||
class QuittableHTTPThread(threading.Thread):
|
||||
def __init__(self, httpd):
|
||||
super().__init__(daemon=False)
|
||||
self.httpd = httpd
|
||||
self.keep_running =True
|
||||
|
||||
def run(self):
|
||||
while self.keep_running:
|
||||
self.httpd.handle_request()
|
||||
|
||||
httpd = HTTPServer(directory_to_serve, (server_name, port))
|
||||
|
||||
# Now loop forever
|
||||
def serve_forever():
|
||||
try:
|
||||
while True:
|
||||
sys.stdout.flush()
|
||||
httpd.serve_forever()
|
||||
except (KeyboardInterrupt, OSError):
|
||||
httpd.shutdown()
|
||||
httpd.server_close()
|
||||
|
||||
thread = threading.Thread(target=serve_forever, daemon=False)
|
||||
thread = QuittableHTTPThread(httpd=httpd)
|
||||
thread.start()
|
||||
|
||||
return httpd
|
||||
return httpd, thread
|
||||
|
||||
|
||||
def start_simple_server(interface, directory_to_serve=None, server_name=None, server_port=None):
|
||||
@ -222,8 +222,8 @@ def start_simple_server(interface, directory_to_serve=None, server_name=None, se
|
||||
port = get_first_available_port(
|
||||
server_port, server_port + TRY_NUM_PORTS
|
||||
)
|
||||
httpd = serve_files_in_background(interface, port, directory_to_serve, server_name)
|
||||
return port, httpd
|
||||
httpd, thread = serve_files_in_background(interface, port, directory_to_serve, server_name)
|
||||
return port, httpd, thread
|
||||
|
||||
|
||||
def close_server(server):
|
||||
|
@ -1,67 +1,40 @@
|
||||
"""
|
||||
This module defines various classes that can serve as the `output` to an interface. Each class must inherit from
|
||||
`AbstractOutput`, and each class must define a path to its template. All of the subclasses of `AbstractOutput` are
|
||||
`OutputComponent`, and each class must define a path to its template. All of the subclasses of `OutputComponent` are
|
||||
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.component import Component
|
||||
import numpy as np
|
||||
import json
|
||||
from gradio import preprocessing_utils
|
||||
from gradio import processing_utils
|
||||
import datetime
|
||||
import operator
|
||||
from numbers import Number
|
||||
import warnings
|
||||
import tempfile
|
||||
import scipy
|
||||
import os
|
||||
|
||||
# Where to find the static resources associated with each template.
|
||||
BASE_OUTPUT_INTERFACE_JS_PATH = 'static/js/interfaces/output/{}.js'
|
||||
|
||||
|
||||
class AbstractOutput(ABC):
|
||||
class OutputComponent(Component):
|
||||
"""
|
||||
An abstract class for defining the methods that all gradio inputs should have.
|
||||
When this is subclassed, it is automatically added to the registry
|
||||
Output Component. All output components subclass this.
|
||||
"""
|
||||
pass
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
def get_template_context(self):
|
||||
"""
|
||||
:return: a dictionary with context variables for the javascript file associated with the context
|
||||
"""
|
||||
return {"label": self.label}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
"""
|
||||
Any postprocessing needed to be performed on function output.
|
||||
"""
|
||||
return prediction
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
"""
|
||||
Return dictionary of shortcut implementations
|
||||
"""
|
||||
return {}
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
All interfaces should define a method that rebuilds the flagged input when it's passed back (i.e. rebuilds image from base64)
|
||||
"""
|
||||
return data
|
||||
|
||||
|
||||
class Textbox(AbstractOutput):
|
||||
class Textbox(OutputComponent):
|
||||
'''
|
||||
Component creates a textbox to render output text or number.
|
||||
Output type: str
|
||||
Output type: Union[str, float, int]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
def __init__(self, type="str", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
type (str): Type of value to be passed to component. "str" expects a string, "number" expects a float value.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -72,20 +45,21 @@ class Textbox(AbstractOutput):
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"text": {},
|
||||
"textbox": {},
|
||||
"number": {},
|
||||
"text": {"type": "str"},
|
||||
"textbox": {"type": "str"},
|
||||
"number": {"type": "number"},
|
||||
}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
|
||||
return str(prediction)
|
||||
def postprocess(self, y):
|
||||
if self.type == "str":
|
||||
return y
|
||||
elif self.type == "number":
|
||||
return str(y)
|
||||
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.")
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'str', 'number'")
|
||||
|
||||
|
||||
class Label(AbstractOutput):
|
||||
class Label(OutputComponent):
|
||||
'''
|
||||
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]
|
||||
@ -104,12 +78,12 @@ class Label(AbstractOutput):
|
||||
self.num_top_classes = num_top_classes
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str) or isinstance(prediction, Number):
|
||||
return {"label": str(prediction)}
|
||||
elif isinstance(prediction, dict):
|
||||
def postprocess(self, y):
|
||||
if isinstance(y, str) or isinstance(y, Number):
|
||||
return {"label": str(y)}
|
||||
elif isinstance(y, dict):
|
||||
sorted_pred = sorted(
|
||||
prediction.items(),
|
||||
y.items(),
|
||||
key=operator.itemgetter(1),
|
||||
reverse=True
|
||||
)
|
||||
@ -124,8 +98,8 @@ class Label(AbstractOutput):
|
||||
} for pred in sorted_pred
|
||||
]
|
||||
}
|
||||
elif isinstance(prediction, int) or isinstance(prediction, float):
|
||||
return {self.LABEL_KEY: str(prediction)}
|
||||
elif isinstance(y, int) or isinstance(y, float):
|
||||
return {self.LABEL_KEY: str(y)}
|
||||
else:
|
||||
raise ValueError("The `Label` output interface expects one of: a string label, or an int label, a "
|
||||
"float label, or a dictionary whose keys are labels and values are confidences.")
|
||||
@ -143,60 +117,61 @@ class Label(AbstractOutput):
|
||||
# return json.loads(data)
|
||||
return data
|
||||
|
||||
class Image(AbstractOutput):
|
||||
class Image(OutputComponent):
|
||||
'''
|
||||
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
|
||||
Component displays an output image.
|
||||
Output type: Union[numpy.array, PIL.Image, str, matplotlib.pyplot]
|
||||
'''
|
||||
|
||||
def __init__(self, plot=False, label=None):
|
||||
def __init__(self, type="numpy", plot=False, label=None):
|
||||
'''
|
||||
Parameters:
|
||||
plot (bool): whether to expect a plot to be returned by the function.
|
||||
type (str): Type of value to be passed to component. "numpy" expects a numpy array with shape (width, height, 3), "pil" expects a PIL image object, "file" expects a file path to the saved image, "plot" expects a matplotlib.pyplot object.
|
||||
plot (bool): DEPRECATED. Whether to expect a plot to be returned by the function.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.plot = plot
|
||||
if plot:
|
||||
warnings.warn("The 'plot' parameter has been deprecated. Set parameter 'type' to 'plot' instead.", DeprecationWarning)
|
||||
self.type = "plot"
|
||||
else:
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"image": {},
|
||||
"plot": {"plot": True}
|
||||
"plot": {"type": "plot"},
|
||||
"pil": {"type": "pil"}
|
||||
}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
"""
|
||||
"""
|
||||
if self.plot:
|
||||
try:
|
||||
return preprocessing_utils.encode_plot_to_base64(prediction)
|
||||
except:
|
||||
raise ValueError("The `Image` output interface expects a `matplotlib.pyplot` object"
|
||||
"if plt=True.")
|
||||
def postprocess(self, y):
|
||||
if self.type in ["numpy", "pil"]:
|
||||
if self.type == "pil":
|
||||
y = np.array(y)
|
||||
return processing_utils.encode_array_to_base64(y)
|
||||
elif self.type == "file":
|
||||
return processing_utils.encode_file_to_base64(y)
|
||||
elif self.type == "plot":
|
||||
return processing_utils.encode_plot_to_base64(y)
|
||||
else:
|
||||
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("Unknown type: " + self.type + ". Please choose from: 'numpy', 'pil', 'file', 'plot'.")
|
||||
|
||||
def rebuild(self, dir, data):
|
||||
"""
|
||||
Default rebuild method to decode a base64 image
|
||||
"""
|
||||
im = preprocessing_utils.decode_base64_to_image(data)
|
||||
im = processing_utils.decode_base64_to_image(data)
|
||||
timestamp = datetime.datetime.now()
|
||||
filename = 'output_{}.png'.format(timestamp.
|
||||
strftime("%Y-%m-%d-%H-%M-%S"))
|
||||
im.save('{}/{}'.format(dir, filename), 'PNG')
|
||||
return filename
|
||||
|
||||
|
||||
class KeyValues(AbstractOutput):
|
||||
class KeyValues(OutputComponent):
|
||||
'''
|
||||
Component displays a table representing values for multiple fields.
|
||||
Output type: Dict[str, value]
|
||||
Output type: Union[Dict, List[Tuple[str, Union[str, int, float]]]]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
@ -206,9 +181,11 @@ class KeyValues(AbstractOutput):
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, dict):
|
||||
return prediction
|
||||
def postprocess(self, y):
|
||||
if isinstance(y, dict):
|
||||
return list(y.items())
|
||||
elif isinstance(y, list):
|
||||
return y
|
||||
else:
|
||||
raise ValueError("The `KeyValues` output interface expects an output that is a dictionary whose keys are "
|
||||
"labels and values are corresponding values.")
|
||||
@ -220,19 +197,51 @@ class KeyValues(AbstractOutput):
|
||||
}
|
||||
|
||||
|
||||
class HighlightedText(AbstractOutput):
|
||||
class HighlightedText(OutputComponent):
|
||||
'''
|
||||
Component creates text that contains spans that are highlighted by category or numerical value.
|
||||
Output is represent as a list of Tuple pairs, where the first element represents the span of text represented by the tuple, and the second element represents the category or value of the text.
|
||||
Output type: List[Tuple[str, Union[float, str]]]
|
||||
'''
|
||||
|
||||
def __init__(self, category_colors=None, label=None):
|
||||
def __init__(self, color_map=None, label=None):
|
||||
'''
|
||||
Parameters:
|
||||
category_colors (Dict[str, float]):
|
||||
color_map (Dict[str, str]): Map between category and respective colors
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.color_map = color_map
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"color_map": self.color_map,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"highlight": {},
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
return y
|
||||
|
||||
|
||||
class Audio(OutputComponent):
|
||||
'''
|
||||
Creates an audio player that plays the output audio.
|
||||
Output type: Union[Tuple[int, numpy.array], str]
|
||||
'''
|
||||
|
||||
def __init__(self, type="numpy", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
type (str): Type of value to be passed to component. "numpy" returns a 2-set tuple with an integer sample_rate and the data numpy.array of shape (samples, 2), "file" returns a temporary file path to the saved wav audio file.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
super().__init__(label)
|
||||
|
||||
def get_template_context(self):
|
||||
@ -243,21 +252,24 @@ class HighlightedText(AbstractOutput):
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"highlight": {},
|
||||
"audio": {},
|
||||
}
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str) or isinstance(prediction, int) or isinstance(prediction, float):
|
||||
return str(prediction)
|
||||
def postprocess(self, y):
|
||||
if self.type in ["numpy", "file"]:
|
||||
if self.type == "numpy":
|
||||
file = tempfile.NamedTemporaryFile()
|
||||
scipy.io.wavfile.write(file, y[0], y[1])
|
||||
y = file.name
|
||||
return processing_utils.encode_file_to_base64(y, type="audio", ext="wav")
|
||||
else:
|
||||
raise ValueError("The `HighlightedText` output interface expects an output that is one of: a string, or"
|
||||
"an int/float that can be converted to a string.")
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'numpy', 'file'.")
|
||||
|
||||
|
||||
class JSON(AbstractOutput):
|
||||
class JSON(OutputComponent):
|
||||
'''
|
||||
Used for JSON output. Expects a JSON string or a Python dictionary or list that can be converted to JSON.
|
||||
Output type: Union[str, Dict[str, Any], List[Any]]
|
||||
Used for JSON output. Expects a JSON string or a Python object that is JSON serializable.
|
||||
Output type: Union[str, Any]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
@ -267,14 +279,12 @@ class JSON(AbstractOutput):
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, dict) or isinstance(prediction, list):
|
||||
return json.dumps(prediction)
|
||||
elif isinstance(prediction, str):
|
||||
return prediction
|
||||
def postprocess(self, y):
|
||||
if isinstance(y, str):
|
||||
return json.dumps(y)
|
||||
else:
|
||||
raise ValueError("The `JSON` output interface expects an output that is a dictionary or list "
|
||||
"or a preformatted JSON string.")
|
||||
return y
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
@ -283,9 +293,9 @@ class JSON(AbstractOutput):
|
||||
}
|
||||
|
||||
|
||||
class HTML(AbstractOutput):
|
||||
class HTML(OutputComponent):
|
||||
'''
|
||||
Used for HTML output. Expects a JSON string or a Python dictionary or list that can be converted to JSON.
|
||||
Used for HTML output. Expects an HTML valid string.
|
||||
Output type: str
|
||||
'''
|
||||
|
||||
@ -296,11 +306,6 @@ class HTML(AbstractOutput):
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
def postprocess(self, prediction):
|
||||
if isinstance(prediction, str):
|
||||
return prediction
|
||||
else:
|
||||
raise ValueError("The `HTML` output interface expects an output that is a str.")
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
@ -309,8 +314,75 @@ class HTML(AbstractOutput):
|
||||
}
|
||||
|
||||
|
||||
# Automatically adds all shortcut implementations in AbstractInput into a dictionary.
|
||||
shortcuts = {}
|
||||
for cls in AbstractOutput.__subclasses__():
|
||||
for shortcut, parameters in cls.get_shortcut_implementations().items():
|
||||
shortcuts[shortcut] = cls(**parameters)
|
||||
class File(OutputComponent):
|
||||
'''
|
||||
Used for file output.
|
||||
Output type: Union[file-like, str]
|
||||
'''
|
||||
|
||||
def __init__(self, label=None):
|
||||
'''
|
||||
Parameters:
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
super().__init__(label)
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"file": {},
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
return {
|
||||
"name": os.path.basename(y),
|
||||
"size": os.path.getsize(y),
|
||||
"data": processing_utils.encode_file_to_base64(y, header=False)
|
||||
}
|
||||
|
||||
|
||||
class Dataframe(OutputComponent):
|
||||
"""
|
||||
Component displays 2D output through a spreadsheet interface.
|
||||
Output type: Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]]
|
||||
"""
|
||||
|
||||
def __init__(self, headers=None, type="pandas", label=None):
|
||||
'''
|
||||
Parameters:
|
||||
headers (List[str]): Header names to dataframe.
|
||||
type (str): Type of value to be passed to component. "pandas" for pandas dataframe, "numpy" for numpy array, or "array" for Python array.
|
||||
label (str): component name in interface.
|
||||
'''
|
||||
self.type = type
|
||||
self.headers = headers
|
||||
super().__init__(label)
|
||||
|
||||
|
||||
def get_template_context(self):
|
||||
return {
|
||||
"headers": self.headers,
|
||||
**super().get_template_context()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_shortcut_implementations(cls):
|
||||
return {
|
||||
"dataframe": {"type": "pandas"},
|
||||
"numpy": {"type": "numpy"},
|
||||
"matrix": {"type": "array"},
|
||||
"list": {"type": "array"},
|
||||
}
|
||||
|
||||
def postprocess(self, y):
|
||||
if self.type == "pandas":
|
||||
return {"headers": list(y.columns), "data": y.values.tolist()}
|
||||
elif self.type in ("numpy", "array"):
|
||||
if self.type == "numpy":
|
||||
y = y.tolist()
|
||||
if len(y) == 0 or not isinstance(y[0], list):
|
||||
y = [y]
|
||||
return {"data": y}
|
||||
else:
|
||||
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'pandas', 'numpy', 'array'.")
|
||||
|
@ -17,12 +17,16 @@ def decode_base64_to_image(encoding):
|
||||
return Image.open(BytesIO(base64.b64decode(image_encoded)))
|
||||
|
||||
|
||||
def convert_file_to_base64(img):
|
||||
with open(img, "rb") as image_file:
|
||||
encoded_string = base64.b64encode(image_file.read())
|
||||
def encode_file_to_base64(f, type="image", ext=None, header=True):
|
||||
with open(f, "rb") as file:
|
||||
encoded_string = base64.b64encode(file.read())
|
||||
base64_str = str(encoded_string, 'utf-8')
|
||||
type = img.split(".")[-1]
|
||||
return "data:image/" + type + ";base64," + base64_str
|
||||
if not header:
|
||||
return base64_str
|
||||
if ext is None:
|
||||
ext = f.split(".")[-1]
|
||||
return "data:" + type + "/" + ext + ";base64," + base64_str
|
||||
|
||||
|
||||
def encode_plot_to_base64(plt):
|
||||
with BytesIO() as output_bytes:
|
||||
@ -61,19 +65,24 @@ def resize_and_crop(img, size, crop_type='center'):
|
||||
return ImageOps.fit(img, size, centering=center)
|
||||
|
||||
##################
|
||||
# AUDIO FILES
|
||||
# OUTPUT
|
||||
##################
|
||||
|
||||
def decode_base64_to_wav_file(encoding):
|
||||
def decode_base64_to_binary(encoding):
|
||||
inp = encoding.split(';')[1].split(',')[1]
|
||||
wav_obj = base64.b64decode(inp)
|
||||
return base64.b64decode(inp)
|
||||
|
||||
|
||||
def decode_base64_to_file(encoding):
|
||||
file_obj = tempfile.NamedTemporaryFile()
|
||||
file_obj.close()
|
||||
with open(file_obj.name, 'wb') as f:
|
||||
f.write(wav_obj)
|
||||
file_obj.write(decode_base64_to_binary(encoding))
|
||||
return file_obj
|
||||
|
||||
|
||||
##################
|
||||
# AUDIO FILES
|
||||
##################
|
||||
|
||||
def generate_mfcc_features_from_audio_file(wav_filename,
|
||||
pre_emphasis=0.95,
|
||||
frame_size= 0.025,
|
@ -27,10 +27,19 @@
|
||||
flex-grow: 1;
|
||||
}
|
||||
.interface {
|
||||
height: 360px;
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
.interface_box {
|
||||
height: 360px;
|
||||
}
|
||||
.interface_mini_box {
|
||||
height: 180px;
|
||||
}
|
||||
.interface_max_box {
|
||||
overflow: auto;
|
||||
max-height: 360px;
|
||||
}
|
||||
.interface:not(*:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
15
gradio/static/css/interfaces/input/file.css
Normal file
15
gradio/static/css/interfaces/input/file.css
Normal file
@ -0,0 +1,15 @@
|
||||
.file_display {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
.file_name {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.file_size {
|
||||
font-size: 18px;
|
||||
}
|
@ -37,10 +37,10 @@
|
||||
.canvas_holder canvas {
|
||||
background-color: white;
|
||||
}
|
||||
.canvas_holder, .saliency_holder {
|
||||
.canvas_holder {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: calc(100% - 36px);
|
||||
}
|
||||
.saliency_holder {
|
||||
position: absolute;
|
||||
|
15
gradio/static/css/interfaces/output/audio.css
Normal file
15
gradio/static/css/interfaces/output/audio.css
Normal file
@ -0,0 +1,15 @@
|
||||
.output_text {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
outline: none;
|
||||
background-color: white;
|
||||
border: solid 1px lightgray;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
padding: 4px;
|
||||
min-height: 30px;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap; /* CSS3 */
|
||||
white-space: -moz-pre-wrap; /* Firefox */
|
||||
word-wrap: break-word; /* IE */
|
||||
}
|
22
gradio/static/css/interfaces/output/highlighted_text.css
Normal file
22
gradio/static/css/interfaces/output/highlighted_text.css
Normal file
@ -0,0 +1,22 @@
|
||||
.highlight_legend {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.color_legend {
|
||||
font-family: monospace;
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(90deg, rgba(58,241,255,1) 0%, rgba(58,241,255,0) 49%, rgba(230,126,34,0) 50%, rgba(230,126,34,1) 100%);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.category-label {
|
||||
display: inline-flex;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.category-label div {
|
||||
width: 24px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
15
gradio/static/css/interfaces/output/html.css
Normal file
15
gradio/static/css/interfaces/output/html.css
Normal file
@ -0,0 +1,15 @@
|
||||
.output_text {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
outline: none;
|
||||
background-color: white;
|
||||
border: solid 1px lightgray;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
padding: 4px;
|
||||
min-height: 30px;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap; /* CSS3 */
|
||||
white-space: -moz-pre-wrap; /* Firefox */
|
||||
word-wrap: break-word; /* IE */
|
||||
}
|
0
gradio/static/css/interfaces/output/json.css
Normal file
0
gradio/static/css/interfaces/output/json.css
Normal file
@ -40,6 +40,7 @@
|
||||
.output_class {
|
||||
font-weight: bold;
|
||||
font-size: 36px;
|
||||
padding: 32px 16px;;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
1
gradio/static/css/vendor/jexcel.min.css
vendored
Normal file
1
gradio/static/css/vendor/jexcel.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
gradio/static/css/vendor/jsonTree.css
vendored
12
gradio/static/css/vendor/jsonTree.css
vendored
@ -13,15 +13,23 @@
|
||||
|
||||
/* Styles for the container of the tree (e.g. fonts, margins etc.) */
|
||||
.jsontree_tree {
|
||||
margin-left: 30px;
|
||||
font-family: 'PT Mono', monospace;
|
||||
font-size: 14px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.jsontree_tree ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.jsontree_tree li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
/* Styles for a list of child nodes */
|
||||
.jsontree_child-nodes {
|
||||
display: none;
|
||||
margin-left: 35px;
|
||||
margin-bottom: 5px;
|
||||
line-height: 2;
|
||||
}
|
||||
|
8
gradio/static/css/vendor/jsuites.min.css
vendored
Normal file
8
gradio/static/css/vendor/jsuites.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -42,13 +42,22 @@ function gradio(config, fn, target) {
|
||||
"checkboxgroup" : checkbox_group,
|
||||
"slider" : slider,
|
||||
"dropdown" : dropdown,
|
||||
"audio" : audio_input,
|
||||
"file" : file_input,
|
||||
"dataframe" : dataframe_input,
|
||||
}
|
||||
let output_to_object_map = {
|
||||
"csv" : {},
|
||||
"image" : image_output,
|
||||
"label" : label_output,
|
||||
"keyvalues" : key_values,
|
||||
"textbox" : textbox_output
|
||||
"textbox" : textbox_output,
|
||||
"highlightedtext": highlighted_text,
|
||||
"audio": audio_output,
|
||||
"json": json_output,
|
||||
"html": html_output,
|
||||
"file" : file_output,
|
||||
"dataframe" : dataframe_output,
|
||||
}
|
||||
let id_to_interface_map = {}
|
||||
|
||||
|
153
gradio/static/js/interfaces/input/audio.js
Normal file
153
gradio/static/js/interfaces/input/audio.js
Normal file
@ -0,0 +1,153 @@
|
||||
const audio_input = {
|
||||
html: `
|
||||
<div class="interface_box">
|
||||
<div class="file_zone hidden">
|
||||
<div class="upload_zone drop_zone">
|
||||
<div class="input_caption">Drop Audio Here<br>- or -<br>Click to Upload</div>
|
||||
</div>
|
||||
<div class="file_display hide">
|
||||
<div class="file_name"></div>
|
||||
<div class="file_size"></div>
|
||||
</div>
|
||||
<input class="hidden_upload" type="file" accept="audio/*" />
|
||||
</div>
|
||||
<div class="upload_zone mic_zone hidden">
|
||||
<img class="not_recording" src="/static/img/mic.png" />
|
||||
<div class="recording hidden volume_display">
|
||||
<div class="volume volume_left">
|
||||
<div class="volume_bar"></div>
|
||||
</div>
|
||||
<img src="/static/img/mic_recording.png" />
|
||||
<div class="volume volume_right">
|
||||
<div class="volume_bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="not_recording input_caption">Click to Record from Microphone</div>
|
||||
<div class="recording hidden input_caption">Click to Stop Recording</div>
|
||||
</div>
|
||||
<div class="player hidden">
|
||||
<div class="waveform"></div>
|
||||
<button class="playpause primary">Play / Pause</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
state: "NO_AUDIO",
|
||||
init: function(opts) {
|
||||
var io = this;
|
||||
this.source = opts.source;
|
||||
this.wavesurfer = WaveSurfer.create({
|
||||
container: io.target.find('.waveform')[0],
|
||||
waveColor: '#888888',
|
||||
progressColor: '#e67e22',
|
||||
barWidth: 3,
|
||||
hideScrollbar: true
|
||||
});
|
||||
if (this.source == "microphone") {
|
||||
this.target.find(".mic_zone").removeClass("hidden");
|
||||
this.target.find(".mic_zone").click(function() {
|
||||
if (io.state == "NO_AUDIO") {
|
||||
if (!has_audio_loaded) {
|
||||
loadAudio();
|
||||
io.mic = new p5.AudioIn();
|
||||
}
|
||||
io.recorder = new p5.SoundRecorder();
|
||||
io.soundFile = new p5.SoundFile();
|
||||
io.recorder.setInput(io.mic);
|
||||
io.target.find(".recording").removeClass("hidden");
|
||||
io.target.find(".not_recording").hide();
|
||||
io.state = "RECORDING";
|
||||
io.mic.start();
|
||||
io.recorder.record(io.soundFile);
|
||||
|
||||
io.interval_id = window.setInterval(function () {
|
||||
var volume = Math.floor(100 * io.mic.getLevel());
|
||||
io.target.find(".volume_bar").width(`${(volume > 0 ? 10 : 0) + Math.round(2 * Math.sqrt(10 * volume))}px`)
|
||||
}, 100)
|
||||
}
|
||||
});
|
||||
|
||||
this.target.find(".mic_zone").mousedown(function() {
|
||||
if (io.state == "RECORDING" || io.state == "STOP_RECORDING") {
|
||||
io.target.find(".upload_zone").hide();
|
||||
io.recorder.stop();
|
||||
var blob = io.soundFile.getBlob();
|
||||
var reader = new window.FileReader();
|
||||
reader.readAsDataURL(blob);
|
||||
reader.onloadend = function() {
|
||||
io.load_preview_from_audio(reader.result);
|
||||
}
|
||||
if (io.interval_id) {
|
||||
window.clearInterval(io.interval_id);
|
||||
}
|
||||
}
|
||||
})
|
||||
} else if (this.source == "upload") {
|
||||
this.target.find(".file_zone").removeClass("hidden");
|
||||
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(".playpause").click(function () {
|
||||
io.wavesurfer.playPause();
|
||||
})
|
||||
},
|
||||
load_preview_from_audio: function(audio) {
|
||||
var io = this;
|
||||
io.audio_data = audio;
|
||||
io.target.find(".player").removeClass("hidden");
|
||||
io.wavesurfer.load(io.audio_data);
|
||||
if (io.state == "STOP_RECORDING") {
|
||||
io.state = "RECORDED";
|
||||
io.submit();
|
||||
}
|
||||
io.state = "RECORDED";
|
||||
},
|
||||
load_preview_from_files: function(files) {
|
||||
if (!files.length || !window.FileReader) {
|
||||
return
|
||||
}
|
||||
var ReaderObj = new FileReader()
|
||||
ReaderObj.readAsDataURL(files[0])
|
||||
ReaderObj.io = this;
|
||||
this.state = "AUDIO_LOADING"
|
||||
ReaderObj.onloadend = function() {
|
||||
let io = this.io;
|
||||
io.target.find(".upload_zone").hide();
|
||||
io.load_preview_from_audio(this.result);
|
||||
}
|
||||
},
|
||||
submit: function() {
|
||||
if (this.state == "RECORDED") {
|
||||
this.io_master.input(this.id, this.audio_data);
|
||||
} else if (this.state == "RECORDING") {
|
||||
this.state = "STOP_RECORDING";
|
||||
this.target.find(".upload_zone").mousedown();
|
||||
}
|
||||
},
|
||||
clear: function() {
|
||||
this.audio_data = null;
|
||||
this.state = "NO_AUDIO";
|
||||
this.target.find(".not_recording").show();
|
||||
this.target.find(".recording").addClass("hidden");
|
||||
this.target.find(".player").addClass("hidden");
|
||||
this.target.find(".upload_zone").show();
|
||||
this.target.find(".hidden_upload").prop("value", "")
|
||||
if (this.wavesurfer) {
|
||||
this.wavesurfer.stop();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ const checkbox = {
|
||||
<label><input class="checkbox" type="checkbox"> </label>
|
||||
</div>`,
|
||||
init: function(opts) {
|
||||
this.target.css("height", "auto");
|
||||
this.target.find("input").checkboxradio();
|
||||
},
|
||||
submit: function() {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user