Skip to content
Snippets Groups Projects
Commit 50285fbe authored by fima's avatar fima :beers:
Browse files

Gui launcher system

parent 4c17c5eb
No related branches found
No related tags found
1 merge request!32Gui launcher system
......@@ -3,6 +3,8 @@ import os
import numpy as np
import gradio as gr
from qim3d.io import load # load or DataLoader?
from qim3d.utils import internal_tools
class Interface:
def __init__(self):
......@@ -13,7 +15,7 @@ class Interface:
# self.width = 960
self.max_masks = 3
self.mask_opacity = 0.5
self.cmy_hex = ['#00ffff','#ff00ff','#ffff00'] # Colors for max_masks>3?
self.cmy_hex = ["#00ffff", "#ff00ff", "#ffff00"] # Colors for max_masks>3?
# CSS path
current_dir = os.path.dirname(os.path.abspath(__file__))
......@@ -34,12 +36,11 @@ class Interface:
height=self.height,
# width=self.width,
show_tips=False,
**kwargs
**kwargs,
)
return
def get_result(self):
# Get the temporary files from gradio
temp_sets = self.interface.temp_file_sets
......@@ -62,7 +63,6 @@ class Interface:
return mask
def create_interface(self, img=None):
with gr.Blocks(css=self.css_path) as gradio_interface:
masks_state = gr.State(value={})
......@@ -71,13 +71,12 @@ class Interface:
with gr.Row():
with gr.Column(scale=1, min_width=320):
upload_img_btn = gr.UploadButton(
label='Upload image',
file_types=['image'],
interactive=True if img is None else False
label="Upload image",
file_types=["image"],
interactive=True if img is None else False,
)
clear_img_btn = gr.Button(
value='Clear image',
interactive=False if img is None else True
value="Clear image", interactive=False if img is None else True
)
with gr.Row():
......@@ -86,17 +85,17 @@ class Interface:
choices=["Mask 1"],
value="Mask 1",
label="Choose which mask to draw",
scale=1
scale=1,
)
with gr.Column(scale=1, min_width=64):
add_mask_btn = gr.Button(
value='Add mask',
value="Add mask",
scale=2,
)
with gr.Row():
prep_dl_btn = gr.Button(
value='Prepare mask for download',
visible=False if img is None else True
value="Prepare mask for download",
visible=False if img is None else True,
)
with gr.Row():
save_output = gr.File(
......@@ -109,13 +108,13 @@ class Interface:
with gr.Row():
input_img = gr.Image(
label="Input",
tool='sketch',
tool="sketch",
value=img,
height=600,
width=600,
brush_color='#00ffff',
brush_color="#00ffff",
mask_opacity=self.mask_opacity,
interactive=False if img is None else True
interactive=False if img is None else True,
)
output_masks = []
......@@ -124,11 +123,13 @@ class Interface:
output_mask = gr.Image(
label=f"Mask {mask_idx+1}",
visible=True if mask_idx == 0 else False,
image_mode='L',
image_mode="L",
height=600,
width=600,
interactive=False if img is None else True, # If statement added bc of bug after Gradio 3.44.x
show_download_button=False
interactive=False
if img is None
else True, # If statement added bc of bug after Gradio 3.44.x
show_download_button=False,
)
output_masks.append(output_mask)
......@@ -136,69 +137,81 @@ class Interface:
operations = Operations(max_masks=self.max_masks, cmy_hex=self.cmy_hex)
# Update component configuration when image is uploaded
upload_img_btn.upload(fn=operations.upload_img_update,
upload_img_btn.upload(
fn=operations.upload_img_update,
inputs=upload_img_btn,
outputs=[input_img,clear_img_btn,upload_img_btn,prep_dl_btn] + output_masks
outputs=[input_img, clear_img_btn, upload_img_btn, prep_dl_btn]
+ output_masks,
)
# Add mask below when 'add mask' button is clicked
add_mask_btn.click(
fn=operations.increment_mask,
inputs=counts,
outputs=[counts, selected_mask] + output_masks
outputs=[counts, selected_mask] + output_masks,
)
# Draw mask when input image is edited
input_img.edit(
fn=operations.update_masks,
inputs=[input_img, selected_mask, masks_state, upload_img_btn],
outputs=output_masks
outputs=output_masks,
)
# Update brush color according to radio setting
selected_mask.change(
fn=operations.update_brush_color,
inputs=selected_mask,outputs=input_img
inputs=selected_mask,
outputs=input_img,
)
# Make file download visible
prep_dl_btn.click(
fn=operations.save_mask,
inputs=output_masks,
outputs=[save_output,save_output]
outputs=[save_output, save_output],
)
# Update 'Add mask' button interactivit according to the current count
counts.change(
fn=operations.set_add_mask_btn_interactivity,
inputs=counts,
outputs=add_mask_btn
outputs=add_mask_btn,
)
# Reset component configuration when image is cleared
clear_img_btn.click(
fn=operations.clear_img_update,
inputs=None,
outputs=[selected_mask,prep_dl_btn,save_output,counts,input_img,upload_img_btn,clear_img_btn] + output_masks
outputs=[
selected_mask,
prep_dl_btn,
save_output,
counts,
input_img,
upload_img_btn,
clear_img_btn,
]
+ output_masks,
)
return gradio_interface
class Operations:
def __init__(self, max_masks, cmy_hex):
self.max_masks = max_masks
self.cmy_hex = cmy_hex
def update_masks(self, input_img, selected_mask, masks_state, file):
# Binarize mask (it is not per default due to anti-aliasing)
input_mask = input_img['mask']
input_mask = input_img["mask"]
input_mask[input_mask > 0] = 255
try:
file_name = file.name
except AttributeError:
file_name = 'nb_img'
file_name = "nb_img"
# Add new file to state dictionary when this function sees it first time
if file_name not in masks_state.keys():
......@@ -206,7 +219,11 @@ class Operations:
# Get index of currently selected and non-selected masks
sel_mask_idx = int(selected_mask[-1]) - 1
nonsel_mask_idxs = [mask_idx for mask_idx in list(range(self.max_masks)) if mask_idx != sel_mask_idx]
nonsel_mask_idxs = [
mask_idx
for mask_idx in list(range(self.max_masks))
if mask_idx != sel_mask_idx
]
# Add background to state first time function is invoked in current session
if len(masks_state[file_name][0]) == 0:
......@@ -234,7 +251,9 @@ class Operations:
# Go from multi-channel to single-channel mask
stacked_masks = np.stack(masks, axis=-1)
final_mask = np.zeros_like(masks[0])
final_mask[np.where(stacked_masks==255)[:2]]=np.where(stacked_masks==255)[-1]+1
final_mask[np.where(stacked_masks == 255)[:2]] = (
np.where(stacked_masks == 255)[-1] + 1
)
# Save output image in a temp space (and to current directory which is a bug)
filename = "mask.tif"
......@@ -250,8 +269,12 @@ class Operations:
counts = int(counts)
counts_update = gr.Number(value=counts)
selected_mask_update = gr.Radio(value = f"Mask {counts}", choices = [f"Mask {i+1}" for i in range(counts)])
output_masks_update = [gr.Image(visible=True)]*counts + [gr.Image(visible=False)]*(self.max_masks-counts)
selected_mask_update = gr.Radio(
value=f"Mask {counts}", choices=[f"Mask {i+1}" for i in range(counts)]
)
output_masks_update = [gr.Image(visible=True)] * counts + [
gr.Image(visible=False)
] * (self.max_masks - counts)
return [counts_update, selected_mask_update] + output_masks_update
......@@ -260,41 +283,81 @@ class Operations:
if sel_mask_idx < len(self.cmy_hex):
input_img_update = gr.Image(brush_color=self.cmy_hex[sel_mask_idx])
else:
input_img_update = gr.Image(brush_color='#000000') # Return black brush
input_img_update = gr.Image(brush_color="#000000") # Return black brush
return input_img_update
def set_add_mask_btn_interactivity(self, counts):
add_mask_btn_update = gr.Button(interactive=True) if counts<self.max_masks else gr.Button(interactive=False)
add_mask_btn_update = (
gr.Button(interactive=True)
if counts < self.max_masks
else gr.Button(interactive=False)
)
return add_mask_btn_update
def clear_img_update(self):
selected_mask_update = gr.Radio(choices = ["Mask 1"], value = "Mask 1") # Reset radio component to only show 'Mask 1'
prep_dl_btn_update = gr.Button(visible=False) # Make 'Prepare mask for download' button invisible
selected_mask_update = gr.Radio(
choices=["Mask 1"], value="Mask 1"
) # Reset radio component to only show 'Mask 1'
prep_dl_btn_update = gr.Button(
visible=False
) # Make 'Prepare mask for download' button invisible
save_output_update = gr.File(visible=False) # Make File save box invisible
counts_update = gr.Number(value=1) # Reset invisible counter to 1
input_img_update = gr.Image(value=None,interactive=False) # Set input image component to non-interactive (so a new image cannot be uploaded directly in the component)
upload_img_btn_update = gr.Button(interactive=True) # Make 'Upload image' button interactive
clear_img_btn_update = gr.Button(interactive=False) # Make 'Clear image' button non-interactive
output_masks_update = [gr.Image(value=None,visible=True if i==0 else False,interactive=False) for i in range(self.max_masks)] # Remove drawn masks and set as invisible except mask 1. 'interactive=False' added bc of bug after Gradio 3.44.x
return [selected_mask_update,
input_img_update = gr.Image(
value=None, interactive=False
) # Set input image component to non-interactive (so a new image cannot be uploaded directly in the component)
upload_img_btn_update = gr.Button(
interactive=True
) # Make 'Upload image' button interactive
clear_img_btn_update = gr.Button(
interactive=False
) # Make 'Clear image' button non-interactive
output_masks_update = [
gr.Image(value=None, visible=True if i == 0 else False, interactive=False)
for i in range(self.max_masks)
] # Remove drawn masks and set as invisible except mask 1. 'interactive=False' added bc of bug after Gradio 3.44.x
return [
selected_mask_update,
prep_dl_btn_update,
save_output_update,
counts_update,
input_img_update,
upload_img_btn_update,
clear_img_btn_update] + output_masks_update
clear_img_btn_update,
] + output_masks_update
def upload_img_update(self, file):
input_img_update = gr.Image(value=load(file.name),interactive=True) # Upload image from button to Image components
clear_img_btn_update = gr.Button(interactive=True) # Make 'Clear image' button interactive
upload_img_btn_update = gr.Button(interactive=False) # Make 'Upload image' button non-interactive
prep_dl_btn_update = gr.Button(visible=True) # Make 'Prepare mask for download' button visible
output_masks_update = [gr.Image(interactive=True)]*self.max_masks # This line is added bc of bug in Gradio after 3.44.x
return [input_img_update,
input_img_update = gr.Image(
value=load(file.name), interactive=True
) # Upload image from button to Image components
clear_img_btn_update = gr.Button(
interactive=True
) # Make 'Clear image' button interactive
upload_img_btn_update = gr.Button(
interactive=False
) # Make 'Upload image' button non-interactive
prep_dl_btn_update = gr.Button(
visible=True
) # Make 'Prepare mask for download' button visible
output_masks_update = [
gr.Image(interactive=True)
] * self.max_masks # This line is added bc of bug in Gradio after 3.44.x
return [
input_img_update,
clear_img_btn_update,
upload_img_btn_update,
prep_dl_btn_update] + output_masks_update
prep_dl_btn_update,
] + output_masks_update
if __name__ == "__main__":
# Get port using the QIM API
port_dict = internal_tools.get_port_dict()
internal_tools.gradio_header(Interface().title, port_dict["port"])
# Creates interface
app = Interface().create_interface()
app.launch(server_name="0.0.0.0", server_port=int(port_dict["port"]))
......@@ -252,7 +252,6 @@ class Session:
).strftime("%Y-%m-%d %H:%M")
self.file_size = os.path.getsize(self.data_path)
def create_summary_dict(self):
# Create dictionary
if self.error_message:
......@@ -477,6 +476,10 @@ class Pipeline:
if __name__ == "__main__":
app = Interface()
app.show_header = True
app.launch(server_name="0.0.0.0", show_error=True)
# Get port using the QIM API
port_dict = internal_tools.get_port_dict()
internal_tools.gradio_header(Interface().title, port_dict["port"])
# Creates interface
app = Interface().create_interface()
app.launch(server_name="0.0.0.0", server_port=int(port_dict["port"]))
......@@ -398,6 +398,10 @@ class Interface:
if __name__ == "__main__":
app = Interface()
app.show_header = True
app.launch(server_name="0.0.0.0", show_error=True, default_port=True)
# Get port using the QIM API
port_dict = internal_tools.get_port_dict()
internal_tools.gradio_header(Interface().title, port_dict["port"])
# Creates interface
app = Interface().create_interface()
app.launch(server_name="0.0.0.0", server_port=int(port_dict["port"]))
......@@ -12,6 +12,7 @@ import plotly.graph_objects as go
import localthickness as lt
import matplotlib
# matplotlib.use("Agg")
import matplotlib.pyplot as plt
......@@ -420,3 +421,13 @@ class Pipeline:
tifffile.imwrite(filename, session.vol_thickness)
return filename
if __name__ == "__main__":
# Get port using the QIM API
port_dict = internal_tools.get_port_dict()
internal_tools.gradio_header(Interface().title, port_dict["port"])
# Creates interface
app = Interface().create_interface()
app.launch(server_name="0.0.0.0", server_port=int(port_dict["port"]))
......@@ -6,16 +6,10 @@ doi = "https://doi.org/10.1007/s10851-021-01041-3"
def test_get_bibtex():
bibtext = qim3d.utils.doi.get_bibtex(doi)
assert (
bibtext
== "@article{Stephensen_2021,\n\tdoi = {10.1007/s10851-021-01041-3},\n\turl = {https://doi.org/10.1007%2Fs10851-021-01041-3},\n\tyear = 2021,\n\tmonth = {jun},\n\tpublisher = {Springer Science and Business Media {LLC}},\n\tvolume = {63},\n\tnumber = {8},\n\tpages = {1069--1083},\n\tauthor = {Hans J. T. Stephensen and Anne Marie Svane and Carlos B. Villanueva and Steven A. Goldman and Jon Sporring},\n\ttitle = {Measuring Shape Relations Using r-Parallel Sets},\n\tjournal = {Journal of Mathematical Imaging and Vision}\n}"
)
assert "Measuring Shape Relations Using r-Parallel Sets" in bibtext
def test_get_reference():
reference = qim3d.utils.doi.get_reference(doi)
assert (
reference
== "Stephensen, H. J. T., Svane, A. M., Villanueva, C. B., Goldman, S. A., & Sporring, J. (2021). Measuring Shape Relations Using r-Parallel Sets. Journal of Mathematical Imaging and Vision, 63(8), 1069â\x80\x931083. https://doi.org/10.1007/s10851-021-01041-3\n"
)
assert "Stephensen" in reference
......@@ -9,7 +9,8 @@ import numpy as np
import socket
import os
import shutil
import requests
import getpass
from PIL import Image
from pathlib import Path
from qim3d.io.logger import log
......@@ -173,6 +174,7 @@ def sizeof(num, suffix="B"):
num /= 1024.0
return f"{num:.1f} Y{suffix}"
def is_server_running(ip, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
......@@ -182,6 +184,7 @@ def is_server_running(ip, port):
except:
return False
def temp_data(folder, remove=False, n=3, img_shape=(32, 32)):
"""Creates a temporary folder to test deep learning tools.
......@@ -198,8 +201,8 @@ def temp_data(folder,remove = False,n = 3,img_shape = (32,32)):
Example:
>>> tempdata('temporary_folder',n = 10, img_shape = (16,16))
"""
folder_trte = ['train','test']
sub_folders = ['images','labels']
folder_trte = ["train", "test"]
sub_folders = ["images", "labels"]
# Creating train/test folder
path_train = Path(folder) / folder_trte[0]
......@@ -221,10 +224,10 @@ def temp_data(folder,remove = False,n = 3,img_shape = (32,32)):
os.makedirs(path_train_lab)
os.makedirs(path_test_lab)
for i in range(n):
img.save(path_train_im / f'img_train{i}.png')
img.save(path_train_lab / f'img_train{i}.png')
img.save(path_test_im / f'img_test{i}.png')
img.save(path_test_lab / f'img_test{i}.png')
img.save(path_train_im / f"img_train{i}.png")
img.save(path_train_lab / f"img_train{i}.png")
img.save(path_test_im / f"img_test{i}.png")
img.save(path_test_lab / f"img_test{i}.png")
if remove:
for filename in os.listdir(folder):
......@@ -235,13 +238,30 @@ def temp_data(folder,remove = False,n = 3,img_shape = (32,32)):
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
log.warning('Failed to delete %s. Reason: %s' % (file_path, e))
log.warning("Failed to delete %s. Reason: %s" % (file_path, e))
os.rmdir(folder)
def stringify_path(path):
"""Converts an os.PathLike object to a string
"""
"""Converts an os.PathLike object to a string"""
if isinstance(path, os.PathLike):
path = path.__fspath__()
return path
def get_port_dict():
# Gets user and port
username = getpass.getuser()
url = f"https://platform.qim.dk/qim-api/get-port/{username}"
response = requests.get(url, timeout=10)
# Check if the request was successful (status code 200)
if response.status_code == 200:
# Parse the JSON response into a Python dictionary
port_dict = response.json()
else:
# Print an error message if the request was not successful
raise (f"Error: {response.status_code}")
return port_dict
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment