mirror of
https://github.com/KrisKennaway/ii-pix.git
synced 2024-09-30 03:55:42 +00:00
- Fill any palettes that have fewer than 16 unique entries after
clustering, using the most frequent pixel colours that are not yet in the palette - Reassign any palettes that are duplicated after clustering
This commit is contained in:
parent
ad50ed103d
commit
fc35387360
85
convert.py
85
convert.py
@ -179,9 +179,9 @@ class ClusterPalette:
|
||||
new_total_image_error) = self._dither_image(
|
||||
new_palettes_cam, penalty)
|
||||
|
||||
# TODO: check for duplicate palettes and unused colours
|
||||
# within a palette
|
||||
self._reassign_unused_palettes(line_to_palette)
|
||||
# TODO: check for unused colours within a palette
|
||||
self._reassign_unused_palettes(
|
||||
line_to_palette, new_palettes_rgb12_iigs)
|
||||
|
||||
if new_total_image_error >= total_image_error:
|
||||
inner_iterations_since_improvement += 1
|
||||
@ -230,46 +230,43 @@ class ClusterPalette:
|
||||
self._colours_cam[
|
||||
self._palette_lines[palette_idx], :, :].reshape(-1, 3))
|
||||
|
||||
# Fix reserved colours from the global palette and pick unique
|
||||
# random colours from the sample points for the remaining initial
|
||||
# centroids. This tends to increase the number of colours in the
|
||||
# resulting image, and improves quality.
|
||||
# Fix reserved colours from the global palette.
|
||||
initial_centroids = self._global_palette
|
||||
pixels_rgb_iigs = dither_pyx.convert_cam16ucs_to_rgb12_iigs(
|
||||
palette_pixels)
|
||||
seen_colours = set()
|
||||
for i in range(self._fixed_colours):
|
||||
seen_colours.add(tuple(initial_centroids[i, :]))
|
||||
|
||||
# Pick unique random colours from the sample points for the
|
||||
# remaining initial centroids.
|
||||
for i in range(self._fixed_colours, 16):
|
||||
choice = np.random.randint(0, pixels_rgb_iigs.shape[
|
||||
0])
|
||||
choice = np.random.randint(0, pixels_rgb_iigs.shape[0])
|
||||
new_colour = pixels_rgb_iigs[choice, :]
|
||||
if tuple(new_colour) in seen_colours:
|
||||
continue
|
||||
seen_colours.add(tuple(new_colour))
|
||||
initial_centroids[i, :] = new_colour
|
||||
|
||||
# If there are any single colours in our source //gs RGB
|
||||
# pixels that represent more than fixed_colour_fraction_threshold
|
||||
# of the pixels, then fix these colours for the palette instead of
|
||||
# clustering them. This reduces artifacting on blocks of
|
||||
# colour.
|
||||
# If there are any single colours in our source //gs RGB pixels that
|
||||
# represent more than fixed_colour_fraction_threshold of the total,
|
||||
# then fix these colours for the palette instead of clustering
|
||||
# them. This reduces artifacting on blocks of colour.
|
||||
fixed_colour_fraction_threshold = 0.1
|
||||
most_frequent_colours = sorted(list(zip(
|
||||
*np.unique(pixels_rgb_iigs, return_counts=True, axis=0))),
|
||||
key=lambda kv: kv[1], reverse=True)
|
||||
fixed_colours = self._fixed_colours
|
||||
for colour, freq in sorted(list(zip(
|
||||
*np.unique(dither_pyx.convert_cam16ucs_to_rgb12_iigs(
|
||||
palette_pixels), return_counts=True, axis=0))),
|
||||
key=lambda kv: kv[1], reverse=True):
|
||||
for colour, freq in most_frequent_colours:
|
||||
if freq < (palette_pixels.shape[0] *
|
||||
fixed_colour_fraction_threshold):
|
||||
break
|
||||
# print(colour, freq)
|
||||
if tuple(colour) not in seen_colours:
|
||||
seen_colours.add(tuple(colour))
|
||||
initial_centroids[fixed_colours, :] = colour
|
||||
fixed_colours += 1
|
||||
|
||||
palettes_rgb12_iigs, palette_error = \
|
||||
palette_rgb12_iigs, palette_error = \
|
||||
dither_pyx.k_means_with_fixed_centroids(
|
||||
n_clusters=16, n_fixed=fixed_colours,
|
||||
samples=palette_pixels,
|
||||
@ -277,14 +274,19 @@ class ClusterPalette:
|
||||
max_iterations=1000, tolerance=0.05,
|
||||
rgb12_iigs_to_cam16ucs=self._rgb12_iigs_to_cam16ucs
|
||||
)
|
||||
# If the k-means clustering returned fewer than 16 unique colours,
|
||||
# fill out the remainder with the most common pixels colours that
|
||||
# have not yet been used.
|
||||
palette_rgb12_iigs = self._fill_short_palette(
|
||||
palette_rgb12_iigs, most_frequent_colours)
|
||||
|
||||
for i in range(16):
|
||||
new_palettes_cam[palette_idx, i, :] = (
|
||||
np.array(dither_pyx.convert_rgb12_iigs_to_cam(
|
||||
self._rgb12_iigs_to_cam16ucs, palettes_rgb12_iigs[
|
||||
self._rgb12_iigs_to_cam16ucs, palette_rgb12_iigs[
|
||||
i]), dtype=np.float32))
|
||||
|
||||
new_palettes_rgb12_iigs[palette_idx, :, :] = palettes_rgb12_iigs
|
||||
new_palettes_rgb12_iigs[palette_idx, :, :] = palette_rgb12_iigs
|
||||
|
||||
self._palettes_accepted = False
|
||||
return new_palettes_cam, new_palettes_rgb12_iigs
|
||||
@ -312,13 +314,48 @@ class ClusterPalette:
|
||||
clusters.cluster_centers_[frequency_order].astype(
|
||||
np.float32)))
|
||||
|
||||
def _reassign_unused_palettes(self, new_line_to_palette):
|
||||
def _fill_short_palette(self, palette_iigs_rgb, most_frequent_colours):
|
||||
"""Fill out the palette to 16 unique entries."""
|
||||
|
||||
palette_set = set()
|
||||
for palette_entry in palette_iigs_rgb:
|
||||
palette_set.add(tuple(palette_entry))
|
||||
if len(palette_set) == 16:
|
||||
return palette_iigs_rgb
|
||||
|
||||
# Add most frequent image colours that are not yet in the palette
|
||||
for colour, freq in most_frequent_colours:
|
||||
if tuple(colour) in palette_set:
|
||||
continue
|
||||
palette_set.add(tuple(colour))
|
||||
# print("Added freq %d" % freq)
|
||||
if len(palette_set) == 16:
|
||||
break
|
||||
|
||||
# We couldn't find any more unique colours, fill out with random ones.
|
||||
while len(palette_set) < 16:
|
||||
palette_set.add(
|
||||
tuple(np.random.randint(0, 16, size=3, dtype=np.uint8)))
|
||||
|
||||
return np.array(tuple(palette_set), dtype=np.uint8)
|
||||
|
||||
def _reassign_unused_palettes(self, line_to_palette, palettes_iigs_rgb):
|
||||
palettes_used = [False] * 16
|
||||
for palette in new_line_to_palette:
|
||||
for palette in line_to_palette:
|
||||
palettes_used[palette] = True
|
||||
best_palette_lines = [v for k, v in sorted(list(zip(
|
||||
self._palette_line_errors, range(200))))]
|
||||
|
||||
all_palettes = set()
|
||||
for palette_idx, palette_iigs_rgb in enumerate(palettes_iigs_rgb):
|
||||
palette_set = set()
|
||||
for palette_entry in palette_iigs_rgb:
|
||||
palette_set.add(tuple(palette_entry))
|
||||
palette_set = frozenset(palette_set)
|
||||
if palette_set in all_palettes:
|
||||
print("Duplicate palette", palette_idx, palette_set)
|
||||
palettes_used[palette_idx] = False
|
||||
|
||||
for palette_idx, palette_used in enumerate(palettes_used):
|
||||
if palette_used:
|
||||
continue
|
||||
|
Loading…
Reference in New Issue
Block a user