Mercurial > ift6266
comparison data_generation/transformations/pycaptcha/Captcha/Visual/Distortions.py @ 167:1f5937e9e530
More moves - transformations into data_generation, added "deep" folder
author | Dumitru Erhan <dumitru.erhan@gmail.com> |
---|---|
date | Fri, 26 Feb 2010 14:15:38 -0500 |
parents | pycaptcha/Captcha/Visual/Distortions.py@4775b4195b4b |
children |
comparison
equal
deleted
inserted
replaced
166:17ae5a1a4dd1 | 167:1f5937e9e530 |
---|---|
1 """ Captcha.Visual.Distortions | |
2 | |
3 Distortion layers for visual CAPTCHAs | |
4 """ | |
5 # | |
6 # PyCAPTCHA Package | |
7 # Copyright (C) 2004 Micah Dowty <micah@navi.cx> | |
8 # | |
9 | |
10 from Captcha.Visual import Layer | |
11 import ImageDraw, Image | |
12 import random, math | |
13 | |
14 | |
15 class WigglyBlocks(Layer): | |
16 """Randomly select and shift blocks of the image""" | |
17 def __init__(self, blockSize=3, sigma=0.01, iterations=300): | |
18 self.blockSize = blockSize | |
19 self.sigma = sigma | |
20 self.iterations = iterations | |
21 self.seed = random.random() | |
22 | |
23 def render(self, image): | |
24 r = random.Random(self.seed) | |
25 for i in xrange(self.iterations): | |
26 # Select a block | |
27 bx = int(r.uniform(0, image.size[0]-self.blockSize)) | |
28 by = int(r.uniform(0, image.size[1]-self.blockSize)) | |
29 block = image.crop((bx, by, bx+self.blockSize-1, by+self.blockSize-1)) | |
30 | |
31 # Figure out how much to move it. | |
32 # The call to floor() is important so we always round toward | |
33 # 0 rather than to -inf. Just int() would bias the block motion. | |
34 mx = int(math.floor(r.normalvariate(0, self.sigma))) | |
35 my = int(math.floor(r.normalvariate(0, self.sigma))) | |
36 | |
37 # Now actually move the block | |
38 image.paste(block, (bx+mx, by+my)) | |
39 | |
40 | |
41 class WarpBase(Layer): | |
42 """Abstract base class for image warping. Subclasses define a | |
43 function that maps points in the output image to points in the input image. | |
44 This warping engine runs a grid of points through this transform and uses | |
45 PIL's mesh transform to warp the image. | |
46 """ | |
47 filtering = Image.BILINEAR | |
48 resolution = 10 | |
49 | |
50 def getTransform(self, image): | |
51 """Return a transformation function, subclasses should override this""" | |
52 return lambda x, y: (x, y) | |
53 | |
54 def render(self, image): | |
55 r = self.resolution | |
56 xPoints = image.size[0] / r + 2 | |
57 yPoints = image.size[1] / r + 2 | |
58 f = self.getTransform(image) | |
59 | |
60 # Create a list of arrays with transformed points | |
61 xRows = [] | |
62 yRows = [] | |
63 for j in xrange(yPoints): | |
64 xRow = [] | |
65 yRow = [] | |
66 for i in xrange(xPoints): | |
67 x, y = f(i*r, j*r) | |
68 | |
69 # Clamp the edges so we don't get black undefined areas | |
70 x = max(0, min(image.size[0]-1, x)) | |
71 y = max(0, min(image.size[1]-1, y)) | |
72 | |
73 xRow.append(x) | |
74 yRow.append(y) | |
75 xRows.append(xRow) | |
76 yRows.append(yRow) | |
77 | |
78 # Create the mesh list, with a transformation for | |
79 # each square between points on the grid | |
80 mesh = [] | |
81 for j in xrange(yPoints-1): | |
82 for i in xrange(xPoints-1): | |
83 mesh.append(( | |
84 # Destination rectangle | |
85 (i*r, j*r, | |
86 (i+1)*r, (j+1)*r), | |
87 # Source quadrilateral | |
88 (xRows[j ][i ], yRows[j ][i ], | |
89 xRows[j+1][i ], yRows[j+1][i ], | |
90 xRows[j+1][i+1], yRows[j+1][i+1], | |
91 xRows[j ][i+1], yRows[j ][i+1]), | |
92 )) | |
93 | |
94 return image.transform(image.size, Image.MESH, mesh, self.filtering) | |
95 | |
96 | |
97 class SineWarp(WarpBase): | |
98 """Warp the image using a random composition of sine waves""" | |
99 | |
100 def __init__(self, | |
101 amplitudeRange = (3, 6.5), | |
102 periodRange = (0.04, 0.1), | |
103 ): | |
104 self.amplitude = random.uniform(*amplitudeRange) | |
105 self.period = random.uniform(*periodRange) | |
106 self.offset = (random.uniform(0, math.pi * 2 / self.period), | |
107 random.uniform(0, math.pi * 2 / self.period)) | |
108 | |
109 def getTransform(self, image): | |
110 return (lambda x, y, | |
111 a = self.amplitude, | |
112 p = self.period, | |
113 o = self.offset: | |
114 (math.sin( (y+o[0])*p )*a + x, | |
115 math.sin( (x+o[1])*p )*a + y)) | |
116 | |
117 ### The End ### |