├── .gitignore ├── LICENCE ├── README.md ├── examples ├── blur.jpg ├── bokeh.jpg ├── glitch.jpg ├── halftone.jpg ├── kaleidoscope.jpg ├── leaves.jpg ├── leopard.jpg ├── ombre.jpg ├── original.jpg ├── plaid.jpg ├── pxl.jpg ├── space.jpg ├── stich.jpg ├── tats.jpg └── triangles.jpg ├── jam_image_filter ├── __init__.py ├── assets │ ├── airbrush.png │ ├── bokeh.png │ ├── leaves.png │ ├── leopard.png │ ├── ombre.jpg │ ├── plaid.png │ ├── space.jpg │ └── tats.png ├── blur.py ├── bokeh.py ├── glitch.py ├── halftone.py ├── kaleidoscope.py ├── leaves.py ├── leopard.py ├── ombre.py ├── plaid.py ├── pxl.py ├── requirements.txt ├── space.py ├── stitch.py ├── tats.py ├── triangles.py ├── util.py ├── win31.py └── win31 │ ├── 256color.bmp │ ├── arcade.bmp │ ├── arches.bmp │ ├── argyle.bmp │ ├── cars.bmp │ ├── castle.bmp │ ├── chitz.bmp │ ├── egypt.bmp │ ├── flock.bmp │ ├── honey.bmp │ ├── leaves.bmp │ ├── marble.bmp │ ├── redbrick.bmp │ ├── rivets.bmp │ ├── squares.bmp │ ├── tartan.bmp │ ├── thatch.bmp │ └── zigzag.bmp └── src └── glitch ├── Makefile └── glitch.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | timj_image_effect.egg-info 3 | *~ 4 | *.egg 5 | *dSYM* 6 | 7 | src/glitch/glitch 8 | 9 | *.pyc 10 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Jam Image Filters 2 | Copyright (C) 2013 This Is My Jam 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This Is My Jam Image Filters 2 | ============================ 3 | 4 | A set of image filters crafted and curated by This Is My Jam. These are currently live on www.thisismyjam.com/jam/style, where they are used to automatically generate backgrounds from jam images. 5 | 6 | There's also a [blog post](http://thisismyjam.tumblr.com/post/44061678763/how-we-built-music-looks-awesome-how-you-can-join) we wrote about how and why we created these filters. 7 | 8 | Installation 9 | ------------ 10 | 11 | To install the Python filters: 12 | 13 | cd jam_image_filter 14 | sudo pip install -r requirements.txt 15 | 16 | To install *glitch*: 17 | 18 | cd src/glitch 19 | make 20 | sudo make install 21 | 22 | Usage 23 | ----- 24 | 25 | The Python filters all reside in the `jam_image_filter` folder. To invoke: 26 | 27 | python FILTER.py INPUT_PATH OUTPUT_PATH 28 | 29 | Contribute 30 | ---------- 31 | 32 | Have an algorithm? Send us a pull request and we'll see if we can get it up on Jam. You can write your script in pretty much any language (as long as we can figure out how to run it, and it doesn't open massive security holes). 33 | 34 | Some constraints: 35 | 36 | * It should take the same argument list as the existing ones, i.e. the first argument should be an input file and the second an output file 37 | * Your script should work with .jpg, .png, and .gif input files (also, grayscale, semi-transparency, etc.) 38 | * Ideally, it should either tile or have 1700x540px dimensions 39 | * It should run under 2 seconds and not use crazy amounts of memory 40 | 41 | There are some useful functions in util.py, but you don't have to use them. 42 | 43 | Examples 44 | -------- 45 | 46 | ![Halftone](https://raw.github.com/thisismyjam/jam-image-filter/master/examples/halftone.jpg) 47 | 48 | ![PXL](https://raw.github.com/thisismyjam/jam-image-filter/master/examples/pxl.jpg) 49 | 50 | ![Glitch](https://raw.github.com/thisismyjam/jam-image-filter/master/examples/glitch.jpg) 51 | 52 | For more examples look inside the `examples` folder. 53 | -------------------------------------------------------------------------------- /examples/blur.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/blur.jpg -------------------------------------------------------------------------------- /examples/bokeh.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/bokeh.jpg -------------------------------------------------------------------------------- /examples/glitch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/glitch.jpg -------------------------------------------------------------------------------- /examples/halftone.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/halftone.jpg -------------------------------------------------------------------------------- /examples/kaleidoscope.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/kaleidoscope.jpg -------------------------------------------------------------------------------- /examples/leaves.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/leaves.jpg -------------------------------------------------------------------------------- /examples/leopard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/leopard.jpg -------------------------------------------------------------------------------- /examples/ombre.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/ombre.jpg -------------------------------------------------------------------------------- /examples/original.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/original.jpg -------------------------------------------------------------------------------- /examples/plaid.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/plaid.jpg -------------------------------------------------------------------------------- /examples/pxl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/pxl.jpg -------------------------------------------------------------------------------- /examples/space.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/space.jpg -------------------------------------------------------------------------------- /examples/stich.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/stich.jpg -------------------------------------------------------------------------------- /examples/tats.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/tats.jpg -------------------------------------------------------------------------------- /examples/triangles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/examples/triangles.jpg -------------------------------------------------------------------------------- /jam_image_filter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/__init__.py -------------------------------------------------------------------------------- /jam_image_filter/assets/airbrush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/airbrush.png -------------------------------------------------------------------------------- /jam_image_filter/assets/bokeh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/bokeh.png -------------------------------------------------------------------------------- /jam_image_filter/assets/leaves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/leaves.png -------------------------------------------------------------------------------- /jam_image_filter/assets/leopard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/leopard.png -------------------------------------------------------------------------------- /jam_image_filter/assets/ombre.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/ombre.jpg -------------------------------------------------------------------------------- /jam_image_filter/assets/plaid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/plaid.png -------------------------------------------------------------------------------- /jam_image_filter/assets/space.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/space.jpg -------------------------------------------------------------------------------- /jam_image_filter/assets/tats.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/assets/tats.png -------------------------------------------------------------------------------- /jam_image_filter/blur.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import math 3 | import scipy.ndimage as ndimage 4 | import scipy 5 | import Image 6 | import ImageFilter 7 | import numpy as np 8 | import util 9 | 10 | def blur(original, sigma=10, darken=True): 11 | original = util.resize_jam_background(original) 12 | original = original.convert('RGB') 13 | 14 | if darken: 15 | original = original.point(lambda p: p * .8) 16 | 17 | image = scipy.misc.fromimage(original, flatten=False) 18 | 19 | image = image.transpose((2, 0, 1)) 20 | r = ndimage.gaussian_filter(image[0], sigma=sigma) 21 | g = ndimage.gaussian_filter(image[1], sigma=sigma) 22 | b = ndimage.gaussian_filter(image[2], sigma=sigma) 23 | image = np.array([r, g, b]).transpose((1, 2, 0)) 24 | 25 | return scipy.misc.toimage(image) 26 | 27 | if __name__ == '__main__': 28 | image = Image.open(sys.argv[1]) 29 | image = blur(image) 30 | image.save(sys.argv[2], quality=90) 31 | -------------------------------------------------------------------------------- /jam_image_filter/bokeh.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import Image 5 | import ImageOps 6 | import util 7 | import colorsys 8 | import random 9 | 10 | def bokeh(image): 11 | image = image.convert('RGB') 12 | colours = util.get_dominant_colours(image, 8) 13 | colours = util.order_colours_by_brightness(colours)[2:7] 14 | colour = random.choice(colours) 15 | colour = util.modify_hsv(colour, s=lambda s: 255, v=lambda v: 255) 16 | light = (255, 244, 180) 17 | 18 | layer = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 19 | 'assets/bokeh.png') 20 | layer = util.random_crop(layer, util.WIDTH, util.HEIGHT) 21 | r, g, b, a = layer.split() 22 | layer = layer.convert('RGB') 23 | layer = ImageOps.grayscale(layer) 24 | layer = ImageOps.colorize(layer, colour, light) 25 | layer.putalpha(a) 26 | im = Image.new('RGB', layer.size) 27 | im.paste(layer, (0, 0), layer) 28 | return im 29 | 30 | if __name__ == '__main__': 31 | im = Image.open(sys.argv[1]) 32 | im = bokeh(im) 33 | im.save(sys.argv[2], quality=90) 34 | -------------------------------------------------------------------------------- /jam_image_filter/glitch.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import math 3 | import Image 4 | import numpy as np 5 | import util 6 | import sh 7 | import random 8 | import tempfile 9 | import os 10 | 11 | # pre/post-processor for /src/glitch 12 | def glitch(im, min_pixelation=3, max_pixelation=18, max_attempts=20, 13 | min_diff=5000, max_diff=25000, darken=True): 14 | 15 | try: 16 | sh.glitch 17 | except sh.CommandNotFound: 18 | print 'Could not find glitch. Did you build and install it?' 19 | sys.exit(1) 20 | 21 | im = im.convert('RGB') 22 | im = util.resize_jam_background(im) 23 | width, height = im.size 24 | 25 | pixelation = random.random() % (max_pixelation - min_pixelation) + min_pixelation 26 | im = im.resize((int(width / pixelation), int(height / pixelation))) 27 | im = im.resize((int(width), int(height))) 28 | 29 | infile = tempfile.NamedTemporaryFile(suffix='.jpg', prefix='glitchtmp', 30 | delete=False) 31 | outfile = tempfile.NamedTemporaryFile(suffix='.jpg', prefix='glitchtmp', 32 | delete=False) 33 | infile.close() 34 | outfile.close() 35 | 36 | im.save(infile.name) 37 | 38 | # naive image diff to ensure image is not too different from original 39 | for attempt in xrange(max_attempts): 40 | sh.glitch(infile.name, outfile.name) 41 | inim = Image.open(infile.name) 42 | outim = Image.open(outfile.name) 43 | inpix = inim.resize((10, 10)).load() 44 | outpix = outim.resize((10, 10)).load() 45 | diff = 0 46 | for y in xrange(10): 47 | for x in xrange(10): 48 | for i in xrange(3): 49 | diff += abs(inpix[x, y][i] - outpix[x, y][i]) 50 | if diff > min_diff and diff < max_diff: 51 | break 52 | 53 | im = Image.open(outfile.name) 54 | if darken: 55 | im = im.point(lambda p: p * .8) 56 | 57 | os.unlink(infile.name) 58 | os.unlink(outfile.name) 59 | 60 | return im 61 | 62 | if __name__ == '__main__': 63 | image = Image.open(sys.argv[1]) 64 | image = glitch(image) 65 | image.save(sys.argv[2], quality=90) 66 | -------------------------------------------------------------------------------- /jam_image_filter/halftone.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import aggdraw 3 | import math 4 | import Image 5 | import util 6 | import random 7 | from ImageEnhance import Contrast 8 | 9 | def rnd(): 10 | return 1 + random.randint(-1, 1) / 6.0 11 | 12 | def halftone(original, radius=3, border=21, black_and_white=False): 13 | original = util.resize_jam_background(original, util.WIDTH + border * 2, 14 | util.HEIGHT + border * 2) 15 | original = original.convert('RGB') 16 | width, height = original.size 17 | 18 | if black_and_white: 19 | bg = (255, 255, 255) 20 | fg = (0, 0, 0) 21 | else: 22 | colours = util.get_dominant_colours(original, 10) 23 | colours = util.order_colours_by_brightness(colours) 24 | fg = tuple(random.choice(colours[-6:])) 25 | bg = tuple(random.choice(colours[:3])) 26 | if fg == bg: 27 | bg = (255, 255, 255) 28 | fg = (0, 0, 0) 29 | 30 | original = Contrast(original).enhance(1.5) 31 | 32 | pix = original.load() 33 | 34 | new = Image.new('RGB', (width, height), bg) 35 | 36 | draw = aggdraw.Draw(new) 37 | pen = aggdraw.Pen(fg) 38 | brush = aggdraw.Brush(fg) 39 | 40 | x_incr = 2 * radius 41 | y_incr = math.sqrt(3) * radius 42 | for y in xrange(0, height + 1, int(y_incr)): 43 | odd_offset = radius * (y / int(y_incr) % 2) 44 | for x in range(odd_offset, width + 1, x_incr): 45 | avg_gray = util.get_avg_gray(pix, x, y, radius) 46 | if avg_gray > .9: 47 | r = radius 48 | rnd = lambda: 1 49 | else: 50 | r = radius * avg_gray 51 | rnd = lambda: 1 + random.randint(-1, 1) / 5.0 52 | draw.ellipse((x - r * rnd(), y - r * rnd(), x + r * rnd(), y + r * rnd()), pen, brush) 53 | draw.flush() 54 | 55 | new = util.centre_crop(new, util.WIDTH, util.HEIGHT) 56 | new = new.point(lambda p: p - 20) 57 | 58 | return new 59 | 60 | if __name__ == '__main__': 61 | image = Image.open(sys.argv[1]) 62 | image = halftone(image) 63 | image.save(sys.argv[2], quality=90) 64 | -------------------------------------------------------------------------------- /jam_image_filter/kaleidoscope.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from PIL import Image 3 | import util 4 | 5 | def kaleidoscope(original, max_width=400, max_height=400, darken=True): 6 | original = original.convert('RGB') 7 | original = util.max_size(original, max_width, max_height) 8 | width, height = original.size 9 | original_pix = original.load() 10 | 11 | length = min(height, width) 12 | 13 | new = Image.new('RGB', (length * 2, length * 2)) 14 | new_pix = new.load() 15 | 16 | for part in xrange(8): 17 | 18 | reverse = part in [1, 4, 6, 7] 19 | flip = part in [3, 4, 5, 6] 20 | turn = part in [2, 3, 6, 7] 21 | 22 | for y in xrange(length): 23 | for x in xrange(length - y - 1, length): 24 | 25 | if turn: 26 | to_x = length + y + 1 27 | to_y = length + (length - x) - 1 28 | else: 29 | to_x = x 30 | to_y = length + y 31 | 32 | if reverse: 33 | to_x = 2 * length - to_x - 1 34 | if flip: 35 | to_y = 2 * length - to_y - 1 36 | 37 | try: 38 | new_pix[to_x, to_y] = original_pix[x, y] 39 | except IndexError: 40 | pass 41 | 42 | if darken: 43 | new = new.point(lambda p: p * .8) 44 | 45 | return new 46 | 47 | if __name__ == '__main__': 48 | image = Image.open(sys.argv[1]) 49 | image = kaleidoscope(image) 50 | image.save(sys.argv[2], quality=90) 51 | -------------------------------------------------------------------------------- /jam_image_filter/leaves.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import Image 5 | import ImageOps 6 | import util 7 | import colorsys 8 | import random 9 | 10 | def leaves(image): 11 | image = image.convert('RGB') 12 | colours = util.get_dominant_colours(image, 8) 13 | light, dark = [colours[i] for i in random.sample(range(len(colours)), 2)] 14 | 15 | layer = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 16 | 'assets/leaves.png') 17 | 18 | layer = layer.convert('RGB') 19 | layer = ImageOps.grayscale(layer) 20 | layer = ImageOps.colorize(layer, dark, light) 21 | 22 | width, height = layer.size 23 | 24 | # shift half of the image horizontally 25 | left = layer.crop((0, 0, width / 2, height)) 26 | right = layer.crop((width / 2, 0, width, height)) 27 | new = Image.new('RGB', (width, height)) 28 | new.paste(left, (width / 2, 0)) 29 | new.paste(right, (0, 0)) 30 | 31 | return new 32 | 33 | if __name__ == '__main__': 34 | im = Image.open(sys.argv[1]) 35 | im = leaves(im) 36 | im.save(sys.argv[2], quality=90) 37 | -------------------------------------------------------------------------------- /jam_image_filter/leopard.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import Image 5 | import ImageOps 6 | import util 7 | import colorsys 8 | import random 9 | 10 | def leopard(image): 11 | image = image.convert('RGB') 12 | colours = util.get_dominant_colours(image, 12) 13 | colours = util.order_colours_by_brightness(colours) 14 | indices = sorted(random.sample(range(len(colours)), 3)) 15 | colours = [colours[i] for i in indices] 16 | colours = util.spread_colours_by_lightness(colours) 17 | light, bg, dark = map(tuple, colours) 18 | 19 | layer = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 20 | 'assets/leopard.png') 21 | layer.load() 22 | r, g, b, a = layer.split() 23 | layer = layer.convert('RGB') 24 | layer = ImageOps.grayscale(layer) 25 | layer = ImageOps.colorize(layer, dark, light) 26 | layer.putalpha(a) 27 | im = Image.new('RGB', layer.size, bg) 28 | im.paste(layer, mask=layer) 29 | return im 30 | 31 | if __name__ == '__main__': 32 | im = Image.open(sys.argv[1]) 33 | im = leopard(im) 34 | im.save(sys.argv[2], quality=90) 35 | -------------------------------------------------------------------------------- /jam_image_filter/ombre.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import Image 5 | import ImageOps 6 | import util 7 | import colorsys 8 | import random 9 | 10 | def ombre(image): 11 | image = image.convert('RGB') 12 | colours = util.get_dominant_colours(image, 12) 13 | colours = util.order_colours_by_brightness(colours) 14 | light = random.choice(colours[:3]) 15 | dark = random.choice(colours[-3:]) 16 | 17 | layer = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 18 | 'assets/ombre.jpg') 19 | layer = util.random_crop(layer, util.WIDTH, util.HEIGHT) 20 | 21 | layer = layer.convert('RGB') 22 | layer = ImageOps.grayscale(layer) 23 | layer = ImageOps.colorize(layer, dark, light) 24 | return layer 25 | 26 | if __name__ == '__main__': 27 | im = Image.open(sys.argv[1]) 28 | im = ombre(im) 29 | im.save(sys.argv[2], quality=90) 30 | -------------------------------------------------------------------------------- /jam_image_filter/plaid.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import Image 5 | import ImageOps 6 | import util 7 | import colorsys 8 | import random 9 | 10 | def plaid(image): 11 | image = image.convert('RGB') 12 | colours = util.get_dominant_colours(image, 12) 13 | colours = util.order_colours_by_brightness(colours) 14 | indices = sorted(random.sample(range(len(colours)), 3)) 15 | colours = [colours[i] for i in indices] 16 | light, bg, dark = map(tuple, colours) 17 | 18 | layer = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 19 | 'assets/plaid.png') 20 | layer.load() 21 | r, g, b, a = layer.split() 22 | layer = layer.convert('RGB') 23 | layer = ImageOps.grayscale(layer) 24 | layer = ImageOps.colorize(layer, dark, light) 25 | layer.putalpha(a) 26 | im = Image.new('RGB', layer.size, bg) 27 | im.paste(layer, mask=layer) 28 | return im 29 | 30 | if __name__ == '__main__': 31 | im = Image.open(sys.argv[1]) 32 | im = plaid(im) 33 | im.save(sys.argv[2], quality=90) 34 | -------------------------------------------------------------------------------- /jam_image_filter/pxl.py: -------------------------------------------------------------------------------- 1 | # Adapted from https://github.com/mccutchen/triangulizor 2 | 3 | # Copyright (C) 2012 by Will McCutchen and individual contributors. 4 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | import sys 24 | import aggdraw 25 | import math 26 | import Image 27 | import itertools 28 | import logging 29 | import util 30 | 31 | def pxl(image, tile_size=32): 32 | """Processes the given image by breaking it down into tiles of the given 33 | size and applying a triangular effect to each tile. Returns the processed 34 | image as a PIL Image object. 35 | 36 | The image can be given as anything suitable for passing to `Image.open` 37 | (ie, the path to an image or as a file-like object containing image data). 38 | 39 | If tile_size is 0, the tile size will be guessed based on the image 40 | size. It will also be adjusted to be divisible by 2 if it is not already. 41 | """ 42 | 43 | # Make sure we have a usable tile size, by guessing based on image size 44 | # and making sure it's a multiple of two. 45 | if tile_size == 0: 46 | tile_size = guess_tile_size(image) 47 | if tile_size % 2 != 0: 48 | tile_size = (tile_size / 2) * 2 49 | 50 | logging.info('Input image size: %r', image.size) 51 | logging.info('Tile size: %r', tile_size) 52 | 53 | # Preprocess image to make sure it's at a size we can handle 54 | image = prep_image(image, tile_size) 55 | logging.info('Prepped image size: %r', image.size) 56 | 57 | # Get pixmap (for direct pixel access) and draw objects for the image. 58 | pix = image.load() 59 | draw = aggdraw.Draw(image) 60 | 61 | # Process the image, tile by tile 62 | for x, y in iter_tiles(image, tile_size): 63 | process_tile(x, y, tile_size, pix, draw, image) 64 | draw.flush() 65 | 66 | return image 67 | 68 | 69 | def process_tile(tile_x, tile_y, tile_size, pix, draw, image): 70 | """Process a tile whose top left corner is at the given x and y 71 | coordinates. 72 | """ 73 | logging.debug('Processing tile (%d, %d)', tile_x, tile_y) 74 | 75 | # Calculate average color for each "triangle" in the given tile 76 | n, e, s, w = triangle_colors(tile_x, tile_y, tile_size, pix) 77 | 78 | # Calculate distance between triangle pairs 79 | d_ne = get_color_dist(n, e) 80 | d_nw = get_color_dist(n, w) 81 | d_se = get_color_dist(s, e) 82 | d_sw = get_color_dist(s, w) 83 | 84 | # Figure out which pair is the closest, which will determine the direction 85 | # we'll split this tile into triangles. A 'right' split runs from top left 86 | # to bottom right. A 'left' split runs bottom left to top right. 87 | closest = sorted([d_ne, d_nw, d_se, d_sw])[0] 88 | if closest in (d_ne, d_sw): 89 | split = 'right' 90 | elif closest in (d_nw, d_se): 91 | split = 'left' 92 | 93 | # Figure out the average color for each side of the "split" 94 | if split == 'right': 95 | top_color = get_average_color([n, e]) 96 | bottom_color = get_average_color([s, w]) 97 | else: 98 | top_color = get_average_color([n, w]) 99 | bottom_color = get_average_color([s, e]) 100 | 101 | draw_triangles(tile_x, tile_y, tile_size, split, top_color, bottom_color, 102 | draw) 103 | 104 | 105 | def triangle_colors(tile_x, tile_y, tile_size, pix): 106 | """Extracts the average color for each triangle in the given tile. Returns 107 | a 4-tuple of colors for the triangles in this order: North, East, South, 108 | West (clockwise). 109 | """ 110 | quad_size = tile_size / 2 111 | 112 | north = [] 113 | for y in xrange(tile_y, tile_y + quad_size): 114 | x_off = y - tile_y 115 | for x in xrange(tile_x + x_off, tile_x + tile_size - x_off): 116 | north.append(pix[x, y]) 117 | 118 | south = [] 119 | for y in xrange(tile_y + quad_size, tile_y + tile_size): 120 | x_off = tile_y + tile_size - y 121 | for x in xrange(tile_x + x_off, tile_x + tile_size - x_off): 122 | south.append(pix[x, y]) 123 | 124 | east = [] 125 | for x in xrange(tile_x, tile_x + quad_size): 126 | y_off = x - tile_x 127 | for y in xrange(tile_y + y_off, tile_y + tile_size - y_off): 128 | east.append(pix[x, y]) 129 | 130 | west = [] 131 | for x in xrange(tile_x + quad_size, tile_x + tile_size): 132 | y_off = tile_x + tile_size - x 133 | for y in xrange(tile_y + y_off, tile_y + tile_size - y_off): 134 | west.append(pix[x, y]) 135 | 136 | return map(get_average_color, [north, east, south, west]) 137 | 138 | 139 | def draw_triangles(tile_x, tile_y, tile_size, split, top_color, bottom_color, 140 | draw): 141 | """Draws a triangle on each half of the tile with the given coordinates 142 | and size. 143 | """ 144 | assert split in ('right', 'left') 145 | 146 | # The four corners of this tile 147 | nw = (tile_x, tile_y) 148 | ne = (tile_x + tile_size - 1, tile_y) 149 | se = (tile_x + tile_size - 1, tile_y + tile_size) 150 | sw = (tile_x, tile_y + tile_size) 151 | 152 | if split == 'left': 153 | # top right triangle 154 | draw_triangle(nw, ne, se, top_color, draw) 155 | # bottom left triangle 156 | draw_triangle(nw, sw, se, bottom_color, draw) 157 | else: 158 | # top left triangle 159 | draw_triangle(sw, nw, ne, top_color, draw) 160 | # bottom right triangle 161 | draw_triangle(sw, se, ne, bottom_color, draw) 162 | 163 | 164 | def draw_triangle(a, b, c, color, draw): 165 | """Draws a triangle with the given vertices in the given color.""" 166 | pen = aggdraw.Pen(color) 167 | brush = aggdraw.Brush(color) 168 | draw.polygon(a + b + c, pen, brush) 169 | 170 | 171 | def get_average_color(colors): 172 | """Calculate the average color from the list of colors, where each color 173 | is a 3-tuple of (r, g, b) values. 174 | """ 175 | c = reduce(color_reducer, colors) 176 | total = len(colors) 177 | return tuple(v / total for v in c) 178 | 179 | 180 | def color_reducer(c1, c2): 181 | """Helper function used to add two colors together when averaging.""" 182 | return tuple(v1 + v2 for v1, v2 in itertools.izip(c1, c2)) 183 | 184 | 185 | def get_color_dist(c1, c2): 186 | """Calculates the "distance" between two colors, where the distance is 187 | another color whose components are the absolute values of the difference 188 | between each component of the input colors. 189 | """ 190 | return tuple(abs(v1 - v2) for v1, v2 in itertools.izip(c1, c2)) 191 | 192 | 193 | def prep_image(image, tile_size): 194 | """Takes an image and a tile size and returns a possibly cropped version 195 | of the image that is evenly divisible in both dimensions by the tile size. 196 | """ 197 | w, h = image.size 198 | x_tiles = w / tile_size # floor division 199 | y_tiles = h / tile_size 200 | new_w = x_tiles * tile_size 201 | new_h = y_tiles * tile_size 202 | if new_w == w and new_h == h: 203 | return image 204 | else: 205 | crop_bounds = (0, 0, new_w, new_h) 206 | return image.crop(crop_bounds) 207 | 208 | 209 | def iter_tiles(image, tile_size): 210 | """Yields (x, y) coordinate pairs for the top left corner of each tile in 211 | the given image, based on the given tile size. 212 | """ 213 | w, h = image.size 214 | for y in xrange(0, h, tile_size): 215 | for x in xrange(0, w, tile_size): 216 | yield x, y 217 | 218 | 219 | def guess_tile_size(image): 220 | """Try to pick an appropriate tile size based on the image's size.""" 221 | # Formula: 5% of the largest dimension of the image 222 | return int(max(image.size) * 0.05) 223 | 224 | if __name__ == '__main__': 225 | image = Image.open(sys.argv[1]) 226 | image = image.convert('RGB') 227 | tile_size = 32 228 | width = util.WIDTH + tile_size - (util.WIDTH % tile_size) 229 | height = util.HEIGHT + tile_size- (util.HEIGHT % tile_size) 230 | image = util.resize_jam_background(image, width, height) 231 | image = pxl(image) 232 | image.save(sys.argv[2], quality=90) 233 | -------------------------------------------------------------------------------- /jam_image_filter/requirements.txt: -------------------------------------------------------------------------------- 1 | PIL 2 | sh 3 | scipy 4 | numpy 5 | -e git+https://github.com/jakul/aggdraw.git#egg=aggdraw 6 | -------------------------------------------------------------------------------- /jam_image_filter/space.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import Image 5 | import ImageOps 6 | import util 7 | import colorsys 8 | import random 9 | 10 | def space(image): 11 | image = image.convert('RGB') 12 | colours = util.get_dominant_colours(image, 12) 13 | colours = util.order_colours_by_brightness(colours) 14 | indices = sorted(random.sample(range(len(colours)), 3)) 15 | colours = [colours[i] for i in indices] 16 | light, bg, dark = map(tuple, colours) 17 | light = (200, 200, 100) 18 | dark = (100, 200, 100) 19 | bg = (0, 0, 50, 255) 20 | 21 | layer = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 22 | 'assets/space.jpg') 23 | layer = util.random_crop(layer, util.WIDTH, util.HEIGHT) 24 | 25 | colours = util.get_dominant_colours(image, 10) 26 | colours = util.order_colours_by_saturation(colours)[:-3] 27 | colours = random.sample(colours, 5) 28 | colours = util.order_colours_by_hue(colours) 29 | 30 | layer = layer.convert('RGB') 31 | gradient = util.create_gradient(layer.size, colours) 32 | im = Image.blend(layer, gradient, .4) 33 | 34 | return im 35 | 36 | if __name__ == '__main__': 37 | im = Image.open(sys.argv[1]) 38 | im = space(im) 39 | im.save(sys.argv[2], quality=90) 40 | -------------------------------------------------------------------------------- /jam_image_filter/stitch.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import aggdraw 3 | import math 4 | import Image 5 | import scipy.spatial.distance as distance 6 | import numpy as np 7 | from ImageEnhance import Contrast 8 | import util 9 | import random 10 | 11 | def stitch(im, pixelation=12): 12 | im = im.convert('RGB') 13 | 14 | im = util.resize_jam_background(im) 15 | width, height = im.size 16 | 17 | # pixelate 18 | im = im.resize((int(math.ceil(width / float(pixelation))), 19 | int(math.ceil(height / float(pixelation))))) 20 | pix = im.load() 21 | 22 | # random bg colour 23 | colours = util.get_dominant_colours(im, 2) 24 | colours = map(tuple, colours) 25 | colours += [(0,0,0), (255,255,255)] 26 | bg_index = random.randint(0, len(colours) - 1) 27 | 28 | width, height = im.size 29 | new = Image.new('RGB', (width * pixelation, height * pixelation), colours[bg_index]) 30 | draw = aggdraw.Draw(new) 31 | width, height = new.size 32 | 33 | # draw stitches 34 | for y in xrange(0, height, pixelation): 35 | for x in xrange(0, width, pixelation): 36 | pen = aggdraw.Pen(pix[x / pixelation, y / pixelation], 2) 37 | draw.line((x, y, x + pixelation - 3, y + pixelation - 3), pen) 38 | draw.line((x + pixelation - 3, y, x, y + pixelation - 3), pen) 39 | draw.flush() 40 | 41 | return new 42 | 43 | if __name__ == '__main__': 44 | image = Image.open(sys.argv[1]) 45 | image = stitch(image) 46 | image.save(sys.argv[2], quality=90) 47 | -------------------------------------------------------------------------------- /jam_image_filter/tats.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import math 4 | import Image 5 | import ImageOps 6 | import util 7 | import colorsys 8 | import random 9 | 10 | def tats(image): 11 | image = image.convert('RGB') 12 | colours = util.get_dominant_colours(image, 9) 13 | colours = util.order_colours_by_brightness(colours) 14 | 15 | bg = random.choice(colours[:3]) 16 | light = random.choice(colours[3:6]) 17 | dark = random.choice(colours[6:]) 18 | 19 | dist = math.sqrt(sum(map(lambda (a, b): math.pow(a - b, 2), zip(light, dark)))) 20 | if dist < 100: 21 | light = util.modify_hls(light, l=lambda l: l + 100) 22 | 23 | light = util.modify_hls(light, s=lambda s: s + 100) 24 | dark = util.modify_hls(dark, s=lambda s: s + 100) 25 | 26 | layer = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 27 | 'assets/tats.png') 28 | layer.load() 29 | r, g, b, a = layer.split() 30 | layer = layer.convert('RGB') 31 | layer = ImageOps.grayscale(layer) 32 | layer = ImageOps.colorize(layer, tuple(dark), tuple(light)) 33 | layer.putalpha(a) 34 | im = Image.new('RGB', layer.size, tuple(bg)) 35 | im.paste(layer, mask=layer) 36 | return im 37 | 38 | if __name__ == '__main__': 39 | im = Image.open(sys.argv[1]) 40 | im = tats(im) 41 | im.save(sys.argv[2], quality=90) 42 | -------------------------------------------------------------------------------- /jam_image_filter/triangles.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import aggdraw 3 | import math 4 | import Image 5 | import random 6 | import numpy as np 7 | import util 8 | import os.path 9 | import ImageOps 10 | 11 | def get_triangle_path(mid_x, mid_y, min_length = 70, max_length = 70): 12 | angle = random.random() * (2 * math.pi / 3) 13 | points = [] 14 | for angle in np.arange(angle, angle + 2 * math.pi, 2 * math.pi / 3): 15 | length = min_length + random.random() * (max_length - min_length) 16 | x = mid_x + math.cos(angle) * length 17 | y = mid_y + math.sin(angle) * length 18 | points.extend([x, y]) 19 | return points 20 | 21 | def triangles(original, n=30, size=150, width=700, 22 | height=700, min_distance=70, opacity=220): 23 | original = original.convert('RGB') 24 | colours = util.get_dominant_colours(original, 8) 25 | colours_ordered = util.order_colours_by_brightness(colours) 26 | brightest_colour = list(random.choice(colours_ordered[:3])) 27 | brightest_colour.append(0) 28 | 29 | new = Image.new('RGBA', (width, height), tuple(brightest_colour)) 30 | 31 | draw = aggdraw.Draw(new) 32 | 33 | centres = [] 34 | 35 | def is_too_close(x, y): 36 | for centre in centres: 37 | if math.sqrt(math.pow(centre[0] - x, 2) + 38 | math.pow(centre[1] - y, 2)) < min_distance: 39 | return True 40 | return False 41 | 42 | for i in xrange(n): 43 | 44 | colour = tuple(colours[int(random.randint(0, len(colours) - 1))]) 45 | x = random.randint(size / 2, width - 1 - size / 2) 46 | y = random.randint(size / 2, height - 1 - size / 2) 47 | 48 | if is_too_close(x, y): 49 | continue 50 | 51 | centres.append((x, y)) 52 | 53 | brush = aggdraw.Brush(colour, opacity) 54 | points = get_triangle_path(x, y, size / 2, size / 2) 55 | draw.polygon(points, None, brush) 56 | draw.flush() 57 | 58 | brightest_colour = brightest_colour[0:3] 59 | 60 | texture = Image.open(os.path.dirname(os.path.abspath(__file__)) + '/' + 61 | 'assets/airbrush.png') 62 | texture = texture.convert('RGBA') 63 | r, g, b, a = texture.split() 64 | texture = ImageOps.grayscale(texture) 65 | texture = ImageOps.colorize(texture, (0, 0, 0), tuple(brightest_colour)) 66 | texture = texture.convert('RGBA') 67 | texture.putalpha(a) 68 | 69 | r, g, b, a = new.split() 70 | 71 | texture = texture.crop((0, 0, width, height)) 72 | texture_layer = Image.new('RGBA', (width, height), (0, 0, 0, 0)) 73 | texture_layer.paste(texture, mask=new) 74 | new.paste(texture_layer, mask=texture_layer) 75 | 76 | im = Image.new('RGB', (width, height), tuple(brightest_colour)) 77 | im.paste(new, mask=a) 78 | 79 | return im 80 | 81 | if __name__ == '__main__': 82 | image = Image.open(sys.argv[1]) 83 | image = triangles(image) 84 | image.save(sys.argv[2], quality=90) 85 | -------------------------------------------------------------------------------- /jam_image_filter/util.py: -------------------------------------------------------------------------------- 1 | import math 2 | import colorsys 3 | import scipy 4 | import scipy.cluster 5 | import scipy.misc 6 | import operator 7 | import math 8 | import Image 9 | import numpy as np 10 | import random 11 | 12 | WIDTH = 1700 13 | HEIGHT = 540 14 | 15 | def rgb_to_gray(r, g, b, a = None): 16 | return 0.299 * r + 0.587 * g + 0.114 * b 17 | 18 | def get_avg_gray(pix, x, y, radius, sample_size = 0.1): 19 | nsamples = math.pow(radius, 2) * sample_size 20 | avg = 0 21 | for y in xrange(y - radius, y + radius, int(1 / sample_size)): 22 | for x in xrange(x - radius, x + radius, int(1 / sample_size)): 23 | try: 24 | if len(pix[x, y]) >= 3: 25 | avg += rgb_to_gray(*pix[x,y]) 26 | else: 27 | avg += pix[x, y] 28 | except IndexError: 29 | pass 30 | return 1 - avg / nsamples / 255.0 31 | 32 | def get_dominant_colours(im, n): 33 | small_width = 50 34 | small_height = 50 35 | orig_width, orig_height = im.size 36 | im = im.resize((small_width, small_height)) 37 | array = scipy.misc.fromimage(im) 38 | width, height, ncolours = array.shape 39 | array = array.reshape(width * height, ncolours) 40 | codes, dist = scipy.cluster.vq.kmeans(array, n) 41 | codes = np.array([map(int, colour) for colour in codes]) 42 | 43 | codes = pad_colours(codes, n) 44 | 45 | #vec, dist = scipy.cluster.vq.vq(array, codes) 46 | #vec = vec.reshape(width, height) 47 | #scale = (orig_width / float(small_width), orig_height / float(small_height)) 48 | return codes#, vec, scale 49 | 50 | def pad_colours(rgbs, n): 51 | new_rgbs = [None] * n 52 | for i in xrange(n): 53 | j = int(i / (n / float(len(rgbs)))) 54 | new_rgbs[i] = rgbs[j] 55 | return new_rgbs 56 | 57 | def order_colours_by_brightness(colours): 58 | colours_value = [] 59 | for colour in colours: 60 | c = colour / 255.0 61 | value = colorsys.rgb_to_hls(*c)[1] 62 | colours_value.append((value, colour)) 63 | colours_value.sort(key=operator.itemgetter(0), reverse=True) 64 | return map(operator.itemgetter(1), colours_value) 65 | 66 | def order_colours_by_hue(colours): 67 | colours_value = [] 68 | for colour in colours: 69 | c = colour / 255.0 70 | value = colorsys.rgb_to_hls(*c)[0] 71 | colours_value.append((value, colour)) 72 | colours_value.sort(key=operator.itemgetter(0), reverse=True) 73 | return map(operator.itemgetter(1), colours_value) 74 | 75 | def order_colours_by_saturation(colours): 76 | colours_value = [] 77 | for colour in colours: 78 | c = colour / 255.0 79 | value = colorsys.rgb_to_hsv(*c)[1] 80 | colours_value.append((value, colour)) 81 | colours_value.sort(key=operator.itemgetter(0), reverse=True) 82 | return map(operator.itemgetter(1), colours_value) 83 | 84 | def resize_jam_background(im, target_width=WIDTH, target_height=HEIGHT, 85 | max_resize=16.0, pixelated=False): 86 | width, height = im.size 87 | scale, crop_left, crop_right, crop_top, crop_bottom = \ 88 | get_resize_params(im, target_width, target_height, max_resize) 89 | 90 | if pixelated: 91 | im = im.resize((int(width * scale), int(height * scale))) 92 | else: 93 | im = im.resize((int(width * scale), int(height * scale)), Image.BILINEAR) 94 | im = im.crop((crop_left, crop_top, crop_right, crop_bottom)) 95 | 96 | return im 97 | 98 | def centre_crop(im, width, height): 99 | im_width, im_height = im.size 100 | width = min(width, im_width) 101 | height = min(height, im_height) 102 | left = max(0, (im_width - width) / 2) 103 | top = max(0, (im_height - height) / 2) 104 | 105 | return im.crop((left, top, left + width, top + height)) 106 | 107 | def random_crop(im, width, height): 108 | im_width, im_height = im.size 109 | width = min(width, im_width) 110 | height = min(height, im_height) 111 | 112 | if im_width > width: 113 | left = random.randint(0, im_width - width - 1) 114 | else: 115 | left = 0 116 | if im_height > height: 117 | top = random.randint(0, im_height - height - 1) 118 | else: 119 | top = 0 120 | 121 | return im.crop((left, top, left + width, top + height)) 122 | 123 | def get_resize_params(im, target_width=WIDTH, target_height=HEIGHT, max_resize=4.0): 124 | width, height = im.size 125 | 126 | scale = min(max_resize, max(target_width / float(width), 127 | target_height / float(height))) 128 | width = int(width * scale) 129 | height = int(height * scale) 130 | 131 | crop_left = int(max(0, (width - target_width) / 2)) 132 | crop_right = int(width - crop_left) 133 | crop_top = int(max(0, (height - target_height) / 2)) 134 | crop_bottom = int(height - crop_top) 135 | 136 | return scale, crop_left, crop_right, crop_top, crop_bottom 137 | 138 | def max_size(im, target_width, target_height): 139 | width, height = im.size 140 | scale = max(width / target_width, height / target_height) 141 | if scale > 1: 142 | return im.resize((width / scale, height / scale)) 143 | else: 144 | return im 145 | 146 | def interpolate_colour(colours, x): 147 | i = (len(colours) - 1) * x 148 | start_i = int(math.floor(i)) 149 | end_i = start_i + 1 150 | delta = i - start_i 151 | 152 | start_rgb = map(int, colours[start_i]) 153 | end_rgb = map(int, colours[end_i]) 154 | 155 | rgb = [] 156 | for i in range(3): 157 | rgb.append(int(start_rgb[i] + (end_rgb[i] - start_rgb[i]) * delta)) 158 | 159 | return tuple(rgb) 160 | 161 | def create_gradient(size, colours): 162 | vertical = True # hard coded for now 163 | width, height = size 164 | im = Image.new('RGB', (1, height)) 165 | pix = im.load() 166 | 167 | for i in xrange(height): 168 | pix[0, i] = interpolate_colour(colours, i / float(height)) 169 | 170 | im = im.resize((width, height), Image.BILINEAR) 171 | return im 172 | 173 | # rgb = (r, g, b), where 0 <= r, g, b <= 255 174 | # returns (h, l, s), where 0 <= h, l, s <= 255 175 | def rgb_to_hls(rgb): 176 | rgb = map(lambda x: x / 255.0, rgb) 177 | h, l, s = colorsys.rgb_to_hls(*rgb) 178 | return tuple(map(lambda x: x * 255.0, [h, l, s])) 179 | 180 | def hls_to_rgb(hls): 181 | hls = map(lambda x: x / 255.0, hls) 182 | r, g, b = colorsys.hls_to_rgb(*hls) 183 | return tuple(map(lambda x: int(x * 255.0), [r, g, b])) 184 | 185 | def modify_hls(rgb, h=None, l=None, s=None): 186 | hls = list(rgb_to_hls(rgb)) 187 | mods = [h, l, s] 188 | for i, mod in enumerate(mods): 189 | if mod: 190 | old = hls[i] 191 | hls[i] = max(min(mod(hls[i]), 255), 0) 192 | return hls_to_rgb(hls) 193 | 194 | def rgb_to_hsv(rgb): 195 | rgb = map(lambda x: x / 255.0, rgb) 196 | h, s, v = colorsys.rgb_to_hsv(*rgb) 197 | return tuple(map(lambda x: x * 255.0, [h, s, v])) 198 | 199 | def hsv_to_rgb(hsv): 200 | hsv = map(lambda x: x / 255.0, hsv) 201 | r, g, b = colorsys.hsv_to_rgb(*hsv) 202 | return tuple(map(lambda x: int(x * 255.0), [r, g, b])) 203 | 204 | def modify_hsv(rgb, h=None, s=None, v=None): 205 | hsv = list(rgb_to_hsv(rgb)) 206 | mods = [h, s, v] 207 | for i, mod in enumerate(mods): 208 | if mod: 209 | old = hsv[i] 210 | hsv[i] = max(min(mod(hsv[i]), 255), 0) 211 | return hsv_to_rgb(hsv) 212 | 213 | def spread_colours_by_lightness(rgbs, min_lightness=50, max_lightness=220): 214 | lightnesses = map(lambda rgb: rgb_to_hls(rgb)[1], rgbs) 215 | bottom, top = min(lightnesses), max(lightnesses) 216 | scale = float((max_lightness - min_lightness)) / (top - bottom) 217 | 218 | def modify_lightness(l): 219 | l = l - bottom 220 | l = l * scale 221 | l = l + min_lightness 222 | return l 223 | 224 | for i, rgb in enumerate(rgbs): 225 | rgbs[i] = modify_hls(rgb, l=modify_lightness) 226 | 227 | return rgbs 228 | -------------------------------------------------------------------------------- /jam_image_filter/win31.py: -------------------------------------------------------------------------------- 1 | import Image 2 | import random 3 | import glob 4 | import sys 5 | import os 6 | 7 | def win31(im): 8 | tiles = glob.glob(os.path.abspath(os.path.dirname(__file__)) + '/win31/*.bmp') 9 | tile = random.choice(tiles) 10 | new = Image.open(tile) 11 | new = new.resize((new.size[0] * 2, new.size[1] * 2)) 12 | return new 13 | 14 | if __name__ == '__main__': 15 | image = Image.open(sys.argv[1]) 16 | image = win31(image) 17 | image.save(sys.argv[2], quality=90) 18 | -------------------------------------------------------------------------------- /jam_image_filter/win31/256color.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/256color.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/arcade.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/arcade.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/arches.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/arches.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/argyle.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/argyle.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/cars.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/cars.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/castle.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/castle.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/chitz.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/chitz.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/egypt.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/egypt.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/flock.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/flock.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/honey.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/honey.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/leaves.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/leaves.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/marble.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/marble.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/redbrick.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/redbrick.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/rivets.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/rivets.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/squares.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/squares.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/tartan.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/tartan.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/thatch.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/thatch.bmp -------------------------------------------------------------------------------- /jam_image_filter/win31/zigzag.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thisismyjam/jam-image-filter/c87b832ad4a8b5db706c443367367feb5232e951/jam_image_filter/win31/zigzag.bmp -------------------------------------------------------------------------------- /src/glitch/Makefile: -------------------------------------------------------------------------------- 1 | glitch: glitch.cpp 2 | g++ -g -o glitch glitch.cpp `Wand-config --cflags --cppflags --ldflags --libs` 3 | 4 | all: glitch 5 | 6 | install: glitch 7 | cp glitch /usr/bin/glitch 8 | -------------------------------------------------------------------------------- /src/glitch/glitch.cpp: -------------------------------------------------------------------------------- 1 | // Based on http://www.jonolick.com/code.html (public domain) 2 | 3 | // Glitches an image by running it through a broken jpeg encoder 4 | // Usage: glitch INFILE OUTFILE 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,5,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; 16 | 17 | void glitch_zigzag(unsigned char *zigzag, int minchanges, int maxchanges) { 18 | int good_indices[] = {46, 49}; 19 | 20 | int changes = minchanges; 21 | if(maxchanges - minchanges > 0) 22 | changes += random() % (maxchanges + 1 - minchanges); 23 | 24 | int i; 25 | for(i = 0; i < changes; i ++) { 26 | int index = good_indices[random() % (sizeof(good_indices) / sizeof(int))]; 27 | zigzag[index] = random() % 63; 28 | } 29 | } 30 | 31 | int glitch_std_ac_values(unsigned char *values, int minchanges, int maxchanges) { 32 | int good_indices[] = {6, 7, 8, 11, 12, 16, 17, 18, 21, 32, 38}; 33 | 34 | int changes = minchanges; 35 | if(maxchanges - minchanges > 0) 36 | changes += random() % (maxchanges + 1 - minchanges); 37 | 38 | int i; 39 | for(i = 0; i < changes; i ++) { 40 | int index = good_indices[random() % (sizeof(good_indices) / sizeof(int))]; 41 | values[index] = random() % 0xff; 42 | } 43 | 44 | return changes; 45 | } 46 | 47 | int glitch(void *array_void, size_t array_size, size_t item_length, 48 | int minswaps, int maxswaps) { 49 | unsigned char *array = (unsigned char *)array_void; 50 | int length = array_size / item_length; 51 | 52 | int swaps = minswaps; 53 | if(maxswaps - minswaps > 0) 54 | swaps += random() % (maxswaps + 1 - minswaps); 55 | 56 | int first, second; 57 | int i; 58 | void *temp = alloca(item_length); 59 | for(i = 0; i < swaps; i ++) { 60 | first = random() % length; 61 | second = random() % length; 62 | if(first < second) 63 | std::swap(first, second); 64 | memcpy(array + second * item_length, array + first * item_length, item_length); 65 | } 66 | 67 | return swaps; 68 | } 69 | 70 | static void jo_writeBits(FILE *fp, int &bitBuf, int &bitCnt, const unsigned short *bs) { 71 | bitCnt += bs[1]; 72 | bitBuf |= bs[0] << (24 - bitCnt); 73 | while(bitCnt >= 8) { 74 | unsigned char c = (bitBuf >> 16) & 255; 75 | putc(c, fp); 76 | if(c == 255) { 77 | putc(0, fp); 78 | } 79 | bitBuf <<= 8; 80 | bitCnt -= 8; 81 | } 82 | } 83 | 84 | static void jo_DCT(float &d0, float &d1, float &d2, float &d3, float &d4, float &d5, float &d6, float &d7) { 85 | float tmp0 = d0 + d7; 86 | float tmp7 = d0 - d7; 87 | float tmp1 = d1 + d6; 88 | float tmp6 = d1 - d6; 89 | float tmp2 = d2 + d5; 90 | float tmp5 = d2 - d5; 91 | float tmp3 = d3 + d4; 92 | float tmp4 = d3 - d4; 93 | 94 | // Even part 95 | float tmp10 = tmp0 + tmp3; // phase 2 96 | float tmp13 = tmp0 - tmp3; 97 | float tmp11 = tmp1 + tmp2; 98 | float tmp12 = tmp1 - tmp2; 99 | 100 | d0 = tmp10 + tmp11; // phase 3 101 | d4 = tmp10 - tmp11; 102 | 103 | float z1 = (tmp12 + tmp13) * 0.707106781f; // c4 104 | d2 = tmp13 + z1; // phase 5 105 | d6 = tmp13 - z1; 106 | 107 | // Odd part 108 | tmp10 = tmp4 + tmp5; // phase 2 109 | tmp11 = tmp5 + tmp6; 110 | tmp12 = tmp6 + tmp7; 111 | 112 | // The rotator is modified from fig 4-8 to avoid extra negations. 113 | float z5 = (tmp10 - tmp12) * 0.382683433f; // c6 114 | float z2 = tmp10 * 0.541196100f + z5; // c2-c6 115 | float z4 = tmp12 * 1.306562965f + z5; // c2+c6 116 | float z3 = tmp11 * 0.707106781f; // c4 117 | 118 | float z11 = tmp7 + z3; // phase 5 119 | float z13 = tmp7 - z3; 120 | 121 | d5 = z13 + z2; // phase 6 122 | d3 = z13 - z2; 123 | d1 = z11 + z4; 124 | d7 = z11 - z4; 125 | } 126 | 127 | static void jo_calcBits(int val, unsigned short bits[2]) { 128 | int tmp1 = val < 0 ? -val : val; 129 | val = val < 0 ? val-1 : val; 130 | bits[1] = 1; 131 | while(tmp1 >>= 1) { 132 | ++bits[1]; 133 | } 134 | bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { 169 | } 170 | // end0pos = first element in reverse order !=0 171 | if(end0pos == 0) { 172 | jo_writeBits(fp, bitBuf, bitCnt, EOB); 173 | return DU[0]; 174 | } 175 | for(int i = 1; i <= end0pos; ++i) { 176 | int startpos = i; 177 | for (; DU[i]==0 && i<=end0pos; ++i) { 178 | } 179 | int nrzeroes = i-startpos; 180 | if ( nrzeroes >= 16 ) { 181 | int lng = nrzeroes>>4; 182 | for (int nrmarker=1; nrmarker <= lng; ++nrmarker) 183 | jo_writeBits(fp, bitBuf, bitCnt, M16zeroes); 184 | nrzeroes &= 15; 185 | } 186 | unsigned short bits[2]; 187 | jo_calcBits(DU[i], bits); 188 | jo_writeBits(fp, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); 189 | jo_writeBits(fp, bitBuf, bitCnt, bits); 190 | } 191 | if(end0pos != 63) { 192 | jo_writeBits(fp, bitBuf, bitCnt, EOB); 193 | } 194 | return DU[0]; 195 | } 196 | 197 | bool jo_write_jpg(const char *filename, const void *data, int width, int height, int comp, int quality) { 198 | 199 | int glitches = 0; 200 | int max_glitches = 1; 201 | 202 | // Constants that don't pollute global namespace 203 | unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; 204 | 205 | unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 206 | 207 | unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; 208 | 209 | unsigned char std_ac_luminance_values[] = { 210 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 211 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 212 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 213 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 214 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 215 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 216 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 217 | }; 218 | 219 | if(glitches < max_glitches) 220 | glitches += glitch_std_ac_values(std_ac_luminance_values, 0, 1); 221 | 222 | unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; 223 | 224 | unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; 225 | 226 | unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; 227 | unsigned char std_ac_chrominance_values[] = { 228 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 229 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 230 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 231 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 232 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 233 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 234 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa 235 | }; 236 | if(glitches < max_glitches) 237 | glitches += glitch_std_ac_values(std_ac_chrominance_values, 0, 1); 238 | 239 | // Huffman tables 240 | unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; 241 | if(glitches < max_glitches) 242 | glitches += glitch(YDC_HT, sizeof(YDC_HT), sizeof(unsigned short), 0, 1); 243 | 244 | unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; 245 | if(glitches < max_glitches) 246 | glitches += glitch(UVDC_HT, sizeof(UVDC_HT), sizeof(unsigned short), 0, 1); 247 | 248 | unsigned short YAC_HT[256][2] = { 249 | {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 250 | {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 251 | {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 252 | {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 253 | {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 254 | {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 255 | {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 256 | {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 257 | {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 258 | {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 259 | {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 260 | {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 261 | {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 262 | {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 263 | {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, 264 | {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} 265 | }; 266 | 267 | unsigned short UVAC_HT[256][2] = { 268 | {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 269 | {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 270 | {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 271 | {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 272 | {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 273 | {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 274 | {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 275 | {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 276 | {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 277 | {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 278 | {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 279 | {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 280 | {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 281 | {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, 282 | {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, 283 | {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} 284 | }; 285 | if(glitches < max_glitches) 286 | glitches += glitch(UVAC_HT, sizeof(UVAC_HT), sizeof(unsigned short), 1, 1); 287 | 288 | int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; 289 | 290 | int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; 291 | 292 | float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; 293 | 294 | if(!data || !filename || !width || !height || comp > 4 || comp < 1 || comp == 2) { 295 | return false; 296 | } 297 | 298 | FILE *fp = fopen(filename, "wb"); 299 | if(!fp) { 300 | return false; 301 | } 302 | 303 | quality = quality ? quality : 90; 304 | quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; 305 | quality = quality < 50 ? 5000 / quality : 200 - quality * 2; 306 | 307 | unsigned char YTable[64], UVTable[64]; 308 | for(int i = 0; i < 64; ++i) { 309 | int yti = (YQT[i]*quality+50)/100; 310 | YTable[s_jo_ZigZag[i]] = yti < 1 ? 1 : yti > 255 ? 255 : yti; 311 | int uvti = (UVQT[i]*quality+50)/100; 312 | UVTable[s_jo_ZigZag[i]] = uvti < 1 ? 1 : uvti > 255 ? 255 : uvti; 313 | } 314 | 315 | float fdtbl_Y[64], fdtbl_UV[64]; 316 | for(int row = 0, k = 0; row < 8; ++row) { 317 | for(int col = 0; col < 8; ++col, ++k) { 318 | fdtbl_Y[k] = 1 / (YTable [s_jo_ZigZag[k]] * aasf[row] * aasf[col]); 319 | fdtbl_UV[k] = 1 / (UVTable[s_jo_ZigZag[k]] * aasf[row] * aasf[col]); 320 | } 321 | } 322 | 323 | // Write Headers 324 | static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; 325 | fwrite(head0, sizeof(head0), 1, fp); 326 | fwrite(YTable, sizeof(YTable), 1, fp); 327 | putc(1, fp); 328 | fwrite(UVTable, sizeof(UVTable), 1, fp); 329 | const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,height>>8,height&0xFF,width>>8,width&0xFF,3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; 330 | fwrite(head1, sizeof(head1), 1, fp); 331 | fwrite(std_dc_luminance_nrcodes+1, sizeof(std_dc_luminance_nrcodes)-1, 1, fp); 332 | fwrite(std_dc_luminance_values, sizeof(std_dc_luminance_values), 1, fp); 333 | putc(0x10, fp); // HTYACinfo 334 | fwrite(std_ac_luminance_nrcodes+1, sizeof(std_ac_luminance_nrcodes)-1, 1, fp); 335 | fwrite(std_ac_luminance_values, sizeof(std_ac_luminance_values), 1, fp); 336 | putc(1, fp); // HTUDCinfo 337 | fwrite(std_dc_chrominance_nrcodes+1, sizeof(std_dc_chrominance_nrcodes)-1, 1, fp); 338 | fwrite(std_dc_chrominance_values, sizeof(std_dc_chrominance_values), 1, fp); 339 | putc(0x11, fp); // HTUACinfo 340 | fwrite(std_ac_chrominance_nrcodes+1, sizeof(std_ac_chrominance_nrcodes)-1, 1, fp); 341 | fwrite(std_ac_chrominance_values, sizeof(std_ac_chrominance_values), 1, fp); 342 | static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; 343 | fwrite(head2, sizeof(head2), 1, fp); 344 | 345 | // Encode 8x8 macroblocks 346 | const unsigned char *imageData = (const unsigned char *)data; 347 | int DCY=0, DCU=0, DCV=0; 348 | int bitBuf=0, bitCnt=0; 349 | int ofsG = comp > 1 ? 1 : 0, ofsB = comp > 1 ? 2 : 0; 350 | for(int y = 0; y < height; y += 8) { 351 | for(int x = 0; x < width; x += 8) { 352 | float YDU[64], UDU[64], VDU[64]; 353 | for(int row = y, pos = 0; row < y+8; ++row) { 354 | for(int col = x; col < x+8; ++col, ++pos) { 355 | int p = row*width*comp + col*comp; 356 | if(row >= height) { 357 | p -= width*comp*(row+1 - height); 358 | } 359 | if(col >= width) { 360 | p -= comp*(col+1 - width); 361 | } 362 | 363 | float r = imageData[p+0], g = imageData[p+ofsG], b = imageData[p+ofsB]; 364 | YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; 365 | UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; 366 | VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; 367 | } 368 | } 369 | 370 | DCY = jo_processDU(fp, bitBuf, bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); 371 | DCU = jo_processDU(fp, bitBuf, bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); 372 | DCV = jo_processDU(fp, bitBuf, bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); 373 | } 374 | } 375 | 376 | // Do the bit alignment of the EOI marker 377 | static const unsigned short fillBits[] = {0x7F, 7}; 378 | jo_writeBits(fp, bitBuf, bitCnt, fillBits); 379 | 380 | // EOI 381 | putc(0xFF, fp); 382 | putc(0xD9, fp); 383 | 384 | fclose(fp); 385 | return true; 386 | } 387 | 388 | int main(int argc, char *argv[]) 389 | { 390 | MagickBooleanType status; 391 | MagickWand *wand; 392 | MagickPixelPacket pixel; 393 | PixelIterator *iterator; 394 | PixelWand **pixels; 395 | long x, y, width, height; 396 | char *data; 397 | char *datap; 398 | 399 | MagickWandGenesis(); 400 | wand = NewMagickWand(); 401 | status = MagickReadImage(wand, argv[1]); 402 | iterator = NewPixelIterator(wand); 403 | 404 | width = MagickGetImageWidth(wand); 405 | height = MagickGetImageHeight(wand); 406 | datap = data = (char *)calloc(width * height * 4, sizeof(char)); 407 | 408 | for(y = 0; y < height; y ++) { 409 | pixels = PixelGetNextIteratorRow(iterator, (size_t *)&width); 410 | for(x = 0; x < width; x ++) { 411 | *(datap ++) = (char)(PixelGetRed(pixels[x]) * 255); 412 | *(datap ++) = (char)(PixelGetGreen(pixels[x]) * 255); 413 | *(datap ++) = (char)(PixelGetBlue(pixels[x]) * 255); 414 | ++ datap; 415 | } 416 | } 417 | 418 | struct timeval tv; 419 | gettimeofday(&tv,NULL); 420 | srandom((unsigned)(tv.tv_usec + time(NULL))); 421 | 422 | jo_write_jpg(argv[2], data, width, height, 4, 90); 423 | 424 | return 0; 425 | } 426 | --------------------------------------------------------------------------------