From 381adbb30a24c4b71e6f36b2bff6fda29fba73ba Mon Sep 17 00:00:00 2001 From: iou1name Date: Tue, 19 Dec 2017 07:45:24 -0500 Subject: [PATCH] first commit --- README.md | 1 + dots.py | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 README.md create mode 100755 dots.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c541a8 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +makes dots diff --git a/dots.py b/dots.py new file mode 100755 index 0000000..a747ab2 --- /dev/null +++ b/dots.py @@ -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) \ No newline at end of file