updated examples layout and flagging output tracking

This commit is contained in:
Ali Abid 2021-02-23 11:44:09 -08:00
parent d2a9da1490
commit 953ddffd06
17 changed files with 352 additions and 114 deletions

View File

@ -1,3 +1,7 @@
import os
import shutil
from gradio import processing_utils
class Component():
"""
A class for defining the methods that all gradio input and output components should have.
@ -19,12 +23,34 @@ class Component():
"""
return {}
def rebuild(self, dir, data):
def save_flagged(self, dir, label, data):
"""
All interfaces should define a method that rebuilds the flagged input when it's passed back (i.e. rebuilds image from base64)
Saves flagged data from component
"""
return data
def restore_flagged(self, data):
"""
Restores flagged data from logs
"""
return data
def save_flagged_file(self, dir, label, data):
file = processing_utils.decode_base64_to_file(data)
old_file_name = file.name
output_dir = os.path.join(dir, label)
if os.path.exists(output_dir):
file_index = len(os.listdir(output_dir))
else:
os.mkdir(output_dir)
file_index = 0
new_file_name = str(file_index)
if "." in old_file_name:
uploaded_format = old_file_name.split(".")[-1].lower()
new_file_name += "." + uploaded_format
shutil.move(old_file_name, os.path.join(dir, label, new_file_name))
return label + "/" + new_file_name
@classmethod
def get_all_shortcut_implementations(cls):
shortcuts = {}

View File

@ -7,6 +7,7 @@ automatically added to a registry, which allows them to be easily referenced in
import datetime
import json
import os
import shutil
import time
import warnings
from gradio.component import Component
@ -466,6 +467,14 @@ class CheckboxGroup(InputComponent):
else:
raise ValueError("Unknown type: " + str(self.type) + ". Please choose from: 'value', 'index'.")
def save_flagged(self, dir, label, data):
"""
Returns: (List[str]])
"""
return json.dumps(data)
def restore_flagged(self, data):
return json.loads(data)
class Radio(InputComponent):
@ -713,15 +722,11 @@ class Image(InputComponent):
im = processing_utils.resize_and_crop(im, (shape[0], shape[1]))
return np.asarray(im).flatten()
def rebuild(self, dir, data):
def save_flagged(self, dir, label, data):
"""
Default rebuild method to decode a base64 image
Returns: (str) path to image file
"""
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
return self.save_flagged_file(dir, label, data)
class Video(InputComponent):
@ -766,6 +771,12 @@ class Video(InputComponent):
def preprocess_example(self, x):
return processing_utils.encode_file_to_base64(x)
def save_flagged(self, dir, label, data):
"""
Returns: (str) path to video file
"""
return self.save_flagged_file(dir, label, data)
class Audio(InputComponent):
"""
Component accepts audio input files.
@ -865,6 +876,12 @@ class Audio(InputComponent):
else:
raise ValueError("Unknown type: " + str(self.type) + ". Please choose from: 'numpy', 'mfcc', 'file'.")
def save_flagged(self, dir, label, data):
"""
Returns: (str) path to audio file
"""
return self.save_flagged_file(dir, label, data)
class File(InputComponent):
"""
@ -906,6 +923,12 @@ class File(InputComponent):
def embed(self, x):
raise NotImplementedError("File doesn't currently support embeddings")
def save_flagged(self, dir, label, data):
"""
Returns: (str) path to file
"""
return self.save_flagged_file(dir, label, data["data"])
class Dataframe(InputComponent):
"""
@ -1000,6 +1023,15 @@ class Dataframe(InputComponent):
def embed(self, x):
raise NotImplementedError("DataFrame doesn't currently support embeddings")
def save_flagged(self, dir, label, data):
"""
Returns: (List[List[Union[str, float]]]) 2D array
"""
return json.dumps(data)
def restore_flagged(self, data):
return json.loads(data)
#######################
# DEPRECATED COMPONENTS
@ -1050,7 +1082,7 @@ class Sketchpad(InputComponent):
def process_example(self, example):
return processing_utils.encode_file_to_base64(example)
def rebuild(self, dir, data):
def save_flagged(self, dir, label, data):
"""
Default rebuild method to decode a base64 image
"""
@ -1089,7 +1121,7 @@ class Webcam(InputComponent):
im, (self.image_width, self.image_height))
return np.array(im)
def rebuild(self, dir, data):
def save_flagged(self, dir, label, data):
"""
Default rebuild method to decode a base64 image
"""
@ -1131,7 +1163,7 @@ class Microphone(InputComponent):
return signal
def rebuild(self, dir, data):
def save_flagged(self, dir, label, data):
inp = data.split(';')[1].split(',')[1]
wav_obj = base64.b64decode(inp)
timestamp = datetime.datetime.now()

View File

@ -222,14 +222,8 @@ class Interface:
except ValueError:
pass
if self.examples is not None:
processed_examples = []
for example_set in self.examples:
processed_set = []
for iface, example in zip(self.input_interfaces, example_set):
processed_set.append(example)
processed_examples.append(processed_set)
config["examples"] = processed_examples
if self.examples is not None and not isinstance(self.examples, str):
config["examples"] = self.examples
return config
def run_prediction(self, processed_input, return_duration=False):

View File

@ -82,6 +82,8 @@ def get_first_available_port(initial, final):
@app.route("/", methods=["GET"])
def main():
if isinstance(app.interface.examples, str):
return redirect("/from_dir/" + app.interface.examples)
return render_template("index.html",
config=app.interface.config,
vendor_prefix=(GRADIO_STATIC_ROOT if app.interface.share else ""),
@ -100,13 +102,16 @@ def main_from_dir(path):
with open(log_file) as logs:
examples = list(csv.reader(logs))
examples = examples[1:] #remove header
input_examples = [example[:len(app.interface.input_interfaces)] for example in examples]
for i, example in enumerate(examples):
for j, (interface, cell) in enumerate(zip(app.interface.input_interfaces + app.interface.output_interfaces, example)):
examples[i][j] = interface.restore_flagged(cell)
examples = [example[:len(app.interface.input_interfaces) + len(app.interface.output_interfaces)] for example in examples]
return render_template("index.html",
config=app.interface.config,
vendor_prefix=(GRADIO_STATIC_ROOT if app.interface.share else ""),
css=app.interface.css,
path=path,
examples=input_examples
examples=examples
)
@ -217,11 +222,13 @@ def predict_examples():
return jsonify(output)
def flag_data(data):
def flag_data(input_data, output_data):
flag_path = os.path.join(app.cwd, app.interface.flagging_dir)
output = [app.interface.input_interfaces[i].rebuild(
flag_path, component_data)
for i, component_data in enumerate(data)]
csv_data = []
for i, interface in enumerate(app.interface.input_interfaces):
csv_data.append(interface.save_flagged(flag_path, app.interface.config["input_interfaces"][i][1]["label"], input_data[i]))
for i, interface in enumerate(app.interface.output_interfaces):
csv_data.append(interface.save_flagged(flag_path, app.interface.config["output_interfaces"][i][1]["label"], output_data[i]))
log_fp = "{}/log.csv".format(flag_path)
is_new = not os.path.exists(log_fp)
@ -230,15 +237,16 @@ def flag_data(data):
writer = csv.writer(csvfile)
if is_new:
headers = [interface[1]["label"] for interface in app.interface.config["input_interfaces"]]
headers += [interface[1]["label"] for interface in app.interface.config["output_interfaces"]]
writer.writerow(headers)
writer.writerow(output)
writer.writerow(csv_data)
@app.route("/api/flag/", methods=["POST"])
def flag():
log_feature_analytics('flag')
data = request.json['data']['input_data']
flag_data(data)
input_data, output_data = request.json['data']['input_data'], request.json['data']['output_data']
flag_data(input_data, output_data)
return jsonify(success=True)

View File

@ -118,12 +118,21 @@ class Label(OutputComponent):
"label": {},
}
def rebuild(self, dir, data):
def save_flagged(self, dir, label, data):
"""
Default rebuild method for label
Returns: (Union[str, Dict[str, number]]): Either a string representing the main category label, or a dictionary with category keys mapping to confidence levels.
"""
# return json.loads(data)
return data
if "confidences" in data:
return json.dumps({example["label"]: example["confidence"] for example in data["confidences"]})
else:
return data["label"]
def restore_flagged(self, data):
try:
data = json.loads(data)
return data
except:
return data
class Image(OutputComponent):
'''
@ -186,15 +195,12 @@ class Image(OutputComponent):
raise ValueError("Unknown type: " + dtype + ". Please choose from: 'numpy', 'pil', 'file', 'plot'.")
return out_y, coordinates
def rebuild(self, dir, data):
def save_flagged(self, dir, label, data):
"""
Default rebuild method to decode a base64 image
Returns: (str) path to image file
"""
im = processing_utils.decode_base64_to_image(data)
timestamp = datetime.datetime.now()
filename = 'output_{}_{}.png'.format(self.label, timestamp.strftime("%Y-%m-%d-%H-%M-%S"))
im.save('{}/{}'.format(dir, filename), 'PNG')
return filename
return self.save_flagged_file(dir, label, data[0])
class Video(OutputComponent):
'''
@ -218,6 +224,12 @@ class Video(OutputComponent):
def postprocess(self, y):
return processing_utils.encode_file_to_base64(y, type="video")
def save_flagged(self, dir, label, data):
"""
Returns: (str) path to image file
"""
return self.save_flagged_file(dir, label, data)
class KeyValues(OutputComponent):
'''
@ -246,6 +258,12 @@ class KeyValues(OutputComponent):
return {
"key_values": {},
}
def save_flagged(self, dir, label, data):
return json.dumps(data)
def restore_flagged(self, data):
return json.loads(data)
class HighlightedText(OutputComponent):
@ -279,6 +297,12 @@ class HighlightedText(OutputComponent):
def postprocess(self, y):
return y
def save_flagged(self, dir, label, data):
return json.dumps(data)
def restore_flagged(self, data):
return json.loads(data)
class Audio(OutputComponent):
'''
@ -316,6 +340,12 @@ class Audio(OutputComponent):
else:
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'numpy', 'file'.")
def save_flagged(self, dir, label, data):
"""
Returns: (str) path to audio file
"""
return self.save_flagged_file(dir, label, data)
class JSON(OutputComponent):
'''
@ -343,6 +373,12 @@ class JSON(OutputComponent):
"json": {},
}
def save_flagged(self, dir, label, data):
return json.dumps(data)
def restore_flagged(self, data):
return json.loads(data)
class HTML(OutputComponent):
'''
@ -392,6 +428,12 @@ class File(OutputComponent):
"data": processing_utils.encode_file_to_base64(y, header=False)
}
def save_flagged(self, dir, label, data):
"""
Returns: (str) path to image file
"""
return self.save_flagged_file(dir, label, data["data"])
class Dataframe(OutputComponent):
"""
@ -446,3 +488,12 @@ class Dataframe(OutputComponent):
return {"data": y}
else:
raise ValueError("Unknown type: " + self.type + ". Please choose from: 'pandas', 'numpy', 'array'.")
def save_flagged(self, dir, label, data):
"""
Returns: (List[List[Union[str, float]]]) 2D array
"""
return json.dumps(data["data"])
def restore_flagged(self, data):
return json.loads(data)

View File

@ -68,13 +68,16 @@ def resize_and_crop(img, size, crop_type='center'):
##################
def decode_base64_to_binary(encoding):
header, data = encoding.split(",")
header = header[5:]
if ";base64" in header:
header = header[0:header.index(";base64")]
extension = None
if "/" in header:
extension = header[header.index("/") + 1:]
if "," in encoding:
header, data = encoding.split(",")
header = header[5:]
if ";base64" in header:
header = header[0:header.index(";base64")]
if "/" in header:
extension = header[header.index("/") + 1:]
else:
data = encoding
return base64.b64decode(data), extension
def decode_base64_to_file(encoding):

View File

@ -74,15 +74,40 @@ h4 {
.close_explain {
cursor: pointer;
}
.examples > button {
.backward {
display: inline-block;
-moz-transform: scale(-1, 1);
-webkit-transform: scale(-1, 1);
transform: scale(-1, 1);
}
.examples_control button {
padding: 8px 16px;
border-radius: 2px;
margin-right: 4px;
margin-right: 8px;
background-color: whitesmoke;
}
.examples_control {
display: flex;
}
.examples_control > div {
display: flex;
align-items: stretch;
}
.examples_control button small {
display: block;
font-weight: bold;
}
.examples_control_right {
padding-left: 8px;
border-left: solid 2px whitesmoke;
}
.examples_control_right .current {
background-color: #e67e22;
color: white;
}
.examples > table {
border-collapse: collapse;
font-family: monospace;
padding: 8px;
background-color: whitesmoke;
border-right: solid 4px whitesmoke;
@ -90,6 +115,20 @@ h4 {
border-bottom: solid 4px whitesmoke;
margin-top: 8px;
}
.examples > table.gallery {
background-color: white;
border: none;
}
.examples > table.gallery > thead {
display: none;
}
.examples > table.gallery > tbody > tr {
padding: 4px;
border-radius: 4px;
margin: 0 8px 8px 0;
background-color: whitesmoke;
display: inline-block;
}
.examples > table th {
padding: 8px 16px;
text-align: left;
@ -118,7 +157,7 @@ h4 {
background-color: lightgray;
}
.examples_body > tr.current_example {
background-color: #ffb573;
background-color: lightgray !important;
}
#credit {
text-align: center;

View File

@ -48,13 +48,25 @@ function gradio(config, fn, target, example_file_path) {
</div>
<div class="examples invisible">
<h4>Examples</small></h4>
<button class="run_examples examples-content">Run All</button>
<button class="load_prev examples-content">Load Previous <em>(CTRL + &larr;)</em></button>
<button class="load_next examples-content">Load Next <em>(CTRL + &rarr;)</em></button>
<button class="order_similar examples-content embedding">Order by Similarity</button>
<button class="view_embeddings examples-content embedding">View Embeddings</button>
<button class="update_embeddings embeddings-content invisible">Update Embeddings</button>
<button class="view_examples embeddings-content invisible">View Examples</button>
<div class="examples_control">
<div class="examples_control_left">
<button class="run_examples examples-content">Run All</button>
<button class="load_prev examples-content">Load Previous <small>CTRL + <span class="backward">&#10140;</span></small></button>
<button class="load_next examples-content">Load Next <small>CTRL + &#10140;</small></button>
<button class="order_similar examples-content embedding">Order by Similarity</button>
<button class="view_embeddings examples-content embedding">View Embeddings</button>
<button class="update_embeddings embeddings-content invisible">Update Embeddings</button>
<button class="view_examples embeddings-content invisible">View Examples</button>
</div>
<div class="examples_control_right">
<button class="table_examples">
<svg width="40" height="24"><rect x="0" y="0" width="40" height="6"></rect><rect x="0" y="9" width="40" height="6"></rect><rect x="0" y="18" width="40" height="6"></rect></svg>
</button>
<button class="gallery_examples current">
<svg width="40" height="24"><rect x="0" y="0" width="18" height="40"></rect><rect x="22" y="0" width="18" height="40"></rect></svg>
</button>
</div>
</div>
<div class="pages invisible">Page:</div>
<table class="examples-content">
</table>
@ -201,7 +213,7 @@ function gradio(config, fn, target, example_file_path) {
target.find(".clear").click(clear_all);
if (!config["allow_embedding"]) {
target.find(".embedding").css("visibility", "hidden");
target.find(".embedding").hide();
}
if (!config["allow_screenshot"] && config["allow_flagging"] !== true && !config["allow_interpretation"]) {
target.find(".screenshot, .record, .flag, .interpret").css("visibility", "hidden");
@ -224,20 +236,24 @@ function gradio(config, fn, target, example_file_path) {
target.find(".interpretation_explained .close_explain").click(function() {
target.find(".interpretation_explained").remove();
});
if (config["examples"]) {
target.find(".examples").removeClass("invisible");
let html = "<thead>"
for (let i = 0; i < config["input_interfaces"].length; i++) {
label = config["input_interfaces"][i][1]["label"];
html += "<th>" + label + "</th>";
}
}
}
}
function load_example(example_id) {
clear_all();
if (!(example_id in config["examples"])) {
return;
}
for (let [i, value] of config["examples"][example_id].entries()) {
input_interfaces[i].load_example(value);
if (i < input_interfaces.length) {
input_interfaces[i].load_example(value);
} else if (i - input_interfaces.length < output_interfaces.length) {
let output_interface = output_interfaces[i - input_interfaces.length];
if ("load_example" in output_interface) {
output_interface.load_example(value);
} else {
output_interface.output(value)
}
}
};
if (io_master.loaded_examples && example_id in io_master.loaded_examples) {
io_master.output({"data": io_master.loaded_examples[example_id]});
@ -293,8 +309,15 @@ function gradio(config, fn, target, example_file_path) {
html += "<tr row=" + example_id + ">";
for (let [j, col] of example.entries()) {
let new_col = JSON.parse(JSON.stringify(col))
if (input_interfaces[j].load_example_preview) {
new_col = input_interfaces[j].load_example_preview(new_col);
if (j < input_interfaces.length) {
if (input_interfaces[j].load_example_preview) {
new_col = input_interfaces[j].load_example_preview(new_col);
}
} else {
let k = j - input_interfaces.length;
if (k < output_interfaces.length && output_interfaces[k].load_example_preview) {
new_col = output_interfaces[k].load_example_preview(new_col);
}
}
html += "<td>" + new_col + "</td>";
}
@ -316,9 +339,13 @@ function gradio(config, fn, target, example_file_path) {
if (config["examples"]) {
target.find(".examples").removeClass("invisible");
let html = "<thead>"
for (let i = 0; i < config["input_interfaces"].length; i++) {
label = config["input_interfaces"][i][1]["label"];
html += "<th>" + label + "</th>";
for (let input_interface of config["input_interfaces"]) {
html += "<th>" + input_interface[1]["label"] + "</th>";
}
if (config["examples"].length > 0 && config["examples"][0].length > config["input_interfaces"].length) {
for (let output_interface of config["output_interfaces"]) {
html += "<th>" + output_interface[1]["label"] + "</th>";
}
}
html += "</thead>";
html += "<tbody class='examples_body'></tbody>";
@ -346,6 +373,23 @@ function gradio(config, fn, target, example_file_path) {
io_master.current_page = page_num;
load_page();
})
set_table_mode = function() {
target.find(".examples-content").removeClass("gallery");
target.find(".examples_control_right button").removeClass("current");
target.find(".table_examples").addClass("current");
}
set_gallery_mode = function() {
target.find(".examples-content").addClass("gallery");
target.find(".examples_control_right button").removeClass("current");
target.find(".gallery_examples").addClass("current");
}
target.on("click", ".table_examples", set_table_mode);
target.on("click", ".gallery_examples", set_gallery_mode);
if (config["examples"].length > 0 && config["examples"][0].length > 1) {
set_table_mode();
} else {
set_gallery_mode();
}
target.find(".load_prev").click(prev_example);
target.find(".load_next").click(next_example);
target.find(".order_similar").click(function() {
@ -518,7 +562,7 @@ function gradio(config, fn, target, example_file_path) {
target.find(".flag").click(function() {
if (io_master.last_output) {
target.find(".flag").addClass("flagged");
window.setTimeout(() => {target.find(".flag").removeClass("flagged");}, 100);
window.setTimeout(() => {target.find(".flag").removeClass("flagged");}, 500);
io_master.flag();
}
})

View File

@ -81,8 +81,8 @@ const dataframe_input = {
interpretation_logic: "Highlights the output contribution of each cell in dataframe.",
load_example_preview: function(data) {
let data_copy = [];
for (let row of data.splice(0,3)) {
new_row = row.splice(0,3)
for (let row of data.slice(0,3)) {
new_row = row.slice(0,3)
if (row.length > 3) {
new_row.push("...");
}

View File

@ -80,29 +80,14 @@ const video_input = {
io.state = "VIDEO_LOADED"
}
},
load_example_preview: function(data) {
return "<video src='"+this.io_master.example_file_path+data+"' height=100>"
},
load_example: function(example_data) {
example_data = this.io_master.example_file_path + example_data;
let io = this;
toDataURL(example_data, function(data) {
if (io.source == "canvas") {
io.clear();
let ctx = this.context;
var img = new Image;
let dimension = io.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_video_data(data, /*update_editor=*/true);
io.state = "VIDEO_LOADED";
}
io.target.find(".upload_zone").hide();
io.target.find(".image_display").removeClass("hide");
io.set_video_data(data, /*update_editor=*/true);
io.state = "VIDEO_LOADED";
})
}
}

View File

@ -31,7 +31,14 @@ const audio_output = {
this.wavesurfer.stop();
}
},
load_example_preview: function(data) {
return "[audio]";
load_example: function(example_data) {
example_data = this.io_master.example_file_path + example_data;
let io = this;
if (io.state == "NO_AUDIO" || io.state == "RECORDED") {
io.clear();
toDataURL(example_data, function(data) {
io.output(data);
})
}
},
}

View File

@ -24,11 +24,14 @@ const dataframe_output = {
jexcel.destroy(this.target.find(".dataframe")[0]);
this.table = null;
},
load_example: function(data) {
this.output({"data": data});
},
load_example_preview: function(data) {
data = JSON.parse(JSON.stringify(data["data"]))
data = JSON.parse(JSON.stringify(data))
let data_copy = [];
for (let row of data.splice(0,3)) {
new_row = row.splice(0,3)
for (let row of data.slice(0,3)) {
new_row = row.slice(0,3)
if (row.length > 3) {
new_row.push("...");
}

View File

@ -25,7 +25,12 @@ const file_output = {
.removeAttr("href")
.removeAttr("download");
},
load_example_preview: function(data) {
return data.name;
},
load_example: function(example_data) {
example_path = this.io_master.example_file_path + example_data;
this.target.find(".file_name").text(example_data);
this.target.find(".file_size").empty();
this.target.find(".interface_mini_box")
.attr("href", example_path)
.attr("download", example_data);
}
}

View File

@ -46,7 +46,16 @@ const image_output = {
this.target.find(".output_image").attr('src', "")
},
load_example_preview: function(data) {
return "<img src='"+data[0]+"' height=100>"
data = this.io_master.example_file_path + data;
return "<img src='"+data+"' height=100>"
},
load_example: function(example_data) {
example_data = this.io_master.example_file_path + example_data;
let io = this;
toDataURL(example_data, function(data) {
io.target.find(".upload_zone").hide();
io.target.find(".image_display").removeClass("hide");
io.output([data, []]);
})
}
}

View File

@ -22,15 +22,42 @@ const label_output = {
}
}
},
load_example: function(data) {
this.output(this.convert_example_to_output(data));
},
load_example_preview: function(data) {
if ("confidences" in data) {
for (let confidence_set of data["confidences"]) {
if (confidence_set["label"] == data["label"]) {
return data["label"] + " (" + (100*confidence_set["confidence"]).toFixed(1) + "%)";
let output = this.convert_example_to_output(data);
if ("confidences" in output) {
if (typeof data == "string") {
try {
data = JSON.parse(data);
} catch (e) {
return output["label"]
}
}
return output["label"] + " (" + (100 * data[output["label"]]).toFixed(2) + "%)";
}
return data["label"];
return output["label"]
},
convert_example_to_output: function(data) {
if (typeof data == "string") {
try {
data = JSON.parse(data);
} catch (e) {
return {"label": data};
}
}
let [max_label, max_confidence] = ["", 0]
let output = {"confidences": []}
for (let [label, confidence] of Object.entries(data)) {
output["confidences"].push({"label": label, "confidence": confidence});
if (confidence > max_confidence) {
max_confidence = confidence;
max_label = label;
}
}
output["label"] = max_label;
return output;
},
clear: function() {
this.target.find(".output_class").empty();

View File

@ -11,7 +11,7 @@ const textbox_output = {
this.target.find(".output_text").empty();
},
load_example_preview: function(data) {
if (data.length > 20) {
if (typeof data == "string" && data.length > 20) {
return data.substring(0,20) + "...";
}
return data;

View File

@ -20,8 +20,13 @@ const video_output = {
this.target.find(".output_image_holder").addClass("hide");
this.target.find(".output_image").attr('src', "")
},
load_example_preview: function(data) {
return "<video controls src='"+data[0]+"' height=100>"
},
load_example: function(example_data) {
example_data = this.io_master.example_file_path + example_data;
let io = this;
toDataURL(example_data, function(data) {
io.target.find(".upload_zone").hide();
io.target.find(".image_display").removeClass("hide");
io.output(data);
})
}
}