0

This question addresses in particular the question of curve fitting in the context of color mixing of paints, pigments, etc.

I am trying to guess the required proportions of two paints, let's say "Brown" (B) and "white" (W) to get to a given lightness value L.

I have made a "calibration curve" in the same fashion as one does so for applying the Beer-lambert law in chemistry. However, the curve is not linear so I cannot use the Beer-Lambert law.

Here's what I've done :

(1)

  • I have measured the spectrum of paint samples for these proportions of mixture, labeled a, b, c, d, ... etc.

    a >>> W = 1, B = 0 (pure white)

    b >>> W = 63/64, B = 1/64

    c >>> W = 31/32, B = 1/32

    d >>> W = 15/16, B = 1/16

    e >>> W = 7/8, B = 1/8

    f >>> W = 3/4, B = 1/4

    g >>> W = 1/2, B = 1/2

    h >>> W = 0, B = 1 (pure brown)

And these are the spectral reflectance curves that I got :

reflectance curves

If I pick-up one reflectance value at a given wavelength, e.g. 500 nm, I get this nice curve, where the x axis represents the proportion of white paint in the mix, and the y axis the reflected light at 500 nm : amount of white VS reflectance at 500 nm

I'd like to guess by interpolation how much white I need to arrive at a certain amount of reflected light.

(2)

I have tried to fit an exponential curve to the data with scipy.optimize.curve_fit but the fit is pretty poor:

exponential fit of data

What kind of function would fit the data closely?

adrienlucca.net
  • 677
  • 2
  • 10
  • 26
  • 1
    I'd fit an exponential curve: http://stackoverflow.com/questions/3433486/how-to-do-exponential-and-logarithmic-curve-fitting-in-python-i-found-only-poly – Javier Jan 23 '17 at 09:33
  • @Javier could you help me to understand how to fit it to the exponential curve, I'm not sure I understand how to do... – adrienlucca.net Jan 23 '17 at 09:59
  • Possible duplicate of [Use of curve\_fit to fit data](http://stackoverflow.com/questions/10857948/use-of-curve-fit-to-fit-data) – Lucas Jan 23 '17 at 10:08
  • Keep in mind that you want the transition to be perceptually uniform, rather than mathematically uniform: bit of a dicussion here: http://matplotlib.org/users/colormaps.html; the rate of change between colours should be constant in the LAB space (rather than RGB/HSV spaces). See also: https://github.com/matplotlib/viscm, http://matplotlib.org/cmocean/ and http://wiki.seg.org/wiki/How_to_evaluate_and_compare_color_maps. – Benjamin Jan 23 '17 at 16:18
  • @Benjamin Sure! I know that, thanks! That's precisely why I need a good interpolation method... The geometric progression (1:1, 3:1, 7:1, 15:1, ...etc.) is usually a good approximation method for a first shot, and it gives a good basis for measuring L*a*b* values. ;) – adrienlucca.net Jan 23 '17 at 21:43

1 Answers1

1

I'll expand my comment since nobody has answered.

From what I see in the figure, there is a pattern. The best way would be to fit a curve that fits that pattern as a whole. You can do this without any math using Eureqa (the free trial should be enough): http://www.nutonian.com/products/eureqa/

If you want to remain in python and fit an exponential distribution, you can do the following: How to do exponential and logarithmic curve fitting in Python? I found only polynomial fitting

So imagine you have for the wavelength 500nm the following values:

y = [10,20,30,30,50,60,70,80,90,100]
x = [0.,0.3,0.5,0.6,0.72,0.77,0.84,0.9,0.95,1]

Then the code to fit the exponential curve would be:

import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

def func(x, a, b, c):
    return a * np.exp(-b * x) + c

popt, pcov = curve_fit(func, x, y)

In this case we get that a,b, and c are:

popt = array([ 7.1907744 , -2.62804994,  2.45029842])

So to get the value of reflected light at a certain x (for instance 0.2), you can do:

func(0.2, 7.1907744 , -2.62804994,  2.45029842)

Which is 14.61

But you say it's a bad fit, if you don't need a model, you can do the following: If you don't really care about having a model you can use this: https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.interp1d.html#scipy.interpolate.interp1d

from scipy import interpolate
f = interpolate.interp1d(x, y, kind="quadratic") #you can try different kinds of interpolation

And then to find a value (for instance x=0.2):

ynew = f(0.2)

Which is 6.549

Or to have many values so you can plot them: ynew = f(np.linspace(0,1,1000)

Community
  • 1
  • 1
Javier
  • 420
  • 2
  • 8