bungeman@google.com | 0abbff9 | 2013-07-27 20:37:56 +0000 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | |
| 3 | ''' |
| 4 | Copyright 2013 Google Inc. |
| 5 | |
| 6 | Use of this source code is governed by a BSD-style license that can be |
| 7 | found in the LICENSE file. |
| 8 | ''' |
| 9 | |
| 10 | import math |
| 11 | import pprint |
| 12 | |
| 13 | def withinStdDev(n): |
| 14 | """Returns the percent of samples within n std deviations of the normal.""" |
| 15 | return math.erf(n / math.sqrt(2)) |
| 16 | |
| 17 | def withinStdDevRange(a, b): |
| 18 | """Returns the percent of samples within the std deviation range a, b""" |
| 19 | if b < a: |
| 20 | return 0; |
| 21 | |
| 22 | if a < 0: |
| 23 | if b < 0: |
| 24 | return (withinStdDev(-a) - withinStdDev(-b)) / 2; |
| 25 | else: |
| 26 | return (withinStdDev(-a) + withinStdDev(b)) / 2; |
| 27 | else: |
| 28 | return (withinStdDev(b) - withinStdDev(a)) / 2; |
| 29 | |
| 30 | |
| 31 | #We have a bunch of smudged samples which represent the average coverage of a range. |
| 32 | #We have a 'center' which may not line up with those samples. |
| 33 | #From the 'center' we want to make a normal approximation where '5' sample width out we're at '3' std deviations. |
| 34 | #The first and last samples may not be fully covered. |
| 35 | |
| 36 | #This is the sub-sample shift for each set of FIR coefficients (the centers of the lcds in the samples) |
| 37 | #Each subpxl takes up 1/3 of a pixel, so they are centered at x=(i/n+1/2n), or 1/6, 3/6, 5/6 of a pixel. |
| 38 | #Each sample takes up 1/4 of a pixel, so the results fall at (x*4)%1, or 2/3, 0, 1/3 of a sample. |
| 39 | samples_per_pixel = 4 |
| 40 | subpxls_per_pixel = 3 |
| 41 | #sample_offsets is (frac, int) in sample units. |
| 42 | sample_offsets = [math.modf((float(subpxl_index)/subpxls_per_pixel + 1.0/(2.0*subpxls_per_pixel))*samples_per_pixel) for subpxl_index in range(subpxls_per_pixel)] |
| 43 | |
| 44 | #How many samples to consider to the left and right of the subpxl center. |
| 45 | sample_units_width = 5 |
| 46 | |
| 47 | #The std deviation at sample_units_width. |
| 48 | std_dev_max = 3 |
| 49 | |
| 50 | #The target sum is in some fixed point representation. |
| 51 | #Values larger the 1 in fixed point simulate ink spread. |
| 52 | target_sum = 0x110 |
| 53 | |
| 54 | for sample_offset, sample_align in sample_offsets: |
| 55 | coeffs = [] |
| 56 | coeffs_rounded = [] |
| 57 | |
| 58 | #We start at sample_offset - sample_units_width |
| 59 | current_sample_left = sample_offset - sample_units_width |
| 60 | current_std_dev_left = -std_dev_max |
| 61 | |
| 62 | done = False |
| 63 | while not done: |
| 64 | current_sample_right = math.floor(current_sample_left + 1) |
| 65 | if current_sample_right > sample_offset + sample_units_width: |
| 66 | done = True |
| 67 | current_sample_right = sample_offset + sample_units_width |
| 68 | current_std_dev_right = current_std_dev_left + ((current_sample_right - current_sample_left) / sample_units_width) * std_dev_max |
| 69 | |
| 70 | coverage = withinStdDevRange(current_std_dev_left, current_std_dev_right) |
| 71 | coeffs.append(coverage * target_sum) |
| 72 | coeffs_rounded.append(int(round(coverage * target_sum))) |
| 73 | |
| 74 | current_sample_left = current_sample_right |
| 75 | current_std_dev_left = current_std_dev_right |
| 76 | |
| 77 | # Now we have the numbers we want, but our rounding needs to add up to target_sum. |
| 78 | delta = 0 |
| 79 | coeffs_rounded_sum = sum(coeffs_rounded) |
| 80 | if coeffs_rounded_sum > target_sum: |
| 81 | # The coeffs add up to too much. Subtract 1 from the ones which were rounded up the most. |
| 82 | delta = -1 |
| 83 | |
| 84 | if coeffs_rounded_sum < target_sum: |
| 85 | # The coeffs add up to too little. Add 1 to the ones which were rounded down the most. |
| 86 | delta = 1 |
| 87 | |
| 88 | if delta: |
| 89 | print "Initial sum is 0x%0.2X, adjusting." % (coeffs_rounded_sum,) |
| 90 | coeff_diff = [(coeff_rounded - coeff) * delta |
| 91 | for coeff, coeff_rounded in zip(coeffs, coeffs_rounded)] |
| 92 | |
| 93 | class IndexTracker: |
| 94 | def __init__(self, index, item): |
| 95 | self.index = index |
| 96 | self.item = item |
| 97 | def __lt__(self, other): |
| 98 | return self.item < other.item |
| 99 | def __repr__(self): |
| 100 | return "arr[%d] == %s" % (self.index, repr(self.item)) |
| 101 | |
| 102 | coeff_pkg = [IndexTracker(i, diff) for i, diff in enumerate(coeff_diff)] |
| 103 | coeff_pkg.sort() |
| 104 | |
| 105 | # num_elements_to_force_round had better be < (2 * sample_units_width + 1) or |
| 106 | # * our math was wildy wrong |
| 107 | # * an awful lot of the curve is out side our sample |
| 108 | # either is pretty bad, and probably means the results will not be useful. |
| 109 | num_elements_to_force_round = abs(coeffs_rounded_sum - target_sum) |
| 110 | for i in xrange(num_elements_to_force_round): |
| 111 | print "Adding %d to index %d to force round %f." % (delta, coeff_pkg[i].index, coeffs[coeff_pkg[i].index]) |
| 112 | coeffs_rounded[coeff_pkg[i].index] += delta |
| 113 | |
| 114 | print "Prepending %d 0x00 for allignment." % (sample_align,) |
| 115 | coeffs_rounded_aligned = ([0] * int(sample_align)) + coeffs_rounded |
| 116 | |
| 117 | print ', '.join(["0x%0.2X" % coeff_rounded for coeff_rounded in coeffs_rounded_aligned]) |
| 118 | print sum(coeffs), hex(sum(coeffs_rounded)) |
| 119 | print |