14
14
from distutils import version
15
15
import hashlib
16
16
import math
17
- import operator
18
17
import os
19
18
import numpy as np
20
19
import shutil
@@ -251,37 +250,21 @@ def crop_to_same(actual_path, actual_image, expected_path, expected_image):
251
250
return actual_image , expected_image
252
251
253
252
def calculate_rms (expectedImage , actualImage ):
254
- # compare the resulting image histogram functions
255
- expected_version = version . LooseVersion ( "1.6" )
256
- found_version = version . LooseVersion ( np . __version__ )
253
+ # calculate the per-pixel errors, then compute the root mean square error
254
+ num_values = np . prod ( expectedImage . shape )
255
+ abs_diff_image = abs ( expectedImage - actualImage )
257
256
258
257
# On Numpy 1.6, we can use bincount with minlength, which is much faster than
259
258
# using histogram
259
+ expected_version = version .LooseVersion ("1.6" )
260
+ found_version = version .LooseVersion (np .__version__ )
260
261
if found_version >= expected_version :
261
- rms = 0
262
-
263
- for i in xrange (0 , 3 ):
264
- h1p = expectedImage [:,:,i ]
265
- h2p = actualImage [:,:,i ]
266
-
267
- h1h = np .bincount (h1p .ravel (), minlength = 256 )
268
- h2h = np .bincount (h2p .ravel (), minlength = 256 )
269
-
270
- rms += np .sum (np .power ((h1h - h2h ), 2 ))
262
+ histogram = np .bincount (abs_diff_image .ravel (), minlength = 256 )
271
263
else :
272
- rms = 0
273
- bins = np .arange (257 )
274
-
275
- for i in xrange (0 , 3 ):
276
- h1p = expectedImage [:,:,i ]
277
- h2p = actualImage [:,:,i ]
264
+ histogram = np .histogram (abs_diff_image , bins = np .arange (257 ))[0 ]
278
265
279
- h1h = np .histogram (h1p , bins = bins )[0 ]
280
- h2h = np .histogram (h2p , bins = bins )[0 ]
281
-
282
- rms += np .sum (np .power ((h1h - h2h ), 2 ))
283
-
284
- rms = np .sqrt (rms / (256 * 3 ))
266
+ sum_of_squares = np .sum (histogram * np .arange (len (histogram ))** 2 )
267
+ rms = np .sqrt (float (sum_of_squares ) / num_values )
285
268
286
269
return rms
287
270
@@ -299,8 +282,9 @@ def compare_images( expected, actual, tol, in_decorator=False ):
299
282
= INPUT VARIABLES
300
283
- expected The filename of the expected image.
301
284
- actual The filename of the actual image.
302
- - tol The tolerance (a unitless float). This is used to
303
- determine the 'fuzziness' to use when comparing images.
285
+ - tol The tolerance (a color value difference, where 255 is the
286
+ maximal difference). The test fails if the average pixel
287
+ difference is greater than this value.
304
288
- in_decorator If called from image_comparison decorator, this should be
305
289
True. (default=False)
306
290
'''
@@ -316,37 +300,25 @@ def compare_images( expected, actual, tol, in_decorator=False ):
316
300
# open the image files and remove the alpha channel (if it exists)
317
301
expectedImage = _png .read_png_int ( expected )
318
302
actualImage = _png .read_png_int ( actual )
303
+ expectedImage = expectedImage [:, :, :3 ]
304
+ actualImage = actualImage [:, :, :3 ]
319
305
320
306
actualImage , expectedImage = crop_to_same (actual , actualImage , expected , expectedImage )
321
307
322
- # compare the resulting image histogram functions
323
- expected_version = version .LooseVersion ("1.6" )
324
- found_version = version .LooseVersion (np .__version__ )
308
+ # convert to signed integers, so that the images can be subtracted without
309
+ # overflow
310
+ expectedImage = expectedImage .astype (np .int16 )
311
+ actualImage = actualImage .astype (np .int16 )
325
312
326
313
rms = calculate_rms (expectedImage , actualImage )
327
314
328
315
diff_image = make_test_filename (actual , 'failed-diff' )
329
316
330
- if ( ( rms / 10000.0 ) <= tol ) :
317
+ if rms <= tol :
331
318
if os .path .exists (diff_image ):
332
319
os .unlink (diff_image )
333
320
return None
334
321
335
- # For Agg-rendered images, we can retry by ignoring pixels with
336
- # differences of only 1
337
- if extension == 'png' :
338
- # Remove differences of only 1
339
- diffImage = np .abs (np .asarray (actualImage , dtype = np .int ) -
340
- np .asarray (expectedImage , dtype = np .int ))
341
- actualImage = np .where (diffImage <= 1 , expectedImage , actualImage )
342
-
343
- rms = calculate_rms (expectedImage , actualImage )
344
-
345
- if ( (rms / 10000.0 ) <= tol ):
346
- if os .path .exists (diff_image ):
347
- os .unlink (diff_image )
348
- return None
349
-
350
322
save_diff_image ( expected , actual , diff_image )
351
323
352
324
if in_decorator :
@@ -360,7 +332,7 @@ def compare_images( expected, actual, tol, in_decorator=False ):
360
332
else :
361
333
# old-style call from mplTest directory
362
334
msg = " Error: Image files did not match.\n " \
363
- " RMS Value: " + str ( rms / 10000.0 ) + "\n " \
335
+ " RMS Value: " + str ( rms ) + "\n " \
364
336
" Expected:\n " + str ( expected ) + "\n " \
365
337
" Actual:\n " + str ( actual ) + "\n " \
366
338
" Difference:\n " + str ( diff_image ) + "\n " \
0 commit comments