From 2124d6bdcfc1717ca7f1c9f7b9e41b7b1f9c0185 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Sat, 5 Apr 2014 14:30:14 +0200 Subject: split aux into aux and interpolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/__main__.py | 1 + src/aux.py | 171 +--------------------------------------------- src/interpolation.py | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 170 deletions(-) create mode 100644 src/interpolation.py (limited to 'src') diff --git a/src/__main__.py b/src/__main__.py index 2b0c9df..08baac8 100755 --- a/src/__main__.py +++ b/src/__main__.py @@ -93,6 +93,7 @@ from monitor import * from weather import * from backlight import * from blackbody import * +from interpolation import * diff --git a/src/aux.py b/src/aux.py index 1a6ac8b..1f4c666 100644 --- a/src/aux.py +++ b/src/aux.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# This module contains auxiliary function. +# This module contains auxiliary functions. from curve import * @@ -49,175 +49,6 @@ def ramps_to_function(r, g, b): return functionise((fp(r), fp(g), fp(b))) -def linearly_interpolate_ramp(r, g, b): # TODO demo this - ''' - Linearly interpolate ramps to the size of the output axes - - @param r:list The red colour curves - @param g:list The green colour curves - @param b:list The blue colour curves - @return :(r:list, g:list, b:list) The input parameters extended to sizes of `o_size`, - or their original size, whatever is larger. - ''' - C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size) - R, G, B = C(r), C(g), C(b) - for small, large in ((r, R), (g, G), (b, B)): - small_, large_ = len(small) - 1, len(large) - 1 - # Only interpolate if scaling up - if large_ > small_: - for i in range(len(large)): - # Scaling - j = i * small_ / large_ - # Floor, weight, ceiling - j, w, k = int(j), j % 1, min(int(j) + 1, small_) - # Interpolation - large[i] = small[j] * (1 - w) + small[k] * w - return (R, G, B) - - -def cubicly_interpolate_ramp(r, g, b): # TODO demo this - ''' - Interpolate ramps to the size of the output axes using cubic Hermite spline - - @param r:list The red colour curves - @param g:list The green colour curves - @param b:list The blue colour curves - @return :(r:list, g:list, b:list) The input parameters extended to sizes of `o_size`, - or their original size, whatever is larger. - ''' - C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size) - R, G, B = C(r), C(g), C(b) - # Basis functions - h00 = lambda t : (1 + 2 * t) * (1 - t) ** 2 - h01 = lambda t : t * (1 - t) ** 2 - h10 = lambda t : t ** 2 * (3 - 2 * t) - h11 = lambda t : t ** 2 * (t - 1) - def tangent(values, index, last): - ''' - Calculate the tangent at a point - - @param values:list Mapping from points to values - @param index:int The point - @param last:int The last point - @return :float The tangent at the point `index` - ''' - if last == 0: return 0 - if index == 0: return values[1] - values[0] - if index == last: return values[last] - values[last - 1] - return (values[index + 1] - values[index - 1]) / 2 - # Interpolate each curve - for small, large in ((r, R), (g, G), (b, B)): - small_, large_ = len(small) - 1, len(large) - 1 - # Only interpolate if scaling up - if large_ > small_: - for i in range(len(large)): - # Scaling - j = i * small_ / large_ - # Floor, weight, ceiling - j, w, k = int(j), j % 1, min(int(j) + 1, small_) - # Points - pj, pk = small[j], small[k] - # Tangents - mj, mk = tangent(small, j, small_), tangent(small, k, small_) - # Interpolation - large[i] = h00(w) * pj + h10(w) * mj + h01(w) * pk + h11(w) * mk - ## Check local monotonicity - eliminate_halos(r, g, b, R, G, B) - return (R, G, B) - - -def polynomially_interpolate_ramp(r, g, b): # TODO Speedup, demo and document this - ''' - Polynomially interpolate ramps to the size of the output axes. - - This function will replace parts of the result with linear interpolation - where local monotonicity have been broken. That is, there is a local - maximum or local minimum generated between two reference points, linear - interpolation will be used instead between those two points. - - @param r:list The red colour curves - @param g:list The green colour curves - @param b:list The blue colour curves - @return :(r:list, g:list, b:list) The input parameters extended to sizes of `o_size`, - or their original size, whatever is larger. - ''' - C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size) - R, G, B, linear = C(r), C(g), C(b), [None] - for ci, (small, large) in enumerate(((r, R), (g, G), (b, B))): - small_, large_ = len(small) - 1, len(large) - 1 - # Only interpolate if scaling up - if large_ > small_: - n = len(small) - ## Construct interpolation matrix (TODO this is not working correctly) - M = [[small[y] ** i for i in range(n)] for y in range(n)] - A = [x / small_ for x in range(n)] - ## Eliminate interpolation matrix - # (XXX this can be done faster by utilising the fact that we have a Vandermonde matrix) - # Eliminiate lower left - for k in range(n - 1): - for i in range(k + 1, n): - m = M[i][k] / M[k][k] - M[i][k + 1:] = [M[i][j] - M[k][j] * m for j in range(k + 1, n)] - A[i] -= A[k] * m - # Eliminiate upper right - for k in reversed(range(n)): - A[:k] = [A[i] - A[k] * M[i][k] / M[k][k] for i in range(k)] - # Eliminiate diagonal - A = [A[k] / M[k][k] for k in range(n)] - ## Construct interpolation function - f = lambda x : sum(A[i] * x ** i for i in range(n)) - ## Apply interpolation - large[:] = [f(x / large_) for x in range(len(large))] - ## Check local monotonicity - eliminate_halos(r, g, b, R, G, B) - return (R, G, B) - - -def eliminate_halos(r, g, b, R, G, B): - ''' - Eliminate haloing effects in interpolations - - @param r:list The original red curve - @param g:list The original green curve - @param b:list The original blue curve - @param R:list The scaled up red curve - @param G:list The scaled up green curve - @param B:list The scaled up blue curve - ''' - linear = None - for ci, (small, large) in enumerate(((r, R), (g, G), (b, B))): - small_, large_ = len(small) - 1, len(large) - 1 - ## Check local monotonicity - for i in range(small_): - # Small curve - x1, x2, y1, y2 = i, i + 1, small[i], small[i + 1] - # Scaled up curve - X1, X2 = int(x1 * large_ / small_), int(x2 * large_ / small_) - Y1, Y2 = large[X1], large[X2] - monotone = True - if y2 == y1: - # Flat part, just make sure it is flat in the interpolation - # without doing a check before. - for x in range(X1, X2 + 1): - large[x] = y1 - elif y2 > y1: - # Increasing - monotone = all(map(lambda x : large[x + 1] >= large[x], range(X1, X2))) and (Y2 > Y1) - elif y2 < y1: - # Decreasing - monotone = all(map(lambda x : large[x + 1] <= large[x], range(X1, X2))) and (Y2 < Y1) - # If the monotonicity has been broken, - if not monotone: - # replace the partition with linear interpolation. - # If linear interpolation has not yet been calculated, - if linear is None: - # then calculate it. - linear = linearly_interpolate_ramp(r, g, b) - # Extract the linear interpolation for the current colour curve, - # and replace the local partition with the linear interpolation. - large[X1 : X2 + 1] = linear[ci][X1 : X2 + 1] - - def functionise(rgb): ''' Convert a three colour curves to a function that applies those adjustments diff --git a/src/interpolation.py b/src/interpolation.py new file mode 100644 index 0000000..f5acba3 --- /dev/null +++ b/src/interpolation.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 + +# Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This module contains interpolation functions. + +from curve import * + + +def linearly_interpolate_ramp(r, g, b): # TODO demo this + ''' + Linearly interpolate ramps to the size of the output axes + + @param r:list The red colour curves + @param g:list The green colour curves + @param b:list The blue colour curves + @return :(r:list, g:list, b:list) The input parameters extended to sizes of `o_size`, + or their original size, whatever is larger. + ''' + C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size) + R, G, B = C(r), C(g), C(b) + for small, large in ((r, R), (g, G), (b, B)): + small_, large_ = len(small) - 1, len(large) - 1 + # Only interpolate if scaling up + if large_ > small_: + for i in range(len(large)): + # Scaling + j = i * small_ / large_ + # Floor, weight, ceiling + j, w, k = int(j), j % 1, min(int(j) + 1, small_) + # Interpolation + large[i] = small[j] * (1 - w) + small[k] * w + return (R, G, B) + + +def cubicly_interpolate_ramp(r, g, b): # TODO demo this + ''' + Interpolate ramps to the size of the output axes using cubic Hermite spline + + @param r:list The red colour curves + @param g:list The green colour curves + @param b:list The blue colour curves + @return :(r:list, g:list, b:list) The input parameters extended to sizes of `o_size`, + or their original size, whatever is larger. + ''' + C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size) + R, G, B = C(r), C(g), C(b) + # Basis functions + h00 = lambda t : (1 + 2 * t) * (1 - t) ** 2 + h01 = lambda t : t * (1 - t) ** 2 + h10 = lambda t : t ** 2 * (3 - 2 * t) + h11 = lambda t : t ** 2 * (t - 1) + def tangent(values, index, last): + ''' + Calculate the tangent at a point + + @param values:list Mapping from points to values + @param index:int The point + @param last:int The last point + @return :float The tangent at the point `index` + ''' + if last == 0: return 0 + if index == 0: return values[1] - values[0] + if index == last: return values[last] - values[last - 1] + return (values[index + 1] - values[index - 1]) / 2 + # Interpolate each curve + for small, large in ((r, R), (g, G), (b, B)): + small_, large_ = len(small) - 1, len(large) - 1 + # Only interpolate if scaling up + if large_ > small_: + for i in range(len(large)): + # Scaling + j = i * small_ / large_ + # Floor, weight, ceiling + j, w, k = int(j), j % 1, min(int(j) + 1, small_) + # Points + pj, pk = small[j], small[k] + # Tangents + mj, mk = tangent(small, j, small_), tangent(small, k, small_) + # Interpolation + large[i] = h00(w) * pj + h10(w) * mj + h01(w) * pk + h11(w) * mk + ## Check local monotonicity + eliminate_halos(r, g, b, R, G, B) + return (R, G, B) + + +def polynomially_interpolate_ramp(r, g, b): # TODO Speedup, demo and document this + ''' + Polynomially interpolate ramps to the size of the output axes. + + This function will replace parts of the result with linear interpolation + where local monotonicity have been broken. That is, there is a local + maximum or local minimum generated between two reference points, linear + interpolation will be used instead between those two points. + + @param r:list The red colour curves + @param g:list The green colour curves + @param b:list The blue colour curves + @return :(r:list, g:list, b:list) The input parameters extended to sizes of `o_size`, + or their original size, whatever is larger. + ''' + C = lambda c : c[:] if len(c) >= o_size else ([None] * o_size) + R, G, B, linear = C(r), C(g), C(b), [None] + for ci, (small, large) in enumerate(((r, R), (g, G), (b, B))): + small_, large_ = len(small) - 1, len(large) - 1 + # Only interpolate if scaling up + if large_ > small_: + n = len(small) + ## Construct interpolation matrix (TODO this is not working correctly) + M = [[small[y] ** i for i in range(n)] for y in range(n)] + A = [x / small_ for x in range(n)] + ## Eliminate interpolation matrix + # (XXX this can be done faster by utilising the fact that we have a Vandermonde matrix) + # Eliminiate lower left + for k in range(n - 1): + for i in range(k + 1, n): + m = M[i][k] / M[k][k] + M[i][k + 1:] = [M[i][j] - M[k][j] * m for j in range(k + 1, n)] + A[i] -= A[k] * m + # Eliminiate upper right + for k in reversed(range(n)): + A[:k] = [A[i] - A[k] * M[i][k] / M[k][k] for i in range(k)] + # Eliminiate diagonal + A = [A[k] / M[k][k] for k in range(n)] + ## Construct interpolation function + f = lambda x : sum(A[i] * x ** i for i in range(n)) + ## Apply interpolation + large[:] = [f(x / large_) for x in range(len(large))] + ## Check local monotonicity + eliminate_halos(r, g, b, R, G, B) + return (R, G, B) + + +def eliminate_halos(r, g, b, R, G, B): + ''' + Eliminate haloing effects in interpolations + + @param r:list The original red curve + @param g:list The original green curve + @param b:list The original blue curve + @param R:list The scaled up red curve + @param G:list The scaled up green curve + @param B:list The scaled up blue curve + ''' + linear = None + for ci, (small, large) in enumerate(((r, R), (g, G), (b, B))): + small_, large_ = len(small) - 1, len(large) - 1 + ## Check local monotonicity + for i in range(small_): + # Small curve + x1, x2, y1, y2 = i, i + 1, small[i], small[i + 1] + # Scaled up curve + X1, X2 = int(x1 * large_ / small_), int(x2 * large_ / small_) + Y1, Y2 = large[X1], large[X2] + monotone = True + if y2 == y1: + # Flat part, just make sure it is flat in the interpolation + # without doing a check before. + for x in range(X1, X2 + 1): + large[x] = y1 + elif y2 > y1: + # Increasing + monotone = all(map(lambda x : large[x + 1] >= large[x], range(X1, X2))) and (Y2 > Y1) + elif y2 < y1: + # Decreasing + monotone = all(map(lambda x : large[x + 1] <= large[x], range(X1, X2))) and (Y2 < Y1) + # If the monotonicity has been broken, + if not monotone: + # replace the partition with linear interpolation. + # If linear interpolation has not yet been calculated, + if linear is None: + # then calculate it. + linear = linearly_interpolate_ramp(r, g, b) + # Extract the linear interpolation for the current colour curve, + # and replace the local partition with the linear interpolation. + large[X1 : X2 + 1] = linear[ci][X1 : X2 + 1] + -- cgit v1.2.3-70-g09d2