1
2 """
3 MoinMoin - Daltonize ImageCorrection - Effect
4
5 Daltonize image correction algorithm implemented according to
6 http://scien.stanford.edu/class/psych221/projects/05/ofidaner/colorblindness_project.htm
7
8 Many thanks to Onur Fidaner, Poliang Lin and Nevran Ozguven for their work
9 on this topic and for releasing their complete research results to the public
10 (unlike the guys from http://www.vischeck.com/). This is of great help for a
11 lot of people!
12
13 Please note:
14 Daltonize ImageCorrection needs
15 * Python Image Library (PIL) from http://www.pythonware.com/products/pil/
16 * NumPy from http://numpy.scipy.org/
17
18 You can call Daltonize from the command-line with
19 "daltonize.py C:\image.png"
20
21 Explanations:
22 * Normally this module is called from Moin.AttachFile.get_file
23 * @param filename, fpath is the filename/fullpath to an image in the attachment
24 dir of a page.
25 * @param color_deficit can either be
26 - 'd' for Deuteranope image correction
27 - 'p' for Protanope image correction
28 - 't' for Tritanope image correct
29 Idea:
30 * Since daltonizing an image takes quite some time and we don't want visually
31 impaired users to wait so long until the page is loaded, this module has a
32 command-line option built-in which could be called as a separate process
33 after a file upload of a non visually impaired user in "AttachFile", e.g
34 "spawnlp(os.NO_WAIT...)"
35 * "AttachFile": If an image attachment is deleted or overwritten by a new version
36 please make sure to delete the daltonized images and redaltonize them.
37 * But all in all: Concrete implementation of ImageCorrection needs further
38 thinking and discussion. This is only a first prototype as proof of concept.
39
40 @copyright: 2007 by Oliver Siemoneit
41 @license: GNU GPL, see COPYING for details.
42 """
43
44 import os.path
45
46 def execute(filename, fpath, color_deficit='d'):
47 modified_filename = "%s-%s-%s" % ('daltonize', color_deficit, filename)
48 head, tail = os.path.split(fpath)
49
50
51 modified_fpath = os.path.join(head, modified_filename)
52
53
54 if os.path.isfile(modified_fpath):
55 return (modified_filename, modified_fpath)
56
57 helpers_available = True
58 try:
59 import numpy
60 from PIL import Image
61 except:
62 helpers_available = False
63 if not helpers_available:
64 return (filename, fpath)
65
66
67 im = Image.open(fpath)
68 if im.mode in ['1', 'L']:
69 return (filename, fpath)
70 im = im.copy()
71 im = im.convert('RGB')
72 RGB = numpy.asarray(im, dtype=float)
73
74
75 lms2lmsd = numpy.array([[1,0,0],[0.494207,0,1.24827],[0,0,1]])
76
77 lms2lmsp = numpy.array([[0,2.02344,-2.52581],[0,1,0],[0,0,1]])
78
79 lms2lmst = numpy.array([[1,0,0],[0,1,0],[-0.395913,0.801109,0]])
80
81 rgb2lms = numpy.array([[17.8824,43.5161,4.11935],[3.45565,27.1554,3.86714],[0.0299566,0.184309,1.46709]])
82 lms2rgb = numpy.linalg.inv(rgb2lms)
83
84 err2mod = numpy.array([[0,0,0],[0.7,1,0],[0.7,0,1]])
85
86
87 if color_deficit == 'd':
88 lms2lms_deficit = lms2lmsd
89 elif color_deficit == 'p':
90 lms2lms_deficit = lms2lmsp
91 elif color_deficit == 't':
92 lms2lms_deficit = lms2lmst
93 else:
94 return (filename, fpath)
95
96
97 LMS = numpy.zeros_like(RGB)
98 for i in range(RGB.shape[0]):
99 for j in range(RGB.shape[1]):
100 rgb = RGB[i,j,:3]
101 LMS[i,j,:3] = numpy.dot(rgb2lms, rgb)
102
103
104 _LMS = numpy.zeros_like(RGB)
105 for i in range(RGB.shape[0]):
106 for j in range(RGB.shape[1]):
107 lms = LMS[i,j,:3]
108 _LMS[i,j,:3] = numpy.dot(lms2lms_deficit, lms)
109
110 _RGB = numpy.zeros_like(RGB)
111 for i in range(RGB.shape[0]):
112 for j in range(RGB.shape[1]):
113 _lms = _LMS[i,j,:3]
114 _RGB[i,j,:3] = numpy.dot(lms2rgb, _lms)
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 error = (RGB-_RGB)
133
134
135 ERR = numpy.zeros_like(RGB)
136 for i in range(RGB.shape[0]):
137 for j in range(RGB.shape[1]):
138 err = error[i,j,:3]
139 ERR[i,j,:3] = numpy.dot(err2mod, err)
140
141 dtpn = ERR + RGB
142
143 for i in range(RGB.shape[0]):
144 for j in range(RGB.shape[1]):
145 dtpn[i,j,0] = max(0, dtpn[i,j,0])
146 dtpn[i,j,0] = min(255, dtpn[i,j,0])
147 dtpn[i,j,1] = max(0, dtpn[i,j,1])
148 dtpn[i,j,1] = min(255, dtpn[i,j,1])
149 dtpn[i,j,2] = max(0, dtpn[i,j,2])
150 dtpn[i,j,2] = min(255, dtpn[i,j,2])
151
152 result = dtpn.astype('uint8')
153
154
155 im_converted = Image.fromarray(result, mode='RGB')
156 im_converted.save(modified_fpath)
157 return (modified_filename, modified_fpath)
158
159
160 if __name__ == '__main__':
161 import sys
162 print "Daltonize image correction for color blind users"
163
164 if len(sys.argv) != 2:
165 print "Calling syntax: daltonize.py [fullpath to image file]"
166 print "Example: daltonize.py C:/wikiinstance/data/pages/PageName/attachments/pic.png"
167 sys.exit(1)
168
169 if not (os.path.isfile(sys.argv[1])):
170 print "Given file does not exist"
171 sys.exit(1)
172
173 extpos = sys.argv[1].rfind(".")
174 if not (extpos > 0 and sys.argv[1][extpos:].lower() in ['.gif', '.jpg', '.jpeg', '.png', '.bmp', '.ico', ]):
175 print "Given file is not an image"
176 sys.exit(1)
177
178 path, fname = os.path.split(sys.argv[1])
179 print "Please wait. Daltonizing in progress..."
180
181 colorblindness = { 'd': 'Deuteranope',
182 'p': 'Protanope',
183 't': 'Tritanope',}
184
185 for col_deficit in ['d', 'p', 't']:
186 print "Creating %s corrected version" % colorblindness[col_deficit]
187 modified_filename, modified_fpath = execute(fname, sys.argv[1], col_deficit)
188 if modified_fpath == sys.argv[1]:
189 print "Error while processing image: PIL/NumPy missing and/or source file is a grayscale image."
190 else:
191 print "Image successfully daltonized"
192
193