8
8
9
9
import matplotlib
10
10
from matplotlib .testing .noseclasses import ImageComparisonFailure
11
- from matplotlib .testing import image_util
11
+ from matplotlib .testing import image_util , util
12
12
from matplotlib import _png
13
+ from matplotlib import _get_configdir
14
+ from distutils import version
15
+ import hashlib
13
16
import math
14
17
import operator
15
18
import os
28
31
]
29
32
30
33
#-----------------------------------------------------------------------
34
+
35
+ def make_test_filename (fname , purpose ):
36
+ """
37
+ Make a new filename by inserting `purpose` before the file's
38
+ extension.
39
+ """
40
+ base , ext = os .path .splitext (fname )
41
+ return '%s-%s%s' % (base , purpose , ext )
42
+
31
43
def compare_float ( expected , actual , relTol = None , absTol = None ):
32
44
"""Fail if the floating point values are not close enough, with
33
45
the givem message.
@@ -87,35 +99,68 @@ def compare_float( expected, actual, relTol = None, absTol = None ):
87
99
# A dictionary that maps filename extensions to functions that map
88
100
# parameters old and new to a list that can be passed to Popen to
89
101
# convert files with that extension to png format.
102
+ def get_cache_dir ():
103
+ cache_dir = os .path .join (_get_configdir (), 'test_cache' )
104
+ if not os .path .exists (cache_dir ):
105
+ try :
106
+ os .makedirs (cache_dir )
107
+ except IOError :
108
+ return None
109
+ if not os .access (cache_dir , os .W_OK ):
110
+ return None
111
+ return cache_dir
112
+
113
+ def get_file_hash (path , block_size = 2 ** 20 ):
114
+ md5 = hashlib .md5 ()
115
+ with open (path , 'rb' ) as fd :
116
+ while True :
117
+ data = fd .read (block_size )
118
+ if not data :
119
+ break
120
+ md5 .update (data )
121
+ return md5 .hexdigest ()
122
+
90
123
converter = { }
91
124
92
125
def make_external_conversion_command (cmd ):
93
- def convert (* args ):
94
- cmdline = cmd (* args )
95
- oldname , newname = args
126
+ def convert (old , new ):
127
+ cmdline = cmd (old , new )
96
128
pipe = subprocess .Popen (cmdline , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
97
129
stdout , stderr = pipe .communicate ()
98
130
errcode = pipe .wait ()
99
- if not os .path .exists (newname ) or errcode :
131
+ if not os .path .exists (new ) or errcode :
100
132
msg = "Conversion command failed:\n %s\n " % ' ' .join (cmdline )
101
133
if stdout :
102
134
msg += "Standard output:\n %s\n " % stdout
103
135
if stderr :
104
136
msg += "Standard error:\n %s\n " % stderr
105
137
raise IOError (msg )
138
+
106
139
return convert
107
140
108
141
if matplotlib .checkdep_ghostscript () is not None :
109
- # FIXME: make checkdep_ghostscript return the command
110
- if sys .platform == 'win32' :
111
- gs = 'gswin32c'
112
- else :
113
- gs = 'gs'
114
- cmd = lambda old , new : \
115
- [gs , '-q' , '-sDEVICE=png16m' , '-dNOPAUSE' , '-dBATCH' ,
116
- '-sOutputFile=' + new , old ]
117
- converter ['pdf' ] = make_external_conversion_command (cmd )
118
- converter ['eps' ] = make_external_conversion_command (cmd )
142
+ def make_ghostscript_conversion_command ():
143
+ # FIXME: make checkdep_ghostscript return the command
144
+ if sys .platform == 'win32' :
145
+ gs = 'gswin32c'
146
+ else :
147
+ gs = 'gs'
148
+ cmd = [gs , '-q' , '-sDEVICE=png16m' , '-sOutputFile=-' ]
149
+
150
+ process = util .MiniExpect (cmd )
151
+
152
+ def do_convert (old , new ):
153
+ process .expect ("GS>" )
154
+ process .sendline ("(%s) run" % old )
155
+ with open (new , 'wb' ) as fd :
156
+ process .expect (">>showpage, press <return> to continue<<" , fd )
157
+ process .sendline ('' )
158
+
159
+ return do_convert
160
+
161
+ converter ['pdf' ] = make_ghostscript_conversion_command ()
162
+ converter ['eps' ] = make_ghostscript_conversion_command ()
163
+
119
164
120
165
if matplotlib .checkdep_inkscape () is not None :
121
166
cmd = lambda old , new : \
@@ -127,7 +172,7 @@ def comparable_formats():
127
172
on this system.'''
128
173
return ['png' ] + converter .keys ()
129
174
130
- def convert (filename ):
175
+ def convert (filename , cache ):
131
176
'''
132
177
Convert the named file into a png file.
133
178
Returns the name of the created file.
@@ -138,11 +183,29 @@ def convert(filename):
138
183
newname = base + '_' + extension + '.png'
139
184
if not os .path .exists (filename ):
140
185
raise IOError ("'%s' does not exist" % filename )
186
+
141
187
# Only convert the file if the destination doesn't already exist or
142
188
# is out of date.
143
189
if (not os .path .exists (newname ) or
144
190
os .stat (newname ).st_mtime < os .stat (filename ).st_mtime ):
191
+ if cache :
192
+ cache_dir = get_cache_dir ()
193
+ else :
194
+ cache_dir = None
195
+
196
+ if cache_dir is not None :
197
+ hash = get_file_hash (filename )
198
+ new_ext = os .path .splitext (newname )[1 ]
199
+ cached_file = os .path .join (cache_dir , hash + new_ext )
200
+ if os .path .exists (cached_file ):
201
+ shutil .copyfile (cached_file , newname )
202
+ return newname
203
+
145
204
converter [extension ](filename , newname )
205
+
206
+ if cache_dir is not None :
207
+ shutil .copyfile (newname , cached_file )
208
+
146
209
return newname
147
210
148
211
verifiers = { }
@@ -206,8 +269,8 @@ def compare_images( expected, actual, tol, in_decorator=False ):
206
269
# Convert the image to png
207
270
extension = expected .split ('.' )[- 1 ]
208
271
if extension != 'png' :
209
- actual = convert (actual )
210
- expected = convert (expected )
272
+ actual = convert (actual , False )
273
+ expected = convert (expected , True )
211
274
212
275
# open the image files and remove the alpha channel (if it exists)
213
276
expectedImage = _png .read_png_int ( expected )
@@ -216,24 +279,42 @@ def compare_images( expected, actual, tol, in_decorator=False ):
216
279
actualImage , expectedImage = crop_to_same (actual , actualImage , expected , expectedImage )
217
280
218
281
# normalize the images
219
- expectedImage = image_util .autocontrast ( expectedImage , 2 )
220
- actualImage = image_util .autocontrast ( actualImage , 2 )
282
+ # expectedImage = image_util.autocontrast( expectedImage, 2 )
283
+ # actualImage = image_util.autocontrast( actualImage, 2 )
221
284
222
285
# compare the resulting image histogram functions
223
- rms = 0
224
- bins = np .arange (257 )
225
- for i in xrange (0 , 3 ):
226
- h1p = expectedImage [:,:,i ]
227
- h2p = actualImage [:,:,i ]
286
+ expected_version = version .LooseVersion ("1.6" )
287
+ found_version = version .LooseVersion (np .__version__ )
288
+
289
+ # On Numpy 1.6, we can use bincount with minlength, which is much faster than
290
+ # using histogram
291
+ if found_version >= expected_version :
292
+ rms = 0
293
+
294
+ for i in xrange (0 , 3 ):
295
+ h1p = expectedImage [:,:,i ]
296
+ h2p = actualImage [:,:,i ]
297
+
298
+ h1h = np .bincount (h1p .ravel (), minlength = 256 )
299
+ h2h = np .bincount (h2p .ravel (), minlength = 256 )
300
+
301
+ rms += np .sum (np .power ((h1h - h2h ), 2 ))
302
+ else :
303
+ rms = 0
304
+ ns = np .arange (257 )
305
+
306
+ for i in xrange (0 , 3 ):
307
+ h1p = expectedImage [:,:,i ]
308
+ h2p = actualImage [:,:,i ]
309
+
310
+ h1h = np .histogram (h1p , bins = bins )[0 ]
311
+ h2h = np .histogram (h2p , bins = bins )[0 ]
228
312
229
- h1h = np .histogram (h1p , bins = bins )[0 ]
230
- h2h = np .histogram (h2p , bins = bins )[0 ]
313
+ rms += np .sum (np .power ((h1h - h2h ), 2 ))
231
314
232
- rms += np .sum (np .power ((h1h - h2h ), 2 ))
233
315
rms = np .sqrt (rms / (256 * 3 ))
234
316
235
- diff_image = os .path .join (os .path .dirname (actual ),
236
- 'failed-diff-' + os .path .basename (actual ))
317
+ diff_image = make_test_filename (actual , 'failed-diff' )
237
318
238
319
if ( (rms / 10000.0 ) <= tol ):
239
320
if os .path .exists (diff_image ):
0 commit comments