Spaces:
Running
on
Zero
Running
on
Zero
HURA 0.2.0
Browse files- README.md +2 -0
- app.py +48 -18
- utils/hex_hura.py +359 -43
README.md
CHANGED
|
@@ -48,6 +48,7 @@ Welcome to HexaGrid Creator, the ultimate tool for transforming your images into
|
|
| 48 |
- **Pre-rendered Maps:** Access a library of ready-to-use hexagon maps for quick customization.
|
| 49 |
- **Add Margins:** Add customizable margins around your images for a polished print-ready look.
|
| 50 |
- **Sketch Pad Integration:** Directly sketch on images to modify or replace them before further processing.
|
|
|
|
| 51 |
|
| 52 |
|
| 53 |
### Why You'll Love It
|
|
@@ -68,6 +69,7 @@ Welcome to HexaGrid Creator, the ultimate tool for transforming your images into
|
|
| 68 |
- **TRELLIS Depth & 3D Model Generation:** Create detailed depth maps and 3D models, complete with GLB and Gaussian file extraction.
|
| 69 |
- **Add Margins:** Fine-tune image margins for a professional finish.
|
| 70 |
- **Sketch Pad Integration:** Use the built-in sketch pad to edit images on the fly before processing.
|
|
|
|
| 71 |
|
| 72 |
Join the hive and start creating with HexaGrid Creator today!
|
| 73 |
|
|
|
|
| 48 |
- **Pre-rendered Maps:** Access a library of ready-to-use hexagon maps for quick customization.
|
| 49 |
- **Add Margins:** Add customizable margins around your images for a polished print-ready look.
|
| 50 |
- **Sketch Pad Integration:** Directly sketch on images to modify or replace them before further processing.
|
| 51 |
+
- **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
|
| 52 |
|
| 53 |
|
| 54 |
### Why You'll Love It
|
|
|
|
| 69 |
- **TRELLIS Depth & 3D Model Generation:** Create detailed depth maps and 3D models, complete with GLB and Gaussian file extraction.
|
| 70 |
- **Add Margins:** Fine-tune image margins for a professional finish.
|
| 71 |
- **Sketch Pad Integration:** Use the built-in sketch pad to edit images on the fly before processing.
|
| 72 |
+
- **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
|
| 73 |
|
| 74 |
Join the hive and start creating with HexaGrid Creator today!
|
| 75 |
|
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from ast import
|
| 2 |
from tokenize import String
|
| 3 |
import gradio as gr
|
| 4 |
from numba.core.types import string
|
|
@@ -25,6 +25,7 @@ from trellis.pipelines import TrellisImageTo3DPipeline
|
|
| 25 |
from trellis.representations import Gaussian, MeshExtractResult
|
| 26 |
from trellis.utils import render_utils, postprocessing_utils
|
| 27 |
from pathlib import Path
|
|
|
|
| 28 |
|
| 29 |
import logging
|
| 30 |
#logging.getLogger("transformers.modeling_utils").setLevel(logging.ERROR)
|
|
@@ -746,6 +747,7 @@ def on_input_image_change(image_path):
|
|
| 746 |
return None, gr.update()
|
| 747 |
img, img_path = convert_to_rgba_png(image_path)
|
| 748 |
width, height = img.size
|
|
|
|
| 749 |
return [img_path, gr.update(width=width, height=height)]
|
| 750 |
|
| 751 |
def update_sketch_dimensions(input_image, sketch_image):
|
|
@@ -761,18 +763,21 @@ def update_sketch_dimensions(input_image, sketch_image):
|
|
| 761 |
return [sk_img, gr.update()]
|
| 762 |
|
| 763 |
|
| 764 |
-
def composite_with_control_sync(input_image,
|
|
|
|
| 765 |
# Load the images using open_image() if they are provided as file paths.
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
|
|
|
|
|
|
| 769 |
|
| 770 |
-
# Resize
|
| 771 |
-
if in_img.size !=
|
| 772 |
-
|
| 773 |
|
| 774 |
# Now composite using the original alpha_composite_with_control function.
|
| 775 |
-
result_img = alpha_composite_with_control(in_img,
|
| 776 |
return result_img
|
| 777 |
|
| 778 |
def replace_with_sketch_image(sketch_image, replace_current_lut_example_img: bool = False):
|
|
@@ -1168,14 +1173,16 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
|
|
| 1168 |
HexaGrid Creator is a web-based application that allows you to apply a hexagon grid overlay to any image. You can customize the size, color, and opacity of the hexagons, as well as the background and border colors. The result is a visually striking image that looks like it was made from hexagonal tiles!
|
| 1169 |
|
| 1170 |
### What Can You Do?
|
| 1171 |
-
- **Generate Hexagon Grids:** Create
|
| 1172 |
-
- **AI-Powered Image Generation:** Use advanced AI models to generate images
|
| 1173 |
-
- **Color Exclusion:**
|
| 1174 |
-
- **Interactive Customization:** Adjust
|
| 1175 |
-
- **Depth
|
| 1176 |
-
- **Image Filter [
|
| 1177 |
-
- **Pre-rendered Maps:** Access a library of
|
| 1178 |
-
- **Add Margins:** Add customizable margins around your images for a polished
|
|
|
|
|
|
|
| 1179 |
|
| 1180 |
### Why You'll Love It
|
| 1181 |
- **Fun and Easy to Use:** With an intuitive interface and real-time previews, creating hexagon grids has never been this fun!
|
|
@@ -1194,6 +1201,8 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
|
|
| 1194 |
- **Image Filter [Look-Up Table (LUT)] Application:** Apply filters (LUTs) to your images for color grading and enhancement.
|
| 1195 |
- **Depth and 3D Model Generation:** Create depth maps and 3D models from your images for enhanced visualization.
|
| 1196 |
- **Add Margins:** Customize margins around your images for a polished finish.
|
|
|
|
|
|
|
| 1197 |
|
| 1198 |
Join the hive and start creating with HexaGrid Creator today!
|
| 1199 |
|
|
@@ -1300,11 +1309,32 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
|
|
| 1300 |
composite_button = gr.Button("Composite to Input Image", elem_classes="solid")
|
| 1301 |
composite_sketch_button = gr.Button("Composite to Sketh", elem_classes="solid")
|
| 1302 |
with gr.Accordion("Blur", open = False):
|
| 1303 |
-
with gr.Row():
|
| 1304 |
blur_amount = gr.Slider(label="Blur Amount", minimum=0, maximum=100, value=5, interactive=True)
|
| 1305 |
with gr.Row():
|
| 1306 |
blur_button = gr.Button("Blur Input Image", elem_classes="solid")
|
| 1307 |
blur_sketch_button = gr.Button("Blur Sketch", elem_classes="solid")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1308 |
with gr.Tabs(selected="hex_gen") as input_tabs:
|
| 1309 |
with gr.Tab("HexaGrid Generation", id="hex_gen") as hexa_gen_tab:
|
| 1310 |
with gr.Column(elem_classes="outline-important"):
|
|
|
|
| 1 |
+
#from ast import Constant
|
| 2 |
from tokenize import String
|
| 3 |
import gradio as gr
|
| 4 |
from numba.core.types import string
|
|
|
|
| 25 |
from trellis.representations import Gaussian, MeshExtractResult
|
| 26 |
from trellis.utils import render_utils, postprocessing_utils
|
| 27 |
from pathlib import Path
|
| 28 |
+
import utils.hex_hura as hex_hura
|
| 29 |
|
| 30 |
import logging
|
| 31 |
#logging.getLogger("transformers.modeling_utils").setLevel(logging.ERROR)
|
|
|
|
| 747 |
return None, gr.update()
|
| 748 |
img, img_path = convert_to_rgba_png(image_path)
|
| 749 |
width, height = img.size
|
| 750 |
+
hex_components["set_height_width_hura_image"](width, height)
|
| 751 |
return [img_path, gr.update(width=width, height=height)]
|
| 752 |
|
| 753 |
def update_sketch_dimensions(input_image, sketch_image):
|
|
|
|
| 763 |
return [sk_img, gr.update()]
|
| 764 |
|
| 765 |
|
| 766 |
+
def composite_with_control_sync(input_image, new_image, slider_value):
|
| 767 |
+
|
| 768 |
# Load the images using open_image() if they are provided as file paths.
|
| 769 |
+
new_img_path, _ = get_image_from_dict(new_image)
|
| 770 |
+
if input_image is None:
|
| 771 |
+
return new_img_path
|
| 772 |
+
in_img = open_image(input_image) if isinstance(input_image, (dict,str)) else input_image
|
| 773 |
+
new_img = open_image(new_img_path)
|
| 774 |
|
| 775 |
+
# Resize new image if dimensions don't match input image.
|
| 776 |
+
if in_img.size != new_img.size:
|
| 777 |
+
new_img = new_img.resize(in_img.size, Image.LANCZOS)
|
| 778 |
|
| 779 |
# Now composite using the original alpha_composite_with_control function.
|
| 780 |
+
result_img = alpha_composite_with_control(in_img, new_img, slider_value)
|
| 781 |
return result_img
|
| 782 |
|
| 783 |
def replace_with_sketch_image(sketch_image, replace_current_lut_example_img: bool = False):
|
|
|
|
| 1173 |
HexaGrid Creator is a web-based application that allows you to apply a hexagon grid overlay to any image. You can customize the size, color, and opacity of the hexagons, as well as the background and border colors. The result is a visually striking image that looks like it was made from hexagonal tiles!
|
| 1174 |
|
| 1175 |
### What Can You Do?
|
| 1176 |
+
- **Generate Hexagon Grids:** Create stunning hexagon, square, or triangle grid overlays with fully customizable parameters.
|
| 1177 |
+
- **AI-Powered Image Generation:** Use advanced AI models and LoRA weights to generate images from your prompts and apply unique grid overlays.
|
| 1178 |
+
- **Color Exclusion:** Pick and exclude specific colors from your hexagon grid for improved clarity.
|
| 1179 |
+
- **Interactive Customization:** Adjust grid size, border size, rotation, background color, and more—all in real-time.
|
| 1180 |
+
- **Depth & 3D Model Generation:** Generate depth maps and interactive 3D models (with GLB and Gaussian extraction) for enhanced visualization.
|
| 1181 |
+
- **Image Filter [LUT] Application:** Apply advanced color grading filters with live previews using LUT files.
|
| 1182 |
+
- **Pre-rendered Maps:** Access a library of ready-to-use hexagon maps for quick customization.
|
| 1183 |
+
- **Add Margins:** Add customizable margins around your images for a polished print-ready look.
|
| 1184 |
+
- **Sketch Pad Integration:** Directly sketch on images to modify or replace them before further processing.
|
| 1185 |
+
- **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
|
| 1186 |
|
| 1187 |
### Why You'll Love It
|
| 1188 |
- **Fun and Easy to Use:** With an intuitive interface and real-time previews, creating hexagon grids has never been this fun!
|
|
|
|
| 1201 |
- **Image Filter [Look-Up Table (LUT)] Application:** Apply filters (LUTs) to your images for color grading and enhancement.
|
| 1202 |
- **Depth and 3D Model Generation:** Create depth maps and 3D models from your images for enhanced visualization.
|
| 1203 |
- **Add Margins:** Customize margins around your images for a polished finish.
|
| 1204 |
+
- **Sketch Pad Integration:** Use the built-in sketch pad to edit images on the fly before processing.
|
| 1205 |
+
- **HURA:** Generate HURA (Hexagonal Uniformly Redundant Arrays) patterns for sketches and masks
|
| 1206 |
|
| 1207 |
Join the hive and start creating with HexaGrid Creator today!
|
| 1208 |
|
|
|
|
| 1309 |
composite_button = gr.Button("Composite to Input Image", elem_classes="solid")
|
| 1310 |
composite_sketch_button = gr.Button("Composite to Sketh", elem_classes="solid")
|
| 1311 |
with gr.Accordion("Blur", open = False):
|
| 1312 |
+
with gr.Row():
|
| 1313 |
blur_amount = gr.Slider(label="Blur Amount", minimum=0, maximum=100, value=5, interactive=True)
|
| 1314 |
with gr.Row():
|
| 1315 |
blur_button = gr.Button("Blur Input Image", elem_classes="solid")
|
| 1316 |
blur_sketch_button = gr.Button("Blur Sketch", elem_classes="solid")
|
| 1317 |
+
with gr.Accordion("Generate Hex Hura Background", open = False):
|
| 1318 |
+
hex_components = hex_hura.render()
|
| 1319 |
+
with gr.Row():
|
| 1320 |
+
hura_alpha_composite_slider = gr.Slider(0,100,50,0.5, label="HURA Transparancy", elem_id="hura_alpha_composite_slider", interactive=True)
|
| 1321 |
+
with gr.Row():
|
| 1322 |
+
hura_button = gr.Button("Composite Input Image", elem_classes="solid")
|
| 1323 |
+
hura_sketch_button = gr.Button("Composite Sketh Image", elem_classes="solid")
|
| 1324 |
+
|
| 1325 |
+
hura_sketch_button.click(
|
| 1326 |
+
fn=composite_with_control_sync,
|
| 1327 |
+
inputs=[sketch_image, hex_components["target_image"], hura_alpha_composite_slider],
|
| 1328 |
+
outputs=[sketch_image],
|
| 1329 |
+
scroll_to_output=True
|
| 1330 |
+
)
|
| 1331 |
+
hura_button.click(
|
| 1332 |
+
fn=composite_with_control_sync,
|
| 1333 |
+
inputs=[input_image, hex_components["target_image"], hura_alpha_composite_slider],
|
| 1334 |
+
outputs=[input_image],
|
| 1335 |
+
scroll_to_output=True
|
| 1336 |
+
)
|
| 1337 |
+
|
| 1338 |
with gr.Tabs(selected="hex_gen") as input_tabs:
|
| 1339 |
with gr.Tab("HexaGrid Generation", id="hex_gen") as hexa_gen_tab:
|
| 1340 |
with gr.Column(elem_classes="outline-important"):
|
utils/hex_hura.py
CHANGED
|
@@ -1,12 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from PIL import Image
|
| 2 |
import math
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
# Define the smoothstep function for vignette effect
|
| 8 |
def smoothstep(edge0, edge1, x):
|
| 9 |
-
"""
|
|
|
|
|
|
|
|
|
|
| 10 |
if edge0 == edge1:
|
| 11 |
return 0.0 if x < edge0 else 1.0
|
| 12 |
t = min(max((x - edge0) / (edge1 - edge0), 0.0), 1.0)
|
|
@@ -15,16 +112,24 @@ def smoothstep(edge0, edge1, x):
|
|
| 15 |
# Define the hexagon function to compute coordinates
|
| 16 |
def hexagon(p):
|
| 17 |
"""
|
| 18 |
-
Compute hexagon coordinates and
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
"""
|
| 21 |
# Transform to hexagonal coordinate system
|
| 22 |
-
q = (p[0] * 2.0 *
|
| 23 |
pi = (math.floor(q[0]), math.floor(q[1]))
|
| 24 |
pf = (q[0] - pi[0], q[1] - pi[1])
|
| 25 |
-
|
| 26 |
-
ca = 1.0 if
|
| 27 |
-
cb = 1.0 if
|
| 28 |
ma = (1.0 if pf[1] >= pf[0] else 0.0, 1.0 if pf[0] >= pf[1] else 0.0)
|
| 29 |
temp = (
|
| 30 |
1.0 - pf[1] + ca * (pf[0] + pf[1] - 1.0) + cb * (pf[1] - 2.0 * pf[0]),
|
|
@@ -38,38 +143,41 @@ def hexagon(p):
|
|
| 38 |
h_xy = (pi[0] + ca - cb * ma[0], pi[1] + ca - cb * ma[1])
|
| 39 |
return (h_xy[0], h_xy[1], e, f)
|
| 40 |
|
| 41 |
-
|
|
|
|
| 42 |
"""
|
| 43 |
-
Generate
|
| 44 |
-
|
| 45 |
-
Parameters:
|
| 46 |
-
p (tuple): A 2-tuple (x, y) of coordinates.
|
| 47 |
-
r (float): Multiplier for p[0]. Default is 1.0.
|
| 48 |
-
v (float): Modulus value controlling the pattern frequency. Default is 10.0.
|
| 49 |
|
|
|
|
|
|
|
|
|
|
| 50 |
Returns:
|
| 51 |
-
float: 1.0
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
rz = 1.0
|
| 56 |
-
for i in range(1, int(v/2)):
|
| 57 |
if math.isclose(math.fmod(i * i, v), l, abs_tol=1e-6):
|
| 58 |
rz = 0.0
|
| 59 |
break
|
| 60 |
return rz
|
| 61 |
|
| 62 |
-
# Define the color palette
|
| 63 |
-
default_colors = [
|
| 64 |
-
(255, 0, 0), # Red
|
| 65 |
-
(0, 255, 0), # Green
|
| 66 |
-
(0, 0, 255) # Blue
|
| 67 |
-
]
|
| 68 |
-
|
| 69 |
# Generate the image with colorful_hexagonal pattern
|
| 70 |
-
def generate_image_color(width, height, colors=
|
| 71 |
"""Generate an RGB image with a colorful hexagonal pattern."""
|
| 72 |
img = Image.new('RGB', (width, height))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
aspect = width / height
|
| 74 |
for j in range(height):
|
| 75 |
for i in range(width):
|
|
@@ -81,14 +189,15 @@ def generate_image_color(width, height, colors=default_colors):
|
|
| 81 |
p_y = q_y * 2.0 - 1.0
|
| 82 |
p = (p_x, p_y)
|
| 83 |
# Scale coordinates for pattern frequency
|
| 84 |
-
h = hexagon((p[0] *
|
| 85 |
h_xy = (int(h[0]), int(h[1]))
|
| 86 |
# Assign color based on hexagon coordinates
|
| 87 |
-
|
|
|
|
| 88 |
col = colors[color_index]
|
| 89 |
# Apply vignette effect
|
| 90 |
q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
|
| 91 |
-
vignette = smoothstep(
|
| 92 |
col = tuple(int(c * vignette) for c in col)
|
| 93 |
# Set the pixel color
|
| 94 |
img.putpixel((i, j), col)
|
|
@@ -104,7 +213,7 @@ def generate_image_grayscale(width, height):
|
|
| 104 |
p_x = (q_x * 2.0 - 1.0) * aspect
|
| 105 |
p_y = q_y * 2.0 - 1.0
|
| 106 |
p = (p_x, p_y)
|
| 107 |
-
h = hexagon((p[0] *
|
| 108 |
rz = ura(h[:2])
|
| 109 |
smooth = smoothstep(-0.2, 0.13, h[2])
|
| 110 |
if rz > 0.5:
|
|
@@ -112,16 +221,223 @@ def generate_image_grayscale(width, height):
|
|
| 112 |
else:
|
| 113 |
col = 1.0 - smooth
|
| 114 |
q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
|
| 115 |
-
vignette = smoothstep(
|
| 116 |
col *= vignette
|
| 117 |
-
color = int(col * 255)
|
| 118 |
img.putpixel((i, j), (color, color, color))
|
| 119 |
return img
|
| 120 |
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# HURA (Hexagonal Uniformly Redundant Arrays) are used for aperture masks and imaging, and encoding.
|
| 2 |
+
# check it out https://ntrs.nasa.gov/citations/19850026627
|
| 3 |
+
# by Surn (Charles Fettinger) 4/5/2025
|
| 4 |
from PIL import Image
|
| 5 |
import math
|
| 6 |
+
import gradio as gr
|
| 7 |
+
from tempfile import NamedTemporaryFile
|
| 8 |
|
| 9 |
+
from transformers.models.deprecated.vit_hybrid import image_processing_vit_hybrid
|
| 10 |
+
import utils.constants as constants
|
| 11 |
+
import utils.color_utils as color_utils
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class HuraConfig:
|
| 15 |
+
"""Configuration for Hexagonal Uniformly Redundant Array pattern generation."""
|
| 16 |
+
|
| 17 |
+
def __init__(self):
|
| 18 |
+
# Core parameters
|
| 19 |
+
self.v = 139 # Prime number parameter (affects pattern complexity)
|
| 20 |
+
self.r = 42 # Pattern frequency parameter
|
| 21 |
+
self.version = "0.2.0"
|
| 22 |
+
|
| 23 |
+
# Pattern generation constants
|
| 24 |
+
self.hex_ratio = 0.5773503 # sqrt(3)/3
|
| 25 |
+
self.pattern_scale = 21.0 # Controls pattern frequency
|
| 26 |
+
self.vignette_inner = 0.97
|
| 27 |
+
self.vignette_outer = 1.01
|
| 28 |
+
|
| 29 |
+
# Colors
|
| 30 |
+
self.default_colors = [
|
| 31 |
+
(255, 0, 0), # Red
|
| 32 |
+
(0, 255, 0), # Green
|
| 33 |
+
(0, 0, 255) # Blue
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
# Prime number calculation
|
| 37 |
+
self.prime_range_start = 1
|
| 38 |
+
self.prime_range_end = 5001
|
| 39 |
+
self._primes_cache = None # Will be lazily loaded
|
| 40 |
+
|
| 41 |
+
def get_v(self):
|
| 42 |
+
"""Get the current V parameter value."""
|
| 43 |
+
return self.v
|
| 44 |
+
|
| 45 |
+
def set_v(self, value):
|
| 46 |
+
"""Set the V parameter value."""
|
| 47 |
+
if not isinstance(value, (int, float)) or value < 1:
|
| 48 |
+
raise ValueError(f"V value must be a positive float, got {value}")
|
| 49 |
+
self.v = value
|
| 50 |
+
|
| 51 |
+
def get_r(self):
|
| 52 |
+
"""Get the current R parameter value."""
|
| 53 |
+
return self.r
|
| 54 |
+
|
| 55 |
+
def set_r(self, value):
|
| 56 |
+
"""Set the R parameter value."""
|
| 57 |
+
if not isinstance(value, (int, float)) or value < 1:
|
| 58 |
+
raise ValueError(f"R value must be a positive float, got {value}")
|
| 59 |
+
self.r = value
|
| 60 |
+
|
| 61 |
+
def get_primes(self):
|
| 62 |
+
"""Get or calculate the list of primes in the configured range."""
|
| 63 |
+
if self._primes_cache is None:
|
| 64 |
+
self._primes_cache = get_primes_in_range(self.prime_range_start, self.prime_range_end)
|
| 65 |
+
return self._primes_cache
|
| 66 |
+
|
| 67 |
+
def find_nearest_prime(self, value):
|
| 68 |
+
"""Find the nearest prime number to the given value."""
|
| 69 |
+
return min(self.get_primes(), key=lambda x: abs(x - value))
|
| 70 |
+
|
| 71 |
+
def reset_colors(self):
|
| 72 |
+
"""Reset default colors to original values."""
|
| 73 |
+
self.default_colors = [
|
| 74 |
+
(255, 0, 0), # Red
|
| 75 |
+
(0, 255, 0), # Green
|
| 76 |
+
(0, 0, 255) # Blue
|
| 77 |
+
]
|
| 78 |
+
return self.default_colors
|
| 79 |
+
|
| 80 |
+
# Initialize the HuraConfig instance
|
| 81 |
+
config = HuraConfig()
|
| 82 |
+
|
| 83 |
+
# For backwards compatibility - consider deprecating these
|
| 84 |
+
__version__ = config.version
|
| 85 |
+
_V = config.v
|
| 86 |
+
_R = config.r
|
| 87 |
+
|
| 88 |
+
def get_v():
|
| 89 |
+
return config.get_v()
|
| 90 |
+
|
| 91 |
+
def set_v(val):
|
| 92 |
+
config.set_v(val)
|
| 93 |
+
|
| 94 |
+
def get_r():
|
| 95 |
+
return config.get_r()
|
| 96 |
+
|
| 97 |
+
def set_r(val):
|
| 98 |
+
config.set_r(val)
|
| 99 |
+
|
| 100 |
+
state_colors = []
|
| 101 |
|
|
|
|
| 102 |
def smoothstep(edge0, edge1, x):
|
| 103 |
+
"""
|
| 104 |
+
Smoothstep function for vignette effect.
|
| 105 |
+
Smoothly interpolate between edge0 and edge1 based on x.
|
| 106 |
+
"""
|
| 107 |
if edge0 == edge1:
|
| 108 |
return 0.0 if x < edge0 else 1.0
|
| 109 |
t = min(max((x - edge0) / (edge1 - edge0), 0.0), 1.0)
|
|
|
|
| 112 |
# Define the hexagon function to compute coordinates
|
| 113 |
def hexagon(p):
|
| 114 |
"""
|
| 115 |
+
Compute hexagon coordinates and metrics for point p.
|
| 116 |
+
|
| 117 |
+
Args:
|
| 118 |
+
p (tuple): Normalized point (x,y) in [-aspect,aspect] � [-1,1] range
|
| 119 |
+
|
| 120 |
+
Returns:
|
| 121 |
+
tuple: (hex_x, hex_y, edge_distance, center_distance)
|
| 122 |
+
- hex_x, hex_y: Integer coordinates of the hexagon cell
|
| 123 |
+
- edge_distance: Distance to nearest edge (0-1)
|
| 124 |
+
- center_distance: Distance to cell center (0-1)
|
| 125 |
"""
|
| 126 |
# Transform to hexagonal coordinate system
|
| 127 |
+
q = (p[0] * 2.0 * config.hex_ratio, p[1] + p[0] * config.hex_ratio)
|
| 128 |
pi = (math.floor(q[0]), math.floor(q[1]))
|
| 129 |
pf = (q[0] - pi[0], q[1] - pi[1])
|
| 130 |
+
mod_val = (pi[0] + pi[1]) % 3.0 # renamed from v
|
| 131 |
+
ca = 1.0 if mod_val >= 1.0 else 0.0
|
| 132 |
+
cb = 1.0 if mod_val >= 2.0 else 0.0
|
| 133 |
ma = (1.0 if pf[1] >= pf[0] else 0.0, 1.0 if pf[0] >= pf[1] else 0.0)
|
| 134 |
temp = (
|
| 135 |
1.0 - pf[1] + ca * (pf[0] + pf[1] - 1.0) + cb * (pf[1] - 2.0 * pf[0]),
|
|
|
|
| 143 |
h_xy = (pi[0] + ca - cb * ma[0], pi[1] + ca - cb * ma[1])
|
| 144 |
return (h_xy[0], h_xy[1], e, f)
|
| 145 |
|
| 146 |
+
# important note: this is not a true hexagonal pattern, but a hexagonal grid
|
| 147 |
+
def ura(p):
|
| 148 |
"""
|
| 149 |
+
Generate binary pattern value based on Uniformly Redundant Array algorithm.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
+
Args:
|
| 152 |
+
p (tuple): Hexagon coordinates (x,y)
|
| 153 |
+
|
| 154 |
Returns:
|
| 155 |
+
float: 1.0 for pattern, 0.0 for background
|
| 156 |
+
|
| 157 |
+
future consideration.. add animation
|
| 158 |
+
#ifdef INCREMENT_R
|
| 159 |
+
float l = mod(p.y + floor(time*1.5)*p.x, v);
|
| 160 |
+
#else
|
| 161 |
+
float l = mod(p.y + r*p.x, v);
|
| 162 |
+
"""
|
| 163 |
+
r = get_r()
|
| 164 |
+
v = get_v()
|
| 165 |
+
l = math.fmod(abs(p[1]) + r * abs(p[0]), v)
|
| 166 |
rz = 1.0
|
| 167 |
+
for i in range(1, int(v/2) + 1):
|
| 168 |
if math.isclose(math.fmod(i * i, v), l, abs_tol=1e-6):
|
| 169 |
rz = 0.0
|
| 170 |
break
|
| 171 |
return rz
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
# Generate the image with colorful_hexagonal pattern
|
| 174 |
+
def generate_image_color(width, height, colors=None):
|
| 175 |
"""Generate an RGB image with a colorful hexagonal pattern."""
|
| 176 |
img = Image.new('RGB', (width, height))
|
| 177 |
+
if colors is None or colors == []:
|
| 178 |
+
colors = config.default_colors
|
| 179 |
+
r = config.get_r()
|
| 180 |
+
v = config.get_v()
|
| 181 |
aspect = width / height
|
| 182 |
for j in range(height):
|
| 183 |
for i in range(width):
|
|
|
|
| 189 |
p_y = q_y * 2.0 - 1.0
|
| 190 |
p = (p_x, p_y)
|
| 191 |
# Scale coordinates for pattern frequency
|
| 192 |
+
h = hexagon((p[0] * config.pattern_scale, p[1] * config.pattern_scale))
|
| 193 |
h_xy = (int(h[0]), int(h[1]))
|
| 194 |
# Assign color based on hexagon coordinates
|
| 195 |
+
rz = math.fmod(abs(h_xy[0]) + r * abs(h_xy[1]),v)
|
| 196 |
+
color_index = int(rz % len(colors))
|
| 197 |
col = colors[color_index]
|
| 198 |
# Apply vignette effect
|
| 199 |
q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
|
| 200 |
+
vignette = smoothstep(config.vignette_outer, config.vignette_inner, max(abs(q[0]), abs(q[1])))
|
| 201 |
col = tuple(int(c * vignette) for c in col)
|
| 202 |
# Set the pixel color
|
| 203 |
img.putpixel((i, j), col)
|
|
|
|
| 213 |
p_x = (q_x * 2.0 - 1.0) * aspect
|
| 214 |
p_y = q_y * 2.0 - 1.0
|
| 215 |
p = (p_x, p_y)
|
| 216 |
+
h = hexagon((p[0] * config.pattern_scale, p[1] * config.pattern_scale))
|
| 217 |
rz = ura(h[:2])
|
| 218 |
smooth = smoothstep(-0.2, 0.13, h[2])
|
| 219 |
if rz > 0.5:
|
|
|
|
| 221 |
else:
|
| 222 |
col = 1.0 - smooth
|
| 223 |
q = (q_x * 2.0 - 1.0, q_y * 2.0 - 1.0)
|
| 224 |
+
vignette = smoothstep(config.vignette_outer, config.vignette_inner, max(abs(q[0]), abs(q[1])))
|
| 225 |
col *= vignette
|
| 226 |
+
color = int(abs(col) * 255)
|
| 227 |
img.putpixel((i, j), (color, color, color))
|
| 228 |
return img
|
| 229 |
|
| 230 |
+
def get_primes_in_range(start: int, end: int) -> list:
|
| 231 |
+
"""
|
| 232 |
+
Return a list of prime numbers between start and end (inclusive).
|
| 233 |
+
|
| 234 |
+
Uses the Sieve of Eratosthenes for efficiency.
|
| 235 |
+
|
| 236 |
+
Parameters:
|
| 237 |
+
start (int): The starting number of the range.
|
| 238 |
+
end (int): The ending number of the range.
|
| 239 |
+
|
| 240 |
+
Returns:
|
| 241 |
+
list: A list of prime numbers between start and end.
|
| 242 |
+
"""
|
| 243 |
+
if end < 2:
|
| 244 |
+
return []
|
| 245 |
+
sieve = [True] * (end + 1)
|
| 246 |
+
sieve[0] = sieve[1] = False
|
| 247 |
+
for i in range(2, int(end ** 0.5) + 1):
|
| 248 |
+
if sieve[i]:
|
| 249 |
+
for j in range(i * i, end + 1, i):
|
| 250 |
+
sieve[j] = False
|
| 251 |
+
return [i for i in range(start, end + 1) if sieve[i]]
|
| 252 |
+
|
| 253 |
+
def find_nearest_prime(value):
|
| 254 |
+
"""Find the closest prime number to the given value."""
|
| 255 |
+
return config.find_nearest_prime(value)
|
| 256 |
+
|
| 257 |
+
def generate_pattern_background(pattern_type="color", width=1024, height=768, v_value=_V, r_value=_R, colors=None):
|
| 258 |
+
# Generate a hexagonal pattern image with the given parameters.
|
| 259 |
+
# Do not pass gr.State values here
|
| 260 |
+
# Set the parameters
|
| 261 |
+
set_v(v_value)
|
| 262 |
+
set_r(r_value)
|
| 263 |
+
print(f"Generating pattern with V: {v_value}, R: {r_value}, Colors: {colors}")
|
| 264 |
+
color_count = 3
|
| 265 |
+
|
| 266 |
+
if pattern_type == "color":
|
| 267 |
+
if colors is None:
|
| 268 |
+
img = generate_image_color(width, height)
|
| 269 |
+
else:
|
| 270 |
+
img = generate_image_color(width, height, colors)
|
| 271 |
+
color_count = len(colors)
|
| 272 |
+
else: # grayscale
|
| 273 |
+
img = generate_image_grayscale(width, height)
|
| 274 |
+
color_count = 1
|
| 275 |
+
|
| 276 |
+
# Save to temporary file and return path
|
| 277 |
+
with NamedTemporaryFile(delete=False,prefix=f"hura_{str(color_count)}_v{str(v_value)}_r{str(r_value)}_", suffix=".png") as tmp:
|
| 278 |
+
img.save(tmp.name, format="PNG")
|
| 279 |
+
constants.temp_files.append(tmp.name)
|
| 280 |
+
return tmp.name
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
def create_color_swatch_html(colors):
|
| 284 |
+
"""Create HTML for displaying color swatches"""
|
| 285 |
+
swatches = ''.join(
|
| 286 |
+
f'<div style="width: 50px; height: 50px; background-color: rgb{c}; '
|
| 287 |
+
f'border: 1px solid #ccc;"></div>'
|
| 288 |
+
for c in colors
|
| 289 |
+
)
|
| 290 |
+
return f'<div style="display: flex; gap: 10px;">{swatches}</div>'
|
| 291 |
+
|
| 292 |
+
def _add_color(color, color_list):
|
| 293 |
+
if color is None:
|
| 294 |
+
return color_list, color_list, ""
|
| 295 |
+
|
| 296 |
+
# Convert hex color to RGB
|
| 297 |
+
rgb_color = color_utils.hex_to_rgb(color)
|
| 298 |
+
color_list = color_list + [rgb_color]
|
| 299 |
+
|
| 300 |
+
# Create HTML to display color swatches
|
| 301 |
+
html = create_color_swatch_html(color_list)
|
| 302 |
+
return color_list, html
|
| 303 |
+
|
| 304 |
+
def _init_colors():
|
| 305 |
+
"""Initialize the color swatches HTML display based on config colors"""
|
| 306 |
+
updated_list = list(config.default_colors)
|
| 307 |
+
# Rebuild the HTML swatches from the updated list
|
| 308 |
+
html = create_color_swatch_html(updated_list)
|
| 309 |
+
return html
|
| 310 |
+
|
| 311 |
+
def reset_colors():
|
| 312 |
+
"""Reset the color list to the default colors."""
|
| 313 |
+
colors = config.reset_colors()
|
| 314 |
+
html = create_color_swatch_html(colors)
|
| 315 |
+
return colors, html
|
| 316 |
+
|
| 317 |
+
def _generate_pattern_from_state(pt, width, height, v_val, r_val, colors_list):
|
| 318 |
+
# colors_list is automatically the raw value from the gr.State input
|
| 319 |
+
return generate_pattern_background(pt, width, height, v_val, r_val, colors_list)
|
| 320 |
+
|
| 321 |
+
def render() -> dict:
|
| 322 |
+
"""
|
| 323 |
+
Renders a colorful or grayscale hexagonal pattern creation interface
|
| 324 |
+
|
| 325 |
+
Returns:
|
| 326 |
+
dict: A dictionary containing:
|
| 327 |
+
- target_image (gr.Image): The generated pattern image component
|
| 328 |
+
- run_generate_hex_pattern (function): Function to generate a pattern with given dimensions
|
| 329 |
+
- set_height_width_hura_image (function): Function to update the slider values
|
| 330 |
+
- width_slider (gr.Slider): The width slider component
|
| 331 |
+
- height_slider (gr.Slider): The height slider component
|
| 332 |
+
"""
|
| 333 |
+
|
| 334 |
+
# Initialize state
|
| 335 |
+
global state_colors
|
| 336 |
+
state_colors = gr.State(config.default_colors)
|
| 337 |
+
init_colors_html = _init_colors()
|
| 338 |
+
|
| 339 |
+
|
| 340 |
+
target_image = gr.Image(label="Generated Pattern", type="filepath")
|
| 341 |
+
with gr.Row():
|
| 342 |
+
pattern_type = gr.Radio(
|
| 343 |
+
label="Pattern Type",
|
| 344 |
+
choices=["color", "grayscale"],
|
| 345 |
+
value="grayscale",
|
| 346 |
+
type="value"
|
| 347 |
+
)
|
| 348 |
+
with gr.Column():
|
| 349 |
+
with gr.Row():
|
| 350 |
+
width_slider = gr.Slider(minimum=256, maximum=2560, value=1024, label="Width", step=8)
|
| 351 |
+
height_slider = gr.Slider(minimum=256, maximum=2560, value=768, label="Height", step=8)
|
| 352 |
+
v_value_slider = gr.Slider(minimum=config.prime_range_start, maximum=config.prime_range_end, value=config.v, label="V Value (Prime Number)", step=1)
|
| 353 |
+
r_value_slider = gr.Slider(minimum=1, maximum=100, value=config.r, label="R Value")
|
| 354 |
+
show_borders_chbox = gr.Checkbox(label="Show Borders", value=True)
|
| 355 |
+
|
| 356 |
+
with gr.Row(visible=False) as color_row:
|
| 357 |
+
color_picker = gr.ColorPicker(label="Pick a Color")
|
| 358 |
+
add_button = gr.Button("Add Color")
|
| 359 |
+
with gr.Column():
|
| 360 |
+
color_display = gr.HTML(label="Color Swatches", value=init_colors_html)
|
| 361 |
+
with gr.Row():
|
| 362 |
+
delete_colors_button = gr.Button("Delete Colors")
|
| 363 |
+
reset_colors_button = gr.Button("Reset Colors")
|
| 364 |
+
with gr.Row():
|
| 365 |
+
generate_button = gr.Button("Generate Pattern")
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
def run_generate_hex_pattern(width: int, height: int) -> str:
|
| 369 |
+
"""
|
| 370 |
+
Generate a colored hexagonal pattern image with the given width and height.
|
| 371 |
+
Uses default V and R values and the default color palette.
|
| 372 |
+
|
| 373 |
+
Returns:
|
| 374 |
+
str: The filepath of the generated image.
|
| 375 |
+
"""
|
| 376 |
+
global state_colors
|
| 377 |
+
width_slider.value=width
|
| 378 |
+
height_slider.value=height
|
| 379 |
+
gr.update()
|
| 380 |
+
# Use the current _V, _R, and default_colors
|
| 381 |
+
filepath = generate_pattern_background(
|
| 382 |
+
pattern_type="color",
|
| 383 |
+
width=width,
|
| 384 |
+
height=height,
|
| 385 |
+
v_value=get_v(),
|
| 386 |
+
r_value=get_r(),
|
| 387 |
+
colors=state_colors.value
|
| 388 |
+
)
|
| 389 |
+
return filepath
|
| 390 |
+
|
| 391 |
+
def set_height_width_hura_image(width, height) -> None:
|
| 392 |
+
"""
|
| 393 |
+
Set the height and width of the image.
|
| 394 |
+
"""
|
| 395 |
+
width_slider.value=width
|
| 396 |
+
height_slider.value=height
|
| 397 |
+
gr.update()
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
pattern_type.change(
|
| 401 |
+
fn=lambda x: gr.update(visible=(x == "color")),
|
| 402 |
+
inputs=pattern_type,
|
| 403 |
+
outputs=color_row
|
| 404 |
+
)
|
| 405 |
+
add_button.click(
|
| 406 |
+
fn=_add_color,
|
| 407 |
+
inputs=[color_picker, state_colors],
|
| 408 |
+
outputs=[state_colors, color_display]
|
| 409 |
+
)
|
| 410 |
+
delete_colors_button.click(
|
| 411 |
+
fn=lambda x: ([], "<div>Add Colors</div>"),
|
| 412 |
+
inputs=[],
|
| 413 |
+
outputs=[state_colors, color_display]
|
| 414 |
+
)
|
| 415 |
+
reset_colors_button.click(
|
| 416 |
+
fn=reset_colors,
|
| 417 |
+
inputs=[],
|
| 418 |
+
outputs=[state_colors,color_display]
|
| 419 |
+
)
|
| 420 |
+
generate_button.click(
|
| 421 |
+
fn=_generate_pattern_from_state,
|
| 422 |
+
inputs=[pattern_type, width_slider, height_slider, v_value_slider, r_value_slider, state_colors],
|
| 423 |
+
outputs=target_image
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
+
v_value_slider.input(
|
| 427 |
+
lambda x: config.find_nearest_prime(x),
|
| 428 |
+
inputs=v_value_slider,
|
| 429 |
+
outputs=v_value_slider
|
| 430 |
+
)
|
| 431 |
+
v_value_slider.change(
|
| 432 |
+
lambda x: config.find_nearest_prime(x),
|
| 433 |
+
inputs=v_value_slider,
|
| 434 |
+
outputs=v_value_slider
|
| 435 |
+
)
|
| 436 |
+
|
| 437 |
+
return {
|
| 438 |
+
"target_image": target_image,
|
| 439 |
+
"run_generate_hex_pattern": run_generate_hex_pattern,
|
| 440 |
+
"set_height_width_hura_image": set_height_width_hura_image,
|
| 441 |
+
"width_slider": width_slider,
|
| 442 |
+
"height_slider": height_slider
|
| 443 |
+
}
|