added requirements to push package

This commit is contained in:
Abubakar Abid 2019-02-19 00:26:09 -08:00
parent 0c2c45c50b
commit c22fdbb36a
20 changed files with 581 additions and 104 deletions

193
.idea/workspace.xml generated
View File

@ -2,15 +2,12 @@
<project version="4">
<component name="ChangeListManager">
<list default="true" id="fd73cd66-e80f-470e-a2ec-e220d3b6b864" name="Default Changelist" comment="">
<change afterPath="$PROJECT_DIR$/Emotional Detector.ipynb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Test Notebook.ipynb" beforeDir="false" afterPath="$PROJECT_DIR$/Test Notebook.ipynb" afterDir="false" />
<change beforePath="$PROJECT_DIR$/css/draw-a-digit.css" beforeDir="false" afterPath="$PROJECT_DIR$/css/draw-a-digit.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/inputs.py" beforeDir="false" afterPath="$PROJECT_DIR$/inputs.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/js/dropzone.js" beforeDir="false" afterPath="$PROJECT_DIR$/js/dropzone.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/js/image-upload-input.js" beforeDir="false" afterPath="$PROJECT_DIR$/js/image-upload-input.js" afterDir="false" />
<change beforePath="$PROJECT_DIR$/preprocessing_utils.py" beforeDir="false" afterPath="$PROJECT_DIR$/preprocessing_utils.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/templates/image_upload_input.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/image_upload_input.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/templates/tmp_html.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/gradio.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/inputs.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/networking.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/outputs.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/preprocessing_utils.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/script.py" beforeDir="false" />
</list>
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
@ -83,7 +80,7 @@
<entry key="html" value="4" />
<entry key="ipynb" value="1" />
<entry key="js" value="5" />
<entry key="py" value="11" />
<entry key="py" value="18" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.open">
@ -93,17 +90,17 @@
<entry key="IPNB" value="1" />
<entry key="JavaScript" value="5" />
<entry key="PLAIN_TEXT" value="1" />
<entry key="Python" value="11" />
<entry key="Python" value="18" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.extensions.edit">
<counts>
<entry key="css" value="11" />
<entry key="dummy" value="10" />
<entry key="dummy" value="19" />
<entry key="gitignore" value="2" />
<entry key="html" value="112" />
<entry key="js" value="272" />
<entry key="py" value="4059" />
<entry key="py" value="4194" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.edit">
@ -111,54 +108,66 @@
<entry key="CSS" value="11" />
<entry key="HTML" value="112" />
<entry key="JavaScript" value="272" />
<entry key="PLAIN_TEXT" value="12" />
<entry key="Python" value="4059" />
<entry key="PLAIN_TEXT" value="21" />
<entry key="Python" value="4194" />
</counts>
</usages-collector>
</session>
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/css/draw-a-digit.css">
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/setup.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1225">
<caret line="96" column="15" selection-start-line="96" selection-start-column="15" selection-end-line="96" selection-end-column="15" />
<state relative-caret-position="525">
<caret line="21" column="17" selection-start-line="21" selection-start-column="17" selection-end-line="21" selection-end-column="17" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/.gitignore">
<entry file="file://$PROJECT_DIR$/gradio/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="175">
<caret line="7" selection-start-line="7" selection-end-line="7" />
<caret line="7" column="19" selection-start-line="7" selection-start-column="19" selection-end-line="7" selection-end-column="19" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/preprocessing_utils.py">
<entry file="file://$PROJECT_DIR$/gradio/inputs.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="336">
<caret line="16" column="31" selection-start-line="16" selection-start-column="31" selection-end-line="16" selection-end-column="31" />
<state relative-caret-position="125">
<caret line="5" column="19" selection-start-line="5" selection-start-column="19" selection-end-line="5" selection-end-column="19" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/gradio.py">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/gradio/networking.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="647">
<caret line="130" column="13" selection-start-line="130" selection-start-column="13" selection-end-line="130" selection-end-column="13" />
<state relative-caret-position="150">
<caret line="6" column="11" selection-start-line="6" selection-start-column="5" selection-end-line="6" selection-end-column="11" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/gradio/outputs.py">
<provider selected="true" editor-type-id="text-editor">
<state>
<folding>
<element signature="e#0#14#0" expanded="true" />
<marker date="1550563754843" expanded="true" signature="1905:2502" ph="..." />
<element signature="e#0#35#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/gradio/preprocessing_utils.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/js/all-io.js">
<provider selected="true" editor-type-id="text-editor">
@ -168,41 +177,6 @@
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/networking.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="486">
<caret line="39" selection-start-line="32" selection-end-line="39" />
<folding>
<element signature="e#0#17#0" expanded="true" />
<marker date="1550562739002" expanded="true" signature="754:937" ph="..." />
<marker date="1550562739002" expanded="true" signature="2659:2661" ph="..." />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/inputs.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="50">
<caret line="2" column="21" selection-start-line="2" selection-start-column="21" selection-end-line="2" selection-end-column="21" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/outputs.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1350">
<caret line="60" selection-start-line="60" selection-end-line="60" />
<folding>
<element signature="e#0#35#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/templates/webcam_input.html">
<provider selected="true" editor-type-id="text-editor">
@ -223,9 +197,6 @@
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>clear</find>
<find>ctx</find>
<find>clear-b</find>
<find>css/</find>
<find>communica</find>
<find>communicate</find>
@ -253,6 +224,9 @@
<find>6002</find>
<find>INITIAL_WEBSOCKET_PORT</find>
<find>print(</find>
<find>inputs.</find>
<find>subprocess</find>
<find>psutil</find>
</findStrings>
<replaceStrings>
<replace>400</replace>
@ -296,6 +270,9 @@
<option value="$PROJECT_DIR$/networking.py" />
<option value="$PROJECT_DIR$/js/all-io.js" />
<option value="$PROJECT_DIR$/gradio.py" />
<option value="$PROJECT_DIR$/gradio/__init__.py" />
<option value="$PROJECT_DIR$/gradio/inputs.py" />
<option value="$PROJECT_DIR$/setup.py" />
</list>
</option>
</component>
@ -335,7 +312,7 @@
<path>
<item name="gradio" type="b2602c69:ProjectViewProjectNode" />
<item name="gradio" type="462c0819:PsiDirectoryNode" />
<item name="js" type="462c0819:PsiDirectoryNode" />
<item name="gradio" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="gradio" type="b2602c69:ProjectViewProjectNode" />
@ -388,7 +365,7 @@
<frame x="-6" y="-6" width="1513" height="1013" extended-state="6" />
<editor active="true" />
<layout>
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.25360826" />
<window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.25360826" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
@ -519,13 +496,6 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="175">
<caret line="7" selection-start-line="7" selection-end-line="7" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/js/webcam-input.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="300">
@ -533,13 +503,6 @@
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/css/draw-a-digit.css">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1225">
<caret line="96" column="15" selection-start-line="96" selection-start-column="15" selection-end-line="96" selection-end-column="15" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/preprocessing_utils.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="336">
@ -577,8 +540,6 @@
<caret line="39" selection-start-line="32" selection-end-line="39" />
<folding>
<element signature="e#0#17#0" expanded="true" />
<marker date="1550562739002" expanded="true" signature="754:937" ph="..." />
<marker date="1550562739002" expanded="true" signature="2659:2661" ph="..." />
</folding>
</state>
</provider>
@ -600,13 +561,69 @@
<entry file="file://$PROJECT_DIR$/gradio.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="647">
<caret line="130" column="13" selection-start-line="130" selection-start-column="13" selection-end-line="130" selection-end-column="13" />
<caret line="130" column="21" selection-start-line="130" selection-start-column="21" selection-end-line="130" selection-end-column="21" />
<folding>
<element signature="e#0#14#0" expanded="true" />
<marker date="1550563754843" expanded="true" signature="1905:2502" ph="..." />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/gradio/script.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/gradio/inputs.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="125">
<caret line="5" column="19" selection-start-line="5" selection-start-column="19" selection-end-line="5" selection-end-column="19" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.gitignore">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="175">
<caret line="7" selection-start-line="7" selection-end-line="7" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/css/draw-a-digit.css">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1225">
<caret line="96" column="15" selection-start-line="96" selection-start-column="15" selection-end-line="96" selection-end-column="15" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/gradio/networking.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="150">
<caret line="6" column="11" selection-start-line="6" selection-start-column="5" selection-end-line="6" selection-end-column="11" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/gradio/outputs.py">
<provider selected="true" editor-type-id="text-editor">
<state>
<folding>
<element signature="e#0#35#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/gradio/preprocessing_utils.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/gradio/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="175">
<caret line="7" column="19" selection-start-line="7" selection-start-column="19" selection-end-line="7" selection-end-column="19" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/setup.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="525">
<caret line="21" column="17" selection-start-line="21" selection-start-column="17" selection-end-line="21" selection-end-column="17" />
</state>
</provider>
</entry>
</component>
</project>

View File

@ -3,9 +3,9 @@ import websockets
import nest_asyncio
import webbrowser
from bs4 import BeautifulSoup
import inputs
import outputs
import networking
from gradio import inputs
from gradio import outputs
from gradio import networking
nest_asyncio.apply()

View File

@ -3,7 +3,7 @@ import base64
from PIL import Image
from io import BytesIO
import numpy as np
import preprocessing_utils
from gradio import preprocessing_utils
class AbstractInput(ABC):
"""

BIN
dist/gradio-0.1.0-py3-none-any.whl vendored Normal file

Binary file not shown.

BIN
dist/gradio-0.1.0.tar.gz vendored Normal file

Binary file not shown.

11
gradio.egg-info/PKG-INFO Normal file
View File

@ -0,0 +1,11 @@
Metadata-Version: 1.0
Name: gradio
Version: 0.1.0
Summary: Python library for easily interacting with trained machine learning models
Home-page: https://github.com/abidlabs/gradio
Author: Abubakar Abid
Author-email: a12d@stanford.edu
License: UNKNOWN
Description: UNKNOWN
Keywords: machine learning,visualization,reproducibility
Platform: UNKNOWN

View File

@ -0,0 +1,12 @@
README.md
setup.py
gradio/__init__.py
gradio/inputs.py
gradio/networking.py
gradio/outputs.py
gradio/preprocessing_utils.py
gradio.egg-info/PKG-INFO
gradio.egg-info/SOURCES.txt
gradio.egg-info/dependency_links.txt
gradio.egg-info/requires.txt
gradio.egg-info/top_level.txt

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,6 @@
numpy
websockets
nest_asyncio
beautifulsoup4Pillow
requests
psutil

View File

@ -0,0 +1 @@
gradio

131
gradio/__init__.py Normal file
View File

@ -0,0 +1,131 @@
import asyncio
import websockets
import nest_asyncio
import webbrowser
from bs4 import BeautifulSoup
from gradio import inputs
from gradio import outputs
from gradio import networking
nest_asyncio.apply()
LOCALHOST_IP = '127.0.0.1'
INITIAL_WEBSOCKET_PORT = 9200
TRY_NUM_PORTS = 100
class Interface():
"""
"""
build_template_path = 'templates/tmp_html.html'
def __init__(self, input, output, model, model_type, preprocessing_fn=None, postprocessing_fn=None):
"""
:param model_type: what kind of trained model, can be 'keras' or 'sklearn'.
:param model_obj: the model object, such as a sklearn classifier or keras model.
:param model_params: additional model parameters.
"""
self.input_interface = inputs.registry[input](preprocessing_fn)
self.output_interface = outputs.registry[output](postprocessing_fn)
self.model_type = model_type
self.model_obj = model
def _build_template(self):
input_template_path = self.input_interface._get_template_path()
output_template_path = self.output_interface._get_template_path()
input_page = open(input_template_path)
output_page = open(output_template_path)
input_soup = BeautifulSoup(input_page.read(), features="html.parser")
output_soup = BeautifulSoup(output_page.read(), features="html.parser")
all_io_url = 'templates/all_io.html'
all_io_page = open(all_io_url)
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(self.build_template_path, "w")
f.write(str(all_io_soup.prettify))
return self.build_template_path
def _set_socket_url_in_js(self, socket_url):
with open('js/all-io.js') as fin:
lines = fin.readlines()
lines[0] = 'var NGROK_URL = "{}"\n'.format(socket_url.replace('http', 'ws'))
with open('js/all-io.js', 'w') as fout:
for line in lines:
fout.write(line)
def _set_socket_port_in_js(self, socket_port):
with open('js/all-io.js') as fin:
lines = fin.readlines()
lines[1] = 'var SOCKET_PORT = {}\n'.format(socket_port)
with open('js/all-io.js', 'w') as fout:
for line in lines:
fout.write(line)
def predict(self, array):
if self.model_type=='sklearn':
return self.model_obj.predict(array)
elif self.model_type=='keras':
return self.model_obj.predict(array)
elif self.model_type=='func':
return self.model_obj(array)
else:
raise ValueError('model_type must be one of: "sklearn" or "keras" or "func".')
async def communicate(self, websocket, path):
"""
Method that defines how this interface communicates with the websocket.
:param websocket: a Websocket object used to communicate with the interface frontend
:param path: ignored
"""
while True:
try:
msg = await websocket.recv()
processed_input = self.input_interface._pre_process(msg)
prediction = self.predict(processed_input)
processed_output = self.output_interface._post_process(prediction)
await websocket.send(str(processed_output))
except websockets.exceptions.ConnectionClosed:
pass
def launch(self, share_link=True):
"""
Standard method shared by interfaces that launches a websocket at a specified IP address.
"""
networking.kill_processes([4040, 4041])
server_port = networking.start_simple_server()
path_to_server = 'http://localhost:{}/'.format(server_port)
path_to_template = self._build_template()
ports_in_use = networking.get_ports_in_use()
for i in range(TRY_NUM_PORTS):
if not ((INITIAL_WEBSOCKET_PORT + i) in ports_in_use):
break
else:
raise OSError("All ports from {} to {} are in use. Please close a port.".format(
INITIAL_WEBSOCKET_PORT, INITIAL_WEBSOCKET_PORT + TRY_NUM_PORTS))
start_server = websockets.serve(self.communicate, LOCALHOST_IP, INITIAL_WEBSOCKET_PORT + i)
self._set_socket_port_in_js(INITIAL_WEBSOCKET_PORT + i)
if share_link:
site_ngrok_url = networking.setup_ngrok(server_port)
socket_ngrok_url = networking.setup_ngrok(INITIAL_WEBSOCKET_PORT, api_url=networking.NGROK_TUNNELS_API_URL2)
self._set_socket_url_in_js(socket_ngrok_url)
print("NOTE: Gradio is in beta stage, please report all bugs to: a12d@stanford.edu")
print("Model available locally at: {}".format(path_to_server + path_to_template))
print("Model available publicly for 8 hours at: {}".format(site_ngrok_url + '/' + path_to_template))
asyncio.get_event_loop().run_until_complete(start_server)
try:
asyncio.get_event_loop().run_forever()
except RuntimeError: # Runtime errors are thrown in jupyter notebooks because of async.
pass
webbrowser.open(path_to_server + path_to_template)

94
gradio/inputs.py Normal file
View File

@ -0,0 +1,94 @@
from abc import ABC, abstractmethod
import base64
from PIL import Image
from io import BytesIO
import numpy as np
from gradio import preprocessing_utils
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, preprocessing_fn=None):
if preprocessing_fn is not None:
self._pre_process = preprocessing_fn
super().__init__()
@abstractmethod
def _get_template_path(self):
"""
All interfaces should define a method that returns the path to its template.
"""
pass
@abstractmethod
def _pre_process(self):
"""
All interfaces should define a method that returns the path to its template.
"""
pass
class Sketchpad(AbstractInput):
def _get_template_path(self):
return 'templates/sketchpad_input.html'
def _pre_process(self, imgstring):
"""
"""
content = imgstring.split(';')[1]
image_encoded = content.split(',')[1]
body = base64.decodebytes(image_encoded.encode('utf-8'))
im = Image.open(BytesIO(base64.b64decode(image_encoded))).convert('L')
im = preprocessing_utils.resize_and_crop(im, (28, 28))
array = np.array(im).flatten().reshape(1, 28, 28, 1)
return array
class Webcam(AbstractInput):
def _get_template_path(self):
return 'templates/webcam_input.html'
def _pre_process(self, imgstring):
"""
"""
content = imgstring.split(';')[1]
image_encoded = content.split(',')[1]
body = base64.decodebytes(image_encoded.encode('utf-8'))
im = Image.open(BytesIO(base64.b64decode(image_encoded))).convert('L')
im = preprocessing_utils.resize_and_crop(im, (48, 48))
array = np.array(im).flatten().reshape(1, 48, 48, 1)
return array
class Textbox(AbstractInput):
def _get_template_path(self):
return 'templates/textbox_input.html'
def _pre_process(self, text):
"""
"""
return text
class ImageUpload(AbstractInput):
def _get_template_path(self):
return 'templates/image_upload_input.html'
def _pre_process(self, imgstring):
"""
"""
content = imgstring.split(';')[1]
image_encoded = content.split(',')[1]
body = base64.decodebytes(image_encoded.encode('utf-8'))
im = Image.open(BytesIO(base64.b64decode(image_encoded))).convert('L')
im = preprocessing_utils.resize_and_crop(im, (48, 48))
array = np.array(im).flatten().reshape(1, 48, 48, 1)
return array
registry = {cls.__name__.lower(): cls for cls in AbstractInput.__subclasses__()}

76
gradio/networking.py Normal file
View File

@ -0,0 +1,76 @@
import subprocess
import requests
import zipfile
import io
import sys
import os
from psutil import process_iter, AccessDenied
from signal import SIGTERM # or SIGKILL
INITIAL_PORT_VALUE = 7860
TRY_NUM_PORTS = 100
LOCALHOST_PREFIX = 'localhost:'
NGROK_TUNNELS_API_URL = "http://localhost:4040/api/tunnels" # TODO(this should be captured from output)
NGROK_TUNNELS_API_URL2 = "http://localhost:4041/api/tunnels" # TODO(this should be captured from output)
NGROK_ZIP_URLS = {
"linux": "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip",
"darwin": "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip",
"win32": "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-amd64.zip",
}
def get_ports_in_use():
ports_in_use = []
for proc in process_iter():
for conns in proc.connections(kind='inet'):
ports_in_use.append(conns.laddr.port)
return ports_in_use
def start_simple_server():
# TODO(abidlabs): increment port number until free port is found
ports_in_use = get_ports_in_use()
for i in range(TRY_NUM_PORTS):
if not((INITIAL_PORT_VALUE + i) in ports_in_use):
break
else:
raise OSError("All ports from {} to {} are in use. Please close a port.".format(
INITIAL_PORT_VALUE, INITIAL_PORT_VALUE + TRY_NUM_PORTS))
subprocess.Popen(['python', '-m', 'http.server', str(INITIAL_PORT_VALUE + i)])
return INITIAL_PORT_VALUE + i
def download_ngrok():
try:
zip_file_url = NGROK_ZIP_URLS[sys.platform]
except KeyError:
print("Sorry, we don't currently support your operating system, please leave us a note on GitHub, and we'll look into it!")
return
r = requests.get(zip_file_url)
z = zipfile.ZipFile(io.BytesIO(r.content))
z.extractall()
def setup_ngrok(local_port, api_url=NGROK_TUNNELS_API_URL):
if not(os.path.isfile('ngrok.exe')):
download_ngrok()
subprocess.Popen(['ngrok', 'http', str(local_port)])
r = requests.get(api_url)
for tunnel in r.json()['tunnels']:
if LOCALHOST_PREFIX + str(local_port) in tunnel['config']['addr']:
return tunnel['public_url']
raise RuntimeError("Not able to retrieve ngrok public URL")
def kill_processes(process_ids):
for proc in process_iter():
for conns in proc.connections(kind='inet'):
if conns.laddr.port in process_ids:
try:
proc.send_signal(SIGTERM) # or SIGKILL
except AccessDenied:
print("Unable to kill process running on port {}, please kill manually.".format(conns.laddr.port))

63
gradio/outputs.py Normal file
View File

@ -0,0 +1,63 @@
from abc import ABC, abstractmethod
import numpy as np
class AbstractOutput(ABC):
"""
An abstract class for defining the methods that all gradio inputs should have.
When this is subclassed, it is automatically added to the registry
"""
def __init__(self, postprocessing_fn=None):
"""
"""
if postprocessing_fn is not None:
self._post_process = postprocessing_fn
super().__init__()
@abstractmethod
def _get_template_path(self):
"""
All interfaces should define a method that returns the path to its template.
"""
pass
@abstractmethod
def _post_process(self):
"""
All interfaces should define a method that returns the path to its template.
"""
pass
class Class(AbstractOutput):
def _get_template_path(self):
return 'templates/class_output.html'
def _post_process(self, prediction):
"""
"""
if isinstance(prediction, np.ndarray):
prediction = prediction.squeeze()
if prediction.size == 1:
return prediction
else:
return prediction.argmax()
elif isinstance(prediction, str):
return prediction
else:
raise ValueError("Unable to post-process model prediction.")
class Textbox(AbstractOutput):
def _get_template_path(self):
return 'templates/textbox_output.html'
def _post_process(self, prediction):
"""
"""
return prediction
registry = {cls.__name__.lower(): cls for cls in AbstractOutput.__subclasses__()}

View File

@ -0,0 +1,53 @@
from PIL import Image
def resize_and_crop(img, size, crop_type='top'):
"""
Resize and crop an image to fit the specified size.
args:
img_path: path for the image to resize.
modified_path: path to store the modified image.
size: `(width, height)` tuple.
crop_type: can be 'top', 'middle' or 'bottom', depending on this
value, the image will cropped getting the 'top/left', 'midle' or
'bottom/rigth' of the image to fit the size.
raises:
Exception: if can not open the file in img_path of there is problems
to save the image.
ValueError: if an invalid `crop_type` is provided.
"""
# Get current and desired ratio for the images
img_ratio = img.size[0] / float(img.size[1])
ratio = size[0] / float(size[1])
# The image is scaled/cropped vertically or horizontally depending on the ratio
if ratio > img_ratio:
img = img.resize((size[0], size[0] * img.size[1] / img.size[0]),
Image.ANTIALIAS)
# Crop in the top, middle or bottom
if crop_type == 'top':
box = (0, 0, img.size[0], size[1])
elif crop_type == 'middle':
box = (0, (img.size[1] - size[1]) / 2, img.size[0], (img.size[1] + size[1]) / 2)
elif crop_type == 'bottom':
box = (0, img.size[1] - size[1], img.size[0], img.size[1])
else:
raise ValueError('ERROR: invalid value for crop_type')
img = img.crop(box)
elif ratio < img_ratio:
img = img.resize((size[1] * img.size[0] // img.size[1], size[1]),
Image.ANTIALIAS)
# Crop in the top, middle or bottom
if crop_type == 'top':
box = (0, 0, size[0], img.size[1])
elif crop_type == 'middle':
box = ((img.size[0] - size[0]) / 2, 0, (img.size[0] + size[0]) / 2, img.size[1])
elif crop_type == 'bottom':
box = (img.size[0] - size[0], 0, img.size[0], img.size[1])
else:
raise ValueError('ERROR: invalid value for crop_type')
img = img.crop(box)
else:
img = img.resize((size[0], size[1]),
Image.ANTIALIAS)
# If the scale is the same, we do not need to crop
return img

View File

@ -1,12 +0,0 @@
from bs4 import BeautifulSoup
sketchpad_url = 'templates/sketchpad_input.html'
all_io_url = 'templates/all_io.html'
sketchpad_page = open(sketchpad_url)
all_io_page = open(all_io_url)
sketchpad_soup = BeautifulSoup(sketchpad_page.read())
all_io_soup = BeautifulSoup(all_io_page.read())
input_tag = all_io_soup.find("div", {"id": "input"})
input_tag.replace_with(sketchpad_soup)
f = open("templates/tmp_html.html", "w")
f.write(str(all_io_soup.prettify))

24
setup.py Normal file
View File

@ -0,0 +1,24 @@
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setup(
name = 'gradio',
version = '0.1.0',
description = 'Python library for easily interacting with trained machine learning models',
author = 'Abubakar Abid',
author_email = 'a12d@stanford.edu',
url = 'https://github.com/abidlabs/gradio',
packages=['gradio'],
keywords = ['machine learning', 'visualization', 'reproducibility'],
install_requires=[
'numpy',
'websockets',
'nest_asyncio',
'beautifulsoup4'
'Pillow',
'requests',
'psutil',
],
)