Skip to content
Newer
Older
100755 124 lines (92 sloc) 3.44 KB
7cf5a49 @mikeharris100 A first attempt
authored Nov 15, 2011
1 #!/usr/bin/env python
2
3 from PIL import Image
4 import numpy as np
5 from itertools import permutations, chain
6 import os
7 import sys
8
9
10 def unshred(filename):
11 pilimg = Image.open(filename)
12 img = np.asarray(pilimg)
13
14 shredwidth = find_shred_width(img)
15 num_shreds = img.shape[1] / shredwidth
16
17 # column indexes of leftmost pixels of each shred
18 leftinds = range(0, img.shape[1], shredwidth)
19
20 # and the rightmost...
21 rightinds = range(shredwidth - 1, img.shape[1], shredwidth)
22
23 diffs = []
24
25 # compare each shred with all possible neighbours
26 for (left, right) in permutations(range(num_shreds), 2):
27 lpix = img[:, rightinds[left], :].flatten(1)
28 rpix = img[:, leftinds[right], :].flatten(1)
29 # calculate a distance metric, Euclid will do for now
30 diff = 10 * (sum((lpix - rpix) ** 2) ** 0.5)
31 diffs.append((left, right, diff))
32
33 pairs = []
34 # loop through all shreds
35 for shred in xrange(num_shreds):
36 # find most likely neighbours
37 pair = sorted(diffs, key=lambda x: x[2])[0]
38 # remove those neighbours (on the same side) from future comparisons
39 diffs = filter(lambda x: x[0] != pair[0] and x[1] != pair[1], diffs)
40 pairs.append(pair)
41
42 # initialise shreds with the least likely neighbours
43 # (will hopefully be the edges)
44 shreds = [sorted(pairs, key=lambda x:x[2])[-1][1]]
45 # walk through pairs to generate l-r shreds
46 for shred in xrange(1, num_shreds):
47 shreds.append(filter(lambda x: x[0] == shreds[-1], pairs)[0][1])
48
49 # convert shred indexes to column indexes
50 cols = list(chain(*[range(shredwidth * shred, shredwidth * (shred + 1))
51 for shred in shreds]))
52
53 # arrange image
54 joined = img[:, cols, :]
55
56 # write out
57 output = Image.fromarray(joined)
58 fname, fext = os.path.splitext(filename)
59 unshreddedfile = fname + "_UNSHREDDED" + fext
60 output.save(unshreddedfile)
61 print("Unshreddified %s" % unshreddedfile)
62
63
64 def find_shred_width(img):
65 h, w, c = img.shape
66
67 stacked = img.transpose((2, 0, 1)).reshape(h * c, w)
68
69 diffs = np.sum((stacked[:, 1:] - stacked[:, :-1]) ** 2, axis=0) ** 0.5
70
71 peaks = diffs[1:] - diffs[:-1] # first derivative
72 peaks2 = peaks[:-1] - peaks[1:] # second
73
74 # we care about onset
75 peaks2[peaks2 < 0] = 0
76
77 # normalise
78 peaks2 = peaks2 / peaks2.max()
79
80 # pick some threshold
81 peakpositions = np.nonzero(peaks2 > 0.5)[0]
82
83 # correct positions
84 peakpositions = peakpositions + 2
85
86 # explicitly add image width to search criteria
87 peakpositions = np.append(peakpositions, w)
88
89 # hopefully shred_width = gcd of peaks
90 return _noisygcd(peakpositions)
91
92
93 def _noisygcd(nums):
94 """
95 Find the best fitting GCD for noisy data
96 """
97 candidates = []
98 for i in range(nums.size):
99 candidate = float(nums[i])
100 goodness = 999 # initialise to some high value
101 n = 0
102 while goodness > 0.1:
103 n += 1
104 n_candidate = candidate / n
105 fit = np.concatenate((n_candidate / nums[nums < n_candidate],
106 nums[nums > n_candidate] / n_candidate))
107 goodness = (fit - fit.round()).std()
108
109 candidates.append(n_candidate)
110 return int(np.mean(candidates).round())
111
112
113 def main():
114 if len(sys.argv) != 2:
115 print("Yo dawg, Ima need a file to process...")
116 sys.exit(1)
117
118 unshred(sys.argv[1])
119 return 0
120
121 if __name__ == "__main__":
122 status = main()
123 sys.exit(status)
Something went wrong with that request. Please try again.