120 lines
4.4 KiB
Python
120 lines
4.4 KiB
Python
|
#!/usr/bin/python
|
||
|
|
||
|
'''
|
||
|
Copyright 2013 Google Inc.
|
||
|
|
||
|
Use of this source code is governed by a BSD-style license that can be
|
||
|
found in the LICENSE file.
|
||
|
'''
|
||
|
|
||
|
import math
|
||
|
import pprint
|
||
|
|
||
|
def withinStdDev(n):
|
||
|
"""Returns the percent of samples within n std deviations of the normal."""
|
||
|
return math.erf(n / math.sqrt(2))
|
||
|
|
||
|
def withinStdDevRange(a, b):
|
||
|
"""Returns the percent of samples within the std deviation range a, b"""
|
||
|
if b < a:
|
||
|
return 0;
|
||
|
|
||
|
if a < 0:
|
||
|
if b < 0:
|
||
|
return (withinStdDev(-a) - withinStdDev(-b)) / 2;
|
||
|
else:
|
||
|
return (withinStdDev(-a) + withinStdDev(b)) / 2;
|
||
|
else:
|
||
|
return (withinStdDev(b) - withinStdDev(a)) / 2;
|
||
|
|
||
|
|
||
|
#We have a bunch of smudged samples which represent the average coverage of a range.
|
||
|
#We have a 'center' which may not line up with those samples.
|
||
|
#From the 'center' we want to make a normal approximation where '5' sample width out we're at '3' std deviations.
|
||
|
#The first and last samples may not be fully covered.
|
||
|
|
||
|
#This is the sub-sample shift for each set of FIR coefficients (the centers of the lcds in the samples)
|
||
|
#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.
|
||
|
#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.
|
||
|
samples_per_pixel = 4
|
||
|
subpxls_per_pixel = 3
|
||
|
#sample_offsets is (frac, int) in sample units.
|
||
|
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)]
|
||
|
|
||
|
#How many samples to consider to the left and right of the subpxl center.
|
||
|
sample_units_width = 5
|
||
|
|
||
|
#The std deviation at sample_units_width.
|
||
|
std_dev_max = 3
|
||
|
|
||
|
#The target sum is in some fixed point representation.
|
||
|
#Values larger the 1 in fixed point simulate ink spread.
|
||
|
target_sum = 0x110
|
||
|
|
||
|
for sample_offset, sample_align in sample_offsets:
|
||
|
coeffs = []
|
||
|
coeffs_rounded = []
|
||
|
|
||
|
#We start at sample_offset - sample_units_width
|
||
|
current_sample_left = sample_offset - sample_units_width
|
||
|
current_std_dev_left = -std_dev_max
|
||
|
|
||
|
done = False
|
||
|
while not done:
|
||
|
current_sample_right = math.floor(current_sample_left + 1)
|
||
|
if current_sample_right > sample_offset + sample_units_width:
|
||
|
done = True
|
||
|
current_sample_right = sample_offset + sample_units_width
|
||
|
current_std_dev_right = current_std_dev_left + ((current_sample_right - current_sample_left) / sample_units_width) * std_dev_max
|
||
|
|
||
|
coverage = withinStdDevRange(current_std_dev_left, current_std_dev_right)
|
||
|
coeffs.append(coverage * target_sum)
|
||
|
coeffs_rounded.append(int(round(coverage * target_sum)))
|
||
|
|
||
|
current_sample_left = current_sample_right
|
||
|
current_std_dev_left = current_std_dev_right
|
||
|
|
||
|
# Now we have the numbers we want, but our rounding needs to add up to target_sum.
|
||
|
delta = 0
|
||
|
coeffs_rounded_sum = sum(coeffs_rounded)
|
||
|
if coeffs_rounded_sum > target_sum:
|
||
|
# The coeffs add up to too much. Subtract 1 from the ones which were rounded up the most.
|
||
|
delta = -1
|
||
|
|
||
|
if coeffs_rounded_sum < target_sum:
|
||
|
# The coeffs add up to too little. Add 1 to the ones which were rounded down the most.
|
||
|
delta = 1
|
||
|
|
||
|
if delta:
|
||
|
print "Initial sum is 0x%0.2X, adjusting." % (coeffs_rounded_sum,)
|
||
|
coeff_diff = [(coeff_rounded - coeff) * delta
|
||
|
for coeff, coeff_rounded in zip(coeffs, coeffs_rounded)]
|
||
|
|
||
|
class IndexTracker:
|
||
|
def __init__(self, index, item):
|
||
|
self.index = index
|
||
|
self.item = item
|
||
|
def __lt__(self, other):
|
||
|
return self.item < other.item
|
||
|
def __repr__(self):
|
||
|
return "arr[%d] == %s" % (self.index, repr(self.item))
|
||
|
|
||
|
coeff_pkg = [IndexTracker(i, diff) for i, diff in enumerate(coeff_diff)]
|
||
|
coeff_pkg.sort()
|
||
|
|
||
|
# num_elements_to_force_round had better be < (2 * sample_units_width + 1) or
|
||
|
# * our math was wildy wrong
|
||
|
# * an awful lot of the curve is out side our sample
|
||
|
# either is pretty bad, and probably means the results will not be useful.
|
||
|
num_elements_to_force_round = abs(coeffs_rounded_sum - target_sum)
|
||
|
for i in xrange(num_elements_to_force_round):
|
||
|
print "Adding %d to index %d to force round %f." % (delta, coeff_pkg[i].index, coeffs[coeff_pkg[i].index])
|
||
|
coeffs_rounded[coeff_pkg[i].index] += delta
|
||
|
|
||
|
print "Prepending %d 0x00 for allignment." % (sample_align,)
|
||
|
coeffs_rounded_aligned = ([0] * int(sample_align)) + coeffs_rounded
|
||
|
|
||
|
print ', '.join(["0x%0.2X" % coeff_rounded for coeff_rounded in coeffs_rounded_aligned])
|
||
|
print sum(coeffs), hex(sum(coeffs_rounded))
|
||
|
print
|