Surn commited on
Commit
8001a73
·
1 Parent(s): e27e10f

Add Blur and Grid Transformation

Browse files
app.py CHANGED
@@ -46,6 +46,7 @@ from utils.misc import (
46
 
47
  from utils.image_utils import (
48
  change_color,
 
49
  open_image,
50
  upscale_image,
51
  lerp_imagemath,
@@ -67,7 +68,8 @@ from utils.image_utils import (
67
  from utils.hex_grid import (
68
  generate_hexagon_grid,
69
  generate_hexagon_grid_interface,
70
- map_sides
 
71
  )
72
 
73
  from utils.excluded_colors import (
@@ -1221,6 +1223,7 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1221
  height=120)
1222
  with gr.Row():
1223
  lut_intensity = gr.Slider(label="Filter Intensity", minimum=-200, maximum=200, value=100, info="0 none, negative inverts the filter", interactive=True)
 
1224
  apply_lut_button = gr.Button("Apply Filter to Input Image", elem_classes="solid", elem_id="apply_lut_button")
1225
  apply_lut_to_sketch_button = gr.Button("Apply Filter to Sketch", elem_classes="solid", elem_id="apply_lut_to_sketch_button")
1226
  with gr.Row():
@@ -1261,7 +1264,14 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1261
  composite_color = gr.ColorPicker(label="Color", value="#ede9ac44")
1262
  composite_opacity = gr.Slider(label="Opacity %", minimum=0, maximum=100, value=50, interactive=True)
1263
  with gr.Row():
1264
- composite_button = gr.Button("Composite", elem_classes="solid")
 
 
 
 
 
 
 
1265
  with gr.Row(elem_id="image_gen"):
1266
  with gr.Accordion("Generate AI Image (optional, fun)", open = False):
1267
  with gr.Row():
@@ -1324,14 +1334,14 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1324
  label="Prompt",
1325
  visible=False,
1326
  elem_classes="solid",
1327
- value="Planetary overhead view, directly from above, centered on the planet’s surface, (rectangular tabletop_map) alien planet map, Battletech_boardgame scifi world with forests, lakes, oceans, continents and snow at the top and bottom, (middle is dark, no_reflections, no_shadows), looking straight down.",
1328
  lines=4
1329
  )
1330
  negative_prompt_textbox = gr.Textbox(
1331
  label="Negative Prompt",
1332
  visible=False,
1333
  elem_classes="solid",
1334
- value="Earth, low quality, bad anatomy, blurry, cropped, worst quality, shadows, people, humans, reflections, shadows, realistic map of the Earth, isometric, text"
1335
  )
1336
  prompt_notes_label = gr.Label(
1337
  "You should use FRM$ as trigger words. @1.5 minutes",
@@ -1425,11 +1435,16 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1425
  hex_button = gr.Button("Generate Hex Grid!", elem_classes="solid", elem_id="btn-generate")
1426
  with gr.Row():
1427
  output_image = gr.Image(label="Hexagon Grid Image", image_mode = "RGBA", elem_classes="centered solid imgcontainer", format="PNG", type="filepath", key="ImgOutput",interactive=True)
1428
- overlay_image = gr.Image(label="Hexagon Overlay Image", image_mode = "RGBA", elem_classes="centered solid imgcontainer", format="PNG", type="filepath", key="ImgOverlay",interactive=True)
1429
- with gr.Row():
1430
- output_blend_multiply_composite = gr.Slider(0,100,50,0.5, label="Multiply Intensity*")
1431
- output_overlay_composite = gr.Slider(0,100,50,0.5, label="Interpolate Intensity")
1432
- output_alpha_composite = gr.Slider(0,100,50,0.5, label="Alpha Composite Intensity")
 
 
 
 
 
1433
  with gr.Accordion("Add Margins (for printing)", open=False):
1434
  with gr.Row():
1435
  border_image_source = gr.Radio(label="Add Margins around which Image", choices=["Input Image", "Overlay Image"], value="Overlay Image")
@@ -1492,13 +1507,14 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1492
  ddd_file_name = gr.State("Hexagon_file")
1493
  with gr.Row():
1494
  gr.Examples(examples=[
1495
- ["assets//examples//hex_map_p1.png", False, True, -32,-31,80,80,-1.8,0,35,0,1,"#FFD0D0", 15],
1496
- ["assets//examples//hex_map_p1_overlayed.png", False, False, -32,-31,80,80,-1.8,0,35,0,1,"#FFD0D0", 75],
1497
- ["assets//examples//hex_flower_logo.png", False, True, -95,-95,100,100,-24,-2,190,30,2,"#FF8951", 50],
1498
- ["assets//examples//hexed_fract_1.png", False, True, 0,0,0,0,0,0,10,0,0,"#000000", 5],
1499
- ["assets//examples//tmpzt3mblvk.png", False, True, -20,10,0,0,-6,-2,35,30,1,"#ffffff", 0],
 
1500
  ],
1501
- inputs=[input_image, filter_color, fill_hex, start_x, start_y, end_x, end_y, x_spacing, y_spacing, hex_size, rotation, border_size, border_color, border_opacity],
1502
  elem_id="examples")
1503
  # with gr.Row():
1504
  # login_button = gr.LoginButton(size="sm", elem_classes="solid centered", elem_id="hf_login_btn")
@@ -1551,27 +1567,14 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1551
  inputs=[sketch_image],
1552
  outputs=[input_image], scroll_to_output=True
1553
  )
1554
- # sketch_replace_lut_example_image_button.click(
1555
- # lambda sketch_image: replace_with_sketch_image(sketch_image, True),
1556
- # inputs=[sketch_image],
1557
- # outputs=[lut_example_image], scroll_to_output=True
1558
- # )
1559
  ##################### model #######################################
1560
  model_textbox.change(
1561
  fn=update_prompt_notes,
1562
  inputs=model_textbox,
1563
  outputs=prompt_notes_label,preprocess=False
1564
  )
1565
- # model_options.change(
1566
- # fn=lambda x: (gr.update(visible=(x == "Manual Entry")), gr.update(value=x) if x != "Manual Entry" else gr.update()),
1567
- # inputs=model_options,
1568
- # outputs=[model_textbox, model_textbox]
1569
- # )
1570
- # model_options.change(
1571
- # fn=update_prompt_notes,
1572
- # inputs=model_options,
1573
- # outputs=prompt_notes_label
1574
- # )
1575
  lora_gallery.select(
1576
  fn=update_selection,
1577
  inputs=[image_size_ratio],
@@ -1579,13 +1582,27 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1579
  )
1580
 
1581
  #################### model end ########################################
1582
-
1583
  composite_button.click(
1584
  fn=lambda input_image, composite_color, composite_opacity: gr.Warning("Please upload an Input Image to get started.") if input_image is None else change_color(input_image, composite_color, composite_opacity),
1585
  inputs=[input_image, composite_color, composite_opacity],
1586
  outputs=[input_image]
1587
  )
1588
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1589
  #use conditioned_image as the input_image for generate_input_image_click
1590
  generate_input_image_from_gallery.click(
1591
  fn=unload_3d_models,
@@ -1618,6 +1635,17 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1618
  inputs=[input_image, sketch_image],
1619
  outputs=[sketch_image, sketch_image]
1620
  )
 
 
 
 
 
 
 
 
 
 
 
1621
  output_overlay_composite.change(
1622
  fn=combine_images_with_lerp,
1623
  inputs=[input_image, output_image, output_overlay_composite],
 
46
 
47
  from utils.image_utils import (
48
  change_color,
49
+ blur_image,
50
  open_image,
51
  upscale_image,
52
  lerp_imagemath,
 
68
  from utils.hex_grid import (
69
  generate_hexagon_grid,
70
  generate_hexagon_grid_interface,
71
+ map_sides,
72
+ transform_grid
73
  )
74
 
75
  from utils.excluded_colors import (
 
1223
  height=120)
1224
  with gr.Row():
1225
  lut_intensity = gr.Slider(label="Filter Intensity", minimum=-200, maximum=200, value=100, info="0 none, negative inverts the filter", interactive=True)
1226
+ with gr.Row():
1227
  apply_lut_button = gr.Button("Apply Filter to Input Image", elem_classes="solid", elem_id="apply_lut_button")
1228
  apply_lut_to_sketch_button = gr.Button("Apply Filter to Sketch", elem_classes="solid", elem_id="apply_lut_to_sketch_button")
1229
  with gr.Row():
 
1264
  composite_color = gr.ColorPicker(label="Color", value="#ede9ac44")
1265
  composite_opacity = gr.Slider(label="Opacity %", minimum=0, maximum=100, value=50, interactive=True)
1266
  with gr.Row():
1267
+ composite_button = gr.Button("Composite to Input Image", elem_classes="solid")
1268
+ composite_sketch_button = gr.Button("Composite to Sketh", elem_classes="solid")
1269
+ with gr.Accordion("Blur", open = False):
1270
+ with gr.Row():
1271
+ blur_amount = gr.Slider(label="Blur Amount", minimum=0, maximum=100, value=5, interactive=True)
1272
+ with gr.Row():
1273
+ blur_button = gr.Button("Blur Input Image", elem_classes="solid")
1274
+ blur_sketch_button = gr.Button("Blur Sketch", elem_classes="solid")
1275
  with gr.Row(elem_id="image_gen"):
1276
  with gr.Accordion("Generate AI Image (optional, fun)", open = False):
1277
  with gr.Row():
 
1334
  label="Prompt",
1335
  visible=False,
1336
  elem_classes="solid",
1337
+ value="Planetary overhead view, directly from above, centered on the planet’s surface, orthographic (rectangular tabletop_map) alien planet map, Battletech_boardgame scifi world with forests, lakes, oceans, continents and snow at the top and bottom, (middle is dark, no_reflections, no_shadows), looking straight down.",
1338
  lines=4
1339
  )
1340
  negative_prompt_textbox = gr.Textbox(
1341
  label="Negative Prompt",
1342
  visible=False,
1343
  elem_classes="solid",
1344
+ value="Earth, low quality, bad anatomy, blurry, cropped, worst quality, shadows, people, humans, reflections, shadows, realistic map of the Earth, isometric, text, camera_angle"
1345
  )
1346
  prompt_notes_label = gr.Label(
1347
  "You should use FRM$ as trigger words. @1.5 minutes",
 
1435
  hex_button = gr.Button("Generate Hex Grid!", elem_classes="solid", elem_id="btn-generate")
1436
  with gr.Row():
1437
  output_image = gr.Image(label="Hexagon Grid Image", image_mode = "RGBA", elem_classes="centered solid imgcontainer", format="PNG", type="filepath", key="ImgOutput",interactive=True)
1438
+ overlay_image = gr.Image(label="Hexagon Overlay Image", image_mode = "RGBA", elem_classes="centered solid imgcontainer", format="PNG", type="filepath", key="ImgOverlay",interactive=True)
1439
+ with gr.Accordion("Grid adjustments", open=True):
1440
+ with gr.Row():
1441
+ with gr.Column(scale=1):
1442
+ output_grid_tilt = gr.Slider(minimum=-90, maximum=90, value=0, step=0.05, label="Tilt Angle (degrees)")
1443
+ output_grid_rotation = gr.Slider(minimum=-180, maximum=180, value=0, step=0.05, label="Rotation Angle (degrees)")
1444
+ with gr.Column(scale=1):
1445
+ output_alpha_composite = gr.Slider(0,100,50,0.5, label="Alpha Composite Intensity*")
1446
+ output_blend_multiply_composite = gr.Slider(0,100,50,0.5, label="Multiply Intensity")
1447
+ output_overlay_composite = gr.Slider(0,100,50,0.5, label="Interpolate Intensity")
1448
  with gr.Accordion("Add Margins (for printing)", open=False):
1449
  with gr.Row():
1450
  border_image_source = gr.Radio(label="Add Margins around which Image", choices=["Input Image", "Overlay Image"], value="Overlay Image")
 
1507
  ddd_file_name = gr.State("Hexagon_file")
1508
  with gr.Row():
1509
  gr.Examples(examples=[
1510
+ ["assets//examples//hex_map_p1.png", False, True, -32,-31,80,80,-1.8,0,35,0,1,"#FFD0D0", 15,0,0],
1511
+ ["assets//examples//hex_map_p1_overlayed.png", False, False, -32,-31,80,80,-1.8,0,35,0,1,"#FFD0D0", 75,0,0],
1512
+ ["assets//examples//hex_flower_logo.png", False, True, -95,-95,100,100,-24,-2,190,30,2,"#FF8951", 50,0,0],
1513
+ ["assets//examples//hexed_fract_1.png", False, True, 0,0,0,0,0,0,10,0,0,"#000000", 5,0,0],
1514
+ ["assets//examples//tmpzt3mblvk.png", False, True, -20,10,0,0,-6,-2,35,30,1,"#ffffff", 0,0,0],
1515
+ ["assets//examples//scifi_city_dof.png", False, False, -95,-95,150,150,-14,1,100,-45,2,"#808080",70,0,45.0 ],
1516
  ],
1517
+ inputs=[input_image, filter_color, fill_hex, start_x, start_y, end_x, end_y, x_spacing, y_spacing, hex_size, rotation, border_size, border_color, border_opacity, output_grid_tilt, output_grid_rotation],
1518
  elem_id="examples")
1519
  # with gr.Row():
1520
  # login_button = gr.LoginButton(size="sm", elem_classes="solid centered", elem_id="hf_login_btn")
 
1567
  inputs=[sketch_image],
1568
  outputs=[input_image], scroll_to_output=True
1569
  )
1570
+
 
 
 
 
1571
  ##################### model #######################################
1572
  model_textbox.change(
1573
  fn=update_prompt_notes,
1574
  inputs=model_textbox,
1575
  outputs=prompt_notes_label,preprocess=False
1576
  )
1577
+
 
 
 
 
 
 
 
 
 
1578
  lora_gallery.select(
1579
  fn=update_selection,
1580
  inputs=[image_size_ratio],
 
1582
  )
1583
 
1584
  #################### model end ########################################
1585
+ ########### special effects ###############
1586
  composite_button.click(
1587
  fn=lambda input_image, composite_color, composite_opacity: gr.Warning("Please upload an Input Image to get started.") if input_image is None else change_color(input_image, composite_color, composite_opacity),
1588
  inputs=[input_image, composite_color, composite_opacity],
1589
  outputs=[input_image]
1590
  )
1591
+ composite_sketch_button.click(
1592
+ fn=lambda sketch_image, composite_color, composite_opacity: gr.Warning("Please upload an Sketch Image to get started.") if Sketch_image is None else change_color(sketch_image, composite_color, composite_opacity),
1593
+ inputs=[sketch_image, composite_color, composite_opacity],
1594
+ outputs=[sketch_image]
1595
+ )
1596
+ blur_button.click(
1597
+ fn=lambda input_image, blur_amount: gr.Warning("Please upload an Input Image to get started.") if input_image is None else blur_image(input_image, blur_amount),
1598
+ inputs=[input_image, blur_amount],
1599
+ outputs=[input_image])
1600
+ blur_sketch_button.click(
1601
+ fn=lambda sketch_image, blur_amount: gr.Warning("Please upload an Sketch Image to get started.") if sketch_image is None else blur_image(sketch_image, blur_amount),
1602
+ inputs=[sketch_image, blur_amount],
1603
+ outputs=[sketch_image])
1604
+
1605
+ ################ end special effects ############################
1606
  #use conditioned_image as the input_image for generate_input_image_click
1607
  generate_input_image_from_gallery.click(
1608
  fn=unload_3d_models,
 
1635
  inputs=[input_image, sketch_image],
1636
  outputs=[sketch_image, sketch_image]
1637
  )
1638
+
1639
+ output_grid_tilt.change(
1640
+ fn=transform_grid,
1641
+ inputs=[output_image, output_grid_tilt, output_grid_rotation],
1642
+ outputs=[output_image]
1643
+ )
1644
+ output_grid_rotation.change(
1645
+ fn=transform_grid,
1646
+ inputs=[output_image, output_grid_tilt, output_grid_rotation],
1647
+ outputs=[output_image]
1648
+ )
1649
  output_overlay_composite.change(
1650
  fn=combine_images_with_lerp,
1651
  inputs=[input_image, output_image, output_overlay_composite],
assets/examples/scifi_city_dof.png ADDED

Git LFS Details

  • SHA256: b8d7d328e0269e1d9ccfdbc6d3114394ef083cb331da53b41d4c5ca2a24b3dce
  • Pointer size: 131 Bytes
  • Size of remote file: 346 kB
utils/constants.py CHANGED
@@ -69,32 +69,32 @@ os.makedirs(TMPDIR, exist_ok=True)
69
 
70
 
71
  PROMPTS = {
72
- "Mecha Wasteland Arena": "Regional overhead view, directly from above, centered on the map, of a Mecha battlefield map. post-industrial wasteland with crumbling structures, volcanic ridges, scrapyards, and ash plains. Features elevated overwatch positions for long-range combat and tight brawling areas for close-quarters engagements. Partial edge hexes are black. Colors: red, gray, muted orange, ash white, dark brown.",
73
- "BorderBlack": "Planetary overhead view, directly from above, centered on the planet’s surface, of a hexagon-based alien world map with black borders. Features rivers, mountains, volcanoes, and polar snow regions. Colors: light blue, green, tan, brown. No reflections or shadows. Partial edge hexes are black.",
74
- "Earth": "Planetary overhead view, directly from above, centered on the planet’s surface, of an Earth-like world map with rivers, mountains, volcanoes, and polar snow regions. Colors: light blue, green, tan, brown. No reflections or shadows. Partial edge hexes are black.",
75
- "Beeuty": "Regional overhead view, directly from above, centered on the map, of a tabletop gaming map with honeycomb-shaped terrain, lakes, dense forests, magical flora, and hex grids. Designed for clarity and strategic gameplay. Colors: yellow, green, purple, brown. Partial edge hexes are black.",
76
- "Scifi City": "Regional overhead view, directly from above, centered on the map, of a futuristic urban battlefield map with lakes, forests, ruined buildings, and city streets. Designed for clarity and strategic gameplay in tabletop games. Colors: teal, dark green, violet, brown. Partial edge hexes are black.",
77
- "Alien Landscape": "Planetary overhead view, directly from above, centered on the planet’s surface, of a barren alien world map composed of hexagon tiles. Features light blue rivers, brown mountains, red volcanoes, and white polar snow. Colors: light blue, green, tan, brown. Partial edge hexes are black.",
78
- "Alien World": "Planetary overhead view, directly from above, centered on the planet’s surface, of an alien world map built from hexagon tiles. Includes rivers, mountains, volcanoes, and snowy regions. Colors: light blue, green, tan, brown. Partial edge hexes are black.",
79
- "Mystic Forest": "Regional overhead view, directly from above, centered on the map, of a mystic forest map with lakes, dense forests, magical flora, and hex grids. Designed for clarity and strategic gameplay in tabletop gaming. Colors: light blue, green, purple, brown. Partial edge hexes are black.",
80
- "Medieval Battlefield": "Regional overhead view, directly from above, centered on the map, of a medieval battlefield map with lakes, forests, and magical fauna. Designed for clarity and strategic gameplay in tabletop games. Colors: teal, dark green, violet, brown. Partial edge hexes are black.",
81
- "Dungeon Interior": "Regional overhead view, directly from above, centered on the map, of a dungeon interior map for tabletop gaming. Features stone walls, corridors, rooms with doors, traps, and treasure chests. Designed for clarity and strategic gameplay. Colors: gray, brown, dark blue. Partial edge hexes are black.",
82
- "Desert Wasteland": "Regional overhead view, directly from above, centered on the map, of a desert wasteland map for tabletop gaming. Features sand dunes, rocky canyons, oases, and ancient ruins. Colors: yellow, tan, brown, blue, green. Partial edge hexes are black.",
83
  "Prompt": None # Indicates that the prompt should be taken from prompt_textbox
84
  }
85
 
86
  NEGATIVE_PROMPTS = {
87
- "Mecha Wasteland Arena": "humans, old_buildings, water, bright colors, text, logos, shadows, Earth geography, isometric",
88
- "BorderBlack": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric",
89
- "Earth": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, missing map of the Earth, isometric",
90
- "Beeuty": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, map of the Earth, isometric",
91
- "Scifi City": "humans, missing_buildings, vehicles, text, logos, reflections, shadows, Earth, isometric",
92
- "Alien Landscape": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric",
93
- "Alien World": "Earth, humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric",
94
- "Mystic Forest": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric",
95
- "Medieval Battlefield": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric",
96
- "Dungeon Interior":"humans, modern_buildings, vehicles, text, logos, reflections, shadows, outdoor elements, realistic map of the Earth, isometric",
97
- "Desert Wasteland":"humans, modern_buildings, vehicles, text, logos, reflections, shadows, lush forests, large bodies of water, snow, realistic map of the Earth, isometric",
98
  "Prompt": None # Indicates that the negative prompt should be taken from negative_prompt_textbox
99
  }
100
 
 
69
 
70
 
71
  PROMPTS = {
72
+ "Mecha Wasteland Arena": "Regional overhead view, directly from above, centered on the map, orthographic Mecha battlefield map. post-industrial wasteland with crumbling structures, volcanic ridges, scrapyards, and ash plains. Features elevated overwatch positions for long-range combat and tight brawling areas for close-quarters engagements. Partial edge hexes are black. Colors: red, gray, muted orange, ash white, dark brown.",
73
+ "BorderBlack": "Planetary overhead view, directly from above, centered on the planet’s surface, orthographic hexagon-based alien world map with black borders. Features rivers, mountains, volcanoes, and polar snow regions. Colors: light blue, green, tan, brown. No reflections or shadows. Partial edge hexes are black.",
74
+ "Earth": "Planetary overhead view, directly from above, centered on the planet’s surface, orthographic Earth-like world map with rivers, mountains, volcanoes, and polar snow regions. Colors: light blue, green, tan, brown. No reflections or shadows. Partial edge hexes are black.",
75
+ "Beeuty": "Regional overhead view, directly from above, centered on the map, orthographic tabletop gaming map with honeycomb-shaped terrain, lakes, dense forests, magical flora, and hex grids. Designed for clarity and strategic gameplay. Colors: yellow, green, purple, brown. Partial edge hexes are black.",
76
+ "Scifi City": "Regional overhead view, directly from above, centered on the map, orthographic futuristic urban battlefield map with lakes, forests, ruined buildings, and city streets. Designed for clarity and strategic gameplay in tabletop games. Colors: teal, dark green, violet, brown. Partial edge hexes are black.",
77
+ "Alien Landscape": "Planetary overhead view, directly from above, centered on the planet’s surface, orthographic barren alien world map composed of hexagon tiles. Features light blue rivers, brown mountains, red volcanoes, and white polar snow. Colors: light blue, green, tan, brown. Partial edge hexes are black.",
78
+ "Alien World": "Planetary overhead view, directly from above, centered on the planet’s surface, orthographic alien world map built from hexagon tiles. Includes rivers, mountains, volcanoes, and snowy regions. Colors: light blue, green, tan, brown. Partial edge hexes are black.",
79
+ "Mystic Forest": "Regional overhead view, directly from above, centered on the map, orthographic mystic forest map with lakes, dense forests, magical flora, and hex grids. Designed for clarity and strategic gameplay in tabletop gaming. Colors: light blue, green, purple, brown. Partial edge hexes are black.",
80
+ "Medieval Battlefield": "Regional overhead view, directly from above, centered on the map, orthographic medieval battlefield map with lakes, forests, and magical fauna. Designed for clarity and strategic gameplay in tabletop games. Colors: teal, dark green, violet, brown. Partial edge hexes are black.",
81
+ "Dungeon Interior": "Regional overhead view, directly from above, centered on the map, orthographic dungeon interior map for tabletop gaming. Features stone walls, corridors, rooms with doors, traps, and treasure chests. Designed for clarity and strategic gameplay. Colors: gray, brown, dark blue. Partial edge hexes are black.",
82
+ "Desert Wasteland": "Regional overhead view, directly from above, centered on the map, orthographic desert wasteland map for tabletop gaming. Features sand dunes, rocky canyons, oases, and ancient ruins. Colors: yellow, tan, brown, blue, green. Partial edge hexes are black.",
83
  "Prompt": None # Indicates that the prompt should be taken from prompt_textbox
84
  }
85
 
86
  NEGATIVE_PROMPTS = {
87
+ "Mecha Wasteland Arena": "humans, old_buildings, water, bright colors, text, logos, shadows, Earth geography, isometric, camera_angle",
88
+ "BorderBlack": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric, camera_angle",
89
+ "Earth": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, missing map of the Earth, isometric, camera_angle",
90
+ "Beeuty": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, map of the Earth, isometric, camera_angle",
91
+ "Scifi City": "humans, missing_buildings, vehicles, text, logos, reflections, shadows, Earth, isometric, camera_angle",
92
+ "Alien Landscape": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric, camera_angle",
93
+ "Alien World": "Earth, humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric, camera_angle",
94
+ "Mystic Forest": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric, camera_angle",
95
+ "Medieval Battlefield": "humans, modern_buildings, vehicles, text, logos, reflections, shadows, realistic map of the Earth, isometric, camera_angle",
96
+ "Dungeon Interior":"humans, modern_buildings, vehicles, text, logos, reflections, shadows, outdoor elements, realistic map of the Earth, isometric, camera_angle",
97
+ "Desert Wasteland":"humans, modern_buildings, vehicles, text, logos, reflections, shadows, lush forests, large bodies of water, snow, realistic map of the Earth, isometric, camera_angle",
98
  "Prompt": None # Indicates that the negative prompt should be taken from negative_prompt_textbox
99
  }
100
 
utils/hex_grid.py CHANGED
@@ -5,17 +5,21 @@ from PIL import Image
5
  import cairocffi as cairo
6
  import pangocffi
7
  import pangocairocffi
 
 
8
  from PIL import Image, ImageDraw, ImageChops, ImageFont #, ImageColor
9
  #from pilmoji import Pilmoji # Import Pilmoji for handling emojis
10
  from utils.excluded_colors import (
11
  excluded_color_list,
12
  )
13
- from utils.image_utils import multiply_and_blend_images
14
  from utils.color_utils import update_color_opacity, parse_hex_color, draw_rotated_text_with_emojis, hex_to_rgb
15
  import random # For random text options
16
  import utils.constants as constants # Import constants
17
  import ast
18
  from utils.misc import number_to_letter
 
 
19
 
20
  def calculate_font_size(hex_size, padding=0.6, size_ceil=20, min_font_size=8):
21
  """
@@ -71,8 +75,8 @@ def generate_hexagon_grid(hex_size, border_size, input_image=None, image_width=0
71
  draw = ImageDraw.Draw(image, mode="RGBA")
72
  hex_width = hex_size * 2
73
  hex_height = hex_size * 2
74
- hex_horizontal_spacing = hex_width + (hex_border_size if hex_border_size < 0 else 0) + x_spacing #* 0.8660254
75
- hex_vertical_spacing = hex_height + (hex_border_size if hex_border_size < 0 else 0) + y_spacing
76
  col = 0
77
  row = 0
78
  def draw_hexagon(x, y, color="#FFFFFFFF", rotation=0, outline_color="#12165380", outline_width=0, sides=6):
@@ -96,18 +100,19 @@ def generate_hexagon_grid(hex_size, border_size, input_image=None, image_width=0
96
  # Calculate offsets based on the number of sides
97
  if sides == 4:
98
  # Squares line up perfectly; no vertical offset is needed.
99
- x_offset = int(hex_width + hex_horizontal_spacing * 2)
100
  y_offset = 0
101
  rotation_offset = -45
102
  elif sides == 3:
103
  # For equilateral triangles, you might offset rows by about one-third
104
  # of the triangle�s height. Adjust as needed.
105
- x_offset = hex_width + hex_horizontal_spacing
106
- y_offset = int((hex_height * 2) + hex_vertical_spacing)
107
  rotation_offset = -60
108
- # Adjust y_offset for columns 1 and 3 to overlap
109
  if col % 2 == 1:
110
- y_offset -= (hex_height // 2) #* 0.8660254
 
111
  rotation_offset = 0
112
  else:
113
  # Default behavior (6 sides)
@@ -468,5 +473,95 @@ def generate_hexagon_grid_interface(hex_size, border_size, image, start_x, start
468
  custom_text_list = custom_text_list,
469
  custom_text_color_list= custom_text_color_list, sides=sides
470
  )
471
- overlay_image = multiply_and_blend_images(image, hexagon_grid_image, 50)
472
- return hexagon_grid_image, overlay_image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  import cairocffi as cairo
6
  import pangocffi
7
  import pangocairocffi
8
+ import numpy as np
9
+ import cv2
10
  from PIL import Image, ImageDraw, ImageChops, ImageFont #, ImageColor
11
  #from pilmoji import Pilmoji # Import Pilmoji for handling emojis
12
  from utils.excluded_colors import (
13
  excluded_color_list,
14
  )
15
+ from utils.image_utils import alpha_composite_with_control, open_image
16
  from utils.color_utils import update_color_opacity, parse_hex_color, draw_rotated_text_with_emojis, hex_to_rgb
17
  import random # For random text options
18
  import utils.constants as constants # Import constants
19
  import ast
20
  from utils.misc import number_to_letter
21
+ from utils.file_utils import get_file_parts
22
+ current_grid = None
23
 
24
  def calculate_font_size(hex_size, padding=0.6, size_ceil=20, min_font_size=8):
25
  """
 
75
  draw = ImageDraw.Draw(image, mode="RGBA")
76
  hex_width = hex_size * 2
77
  hex_height = hex_size * 2
78
+ hex_horizontal_spacing = (hex_width + (hex_border_size if hex_border_size < 0 else 0) + x_spacing) * ((6 / sides) if sides > 3 else 1.3333) #* 0.8660254
79
+ hex_vertical_spacing = (hex_height + (hex_border_size if hex_border_size < 0 else 0) + y_spacing) * ((6 / sides) if sides > 3 else 3.0)
80
  col = 0
81
  row = 0
82
  def draw_hexagon(x, y, color="#FFFFFFFF", rotation=0, outline_color="#12165380", outline_width=0, sides=6):
 
100
  # Calculate offsets based on the number of sides
101
  if sides == 4:
102
  # Squares line up perfectly; no vertical offset is needed.
103
+ x_offset = hex_size
104
  y_offset = 0
105
  rotation_offset = -45
106
  elif sides == 3:
107
  # For equilateral triangles, you might offset rows by about one-third
108
  # of the triangle�s height. Adjust as needed.
109
+ x_offset = -hex_border_size * 2 #hex_width * math.tan(math.pi / sides) - hex_border_size * 2
110
+ y_offset = -hex_border_size * 2
111
  rotation_offset = -60
112
+ # Adjust y_offset for columns 1 and 2 to overlap
113
  if col % 2 == 1:
114
+ x_offset += int(hex_size * 0.8660254)
115
+ y_offset -= int(hex_height * 1.5)
116
  rotation_offset = 0
117
  else:
118
  # Default behavior (6 sides)
 
473
  custom_text_list = custom_text_list,
474
  custom_text_color_list= custom_text_color_list, sides=sides
475
  )
476
+ overlay_image = alpha_composite_with_control(image, hexagon_grid_image, 50)
477
+ return hexagon_grid_image, overlay_image
478
+
479
+
480
+ def transform_grid(grid_path, tilt_angle=0, rotation_angle=0):
481
+ """
482
+ Transform a 2D grid image with a perspective tilt and optional rotation.
483
+
484
+ Args:
485
+ grid_path (str): Filepath to the 2D grid image.
486
+ tilt_angle (float): Tilt angle in degrees (0 to 90) for z-axis perspective.
487
+ rotation_angle (float): Rotation angle in degrees (0 to 360) in the x-y plane.
488
+
489
+ Returns:
490
+ str: Filepath to the transformed grid image.
491
+ """
492
+ if grid_path is None or tilt_angle is None or rotation_angle is None:
493
+ return grid_path
494
+
495
+ global current_grid
496
+ if "_transform" not in grid_path:
497
+ #save current grid for next round
498
+ current_grid = grid_path
499
+ else:
500
+ grid_path = current_grid
501
+
502
+ # Load the grid image
503
+ grid_original = open_image(grid_path).convert('RGBA') # RGBA for transparency
504
+ grid = grid_original.copy()
505
+ width, height = grid.size
506
+
507
+ # Step 1: Rotate the grid in the x-y plane (around z-axis) if needed
508
+ if rotation_angle != 0:
509
+ grid = grid.rotate(rotation_angle, expand=True, fillcolor=(0, 0, 0, 0))
510
+ # Resize back to original dimensions if necessary
511
+ if grid.size != (width, height):
512
+ grid = grid.resize((width, height), Image.Resampling.LANCZOS)
513
+
514
+ # Step 2: Define the perspective transformation
515
+ # Original corners of the grid (in grid space)
516
+ src_pts = np.array([
517
+ [0, 0], # Top-left
518
+ [width, 0], # Top-right
519
+ [width, height], # Bottom-right
520
+ [0, height] # Bottom-left
521
+ ], dtype=np.float32)
522
+
523
+ # Calculate the perspective shift based on tilt_angle
524
+ # Tilt angle of 0 means no perspective (flat), 90 means extreme perspective
525
+ # We simulate the top moving away by shrinking the top width
526
+ perspective_factor = np.sin(np.radians(tilt_angle)) # 0 to 1
527
+ top_width_shrink = width * (1 - perspective_factor * 0.8) # Shrink top by up to 80%
528
+ top_shift = (width - top_width_shrink) / 2
529
+
530
+ # Destination points after perspective transform
531
+ dst_pts = np.array([
532
+ [top_shift, 0], # Top-left
533
+ [width - top_shift, 0], # Top-right
534
+ [width, height], # Bottom-right
535
+ [0, height] # Bottom-left
536
+ ], dtype=np.float32)
537
+
538
+ # Step 3: Compute the perspective transformation matrix
539
+ M = cv2.getPerspectiveTransform(src_pts, dst_pts)
540
+
541
+ # Step 4: Apply the perspective transformation using OpenCV
542
+ grid_np = np.array(grid)
543
+ transformed = cv2.warpPerspective(
544
+ grid_np,
545
+ M,
546
+ (width, height),
547
+ flags=cv2.INTER_LINEAR,
548
+ borderMode=cv2.BORDER_CONSTANT,
549
+ borderValue=(0, 0, 0, 0) # Transparent background
550
+ )
551
+
552
+ # Step 5: Convert back to PIL image
553
+ transformed_image = Image.fromarray(transformed)
554
+
555
+ # Step 6: Save the result
556
+ directory, _, name, _, new_ext = get_file_parts(grid_path)
557
+ if constants.TMPDIR:
558
+ directory = constants.TMPDIR
559
+ #save original image to temp folder
560
+ # output_path = os.path.join(directory, name + new_ext)
561
+ # grid_original.save(output_path)
562
+ # save new image to temp folder
563
+ new_filename = name + "_transform" + new_ext
564
+ output_path = os.path.join(directory, new_filename)
565
+ transformed_image.save(output_path)
566
+
567
+ return output_path
utils/image_utils.py CHANGED
@@ -10,7 +10,7 @@ from typing import List, Union, is_typeddict
10
  #import numpy as np
11
  #import math
12
  from pathlib import Path
13
- from utils.constants import default_lut_example_img, PRE_RENDERED_MAPS_JSON_LEVELS, BASE_HEIGHT
14
  from utils.color_utils import (
15
  detect_color_format,
16
  update_color_opacity
@@ -233,6 +233,42 @@ def change_color(image, color, opacity=0.75):
233
  return image
234
  return result
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  def convert_str_to_int_or_zero(value):
237
  """
238
  Converts a string to an integer, or returns zero if the conversion fails.
@@ -434,6 +470,8 @@ def multiply_and_blend_images(base_image, image2, alpha_percent=50):
434
  # Blend the multiplied result with the original
435
  blended_image = Image.blend(base_image, multiplied_image, alpha)
436
  if name is not None:
 
 
437
  new_image_path = os.path.join(directory, name + f"_mb{str(alpha_percent)}.png")
438
  blended_image.save(new_image_path)
439
  return new_image_path
@@ -475,6 +513,8 @@ def alpha_composite_with_control(base_image, image_with_alpha, alpha_percent=100
475
  # Composite the images
476
  result = Image.alpha_composite(base_image, image_with_alpha)
477
  if name is not None:
 
 
478
  new_image_path = os.path.join(directory, name + f"_alpha{str(alpha_percent)}.png")
479
  result.save(new_image_path)
480
  return new_image_path
@@ -890,6 +930,8 @@ def apply_lut_to_image_path(lut_filename: str, image_path: str, intensity: int =
890
 
891
  # Split the path into directory and filename
892
  directory, file_name = os.path.split(image_path)
 
 
893
  lut_directory, lut_file_name = os.path.split(lut_filename)
894
 
895
  # Split the filename into name and extension
 
10
  #import numpy as np
11
  #import math
12
  from pathlib import Path
13
+ from utils.constants import default_lut_example_img, PRE_RENDERED_MAPS_JSON_LEVELS, BASE_HEIGHT, TMPDIR
14
  from utils.color_utils import (
15
  detect_color_format,
16
  update_color_opacity
 
233
  return image
234
  return result
235
 
236
+ def blur_image(input_path, radius=5, blur_type="gaussian"):
237
+ input_path, _ = get_image_from_dict(input_path)
238
+ directory, _, name, _, new_ext = get_file_parts(input_path)
239
+ # If the extension changes, rename the file
240
+ name = name + "_blur"
241
+ output_path = os.path.join(directory, name + new_ext)
242
+
243
+ try:
244
+ # Open and verify the image
245
+ with open_image(input_path) as img:
246
+ # Convert to RGB if needed (handles RGBA, etc.)
247
+ if img.mode in ('RGBA', 'P'):
248
+ img = img.convert('RGB')
249
+
250
+ # Apply selected blur type
251
+ if blur_type.lower() == "gaussian":
252
+ blurred = img.filter(ImageFilter.GaussianBlur(radius=radius))
253
+ elif blur_type.lower() == "box":
254
+ blurred = img.filter(ImageFilter.BoxBlur(radius=radius))
255
+ elif blur_type.lower() == "simple":
256
+ blurred = img.filter(ImageFilter.BLUR)
257
+ else:
258
+ raise ValueError("Unsupported blur type")
259
+
260
+ # Save with quality setting
261
+ blurred.save(output_path, quality=95)
262
+ print(f"Successfully blurred image saved to {output_path}")
263
+
264
+ except FileNotFoundError:
265
+ print(f"Error: Input file '{input_path}' not found")
266
+ return None
267
+ except Exception as e:
268
+ print(f"Error processing image: {str(e)}")
269
+ return input_path
270
+ return output_path
271
+
272
  def convert_str_to_int_or_zero(value):
273
  """
274
  Converts a string to an integer, or returns zero if the conversion fails.
 
470
  # Blend the multiplied result with the original
471
  blended_image = Image.blend(base_image, multiplied_image, alpha)
472
  if name is not None:
473
+ if TMPDIR:
474
+ directory = TMPDIR
475
  new_image_path = os.path.join(directory, name + f"_mb{str(alpha_percent)}.png")
476
  blended_image.save(new_image_path)
477
  return new_image_path
 
513
  # Composite the images
514
  result = Image.alpha_composite(base_image, image_with_alpha)
515
  if name is not None:
516
+ if TMPDIR:
517
+ directory = TMPDIR
518
  new_image_path = os.path.join(directory, name + f"_alpha{str(alpha_percent)}.png")
519
  result.save(new_image_path)
520
  return new_image_path
 
930
 
931
  # Split the path into directory and filename
932
  directory, file_name = os.path.split(image_path)
933
+ if TMPDIR:
934
+ directory = TMPDIR
935
  lut_directory, lut_file_name = os.path.split(lut_filename)
936
 
937
  # Split the filename into name and extension