add patch evluation
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
#%%
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
def merge_intervals(intervals):
|
||||
# Merge overlapping intervals.
|
||||
if not intervals:
|
||||
return []
|
||||
intervals.sort(key=lambda x: x[0])
|
||||
merged = [intervals[0]]
|
||||
for current in intervals[1:]:
|
||||
last = merged[-1]
|
||||
if current[0] <= last[1]:
|
||||
merged[-1] = (last[0], max(last[1], current[1]))
|
||||
else:
|
||||
merged.append(current)
|
||||
return merged
|
||||
|
||||
def get_allowed_intervals(neighbor_values, delta):
|
||||
# Compute forbidden intervals for this channel from neighbor values.
|
||||
forbidden = []
|
||||
for c in neighbor_values:
|
||||
low = max(0, c - delta)
|
||||
high = min(1, c + delta)
|
||||
forbidden.append((low, high))
|
||||
# Merge overlapping forbidden intervals.
|
||||
merged = merge_intervals(forbidden)
|
||||
allowed = []
|
||||
# Allowed region before the first forbidden interval.
|
||||
if merged:
|
||||
if merged[0][0] > 0:
|
||||
allowed.append((0, merged[0][0]))
|
||||
# Allowed regions between forbidden intervals.
|
||||
for i in range(1, len(merged)):
|
||||
allowed.append((merged[i-1][1], merged[i][0]))
|
||||
# Allowed region after the last forbidden interval.
|
||||
if merged[-1][1] < 1:
|
||||
allowed.append((merged[-1][1], 1))
|
||||
else:
|
||||
allowed.append((0, 1))
|
||||
return 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]
|
||||
total_length = sum(lengths)
|
||||
if total_length <= 0:
|
||||
return None
|
||||
point = np.random.rand() * total_length
|
||||
cumulative = 0
|
||||
for (start, end), length in zip(allowed, lengths):
|
||||
cumulative += length
|
||||
if point <= cumulative:
|
||||
return np.random.uniform(start, end)
|
||||
return allowed[-1][0]
|
||||
|
||||
def sample_channel(neighbor_values, delta):
|
||||
# Compute allowed intervals and sample a value.
|
||||
allowed = get_allowed_intervals(neighbor_values, delta)
|
||||
if not allowed:
|
||||
return None
|
||||
return sample_from_allowed(allowed)
|
||||
|
||||
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)
|
||||
color = np.zeros(3)
|
||||
for channel in range(3):
|
||||
neighbor_channel = [n[channel] for n in neighbors]
|
||||
sampled = sample_channel(neighbor_channel, delta)
|
||||
# If the allowed intervals are empty, use a fallback.
|
||||
if sampled is None:
|
||||
sampled = np.random.rand()
|
||||
color[channel] = sampled
|
||||
return color
|
||||
|
||||
def generate_color_pattern(h, w, d, r):
|
||||
"""
|
||||
Generate a dense color pattern.
|
||||
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 j in range(num_patches_y):
|
||||
# Collect neighbor patch colors (using mean because patches have uniform color).
|
||||
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:
|
||||
# For the first patch use a random color.
|
||||
new_color = np.random.rand(3)
|
||||
else:
|
||||
new_color = generate_valid_color(neighbors, r)
|
||||
|
||||
pattern[i*d:(i+1)*d, j*d:(j+1)*d] = new_color
|
||||
print('Patch ({}, {})'.format(i, j))
|
||||
return pattern
|
||||
|
||||
def main():
|
||||
# Parameters for the pattern.
|
||||
# For example, using 100x100 pixels image with patches of 3x3 pixels.
|
||||
height = 200
|
||||
width = 200
|
||||
patch_size = 3
|
||||
# Set r to a value that gives sufficient difference.
|
||||
# Note: r is the threshold for squared difference per channel, and the condition is
|
||||
# |c - c_neighbor| >= sqrt(r). Very high r may make it impossible to satisfy.
|
||||
r = 0.5
|
||||
|
||||
pattern = generate_color_pattern(height, width, patch_size, r)
|
||||
# Scale to 0-255 for display and saving.
|
||||
pattern_display = (pattern * 255).astype(np.uint8)
|
||||
|
||||
# Display the pattern with OpenCV.
|
||||
cv2.imshow('Pattern', pattern_display)
|
||||
cv2.waitKey(0)
|
||||
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