mirror of
https://github.com/gradio-app/gradio.git
synced 2024-12-21 02:19:59 +08:00
version 0.7.8
This commit is contained in:
parent
772416f3c0
commit
84b9bf548c
@ -45,6 +45,7 @@ class Interface:
|
||||
preprocessing_fns=None,
|
||||
postprocessing_fns=None,
|
||||
verbose=True,
|
||||
saliency=None,
|
||||
):
|
||||
"""
|
||||
:param inputs: a string or `AbstractInput` representing the input interface.
|
||||
@ -54,6 +55,8 @@ class Interface:
|
||||
provided.
|
||||
:param preprocessing_fns: an optional function that overrides the preprocessing function of the input interface.
|
||||
:param postprocessing_fns: an optional function that overrides the postprocessing fn of the output interface.
|
||||
:param saliency: an optional function that takes the model and the processed input and returns a 2-d array
|
||||
|
||||
"""
|
||||
if isinstance(inputs, str):
|
||||
self.input_interface = gradio.inputs.registry[inputs.lower()](
|
||||
@ -94,6 +97,7 @@ class Interface:
|
||||
self.validate_flag = False
|
||||
self.simple_server = None
|
||||
self.hash = random.getrandbits(32)
|
||||
self.saliency = saliency
|
||||
|
||||
@staticmethod
|
||||
def _infer_model_type(model):
|
||||
@ -132,6 +136,7 @@ class Interface:
|
||||
Method that calls the relevant method of the model object to make a prediction.
|
||||
:param preprocessed_input: the preprocessed input returned by the input interface
|
||||
"""
|
||||
print(preprocessed_input.shape)
|
||||
if self.model_type == "sklearn":
|
||||
return self.model_obj.predict(preprocessed_input)
|
||||
elif self.model_type == "keras":
|
||||
|
@ -7,7 +7,6 @@ import socket
|
||||
import threading
|
||||
from http.server import HTTPServer as BaseHTTPServer, SimpleHTTPRequestHandler
|
||||
import pkg_resources
|
||||
from bs4 import BeautifulSoup
|
||||
from distutils import dir_util
|
||||
from gradio import inputs, outputs
|
||||
import json
|
||||
@ -15,7 +14,6 @@ from gradio.tunneling import create_tunnel
|
||||
import urllib.request
|
||||
from shutil import copyfile
|
||||
|
||||
|
||||
INITIAL_PORT_VALUE = (
|
||||
7860
|
||||
) # The http server will try to open on port 7860. If not available, 7861, 7862, etc.
|
||||
@ -25,9 +23,7 @@ TRY_NUM_PORTS = (
|
||||
LOCALHOST_NAME = "localhost"
|
||||
GRADIO_API_SERVER = "https://api.gradio.app/v1/tunnel-request"
|
||||
|
||||
BASE_TEMPLATE = pkg_resources.resource_filename(
|
||||
"gradio", "templates/base_template.html"
|
||||
)
|
||||
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"
|
||||
@ -37,52 +33,18 @@ CONFIG_FILE = "static/config.json"
|
||||
ASSOCIATION_PATH_IN_STATIC = "static/apple-app-site-association"
|
||||
ASSOCIATION_PATH_IN_ROOT = "apple-app-site-association"
|
||||
|
||||
FLAGGING_DIRECTORY = 'gradio-flagged/{}'
|
||||
FLAGGING_FILENAME = 'gradio-flagged.txt'
|
||||
FLAGGING_DIRECTORY = 'static/flagged/'
|
||||
FLAGGING_FILENAME = 'data.txt'
|
||||
|
||||
|
||||
def build_template(temp_dir, input_interface, output_interface):
|
||||
"""
|
||||
Builds a complete HTML template with supporting JS and CSS files in a given directory.
|
||||
:param temp_dir: string with path to temp directory in which the template should be built
|
||||
:param input_interface: an AbstractInput object which includes is used to get the input template
|
||||
:param output_interface: an AbstractInput object which includes is used to get the input template
|
||||
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
|
||||
"""
|
||||
input_template_path = pkg_resources.resource_filename(
|
||||
"gradio",
|
||||
inputs.BASE_INPUT_INTERFACE_TEMPLATE_PATH.format(input_interface.get_name()),
|
||||
)
|
||||
output_template_path = pkg_resources.resource_filename(
|
||||
"gradio",
|
||||
outputs.BASE_OUTPUT_INTERFACE_TEMPLATE_PATH.format(output_interface.get_name()),
|
||||
)
|
||||
input_page = open(input_template_path)
|
||||
output_page = open(output_template_path)
|
||||
input_soup = BeautifulSoup(
|
||||
render_string_or_list_with_tags(
|
||||
input_page.read(), input_interface.get_template_context()
|
||||
),
|
||||
features="html.parser",
|
||||
)
|
||||
output_soup = BeautifulSoup(
|
||||
render_string_or_list_with_tags(
|
||||
output_page.read(), output_interface.get_template_context()
|
||||
),
|
||||
features="html.parser",
|
||||
)
|
||||
dir_util.copy_tree(STATIC_TEMPLATE_LIB, temp_dir)
|
||||
dir_util.copy_tree(STATIC_PATH_LIB, os.path.join(temp_dir, STATIC_PATH_TEMP))
|
||||
|
||||
all_io_page = open(BASE_TEMPLATE)
|
||||
all_io_soup = BeautifulSoup(all_io_page.read(), features="html.parser")
|
||||
input_tag = all_io_soup.find("div", {"id": "input"})
|
||||
output_tag = all_io_soup.find("div", {"id": "output"})
|
||||
|
||||
# input_tag.replace_with(input_soup)
|
||||
# output_tag.replace_with(output_soup)
|
||||
|
||||
f = open(os.path.join(temp_dir, TEMPLATE_TEMP), "w")
|
||||
f.write(str(all_io_soup))
|
||||
|
||||
copy_files(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))
|
||||
@ -103,15 +65,6 @@ def build_template(temp_dir, input_interface, output_interface):
|
||||
)
|
||||
|
||||
|
||||
def copy_files(src_dir, dest_dir):
|
||||
"""
|
||||
Copies all the files from one directory to another
|
||||
:param src_dir: string path to source directory
|
||||
:param dest_dir: string path to destination directory
|
||||
"""
|
||||
dir_util.copy_tree(src_dir, dest_dir)
|
||||
|
||||
|
||||
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
|
||||
@ -216,6 +169,10 @@ def serve_files_in_background(interface, port, directory_to_serve=None):
|
||||
prediction = interface.predict(processed_input)
|
||||
processed_output = interface.output_interface.postprocess(prediction)
|
||||
output = {"action": "output", "data": processed_output}
|
||||
if interface.saliency is not None:
|
||||
import numpy as np
|
||||
saliency = interface.saliency(interface.model_obj, processed_input, prediction)
|
||||
output['saliency'] = saliency.tolist()
|
||||
|
||||
# Prepare return json dictionary.
|
||||
self.wfile.write(json.dumps(output).encode())
|
||||
@ -224,15 +181,80 @@ def serve_files_in_background(interface, port, directory_to_serve=None):
|
||||
self._set_headers()
|
||||
data_string = self.rfile.read(int(self.headers["Content-Length"]))
|
||||
msg = json.loads(data_string)
|
||||
flag_dir = FLAGGING_DIRECTORY.format(interface.hash)
|
||||
flag_dir = os.path.join(directory_to_serve, FLAGGING_DIRECTORY)
|
||||
os.makedirs(flag_dir, exist_ok=True)
|
||||
dict = {'input': interface.input_interface.rebuild_flagged(flag_dir, msg),
|
||||
'output': interface.output_interface.rebuild_flagged(flag_dir, msg),
|
||||
'message': msg['data']['message']}
|
||||
output = {'input': interface.input_interface.rebuild_flagged(flag_dir, msg),
|
||||
'output': interface.output_interface.rebuild_flagged(flag_dir, msg),
|
||||
'message': msg['data']['message']}
|
||||
with open(os.path.join(flag_dir, FLAGGING_FILENAME), 'a+') as f:
|
||||
f.write(json.dumps(dict))
|
||||
f.write(json.dumps(output))
|
||||
f.write("\n")
|
||||
|
||||
#TODO(abidlabs): clean this up
|
||||
elif self.path == "/api/auto/rotation":
|
||||
from gradio import validation_data, preprocessing_utils
|
||||
import numpy as np
|
||||
|
||||
self._set_headers()
|
||||
data_string = self.rfile.read(int(self.headers["Content-Length"]))
|
||||
msg = json.loads(data_string)
|
||||
img_orig = preprocessing_utils.decode_base64_to_image(msg["data"])
|
||||
img_orig = img_orig.convert('RGB')
|
||||
img_orig = img_orig.resize((224, 224))
|
||||
|
||||
flag_dir = os.path.join(directory_to_serve, FLAGGING_DIRECTORY)
|
||||
os.makedirs(flag_dir, exist_ok=True)
|
||||
|
||||
for deg in range(-180, 180+45, 45):
|
||||
img = img_orig.rotate(deg)
|
||||
img_array = np.array(img) / 127.5 - 1
|
||||
prediction = interface.predict(np.expand_dims(img_array, axis=0))
|
||||
processed_output = interface.output_interface.postprocess(prediction)
|
||||
output = {'input': interface.input_interface.save_to_file(flag_dir, img),
|
||||
'output': interface.output_interface.rebuild_flagged(
|
||||
flag_dir, {'data': {'output': processed_output}}),
|
||||
'message': f'rotation by {deg} degrees'}
|
||||
|
||||
with open(os.path.join(flag_dir, FLAGGING_FILENAME), 'a+') as f:
|
||||
f.write(json.dumps(output))
|
||||
f.write("\n")
|
||||
|
||||
# Prepare return json dictionary.
|
||||
self.wfile.write(json.dumps({}).encode())
|
||||
|
||||
elif self.path == "/api/auto/lighting":
|
||||
from gradio import validation_data, preprocessing_utils
|
||||
import numpy as np
|
||||
from PIL import ImageEnhance
|
||||
|
||||
self._set_headers()
|
||||
data_string = self.rfile.read(int(self.headers["Content-Length"]))
|
||||
msg = json.loads(data_string)
|
||||
img_orig = preprocessing_utils.decode_base64_to_image(msg["data"])
|
||||
img_orig = img_orig.convert('RGB')
|
||||
img_orig = img_orig.resize((224, 224))
|
||||
enhancer = ImageEnhance.Brightness(img_orig)
|
||||
|
||||
flag_dir = os.path.join(directory_to_serve, FLAGGING_DIRECTORY)
|
||||
os.makedirs(flag_dir, exist_ok=True)
|
||||
|
||||
for i in range(9):
|
||||
img = enhancer.enhance(i/4)
|
||||
img_array = np.array(img) / 127.5 - 1
|
||||
prediction = interface.predict(np.expand_dims(img_array, axis=0))
|
||||
processed_output = interface.output_interface.postprocess(prediction)
|
||||
output = {'input': interface.input_interface.save_to_file(flag_dir, img),
|
||||
'output': interface.output_interface.rebuild_flagged(
|
||||
flag_dir, {'data': {'output': processed_output}}),
|
||||
'message': f'brighting adjustment by a factor of {i}'}
|
||||
|
||||
with open(os.path.join(flag_dir, FLAGGING_FILENAME), 'a+') as f:
|
||||
f.write(json.dumps(output))
|
||||
f.write("\n")
|
||||
|
||||
# Prepare return json dictionary.
|
||||
self.wfile.write(json.dumps({}).encode())
|
||||
|
||||
else:
|
||||
self.send_error(404, 'Path not found: %s' % self.path)
|
||||
|
||||
|
@ -1,13 +1,21 @@
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
import base64
|
||||
import tempfile
|
||||
import scipy.io.wavfile
|
||||
from scipy.fftpack import dct
|
||||
import numpy as np
|
||||
|
||||
|
||||
def encoding_to_image(encoding):
|
||||
#########################
|
||||
# IMAGE PRE-PROCESSING
|
||||
#########################
|
||||
def decode_base64_to_image(encoding):
|
||||
content = encoding.split(';')[1]
|
||||
image_encoded = content.split(',')[1]
|
||||
return Image.open(BytesIO(base64.b64decode(image_encoded)))
|
||||
|
||||
|
||||
def resize_and_crop(img, size, crop_type='top'):
|
||||
"""
|
||||
Resize and crop an image to fit the specified size.
|
||||
@ -58,3 +66,89 @@ def resize_and_crop(img, size, crop_type='top'):
|
||||
Image.ANTIALIAS)
|
||||
# If the scale is the same, we do not need to crop
|
||||
return img
|
||||
|
||||
|
||||
##################
|
||||
# AUDIO FILES
|
||||
##################
|
||||
|
||||
def decode_base64_to_wav_file(encoding):
|
||||
inp = encoding.split(';')[1].split(',')[1]
|
||||
wav_obj = base64.b64decode(inp)
|
||||
file_obj = tempfile.NamedTemporaryFile()
|
||||
file_obj.close()
|
||||
with open(file_obj.name, 'wb') as f:
|
||||
f.write(wav_obj)
|
||||
return file_obj
|
||||
|
||||
|
||||
def generate_mfcc_features_from_audio_file(wav_filename,
|
||||
pre_emphasis=0.95,
|
||||
frame_size= 0.025,
|
||||
frame_stride=0.01,
|
||||
NFFT=512,
|
||||
nfilt=40,
|
||||
num_ceps=12,
|
||||
cep_lifter=22):
|
||||
"""
|
||||
Loads and preprocesses a .wav audio file into mfcc coefficients, the typical inputs to models.
|
||||
Adapted from: https://haythamfayek.com/2016/04/21/speech-processing-for-machine-learning.html
|
||||
:param wav_filename: string name of audio file to process.
|
||||
:param pre_emphasis: a float factor, typically 0.95 or 0.97, which amplifies high frequencies.
|
||||
:param frame_size: a float that is the length, in seconds, of time frame over which to take the fft.
|
||||
:param frame_stride: a float that is the offset, in seconds, between consecutive time frames.
|
||||
:param NFFT: The number of points in the short-time fft for each time frame.
|
||||
:param nfilt: The number of filters on the Mel-scale to extract frequency bands.
|
||||
:param num_ceps: the number of cepstral coefficients to retrain.
|
||||
:param cep_lifter: the int factor, by which to de-emphasize higher-frequency.
|
||||
:return: a numpy array of mfcc coefficients.
|
||||
"""
|
||||
sample_rate, signal = scipy.io.wavfile.read(wav_filename)
|
||||
emphasized_signal = np.append(signal[0], signal[1:] - pre_emphasis * signal[:-1])
|
||||
|
||||
frame_length, frame_step = frame_size * sample_rate, frame_stride * sample_rate # Convert from seconds to samples
|
||||
signal_length = len(emphasized_signal)
|
||||
frame_length = int(round(frame_length))
|
||||
frame_step = int(round(frame_step))
|
||||
num_frames = int(np.ceil(float(np.abs(signal_length - frame_length)) / frame_step)) # Make sure that we have at least 1 frame
|
||||
|
||||
pad_signal_length = num_frames * frame_step + frame_length
|
||||
z = np.zeros((pad_signal_length - signal_length))
|
||||
pad_signal = np.append(emphasized_signal, z) # Pad Signal to make sure that all frames have equal number of samples without truncating any samples from the original signal
|
||||
|
||||
indices = np.tile(np.arange(0, frame_length), (num_frames, 1)) + np.tile(np.arange(0, num_frames * frame_step, frame_step), (frame_length, 1)).T
|
||||
frames = pad_signal[indices.astype(np.int32, copy=False)]
|
||||
|
||||
frames *= np.hamming(frame_length)
|
||||
mag_frames = np.absolute(np.fft.rfft(frames, NFFT)) # Magnitude of the FFT
|
||||
pow_frames = ((1.0 / NFFT) * ((mag_frames) ** 2)) # Power Spectrum
|
||||
|
||||
low_freq_mel = 0
|
||||
high_freq_mel = (2595 * np.log10(1 + (sample_rate / 2) / 700)) # Convert Hz to Mel
|
||||
mel_points = np.linspace(low_freq_mel, high_freq_mel, nfilt + 2) # Equally spaced in Mel scale
|
||||
hz_points = (700 * (10**(mel_points / 2595) - 1)) # Convert Mel to Hz
|
||||
bin = np.floor((NFFT + 1) * hz_points / sample_rate)
|
||||
|
||||
fbank = np.zeros((nfilt, int(np.floor(NFFT / 2 + 1))))
|
||||
for m in range(1, nfilt + 1):
|
||||
f_m_minus = int(bin[m - 1]) # left
|
||||
f_m = int(bin[m]) # center
|
||||
f_m_plus = int(bin[m + 1]) # right
|
||||
|
||||
for k in range(f_m_minus, f_m):
|
||||
fbank[m - 1, k] = (k - bin[m - 1]) / (bin[m] - bin[m - 1])
|
||||
for k in range(f_m, f_m_plus):
|
||||
fbank[m - 1, k] = (bin[m + 1] - k) / (bin[m + 1] - bin[m])
|
||||
filter_banks = np.dot(pow_frames, fbank.T)
|
||||
filter_banks = np.where(filter_banks == 0, np.finfo(float).eps, filter_banks) # Numerical Stability
|
||||
filter_banks = 20 * np.log10(filter_banks) # dB
|
||||
|
||||
mfcc = dct(filter_banks, type=2, axis=1, norm='ortho')[:, 0: (num_ceps + 1)] # Keep filters 1-13 by default.
|
||||
(nframes, ncoeff) = mfcc.shape
|
||||
n = np.arange(ncoeff)
|
||||
lift = 1 + (cep_lifter / 2) * np.sin(np.pi * n / cep_lifter)
|
||||
mfcc *= lift
|
||||
|
||||
filter_banks -= (np.mean(filter_banks, axis=0) + 1e-8)
|
||||
mfcc -= (np.mean(mfcc, axis=0) + 1e-8)
|
||||
return mfcc[np.newaxis, :, :] # Create a batch dimension.
|
||||
|
30
build/lib/gradio/static/css/bulk_style.css
Normal file
30
build/lib/gradio/static/css/bulk_style.css
Normal file
@ -0,0 +1,30 @@
|
||||
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;
|
||||
}
|
@ -36,7 +36,7 @@
|
||||
.panel_buttons {
|
||||
display: flex;
|
||||
}
|
||||
.submit, .clear, .flag {
|
||||
.submit, .clear, .panel_button {
|
||||
background-color: #F6F6F6 !important;
|
||||
flex-grow: 1;
|
||||
padding: 8px !important;
|
||||
@ -74,6 +74,11 @@
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
flex-flow: column;
|
||||
}
|
||||
.upload_zone img {
|
||||
height: 120px;
|
||||
|
||||
}
|
||||
.drop_zone {
|
||||
border: dashed 8px #DDD;
|
||||
@ -102,3 +107,20 @@
|
||||
.flag.flagged {
|
||||
background-color: pink !important;
|
||||
}
|
||||
|
||||
.test_panel {
|
||||
margin-top: 5px;
|
||||
padding:10px;
|
||||
background-color: #eea45d6b !important;
|
||||
border: 1px solid #EEA45D;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tests {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.test_panel > input[type="button"] {
|
||||
max-width: 100px;
|
||||
}
|
||||
|
@ -4,13 +4,36 @@
|
||||
.image_display {
|
||||
height: 100%;
|
||||
}
|
||||
.image_preview_holder {
|
||||
.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;
|
||||
background-color: #CCCCCC;
|
||||
}
|
||||
.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%;
|
||||
|
52
build/lib/gradio/static/css/interfaces/input/microphone.css
Normal file
52
build/lib/gradio/static/css/interfaces/input/microphone.css
Normal file
@ -0,0 +1,52 @@
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.recording.input_caption {
|
||||
color: #C00000;
|
||||
}
|
||||
.volume_display {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
.volume {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.volume_left {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.volume_bar {
|
||||
height: 12px;
|
||||
background-color: #C00000;
|
||||
transition: width 0.1s;
|
||||
}
|
||||
.volume_left .volume_bar {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
.volume_right .volume_bar {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
.player {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.waveform {
|
||||
width: 100%;
|
||||
}
|
||||
.waveform > wave {
|
||||
overflow: visible !important;
|
||||
}
|
||||
.waveform canvas {
|
||||
border: none !important;
|
||||
}
|
||||
.playpause {
|
||||
margin-top: 26px;
|
||||
font-size: 20px;
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
BIN
build/lib/gradio/static/img/mic_recording.png
Normal file
BIN
build/lib/gradio/static/img/mic_recording.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -10,7 +10,7 @@ var io_master = {
|
||||
data: JSON.stringify(post_data),
|
||||
success: function(output){
|
||||
if (output['action'] == 'output') {
|
||||
io_master.output(output['data']);
|
||||
io_master.output(output);
|
||||
}
|
||||
},
|
||||
error: function(XMLHttpRequest, textStatus, errorThrown) {
|
||||
@ -21,8 +21,27 @@ var io_master = {
|
||||
});
|
||||
},
|
||||
output: function(data) {
|
||||
this.last_output = data;
|
||||
this.output_interface.output(data);
|
||||
this.last_output = data["data"];
|
||||
this.output_interface.output(data["data"]);
|
||||
if (this.input_interface.output && data["saliency"]) {
|
||||
this.input_interface.output(data["saliency"]);
|
||||
}
|
||||
},
|
||||
test: function(type, data) {
|
||||
var post_data = {
|
||||
'data': data
|
||||
};
|
||||
if (type == "rotation") {
|
||||
$.ajax({type: "POST",
|
||||
url: "/api/auto/rotation",
|
||||
data: JSON.stringify(post_data)
|
||||
});
|
||||
} else if (type == "lighting") {
|
||||
$.ajax({type: "POST",
|
||||
url: "/api/auto/lighting",
|
||||
data: JSON.stringify(post_data)
|
||||
});
|
||||
}
|
||||
},
|
||||
flag: function(message) {
|
||||
var post_data = {
|
||||
|
@ -7,8 +7,13 @@ const image_input = {
|
||||
<div class="edit_holder">
|
||||
<button class="edit_image interface_button primary">Edit</button>
|
||||
</div>
|
||||
<div class="image_preview_holder">
|
||||
<img class="image_preview" />
|
||||
<div class="view_holders">
|
||||
<div class="image_preview_holder">
|
||||
<img class="image_preview" />
|
||||
</div>
|
||||
<div class="saliency_holder hide">
|
||||
<canvas class="saliency"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input class="hidden_upload" type="file" accept="image/x-png,image/gif,image/jpeg" />`
|
||||
@ -20,11 +25,22 @@ const image_input = {
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
test_html: `
|
||||
<div class="panel_buttons test_panel">
|
||||
<div><strong>Rotation</strong>: (rotates between -180 and 180 degrees)</div>
|
||||
<input type="button" class="panel_button rotate_test" value="Run"/>
|
||||
</div>
|
||||
<div class="panel_buttons test_panel">
|
||||
<div><strong>Lighting</strong>: (adjusts the lighting to 9 different levels)</div>
|
||||
<input type="button" class="panel_button light_test" value="Run"/>
|
||||
</div>
|
||||
<a href="bulk_data.html"><input type="button" class="panel_button" value="Bulk Data" style="margin-top: 10px;"/></a>
|
||||
`,
|
||||
init: function() {
|
||||
var io = this;
|
||||
$('body').append(this.overlay_html.format(this.id));
|
||||
this.overlay_target = $(`.overlay[interface_id=${this.id}]`);
|
||||
this.target.find(".upload_zone").click(function (e) {
|
||||
let io = get_interface(e.target)
|
||||
io.target.find(".hidden_upload").click();
|
||||
});
|
||||
this.target.on('drag dragstart dragend dragover dragenter dragleave drop',
|
||||
@ -33,19 +49,17 @@ const image_input = {
|
||||
e.stopPropagation();
|
||||
})
|
||||
this.target.on('drop', '.drop_zone', function(e) {
|
||||
let io = get_interface(e.target)
|
||||
files = e.originalEvent.dataTransfer.files;
|
||||
io.load_preview_from_files(files)
|
||||
});
|
||||
this.target.find('.hidden_upload').on('change', function (e) {
|
||||
if (this.files) {
|
||||
let io = get_interface(e.target);
|
||||
io.load_preview_from_files(this.files);
|
||||
}
|
||||
})
|
||||
this.target.find('.edit_image').click(function (e) {
|
||||
let io = get_interface(e.target);
|
||||
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], {
|
||||
@ -65,7 +79,6 @@ const image_input = {
|
||||
<button class="tui_cancel tui_close interface_button secondary">Cancel</button>
|
||||
`)
|
||||
this.overlay_target.find('.tui_close').click(function (e) {
|
||||
let io = get_interface(e.target);
|
||||
io.overlay_target.addClass("hide");
|
||||
if ($(e.target).hasClass('tui_save')) {
|
||||
// if (io.tui_editor.ui.submenu == "crop") {
|
||||
@ -73,6 +86,17 @@ const image_input = {
|
||||
// }
|
||||
io.set_image_data(io.tui_editor.toDataURL(), /*update_editor=*/false);
|
||||
}
|
||||
});
|
||||
$(".tests").html(this.test_html);
|
||||
$(".rotate_test").click(function () {
|
||||
if (io.image_data) {
|
||||
io.io_master.test("rotation", io.image_data);
|
||||
}
|
||||
})
|
||||
$(".light_test").click(function () {
|
||||
if (io.image_data) {
|
||||
io.io_master.test("lighting", io.image_data);
|
||||
}
|
||||
})
|
||||
},
|
||||
submit: function() {
|
||||
@ -89,6 +113,37 @@ const image_input = {
|
||||
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');
|
||||
var cell_width = width / data[0].length
|
||||
var cell_height = height / data.length
|
||||
var r = 0
|
||||
data.forEach(function(row) {
|
||||
var c = 0
|
||||
row.forEach(function(cell) {
|
||||
if (cell < 0.25) {
|
||||
ctx.fillStyle = "white";
|
||||
} else if (cell < 0.5) {
|
||||
ctx.fillStyle = "yellow";
|
||||
} else if (cell < 0.75) {
|
||||
ctx.fillStyle = "orange";
|
||||
} else {
|
||||
ctx.fillStyle = "red";
|
||||
}
|
||||
ctx.fillRect(c * cell_width, r * cell_height, cell_width, cell_height);
|
||||
c++;
|
||||
})
|
||||
r++;
|
||||
})
|
||||
}
|
||||
},
|
||||
state: "NO_IMAGE",
|
||||
image_data: null,
|
||||
|
95
build/lib/gradio/static/js/interfaces/input/microphone.js
Normal file
95
build/lib/gradio/static/js/interfaces/input/microphone.js
Normal file
@ -0,0 +1,95 @@
|
||||
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>
|
||||
</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",
|
||||
init: function() {
|
||||
var io = this;
|
||||
this.wavesurfer = WaveSurfer.create({
|
||||
container: '.waveform',
|
||||
waveColor: '#888888',
|
||||
progressColor: '#EEA45D',
|
||||
barWidth: 3,
|
||||
hideScrollbar: true
|
||||
});
|
||||
this.target.find(".upload_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);
|
||||
|
||||
var 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(".upload_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.audio_data = reader.result;
|
||||
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";
|
||||
}
|
||||
window.clearInterval(interval_id);
|
||||
}
|
||||
})
|
||||
this.target.find(".playpause").click(function () {
|
||||
io.wavesurfer.playPause();
|
||||
})
|
||||
},
|
||||
submit: function() {
|
||||
if (this.state == "RECORDED") {
|
||||
this.io_master.input(this.id, this.audio_data);
|
||||
} else if (this.state == "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();
|
||||
if (this.wavesurfer) {
|
||||
this.wavesurfer.stop();
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ const sketchpad_input = {
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>`,
|
||||
init: function() {
|
||||
var io = this;
|
||||
var dimension = Math.min(this.target.find(".canvas_holder").width(),
|
||||
this.target.find(".canvas_holder").height()) - 2 // dimension - border
|
||||
var id = this.id;
|
||||
@ -21,7 +22,6 @@ const sketchpad_input = {
|
||||
this.canvas = this.target.find('.canvas_holder canvas')[0];
|
||||
this.context = this.canvas.getContext("2d");
|
||||
this.target.find(".brush").click(function (e) {
|
||||
let io = get_interface(e.target)
|
||||
io.target.find(".brush").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
io.sketchpad.penSize = $(this).attr("size");
|
||||
|
@ -2,7 +2,8 @@ input_to_object_map = {
|
||||
"csv" : {},
|
||||
"imageupload" : image_input,
|
||||
"sketchpad" : sketchpad_input,
|
||||
"textbox" : textbox_input
|
||||
"textbox" : textbox_input,
|
||||
"microphone" : microphone
|
||||
}
|
||||
output_to_object_map = {
|
||||
"csv" : {},
|
||||
@ -18,11 +19,6 @@ function set_interface_id(interface, id) {
|
||||
interface.target.attr("interface_id", id);
|
||||
}
|
||||
|
||||
function get_interface(target) {
|
||||
return id_to_interface_map[$(target).closest(".interface, .interface_extension").
|
||||
attr("interface_id")];
|
||||
}
|
||||
|
||||
var config;
|
||||
$.getJSON("static/config.json", function(data) {
|
||||
config = data;
|
||||
|
@ -13,7 +13,7 @@ $("#send_link").click(function(evt) {
|
||||
"type": "POST",
|
||||
"crossDomain": true,
|
||||
"data": {
|
||||
"url": config["ngrok_socket_url"],
|
||||
"url": config["share_url"],
|
||||
"name": name,
|
||||
"email": email
|
||||
},
|
||||
|
3
build/lib/gradio/static/js/vendor/p5.dom.min.js
vendored
Normal file
3
build/lib/gradio/static/js/vendor/p5.dom.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
build/lib/gradio/static/js/vendor/p5.min.js
vendored
Normal file
3
build/lib/gradio/static/js/vendor/p5.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
30
build/lib/gradio/static/js/vendor/p5.sound.min.js
vendored
Normal file
30
build/lib/gradio/static/js/vendor/p5.sound.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
build/lib/gradio/static/js/vendor/wavesurfer.min.js
vendored
Normal file
6
build/lib/gradio/static/js/vendor/wavesurfer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -5,6 +5,6 @@ en = {
|
||||
"restarting python interpreter.",
|
||||
"COLAB_NO_LOCAL": "Cannot display local interface on google colab, public link created.",
|
||||
"PUBLIC_SHARE_TRUE": "To create a public link, set `share=True` in the argument to `launch()`.",
|
||||
"MODEL_PUBLICLY_AVAILABLE_URL": "Model available publicly at: {}",
|
||||
"MODEL_PUBLICLY_AVAILABLE_URL": "Model available publicly at: {} (may take up to a minute for link to be usable)",
|
||||
"GENERATING_PUBLIC_LINK": "Generating public link (may take a few seconds...):",
|
||||
}
|
||||
|
33
build/lib/gradio/templates/bulk_data.html
Normal file
33
build/lib/gradio/templates/bulk_data.html
Normal file
@ -0,0 +1,33 @@
|
||||
<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>
|
93
build/lib/gradio/templates/index.html
Normal file
93
build/lib/gradio/templates/index.html
Normal file
@ -0,0 +1,93 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- <iframe src=""></iframe> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<title>Gradio</title>
|
||||
<link rel="stylesheet" href="../static/css/style.css">
|
||||
<link rel="stylesheet" href="../static/css/gradio.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/input/csv.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/input/image_upload.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/input/sketchpad.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/input/textbox.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/input/csv.css">
|
||||
<link rel="stylesheet" href="../static/css/interfaces/input/microphone.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/textbox.css">
|
||||
<link rel="stylesheet" href="../static/css/loading.css"/>
|
||||
<!-- TUI EDITOR -->
|
||||
<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">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav>
|
||||
<a href="https://gradio.app"><img src="../static/img/logo_inline.png" /></a>
|
||||
</nav>
|
||||
<div id="share_row">
|
||||
<button id="share" class="primary">Share this Interface</button>
|
||||
<div id="share_form">
|
||||
<input type="text" id="share_name" placeholder="sender name (you)"></input>
|
||||
<input type="text" id="share_email" placeholder="emails (comma-separated if multiple)"></input>
|
||||
<button class="primary" id="send_link">Send Link</button>
|
||||
</div>
|
||||
<div id="share_complete">
|
||||
<span id="share_message"></span>
|
||||
<button class="secondary" id="share_more"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="panels">
|
||||
<div class="panel">
|
||||
<div class="panel_header">Input</div>
|
||||
<div id="input_interface" class="interface"></div>
|
||||
<div class="panel_buttons">
|
||||
<input class="submit" type="submit" value="submit"/>
|
||||
<input class="clear" type="reset" value="clear">
|
||||
</div>
|
||||
<div class="tests"></div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel_header">
|
||||
Output
|
||||
<div class="loading">
|
||||
<img src="../static/img/logo_mini.png" class="ld ld-skew"/>
|
||||
<span class="loading_time"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="output_interface" class="interface"></div>
|
||||
<div class="panel_buttons">
|
||||
<input type="text" class="flag_message" placeholder="(Optional message for flagging)"/>
|
||||
<input type="button" class="panel_button flag" value="flag"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../static/js/vendor/jquery.min.js"></script>
|
||||
<!-- TUI EDITOR -->
|
||||
<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>
|
||||
<script src="../static/js/vendor/tui-color-picker.js"></script>
|
||||
<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/utils.js"></script>
|
||||
<script src="../static/js/all_io.js"></script>
|
||||
<script src="../static/js/interfaces/input/csv.js"></script>
|
||||
<script src="../static/js/interfaces/input/image_upload.js"></script>
|
||||
<script src="../static/js/vendor/sketchpad.js"></script>
|
||||
<script src="../static/js/interfaces/input/sketchpad.js"></script>
|
||||
<script src="../static/js/interfaces/input/textbox.js"></script>
|
||||
<script src="../static/js/interfaces/input/csv.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/textbox.js"></script>
|
||||
<script src="../static/js/share.js"></script>
|
||||
<script src="../static/js/load_interfaces.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,6 +1,6 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: gradio
|
||||
Version: 0.7.7
|
||||
Version: 0.7.8
|
||||
Summary: Python library for easily interacting with trained machine learning models
|
||||
Home-page: https://github.com/abidlabs/gradio
|
||||
Author: Abubakar Abid
|
||||
|
@ -19,12 +19,14 @@ gradio.egg-info/top_level.txt
|
||||
gradio/static/apple-app-site-association
|
||||
gradio/static/config.json
|
||||
gradio/static/css/.DS_Store
|
||||
gradio/static/css/bulk_style.css
|
||||
gradio/static/css/font-awesome.min.css
|
||||
gradio/static/css/gradio.css
|
||||
gradio/static/css/loading.css
|
||||
gradio/static/css/style.css
|
||||
gradio/static/css/interfaces/input/csv.css
|
||||
gradio/static/css/interfaces/input/image_upload.css
|
||||
gradio/static/css/interfaces/input/microphone.css
|
||||
gradio/static/css/interfaces/input/sketchpad.css
|
||||
gradio/static/css/interfaces/input/textbox.css
|
||||
gradio/static/css/interfaces/output/image.css
|
||||
@ -38,6 +40,7 @@ gradio/static/img/logo_loading.gif
|
||||
gradio/static/img/logo_mini.png
|
||||
gradio/static/img/logo_only.png
|
||||
gradio/static/img/mic.png
|
||||
gradio/static/img/mic_recording.png
|
||||
gradio/static/img/table.png
|
||||
gradio/static/img/webcam.png
|
||||
gradio/static/img/vendor/icon-a.svg
|
||||
@ -50,6 +53,7 @@ gradio/static/js/share.js
|
||||
gradio/static/js/utils.js
|
||||
gradio/static/js/interfaces/input/csv.js
|
||||
gradio/static/js/interfaces/input/image_upload.js
|
||||
gradio/static/js/interfaces/input/microphone.js
|
||||
gradio/static/js/interfaces/input/sketchpad.js
|
||||
gradio/static/js/interfaces/input/textbox.js
|
||||
gradio/static/js/interfaces/output/image.js
|
||||
@ -59,20 +63,18 @@ gradio/static/js/vendor/FileSaver.min.js
|
||||
gradio/static/js/vendor/black-theme.js
|
||||
gradio/static/js/vendor/fabric.js
|
||||
gradio/static/js/vendor/jquery.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
|
||||
gradio/static/js/vendor/papaparse.min.js
|
||||
gradio/static/js/vendor/sketchpad.js
|
||||
gradio/static/js/vendor/tui-code-snippet.min.js
|
||||
gradio/static/js/vendor/tui-color-picker.js
|
||||
gradio/static/js/vendor/tui-image-editor.js
|
||||
gradio/static/js/vendor/wavesurfer.min.js
|
||||
gradio/static/js/vendor/white-theme.js
|
||||
gradio/templates/base_template.html
|
||||
gradio/templates/input/csv.html
|
||||
gradio/templates/input/image_upload.html
|
||||
gradio/templates/input/sketchpad.html
|
||||
gradio/templates/input/textbox.html
|
||||
gradio/templates/output/image.html
|
||||
gradio/templates/output/label.html
|
||||
gradio/templates/output/textbox.html
|
||||
gradio/templates/bulk_data.html
|
||||
gradio/templates/index.html
|
||||
test/test_inputs.py
|
||||
test/test_interface.py
|
||||
test/test_networking.py
|
||||
|
@ -4,3 +4,4 @@ Pillow
|
||||
requests
|
||||
psutil
|
||||
paramiko
|
||||
scipy
|
||||
|
Loading…
Reference in New Issue
Block a user