John Dolese

125 Reputation

4 Badges

10 years, 156 days

MaplePrimes Activity


These are Posts that have been published by John Dolese

CameraProfiler.mw

D700_Profile.xlsx

This Application uses the xrite color checker to obtain Forward and Color Matrices as defined by Adobe.

It compares the camera gamut based on the above matrices to the 1931 Standard Observer using LEDs.

 

 

 

The CIELAB perceptual model of human vision can be used to predict the color of a wavelength in sRGB. I found the CIEDE2000 corrections to hue, chroma, and lighness were required to get the right values in the blue to violet region. The CIEDE2000 results seem pretty close to what I see looking through a diffraction grating considering I have no way of knowing how my eyes are adapted.

Use the new, faster  version. It can run in under 1 minute;

nprocWavelength_Color_CIEDE2000.mw   and  WtoC_data.xlsx

You will need the data for the color matching functions in the WtoC Excel File. The data directory can now be controlled in the mw file.

 

 Obtain the tri-stimulus XYZ values from the CIE Color matching functions.

 Show the gamut of maximum chroma for the standard observer model with a D65 Illuminant.

 Approximate the white point of a Planckian source and compare to D65.

 Translate the maximum chroma gamut in xy to Lab (CIE L*a*b*) for perceived gamut (Violet and Magenta come together)

 Map the RGB color cube of fully saturated color into Lab and compare to perceivable colors.

10/6/15  Initial Document

•12/28/15 Improve RGB gamut with more data points: Procedures added for RGB to Lab: Wavlength Colors now based on CIEDE2000 model for Lab.                   

 

 Here is the latest version of this document, the MSL_data must be in a directory set in the mw file;

MSL_data.xlsx    Vision_RGB_Gamut.mw

This application calculates the number of photons reaching a camera sensor for a given exposure. A blackbody model of the sun is generated. The "Sunny 16" rule for exposure is demonstrated. Calculations are done using units.Photon_Exposure_Array.mw

Photon ExposureNULLNULL

Blackbody Model of the Sun

    h := Units:-Standard:-`*`(Units:-Standard:-`*`(0.6626069e-33, Units:-Standard:-`^`(Unit('m'), 2)), Units:-Standard:-`*`(Unit('kg'), Units:-Standard:-`/`(Unit('s')))): 

Plank Constant       

  kb := Units:-Standard:-`*`(Units:-Standard:-`*`(0.1380650e-22, Units:-Standard:-`*`(Units:-Standard:-`^`(Unit('m'), 2), Units:-Standard:-`/`(Units:-Standard:-`^`(Unit('s'), 2)))), Units:-Standard:-`*`(Unit('kg'), Units:-Standard:-`/`(Unit('K')))): 

Boltzman Constant  

c := Units:-Standard:-`*`(0.2997925e9, Units:-Standard:-`*`(Unit('m'), Units:-Standard:-`/`(Unit('s')))):  ``

Light Speed

Rsun := Units:-Standard:-`*`(Units:-Standard:-`*`(6.955, Units:-Standard:-`^`(10, 8)), Unit('m')): ``

Sun Radius  

Re_orb := Units:-Standard:-`*`(Units:-Standard:-`*`(1.496, Units:-Standard:-`^`(10, 11)), Unit('m')): ``

Earth Orbit

Tsun := Units:-Standard:-`*`(5800, Unit('K')): ``

Sun Color Temperature     

 tf_atm := .718: 

Transmission Factor  

 

Sun: Spectral Radiant Exitance to Earth: Spectral Irradiance                   

  "M(lambda):=(2*Pi*h*c^(2))/((lambda)^(5))*1/((e)^((h*c)/(lambda*kb*Tsun))-1)*(Rsun/(Re_orb))^(2)*tf_atm:" NULL

evalf(M(Units:-Standard:-`*`(555, Unit('nm')))) = 1277414308.*Units:-Unit(('kg')/(('m')*('s')^3))"(->)"1.277414308*Units:-Unit(('W')/(('nm')*('m')^2))NULL

Photopic Relative Response VP vs λ

 

csvFile := FileTools[Filename]("/VPhotopic.csv")NULL = "VPhotopic.csv"NULL

VPdata := ImportMatrix(csvFile) = Vector(4, {(1) = ` 471 x 2 `*Matrix, (2) = `Data Type: `*float[8], (3) = `Storage: `*rectangular, (4) = `Order: `*Fortran_order})NULLNULL

 

`λP` := [seq(1 .. 4000)]:

VP := ArrayInterpolation(VPdata, `λP`):             (ArrayInterpolation for x,y data VPdata returns y' for new x data lambdaP)

NULLVParray := [`$`([`λP`[n], VP[n]], n = 1 .. 4000)]:                     

Mearth := [`$`([n, Units:-Standard:-`*`(Units:-Standard:-`*`(M(Units:-Standard:-`*`(n, Unit('nm'))), Unit('nm')), Units:-Standard:-`*`(Units:-Standard:-`^`(Unit('s'), 3), Units:-Standard:-`/`(Unit('kg'))))], n = 1 .. 4000)]:````

``

dualaxisplot(plot([Mearth], lambda = 300 .. 900, style = line, color = [blue], labels = ["λ (nm)", "M (W/nm m^2)"], title = "Spectral Radiant Exitance of the Sun", titlefont = ["ARIAL", 15], legend = [Exitance], size = [800, 300]), plot([VParray], style = line, color = [green], labels = ["λ (nm)", "Relative Response"], legend = [Units:-Standard:-`*`(Units:-Standard:-`*`(Photopic, Relative), Response)]))

 

``

 

 

 

Illuminance in Radiometric and Photometric Units:

E__r := sum(Units:-Standard:-`*`(M(Units:-Standard:-`*`(lambda, Unit('nm'))), Unit('nm')), lambda = 200 .. 4000) = 984.7275549*Units:-Unit(('kg')/('s')^3)"(->)"984.7275549*Units:-Unit(('W')/('m')^2)NULL

NULL

E__po := Units:-Standard:-`*`(Units:-Standard:-`*`(683.002, Units:-Standard:-`*`(Unit('lm'), Units:-Standard:-`/`(Unit('W')))), sum(Units:-Standard:-`*`(Units:-Standard:-`*`(VP[lambda], M(Units:-Standard:-`*`(lambda, Unit('nm')))), Unit('nm')), lambda = 200 .. 4000)) = HFloat(91873.47376063903)*Units:-Unit('lx')NULL

Translation from Illuminance to Luminance for Reflected Light;

 

Object Reflectance          R__o:      

Object Luminance           L__po := proc (R__o) options operator, arrow; R__o*E__po/(Pi*Unit('sr')) end proc:                evalf(L__po(1)) = HFloat(29244.234968360346)*Units:-Unit(('cd')/('m')^2) 

 

Illuminance of a Camera Sensor  Eps applied for time texp determines Luminous Exposure Hp;

Ideal Illuminance is determined by the exposure time texp, effective f-number N and to a less extent the angle to the optical axis θ;

 

• 

H       Luminous Exposure

• 

Eps     Illuminance to the Camera

• 

N                                               Effective F-Number

• 

texp             Exposure Time

• 

θ        Angle to the Optical Axis    

 

E__ps_ideal = Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`*`(Pi, Units:-Standard:-`/`(4)), L__po), Units:-Standard:-`*`(Units:-Standard:-`^`(cos(theta), 4), Units:-Standard:-`/`(Units:-Standard:-`^`(N, 2)))):

H__p_ideal = Units:-Standard:-`*`(E__ps_ideal, t__exp):

 

The camera meter determines the exposure time texp to balance the object luminance, reflectance and effective f-number. It does this based on an internal constant k and the camera ISO s.

• 

s        ISO Gain (Based on saturation at 3 stops above the average scene luminance)

• 

k       Reflected Light Meter Calibration Constant      k__m := Units:-Standard:-`*`(Units:-Standard:-`*`(12.5, Unit('lx')), Unit('s')):  

                                                                                                  for Nikon, Canon and Sekonic

• 

c        Incident Light Meter Calibration Constant       c__m := Units:-Standard:-`*`(Units:-Standard:-`*`(250, Unit('lx')), Unit('s')):        

                                                                                                  for Sekonic with flat domeNULL

N^2/t__exp = `#mrow(mi("\`E__po\`"),mo("⋅"),mi("s"))`/c__m                        (Incident Light Meter)  NULL 

Units:-Standard:-`*`(Units:-Standard:-`^`(N, 2), Units:-Standard:-`/`(t__exp)) = Units:-Standard:-`*`(`#mrow(mi("\`L__po\`"),mo("⋅"),mi("s"))`, Units:-Standard:-`/`(k__m)):                        (Reflected Light Meter)

NULL

Solve for H in terms of the Camera Meter Constant k and s

 

Es = Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`*`(Pi, Units:-Standard:-`/`(4)), Lo), Units:-Standard:-`*`(Units:-Standard:-`^`(cos(theta), 4), Units:-Standard:-`/`(Units:-Standard:-`^`(N, 2)))): NULL

t = Units:-Standard:-`*`(Units:-Standard:-`*`(km, Units:-Standard:-`^`(N, 2)), Units:-Standard:-`/`(Units:-Standard:-`*`(Lo, s))):NULL

NULL

NULL

H = Es*t

H = Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`*`(Pi, Units:-Standard:-`/`(4)), Lo), Units:-Standard:-`*`(Units:-Standard:-`^`(cos(theta), 4), Units:-Standard:-`/`(Units:-Standard:-`^`(N, 2)))), Units:-Standard:-`*`(Units:-Standard:-`*`(km, Units:-Standard:-`^`(N, 2)), Units:-Standard:-`/`(Units:-Standard:-`*`(Lo, s))))"(=)"H = (1/4)*Pi*cos(theta)^4*km/sNULLNULL

 t = H/Es

t = Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`*`(Pi, Units:-Standard:-`/`(4)), Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`^`(cos(theta), 4), km), Units:-Standard:-`/`(s))), Units:-Standard:-`/`(Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`*`(Pi, Units:-Standard:-`/`(4)), Lo), Units:-Standard:-`*`(Units:-Standard:-`^`(cos(theta), 4), Units:-Standard:-`/`(Units:-Standard:-`^`(N, 2))))))"(=)"t = km*N^2/(Lo*s)NULLNULL

H__p := proc (s, theta) options operator, arrow; (1/4)*Pi*k__m*cos(theta)^4/s end proc:                                              

  evalf(H__p(100, 0)) = 0.9817477044e-1*Units:-Unit(('cd')*('s')/('m')('radius')^2)"(->)"0.9817477044e-1*Units:-Unit(('lx')*('s'))NULL

 

Note:  Meters are typically set for a scene reflectance 3 stops below 100% or 12.5%.

           

  E__ps := proc (N, R__o, theta) options operator, arrow; (1/4)*Pi*Unit('sr')*R__o*E__po*cos(theta)^4/(Pi*Unit('sr')*N^2) end proc:               

 evalf(E__ps(16, Units:-Standard:-`/`(Units:-Standard:-`^`(2, 3)), 0)) = HFloat(11.215023652421756)*Units:-Unit('lx')                                                                                                   

t__exp_ideal := proc (N, s, R__o) options operator, arrow; H__p(s, theta)/E__ps(N, R__o, theta) end proc:                                     

  evalf(t__exp_ideal(16, 100, Units:-Standard:-`/`(Units:-Standard:-`^`(2, 3)))) = HFloat(0.008753862094289947)*Units:-Unit('s') NULL NULL

 

 

Actual exposure time includes typical lens losses;

 m := Units:-Standard:-`/`(80):``

Magnification  

  T := .9:``

Lens Transmittance

 F := 1.03:``

Lens Flare

V := 1: ``

Vignetting

 

                                                  ``

Total Lens Efficiency

q := Units:-Standard:-`*`(Units:-Standard:-`*`(Units:-Standard:-`*`(T, F), V), Units:-Standard:-`^`(Units:-Standard:-`+`(1, Units:-Standard:-`-`(m)), 2)):                                      evalf(q) = .9039698438NULL

 

Replacing Eps with q*Eps we get the "Sunny 16" relation between exposure time and ISO;  NULL

t__exp := proc (N, s, R__o) options operator, arrow; H__p(s, theta)/(q*E__ps(N, R__o, theta)) end proc:NULL               evalf(t__exp(16, 100, Units:-Standard:-`/`(Units:-Standard:-`^`(2, 3)))) = HFloat(0.009683798806264942)*Units:-Unit('s')NULL

t__exp_alt := proc (N, s, R__o) options operator, arrow; k__m*N^2*Pi/(s*q*R__o*E__po) end proc:                  evalf(t__exp_alt(16, 100, Units:-Standard:-`/`(Units:-Standard:-`^`(2, 3)))) = HFloat(0.00968379880412244)*Units:-Unit('s') 

• 

The Number of Photons NP Reaching the Sensor Area A;

• 

Circle of confusion for 24x36mm "Full Frame" for 1 arcminute view at twice the diagonal:

                          A__cc := Units:-Standard:-`*`(Units:-Standard:-`*`(Pi, Units:-Standard:-`^`(Units:-Standard:-`*`(12.6, Unit('`μm`')), 2)), Units:-Standard:-`/`(4)):    

     

• 

  Sensor Bandwidth                                          Photopic Response VP

• 

  Exposure Time for Zone 5: Rscene=12.5% , Saturation in Zone 8 Rscene=100%

• 

  Camera ISO differs from Saturation ISO. Typical Saturation ISO is 2300 when the camera is set to 3200. See DxoMark.

 

NULL

The average number of photons for exposure time based on Reflectance of the scene  relative to the metered value:    

Zone 5;   R__meter := R__scene: 

NP := proc (s, R__o, theta) options operator, arrow; (1/4)*t__exp(N, s, R__meter)*A__cc*q*R__scene*cos(theta)^4*(sum(VP[lambda]*M(lambda*Unit('nm'))*Unit('nm')*lambda*Unit('nm')/(h*c), lambda = 200 .. 4000))/N^2 end proc: 

                                                                               evalf(NP(2300, 1, Units:-Standard:-`*`(0, Unit('deg')))) = HFloat(2191.5645712603696)  NULL

Zone 8;       R__meter := Units:-Standard:-`*`(R__scene, Units:-Standard:-`/`(Units:-Standard:-`^`(2, 3))):   NULL

NP__sat := proc (s, theta) options operator, arrow; (1/4)*t__exp(N, s, R__meter)*A__cc*q*R__scene*cos(theta)^4*(sum(VP[lambda]*M(lambda*Unit('nm'))*Unit('nm')*lambda*Unit('nm')/(h*c), lambda = 200 .. 4000))/N^2 end proc:  NULL

                                                                              evalf(NP__sat(2300, Units:-Standard:-`*`(0, Unit('deg')))) = HFloat(17532.516570082957)NULL

NULL

 

Approximate Formula

 

H__sat := proc (s__sat) options operator, arrow; H__p(s__sat, 0)*E__ps(N, 1, 0)/E__ps(N, 1/8, 0) end proc:      

                                                                                       evalf(H__sat(s__sat)) = HFloat(78.53981635)*Units:-Unit(('cd')*('s')/('m')('radius')^2)/s__satNULLNULL

Average Visible Photon Energy

P__e_ave := Units:-Standard:-`*`(Units:-Standard:-`/`(Units:-Standard:-`+`(850, -350)), sum(Units:-Standard:-`*`(Units:-Standard:-`*`(h, c), Units:-Standard:-`/`(Units:-Standard:-`*`(lambda, Unit('nm')))), lambda = 350 .. 850)):                    evalf(P__e_ave) = 0.3533174192e-18*Units:-Unit('J') 

NPtyp := proc (s__sat) options operator, arrow; H__sat(s__sat)*A__cc/(683.002*(Unit('lm')/Unit('W'))*P__e_ave) end proc: 

                               evalf(NPtyp(2300)) = HFloat(17644.363333654386)"(->)"HFloat(17644.363333654386)NULL

NULL

 

Download Photon_Exposure_Array.mw

Obsolete

See my Camera Profiler application instead.

 

This application creates DNG matrices by optimizing Delta E from a raw photo of x-rites color checker. The color temperature for the photograph is also estimated.  Inputs are raw data from RawDigger and generic camera color response from DXO Mark.

Initialization

   

NULL

NULL

NULL

NULL

NULL

XYZoptical to RGB to XYZdata

 

 

Sr,g,b is the relative spectral transmittance of the filter array not selectivity for XY or Z of a given color.

Pulling Sr,g,b out of the integral assumes they are scalars. For example Sr attenuates X, Y and Z by the same amount.

Raw Balance is not White Point Adaptation.

The transmission loss of Red and Blue pixels relative to green is compensated by D=inverse(S). The relation to incident chromaticity, xy is unchanged as S.D=1.

(See Bruce Lindbloom; "Spectrum to XYZ" and "RGB/XYZ Matrices" also, Marcel Patek; "Transformation of RGB Primaries")

 

 

X = (Int(I*xbar*S, lambda))/N:

Y = (Int(I*ybar*S, lambda))/N:

Z = (Int(I*zbar*S, lambda))/N:

N = Int(I*ybar, lambda):

• 

XYZ to RGB

(Vector(3, {(1) = R_Tbb, (2) = G_Tbb, (3) = B_Tbb})) = (Matrix(3, 3, {(1, 1) = XR*Sr, (1, 2) = YR*Sr, (1, 3) = ZR*Sr, (2, 1) = XG*Sg, (2, 2) = YG*Sg, (2, 3) = ZG*Sg, (3, 1) = XB*Sb, (3, 2) = YB*Sb, (3, 3) = ZB*Sb})).(Vector(3, {(1) = X_Tbb, (2) = Y_Tbb, (3) = Z_Tbb}))

NULL

(Vector(3, {(1) = R_Tbb, (2) = G_Tbb, (3) = B_Tbb})) = (Matrix(3, 3, {(1, 1) = Sr, (1, 2) = 0, (1, 3) = 0, (2, 1) = 0, (2, 2) = Sg, (2, 3) = 0, (3, 1) = 0, (3, 2) = 0, (3, 3) = Sb})).(Matrix(3, 3, {(1, 1) = XR, (1, 2) = YR, (1, 3) = ZR, (2, 1) = XG, (2, 2) = YG, (2, 3) = ZG, (3, 1) = XB, (3, 2) = YB, (3, 3) = ZB})).(Vector(3, {(1) = X_Tbb, (2) = Y_Tbb, (3) = Z_Tbb}))

 

Camera_Neutral = (Matrix(3, 3, {(1, 1) = Sr, (1, 2) = 0, (1, 3) = 0, (2, 1) = 0, (2, 2) = Sg, (2, 3) = 0, (3, 1) = 0, (3, 2) = 0, (3, 3) = Sb})).(Matrix(3, 3, {(1, 1) = XR, (1, 2) = YR, (1, 3) = ZR, (2, 1) = XG, (2, 2) = YG, (2, 3) = ZG, (3, 1) = XB, (3, 2) = YB, (3, 3) = ZB})).(Vector(3, {(1) = X_wht, (2) = Y_wht, (3) = Z_wht}))

NULL

NULL

NULL

• 

RGB to XYZ (The extra step of adaptation to D50 is included below)

 

(Vector(3, {(1) = X_D50, (2) = Y_D50, (3) = Z_D50})) = (Matrix(3, 3, {(1, 1) = XTbbtoXD50, (1, 2) = YTbbtoXD50, (1, 3) = ZTbbtoXD50, (2, 1) = XTbbtoYD50, (2, 2) = YTbbtoYD50, (2, 3) = ZTbbtoYD50, (3, 1) = XTbbtoZD50, (3, 2) = YTbbtoZD50, (3, 3) = ZTbbtoZD50})).(Matrix(3, 3, {(1, 1) = RX*Dr, (1, 2) = GX*Dg, (1, 3) = BX*Db, (2, 1) = RY*Dr, (2, 2) = GY*Dg, (2, 3) = BY*Db, (3, 1) = RZ*Dr, (3, 2) = GZ*Dg, (3, 3) = BZ*Db})).(Vector(3, {(1) = R_Tbb, (2) = G_Tbb, (3) = B_Tbb})) NULL

NULL

(Vector(3, {(1) = X_D50, (2) = Y_D50, (3) = Z_D50})) = (Matrix(3, 3, {(1, 1) = XTbbtoXD50, (1, 2) = YTbbtoXD50, (1, 3) = ZTbbtoXD50, (2, 1) = XTbbtoYD50, (2, 2) = YTbbtoYD50, (2, 3) = ZTbbtoYD50, (3, 1) = XTbbtoZD50, (3, 2) = YTbbtoZD50, (3, 3) = ZTbbtoZD50})).(Matrix(3, 3, {(1, 1) = RX, (1, 2) = GX, (1, 3) = BX, (2, 1) = RY, (2, 2) = GY, (2, 3) = BY, (3, 1) = RZ, (3, 2) = GZ, (3, 3) = BZ})).(Matrix(3, 3, {(1, 1) = Dr, (1, 2) = 0, (1, 3) = 0, (2, 1) = 0, (2, 2) = Dg, (2, 3) = 0, (3, 1) = 0, (3, 2) = 0, (3, 3) = Db})).(Vector(3, {(1) = R_Tbb, (2) = G_Tbb, (3) = B_Tbb}))

NULL

(Vector(3, {(1) = X_D50, (2) = Y_D50, (3) = Z_D50})) = (Matrix(3, 3, {(1, 1) = RX_D50, (1, 2) = GX_D50, (1, 3) = BX_D50, (2, 1) = RY_D50, (2, 2) = GY_D50, (2, 3) = BY_D50, (3, 1) = RZ_D50, (3, 2) = GZ_D50, (3, 3) = BZ_D50})).(Matrix(3, 3, {(1, 1) = Dr, (1, 2) = 0, (1, 3) = 0, (2, 1) = 0, (2, 2) = Dg, (2, 3) = 0, (3, 1) = 0, (3, 2) = 0, (3, 3) = Db})).(Vector(3, {(1) = R_Tbb, (2) = G_Tbb, (3) = B_Tbb}))

NULL

(Vector(3, {(1) = X_D50wht, (2) = Y_D50wht, (3) = Z_D50wht})) = (Matrix(3, 3, {(1, 1) = RX_D50, (1, 2) = GX_D50, (1, 3) = BX_D50, (2, 1) = RY_D50, (2, 2) = GY_D50, (2, 3) = BY_D50, (3, 1) = RZ_D50, (3, 2) = GZ_D50, (3, 3) = BZ_D50})).(Matrix(3, 3, {(1, 1) = Dr, (1, 2) = 0, (1, 3) = 0, (2, 1) = 0, (2, 2) = Dg, (2, 3) = 0, (3, 1) = 0, (3, 2) = 0, (3, 3) = Db})).Camera_Neutral

NULL

Functions

   

NULL

Input Data

   

NULL

Solve for Camera to XYZ D50 and T

   

NULL

 

 

1 2 Page 1 of 2