OLD | NEW |
(Empty) | |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. |
| 4 import bisect |
| 5 import math |
| 6 |
| 7 def Clamp(value, low=0.0, high=1.0): |
| 8 return min(max(value, low), high) |
| 9 |
| 10 def NormalizeSamples(samples): |
| 11 ''' Sort the N samples, and map them linearly to the range [0,1] such that the |
| 12 first sample is 0.5/N and the last sample is (N-0.5)/N. Background: the |
| 13 discrepancy of the sample set i/(N-1); i=0,...,N-1 is 2/N, twice the |
| 14 discrepancy of the sample set (i+1/2)/N; i=0,...,N-1. In our case we |
| 15 don't want to distinguish between these two cases, as our original domain |
| 16 is not bounded (it is for Monte Carlo integration, where discrepancy was |
| 17 first used). |
| 18 ''' |
| 19 samples = sorted(samples) |
| 20 low = min(samples) |
| 21 high = max(samples) |
| 22 new_low = 0.5 / len(samples) |
| 23 new_high = (len(samples)-0.5) / len(samples) |
| 24 scale = (new_high - new_low) / (high - low) |
| 25 for i in xrange(0, len(samples)): |
| 26 samples[i] = float(samples[i] - low) * scale + new_low |
| 27 return samples, scale |
| 28 |
| 29 def Discrepancy(samples, interval_multiplier = 10000): |
| 30 ''' Compute the discrepancy of a set of 1D samples from the unit interval |
| 31 [0,1]. The samples must be sorted. |
| 32 |
| 33 http://en.wikipedia.org/wiki/Low-discrepancy_sequence |
| 34 http://mathworld.wolfram.com/Discrepancy.html |
| 35 ''' |
| 36 if (len(samples) < 3): |
| 37 return 0 |
| 38 |
| 39 max_local_discrepancy = 0 |
| 40 locations = [] |
| 41 # For each location, stores the number of samples less than that location. |
| 42 left = [] |
| 43 # For each location, stores the number of samples less than or equal to that |
| 44 # location. |
| 45 right = [] |
| 46 |
| 47 interval_count = len(samples) * interval_multiplier |
| 48 # Compute number of locations the will roughly result in the requested number |
| 49 # of intervals. |
| 50 location_count = int(math.ceil(math.sqrt(interval_count*2))) |
| 51 inv_sample_count = 1.0 / len(samples) |
| 52 |
| 53 # Generate list of equally spaced locations. |
| 54 for i in xrange(0, location_count): |
| 55 location = float(i) / (location_count-1) |
| 56 locations.append(location) |
| 57 left.append(bisect.bisect_left(samples, location)) |
| 58 right.append(bisect.bisect_right(samples, location)) |
| 59 |
| 60 # Iterate over the intervals defined by any pair of locations. |
| 61 for i in xrange(0, len(locations)): |
| 62 for j in xrange(i, len(locations)): |
| 63 # Compute length of interval and number of samples in the interval. |
| 64 length = locations[j] - locations[i] |
| 65 count = right[j] - left[i] |
| 66 |
| 67 # Compute local discrepancy and update max_local_discrepancy. |
| 68 local_discrepancy = abs(float(count)*inv_sample_count - length) |
| 69 max_local_discrepancy = max(local_discrepancy, max_local_discrepancy) |
| 70 |
| 71 return max_local_discrepancy |
| 72 |
| 73 def FrameDiscrepancy(frame_timestamps, absolute = True, |
| 74 interval_multiplier = 10000): |
| 75 ''' A discrepancy based metric for measuring jank. |
| 76 |
| 77 FrameDiscrepancy quantifies the largest area of jank observed in a series |
| 78 of timestamps. Note that this is different form metrics based on the |
| 79 max_frame_time. For example, the time stamp series A = [0,1,2,3,5,6] and |
| 80 B = [0,1,2,3,5,7] have the same max_frame_time = 2, but |
| 81 Discrepancy(B) > Discrepancy(A). |
| 82 |
| 83 Two variants of discrepancy can be computed: |
| 84 |
| 85 Relative discrepancy is following the original definition of |
| 86 discrepancy. It characterized the largest area of jank, relative to the |
| 87 duration of the entire time stamp series. We normalize the raw results, |
| 88 because the best case discrepancy for a set of N samples is 1/N (for |
| 89 equally spaced samples), and we want our metric to report 0.0 in that |
| 90 case. |
| 91 |
| 92 Absolute discrepancy also characterizes the largest area of jank, but its |
| 93 value wouldn't change (except for imprecisions due to a low |
| 94 interval_multiplier) if additional 'good' frames were added to an |
| 95 exisiting list of time stamps. Its range is [0,inf] and the unit is |
| 96 milliseconds. |
| 97 |
| 98 The time stamp series C = [0,2,3,4] and D = [0,2,3,4,5] have the same |
| 99 absolute discrepancy, but D has lower relative discrepancy than C. |
| 100 ''' |
| 101 samples, sample_scale = NormalizeSamples(frame_timestamps) |
| 102 discrepancy = Discrepancy(samples, interval_multiplier) |
| 103 inv_sample_count = 1.0 / len(samples) |
| 104 if absolute: |
| 105 # Compute absolute discrepancy |
| 106 discrepancy /= sample_scale |
| 107 else: |
| 108 # Compute relative discrepancy |
| 109 discrepancy = Clamp((discrepancy-inv_sample_count) / (1.0-inv_sample_count)) |
| 110 return discrepancy |
OLD | NEW |