first commit
This commit is contained in:
commit
381adbb30a
216
dots.py
Executable file
216
dots.py
Executable file
|
@ -0,0 +1,216 @@
|
||||||
|
#! /usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Makes one of those meme pics with the dots.
|
||||||
|
"""
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageChops, ImageStat
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
class Dots():
|
||||||
|
def __init__(self):
|
||||||
|
self.maxRadius = 11
|
||||||
|
self.tolerance = 70
|
||||||
|
|
||||||
|
self.original = None
|
||||||
|
self.image = None
|
||||||
|
self.colors = None
|
||||||
|
|
||||||
|
|
||||||
|
def differenceImage(self, img1, img2):
|
||||||
|
"""
|
||||||
|
Returns the difference between two images as a numpy array.
|
||||||
|
"""
|
||||||
|
img1, img2 = map(np.uint8, [img1, img2])
|
||||||
|
a = img1 - img2
|
||||||
|
b = np.uint8(img1 < img2) * 254 + 1
|
||||||
|
return a * b
|
||||||
|
|
||||||
|
|
||||||
|
def compareDotsV1(self, back, dot, origDot):
|
||||||
|
"""
|
||||||
|
Finds the pseudo-distance between each of the images and determines
|
||||||
|
if the placed dot is better or worse than before it was placed.
|
||||||
|
"""
|
||||||
|
diff1 = ImageChops.difference(origDot, dot)
|
||||||
|
diff2 = ImageChops.difference(origDot, back)
|
||||||
|
return np.mean(diff1) < np.mean(diff2)
|
||||||
|
|
||||||
|
|
||||||
|
def compareDotsV12(self, back, dot, origDot):
|
||||||
|
"""
|
||||||
|
Finds the pseudo-distance between each of the images and determines
|
||||||
|
if the placed dot is better or worse than before it was placed.
|
||||||
|
"""
|
||||||
|
diff1 = ImageChops.difference(origDot, dot)
|
||||||
|
diff2 = ImageChops.difference(origDot, back)
|
||||||
|
median1 = ImageStat.Stat(diff1).median
|
||||||
|
median2 = ImageStat.Stat(diff2).median
|
||||||
|
return sum(median1)/3 < sum(median2)/3
|
||||||
|
|
||||||
|
|
||||||
|
def compareDotsV2(self, back, dot, origDot):
|
||||||
|
"""
|
||||||
|
Finds the pseudo-distance between each of the images and determines
|
||||||
|
if the placed dot is better or worse than before it was placed.
|
||||||
|
"""
|
||||||
|
diff1 = self.differenceImage(origDot, dot)
|
||||||
|
diff2 = self.differenceImage(origDot, back)
|
||||||
|
return np.mean(diff1) < np.mean(diff2)
|
||||||
|
|
||||||
|
|
||||||
|
def placeDot(self, draw):
|
||||||
|
"""
|
||||||
|
Places random dot, then compares pixels.
|
||||||
|
"""
|
||||||
|
center = tuple(map(random.randrange, self.original.size))
|
||||||
|
radius = random.randrange(1, self.maxRadius)
|
||||||
|
boundBox = [center[0] - radius, center[1] - radius,
|
||||||
|
center[0] + radius, center[1] + radius]
|
||||||
|
boundBoxCir = [boundBox[0], boundBox[1], boundBox[2]-1, boundBox[3]-1]
|
||||||
|
# color = tuple(map(random.randrange, (256, 256, 256)))
|
||||||
|
color = random.choice(self.colors)
|
||||||
|
|
||||||
|
# backup of the original
|
||||||
|
back = self.image.crop(boundBox)
|
||||||
|
draw.ellipse(boundBoxCir, fill=color)
|
||||||
|
dot = self.image.crop(boundBox)
|
||||||
|
origDot = self.original.crop(boundBox)
|
||||||
|
|
||||||
|
if self.compareDots(back, dot, origDot):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.image.paste(back, boundBox)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def compareImage(self):
|
||||||
|
"""
|
||||||
|
Compares the original image to the new image. If the mean is less
|
||||||
|
than the tolerance, the new image is close enough to be done.
|
||||||
|
"""
|
||||||
|
# pass
|
||||||
|
diff = self.differenceImage(self.original, self.image)
|
||||||
|
mean = np.mean(diff)
|
||||||
|
# print(mean)
|
||||||
|
return mean < self.tolerance
|
||||||
|
|
||||||
|
|
||||||
|
def buildDots(self, imagePath, output):
|
||||||
|
"""
|
||||||
|
Places randomly sized, randomly colored and radomly placed dots onto
|
||||||
|
a blank canvas the same size as the original. The dot is then compared
|
||||||
|
to the original: if the color of the effected pixels are closer to that
|
||||||
|
of the original, the dot is kept. otherwise, it is removed.
|
||||||
|
"""
|
||||||
|
# TODO: handle RGBA
|
||||||
|
self.original = Image.open(imagePath)
|
||||||
|
self.colors = list(set(self.original.getdata()))
|
||||||
|
self.image = Image.new("RGB", self.original.size, (255,255,255))
|
||||||
|
draw = ImageDraw.Draw(self.image)
|
||||||
|
|
||||||
|
# while True:
|
||||||
|
for n in range(500000):
|
||||||
|
ret = self.placeDot(draw)
|
||||||
|
# if ret:
|
||||||
|
# if self.compareImage():
|
||||||
|
# break
|
||||||
|
print(n)
|
||||||
|
self.image.save(output, "PNG")
|
||||||
|
|
||||||
|
|
||||||
|
def testCompareDiffernece(size, func, nargs):
|
||||||
|
"""
|
||||||
|
Test for Dots.compareDots() or Dots.differenceImage(). func should be the
|
||||||
|
function you're testing for, and nargs should be the number of args it
|
||||||
|
takes.
|
||||||
|
"""
|
||||||
|
images = []
|
||||||
|
for n in range(nargs):
|
||||||
|
color = (n*100,) * 3
|
||||||
|
image = Image.new("RGB", (size, size), color)
|
||||||
|
images.append(image)
|
||||||
|
|
||||||
|
then = time.time()
|
||||||
|
func(*images)
|
||||||
|
return time.time() - then
|
||||||
|
|
||||||
|
|
||||||
|
def printResults(times):
|
||||||
|
"""
|
||||||
|
Format and print the results of our tests.
|
||||||
|
"""
|
||||||
|
mean = format(np.mean(times), '.8f')
|
||||||
|
minT = format(min(times), '.8f')
|
||||||
|
maxT = format(max(times), '.8f')
|
||||||
|
return f"Mean: {mean} Min: {minT} Max: {maxT}"
|
||||||
|
|
||||||
|
|
||||||
|
def test():
|
||||||
|
"""
|
||||||
|
Testing shit for speed.
|
||||||
|
"""
|
||||||
|
dots = Dots()
|
||||||
|
sizes = [1, 5, 10, 20, 30, 40, 50, 100, 500, 1000]
|
||||||
|
# print("------- Testing Dots.differenceImage() -------")
|
||||||
|
# for size in sizes:
|
||||||
|
# times = []
|
||||||
|
# for n in range(1000):
|
||||||
|
# t = testCompareDiffernece(size, dots.differenceImage, 2)
|
||||||
|
# times.append(t)
|
||||||
|
# print(f"Size: {size}x{size}\t{printResults(times)}")
|
||||||
|
|
||||||
|
print("\n------- Testing Dots.compareDotsV1() (PIL-NumPy) -------")
|
||||||
|
for size in sizes:
|
||||||
|
times = []
|
||||||
|
for n in range(1000):
|
||||||
|
t = testCompareDiffernece(size, dots.compareDotsV1, 3)
|
||||||
|
times.append(t)
|
||||||
|
print(f"Size: {size}x{size}\t{printResults(times)}")
|
||||||
|
|
||||||
|
print("\n------- Testing Dots.compareDotsV12() (Pure PIL) -------")
|
||||||
|
for size in sizes:
|
||||||
|
times = []
|
||||||
|
for n in range(1000):
|
||||||
|
t = testCompareDiffernece(size, dots.compareDotsV12, 3)
|
||||||
|
times.append(t)
|
||||||
|
print(f"Size: {size}x{size}\t{printResults(times)}")
|
||||||
|
|
||||||
|
print("\n------- Testing Dots.compareDotsV2() (NumPy) -------")
|
||||||
|
for size in sizes:
|
||||||
|
times = []
|
||||||
|
for n in range(1000):
|
||||||
|
t = testCompareDiffernece(size, dots.compareDotsV2, 3)
|
||||||
|
times.append(t)
|
||||||
|
print(f"Size: {size}x{size}\t{printResults(times)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Makes one of those meme images with the dots.")
|
||||||
|
parser.add_argument(
|
||||||
|
"imagePath",
|
||||||
|
nargs="?",
|
||||||
|
help="Path to the original image.")
|
||||||
|
parser.add_argument(
|
||||||
|
"output",
|
||||||
|
nargs="?",
|
||||||
|
help="Path to output image.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--test",
|
||||||
|
action="store_true",
|
||||||
|
help="Speed testing.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.test:
|
||||||
|
test()
|
||||||
|
exit()
|
||||||
|
|
||||||
|
dots = Dots()
|
||||||
|
then = time.time()
|
||||||
|
dots.buildDots(args.imagePath, args.output)
|
||||||
|
print(time.time() - then)
|
Loading…
Reference in New Issue
Block a user