TilePaintApp / app.py
XINZHANG94's picture
Create app.py
bc43efa verified
import os
import gradio as gr
from PIL import Image
from utils.setting_names import names_mapping
from utils import (
load_tiles_data,
pixel_palette_image,
map_tiles,
overlay_preview,
overlay_tiles,
apply_border,
create_color_grid,
create_logo_text,
combine_component_image,
save_tiles_data,
update_wall_view
)
langangue = 'cn'
font_name = "华文细黑.ttf"
abs_path_font = os.path.abspath(__file__).split(os.sep)[:-1]
font_abs_path = os.sep.join(abs_path_font)
avaliable_tile_types = [
"磷光", "磷光AB", "磷光BA",
"矩形",
"三角尖上", "三角尖下",
"圆平铺",
"圆错位"
]
# avaliable_tile_types_map = {
# "磷光": "Hexagon",
# "磷光AB": "HexagonCurved_AB",
# "磷光BA": "HexagonCurved_BA",
# "矩形": "Rectangle",
# "三角尖上": "Triangle_TipUp",
# "三角尖下": "Triangle_TipDown",
# "圆平铺": "Circle_Alignment",
# "圆错位": "Circle_Displacement"
# }
names = dict((label_name, lan[langangue]) for label_name, lan in names_mapping.items())
def get_image_examples():
examples=[
os.path.join(os.path.dirname(__file__), "images/0.png"),
os.path.join(os.path.dirname(__file__), "images/1.jpg"),
os.path.join(os.path.dirname(__file__), "images/2.jpg"),
os.path.join(os.path.dirname(__file__), "images/3.jpg"),
os.path.join(os.path.dirname(__file__), "images/4.png"),
os.path.join(os.path.dirname(__file__), "images/5.png"),
]
return examples
with gr.Blocks() as demo:
gr.Markdown(names["page_header"])
with gr.Row(equal_height=False):
with gr.Column():
with gr.Accordion(open=True, label=names['raw_image']):
raw_image = gr.Image(type="pil")
# parameters for image color reduce
with gr.Accordion(open=True, label=names["image_color_reduce_settings"]):
with gr.Row(equal_height=True):
max_colors = gr.Slider(minimum=1, maximum=30, step=1, value=16, label=names["max_colors"])
flip_vertical = gr.Checkbox(label=names['flip_vertical'], value=False)
flip_horizontal = gr.Checkbox(label=names['flip_horizontal'], value=False)
rotation_angle = gr.Number(precision=0, minimum=0, maximum=360, step=1, label=names['rotation_counter_clock_wise'], value=0)
scale_ratio = gr.Slider(minimum=0.1, maximum=1, step=0.1, label=names['scale_ratio'], value=0.25, interactive=True)
rgb_palette = gr.Textbox(placeholder=names["rgb_palette_place_holder"], label=names["rgb_palette"])
color_list = gr.Textbox(label=names["color_list"])
btn_reduce_colors = gr.Button(names['run_reduce_colors'], variant="primary")
color_reduced_path = gr.Textbox(visible=False)
color_reduced_image = gr.Image(type="pil", label=names['color_reduced_image'], interactive=False)
# parameters for tile settings
with gr.Accordion(open=True, label=names["wall_settings"]):
with gr.Row(equal_height=True):
painting_width = gr.Number(precision=None, minimum=1, maximum=99999, step=1, label=names['painting_width'], value=2400)
painting_height = gr.Number(precision=None, minimum=1, maximum=99999, step=1, label=names['painting_height'], value=3500)
painting_color = gr.Dropdown(
['白色', '黑色'],
label=names["painting_color"], value='白色'
)
with gr.Accordion(open=False, label=names["wall_mask_settings"]):
with gr.Row(equal_height=True):
wall_boundary_points = gr.Textbox(placeholder=names["wall_boundary_points_place_holder"], label=names["wall_boundary_points"])
wall_curve_types = gr.Textbox(placeholder=names["wall_curve_types_place_holder"], label=names["wall_curve_types"])
wall_view = gr.Image(type="pil", label=names['wall_image'], interactive=False, visible=True)
btn_wall_preview = gr.Button(names['btn_wall_preview'], variant="primary")
use_wall = gr.Checkbox(label=names['use_wall'], value=True)
with gr.Accordion(open=True, label=names["tile_settings"]):
with gr.Row(equal_height=True):
tile_type = gr.Dropdown(
avaliable_tile_types,
label=names["tile_type"], value=avaliable_tile_types[0]
)
tile_layout_vertical = gr.Checkbox(label=names['tile_layout_vertical'], value=True)
efficiency_priority = gr.Checkbox(label=names['efficiency_priority'], value=True)
with gr.Row(equal_height=True, visible=False) as option1_row:
tile_width = gr.Slider(minimum=1.0, maximum=999, step=0.1, label=names['tile_width'], value=100, interactive=True)
tile_height = gr.Slider(minimum=1.0, maximum=999, step=0.1, label=names['tile_height'], value=100, interactive=True)
with gr.Row(equal_height=True, visible=True) as option2_row:
tile_width_hex = gr.Slider(minimum=1.0, maximum=999, step=0.1, label=names['tile_width'], value=50, interactive=True)
tile_height_middle = gr.Slider(minimum=0.0, maximum=999, step=0.1, label=names['tile_height_middle'], value=60, interactive=True)
tile_height_tip = gr.Slider(minimum=1.0, maximum=999, step=0.1, label=names['tile_height_tip'], value=30, interactive=True)
with gr.Row(equal_height=True, visible=False) as option3_row:
tile_radius = gr.Slider(minimum=10.0, maximum=999, step=0.1, label=names['tile_radius'], value=25, interactive=True)
with gr.Row(equal_height=True):
x_center_start = gr.Slider(minimum=0, maximum=25, step=0.1, label=names['x_center_start'], value=0, interactive=True)
y_center_start = gr.Slider(minimum=0, maximum=60, step=0.1, label=names['y_center_start'], value=0, interactive=True)
with gr.Row(equal_height=True):
gap_horizontal = gr.Slider(minimum=0, maximum=10, step=0.1, label=names['gap_horizontal'], value=0, interactive=True)
gap_vertical = gr.Slider(minimum=0, maximum=10, step=0.1, label=names['gap_vertical'], value=0, interactive=True)
def update_options_and_center(tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius):
option1_visibility = False
option2_visibility = False
option3_visibility = False
if tile_type in ["矩形"]:
option1_visibility = True
option2_visibility = False
option3_visibility = False
if tile_layout_vertical:
x_value = tile_width / 2
y_value = tile_height / 2
x_max = tile_width / 2
y_max = tile_height / 2
else:
x_value = tile_height / 2
y_value = tile_width / 2
x_max = tile_height / 2
y_max = tile_width / 2
elif tile_type in ["三角尖上", "三角尖下"]:
option1_visibility = True
option2_visibility = False
option3_visibility = False
x_value = 0
y_value = 0
if tile_layout_vertical:
x_max = tile_width / 2
y_max = tile_height / 2
else:
x_max = tile_height / 2
y_max = tile_width / 2
elif tile_type in ["磷光AB", "磷光BA", "磷光"]:
option1_visibility = False
option2_visibility = True
option3_visibility = False
x_value = 0
y_value = 0
if tile_layout_vertical:
x_max = tile_width_hex / 2
y_max = (2 * tile_height_tip + tile_height_middle) / 2
else:
x_max = (2 * tile_height_tip + tile_height_middle) / 2
y_max = tile_width_hex / 2
elif tile_type in ["圆平铺", "圆错位"]:
option1_visibility = False
option2_visibility = False
option3_visibility = True
x_value = 0
y_value = 0
x_max = tile_radius
y_max = tile_radius
x_value = round(x_value, 1)
y_value = round(y_value, 1)
x_max = round(x_max, 1)
y_max = round(y_max, 1)
return [
gr.update(visible=option1_visibility),
gr.update(visible=option2_visibility),
gr.update(visible=option3_visibility),
gr.update(value=x_value, maximum=x_max),
gr.update(value=y_value, maximum=y_max),
]
tile_type.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
tile_layout_vertical.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
tile_width.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
tile_height.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
tile_width_hex.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
tile_height_tip.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
tile_height_middle.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
tile_radius.change(
fn=update_options_and_center,
inputs=[tile_type, tile_layout_vertical, tile_width, tile_height, tile_width_hex, tile_height_tip, tile_height_middle, tile_radius],
outputs=[option1_row, option2_row, option3_row, x_center_start, y_center_start]
)
with gr.Column():
btn_map_tiles = gr.Button(names['run_map_tiles'], variant="primary")
tiled_image = gr.Image(type="pil", label=names['tiled_image'], interactive=False)
temp_path_tiled_image = gr.Textbox(visible=False)
temp_tiles_data_path = gr.Textbox(visible=False)
with gr.Column():
with gr.Accordion(open=False, label=names["overlay"]):
overlay_file = gr.File(label=names['load_overlay_data'], file_types=["json"])
def update_max_x_slider(value):
return gr.Slider(minimum=-1000, maximum=value, step=1, value=0, label=names["overlay_x_start"])
def update_max_y_slider(value):
return gr.Slider(minimum=-1000, maximum=value, step=1, value=0, label=names["overlay_y_start"])
overlay_global_mode = gr.Checkbox(label=names['overlay_global_mode'], value=False)
overlay_keep_colors = gr.Textbox(placeholder=names["overlay_keep_colors_place_holder"], label=names["overlay_keep_colors"])
overlay_x_start = gr.Slider(minimum=-1000, maximum=99999, step=1, value=0, label=names["overlay_x_start"])
overlay_y_start = gr.Slider(minimum=-1000, maximum=99999, step=1, value=0, label=names["overlay_y_start"])
painting_width.change(fn=update_max_x_slider, inputs=painting_width, outputs=overlay_x_start)
painting_height.change(fn=update_max_y_slider, inputs=painting_height, outputs=overlay_y_start)
btn_overlay_preview = gr.Button(names['btn_overlay_preview'], variant="primary")
overlay_img = gr.Image(type="pil", label=names['overlay_img'], interactive=False)
btn_create_overlay = gr.Button(names['btn_create_overlay'], variant="primary")
with gr.Column():
gr.Examples(
examples=get_image_examples(),
inputs=[raw_image],
fn=None,
outputs=[raw_image],
cache_examples=False,
label=names['examples']
)
with gr.Column():
with gr.Accordion(open=True, label=names['label_image']):
label_image = gr.Image(type="pil", label=names['label_image'], interactive=False)
with gr.Row(equal_height=True):
temp_path_label_image = gr.Textbox(visible=False)
def adjust_font_size(painting_width, painting_height):
# You can define your logic here to adjust the font size based on painting dimensions
# For example, a simple proportional adjustment might be:
base_width = 2400
base_height = 3500
base_font_size = 50
width_ratio = painting_width / base_width
height_ratio = painting_height / base_height
new_font_size = int(base_font_size * min(width_ratio, height_ratio))
return new_font_size
label_font_size = gr.Number(precision=0, minimum=1, maximum=500, step=1, label=names['label_font_size'], value=50)
painting_width.change(
fn=adjust_font_size,
inputs=[painting_width, painting_height],
outputs=[label_font_size]
)
painting_height.change(
fn=adjust_font_size,
inputs=[painting_width, painting_height],
outputs=[label_font_size]
)
btn_label = gr.Button(names['create_label'], variant="primary")
with gr.Accordion(open=True, label=names['logo_image']):
logo_image = gr.Image(type="pil", label=names['logo_image'], interactive=False)
temp_path_logo_image = gr.Textbox(visible=False)
btn_logo = gr.Button(names['create_logo'], variant="primary")
with gr.Accordion(open=True, label=names['grid_image']):
grid_image = gr.Image(type="pil", label=names['grid_image'], interactive=False)
temp_path_grid_image = gr.Textbox(visible=False)
btn_grid = gr.Button(names['create_grid'], variant="primary")
with gr.Accordion(open=True, label=names['final_image']):
final_image = gr.Image(type="pil", label=names['final_image'], interactive=False)
temp_path_final_image = gr.Textbox(visible=False)
btn_final_image = gr.Button(names['create_final_image'], variant="primary")
save_button = gr.Button("保存文件")
save_text = gr.Textbox()
with gr.Accordion(open=False, label=names['load_tiles_data']):
tile_file = gr.File(label=names['load_tiles_data'], file_types=["json"])
rebuild_color_reduced_img = gr.Checkbox(label=names['rebuild_color_reduced_img'], value=False)
tile_file.change(fn=load_tiles_data, inputs=[tile_file, rebuild_color_reduced_img], outputs=[
max_colors,
color_list,
rgb_palette,
color_reduced_image,
color_reduced_path,
painting_width,
painting_height,
painting_color,
wall_boundary_points,
wall_curve_types,
tile_type,
tile_layout_vertical,
tile_width,
tile_height,
tile_width_hex,
tile_height_tip,
tile_height_middle,
tile_radius,
x_center_start,
y_center_start,
gap_horizontal,
gap_vertical,
tiled_image,
temp_path_tiled_image,
temp_tiles_data_path
])
def load_temp_image(temp_path):
if isinstance(temp_path, str):
return Image.open(temp_path)
else:
raise ValueError(f"Expected a string path, got {type(temp_path)}")
btn_reduce_colors.click(
fn=pixel_palette_image,
inputs=[
raw_image,
rgb_palette,
max_colors,
flip_vertical,
flip_horizontal,
rotation_angle,
scale_ratio
],
outputs=[color_reduced_path, color_list]
).then(
fn=load_temp_image,
inputs=[color_reduced_path],
outputs=color_reduced_image
)
btn_wall_preview.click(
fn=update_wall_view,
inputs=[
painting_width,
painting_height,
wall_boundary_points,
wall_curve_types
],
outputs=wall_view
)
btn_map_tiles.click(
fn=map_tiles,
inputs=[
color_list,
color_reduced_path,
painting_width,
painting_height,
painting_color,
wall_boundary_points,
wall_curve_types,
use_wall,
tile_type,
tile_layout_vertical,
efficiency_priority,
tile_width,
tile_height,
tile_width_hex,
tile_height_tip,
tile_height_middle,
tile_radius,
x_center_start,
y_center_start,
gap_horizontal,
gap_vertical
],
outputs=[tiled_image, temp_path_tiled_image, temp_tiles_data_path]
)
btn_overlay_preview.click(
fn=overlay_preview,
inputs=[
overlay_file, overlay_keep_colors
],
outputs=[overlay_keep_colors, overlay_img]
)
btn_create_overlay.click(
fn=overlay_tiles,
inputs=[
temp_tiles_data_path, overlay_file, overlay_x_start, overlay_y_start, overlay_global_mode, overlay_keep_colors, painting_color
],
outputs=[color_list, tiled_image, temp_path_tiled_image, temp_tiles_data_path]
)
btn_label.click(
fn=apply_border,
inputs=[
temp_tiles_data_path,
label_font_size,
painting_color,
],
outputs=[label_image, temp_path_label_image]
)
btn_logo.click(
fn=create_logo_text,
inputs=[
painting_width,
painting_height
],
outputs=[logo_image, temp_path_logo_image]
)
btn_grid.click(
fn=create_color_grid,
inputs=[
painting_width,
painting_height,
tile_width,
tile_height,
tile_width_hex,
tile_height_tip,
tile_height_middle,
tile_radius,
color_list,
tile_type
],
outputs=[grid_image, temp_path_grid_image]
)
btn_final_image.click(
fn=combine_component_image,
inputs=[
painting_width,
painting_height,
temp_path_label_image,
temp_path_logo_image,
temp_path_grid_image
],
outputs=[final_image, temp_path_final_image]
)
save_button.click(
fn=save_tiles_data,
inputs=[temp_path_label_image, temp_path_logo_image, temp_path_final_image, temp_tiles_data_path],
outputs=save_text)
demo.launch(share=False, debug=False)