thearn commited on
Commit
962923b
·
0 Parent(s):

first commit

Browse files
Files changed (5) hide show
  1. .gitattributes +2 -0
  2. .gitignore +52 -0
  3. LICENSE.txt +11 -0
  4. README.md +154 -0
  5. magic_eye_solver.py +111 -0
.gitattributes ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ examples/*.jpg filter=lfs diff=lfs merge=lfs -text
2
+ examples/*.gif filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ # IDE #
3
+ #######
4
+ *.wpr
5
+ *.wpu
6
+
7
+ # Directories #
8
+ ###############
9
+ build/
10
+ dist/
11
+ *.egg-info
12
+
13
+ # Compiled source #
14
+ ###################
15
+ *.com
16
+ *.class
17
+ *.dll
18
+ *.exe
19
+ *.o
20
+ *.so
21
+ *.pyc
22
+
23
+ # Packages #
24
+ ############
25
+ # it's better to unpack these files and commit the raw source
26
+ # git has its own built in compression methods
27
+ *.7z
28
+ *.dmg
29
+ *.gz
30
+ *.iso
31
+ *.jar
32
+ *.rar
33
+ *.tar
34
+ *.zip
35
+ *.egg
36
+
37
+ # Logs and databases #
38
+ ######################
39
+ *.log
40
+ *.sql
41
+ *.sqlite
42
+
43
+ # OS generated files #
44
+ ######################
45
+ .DS_Store
46
+ .DS_Store?
47
+ ._*
48
+ .Spotlight-V100
49
+ .Trashes
50
+ Icon?
51
+ ehthumbs.db
52
+ Thumbs.db
LICENSE.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Licensed under the Apache License, Version 2.0 (the "License");
2
+ you may not use this file except in compliance with the License.
3
+ You may obtain a copy of the License at
4
+
5
+ http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
README.md ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ![Alt text](http://i.imgur.com/AUmpOSr.png "Example" )
2
+
3
+ # magiceye-solver
4
+ Are you as frustrated by not being able to see those magic eye optical illusions as I am? Well, now you're in luck.
5
+
6
+ This is a short python code that demonstrates how to automatically "solve" a magic eye autostereogram by estimating a
7
+ projection of the underlying image. This provides a decent contour outline of the hidden object, though most finer detail
8
+ tends to be lost.
9
+
10
+ Requirements:
11
+ ---------------
12
+
13
+ - Python 2.7+
14
+ - Numpy 1.5+
15
+ - Scipy 0.12+
16
+
17
+ Optional:
18
+
19
+ - scikit-image 0.8+ (code will attempt to import filtering functions for additional post processing, but will not raise an error if
20
+ library is not available)
21
+
22
+ Example usages:
23
+ ----------
24
+ This code can be used in three different ways.
25
+ ### Run directly, with a command line argument:
26
+
27
+ Run `magic_eye_solver.py` with the filename of the image that you would like to
28
+ process passed as an argument:
29
+
30
+ ```bash
31
+ $ python magic_eye_solver.py "example_images/example1.png"
32
+ ```
33
+ This generates text output:
34
+ ```
35
+ Solving image example_images/example1.png...
36
+ Saving solution to example_images/example1_solution.png...
37
+ Saving joined to example_images/example1_joined.png...
38
+ ```
39
+
40
+ This will generate `example1_solution.png` and `example1_joined.png` in the same
41
+ directory as the original files, showing the computed solution and a side-by-side
42
+ comparison of the original image and the solution, respectively.
43
+
44
+ ### Run directly, without a command line argument:
45
+ If a filename is not specified, a list of png and jpg image files will be presented
46
+ to the user, who will then be prompted to select from the file names shown.
47
+
48
+ For example, we run the file directly, with no argument:
49
+ ```bash
50
+ $ python magic_eye_solver.py
51
+ ```
52
+ Select all example files (example1.png through example7.png) from a generated list (notice that banner.png is not
53
+ selected by the user):
54
+ ```
55
+ Please select from the following images:
56
+ ========================================
57
+ (selection filename)
58
+ 0 example_images/banner.png
59
+ 1 example_images/example1.png
60
+ 2 example_images/example2.png
61
+ 3 example_images/example3.png
62
+ 4 example_images/example4.png
63
+ 5 example_images/example5.png
64
+ 6 example_images/example6.png
65
+ 7 example_images/example7.png
66
+
67
+ Make selections (separate by commas): 1,2,3,4,5,6,7
68
+
69
+ Solving image example_images/example1.png...
70
+ Saving solution to example_images/example1_solution.png...
71
+ Saving joined to example_images/example1_joined.png...
72
+
73
+ Solving image example_images/example2.png...
74
+ Saving solution to example_images/example2_solution.png...
75
+ Saving joined to example_images/example2_joined.png...
76
+
77
+ Solving image example_images/example3.png...
78
+ Saving solution to example_images/example3_solution.png...
79
+ Saving joined to example_images/example3_joined.png...
80
+
81
+ Solving image example_images/example4.png...
82
+ Saving solution to example_images/example4_solution.png...
83
+ Saving joined to example_images/example4_joined.png...
84
+
85
+ Solving image example_images/example5.png...
86
+ Saving solution to example_images/example5_solution.png...
87
+ Saving joined to example_images/example5_joined.png...
88
+
89
+ Solving image example_images/example6.png...
90
+ Saving solution to example_images/example6_solution.png...
91
+ Saving joined to example_images/example6_joined.png...
92
+
93
+ Solving image example_images/example7.png...
94
+ Saving solution to example_images/example7_solution.png...
95
+ Saving joined to example_images/example7_joined.png...
96
+ ```
97
+
98
+ ### Imported and used as a library:
99
+ The `solve_magiceye()` method can also be imported from `magic_eye_solver.py` and
100
+ used in your own application like any other image/array processing function:
101
+
102
+ ```python
103
+ from magic_eye_solver import solve_magiceye
104
+ import pylab #matplotlib plotting
105
+
106
+ image = pylab.imread("example_images/example1.png") #load magiceye image
107
+
108
+ solution = solve_magiceye(image) #solve it
109
+
110
+ pylab.imshow(solution, cmap = pylab.cm.gray) #plot the solution
111
+
112
+ pylab.show() #show the plot
113
+
114
+ ```
115
+
116
+ How it works:
117
+ -------------
118
+ - For each of the R, G, and B channels of a magic eye image:
119
+ 1. An autocorrelation is computed (via FFT) to find strong horizontal periodicities in the inputted image
120
+ 2. The sum of all horizontal translative shifts of the image up to the peak autocorrelation
121
+ shift is computed. That is, the entire image is "smeared" horizontally by the distance determined in step 1.
122
+ 3. An edge detection and uniform filter is applied to clean up the resulting sum and help separate the cumulated noise
123
+ from useful objective information.
124
+ - The most leptokurtotic (high in sample kurtosis) of three channels processed as above is returned as the solution
125
+ that is most likely to have the clearest 2D grayscale projection of the underlying image.
126
+
127
+ Notes / todo list:
128
+ ---------
129
+ - The post-process filtering should be improved to clean up the output a bit more. The solutions are kind of grainy.
130
+ - This certainly seems to work better for some autostereogram images than others, but still seems to give generally
131
+ useful output for the test images I've been able to collect so far.
132
+ - Good alternative solution methods are likely to exist, so there is still plenty of
133
+ experimentation left to do with this.
134
+ - I experimented with PCA and ICA (both as pre-processing the R, G, B channels and as post-processing of the results),
135
+ but this didn't improve the results very much.
136
+
137
+ Example results
138
+ ----------------
139
+ The following examples show the computed solutions of the magiceye images
140
+ found in the `example_images` directory.
141
+
142
+ ![Alt text](http://i.imgur.com/AUmpOSr.png "Solution 1")
143
+
144
+ ![Alt text](http://i.imgur.com/77qq4xY.jpg "Solution 2")
145
+
146
+ ![Alt text](http://i.imgur.com/WZVGvkX.jpg "Solution 3")
147
+
148
+ ![Alt text](http://i.imgur.com/3H9zeCJ.jpg "Solution 4")
149
+
150
+ ![Alt text](http://i.imgur.com/Xru4K0v.jpg "Solution 5")
151
+
152
+ ![Alt text](http://i.imgur.com/fAuwqXZ.jpg "Solution 6")
153
+
154
+ ![Alt text](http://i.imgur.com/WmVzQdv.jpg "Solution 7")
magic_eye_solver.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from numpy.fft import fft2, ifft2
3
+ from scipy.ndimage import filters
4
+ from scipy.stats import kurtosis
5
+ from scipy.misc import imread, imsave
6
+ try:
7
+ from skimage import filter as ski_filter
8
+ except:
9
+ ski_filter = None
10
+
11
+ def fft_2d_autocorr(x):
12
+ """ 2D autocorrelation, using FFT"""
13
+ fr = fft2(x)
14
+ fr2 = fft2(np.flipud(np.fliplr(x)))
15
+ m,n = fr.shape
16
+ cc = np.real(ifft2(fr*fr2))
17
+ cc = np.roll(cc, -m/2+1,axis=0)
18
+ cc = np.roll(cc, -n/2+1,axis=1)
19
+ return cc
20
+
21
+ def offset(mx):
22
+ ac = fft_2d_autocorr(mx)
23
+ ac=ac[len(ac)/2]
24
+ idx= np.where((ac-np.median(ac))/ac.std() > 3)[0]
25
+ diffs=[]
26
+ diffs = np.ediff1d(idx)
27
+ return np.max( diffs)
28
+
29
+ def shift_pic(mx):
30
+ gap = offset(mx)
31
+ m,n = mx.shape
32
+ mx2 = np.zeros((m,n))
33
+ for i in xrange(int(gap)):
34
+ mx2 += np.roll(mx,-i, axis = 1)
35
+ return mx2[:,:-gap]
36
+
37
+ def post_process(mx2):
38
+ mx2 = ski_filter.hprewitt(mx2)
39
+ return mx2
40
+
41
+ def solve_magiceye(x):
42
+ m,n,c = x.shape
43
+ k_ = -3
44
+ for i in xrange(3):
45
+ color = x[:,:,i]
46
+ mx2 = shift_pic(color)
47
+ mx2 = filters.prewitt(mx2)
48
+ mx2 = filters.uniform_filter(mx2, size = (5,5))
49
+ if ski_filter:
50
+ mx2 = post_process(mx2)
51
+ k = kurtosis(mx2.flatten())
52
+ if k > k_:
53
+ solution = mx2
54
+ k_ = k
55
+ return solution
56
+
57
+ if __name__ == "__main__":
58
+ """
59
+ Generates solutions either from from command line arguments
60
+ or by selection of suitable images generated from a list
61
+ """
62
+ import os
63
+ import sys
64
+ if len(sys.argv) < 2:
65
+ pngs = []
66
+ for root, dirs, files in os.walk(os.getcwd()):
67
+ dirname = root.split(os.path.sep)[-1]
68
+ for fn in files:
69
+ typ = fn.split('.')[-1]
70
+ if typ == 'png' or typ == "jpg":
71
+ if dirname != os.getcwd().split('/')[-1]:
72
+ pngs.append('/'.join([dirname,fn]))
73
+ else:
74
+ pngs.append(fn)
75
+ print
76
+ print "Please select from the following images:"
77
+ print "========================================"
78
+ print "(selection filename)"
79
+ i = 0
80
+ for fn in pngs:
81
+ print i, fn
82
+ i+=1
83
+ print
84
+ print "Make selections (separate by commas):",
85
+ select = raw_input()
86
+ fns = [pngs[int(sub.strip())] for sub in select.split(',')]
87
+ else:
88
+ fns = [sys.argv[1]]
89
+ print
90
+ for fn in fns:
91
+ x=imread(fn)
92
+ m,n,_ = x.shape
93
+ print "Solving image %s..." % fn
94
+ solution = solve_magiceye(x)
95
+ sfn = fn.split('.')[0]+'_solution.png'
96
+ print "Saving solution to %s..." % sfn
97
+ imsave(sfn,solution)
98
+
99
+ solution=imread(sfn)
100
+ n2=solution.shape[1]
101
+ joined = np.zeros((m,n+n2,3))
102
+ joined[:,:n,:] = x
103
+ joined[:,n:,0] = solution
104
+ joined[:,n:,1] = solution
105
+ joined[:,n:,2] = solution
106
+
107
+ sfn = fn.split('.')[0]+'_joined.png'
108
+ print "Saving joined to %s..." % sfn
109
+ imsave(sfn,joined)
110
+ print
111
+