|
@@ -69,6 +69,7 @@ import os
|
|
|
try:
|
|
|
import PIL
|
|
|
from PIL import Image
|
|
|
+
|
|
|
pillow = True
|
|
|
try:
|
|
|
PIL.__version__ # test if user has Pillow or PIL
|
|
@@ -98,8 +99,9 @@ def get_cKDTree():
|
|
|
# technically the first byte says how many bytes follow, after which that
|
|
|
# amount (max 255) follows)
|
|
|
|
|
|
+
|
|
|
def checkImages(images):
|
|
|
- """ checkImages(images)
|
|
|
+ """checkImages(images)
|
|
|
Check numpy images and correct intensity range etc.
|
|
|
The same for all movie formats.
|
|
|
|
|
@@ -131,11 +133,11 @@ def checkImages(images):
|
|
|
pass # ok
|
|
|
elif im.ndim == 3:
|
|
|
if im.shape[2] not in [3, 4]:
|
|
|
- raise ValueError('This array can not represent an image.')
|
|
|
+ raise ValueError("This array can not represent an image.")
|
|
|
else:
|
|
|
- raise ValueError('This array can not represent an image.')
|
|
|
+ raise ValueError("This array can not represent an image.")
|
|
|
else:
|
|
|
- raise ValueError('Invalid image type: ' + str(type(im)))
|
|
|
+ raise ValueError("Invalid image type: " + str(type(im)))
|
|
|
|
|
|
# Done
|
|
|
return images2
|
|
@@ -151,8 +153,7 @@ def intToBin(i):
|
|
|
|
|
|
|
|
|
class GifWriter:
|
|
|
- """Class that contains methods for helping write the animated GIF file.
|
|
|
- """
|
|
|
+ """Class that contains methods for helping write the animated GIF file."""
|
|
|
|
|
|
def getheaderAnim(self, im):
|
|
|
"""Get animation header. To replace PILs getheader()[0]
|
|
@@ -183,7 +184,7 @@ class GifWriter:
|
|
|
xy = (0, 0)
|
|
|
|
|
|
# Image separator,
|
|
|
- bb = '\x2C'
|
|
|
+ bb = "\x2C"
|
|
|
|
|
|
# Image position and size
|
|
|
bb += intToBin(xy[0]) # Left position
|
|
@@ -193,19 +194,19 @@ class GifWriter:
|
|
|
|
|
|
# packed field: local color table flag1, interlace0, sorted table0,
|
|
|
# reserved00, lct size111=7=2^(7 + 1)=256.
|
|
|
- bb += '\x87'
|
|
|
+ bb += "\x87"
|
|
|
|
|
|
# LZW min size code now comes later, beginning of [image data] blocks
|
|
|
return bb
|
|
|
|
|
|
- def getAppExt(self, loops=float('inf')):
|
|
|
+ def getAppExt(self, loops=float("inf")):
|
|
|
"""Application extension. This part specifies the amount of loops.
|
|
|
If loops is 0 or inf, it goes on infinitely.
|
|
|
|
|
|
:param float loops:
|
|
|
"""
|
|
|
|
|
|
- if loops == 0 or loops == float('inf'):
|
|
|
+ if loops == 0 or loops == float("inf"):
|
|
|
loops = 2 ** 16 - 1
|
|
|
# bb = ""
|
|
|
# application extension should not be used
|
|
@@ -217,7 +218,7 @@ class GifWriter:
|
|
|
bb += "NETSCAPE2.0"
|
|
|
bb += "\x03\x01"
|
|
|
bb += intToBin(loops)
|
|
|
- bb += '\x00' # end
|
|
|
+ bb += "\x00" # end
|
|
|
return bb
|
|
|
|
|
|
def getGraphicsControlExt(self, duration=0.1, dispose=2):
|
|
@@ -239,13 +240,13 @@ class GifWriter:
|
|
|
:param dispose:
|
|
|
"""
|
|
|
|
|
|
- bb = '\x21\xF9\x04'
|
|
|
+ bb = "\x21\xF9\x04"
|
|
|
bb += chr((dispose & 3) << 2) # low bit 1 == transparency,
|
|
|
# 2nd bit 1 == user input , next 3 bits, the low two of which are used,
|
|
|
# are dispose.
|
|
|
bb += intToBin(int(duration * 100)) # in 100th of seconds
|
|
|
- bb += '\x00' # no transparent color
|
|
|
- bb += '\x00' # end
|
|
|
+ bb += "\x00" # no transparent color
|
|
|
+ bb += "\x00" # end
|
|
|
return bb
|
|
|
|
|
|
def handleSubRectangles(self, images, subRectangles):
|
|
@@ -262,7 +263,7 @@ class GifWriter:
|
|
|
xy = subRectangles
|
|
|
if xy is None:
|
|
|
xy = (0, 0)
|
|
|
- if hasattr(xy, '__len__'):
|
|
|
+ if hasattr(xy, "__len__"):
|
|
|
if len(xy) == len(images):
|
|
|
xy = [xxyy for xxyy in xy]
|
|
|
else:
|
|
@@ -285,7 +286,9 @@ class GifWriter:
|
|
|
tmp = im.convert() # Make without palette
|
|
|
a = np.asarray(tmp)
|
|
|
if len(a.shape) == 0:
|
|
|
- raise MemoryError("Too little memory to convert PIL image to array")
|
|
|
+ raise MemoryError(
|
|
|
+ "Too little memory to convert PIL image to array"
|
|
|
+ )
|
|
|
images[i] = a
|
|
|
|
|
|
# Determine the sub rectangles
|
|
@@ -295,7 +298,7 @@ class GifWriter:
|
|
|
return images, xy
|
|
|
|
|
|
def getSubRectangles(self, ims):
|
|
|
- """ getSubRectangles(ims)
|
|
|
+ """getSubRectangles(ims)
|
|
|
|
|
|
Calculate the minimal rectangles that need updating each frame.
|
|
|
Returns a two-element tuple containing the cropped images and a
|
|
@@ -324,7 +327,7 @@ class GifWriter:
|
|
|
for im in ims[1:]:
|
|
|
|
|
|
# Get difference, sum over colors
|
|
|
- diff = np.abs(im-prev)
|
|
|
+ diff = np.abs(im - prev)
|
|
|
if diff.ndim == 3:
|
|
|
diff = diff.sum(2)
|
|
|
# Get begin and end for both dimensions
|
|
@@ -350,7 +353,7 @@ class GifWriter:
|
|
|
return ims2, xy
|
|
|
|
|
|
def convertImagesToPIL(self, images, dither, nq=0):
|
|
|
- """ convertImagesToPIL(images, nq=0)
|
|
|
+ """convertImagesToPIL(images, nq=0)
|
|
|
|
|
|
Convert images to Paletted PIL images, which can then be
|
|
|
written to a single animaged GIF.
|
|
@@ -364,11 +367,11 @@ class GifWriter:
|
|
|
images2.append(im)
|
|
|
elif np and isinstance(im, np.ndarray):
|
|
|
if im.ndim == 3 and im.shape[2] == 3:
|
|
|
- im = Image.fromarray(im, 'RGB')
|
|
|
+ im = Image.fromarray(im, "RGB")
|
|
|
elif im.ndim == 3 and im.shape[2] == 4:
|
|
|
- im = Image.fromarray(im[:, :, :3], 'RGB')
|
|
|
+ im = Image.fromarray(im[:, :, :3], "RGB")
|
|
|
elif im.ndim == 2:
|
|
|
- im = Image.fromarray(im, 'L')
|
|
|
+ im = Image.fromarray(im, "L")
|
|
|
images2.append(im)
|
|
|
|
|
|
# Convert to paletted PIL images
|
|
@@ -388,14 +391,14 @@ class GifWriter:
|
|
|
# Adaptive PIL algorithm
|
|
|
AD = Image.ADAPTIVE
|
|
|
for im in images:
|
|
|
- im = im.convert('P', palette=AD, dither=dither)
|
|
|
+ im = im.convert("P", palette=AD, dither=dither)
|
|
|
images2.append(im)
|
|
|
|
|
|
# Done
|
|
|
return images2
|
|
|
|
|
|
def writeGifToFile(self, fp, images, durations, loops, xys, disposes):
|
|
|
- """ writeGifToFile(fp, images, durations, loops, xys, disposes)
|
|
|
+ """writeGifToFile(fp, images, durations, loops, xys, disposes)
|
|
|
|
|
|
Given a set of images writes the bytes to the specified stream.
|
|
|
Requires different handling of palette for PIL and Pillow:
|
|
@@ -447,8 +450,9 @@ class GifWriter:
|
|
|
# Gather info
|
|
|
data = getdata(im)
|
|
|
imdes, data = data[0], data[1:]
|
|
|
- graphext = self.getGraphicsControlExt(durations[frames],
|
|
|
- disposes[frames])
|
|
|
+ graphext = self.getGraphicsControlExt(
|
|
|
+ durations[frames], disposes[frames]
|
|
|
+ )
|
|
|
# Make image descriptor suitable for using 256 local color palette
|
|
|
lid = self.getImageDescriptor(im, xys[frames])
|
|
|
|
|
@@ -458,7 +462,7 @@ class GifWriter:
|
|
|
fp.write(graphext)
|
|
|
fp.write(lid) # write suitable image descriptor
|
|
|
fp.write(palette) # write local color table
|
|
|
- fp.write('\x08') # LZW minimum size code
|
|
|
+ fp.write("\x08") # LZW minimum size code
|
|
|
else:
|
|
|
# Use global color palette
|
|
|
fp.write(graphext)
|
|
@@ -492,7 +496,7 @@ def writeGif(filename, images, duration=0.1, repeat=True, **kwargs):
|
|
|
"""
|
|
|
if pillow:
|
|
|
# Pillow >= 3.4.0 has animated GIF writing
|
|
|
- version = [int(i) for i in PIL.__version__.split('.')]
|
|
|
+ version = [int(i) for i in PIL.__version__.split(".")]
|
|
|
if version[0] > 3 or (version[0] == 3 and version[1] >= 4):
|
|
|
writeGifPillow(filename, images, duration, repeat)
|
|
|
return
|
|
@@ -517,11 +521,25 @@ def writeGifPillow(filename, images, duration=0.1, repeat=True):
|
|
|
quantized = []
|
|
|
for im in images:
|
|
|
quantized.append(im.quantize())
|
|
|
- quantized[0].save(filename, save_all=True, append_images=quantized[1:], loop=loop, duration=duration * 1000)
|
|
|
-
|
|
|
-
|
|
|
-def writeGifVisvis(filename, images, duration=0.1, repeat=True, dither=False,
|
|
|
- nq=0, subRectangles=True, dispose=None):
|
|
|
+ quantized[0].save(
|
|
|
+ filename,
|
|
|
+ save_all=True,
|
|
|
+ append_images=quantized[1:],
|
|
|
+ loop=loop,
|
|
|
+ duration=duration * 1000,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def writeGifVisvis(
|
|
|
+ filename,
|
|
|
+ images,
|
|
|
+ duration=0.1,
|
|
|
+ repeat=True,
|
|
|
+ dither=False,
|
|
|
+ nq=0,
|
|
|
+ subRectangles=True,
|
|
|
+ dispose=None,
|
|
|
+):
|
|
|
"""Write an animated gif from the specified images.
|
|
|
Uses VisVis implementation. Unfortunately it produces corrupted GIF
|
|
|
with Pillow >= 3.4.0.
|
|
@@ -576,7 +594,7 @@ def writeGifVisvis(filename, images, duration=0.1, repeat=True, dither=False,
|
|
|
loops = int(repeat)
|
|
|
|
|
|
# Check duration
|
|
|
- if hasattr(duration, '__len__'):
|
|
|
+ if hasattr(duration, "__len__"):
|
|
|
if len(duration) == len(images):
|
|
|
duration = [d for d in duration]
|
|
|
else:
|
|
@@ -596,7 +614,7 @@ def writeGifVisvis(filename, images, duration=0.1, repeat=True, dither=False,
|
|
|
# Check dispose
|
|
|
if dispose is None:
|
|
|
dispose = defaultDispose
|
|
|
- if hasattr(dispose, '__len__'):
|
|
|
+ if hasattr(dispose, "__len__"):
|
|
|
if len(dispose) != len(images):
|
|
|
raise ValueError("len(xy) doesn't match amount of images.")
|
|
|
else:
|
|
@@ -606,7 +624,7 @@ def writeGifVisvis(filename, images, duration=0.1, repeat=True, dither=False,
|
|
|
images = gifWriter.convertImagesToPIL(images, dither, nq)
|
|
|
|
|
|
# Write
|
|
|
- fp = open(filename, 'wb')
|
|
|
+ fp = open(filename, "wb")
|
|
|
try:
|
|
|
gifWriter.writeGifToFile(fp, images, duration, loops, xy, dispose)
|
|
|
finally:
|
|
@@ -629,7 +647,7 @@ def readGif(filename, asNumpy=True):
|
|
|
|
|
|
# Check whether it exists
|
|
|
if not os.path.isfile(filename):
|
|
|
- raise IOError('File not found: ' + str(filename))
|
|
|
+ raise IOError("File not found: " + str(filename))
|
|
|
|
|
|
# Load file using PIL
|
|
|
pilIm = PIL.Image.open(filename)
|
|
@@ -662,7 +680,7 @@ def readGif(filename, asNumpy=True):
|
|
|
|
|
|
|
|
|
class NeuQuant:
|
|
|
- """ NeuQuant(image, samplefac=10, colors=256)
|
|
|
+ """NeuQuant(image, samplefac=10, colors=256)
|
|
|
|
|
|
samplefac should be an integer number of 1 or higher, 1
|
|
|
being the highest quality, but the slowest performance.
|
|
@@ -745,11 +763,11 @@ class NeuQuant:
|
|
|
self.NCYCLES = 100 # Number of learning cycles
|
|
|
self.NETSIZE = colors # Number of colours used
|
|
|
self.SPECIALS = 3 # Number of reserved colours used
|
|
|
- self.BGCOLOR = self.SPECIALS-1 # Reserved background colour
|
|
|
+ self.BGCOLOR = self.SPECIALS - 1 # Reserved background colour
|
|
|
self.CUTNETSIZE = self.NETSIZE - self.SPECIALS
|
|
|
self.MAXNETPOS = self.NETSIZE - 1
|
|
|
|
|
|
- self.INITRAD = self.NETSIZE/8 # For 256 colours, radius starts at 32
|
|
|
+ self.INITRAD = self.NETSIZE / 8 # For 256 colours, radius starts at 32
|
|
|
self.RADIUSBIASSHIFT = 6
|
|
|
self.RADIUSBIAS = 1 << self.RADIUSBIASSHIFT
|
|
|
self.INITBIASRADIUS = self.INITRAD * self.RADIUSBIAS
|
|
@@ -759,19 +777,19 @@ class NeuQuant:
|
|
|
self.INITALPHA = 1 << self.ALPHABIASSHIFT # biased by 10 bits
|
|
|
|
|
|
self.GAMMA = 1024.0
|
|
|
- self.BETA = 1.0/1024.0
|
|
|
+ self.BETA = 1.0 / 1024.0
|
|
|
self.BETAGAMMA = self.BETA * self.GAMMA
|
|
|
|
|
|
# The network itself
|
|
|
- self.network = np.empty((self.NETSIZE, 3), dtype='float64')
|
|
|
+ self.network = np.empty((self.NETSIZE, 3), dtype="float64")
|
|
|
# The network itself
|
|
|
- self.colormap = np.empty((self.NETSIZE, 4), dtype='int32')
|
|
|
+ self.colormap = np.empty((self.NETSIZE, 4), dtype="int32")
|
|
|
|
|
|
- self.netindex = np.empty(256, dtype='int32') # For network lookup - really 256
|
|
|
+ self.netindex = np.empty(256, dtype="int32") # For network lookup - really 256
|
|
|
|
|
|
# Bias and freq arrays for learning
|
|
|
- self.bias = np.empty(self.NETSIZE, dtype='float64')
|
|
|
- self.freq = np.empty(self.NETSIZE, dtype='float64')
|
|
|
+ self.bias = np.empty(self.NETSIZE, dtype="float64")
|
|
|
+ self.freq = np.empty(self.NETSIZE, dtype="float64")
|
|
|
|
|
|
self.pixels = None
|
|
|
self.samplefac = samplefac
|
|
@@ -810,11 +828,11 @@ class NeuQuant:
|
|
|
return self.NETSIZE
|
|
|
|
|
|
def setUpArrays(self):
|
|
|
- self.network[0, 0] = 0.0 # Black
|
|
|
+ self.network[0, 0] = 0.0 # Black
|
|
|
self.network[0, 1] = 0.0
|
|
|
self.network[0, 2] = 0.0
|
|
|
|
|
|
- self.network[1, 0] = 255.0 # White
|
|
|
+ self.network[1, 0] = 255.0 # White
|
|
|
self.network[1, 1] = 255.0
|
|
|
self.network[1, 2] = 255.0
|
|
|
|
|
@@ -826,7 +844,7 @@ class NeuQuant:
|
|
|
|
|
|
for i in range(self.SPECIALS, self.NETSIZE):
|
|
|
p = self.network[i]
|
|
|
- p[:] = (255.0 * (i-self.SPECIALS)) / self.CUTNETSIZE
|
|
|
+ p[:] = (255.0 * (i - self.SPECIALS)) / self.CUTNETSIZE
|
|
|
|
|
|
self.freq[i] = 1.0 / self.NETSIZE
|
|
|
self.bias[i] = 0.0
|
|
@@ -836,40 +854,40 @@ class NeuQuant:
|
|
|
def altersingle(self, alpha, i, b, g, r):
|
|
|
"""Move neuron i towards biased (b, g, r) by factor alpha"""
|
|
|
n = self.network[i] # Alter hit neuron
|
|
|
- n[0] -= (alpha * (n[0] - b))
|
|
|
- n[1] -= (alpha * (n[1] - g))
|
|
|
- n[2] -= (alpha * (n[2] - r))
|
|
|
+ n[0] -= alpha * (n[0] - b)
|
|
|
+ n[1] -= alpha * (n[1] - g)
|
|
|
+ n[2] -= alpha * (n[2] - r)
|
|
|
|
|
|
def geta(self, alpha, rad):
|
|
|
try:
|
|
|
return self.a_s[(alpha, rad)]
|
|
|
except KeyError:
|
|
|
- length = rad * 2-1
|
|
|
- mid = length/2
|
|
|
- q = np.array(list(range(mid-1, -1, -1)) + list(range(-1, mid)))
|
|
|
- a = alpha * (rad * rad - q * q)/(rad * rad)
|
|
|
+ length = rad * 2 - 1
|
|
|
+ mid = length / 2
|
|
|
+ q = np.array(list(range(mid - 1, -1, -1)) + list(range(-1, mid)))
|
|
|
+ a = alpha * (rad * rad - q * q) / (rad * rad)
|
|
|
a[mid] = 0
|
|
|
self.a_s[(alpha, rad)] = a
|
|
|
return a
|
|
|
|
|
|
def alterneigh(self, alpha, rad, i, b, g, r):
|
|
|
- if i-rad >= self.SPECIALS-1:
|
|
|
- lo = i-rad
|
|
|
+ if i - rad >= self.SPECIALS - 1:
|
|
|
+ lo = i - rad
|
|
|
start = 0
|
|
|
else:
|
|
|
- lo = self.SPECIALS-1
|
|
|
- start = (self.SPECIALS-1 - (i-rad))
|
|
|
+ lo = self.SPECIALS - 1
|
|
|
+ start = self.SPECIALS - 1 - (i - rad)
|
|
|
|
|
|
if i + rad <= self.NETSIZE:
|
|
|
hi = i + rad
|
|
|
- end = rad * 2-1
|
|
|
+ end = rad * 2 - 1
|
|
|
else:
|
|
|
hi = self.NETSIZE
|
|
|
- end = (self.NETSIZE - (i + rad))
|
|
|
+ end = self.NETSIZE - (i + rad)
|
|
|
|
|
|
a = self.geta(alpha, rad)[start:end]
|
|
|
|
|
|
- p = self.network[lo + 1:hi]
|
|
|
+ p = self.network[lo + 1 : hi]
|
|
|
p -= np.transpose(np.transpose(p - np.array([b, g, r])) * a)
|
|
|
|
|
|
# def contest(self, b, g, r):
|
|
@@ -901,7 +919,7 @@ class NeuQuant:
|
|
|
bestpos = i + np.argmin(dists)
|
|
|
biasdists = dists - self.bias[i:j]
|
|
|
bestbiaspos = i + np.argmin(biasdists)
|
|
|
- self.freq[i:j] *= (1-self.BETA)
|
|
|
+ self.freq[i:j] *= 1 - self.BETA
|
|
|
self.bias[i:j] += self.BETAGAMMA * self.freq[i:j]
|
|
|
self.freq[bestpos] += self.BETA
|
|
|
self.bias[bestpos] -= self.BETAGAMMA
|
|
@@ -916,7 +934,7 @@ class NeuQuant:
|
|
|
|
|
|
def learn(self):
|
|
|
biasRadius = self.INITBIASRADIUS
|
|
|
- alphadec = 30 + ((self.samplefac-1)/3)
|
|
|
+ alphadec = 30 + ((self.samplefac - 1) / 3)
|
|
|
lengthcount = self.pixels.size
|
|
|
samplepixels = lengthcount / self.samplefac
|
|
|
delta = samplepixels / self.NCYCLES
|
|
@@ -943,16 +961,16 @@ class NeuQuant:
|
|
|
step = NeuQuant.PRIME4
|
|
|
|
|
|
i = 0
|
|
|
- printed_string = ''
|
|
|
+ printed_string = ""
|
|
|
while i < samplepixels:
|
|
|
if i % 100 == 99:
|
|
|
- tmp = '\b' * len(printed_string)
|
|
|
- printed_string = str((i + 1) * 100/samplepixels) + "%\n"
|
|
|
+ tmp = "\b" * len(printed_string)
|
|
|
+ printed_string = str((i + 1) * 100 / samplepixels) + "%\n"
|
|
|
print(tmp + printed_string)
|
|
|
p = self.pixels[pos]
|
|
|
- r = (p >> 16) & 0xff
|
|
|
- g = (p >> 8) & 0xff
|
|
|
- b = (p) & 0xff
|
|
|
+ r = (p >> 16) & 0xFF
|
|
|
+ g = (p >> 8) & 0xFF
|
|
|
+ b = (p) & 0xFF
|
|
|
|
|
|
if i == 0: # Remember background colour
|
|
|
self.network[self.BGCOLOR] = [b, g, r]
|
|
@@ -977,7 +995,7 @@ class NeuQuant:
|
|
|
if rad <= 1:
|
|
|
rad = 0
|
|
|
|
|
|
- finalAlpha = (1.0 * alpha)/self.INITALPHA
|
|
|
+ finalAlpha = (1.0 * alpha) / self.INITALPHA
|
|
|
print("Finished 1D learning: final alpha = %1.2f!" % finalAlpha)
|
|
|
|
|
|
def fix(self):
|
|
@@ -1029,7 +1047,7 @@ class NeuQuant:
|
|
|
for i in range(self.NETSIZE):
|
|
|
palette.extend(self.colormap[i][:3])
|
|
|
|
|
|
- palette.extend([0] * (256-self.NETSIZE) * 3)
|
|
|
+ palette.extend([0] * (256 - self.NETSIZE) * 3)
|
|
|
|
|
|
# a palette image to use for quant
|
|
|
self.pimage = Image.new("P", (1, 1), 0)
|
|
@@ -1045,7 +1063,7 @@ class NeuQuant:
|
|
|
if get_cKDTree():
|
|
|
return self.quantize_with_scipy(image)
|
|
|
else:
|
|
|
- print('Scipy not available, falling back to slower version.')
|
|
|
+ print("Scipy not available, falling back to slower version.")
|
|
|
return self.quantize_without_scipy(image)
|
|
|
|
|
|
def quantize_with_scipy(self, image):
|
|
@@ -1057,13 +1075,13 @@ class NeuQuant:
|
|
|
kdtree = cKDTree(self.colormap[:, :3], leafsize=10)
|
|
|
result = kdtree.query(px2)
|
|
|
colorindex = result[1]
|
|
|
- print("Distance: %1.2f" % (result[0].sum()/(w * h)))
|
|
|
+ print("Distance: %1.2f" % (result[0].sum() / (w * h)))
|
|
|
px2[:] = self.colormap[colorindex, :3]
|
|
|
|
|
|
return Image.fromarray(px).convert("RGB").quantize(palette=self.paletteImage())
|
|
|
|
|
|
def quantize_without_scipy(self, image):
|
|
|
- """" This function can be used if no scipy is available.
|
|
|
+ """ " This function can be used if no scipy is available.
|
|
|
It's 7 times slower though.
|
|
|
|
|
|
:param image:
|
|
@@ -1088,16 +1106,16 @@ class NeuQuant:
|
|
|
|
|
|
def inxsearch(self, r, g, b):
|
|
|
"""Search for BGR values 0..255 and return colour index"""
|
|
|
- dists = (self.colormap[:, :3] - np.array([r, g, b]))
|
|
|
+ dists = self.colormap[:, :3] - np.array([r, g, b])
|
|
|
a = np.argmin((dists * dists).sum(1))
|
|
|
return a
|
|
|
|
|
|
|
|
|
-if __name__ == '__main__':
|
|
|
+if __name__ == "__main__":
|
|
|
im = np.zeros((200, 200), dtype=np.uint8)
|
|
|
- im[10: 30, :] = 100
|
|
|
- im[:, 80: 120] = 255
|
|
|
- im[-50: -40, :] = 50
|
|
|
+ im[10:30, :] = 100
|
|
|
+ im[:, 80:120] = 255
|
|
|
+ im[-50:-40, :] = 50
|
|
|
|
|
|
images = [im * 1.0, im * 0.8, im * 0.6, im * 0.4, im * 0]
|
|
|
- writeGif('lala3.gif', images, duration=0.5, dither=0)
|
|
|
+ writeGif("lala3.gif", images, duration=0.5, dither=0)
|