Merge branch 'stable' of yahoo.com.hk@192.168.192.190:D:\repo\orisyssdk_git into stable
This commit is contained in:
+117
-284
@@ -1,23 +1,12 @@
|
|||||||
#%%
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import cv2
|
import cv2
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
|
||||||
# Flat pattern scale before warping to a dome: 1 mm == 100 pixels.
|
# ----------------------------------------------------------------------
|
||||||
PX_PER_MM = 100.0
|
# Utility functions from the original flat pattern generator
|
||||||
|
# (unchanged, except we now work with 0‑1 floats)
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
def mm_to_px(mm, scale_px_per_mm=PX_PER_MM):
|
|
||||||
"""Convert millimetres to pixels (default 1 mm = 100 px)."""
|
|
||||||
return mm * scale_px_per_mm
|
|
||||||
|
|
||||||
|
|
||||||
def px_to_mm(px, scale_px_per_mm=PX_PER_MM):
|
|
||||||
"""Convert pixels to millimetres (default 1 mm = 100 px)."""
|
|
||||||
return px / scale_px_per_mm
|
|
||||||
|
|
||||||
|
|
||||||
def merge_intervals(intervals):
|
def merge_intervals(intervals):
|
||||||
# Merge overlapping intervals.
|
|
||||||
if not intervals:
|
if not intervals:
|
||||||
return []
|
return []
|
||||||
intervals.sort(key=lambda x: x[0])
|
intervals.sort(key=lambda x: x[0])
|
||||||
@@ -31,23 +20,18 @@ def merge_intervals(intervals):
|
|||||||
return merged
|
return merged
|
||||||
|
|
||||||
def get_allowed_intervals(neighbor_values, delta):
|
def get_allowed_intervals(neighbor_values, delta):
|
||||||
# Compute forbidden intervals for this channel from neighbor values.
|
|
||||||
forbidden = []
|
forbidden = []
|
||||||
for c in neighbor_values:
|
for c in neighbor_values:
|
||||||
low = max(0, c - delta)
|
low = max(0, c - delta)
|
||||||
high = min(1, c + delta)
|
high = min(1, c + delta)
|
||||||
forbidden.append((low, high))
|
forbidden.append((low, high))
|
||||||
# Merge overlapping forbidden intervals.
|
|
||||||
merged = merge_intervals(forbidden)
|
merged = merge_intervals(forbidden)
|
||||||
allowed = []
|
allowed = []
|
||||||
# Allowed region before the first forbidden interval.
|
|
||||||
if merged:
|
if merged:
|
||||||
if merged[0][0] > 0:
|
if merged[0][0] > 0:
|
||||||
allowed.append((0, merged[0][0]))
|
allowed.append((0, merged[0][0]))
|
||||||
# Allowed regions between forbidden intervals.
|
|
||||||
for i in range(1, len(merged)):
|
for i in range(1, len(merged)):
|
||||||
allowed.append((merged[i-1][1], merged[i][0]))
|
allowed.append((merged[i-1][1], merged[i][0]))
|
||||||
# Allowed region after the last forbidden interval.
|
|
||||||
if merged[-1][1] < 1:
|
if merged[-1][1] < 1:
|
||||||
allowed.append((merged[-1][1], 1))
|
allowed.append((merged[-1][1], 1))
|
||||||
else:
|
else:
|
||||||
@@ -55,7 +39,6 @@ def get_allowed_intervals(neighbor_values, delta):
|
|||||||
return allowed
|
return allowed
|
||||||
|
|
||||||
def sample_from_allowed(allowed):
|
def sample_from_allowed(allowed):
|
||||||
# Choose a random value from the allowed intervals with probability proportional to interval length.
|
|
||||||
lengths = [end - start for start, end in allowed]
|
lengths = [end - start for start, end in allowed]
|
||||||
total_length = sum(lengths)
|
total_length = sum(lengths)
|
||||||
if total_length <= 0:
|
if total_length <= 0:
|
||||||
@@ -69,316 +52,166 @@ def sample_from_allowed(allowed):
|
|||||||
return allowed[-1][0]
|
return allowed[-1][0]
|
||||||
|
|
||||||
def sample_channel(neighbor_values, delta):
|
def sample_channel(neighbor_values, delta):
|
||||||
# Compute allowed intervals and sample a value.
|
|
||||||
allowed = get_allowed_intervals(neighbor_values, delta)
|
allowed = get_allowed_intervals(neighbor_values, delta)
|
||||||
if not allowed:
|
if not allowed:
|
||||||
return None
|
return None
|
||||||
return sample_from_allowed(allowed)
|
return sample_from_allowed(allowed)
|
||||||
|
|
||||||
def generate_valid_color(neighbors, r):
|
def generate_valid_color(neighbors, r):
|
||||||
"""
|
|
||||||
Generate a random color that satisfies, for each neighbor,
|
|
||||||
|R - R_neighbor| >= sqrt(r), and similarly for G and B.
|
|
||||||
neighbors: list of neighbor colors (each is an array of shape (3,))
|
|
||||||
r: threshold for squared difference.
|
|
||||||
"""
|
|
||||||
delta = np.sqrt(r)
|
delta = np.sqrt(r)
|
||||||
color = np.zeros(3)
|
color = np.zeros(3)
|
||||||
for channel in range(3):
|
for channel in range(3):
|
||||||
neighbor_channel = [n[channel] for n in neighbors]
|
neighbor_channel = [n[channel] for n in neighbors]
|
||||||
sampled = sample_channel(neighbor_channel, delta)
|
sampled = sample_channel(neighbor_channel, delta)
|
||||||
# If the allowed intervals are empty, use a fallback.
|
|
||||||
if sampled is None:
|
if sampled is None:
|
||||||
sampled = np.random.rand()
|
sampled = np.random.rand()
|
||||||
color[channel] = sampled
|
color[channel] = sampled
|
||||||
return color
|
return color
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
def _easing_progress(t, easing, gamma):
|
# Cylinder‑specific generator
|
||||||
"""
|
# ----------------------------------------------------------------------
|
||||||
Map normalized position t in [0, 1] along an axis to a progress u in [0, 1]
|
def generate_color_pattern_cylinder(
|
||||||
for patch-size interpolation: size = start + (end - start) * u.
|
R=9.0, # cylinder radius (mm)
|
||||||
"""
|
H=20.0, # cylinder height (mm)
|
||||||
t = float(np.clip(t, 0.0, 1.0))
|
fov_deg=150.0, # vertical FOV (degrees)
|
||||||
e = (easing or "linear").lower()
|
pixels_per_mm=100, # texture resolution
|
||||||
if e == "linear":
|
n_azimuth=36, # number of patches around the circumference
|
||||||
return t
|
n_radial=20, # number of patches vertically (visible part)
|
||||||
if e == "power":
|
r=0.2, # color randomness regulation factor
|
||||||
g = float(gamma)
|
output_path='cylinder_pattern.png'
|
||||||
return t**g
|
|
||||||
if e == "cosine":
|
|
||||||
return 0.5 * (1.0 - np.cos(np.pi * t))
|
|
||||||
if e in ("dome", "dome_sin2"):
|
|
||||||
# Shallow rise from left (t=0) to right (t=1); slows change near the left
|
|
||||||
# (useful when the left is farther on a dome and needs larger flat patches).
|
|
||||||
return np.sin(0.5 * np.pi * t) ** 2
|
|
||||||
raise ValueError(
|
|
||||||
f"Unknown easing {e!r}; use 'linear', 'power', 'cosine', or 'dome'"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def patch_sizes_nonlinear_along_length(
|
|
||||||
total_len_px,
|
|
||||||
patch_px_start,
|
|
||||||
patch_px_end,
|
|
||||||
*,
|
|
||||||
easing="power",
|
|
||||||
gamma=2.0,
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Integer segment lengths along an axis of length ``total_len_px``, with **nonlinear**
|
Generate a texture map for the inner cylinder wall.
|
||||||
nominal patch size from ``patch_px_start`` (at the beginning of the axis, e.g. left)
|
Patches are arranged so that, from a camera at the bottom
|
||||||
to ``patch_px_end`` (at the end, e.g. right).
|
looking upward with perspective projection, they appear
|
||||||
|
uniform in angular and radial extent.
|
||||||
At cumulative pixel position ``pos`` (start of a segment), let ``t = pos / total_len_px``.
|
|
||||||
Nominal size is::
|
|
||||||
|
|
||||||
patch_px_start + (patch_px_end - patch_px_start) * easing(t)
|
|
||||||
|
|
||||||
Segments are at least 1 pixel wide; the sum of segment lengths equals ``total_len_px``.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
total_len_px : int
|
|
||||||
Full extent in pixels (e.g. image width after ``mm_to_px(length_mm)``).
|
|
||||||
patch_px_start, patch_px_end : int
|
|
||||||
Patch size in pixels at the start vs end of the axis.
|
|
||||||
easing : str
|
|
||||||
- ``'linear'``: same as uniform interpolation in ``t``
|
|
||||||
- ``'power'``: ``t ** gamma`` (gamma > 1 keeps sizes nearer ``patch_px_start`` longer)
|
|
||||||
- ``'cosine'``: smooth cosine half-period
|
|
||||||
- ``'dome'``: ``sin(pi/2 * t) ** 2`` (gentle dome-style progression)
|
|
||||||
gamma : float
|
|
||||||
Exponent for ``easing='power'`` (ignored otherwise).
|
|
||||||
|
|
||||||
See Also
|
|
||||||
--------
|
|
||||||
patch_sizes_from_mm : same logic with inputs in millimetres.
|
|
||||||
"""
|
"""
|
||||||
if total_len_px <= 0:
|
|
||||||
return []
|
|
||||||
a = max(1, int(round(float(patch_px_start))))
|
|
||||||
b = max(1, int(round(float(patch_px_end))))
|
|
||||||
tl = float(total_len_px)
|
|
||||||
sizes = []
|
|
||||||
pos = 0
|
|
||||||
while pos < total_len_px:
|
|
||||||
t = pos / tl
|
|
||||||
u = _easing_progress(t, easing, gamma)
|
|
||||||
nominal = a + (b - a) * u
|
|
||||||
seg = int(max(1, round(float(nominal))))
|
|
||||||
seg = min(seg, total_len_px - pos)
|
|
||||||
sizes.append(seg)
|
|
||||||
pos += seg
|
|
||||||
diff = total_len_px - sum(sizes)
|
|
||||||
if diff != 0 and sizes:
|
|
||||||
sizes[-1] += diff
|
|
||||||
sizes[-1] = max(1, sizes[-1])
|
|
||||||
return sizes
|
|
||||||
|
|
||||||
|
# Camera half‑angle (rad)
|
||||||
|
half_fov = np.radians(fov_deg / 2.0)
|
||||||
|
|
||||||
def patch_sizes_from_mm(
|
# Visible height range (z_min = lowest point seen by camera)
|
||||||
length_mm,
|
z_min = R / np.tan(half_fov) if half_fov < np.pi/2 else 0.0
|
||||||
patch_mm_start,
|
z_max = H
|
||||||
patch_mm_end,
|
|
||||||
*,
|
|
||||||
px_per_mm=PX_PER_MM,
|
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Same as ``patch_sizes_nonlinear_along_length`` but axis length and patch bounds
|
|
||||||
are given in **millimetres**; converted with ``1 mm = px_per_mm`` pixels (default 100).
|
|
||||||
"""
|
|
||||||
L = int(round(float(length_mm) * float(px_per_mm)))
|
|
||||||
a = max(1, int(round(float(patch_mm_start) * float(px_per_mm))))
|
|
||||||
b = max(1, int(round(float(patch_mm_end) * float(px_per_mm))))
|
|
||||||
return patch_sizes_nonlinear_along_length(L, a, b, **kwargs)
|
|
||||||
|
|
||||||
|
# Image plane radial coordinate r' = R / z
|
||||||
|
r_top = R / z_max # smallest r' (top)
|
||||||
|
r_bottom = R / z_min # largest r' (bottom)
|
||||||
|
|
||||||
def patch_sizes_along_length(total_len, size_at_start, size_at_end):
|
# Define patch grid in image space
|
||||||
"""
|
theta_step = 2 * np.pi / n_azimuth
|
||||||
Integer segment lengths along an axis of length `total_len`, varying **linearly**
|
r_step = (r_bottom - r_top) / n_radial
|
||||||
from `size_at_start` (at the beginning of the axis) to `size_at_end` (at the end).
|
|
||||||
|
|
||||||
Equivalent to ``patch_sizes_nonlinear_along_length(..., easing='linear')``.
|
# Texture dimensions
|
||||||
"""
|
width_px = int(np.round(2 * np.pi * R * pixels_per_mm))
|
||||||
return patch_sizes_nonlinear_along_length(
|
height_px = int(np.round(H * pixels_per_mm))
|
||||||
total_len, size_at_start, size_at_end, easing="linear", gamma=1.0
|
print(f"Texture size: {width_px} x {height_px} pixels")
|
||||||
)
|
|
||||||
|
|
||||||
|
# Store patch colors in a 2D array (rows = radial index, columns = azimuth)
|
||||||
|
patch_colors = np.zeros((n_radial, n_azimuth, 3), dtype=np.float32)
|
||||||
|
|
||||||
def generate_color_pattern_varying_horizontal(
|
# Generate colors row by row (top to bottom)
|
||||||
h,
|
for j in range(n_radial): # j = 0 → top, j = n_radial-1 → bottom
|
||||||
w,
|
r_start = r_top + j * r_step
|
||||||
patch_w_max_left,
|
r_end = r_start + r_step
|
||||||
patch_w_min_right,
|
# Corresponding z values (note: r increases → z decreases)
|
||||||
r,
|
z_start = R / r_start # larger z (higher up)
|
||||||
*,
|
z_end = R / r_end # smaller z (lower down)
|
||||||
easing="linear",
|
v_start = z_start / H
|
||||||
gamma=2.0,
|
v_end = z_end / H
|
||||||
):
|
# Ensure v_start > v_end (top has larger v)
|
||||||
"""
|
if v_start < v_end:
|
||||||
Like ``generate_color_pattern``, but patch **size** varies along x (largest on the
|
v_start, v_end = v_end, v_start
|
||||||
left, smallest on the right). Each patch is **square**: side length equals that
|
|
||||||
column's width, and the same step is used vertically within the column.
|
|
||||||
|
|
||||||
Parameters
|
for i in range(n_azimuth):
|
||||||
----------
|
theta_start = i * theta_step
|
||||||
h, w : int
|
theta_end = theta_start + theta_step
|
||||||
Output image size.
|
u_start = theta_start / (2 * np.pi)
|
||||||
patch_w_max_left : int
|
u_end = theta_end / (2 * np.pi)
|
||||||
Square side length at the left edge (maximum).
|
|
||||||
patch_w_min_right : int
|
|
||||||
Square side length toward the right edge (minimum).
|
|
||||||
r : float
|
|
||||||
Same neighbor color constraint as ``generate_color_pattern``.
|
|
||||||
easing : str
|
|
||||||
Passed to ``patch_sizes_nonlinear_along_length`` (e.g. ``'dome'`` for dome warp).
|
|
||||||
gamma : float
|
|
||||||
Exponent when ``easing='power'``.
|
|
||||||
|
|
||||||
Note
|
# Collect neighbor colors (above and left)
|
||||||
----
|
|
||||||
If ``h`` is not a multiple of a column's side length ``s``, the last row in that
|
|
||||||
column is shorter than ``s`` pixels tall while the width stays ``s`` (a thin strip).
|
|
||||||
"""
|
|
||||||
pattern = np.zeros((h, w, 3), dtype=np.float32)
|
|
||||||
|
|
||||||
col_widths = patch_sizes_nonlinear_along_length(
|
|
||||||
w, patch_w_max_left, patch_w_min_right, easing=easing, gamma=gamma
|
|
||||||
)
|
|
||||||
x_edges = np.concatenate([[0], np.cumsum(col_widths, dtype=np.int64)])
|
|
||||||
num_cols = len(col_widths)
|
|
||||||
|
|
||||||
for j in range(num_cols):
|
|
||||||
s = int(col_widths[j])
|
|
||||||
x0 = int(x_edges[j])
|
|
||||||
x1 = int(x_edges[j + 1])
|
|
||||||
y = 0
|
|
||||||
while y < h:
|
|
||||||
y1 = min(y + s, h)
|
|
||||||
neighbors = []
|
neighbors = []
|
||||||
if y >= s:
|
# Above neighbor (previous row, same column)
|
||||||
patch_above = pattern[y - s : y, x0:x1]
|
|
||||||
if patch_above.size:
|
|
||||||
neighbors.append(np.mean(patch_above, axis=(0, 1)))
|
|
||||||
if j > 0:
|
if j > 0:
|
||||||
xl0 = int(x_edges[j - 1])
|
neighbors.append(patch_colors[j-1, i])
|
||||||
patch_left = pattern[y:y1, xl0:x0]
|
# Left neighbor (same row, previous column) – wrap around
|
||||||
if patch_left.size:
|
left_i = (i - 1) % n_azimuth
|
||||||
neighbors.append(np.mean(patch_left, axis=(0, 1)))
|
if i > 0 or j > 0: # at least one neighbor exists
|
||||||
|
neighbors.append(patch_colors[j, left_i])
|
||||||
|
|
||||||
if len(neighbors) == 0:
|
if len(neighbors) == 0:
|
||||||
new_color = np.random.rand(3)
|
new_color = np.random.rand(3)
|
||||||
else:
|
else:
|
||||||
new_color = generate_valid_color(neighbors, r)
|
new_color = generate_valid_color(neighbors, r)
|
||||||
|
|
||||||
pattern[y:y1, x0:x1] = new_color
|
patch_colors[j, i] = new_color
|
||||||
y = y1
|
|
||||||
|
|
||||||
return pattern
|
# Render the texture image
|
||||||
|
img = Image.new('RGB', (width_px, height_px), color=(255, 255, 255))
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
|
for j in range(n_radial):
|
||||||
|
r_start = r_top + j * r_step
|
||||||
|
r_end = r_start + r_step
|
||||||
|
z_start = R / r_start
|
||||||
|
z_end = R / r_end
|
||||||
|
|
||||||
def generate_color_pattern(h, w, d, r):
|
# Determine v for top and bottom edges
|
||||||
"""
|
v_top = max(z_start, z_end) / H # larger z -> higher on cylinder
|
||||||
Generate a dense color pattern.
|
v_bottom = min(z_start, z_end) / H # smaller z -> lower on cylinder
|
||||||
h, w: height and width of the output image in pixels.
|
|
||||||
d: patch size in pixels (each patch is d x d).
|
|
||||||
r: randomness regulation factor. For each neighbor the condition is:
|
|
||||||
(|channel_new - channel_neighbor|)^2 >= r.
|
|
||||||
"""
|
|
||||||
pattern = np.zeros((h, w, 3), dtype=np.float32)
|
|
||||||
num_patches_x = h // d
|
|
||||||
num_patches_y = w // d
|
|
||||||
|
|
||||||
for i in range(num_patches_x):
|
for i in range(n_azimuth):
|
||||||
for j in range(num_patches_y):
|
u_start = i * theta_step / (2 * np.pi)
|
||||||
# Collect neighbor patch colors (using mean because patches have uniform color).
|
u_end = (i+1) * theta_step / (2 * np.pi)
|
||||||
neighbors = []
|
|
||||||
if i > 0:
|
|
||||||
patch_above = pattern[(i-1)*d:i*d, j*d:(j+1)*d]
|
|
||||||
neighbors.append(np.mean(patch_above, axis=(0, 1)))
|
|
||||||
if j > 0:
|
|
||||||
patch_left = pattern[i*d:(i+1)*d, (j-1)*d:j*d]
|
|
||||||
neighbors.append(np.mean(patch_left, axis=(0, 1)))
|
|
||||||
|
|
||||||
if len(neighbors) == 0:
|
# Pixel coordinates (ensure integer ordering)
|
||||||
# For the first patch use a random color.
|
x0 = int(u_start * width_px)
|
||||||
new_color = np.random.rand(3)
|
x1 = int(u_end * width_px)
|
||||||
else:
|
y0 = int(v_bottom * height_px)
|
||||||
new_color = generate_valid_color(neighbors, r)
|
y1 = int(v_top * height_px)
|
||||||
|
|
||||||
pattern[i*d:(i+1)*d, j*d:(j+1)*d] = new_color
|
# Sanity: x0 < x1, y0 < y1
|
||||||
print('Patch ({}, {})'.format(i, j))
|
if x0 > x1:
|
||||||
return pattern
|
x0, x1 = x1, x0
|
||||||
|
if y0 > y1:
|
||||||
|
y0, y1 = y1, y0
|
||||||
|
|
||||||
|
# Avoid degenerate rectangles (patch may span seam)
|
||||||
|
if x1 <= x0:
|
||||||
|
x1 = x0 + 1
|
||||||
|
if y1 <= y0:
|
||||||
|
y1 = y0 + 1
|
||||||
|
|
||||||
def draw_center_cross_bgr(
|
color = tuple((patch_colors[j, i] * 255).astype(np.uint8))
|
||||||
img,
|
draw.rectangle([x0, y0, x1, y1], fill=color)
|
||||||
*,
|
|
||||||
color=(0, 0, 0),
|
|
||||||
arm_half_len=None,
|
|
||||||
thickness=3,
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Draw a black (+) cross centered on the image for print / camera alignment.
|
|
||||||
`img` is modified in place (BGR uint8).
|
|
||||||
"""
|
|
||||||
h, w = img.shape[:2]
|
|
||||||
cx, cy = w // 2, h // 2
|
|
||||||
if arm_half_len is None:
|
|
||||||
arm_half_len = max(8, min(h, w) // 40)
|
|
||||||
half = int(arm_half_len)
|
|
||||||
cv2.line(
|
|
||||||
img,
|
|
||||||
(cx - half, cy),
|
|
||||||
(cx + half, cy),
|
|
||||||
color,
|
|
||||||
thickness,
|
|
||||||
lineType=cv2.LINE_AA,
|
|
||||||
)
|
|
||||||
cv2.line(
|
|
||||||
img,
|
|
||||||
(cx, cy - half),
|
|
||||||
(cx, cy + half),
|
|
||||||
color,
|
|
||||||
thickness,
|
|
||||||
lineType=cv2.LINE_AA,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
img.save(output_path)
|
||||||
|
print(f"Saved texture to {output_path}")
|
||||||
|
|
||||||
def main():
|
# Also return as numpy array (BGR for OpenCV)
|
||||||
# Parameters for the pattern.
|
img_np = np.array(img)[:, :, ::-1] # RGB -> BGR
|
||||||
# For example, using 100x100 pixels image with patches of 3x3 pixels.
|
return img_np
|
||||||
# height = 2750
|
|
||||||
# width = 1950
|
# ----------------------------------------------------------------------
|
||||||
height = 1502
|
# Example usage
|
||||||
width = 1738
|
# ----------------------------------------------------------------------
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# Generate pattern with 36 azimuth patches and 20 radial patches
|
||||||
patch_size = 12
|
patch_size = 12
|
||||||
# Set r to a value that gives sufficient difference.
|
R = 9.0
|
||||||
# Note: r is the threshold for squared difference per channel, and the condition is
|
H = 20.0
|
||||||
# |c - c_neighbor| >= sqrt(r). Very high r may make it impossible to satisfy.
|
fov_deg = 150.0
|
||||||
r = 1000
|
pixels_per_mm = 100
|
||||||
|
n_azimuth = int(2*np.pi*R*pixels_per_mm / patch_size)
|
||||||
# 1 mm = 100 px: e.g. width_mm = px_to_mm(width); patch sizes in mm -> px with mm_to_px.
|
n_radial = int(H*pixels_per_mm / patch_size)
|
||||||
# Column widths only (nonlinear for dome — left farther): patch_sizes_from_mm(
|
r = 0.05
|
||||||
# length_mm=px_to_mm(width), patch_mm_start=0.30, patch_mm_end=0.06, easing="dome"
|
pattern = generate_color_pattern_cylinder(
|
||||||
# )
|
R=9.0, H=20.0, fov_deg=150.0, pixels_per_mm=100,
|
||||||
pattern = generate_color_pattern_varying_horizontal(
|
n_azimuth=n_azimuth, n_radial=n_radial, r=0.05
|
||||||
height, width, patch_w_max_left=30, patch_w_min_right=6, r=r, easing="linear"
|
|
||||||
)
|
)
|
||||||
# pattern = generate_color_pattern(height, width, patch_size, r)
|
|
||||||
# Scale to 0-255 for display and saving.
|
|
||||||
pattern_display = (pattern * 255).astype(np.uint8)
|
|
||||||
draw_center_cross_bgr(pattern_display)
|
|
||||||
|
|
||||||
# Display the pattern with OpenCV.
|
# Display with OpenCV
|
||||||
cv2.imshow('Pattern', pattern_display)
|
cv2.imshow('Cylinder Color Pattern', pattern)
|
||||||
cv2.waitKey(0)
|
cv2.waitKey(0)
|
||||||
cv2.destroyAllWindows()
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
# Save the pattern as an image file.
|
|
||||||
cv2.imwrite('color_pattern.png', pattern_display)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user