diff --git a/colorpy/ColorPy.html b/colorpy/ColorPy.html index 23dbb8e..a5de76a 100644 --- a/colorpy/ColorPy.html +++ b/colorpy/ColorPy.html @@ -5,9 +5,9 @@
-ColorPy is a Python package that can convert physical descriptions of light - -spectra of light intensity vs. wavelength - into RGB colors that can be drawn on -a computer screen. It provides a nice set of attractive plots that you can -make of such spectra, and some other color related functions as well. All +ColorPy is a Python package that can convert physical descriptions of light - +spectra of light intensity vs. wavelength - into RGB colors that can be drawn on +a computer screen. It provides a nice set of attractive plots that you can +make of such spectra, and some other color related functions as well. All of the plots in this documentation were created with ColorPy.
-ColorPy is free software. ('Free' as in speech and beer.) It is -released under the GNU Lesser GPL license. You are free to use ColorPy -for any application that you like, including commercial applications. If -you modify ColorPy, you should release the source code for your modifications. +ColorPy is free software. ('Free' as in speech and beer.) It is +released under the GNU Lesser GPL license. You are free to use ColorPy +for any application that you like, including commercial applications. If +you modify ColorPy, you should release the source code for your modifications. You have no obligation to release any source for your products that just use ColorPy, however.
-Several years ago, I developed some C++ code to do these kinds of physical color -calculations. Recently, I decided to port the code to Python, and publish -the library as open source under the GNU LGPL license. I decided to make -use of (and assume the existence of) NumPy and MatPlotLib for this. These -libraries make it easy to make some nice, attractive, and informative, plots of +Several years ago, I developed some C++ code to do these kinds of physical color +calculations. Recently, I decided to port the code to Python, and publish +the library as open source under the GNU LGPL license. I decided to make +use of (and assume the existence of) NumPy and MatPlotLib for this. These +libraries make it easy to make some nice, attractive, and informative, plots of spectra. Besides, Python is just more fun than C++.
-So what can ColorPy do? The short answer, is to scan this document, and -examine the various plots of spectra and their colors. You can use ColorPy -to make the same kinds of plots, for whatever spectra you have and are -interested in. ColorPy also provides conversions between several important -three-dimensional 'color spaces', specifically RGB, XYZ, Luv, and Lab. -(There can be many different RGB spaces, depending on the particular display -used to view the results. By default, ColorPy uses the sRGB space, but you +So what can ColorPy do? The short answer, is to scan this document, and +examine the various plots of spectra and their colors. You can use ColorPy +to make the same kinds of plots, for whatever spectra you have and are +interested in. ColorPy also provides conversions between several important +three-dimensional 'color spaces', specifically RGB, XYZ, Luv, and Lab. +(There can be many different RGB spaces, depending on the particular display +used to view the results. By default, ColorPy uses the sRGB space, but you can configure it to use other RGB spaces if you like.)
@@ -79,58 +79,58 @@
-To use ColorPy, you must have installed the following: -Python, -NumPy, and +To use ColorPy, you must have installed the following: +Python, +NumPy, and MatPlotLib. Typically, SciPy -is installed along with NumPy and MatPlotLib. -ColorPy doesn't use SciPy explicitly, although MatPlotLib may require SciPy. -(I am not sure.) ColorPy is a 'pure' Python distribution, so you do not -need any extra software to build it. I have tested ColorPy both on Windows -XP and Ubuntu Linux, and it should run on any system where you can install the -prerequisites. If, for some reason, you can only install NumPy but not -MatPlotLib, you should still be able to do many of the calculations, but will +is installed along with NumPy and MatPlotLib. +ColorPy doesn't use SciPy explicitly, although MatPlotLib may require SciPy. +(I am not sure.) ColorPy is a 'pure' Python distribution, so you do not +need any extra software to build it. I have tested ColorPy both on Windows +XP and Ubuntu Linux, and it should run on any system where you can install the +prerequisites. If, for some reason, you can only install NumPy but not +MatPlotLib, you should still be able to do many of the calculations, but will not be able to make any of the nice plots.
-ColorPy generally uses wavelengths measured in nanometers (nm), 10-9 m. -Otherwise, typical metric units are used. For descriptions of spectra, -ColorPy uses two-dimensional NumPy arrays, with two columns and an arbitrary -number of rows. Each row of these arrays represents the light intensity -for one wavelength, with the value in the first column being the wavelength in -nm, and the value in the second column being the light intensity at that -wavelength. ColorPy can provide a blank spectrum array, via -colorpy.ciexyx.empty_spectrum(), which will have rows for each wavelength from 360 nm to -830 nm, at 1 nm increments. (Wavelengths outside this range are generally -ignored, as the eye cannot see them.) However, you can create your own spectrum +ColorPy generally uses wavelengths measured in nanometers (nm), 10-9 m. +Otherwise, typical metric units are used. For descriptions of spectra, +ColorPy uses two-dimensional NumPy arrays, with two columns and an arbitrary +number of rows. Each row of these arrays represents the light intensity +for one wavelength, with the value in the first column being the wavelength in +nm, and the value in the second column being the light intensity at that +wavelength. ColorPy can provide a blank spectrum array, via +colorpy.ciexyx.empty_spectrum(), which will have rows for each wavelength from 360 nm to +830 nm, at 1 nm increments. (Wavelengths outside this range are generally +ignored, as the eye cannot see them.) However, you can create your own spectrum arrays with any set of wavelengths you like.
-Color values are represented as three-component NumPy vectors. -(One-dimensional arrays). Typically, these are vectors of floats, with the -exception of displayable irgb colors, which are arrays of integers (in the range +Color values are represented as three-component NumPy vectors. +(One-dimensional arrays). Typically, these are vectors of floats, with the +exception of displayable irgb colors, which are arrays of integers (in the range 0 - 255).
-We are interested in working with physical descriptions of light spectra, that -is, functions of intensity vs. wavelength. However, color is perceived as -a three-dimensional quantity, as there are three sets of color receptors in the -eye, which respond approximately to red, green and blue light. So how do +We are interested in working with physical descriptions of light spectra, that +is, functions of intensity vs. wavelength. However, color is perceived as +a three-dimensional quantity, as there are three sets of color receptors in the +eye, which respond approximately to red, green and blue light. So how do we reduce a function of intensity vs. wavelength to a three-dimensional value?
-This fundamental step is done by integrating the intensity function with a set -of three matching functions. The standard matching functions were defined -by the Commission Internationale de l'Eclairage -(CIE), based on experiments with viewers matching the color of single wavelength -lights. The matching functions generally used in computer graphics are -those developed in 1931, which used a 2 degree field of view. (There is -also a set of matching functions developed in 1964, covering a field of view of -10 degrees, but the larger field of view does not correspond to typical -conditions in viewing computer graphics.) So the mapping is done as +This fundamental step is done by integrating the intensity function with a set +of three matching functions. The standard matching functions were defined +by the Commission Internationale de l'Eclairage +(CIE), based on experiments with viewers matching the color of single wavelength +lights. The matching functions generally used in computer graphics are +those developed in 1931, which used a 2 degree field of view. (There is +also a set of matching functions developed in 1964, covering a field of view of +10 degrees, but the larger field of view does not correspond to typical +conditions in viewing computer graphics.) So the mapping is done as follows:
@@ -138,54 +138,54 @@ Fundamentals - Mapping spectra to three-dimensional color values
Y = ∫ I (λ) * CIE-Y (λ) * dλ
Z = ∫ I (λ) * CIE-Z (λ) * dλ
-where I (λ) is the spectrum of light intensity vs. wavelength, and CIE-X (λ), -CIE-Y (λ) and CIE-Z (λ) are the matching functions. The CIE matching -functions are defined over the interval of 360 nm to 830 nm, and are zero for -all wavelengths outside this interval, so these are the bounds for the +where I (λ) is the spectrum of light intensity vs. wavelength, and CIE-X (λ), +CIE-Y (λ) and CIE-Z (λ) are the matching functions. The CIE matching +functions are defined over the interval of 360 nm to 830 nm, and are zero for +all wavelengths outside this interval, so these are the bounds for the integrals.
-So what do these matching functions look like? Let's take a look at a plot +So what do these matching functions look like? Let's take a look at a plot (made with ColorPy, of course.)
Figure 1 - The 1931 CIE XYZ matching functions.
-This plot shows the three matching functions vs. wavelength. The colors -underneath the curve, at each wavelength, are the (approximate) colors that the -human eye will perceive for a pure spectral line at that wavelength, of constant -intensity. The apparent brightness of the color at each wavelength -indicates how strongly the eye perceives that wavelength - the intensity for -each wavelength is the same. (The next section will explain how we get the +This plot shows the three matching functions vs. wavelength. The colors +underneath the curve, at each wavelength, are the (approximate) colors that the +human eye will perceive for a pure spectral line at that wavelength, of constant +intensity. The apparent brightness of the color at each wavelength +indicates how strongly the eye perceives that wavelength - the intensity for +each wavelength is the same. (The next section will explain how we get the RGB values for the colors.)
-Each of the three plots was generated via colorpy.plots.spectrum_subplot (spectrum), +Each of the three plots was generated via colorpy.plots.spectrum_subplot (spectrum), where spectrum is the value of the matching function vs. wavelength.
-All three of the matching functions are zero or positive everywhere. Since -the light intensity at any wavelength is never negative, this means that the -resulting XYZ color values are never negative. Also, the Y matching -function corresponds exactly to the luminous efficiency of the eye - the eye's -response to light of constant luminance. (These facts are some of the +All three of the matching functions are zero or positive everywhere. Since +the light intensity at any wavelength is never negative, this means that the +resulting XYZ color values are never negative. Also, the Y matching +function corresponds exactly to the luminous efficiency of the eye - the eye's +response to light of constant luminance. (These facts are some of the reasons that make this particular set of matching functions so useful.)
-So now we can map a spectrum of intensity vs. wavelength into a -three-dimensional value. Before we consider how to convert this into an -RGB color value that we can draw, we will first discuss some typical scaling +So now we can map a spectrum of intensity vs. wavelength into a +three-dimensional value. Before we consider how to convert this into an +RGB color value that we can draw, we will first discuss some typical scaling operations on XYZ colors.
-Often, it is useful to consider the 'chromaticity' of a color, that is, the hue -and saturation, independent of the intensity. This is typically done by -scaling the XYZ values so that their sum is 1.0. The resulting scaled -values are conventionally written as lower case letters x,y,z. With this -scaling, x+y+z = 1.0. The chromaticity can be -specified by the resulting x and y values, and the z component can be reconstructed as z = 1.0 - x - y. -It is also common to specify colors with their chromaticity (x and y), as well -as the total brightness (Y). Occasionally, one also wants to scale an XYZ +Often, it is useful to consider the 'chromaticity' of a color, that is, the hue +and saturation, independent of the intensity. This is typically done by +scaling the XYZ values so that their sum is 1.0. The resulting scaled +values are conventionally written as lower case letters x,y,z. With this +scaling, x+y+z = 1.0. The chromaticity can be +specified by the resulting x and y values, and the z component can be reconstructed as z = 1.0 - x - y. +It is also common to specify colors with their chromaticity (x and y), as well +as the total brightness (Y). Occasionally, one also wants to scale an XYZ color so that the resulting Y value is 1.0.
-ColorPy represents XYZ colors (and other types of colors) as three-component -vectors. There are some 'constructor' like functions to create such +ColorPy represents XYZ colors (and other types of colors) as three-component +vectors. There are some 'constructor' like functions to create such arrays, and perform these kinds of scaling:
@@ -195,187 +195,187 @@ Fundamentals - Mapping spectra to three-dimensional color values
colorpy.colormodels.xyz_normalize_Y1 (xyz)
-Notice that color types are generally specified in ColorPy with lower case -letters, as this is more readable. (I.e., xyz_color instead of XYZ_color.) -The user must keep track of the particular normalization that applies in each +Notice that color types are generally specified in ColorPy with lower case +letters, as this is more readable. (I.e., xyz_color instead of XYZ_color.) +The user must keep track of the particular normalization that applies in each situation.
-So how do we convert one of these XYZ colors to an RGB color that I can draw on +So how do we convert one of these XYZ colors to an RGB color that I can draw on my computer?
-The short answer, is to call colorpy.colormodels.irgb_from_xyz (xyz), where xyz is the -XYZ color vector. This will return a three element integer vector, with -each component in the range 0 - 255. There is also a function -colorpy.colormodels.irgb_string_from_xyz (xyz) that will return a hex string, such as +The short answer, is to call colorpy.colormodels.irgb_from_xyz (xyz), where xyz is the +XYZ color vector. This will return a three element integer vector, with +each component in the range 0 - 255. There is also a function +colorpy.colormodels.irgb_string_from_xyz (xyz) that will return a hex string, such as '#FF0000' for red.
-There are several subtleties and approximations in the behavior of these +There are several subtleties and approximations in the behavior of these functions, which are important to understand what is happening.
-The first step in the conversion, is to convert the XYZ color to a 'linear' RGB -color. By 'linear', we mean that the light intensity is proportional to -the numerical color values. ColorPy represents such linear RGB values as -floats, with the nominal range of 0.0 - 1.0 covering the range of intensity that the -monitor display can produce. (This implies an assumption as to the -physical brightness of the display.) The conversion from XYZ to linear RGB -is done by multiplication by a 3x3 element array. So, which array to use? -The specific values of the array depend on the physical display in question, -specifically the chromaticities of the monitor phosphors. Not all displays -have the exact same red, green and blue monitor primaries, and so any conversion -matrix cannot apply to all displays. This can be a considerable -complication, but fortunately, there is a specification of monitor -chromaticities that we can assume, part of the sRGB standard, and are likely to -be a close match to most actual displays. ColorPy uses this assumption by -default, although you can change the assumed monitor chromaticities to nearly +The first step in the conversion, is to convert the XYZ color to a 'linear' RGB +color. By 'linear', we mean that the light intensity is proportional to +the numerical color values. ColorPy represents such linear RGB values as +floats, with the nominal range of 0.0 - 1.0 covering the range of intensity that the +monitor display can produce. (This implies an assumption as to the +physical brightness of the display.) The conversion from XYZ to linear RGB +is done by multiplication by a 3x3 element array. So, which array to use? +The specific values of the array depend on the physical display in question, +specifically the chromaticities of the monitor phosphors. Not all displays +have the exact same red, green and blue monitor primaries, and so any conversion +matrix cannot apply to all displays. This can be a considerable +complication, but fortunately, there is a specification of monitor +chromaticities that we can assume, part of the sRGB standard, and are likely to +be a close match to most actual displays. ColorPy uses this assumption by +default, although you can change the assumed monitor chromaticities to nearly anything you like.
-So for now, let's assume the standard sRGB chromaticities, which gives us the +So for now, let's assume the standard sRGB chromaticities, which gives us the correct 3x3 matrix, and so we can convert our XYZ colors to linear RGB colors.
-We then come to the next obstacle... The RGB values that we get from this -process are often out of range - meaning that they are either greater than 1.0, -or even that they are negative! The first case is fairly straightforward, -it means that the color is too bright for the display. The second case -means that the color is too saturated and vivid for the display. The -display must compose all colors from some combination of positive amounts of the -colors of its red, green and blue phosphors. The colors of these phosphors -are not perfectly saturated, they are washed out, mixed with white, to some -extent. So not all colors can be displayed accurately. As an -example, the colors of pure spectral lines, all have some negative component. -Something must be done to put these values into the 0.0 - 1.0 range that can +We then come to the next obstacle... The RGB values that we get from this +process are often out of range - meaning that they are either greater than 1.0, +or even that they are negative! The first case is fairly straightforward, +it means that the color is too bright for the display. The second case +means that the color is too saturated and vivid for the display. The +display must compose all colors from some combination of positive amounts of the +colors of its red, green and blue phosphors. The colors of these phosphors +are not perfectly saturated, they are washed out, mixed with white, to some +extent. So not all colors can be displayed accurately. As an +example, the colors of pure spectral lines, all have some negative component. +Something must be done to put these values into the 0.0 - 1.0 range that can actually be displayed, known as color clipping.
-In the first case, values larger than 1.0, ColorPy scales the color so that the -maximum component is 1.0. This reduces the brightness without changing the -chromaticity. The second case requires some change in chromaticity. -By default, ColorPy will add white to the color, just enough to make all of the -components non-negative. (You can also have ColorPy clamp the negative -values to zero. My personal, qualitative, assessment is that adding white -produces somewhat better results. There is also the potential to develop a +In the first case, values larger than 1.0, ColorPy scales the color so that the +maximum component is 1.0. This reduces the brightness without changing the +chromaticity. The second case requires some change in chromaticity. +By default, ColorPy will add white to the color, just enough to make all of the +components non-negative. (You can also have ColorPy clamp the negative +values to zero. My personal, qualitative, assessment is that adding white +produces somewhat better results. There is also the potential to develop a better clipping function.)
-So now we have linear RGB values in the range 0.0 - 1.0. The next subtlety -in the conversion process, is that the intensity of colors on the display is not -simply proportional to the color values given to the hardware. This -situation is known as 'gamma correction', and is particularly significant for -CRT displays. The voltage on the electron gun in the CRT display is -proportional to the RGB values given to the hardware to display, but the -intensity of the resulting light is *not* proportional to this voltage, in fact -the relationship is a power law. The particular correction for this -depends on the physical display in question. LCD displays add another -complication, as it is not clear (at least to me) what the correct conversion is -in this case. Again, we rely on the sRGB standard to decide what to do. -That standard assumes a physical 'gamma' exponent of about 2.2, and ColorPy -applies this correction by default. You can change this to a different +So now we have linear RGB values in the range 0.0 - 1.0. The next subtlety +in the conversion process, is that the intensity of colors on the display is not +simply proportional to the color values given to the hardware. This +situation is known as 'gamma correction', and is particularly significant for +CRT displays. The voltage on the electron gun in the CRT display is +proportional to the RGB values given to the hardware to display, but the +intensity of the resulting light is *not* proportional to this voltage, in fact +the relationship is a power law. The particular correction for this +depends on the physical display in question. LCD displays add another +complication, as it is not clear (at least to me) what the correct conversion is +in this case. Again, we rely on the sRGB standard to decide what to do. +That standard assumes a physical 'gamma' exponent of about 2.2, and ColorPy +applies this correction by default. You can change this to a different exponent if you like.
-The final step after gamma correction, is to convert the RGB components from the -range 0.0 - 1.0 to 0 - 255, which is the typical range needed to pass to the -hardware. This is done with simple scaling and rounding. The final -result of all of these conversions, RGB color values in the range 0 - 255, is -referred to as an irgb_color. This is the color type that can be passed to +The final step after gamma correction, is to convert the RGB components from the +range 0.0 - 1.0 to 0 - 255, which is the typical range needed to pass to the +hardware. This is done with simple scaling and rounding. The final +result of all of these conversions, RGB color values in the range 0 - 255, is +referred to as an irgb_color. This is the color type that can be passed to drawing functions.
-Summarizing of these conversions, with the functions that ColorPy uses +Summarizing of these conversions, with the functions that ColorPy uses internally:
-colorpy.colormodels.rgb_from_xyz (xyz) - Converts an XYZ color to a linear RGB color, -with components in the nominal range 0.0 - 1.0, but possibly out of range -(greater than 1.0, or negative). The resulting linear RGB color cannot be +colorpy.colormodels.rgb_from_xyz (xyz) - Converts an XYZ color to a linear RGB color, +with components in the nominal range 0.0 - 1.0, but possibly out of range +(greater than 1.0, or negative). The resulting linear RGB color cannot be directly passed to drawing functions.
-colorpy.colormodels.irgb_from_rgb (rgb) - Converts a linear RGB color in the nominal -range 0.0 - 1.0 to a displayable irgb color, definitely in the range 0 - 255. -Color clipping may be applied (intensity as well as chromaticity), and gamma -correction is accounted for. This result can be passed to drawing +colorpy.colormodels.irgb_from_rgb (rgb) - Converts a linear RGB color in the nominal +range 0.0 - 1.0 to a displayable irgb color, definitely in the range 0 - 255. +Color clipping may be applied (intensity as well as chromaticity), and gamma +correction is accounted for. This result can be passed to drawing functions.
-With all of this, let's plot some real colors. First, consider the pure -spectral lines - that is, spectra that are all black (zero -intensity), except at a single wavelength. We consider all the wavelengths -from 360 nm to 830 nm, which covers the range of human vision (and the range of +With all of this, let's plot some real colors. First, consider the pure +spectral lines - that is, spectra that are all black (zero +intensity), except at a single wavelength. We consider all the wavelengths +from 360 nm to 830 nm, which covers the range of human vision (and the range of the CIE XYZ matching functions.)
-The two-part plot below shows the result. The top section, shows the best -colors that ColorPy can draw for each wavelength. The amount of light -intensity for each wavelength is the same. But since the human eye has -different sensitivity to different wavelengths, the apparent brightness looks different -for different colors. For example, the color for 750 nm is quite dark, -while the color for 550 nm is quite bright. They represent lines with the -same physical luminance, however. The bottom section shows the -linear RGB values corresponding to each wavelength. You can see that there -are negative RGB values on this plot. In fact, there is a negative -component at every wavelength - none of the pure spectral lines can be displayed -with full saturation. (The overall intensity scale is arbitrary, and has +The two-part plot below shows the result. The top section, shows the best +colors that ColorPy can draw for each wavelength. The amount of light +intensity for each wavelength is the same. But since the human eye has +different sensitivity to different wavelengths, the apparent brightness looks different +for different colors. For example, the color for 750 nm is quite dark, +while the color for 550 nm is quite bright. They represent lines with the +same physical luminance, however. The bottom section shows the +linear RGB values corresponding to each wavelength. You can see that there +are negative RGB values on this plot. In fact, there is a negative +component at every wavelength - none of the pure spectral lines can be displayed +with full saturation. (The overall intensity scale is arbitrary, and has been chosen so that the largest RGB component for any wavelength is 1.0.)
Figure 2 - RGB values for the pure spectral lines.
-This specific plot was made with colorpy.plots.visible_spectrum_plot (), and the real -work was done with colorpy.plots.color_vs_param_plot (param_list, rgb_colors, title, -filename, tight=False, plotfunc=pylab.plot, xlabel='param', ylabel='RGB Color'). -This function accepts two lists, one of an arbitrary parameter (wavelength in -this case), and one of linear RGB colors. (The two lists must be of the -same size.) You also must supply a title and filename for the plot. -Optional arguments include a request that the x-axis be 'tightened' to only -include the range of the parameters, a different plotting function from the -default, and different labels for the axes. This is a very handy function, +This specific plot was made with colorpy.plots.visible_spectrum_plot (), and the real +work was done with colorpy.plots.color_vs_param_plot (param_list, rgb_colors, title, +filename, tight=False, plotfunc=pylab.plot, xlabel='param', ylabel='RGB Color'). +This function accepts two lists, one of an arbitrary parameter (wavelength in +this case), and one of linear RGB colors. (The two lists must be of the +same size.) You also must supply a title and filename for the plot. +Optional arguments include a request that the x-axis be 'tightened' to only +include the range of the parameters, a different plotting function from the +default, and different labels for the axes. This is a very handy function, useful for many other plots besides this one.
-You can see that there are negative RGB values for these colors, and those +You can see that there are negative RGB values for these colors, and those actually drawn have been clipped to something displayable.
-Another way to understand the limited color gamut (range of displayable colors) of physical displays, is to consider -the 'shark fin' CIE chromaticity diagram. On this plot, we draw the chromaticities of the pure -spectral lines. These trace out a fin shaped region. The low -wavelength colors start at the lower left corner of the fin, and as the -wavelength increases, moves up on the plot towards green, and then down and -to the right towards yellow and red. The longest wavelength corresponds to -the red corner at the far right. The straight line connecting the long -wavelength red to the short wavelength blue is not composed of pure spectral -lines, rather these 'purples' are a linear combination of the extreme red and -blue colors. The outer boundary of this diagram represents the spectrally -pure colors. Just inside this boundary, we draw the best color match for +Another way to understand the limited color gamut (range of displayable colors) of physical displays, is to consider +the 'shark fin' CIE chromaticity diagram. On this plot, we draw the chromaticities of the pure +spectral lines. These trace out a fin shaped region. The low +wavelength colors start at the lower left corner of the fin, and as the +wavelength increases, moves up on the plot towards green, and then down and +to the right towards yellow and red. The longest wavelength corresponds to +the red corner at the far right. The straight line connecting the long +wavelength red to the short wavelength blue is not composed of pure spectral +lines, rather these 'purples' are a linear combination of the extreme red and +blue colors. The outer boundary of this diagram represents the spectrally +pure colors. Just inside this boundary, we draw the best color match for each wavelength.
-The triangle inside the fin represents the range (gamut) of colors that the -physical display can show. The vertices labeled Red, Green and Blue -represent the chromaticities of the monitor primaries, and the point labeled -White represents the white point with all primaries at full strength. -(This plot assumes the standard sRGB primaries and white point.) The -points inside the inner triangle are the only colors that the display can render -accurately. (This figure -could use a little work. It would be nice to label the outer boundary -of the fin with the corresponding wavelength.) The points outside the -inner triangle are colors that must be approximated. (Points outside the +The triangle inside the fin represents the range (gamut) of colors that the +physical display can show. The vertices labeled Red, Green and Blue +represent the chromaticities of the monitor primaries, and the point labeled +White represents the white point with all primaries at full strength. +(This plot assumes the standard sRGB primaries and white point.) The +points inside the inner triangle are the only colors that the display can render +accurately. (This figure +could use a little work. It would be nice to label the outer boundary +of the fin with the corresponding wavelength.) The points outside the +inner triangle are colors that must be approximated. (Points outside the outer 'fin' do not correspond to any color at all.)
-You can see that the standard monitor is much more limited in displaying greens -than blues and reds. If someone is able to invent a much purer green -colored phosphor, with low persistence so it is suitable for animations and -hence real displays, then the world of computer graphics will get significantly -more richly colored! Also notice that there is no possible set of of three -monitor phosphor chromaticities that can cover the entire visible gamut. -For any three points inside the 'fin', the enclosed triangle must necessarily -exclude some of the pure spectral colors, even if the monitor phosphors were +You can see that the standard monitor is much more limited in displaying greens +than blues and reds. If someone is able to invent a much purer green +colored phosphor, with low persistence so it is suitable for animations and +hence real displays, then the world of computer graphics will get significantly +more richly colored! Also notice that there is no possible set of of three +monitor phosphor chromaticities that can cover the entire visible gamut. +For any three points inside the 'fin', the enclosed triangle must necessarily +exclude some of the pure spectral colors, even if the monitor phosphors were perfectly spectrally pure.
Figure 3 - CIE chromaticity diagram of the visible gamut.
-Colors inside
-the inner triangle can be accurately drawn on the display, points outside (but
+Colors inside
+the inner triangle can be accurately drawn on the display, points outside (but
inside the fin) must be approximated.
-This figure is drawn with colorpy.plots.shark_fin_plot(). This is kind of a +This figure is drawn with colorpy.plots.shark_fin_plot(). This is kind of a specialized figure, probably not that useful for other things. Now, let's consider some more examples.
@@ -388,24 +388,24 @@-The simplest way that ColorPy can be used to display colors, is to display a set -of known XYZ (or RGB) colors. As an example, we use the MacBeth ColorChecker Chart. -This is a set of standard reference colors that is used in photography and -video. (You can buy the physical chart from photographic suppliers. -It is not particularly cheap.) It is used to verify that colors are being -reproduced accurately on film. [Hall, p. 119] provides a set of xy colors for -the patches on this chart (which must assume some particular lighting -environment), and ColorPy can convert these into displayable colors. This -serves as a test that the xyz to rgb conversions are operating correctly, which +The simplest way that ColorPy can be used to display colors, is to display a set +of known XYZ (or RGB) colors. As an example, we use the MacBeth ColorChecker Chart. +This is a set of standard reference colors that is used in photography and +video. (You can buy the physical chart from photographic suppliers. +It is not particularly cheap.) It is used to verify that colors are being +reproduced accurately on film. [Hall, p. 119] provides a set of xy colors for +the patches on this chart (which must assume some particular lighting +environment), and ColorPy can convert these into displayable colors. This +serves as a test that the xyz to rgb conversions are operating correctly, which is analogous to the sort of thing the real chart is used for.
-This 'patch' plot is made with colorpy.plots.xyz_patch_plot (xyz_colors, color_names, +This 'patch' plot is made with colorpy.plots.xyz_patch_plot (xyz_colors, color_names, title, filename, patch_gap=0.05, num_across=6), where xyz_colors and color_names -are two lists, the first with the XYZ color values to draw, and the second with -names to draw on the plot. The two lists must be of the same size, but you -can pass None for the second list to skip the labels. You also must supply -a title and filename, and there are optional arguments to fine-tune the size and -arrangement of the patches. There is also a similar function +are two lists, the first with the XYZ color values to draw, and the second with +names to draw on the plot. The two lists must be of the same size, but you +can pass None for the second list to skip the labels. You also must supply +a title and filename, and there are optional arguments to fine-tune the size and +arrangement of the patches. There is also a similar function colorpy.plots.rgb_patch_plot() when you have known RGB values that you want to draw.
@@ -415,121 +415,121 @@-For a more interesting example, one that involves physical light spectra, consider the colors of thermal -blackbodies. A 'blackbody' is an object that is in thermal equilibrium -with its environment, at some temperature T. Such an object will radiate -light energy, with a particular spectrum of intensity vs. wavelength. This -spectrum depends only on the temperature of the blackbody, and is completely -independent of the composition of the blackbody. The theory of blackbodies -was important in the development of quantum mechanics, the first application of -quantum mechanics was by Max Planck in deriving the blackbody spectrum. Many real light +For a more interesting example, one that involves physical light spectra, consider the colors of thermal +blackbodies. A 'blackbody' is an object that is in thermal equilibrium +with its environment, at some temperature T. Such an object will radiate +light energy, with a particular spectrum of intensity vs. wavelength. This +spectrum depends only on the temperature of the blackbody, and is completely +independent of the composition of the blackbody. The theory of blackbodies +was important in the development of quantum mechanics, the first application of +quantum mechanics was by Max Planck in deriving the blackbody spectrum. Many real light sources are approximately blackbodies.
-Blackbody theory shows that the 'monochromatic specific intensity' -Bλ(T), the power at wavelength λ radiated per unit wavelength +Blackbody theory shows that the 'monochromatic specific intensity' +Bλ(T), the power at wavelength λ radiated per unit wavelength per unit solid angle, is [Shu p. 78]:
Bλ(T) = (2hc2)/(λ5) * (1 / (exp(hc/λkT) - 1))
-where h = Planck's constant, c = speed of light, k +where h = Planck's constant, c = speed of light, k = Boltzman's constant, λ = wavelength, and T = temperature.
-Using this relation, we can construct a spectrum in ColorPy, and then determine -the rgb color of the blackbody radiator. The module blackbody provides the +Using this relation, we can construct a spectrum in ColorPy, and then determine +the rgb color of the blackbody radiator. The module blackbody provides the appropriate functions. First, the function blackbody.blackbody_specific_intensity (wl_nm, T_K) -calculates the Bλ(T) above, for any wavelength and temperature. This is -then converted into a spectrum of intensity vs. wavelength. The function -ciexyz.empty_spectrum() is called to get a NumPy array to hold the -spectrum. This array has one row for each wavelength to be used, -from 360 to 830 nm, with an increment of 1 nm. The first column is already -filled in with the wavelength (in nm), the second column is to be filled with -the intensity, and is initially zero. Since Bλ(T) represents the power per unit wavelength, this -must be multiplied by the width of the interval, which is 1 nm. This -resulting spectrum is then converted into an xyz color with -ciexyz.xyz_from_spectrum(). This can then be converted to a -displayable irgb color and -drawn. The whole process is performed by colorpy.blackbody.blackbody_spectrum(), +calculates the Bλ(T) above, for any wavelength and temperature. This is +then converted into a spectrum of intensity vs. wavelength. The function +ciexyz.empty_spectrum() is called to get a NumPy array to hold the +spectrum. This array has one row for each wavelength to be used, +from 360 to 830 nm, with an increment of 1 nm. The first column is already +filled in with the wavelength (in nm), the second column is to be filled with +the intensity, and is initially zero. Since Bλ(T) represents the power per unit wavelength, this +must be multiplied by the width of the interval, which is 1 nm. This +resulting spectrum is then converted into an xyz color with +ciexyz.xyz_from_spectrum(). This can then be converted to a +displayable irgb color and +drawn. The whole process is performed by colorpy.blackbody.blackbody_spectrum(), which is listed below.
def blackbody_spectrum (T_K):
'''Get the spectrum of a blackbody, as a numpy array.'''
spectrum = ciexyz.empty_spectrum()
(num_rows, num_cols) = spectrum.shape
- for i in xrange (0, num_rows):
+ for i in range (0, num_rows):
specific_intensity = blackbody_specific_intensity (spectrum [i][0], T_K)
# scale by size of wavelength interval
spectrum [i][1] = specific_intensity * ciexyz.delta_wl_nm * 1.0e-9
return spectrum
-So let's look at some results of these calculations, which also introduce a new -type of ColorPy plot, the spectrum plot. First, consider a -blackbody with a temperature of 5778 K. This is the surface temperature of -the Sun [Wikipedia], and this spectrum approximates that of the sun. Figure 5 below -shows both the overall color of the resulting spectrum, with a graph of the -spectrum itself below the color patch. The overall color is nearly white. -The spectrum shows that the peak intensity is in the green region, around 500 nm -or so, but with significant contributions from both lower and higher -wavelengths. The colors in the spectrum plot are indicate the extent to -which the eye is sensitive to the particular wavelength. Each color band -has the same amount of luminance, so the apparent brightness of the color indicates the -extent to which the eye is sensitive to that wavelength. The eye has a -very low sensitivity to wavelengths below 400 nm or so, and to wavelengths above -700 nm or so. Thus, the resulting color in the spectrum plot is nearly -black. With a wavelength of 550 nm, on the other hand, the eye is quite -sensitive to this wavelength, and the resulting color is therefore bright -(green). This way of drawing the spectrum is intended to help show the -contributions of each wavelength in the spectrum to the overall color. For -example, in this case, there is a considerable amount of power at wavelengths -from 700 nm to 830 nm. However, the eye has a low sensitivity to these, +So let's look at some results of these calculations, which also introduce a new +type of ColorPy plot, the spectrum plot. First, consider a +blackbody with a temperature of 5778 K. This is the surface temperature of +the Sun [Wikipedia], and this spectrum approximates that of the sun. Figure 5 below +shows both the overall color of the resulting spectrum, with a graph of the +spectrum itself below the color patch. The overall color is nearly white. +The spectrum shows that the peak intensity is in the green region, around 500 nm +or so, but with significant contributions from both lower and higher +wavelengths. The colors in the spectrum plot are indicate the extent to +which the eye is sensitive to the particular wavelength. Each color band +has the same amount of luminance, so the apparent brightness of the color indicates the +extent to which the eye is sensitive to that wavelength. The eye has a +very low sensitivity to wavelengths below 400 nm or so, and to wavelengths above +700 nm or so. Thus, the resulting color in the spectrum plot is nearly +black. With a wavelength of 550 nm, on the other hand, the eye is quite +sensitive to this wavelength, and the resulting color is therefore bright +(green). This way of drawing the spectrum is intended to help show the +contributions of each wavelength in the spectrum to the overall color. For +example, in this case, there is a considerable amount of power at wavelengths +from 700 nm to 830 nm. However, the eye has a low sensitivity to these, and so these wavelengths do not contribute much to the overall color.
-These kinds of plots are made with colorpy.plots.spectrum_plot (spectrum, title, -filename, xlabel, ylabel), where spectrum is the numpy array with the spectrum +These kinds of plots are made with colorpy.plots.spectrum_plot (spectrum, title, +filename, xlabel, ylabel), where spectrum is the numpy array with the spectrum data, and the other parameters are the title, filename, and axis labels.
-Figure 5 - Color of a 5778 K blackbody. This approximates the spectrum of
+Figure 5 - Color of a 5778 K blackbody. This approximates the spectrum of
the Sun.
-Since the temperature is just a parameter to the function calls, we can do the -same thing for any other temperature that we like. The nearby star Proxima -Centauri is much cooler than the sun, it has a surface temperature of about 3000 -K. -[Wikipedia]. The spectrum of a 3000 K blackbody is shown below, -with the same kind of plot. -The overall color in this case is orange, and the spectrum is concentrated at +Since the temperature is just a parameter to the function calls, we can do the +same thing for any other temperature that we like. The nearby star Proxima +Centauri is much cooler than the sun, it has a surface temperature of about 3000 +K. +[Wikipedia]. The spectrum of a 3000 K blackbody is shown below, +with the same kind of plot. +The overall color in this case is orange, and the spectrum is concentrated at longer wavelengths.
Figure 6 - Spectrum of 3000 K blackbody. This approximates the spectrum of Proxima Centauri.
-We can also evaluate this at higher temperatures. The star Rigel, in the -constellation Orion, has a -surface temperature of 11000 K [Wikipedia], and the resulting blackbody spectrum is shown -below. The overall color is now blue-white, and the intensity is +We can also evaluate this at higher temperatures. The star Rigel, in the +constellation Orion, has a +surface temperature of 11000 K [Wikipedia], and the resulting blackbody spectrum is shown +below. The overall color is now blue-white, and the intensity is concentrated at low wavelengths.
-Figure 7 - Spectrum of 11000 K blackbody. This approximates the spectrum
+Figure 7 - Spectrum of 11000 K blackbody. This approximates the spectrum
of the star Rigel.
-But why limit ourselves to just a handful of temperatures? We can -calculate the blackbody spectrum for very many temperatures, and ColorPy allows -us to plot the resulting color vs. temperature, while also showing a plot of the -rgb color values. In the figure below, we have calculated the blackbody -color for temperatures ranging from 1200 K to 16000 K. The top subplot -shows the resulting color, as a function of temperature, while the lower plot +But why limit ourselves to just a handful of temperatures? We can +calculate the blackbody spectrum for very many temperatures, and ColorPy allows +us to plot the resulting color vs. temperature, while also showing a plot of the +rgb color values. In the figure below, we have calculated the blackbody +color for temperatures ranging from 1200 K to 16000 K. The top subplot +shows the resulting color, as a function of temperature, while the lower plot shows the linear RGB values. -For low temperatures, the blackbody is red to orange, and as the temperature +For low temperatures, the blackbody is red to orange, and as the temperature increases, the color becomes white, and then blue.
-This style plot was generated with colorpy.plots.color_vs_param_plot (param_list,
-rgb_colors, title, filename, tight, plotfunc, xlabel, ylabel). The
-arguments tight, plotfunc, xlabel and ylabel are optional, in this case we set
-tight=True and plotfunc=pylab.semilogy to obtain the semilog plot, which is
+This style plot was generated with colorpy.plots.color_vs_param_plot (param_list,
+rgb_colors, title, filename, tight, plotfunc, xlabel, ylabel). The
+arguments tight, plotfunc, xlabel and ylabel are optional, in this case we set
+tight=True and plotfunc=pylab.semilogy to obtain the semilog plot, which is
needed for the very large range of color values covered.
-Let's also consider similar plots over different temperature ranges. -First, consider the range 950 K to 1200 K, shown below. The colors are all +Let's also consider similar plots over different temperature ranges. +First, consider the range 950 K to 1200 K, shown below. The colors are all nearly red, and are brighter for higher temperatures.
-This provides another example to discuss the color intensity scaling that ColorPy -uses. ColorPy attempts to calculate a color value that, when displayed on a +This provides another example to discuss the color intensity scaling that ColorPy +uses. ColorPy attempts to calculate a color value that, when displayed on a computer monitor, will match the physical brightness of the spectrum. -In this plot, the red value reaches 1.0 at about 1150 K. This means -(approximately) that an 1150 K blackbody is as bright as the monitor at full -intensity. Similarly, a 950 K blackbody is about 1.5% as bright as the -monitor. This, by the way, suggests an experimental test of whether the -display brightness assumed by ColorPy is correct - a 1150 K blackbody is +In this plot, the red value reaches 1.0 at about 1150 K. This means +(approximately) that an 1150 K blackbody is as bright as the monitor at full +intensity. Similarly, a 950 K blackbody is about 1.5% as bright as the +monitor. This, by the way, suggests an experimental test of whether the +display brightness assumed by ColorPy is correct - a 1150 K blackbody is predicted to be as bright as the display monitor at full red.
-Also notice that there is no blue line on this plot. For temperatures as -low as 1200 K (and all lower), the correct blue value is negative. (This -means that the monitor is not capable of displaying the color at full saturation -- it is necessarily washed out.) Since negative values cannot be plotted -on a log axis, there is no blue on this plot. The green value also becomes +Also notice that there is no blue line on this plot. For temperatures as +low as 1200 K (and all lower), the correct blue value is negative. (This +means that the monitor is not capable of displaying the color at full saturation +- it is necessarily washed out.) Since negative values cannot be plotted +on a log axis, there is no blue on this plot. The green value also becomes negative near the left edge of the plot.
Figure 9 - Colors of relatively cool blackbodies.
-Now consider a high temperature range, 10000 K to 40000 K, shown in the plot -below. In this situation, the colors are far in excess of that -displayable on the monitor. A 40000 K blackbody is far far brighter than -any computer monitor! In fact, the intensity is on the order of 109 times -that of what the monitor can display. When displaying very bright colors -such as this, ColorPy will scale them to the brightest color, of the same hue -and saturation, that can be displayed. In this case, where all the colors -are scaled like this, the resulting RGB values all have similar brightness. -So, all the colors on this chart are of similar brightness. However, there -is actually a large range in physical brightness - a 40000 K blackbody is much -brighter than a 10000 K blackbody. Note how this contrasts with the -situation on the cool blackbody plot, where the intensity of the displayed -colors varied with temperature. There is a physical variation in intensity -in both cases, but ColorPy can only show this when the colors are in the range +Now consider a high temperature range, 10000 K to 40000 K, shown in the plot +below. In this situation, the colors are far in excess of that +displayable on the monitor. A 40000 K blackbody is far far brighter than +any computer monitor! In fact, the intensity is on the order of 109 times +that of what the monitor can display. When displaying very bright colors +such as this, ColorPy will scale them to the brightest color, of the same hue +and saturation, that can be displayed. In this case, where all the colors +are scaled like this, the resulting RGB values all have similar brightness. +So, all the colors on this chart are of similar brightness. However, there +is actually a large range in physical brightness - a 40000 K blackbody is much +brighter than a 10000 K blackbody. Note how this contrasts with the +situation on the cool blackbody plot, where the intensity of the displayed +colors varied with temperature. There is a physical variation in intensity +in both cases, but ColorPy can only show this when the colors are in the range displayable by the monitor.
@@ -589,61 +589,61 @@
-For a second example, consider Rayleigh scattering. This concerns the -scattering of light by small particles (much smaller than the wavelength of the -light.) In this situation, the amount of scattering is inversely -proportional to the fourth power of the wavelength. To compute the -spectrum resulting from Rayleigh scattering, we need a description of the -original light source, the 'Illuminant', as the power emitted as a function of -wavelength. Then, the spectrum resulting from Rayleigh scattering is [van de Hulst, +For a second example, consider Rayleigh scattering. This concerns the +scattering of light by small particles (much smaller than the wavelength of the +light.) In this situation, the amount of scattering is inversely +proportional to the fourth power of the wavelength. To compute the +spectrum resulting from Rayleigh scattering, we need a description of the +original light source, the 'Illuminant', as the power emitted as a function of +wavelength. Then, the spectrum resulting from Rayleigh scattering is [van de Hulst, p. 65]:
Scattered Intensity (λ) = Illuminant (λ) * a * (1 / λ4)
-where a is a proportionality constant. (In the plots below, we have -arbitrarily taken the constant a so that the value of the Rayleigh scattering +where a is a proportionality constant. (In the plots below, we have +arbitrarily taken the constant a so that the value of the Rayleigh scattering term at 550 nm is 1.0.)
-Often, the intensity of the illuminant is arbitrary. The illuminants -provided by ColorPy are scaled so that they have Y = 1.0, which means that they -are about as bright as the monitor at full white. One obvious choice for -an illuminant is a blackbody. ColorPy will provide an illuminant for a -blackbody of any temperature you like, with colorpy.illuminants.get_blackbody_illuminant (T_K). -(Remember that these illuminants are scaled to have a Y = 1.0, rather than the +Often, the intensity of the illuminant is arbitrary. The illuminants +provided by ColorPy are scaled so that they have Y = 1.0, which means that they +are about as bright as the monitor at full white. One obvious choice for +an illuminant is a blackbody. ColorPy will provide an illuminant for a +blackbody of any temperature you like, with colorpy.illuminants.get_blackbody_illuminant (T_K). +(Remember that these illuminants are scaled to have a Y = 1.0, rather than the (typically) much larger brightness of a true blackbody.)
-A familiar illuminant is the one for a 5778 K blackbody, which approximates the -illumination of the Sun. The plot below shows the effect of Rayleigh -scattering of this illuminant. The overall color is sky blue, with the -spectrum concentrated at low wavelengths. This result is as expected, as -Rayleigh scattering is the basic physical +A familiar illuminant is the one for a 5778 K blackbody, which approximates the +illumination of the Sun. The plot below shows the effect of Rayleigh +scattering of this illuminant. The overall color is sky blue, with the +spectrum concentrated at low wavelengths. This result is as expected, as +Rayleigh scattering is the basic physical reason that the sky is blue.
Figure 11 - Rayleigh scattering of a 5778 K blackbody. Why the sky is blue.
-But why limit ourselves to the color of our Sun? With ColorPy, we can do -the same calculations for a blackbody of any temperature we like. So we -repeat the process, first for a temperature of 3000 K (Proxima Centauri), and -then for a temperature of 11000 K (Rigel). The plots below show the -results. If we lived on a planet around Proxima Centauri, the sky would be -nearly white. On the other hand, if we lived on a planet near Rigel, the +But why limit ourselves to the color of our Sun? With ColorPy, we can do +the same calculations for a blackbody of any temperature we like. So we +repeat the process, first for a temperature of 3000 K (Proxima Centauri), and +then for a temperature of 11000 K (Rigel). The plots below show the +results. If we lived on a planet around Proxima Centauri, the sky would be +nearly white. On the other hand, if we lived on a planet near Rigel, the sky would also be blue, but a deeper shade of blue than we have on Earth.
-Figure 12 - Rayleigh scattering of a 3000 K blackbody. The color of the sky
+Figure 12 - Rayleigh scattering of a 3000 K blackbody. The color of the sky
from near Proxima Centauri.
-Figure 13 - Rayleigh scattering of a 11000 K blackbody. The color of the
+Figure 13 - Rayleigh scattering of a 11000 K blackbody. The color of the
sky from near Rigel.
-We can also make a plot for many temperatures, and get a plot of the color of -the sky vs. blackbody illuminant temperature, similar to the plot we made of the -color of the blackbody itself vs. temperature. The range of sky colors is -about the same as the range of blackbody colors, but the sky colors are bluer +We can also make a plot for many temperatures, and get a plot of the color of +the sky vs. blackbody illuminant temperature, similar to the plot we made of the +color of the blackbody itself vs. temperature. The range of sky colors is +about the same as the range of blackbody colors, but the sky colors are bluer than the blackbody colors, for any given temperature.
@@ -656,17 +656,17 @@
ColorPy provides several different 'illuminant' functions that can be used as -light sources. In addition to the blackbody illuminants, ColorPy provides -the CIE standard Illuminant D65. Illuminant D65 is intended to provide a -good approximation for normal daylight. Illuminant D65 is recommended as -the general-purpose, default, illumination for daytime conditions (on Earth only +
ColorPy provides several different 'illuminant' functions that can be used as +light sources. In addition to the blackbody illuminants, ColorPy provides +the CIE standard Illuminant D65. Illuminant D65 is intended to provide a +good approximation for normal daylight. Illuminant D65 is recommended as +the general-purpose, default, illumination for daytime conditions (on Earth only however!)
-Illuminant D65 is given by a table of intensity vs. wavelength, rather than -a mathematical formula. ColorPy provides this illuminant, normalized so -that Y = 1.0, as usual. The spectrum of D65 is shown below. Note -that the overall color is white. In fact, D65 is used as the white point -for determining the xyz to rgb conversion matrix, so D65 is in fact the very +
Illuminant D65 is given by a table of intensity vs. wavelength, rather than +a mathematical formula. ColorPy provides this illuminant, normalized so +that Y = 1.0, as usual. The spectrum of D65 is shown below. Note +that the overall color is white. In fact, D65 is used as the white point +for determining the xyz to rgb conversion matrix, so D65 is in fact the very definition of white!
@@ -678,22 +678,22 @@
-In this example, we will calculate the colors produced by interference films, -such as a soap bubble, or an oil slick on water. This time, we will use +In this example, we will calculate the colors produced by interference films, +such as a soap bubble, or an oil slick on water. This time, we will use D65 as the illuminant.
-The physical situation can be described by illumination from above, passing -through a dielectric medium with some index of refraction n1. As the wave -propagates, it meets an interface where the index of refraction changes to a new -value n2. At the interface, part of the original wave is reflected, and -part continues into the new medium. The material n2 is considered to be in -a thin layer. After passing through this thin layer, the wave meets a -second interface, where the index of refraction changes from n2 to n3. -Again, part of the incident wave is reflected from the interface, and part +The physical situation can be described by illumination from above, passing +through a dielectric medium with some index of refraction n1. As the wave +propagates, it meets an interface where the index of refraction changes to a new +value n2. At the interface, part of the original wave is reflected, and +part continues into the new medium. The material n2 is considered to be in +a thin layer. After passing through this thin layer, the wave meets a +second interface, where the index of refraction changes from n2 to n3. +Again, part of the incident wave is reflected from the interface, and part continues to propagate.
-We will assume that the regions where the index is n1 and n3 are infinite in -extent, while the region where the index is n2 is limited to a thin layer, with +We will assume that the regions where the index is n1 and n3 are infinite in +extent, while the region where the index is n2 is limited to a thin layer, with thickness t.
Some typical indices of refraction of real materials are:
@@ -703,70 +703,70 @@
-The total reflected wave from the system is a combination of the wave reflected -from the first interface (n1 to n2), and the wave reflected from the second +The total reflected wave from the system is a combination of the wave reflected +from the first interface (n1 to n2), and the wave reflected from the second interface (n2 to n3).
-There may be multiple reflections - e.g. the wave reflected from -the second interface will not fully pass through the (now reversed) interface -from n2 to n1, rather only part will pass, while some will reflect again and -head back to the interface from n2 to n3. The calculations in ColorPy +There may be multiple reflections - e.g. the wave reflected from +the second interface will not fully pass through the (now reversed) interface +from n2 to n1, rather only part will pass, while some will reflect again and +head back to the interface from n2 to n3. The calculations in ColorPy consider all numbers of multiple reflections, not just a single reflection.
-What makes the interesting colors, is that the two waves travel through a -different path length, and this results in them being out of phase. The -exact change in phase depends on both the wavelength of the light, the thickness -of the layer, and the index of refraction of the layer. Depending on the -specifics, the two waves may constructively interfere, resulting in a large -amplitude of the reflected wave, or the waves may destructively interfere, -resulting in a small (or zero) amplitude of the reflected wave, or something in +What makes the interesting colors, is that the two waves travel through a +different path length, and this results in them being out of phase. The +exact change in phase depends on both the wavelength of the light, the thickness +of the layer, and the index of refraction of the layer. Depending on the +specifics, the two waves may constructively interfere, resulting in a large +amplitude of the reflected wave, or the waves may destructively interfere, +resulting in a small (or zero) amplitude of the reflected wave, or something in between.
-Whether the interference is constructive or destructive depends on the -wavelength, and for thin films, part of the spectrum will be reduced from -destructive interference, and part enhanced from constructive interference, +Whether the interference is constructive or destructive depends on the +wavelength, and for thin films, part of the spectrum will be reduced from +destructive interference, and part enhanced from constructive interference, resulting in a significant color shift.
-First, consider a soap bubble. In this situation, material 1 is air, -material 2 is a solution of soap in water, while material 3 is air again. -(The inside of the bubble.) So, n1 = 1.003, n2 = 1.33, and n3 = 1.003. -Calculating the color of the total reflection, with an illuminant of D65, as a -function of the thickness of the layer, results in the plot below. Note +First, consider a soap bubble. In this situation, material 1 is air, +material 2 is a solution of soap in water, while material 3 is air again. +(The inside of the bubble.) So, n1 = 1.003, n2 = 1.33, and n3 = 1.003. +Calculating the color of the total reflection, with an illuminant of D65, as a +function of the thickness of the layer, results in the plot below. Note that the RGB components oscillate as the thickness is varied.
-The phase relationship between the red, green and blue components affects the +The phase relationship between the red, green and blue components affects the resulting color.
-For the most -part, these stay within the displayable range of 0.0 to 1.0, but there are a few -places where the red component becomes negative, in the most vivid green -regions. These vivid green colors cannot be properly displayed on the -monitor, the true color is more saturated than what is shown. Most of the +For the most +part, these stay within the displayable range of 0.0 to 1.0, but there are a few +places where the red component becomes negative, in the most vivid green +regions. These vivid green colors cannot be properly displayed on the +monitor, the true color is more saturated than what is shown. Most of the other colors can be displayed properly.
Figure 16 - Color of reflections from a soap bubble. The illuminant is D65.
-For a particular thickness of the film, we can plot the reflectance spectrum. -The plots below show the spectrum for some of the particularly vivid colors, for -thicknesses (not wavelengths!) of 400 nm and 500 nm. For these plots, we -used an illuminant that is constant over wavelength, rather than D65. The -only reason is to avoid the jaggedness of the D65 curve from making the plot +For a particular thickness of the film, we can plot the reflectance spectrum. +The plots below show the spectrum for some of the particularly vivid colors, for +thicknesses (not wavelengths!) of 400 nm and 500 nm. For these plots, we +used an illuminant that is constant over wavelength, rather than D65. The +only reason is to avoid the jaggedness of the D65 curve from making the plot more confusing.
-Figure 17 - Spectrum of soap bubble reflection for a thickness of 400 nm.
+Figure 17 - Spectrum of soap bubble reflection for a thickness of 400 nm.
The illuminant is constant over wavelength.
-Figure 18 - Spectrum of soap bubble reflection for a thickness of 500 nm.
+Figure 18 - Spectrum of soap bubble reflection for a thickness of 500 nm.
The illuminant is constant over wavelength.
-We can do the same thing for an oil slick floating on water. In this case, -medium 1 is air (n = 1.003), medium 2 is oil (n = 1.44), and medium 3 is water -(n = 1.33). The result is shown below. Note that the colors are not -as saturated and vivid as for the reflection from a soap bubble. Since -these colors are less saturated than the soap bubble reflections, all of them +We can do the same thing for an oil slick floating on water. In this case, +medium 1 is air (n = 1.003), medium 2 is oil (n = 1.44), and medium 3 is water +(n = 1.33). The result is shown below. Note that the colors are not +as saturated and vivid as for the reflection from a soap bubble. Since +these colors are less saturated than the soap bubble reflections, all of them are properly displayable on the computer.
@@ -779,44 +779,44 @@
-ColorPy also provides some color manipulation functions, that are not directly -related to physical color calculations. The most important of these are -routines to convert colors into the nearly perceptually uniform color spaces Luv +ColorPy also provides some color manipulation functions, that are not directly +related to physical color calculations. The most important of these are +routines to convert colors into the nearly perceptually uniform color spaces Luv and Lab.
-The common color spaces rgb and xyz are not very perceptually uniform. -This means that, if you calculate the distance between two colors +The common color spaces rgb and xyz are not very perceptually uniform. +This means that, if you calculate the distance between two colors C1 and C2, using the usual Euclidean metric, namely:
-Distance = √ ((Red2 - Red1)2 + -(Green2 - Green1)2 + +Distance = √ ((Red2 - Red1)2 + +(Green2 - Green1)2 + (Blue2 - Blue1)2)
-the calculated distance values do not agree well with the psychological apparent -differences in the colors. I.e., the mind may see two colors as very -different, but where they have a small distance, or alternately the mind may see -two colors as similar, but where they have a large color distance. This -causes difficulties in calculating the 'closest color'. (Since xyz and rgb -values are linearly related, the same distance issues apply to both of those +the calculated distance values do not agree well with the psychological apparent +differences in the colors. I.e., the mind may see two colors as very +different, but where they have a small distance, or alternately the mind may see +two colors as similar, but where they have a large color distance. This +causes difficulties in calculating the 'closest color'. (Since xyz and rgb +values are linearly related, the same distance issues apply to both of those spaces.)
-The color spaces Luv and Lab are designed to be a much more perceptually uniform -color space than rgb or xyz. If colors are transformed into Luv or Lab -space, then mathematical distance calculations, using the Euclidean metric, on -Luv and Lab values will provide a much better assessment of how different the +The color spaces Luv and Lab are designed to be a much more perceptually uniform +color space than rgb or xyz. If colors are transformed into Luv or Lab +space, then mathematical distance calculations, using the Euclidean metric, on +Luv and Lab values will provide a much better assessment of how different the colors appear.
-These perceptually uniform color spaces are not perfect. (The fact -that there are two of them, is a clear sign that they are imperfect.) -However, they should do a substantially better job in measuring the apparent +These perceptually uniform color spaces are not perfect. (The fact +that there are two of them, is a clear sign that they are imperfect.) +However, they should do a substantially better job in measuring the apparent differences in colors.
-ColorPy provides routines to transform color values from xyz into both Luv and -Lab, and also routines to convert back to xyz. Coupled with the -conversions between xyz and rgb, one can convert between any of the desired -color spaces. Most descriptions of the Luv and Lab models only provide the -transformation from xyz to Luv and Lab, but ColorPy also provides the inverses. +ColorPy provides routines to transform color values from xyz into both Luv and +Lab, and also routines to convert back to xyz. Coupled with the +conversions between xyz and rgb, one can convert between any of the desired +color spaces. Most descriptions of the Luv and Lab models only provide the +transformation from xyz to Luv and Lab, but ColorPy also provides the inverses. The conversion routines are:
colorpy.colormodels.luv_from_xyz (xyz)
@@ -824,19 +824,19 @@
-The Luv and Lab conversions depend on the definition of the white point. -By default, the white point used in specifying the rgb to xyz conversions is -used, this is CIE D65 by default. You can change this, by passing the +The Luv and Lab conversions depend on the definition of the white point. +By default, the white point used in specifying the rgb to xyz conversions is +used, this is CIE D65 by default. You can change this, by passing the desired chromaticity of the white point, to:
colorpy.colormodels.init_Luv_Lab_white_point (white_point)
-One potential application of these spaces, would be an improvement in the -color clipping algorithm. When ColorPy needs to clip an undisplayable -color value (with rgb values either negative or greater than 1.0), the best -action would probably be to choose the displayable color that is perceptually -closest to the desired color. If this was done with these color spaces, -rather than the current clipping algorithm, a better selection of out-of-gamut +One potential application of these spaces, would be an improvement in the +color clipping algorithm. When ColorPy needs to clip an undisplayable +color value (with rgb values either negative or greater than 1.0), the best +action would probably be to choose the displayable color that is perceptually +closest to the desired color. If this was done with these color spaces, +rather than the current clipping algorithm, a better selection of out-of-gamut colors might result.
-If you are installing from the Windows binary distribution, all you need to do -is double-click the executable, and follow the installation prompts. +If you are installing from the Windows binary distribution, all you need to do +is double-click the executable, and follow the installation prompts. Otherwise, you must first unpack the distribution, and then install.
@@ -868,9 +868,9 @@
Windows -
-Unzip the .zip distribution. Recent versions of Windows (XP or later), will
-unpack the directory automatically, you can simply enter the directory in
-Windows Explorer. You will probably need to copy the uncompressed files into
+Unzip the .zip distribution. Recent versions of Windows (XP or later), will
+unpack the directory automatically, you can simply enter the directory in
+Windows Explorer. You will probably need to copy the uncompressed files into
another directory.
@@ -888,13 +888,13 @@
-It is possible that you may need to supply a path to the Python executable. +It is possible that you may need to supply a path to the Python executable. You will probably need administrator privileges to do this. This should complete the installation.
-After downloading and installing, I recommend that you run the test cases, and -then create the sample figures. These will provide a check that the module +After downloading and installing, I recommend that you run the test cases, and +then create the sample figures. These will provide a check that the module is working correctly.
@@ -906,7 +906,7 @@
-This will generate the sample figures (typically .png files), including all +This will generate the sample figures (typically .png files), including all those in this documentation, as well as several others.
-colorpy.ciexyz.py - Spectral response curves for 1931 CIE XYZ 2 degree field of view
+colorpy.ciexyz.py - Spectral response curves for 1931 CIE XYZ 2 degree field of view
matching functions.
Description:
This module provides the CIE standard XYZ color matching functions.
-The 1931 tabulation, for a 2 degree field of view, is used in preference to the
+The 1931 tabulation, for a 2 degree field of view, is used in preference to the
10 degree 1964 set,
as is conventional in computer graphics.
-The matching functions are stored internally at 1 nm increments, and linear
+The matching functions are stored internally at 1 nm increments, and linear
interpolation is
used for any wavelength in between.
ColorPy attempts to scale the matching functions so that:
-A spectrum, constant with wavelength, over the range 360 nm to 830 nm, with a
+A spectrum, constant with wavelength, over the range 360 nm to 830 nm, with a
total intensity
-equal to the (assumed) physical intensity of the monitor, will sample with Y =
+equal to the (assumed) physical intensity of the monitor, will sample with Y =
1.0.
-This scaling corresponds with that in colormodels.py, which assumes Y = 1.0 at
+This scaling corresponds with that in colormodels.py, which assumes Y = 1.0 at
full white.
-NOTE - I suspect that the scaling is not quite correct. I think it is at least
+NOTE - I suspect that the scaling is not quite correct. I think it is at least
close.
-Ideally, we would like the spectrum of the actual monitor display, at full
+Ideally, we would like the spectrum of the actual monitor display, at full
white, which is not
independent of wavelength, to sample to Y = 1.0.
Constants and Functions:
-start_wl_nm, end_wl_nm - Default starting and ending range of wavelengths, in
+start_wl_nm, end_wl_nm - Default starting and ending range of wavelengths, in
nm, as integers.
delta_wl_nm - Default wavelength spacing, in nm, as a float.
-DEFAULT_DISPLAY_INTENSITY - Default assumed intensity of monitor display, in
+DEFAULT_DISPLAY_INTENSITY - Default assumed intensity of monitor display, in
W/m^2
init (monitor_intensity = DEFAULT_DISPLAY_INTENSITY)
- Initialization of color matching curves. Called at module startup with default
+ Initialization of color matching curves. Called at module startup with default
arguments.
This can be called again to change the assumed display intensity.
@@ -1214,7 +1214,7 @@
References:
-Wyszecki and Stiles, Color Science: Concepts and Methods, Quantitative Data and
+Wyszecki and Stiles, Color Science: Concepts and Methods, Quantitative Data and
Formulae,
-2nd edition, John Wiley, 1982. Wiley Classics Library Edition 2000. ISBN
+2nd edition, John Wiley, 1982. Wiley Classics Library Edition 2000. ISBN
0-471-39918-3.
-CVRL Color and Vision Database - http://cvrl.ioo.ucl.ac.uk/index.htm - (accessed
+CVRL Color and Vision Database - http://cvrl.ioo.ucl.ac.uk/index.htm - (accessed
17 Sep 2008)
Color and Vision Research Laboratories.
Provides a set of data sets related to color vision.
@@ -1259,15 +1259,15 @@
-ColorPy is certainly not perfect. Some of the problems it likely has, and +ColorPy is certainly not perfect. Some of the problems it likely has, and so possible avenues for future improvements, are as follows:
-I am not sure that the (assumed) physical luminance of the monitor display is -correct. I am pretty sure that it is close, but things are confusing -enough that this may not be quite right. In most cases, this should not -matter, as the intensities are typically scaled by an arbitrary factor. In -any case, attempting to scale to the physical display brightness might not be -the best course of action in many cases. For example, for a plot of very -bright colors (such as hot blackbodies), all the colors are clamped to the +I am not sure that the (assumed) physical luminance of the monitor display is +correct. I am pretty sure that it is close, but things are confusing +enough that this may not be quite right. In most cases, this should not +matter, as the intensities are typically scaled by an arbitrary factor. In +any case, attempting to scale to the physical display brightness might not be +the best course of action in many cases. For example, for a plot of very +bright colors (such as hot blackbodies), all the colors are clamped to the maximum brightness, when there is a large range of luminance in the data.
-There are many places where the Python code is not well 'vectorized', that is, a -single Python call might be able to operate on an entire spectrum, for example, -but the current code requires a Python call for each wavelength. This will -certainly degrade performance, and ColorPy is definitely slower than the +There are many places where the Python code is not well 'vectorized', that is, a +single Python call might be able to operate on an entire spectrum, for example, +but the current code requires a Python call for each wavelength. This will +certainly degrade performance, and ColorPy is definitely slower than the original C++ code. Still, I think the performance is acceptable.
-The default gamma correction may not be ideal for LCD displays. As these -are getting more and more common, this is becoming the most important case, +The default gamma correction may not be ideal for LCD displays. As these +are getting more and more common, this is becoming the most important case, rather than CRT displays.
-The color clipping method can probably be improved, it is likely that the Luv +The color clipping method can probably be improved, it is likely that the Luv and Lab color models could help with this.
-There are some standard illuminants (especially D55 and D75) that would be nice +There are some standard illuminants (especially D55 and D75) that would be nice to add.
-ColorPy does not have support for HSV (Hue-Saturation-Value) and HLS -(Hue-Lightness-Saturation) color models. These are not particularly -relevant for physically based color modeling, but they are reasonably common in +ColorPy does not have support for HSV (Hue-Saturation-Value) and HLS +(Hue-Lightness-Saturation) color models. These are not particularly +relevant for physically based color modeling, but they are reasonably common in the computing world, and it would be nice to add this support for that reason.
-There are also surely bugs that I have not found, and also things that could be +There are also surely bugs that I have not found, and also things that could be more conveniently arranged for many uses.
@@ -1796,7 +1796,7 @@Wyszecki - Wyszecki and Stiles, Color Science: Concepts and Methods, Quantitative Data and Formulae, 2nd edition, John Wiley, 1982. Wiley Classics Library Edition 2000. ISBN 0-471-39918-3.
-Judd - Judd and Wyszecki, Color in Business, Science and +
Judd - Judd and Wyszecki, Color in Business, Science and Industry, 1975.
Waves - Frank S. Crawford, Jr., Waves: Berkeley Physics Course - Volume 3, @@ -1808,14 +1808,14 @@
Minnaert - M. Minnaert, The nature of light and color in the open air, translation H.M. Kremer-Priest, Dover Publications, New York, 1954. ISBN 486-20196-1.
-Kasson - Kasson and Plouffe, An Analysis of Selected Computer Interchange Color +
Kasson - Kasson and Plouffe, An Analysis of Selected Computer Interchange Color Spaces, ACM Transactions on Graphics, Vol. 11, No. 4, October 1992.
Poynton - Frequently asked questions about Gamma and Color, posted to comp.graphics.algorithms, 25 Jan 1995.
-sRGB - http://www.color.org/sRGB.xalter -
+sRGB - http://www.color.org/sRGB.xalter -
(accessed 15 Sep 2008)
'A Standard Default Color Space for the Internet: sRGB'.,
Michael Stokes (Hewlett-Packard), Matthew Anderson (Microsoft),
@@ -1824,16 +1824,16 @@
-CVRL Color and Vision Database -
+CVRL Color and Vision Database -
http://cvrl.ioo.ucl.ac.uk/index.htm - (accessed 17 Sep 2008)
Color and Vision Research Laboratories.
Provides a set of data sets related to color vision.
ColorPy uses the tables from this site for the 1931 CIE XYZ matching functions,
and for Illuminant D65, both at 1 nm wavelength increments.
-CIE Standards maintained by CVRL -
+CIE Standards maintained by CVRL -
http://cvrl.ioo.ucl.ac.uk/cie.htm - (accessed 17 Sep 2008)
The 1931 CIE XYZ and D65 tables that ColorPy uses were obtained from the following files, linked here:
http://cvrl.ioo.ucl.ac.uk/database/data/cmfs/ciexyz31_1.txt
@@ -1841,7 +1841,7 @@
-CIE International Commission on Illumination -
+CIE International Commission on Illumination -
http://www.cie.co.at/ - (accessed 17 Sep 2008)
Official website of the CIE.
There are tables of the standard functions (matching functions, illuminants) here:
@@ -1858,7 +1858,7 @@
Williams et. al. - +
Williams et. al. -
Darren L. Williams et. al., 'Beyond lambda-max: Transforming Visible Spectra into 24-bit Color Values'.
Journal of Chemical Education, Vol 84, No 11, Nov 2007, p1873-1877.
A student laboratory experiment to measure the transmission spectra of some common chemical solutions,
@@ -1867,4 +1867,4 @@