Skip to content

Instantly share code, notes, and snippets.

@pashango2
Last active February 27, 2017 12:41
Show Gist options
  • Save pashango2/487e5147015655a7fd6db3cbc1c7c833 to your computer and use it in GitHub Desktop.
Save pashango2/487e5147015655a7fd6db3cbc1c7c833 to your computer and use it in GitHub Desktop.
Halftone effect for Python3 and pillow
# coding: utf-8
from PIL import Image, ImageOps, ImageChops, ImageFilter, ImageDraw, ImageDraw2
import math
def create_mat(_rot, _pitch):
_pi = math.pi * (_rot / 180.)
_scale = 1.0 / _pitch
def _mul(x, y):
return (
(x * math.cos(_pi) - y * math.sin(_pi)) * _scale,
(x * math.sin(_pi) + y * math.cos(_pi)) * _scale,
)
def _imul(x, y):
return (
(x * math.cos(_pi) + y * math.sin(_pi)) * _pitch,
(x * -math.sin(_pi) + y * math.cos(_pi)) * _pitch,
)
return _mul, _imul
def x_y_iter(w, h, sx, ex, sy, ey):
fw, fh = float(w), float(h)
for y in range(h + 1):
ty = y / fh
yy = (1. - ty) * sy + ty * ey
for x in range(w + 1):
tx = x / fw
xx = (1. - tx) * sx + tx * ex
yield xx, yy
def halftone(img, rot, pitch):
mat, imat = create_mat(rot, pitch)
w_half = img.size[0] // 2
h_half = img.size[1] // 2
pitch_2 = pitch / 2.
# バウンディングボックスを計算
bounding_rect = [
(-w_half - pitch_2, -h_half - pitch_2),
(-w_half - pitch_2, h_half + pitch_2),
]
x, y = zip(*[mat(x, y) for x, y in bounding_rect])
w, h = max(abs(t) for t in x), max(abs(t) for t in y)
# ガウシアンフィルターで平均化する
gmono = img.filter(ImageFilter.GaussianBlur(pitch / 2))
# スキャンを実行し、(x, y, color)の配列を生成する
dots = []
for x, y in x_y_iter(int(w * 2) + 1, int(h * 2) - 1, -w, w, -h + 1., h - 1.):
x, y = imat(x, y)
x += w_half
y += h_half
if -pitch_2 < x < img.size[0] + pitch_2 and -pitch_2 < y < img.size[1] + pitch_2:
color = gmono.getpixel((
min(max(x, 0), img.size[0]-1),
min(max(y, 0), img.size[1]-1)
))
t = pitch_2 * (1.0 - (color / 255))
dots.append((x, y, color))
return dots
def halftone_cmyk(img, pitch, dot_radius, scale=1., rots=(15, 75, 35, 45)):
c, m, y, k = cmyk.split()
cdots = halftone(c, rots[0], pitch)
mdots = halftone(m, rots[1], pitch)
ydots = halftone(y, rots[2], pitch)
kdots = halftone(k, rots[3], pitch)
nc = dot_image(img.size, cdots, dot_radius, scale=scale)
nm = dot_image(img.size, mdots, dot_radius, scale=scale)
ny = dot_image(img.size, ydots, dot_radius, scale=scale)
nk = dot_image(img.size, kdots, dot_radius, scale=scale)
return Image.merge("CMYK", [nc, nm, ny, nk]).convert("RGB")
def dot_image(size, dots, dot_radius, base_color=0, dot_color=0xFF, scale=1.0):
img = Image.new("L", tuple(int(x * scale) for x in size), base_color)
draw = ImageDraw.Draw(img)
for x, y, color in dots:
t = dot_radius * (color / 255) * scale
x *= scale
y *= scale
draw.ellipse((x - t, y - t, x + t, y + t), dot_color)
return img
def dot_image_by_cairo(size, dots, dot_radius, base_color=(0, 0, 0), dot_color=(1., 1., 1.), scale=1.0):
import cairo
w, h = tuple(int(x * scale) for x in img.size)
surface = cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
ctx = cairo.Context(surface)
ctx.set_source_rgb(*base_color)
ctx.rectangle(0, 0, w, h)
ctx.fill()
for x, y, color in dots:
fcolor = color / 255.
t = dot_radius * fcolor * scale
ctx.set_source_rgb(*dot_color)
ctx.arc(x, y, t, 0, 2 * math.pi)
ctx.fill()
return Image.frombuffer("RGBA", img.size, surface.get_data(), "raw", "RGBA", 0, 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment