DCamProf - a digital camera profiling tool

News

2015-05-01
New patch release 0.5.1. I hadn't tested on Lightroom properly. It was shown that Lightroom cannot handle too high precision on matrix rationals and it doesn't like if the standard observer WP is different from 1931_2. I have now changed default observer to 1931_2 as it's an easier to use default, and made an automatic remapping in the make-dcp tool to handle the case when a different observer was used during profile creation.

2015-04-30
Here's the first release of DCamProf to the public (0.5.0). This is an early one, and while you can make camera profiles with it it's still in a "hackish" state, probably some bugs left and certainly slow. There's lots of silly loops over loops here and there. My excuse is that I've been focused to get things working first rather than get stuck optimizing for speed. As usual with these kind of projects it has taken far more time than I initially planned, but then it's far more feature-rich too!

What is DCamProf?

DCamProf is a free and open-source command line tool for generating camera profiles and perform tasks related to camera profiles and profiling.

To generate a camera profile you need either the camera spectral sensitivity functions (SSFs) or a measured target. DCamProf has no measurement functionality, but you can use the free and open-source Argyll CMS to get a .ti3 file with measurement data which DCamProf can read.

Here's a feature list:

  • Generate camera profiles from test target measurements or camera spectral sensitivity functions (SSFs).
  • Import (and export) measurement data from Argyll.
  • Detailed control of matrix and LUT optimizers to hand-tune the trade-off between accuracy and smoothness.
  • Test profile color matching performance, with free choice of illuminant.
  • Save reports and data files for plotting (using gnuplot for example).
  • Simulate reflective spectra all the way to the locus.
  • Built-in spectral database with Munsell, Macbeth CC24, spectra from nature and common illuminants.
  • Import spectral data to be used in targets or as illuminants.
  • Analyze camera color separation performance under different illuminants using SSFs.
  • Native camera profile format, can be converted to DNG profiles (DCP).
  • Decode and hand-edit DCP profiles, and re-encode them again.
As seen it currently can only export DNG Camera Profiles (DCP), but if there is an interest to support ICC profiles I may add that as well. (ICC profiles are a bit more tricky as there's no standard on how raw files are pre-processed before application so one have to make them for a specific raw converter.)

Note that many features are related to camera SSFs, and indeed you get most out of DCamProf if you have SSFs available. You don't need them to make great profiles though, having them is more about flexibility and convenience than accuracy. You can then also learn many things of how cameras work by testing various scenarios, testing the efficiency of a specific target design, testing how a profile performs under a different illuminant etc.

The reason I started this software was that 1) Argyll can't do DCPs, and 2) I was not pleased with the commercially available alternatives for making own camera profiles. Too much hidden under the hood, too little control, and many indications that the quality of the finalized profiles was not that good, mainly regarding with smoothness. Then I added the SSF ability and the software grew to something more than just a profile maker, now you can say it's a camera color rendering simulator as well.

The software is quite technical, but if you can use Argyll you can use DCamProf. At some point I will probably make a more in-depth article about camera profiling and using DCamProf which I will link here, but for now there's only the minimal reference documentation found on this page.

Downloading and building DCamProf

Download the source code for DCamProf v0.5.1. It's developed on Linux and compiling it there is easy as all third parties should be available as standard packages in your Linux distribution. It should also be relatively easy to build on Mac OS X. I haven't tried on Windows, but it's probably easy to build using Cygwin tools.

DCamProf uses OpenMP to make use of all your CPU cores in parallel. You can build it without OpenMP (remove the -fopenmp words from the Makefile) but some aspects of the program will then run much slower as it will use only one core. At the time of writing Mac OS X standard compiler clang doesn't support OpenMP (it's in the works though), unless you build your own clang from source.

An alternative to building it on your OS X or Windows platform is simply to install a Linux virtual box and run it there, make sure you give the virtual machine access to all cores. An added bonus is then easy access to Argyll, gnuplot, exiftool and other related tools.

How DCamProf models cameras

DCamProf looks on the perfect camera as a colorimetric camera, that is the SSFs matches the color matching functions for the CIE XYZ color space. No real camera is colorimetric so the goal of profiling is to make it perform as close as possible to one.

DCamProf assumes that the camera is linear, that is if you for example double the intensity of a certain spectrum the raw values will also double and there will be no change in their relation. This is indeed true for any normal digital camera today, with the possible exception of extreme underexposure and very close to clipping where there can be non-linear effects.

The linearity assumption leads to that the correction lookup table only needs to be indexed on chromaticity (that is saturation and hue, but not lightness), but the output still needs correction factors for all three dimensions as some colors can be rendered too dark or too light with a fixed factor throughout the full lightness range. That is DCamProf works with a LUT with 2D input and 3D output, commonly referred to as a 2.5D LUT.

2.5D vs 3D LUT

With a 2.5D LUT we assume that the same color in a darker shade will have the same shape of its spectrum, only scaled down. This is true if you render colors darker by reducing the camera exposure in a fixed condition. However, if we compare a dark and light color of the same hue and saturation in a printed media the spectrum shapes will differ because a typical print technology will alter the colorant mix (eg inks) depending on lightness.

This means that our linearity assumption breaks as the relative mix of camera raw values may differ slightly between dark and light colors and in this case a full 3D LUT could make a more exact correction. However, this only makes sense in highly controlled conditions when copying known media (such as printed photographs), that is when you're using the camera just like a flatbed scanner. The light source must be fixed, the camera exposure must be fixed, and the camera profile must be designed using a target made with the same materials as the objects you shoot.

As a 3D LUT only makes sense in this very narrow use case DCamProf supports only 2.5D (so far). If you really need a 3D LUT you can use Argyll, but you're then limited to ICC profiles. For strict reproduction work that may be a better approach.

Note that commercial raw converters often use 3D LUTs, not to achieve better accuracy though but to make film-like subjective lightness-dependent adjustments. For example making dark shades more saturated and cooler (bluer) while highlights of the same color is made warmer (redder). DCamProf is not designed to make profiles that provide subjective "looks". The idea is instead that you start off with as accurate color as possible and then tune the color to your own liking using post-processing color adjustment tools available in raw converters and photo editors. Personally I prefer this way to work as I'm then myself in full control of the look, being fully aware of when and how I deviate from realism rather than letting my look be influenced in unclear ways by the taste of the raw converter manufacturer.

Basic workflow for making a profile using a test target

  1. Get or make a physical test target. The classic Macbeth/X-Rite 24 patch color checker is a fine choice.
  2. Get or make a reference file for the test target, preferably containing reflectance spectra.
    • If you're using that 24 patch color checker like most will be doing, the reference file with reflectance spectra assembled by BabelColor is the second best choice if you can't measure it yourself. This data should be valid for the nowadays more popular "ColorChecker Passport" product too.
    • The above BabelColor CGATS text file example needs some conversion to be used with Argyll. To save you some time I've don it for you, look for "cc24_ref.cie" in the DCamProf distribution.
  3. Shoot your test target under the desired light source. Store in raw format.
  4. Convert the raw file to a 16 bit linear TIFF without white balancing.
    • You can use a recent version of RawTherapee (export for profiling, with disabled white balance), or DCRaw
      • dcraw -v -r 1 1 1 1 -o 0 -H 0 -T -W -g 1 1 <rawfile>
    • If you have an odd camera format and you want to use the profile in Adobe's products it may be safer to convert to DNG first using Adobe's DNG converter, as raw decoding of proprietary formats may differ a little concerning black levels, white levels and calibration data application.
    • Crop so only the target is visible, and rotate if needed. Argyll is very sensitive to target orientation. If you use some image editor to do this make sure that the full 16 bit range is kept, that is don't use 8 bit Gimp. If you use RawTherapee you can crop and rotate in there.
  5. Use Argyll scanin command to generate a .ti3 file.
    • It needs the target reference file, test target layout file and raw image as 16 bit TIFF as input.
    • scanin -v -dipn rawfile.tif ColorChecker.cht cc24_ref.cie
    • The scanin command will generate a diag.tif which shows patch matching (look at it to see that it matched) and a rawfile.ti3 file which contains the raw values read from rawfile.tif together with reference data from the cc24_ref.cie file.
  6. Use DCamProf to make a profile from Argyll's rawfile.ti3 target file.
    • dcamprof make-profile rawfile.ti3 profile.json
    • The above command doesn't specify any illuminants, which means that the profile will be made for D50 and the rawfile.ti3 must contain reflectance spectra (it will if the example cc24_ref.cie is used) or have it's XYZ values related to D50. To change calibration illuminant use the -i parameter, and if the .ti3 lacks reflectance spectra specify its XYZ illuminant using -I.
  7. Convert the native format profile to a DCP.
    • dcamprof make-dcp -c "Camera manufacturer and model" profile.json profile.dcp
    • For many raw converters the camera manufacturer and model must exactly match what the raw converter is expecting. For example if using Adobe Lightroom the name must match the name Lightroom is using for it's own DCPs.
  8. Optionally use DCamProf's dcp2json and json2dcp commands to do any manual edits of the DCP file, such as changing profile name and copyright.
  9. The DNG profile is now ready to use in your raw converter.

Basic workflow for making a profile from camera SSFs

If you have the camera's spectral sensitivity functions you can skip the target shooting process.
  1. Format your camera's SSF data into a JSON file that DCamProf can read.
    • Use the distributed examples as a guide for how the JSON file should be formatted.
    • If you don't have the equipment or knowledge to measure your camera's SSFs, you can look in the SSF links section and see if you're lucky and can find your camera in one of the sources.
  2. Generate a "virtual" target with your desired spectral data.
    • You can use DCamProf's built-in spectral database or use its ability to generate spectra, or import from some other spectral source (see provided import_spectra.txt for formatting, it's the Argyll .ti3 format, but you can use a subset).
    • Here's a basic example when we just generate a color checker from the built-in spectral database target:
      • dcamprof make-target -c ssf.json -p cc24 target.ti3
    • The resulting target.ti3 contains reflectance spectra for all patches, plus XYZ reference values and RGB values for the camera rendered using the SSFs found in ssf.json.
  3. Make the profile
    • dcamprof make-profile -c ssf.json target.ti3 profile.json
    • We don't really need to provide the camera's SSF again (ssf.json) as the target file already contains rendered RGB and XYZ values, but it's a good habit since then the RGB (and XYZ) values will be regenerated from spectra each time which is convenient and reduces the risk of making mistakes.
  4. Convert the native profile to a DNG Profile.
    • dcamprof make-dcp -c "Camera manufacturer and model" profile.json profile.dcp
    • See the description for the basic workflow using a test target for more details.
In this example workflow we keep the illuminants at default, D50. As we let the spectral information follow through in the workflow we can change calibration illuminant late in the process, when making the profile:
  dcamprof make-profile -c ssf.json -i StdA target.ti3 profile.json

Choosing test target

Due to natural limitations of camera profiling precision it's quite hard to improve on the classic 24 patch Macbeth color checker when it comes to making profiles for all-around use. It's more important to have a good reference measurement of the test target than to have many patches. If you don't believe me please feel free to make your own experiments with DCamProf; by using camera SSFs you can simulate profiling with both few and many patches and compare target matching between them.

DCamProf allows you to use any target you like though, you can even print your own and use a spectrometer and Argyll to get reference values. A modern inkjet printer with the higher end pigment ink sets seems to have quite nice smooth spectra so the quality of these targets should be fine. Use a high quality semi-gloss paper (without optical brighteners!) to produce high saturation patches. Although darker repeats of colors does not hurt there's not much gain from it as the LUT is 2.5D, so an IT-8 style target (most patches are just repeats in darker shades) does not make that much sense.

The profiling process requires at least one white (or neutral gray) patch. A slightly off-white patch is no problem (for example if you print your own target on OBA-free paper it's likely slightly yellow), DCamProf will compensate.

If you have the camera's SSFs you can use the built-in spectral databases (or import your own) rather than shooting real test targets. In that case you will probably want to select spectral data that matches what you are going to shoot, for example reflectance spectra from nature if you are a landscape photographer.

The classic 24 patch Macbeth color checker, originally devised in the 1970's. Despite its age it still holds up well for designing profiles, thanks to relatively saturated colors with a relatively large spread. As seen in the u'v' chromaticity diagram (with locus, AdobeRGB and Pointer's gamut) there's still space to fill though, and some patches are occupying almost the same chromaticity coordinate which is not that useful when making 2.5D LUTs.

Test target reference files

The foundation of profiling using test targets is that the profiling software knows what CIE XYZ coordinate each color patch corresponds to, or even better which reflectance spectrum each color patch has so the software can calculate the XYZ values internally.

Higher end test targets may be individually measured so you get a CGATS text file with reference values, and Argyll's scanin tool can use them directly. If you get a standard 24 patch Macbeth color checker you probably don't have an individual reference file and then a generic file like the one provided with DCamProf (cc24_ref.cie) will have to do. Having the reflectance spectra is strongly preferred over pre-calculated XYZ values, so do get that if you can. The problem with pre-calculated values and no spectra is that when changing illuminants the software cannot re-calculate XYZ from scratch using spectral data, but must rely on a chromatic adaptation transform which is less exact. It's also a higher risk for the user to mess up by forgetting to inform DCamProf of which illuminant the XYZ values are related to. If there's spectral data the reference values are always re-generated from scratch to fit the currently used illuminant, which is both exact and convenient.

If you have a spectrometer (usually designed for printer profiling) you can measure your target and generate your own reference file with spectra. Using Argyll you do like this:

  1. Create or find an Argyll .ti2 text file which contains the test target layout needed for the spectrometer scan. Note that Argyll is distributed with .ti2 files for many of the popular commercial test targets, the file is called ColorChecker.ti2 for the Macbeth 24.
  2. Scan the target with Argyll's chartread (exclude the .ti2 suffix, for most Argyll commands the suffix should be excluded):
    • chartread -v -H target
    • Note that some targets may have too small patches to be read successfully with your instrument. For example an X-Rite ColorChecker Passport cannot be read by an X-Rite Colormunki spectrometer.
  3. Convert the resulting .ti3 file (which contains complete spectra for each patch) to a new .ti3 file with reference CIE XYZ values with your desired illuminant.
    • spec2cie -v -i D65 target.ti3 reference.cie
    • In the above example "D65" was chosen, but you can also choose "A" or "D50", or any other supported by the spec2cie tool.
    • As the spectral data will be kept in the file it does not really matter what illuminant (or observer) you use, you can change that again when generating the profile with DCamProf. The described method is however also compatible with a standard Argyll workflow.
  4. The resulting reference.cie can now be used together with Argyll's scanin tool.
  5. If you don't have a use-able .cht file for the chart layout (a more detailed layout information than needed for the spectrometer scan), you need to generate one. Unfortunately that is a bit of a headache. You can use the scanin tool as a help for that (using the -g parameter), but it is quite messy with lots of manual edits. At the time of writing I have not tried doing it myself and as long as you're using a reasonable popular target there will be a .cht file distributed with Argyll. For the Macbeth 24 the file is called ColorChecker.cht.
It's probably better to measure your own target and get full spectral information than getting a typical pre-generated reference file with only XYZ values for some pre-defined illuminant. If it really is better depends on the precision of your instrument, the sample-to-sample variation of test targets and the quality of the provided reference file. It's not possible to really know what will be best, you can try both and see what you like the most. If there's some serious problem with the reference file it's usually noticed when making the profile, the LUT must make extreme stretches etc.

In some cases you could get the reference spectra in some format that Argyll can't read directly. Argyll is delivered with a few conversion tools to handle other common text formats, cb2ti3, kodak2ti3 and txt2ti3. You may be helped by making a dummy conversion using DCamProf, like this: dcamprof make-target -p input.txt -a "name" output.ti3, and sometimes you may have to do some manual edits in a text editor too to get it into a format Argyll accepts.

Shooting test targets

To consider:
  • Even light from the desired illuminant
  • Avoid reflections on the target (a challenge with glossy targets)
  • Avoid colored reflections from nearby surfaces
  • Avoid vignetting
  • Avoid perspective distortion
  • Avoid over- or underexposure
If you are simulating daylight using an artificial light source it's better to use a high temperature halogen lamp, such as a Solux lamp on overdrive, than a fluorescent. Or just shoot outside in real daylight.

Uneven lighting is a common problem in camera profiling, so do make sure you have even light. If you notice strange bending in the lightness dimension when making the profile, the issue could be an unevenly lit test target.

Minimize vignetting by shooting the target relatively small in the center of the image, and use a relatively small aperture (say f/8 if on 135 full frame).

Avoid reflections from nearby colored surfaces that may distort the color of the light source. If shooting outdoor, shooting in an open space with someone holding up the test target in front away from the body is a good alternative.

If you know what you are doing you can push the exposure a little extra to get optimal "expose to the right" (ETTR) and thus as low noise as possible. But be careful, clipped colors will be a disaster in terms of results. I use to exposure bracket a few shots and check the levels in the linear raw conversion to see that there is no clipping.

Argyll's scanin is sensitive to perspective distortion, so try to shoot as straight on as possible, and correct any residual rotation/perspective in the raw conversion.

File formats

JSON

DCamProf uses JSON as a base for its own file formats. Open the files that comes in the data-examples directory in the DCamProf archive for documentation. The JSON parser in DCamProf has been modified to parse floating point numbers with maximum possible precision.

If you get a JSON format error of your hand-edited files it can be hard to figure out where it is, then you can use one of the online JSON validators like JSON lint.

Argyll .ti3

DCamProf reads Argyll .ti3 files produced by the scanin tool. Note that the Argyll .ti3 format is rich in features and DCamProf only cares about a subset of it. It expects to get RGB measurement triplets matched with XYZ reference values, and possibly (hopefully) spectral data.

DCamProf can also generate .ti3 files and will then add some columns specific to DCamProf. Files remain compatible with Argyll though as unknown columns are ignored.

The .ti3 format (or rather an even more reduced subset of it) is also used when you want to import spectral data when you make a target to be processed by camera SSFs. An example of this exists in the data-examples directory.

DCP

DCamProf can read and write DNG camera profiles (DCPs).

Command reference

DCamProf is a collection of tools built into a single binary. The first parameter specifies the command (tool) you want to run, then followed by command-specific arguments:
  dcamprof <command> [command-specific flags] <command args>
If you run the binary without parameters you get a list of all commands and their flags.

The basic workflow is:

  1. Make a target file containing test patches with camera RGB and reference XYZ values, and preferably also the reflectance spectra. This is either done with Argyll from test target raw photos, or by using the make-target command to render values based on provided camera SSFs.
  2. Make a camera profile using the target file, using the command make-profile. This will output a generic profile in DCamProf's own JSON format.
  3. Convert a DCamProf profile to a standardized format, currently only DCP is supported, using the command make-dcp.
  4. Optionally manually edit the result (copyright strings etc) by using the dcp2json and json2dcp commands.
  5. Optionally evaluate target matching performance using the test-profile command.
Additionally you can use the make-target command to generate new RGB and XYZ values based on your chosen illuminant and observer. This requires the full spectrum of target patches, and to make RGB values you also need the camera's SSFs. For convenience value re-generation is supported also directly in the make-profile and test-profile commands.

Here follows a description of each command available.

make-target

  dcamprof make-profile <flags, with inputs> <output.ti3>
Make a target file which contains raw camera RGB values paired with reference XYZ values, and (optionally) spectral reflectance. The file format is Argyll's .ti3, with some DCamProf extensions.

If you're using Argyll for measuring a target you don't need to use this command, but you can still use it to regenerate XYZ values with a different observer for example (this requires that the .ti3 file contains spectral data).

If you have your camera's SSFs you don't need to shoot any physical target, then you render the .ti3 file from scratch using this command.

Overview of flags:

  • -c <ssf.json>, camera's spectral sensitivity functions, only needed if you want to (re-)generate camera raw RGB values.
  • -o <observer>, only required when (re-)generating XYZ reference values from spectra, normally the default 1931_2 is a good choice.
  • -i <illuminant>, only required when (re-)generating XYZ and/or RGB values from spectra (default: D50)
  • -p <patches.ti3>, include patch set, in Argyll .ti3 format. The file can be produced by Argyll, DCamProf or any other software with compatible format. It can contain XYZ and RGB values, and preferably it should contain spectral reflectance of the patches too. If spectra is available the XYZ and RGB values are re-generated when possible (unless -R and/or -X parameters are provided).
  • -a <name>, assign (new) class name to previously included patch set (-p). Class names is a DCamProf extension to the .ti3 format (that is Argyll files lacks it). Class-names are useful when assembling a single target file from multiple spectral sources and you want to weight them differently during profile making. See documentation for make-profile for further details.
  • -g <generated grid spacing>, adjust the grid spacing when generating spectral grids. The spacing is given in u'v' chromaticity distance, default is 0.03.
  • -d <distance>, minimum u'v' chromaticity distance between patches of different classes (default is 0.02). If you mix different spectral sources, for example greens from nature in one set and greens from artificial sources in another which overlap, this can lead to a messy-looking target and give contradicting optimization goals for certain colors. DCamProf can handle contradicting spectra well, but to keep the target cleaner you can use this parameter (which is enabled per default, set it to 0 to disable). The patch set listed first on the command line takes priority, that is overlapping patches of later sets are dropped.
  • -b <distance>, exclude patch if there is a lighter patch with same chromaticity. Suggested chromaticity distance 0.004 (default: not active). As DCamProf makes a 2.5D LUT darker patches with the same chromaticity will not really add much value, so to clean up the target you can choose to remove those. If kept they will be grouped together with lighter colors used for average correction.
  • -X, -R, don't regenerate XYZ/RGB values of imported patch sets. Per default target values are regenerated to match chosen observer, illuminant and camera SSF, if all required information is available. This is usually the best, but if you for some reason want to keep the reference values provided in the imported file use these flags.
  • -n, exclude spectra in output (default: include if all inputs has it). Target which include spectra are more flexible as XYZ (and RGB) values can be regenerated with a different observer/illuminant/camera, but makes a larger file which is harder to read. If you don't need spectra you can exclude it. Note that if some of the inputs lacks spectra the output will not have any either.
  • -r <dir>, directory to save informational reports and plots.

Built-in spectral data

DCamProf has a few spectral databases built-in. These come from freely available sources, see the acknowledgments for information.
  • cc24 -- spectral reflectance of the classic Macbeth 24 patch color checker
  • kuopio-natural -- spectral reflectance of colors occurring in typical nature in Finland, leaves, flowers etc.
  • munsell -- spectral reflectance of the full 1600 patch Munsell glossy patch set.
  • munsell-bright -- subset of Munsell, only the lightest and most saturated colors included.
This is a good start which you can do a lot with, but I'm looking for more spectral data to include in future releases of DCamProf, so if you know of some good source please let me know.

Generated spectral data

DCamProf has a spectral rendering algorithm that can make reflectance spectra to match any given XYZ coordinate for the chosen observer and illuminant. It's sort of an impossible task as there are an infinite amount of spectra to choose from. In this infinite set DCamProf finds a smooth spectra which has similar properties to real reflectance spectra.

Although not a full substitute to real measured data it can be used for experiments, test profile performance, establishing a baseline or filling out for areas where you don't have real spectral data. And indeed, a profile rendered completely from generated spectra will work, try if you like.

You can generate spectra along the chromaticity border of a gamut and optionally fill the inside with grid of patches. The samples are always made as light as possible (as high reflectance as possible) for the given chromaticity. Extremely saturated colors are by necessity narrow-band and will thus be darker than less saturated colors.

The gamuts available are locus, pointer, srgb, adobergb and prophoto. Add "-grid" suffix, eg "pointer-grid" to create a grid. The grid spacing can be adjusted with the -g parameter. Gamuts with extreme or even out of human gamut colors like locus and prophoto will cause the spectral renderer to fail producing spectra on some chromaticity coordinates, this is normal.

Be warned that spectral data generation is very processing intensive. DCamProf uses OpenMP to process several patches in parallel on all available cores, but it can still take minutes to produce a grid, or even hours if it's really dense.

A generated reflectance spectrum made by DCamProf (blue) together with a measured spectrum from a real Munsell color patch (red). Both lead to the same XYZ coordinate when integrated with the observer's CMFs. That is this shows one example of two different spectra that produces the identical vision color.

The DCamProf spectral generator strives for smooth spectra, and its result is thus a little bit more rounded than the Munsell patch in this example.

Observers

The observer is a mathematical model of the eye, defining its spectral sensitivity functions, or color matching functions (CMFs). It's not intended to exactly match the eye's cone response, but to provide "equal" results. The observer SSFs have been mathematically transformed to work better in real applications.

When you integrate these CMFs with a spectrum you get the CIE XYZ tristimulus coordinates. That is the observer is key element in modeling what colors we see.

As there's no method to actually measure the signals the eye sends to the brain the CMFs have been derived based on results from color matching experiments. The precision is thus dependent on the color matching skills of the people involved in the experiment.

The original observer was published as early as 1931, and it's still the number one standard observer. This is not because it's the most exact one, but because the CIE standard organization will not accept new standards unless significant improvement is made. Some minor improvements have been made over the years, but the original 1931 standard observer holds up well enough.

There are 2 and 10 degree variants of observers. This simply refers to how large area of the eye the tested color patch covers. With the more narrow 2 degree angle the eye is slightly better at color separation, but the 10 degree generally matches real situations better. The 1931 is a 2 degree observer, and the first standardized 10 degree observer was published in 1964.

DCamProf contains a number of observers, you can see a list when running the command without parameters. I'd like to use the 2006 observer as the default one as it's more accurate than the original 1931, and I'd also rather use the 10 degree observer as I think it matches real situations better than the 2 degree. However, as most DCP software expects a 1931_2 observer I've chosen that as the default.

Examples

Re-generate XYZ reference values with a new illuminant (D65) and observer (using the default 1931_2) for an Argyll-generate .ti3 file:
  dcamprof make-target -i D65 -p argyll.ti3 output.ti3
Generate targets files from scratch using camera SSF and built-in database:
  dcamprof make-target -c 5dmk2-ssf.json -i StdA -p cc24 output.ti3
  dcamprof make-target -c 5dmk2-ssf.json -i StdA -p cc24 -p munsell output.ti3
Use the spectral generator to make targets from scratch:
  dcamprof make-target -c 5dmk2-ssf.json -i 7500K -g 0.01 -p pointer-grid output.ti3
  dcamprof make-target -c 5dmk2-ssf.json -i D65 -p pointer -p srgb-grid output.ti3
Re-generate both RGB and XYZ values from a previously created file which contains spectral information:
  dcamprof make-target -c 5dmk2-ssf.json -i D65 -p input.ti3 output.ti3
Assemble a target from imported spectra and built-in database:
  dcamprof make-target -p input1.txt -a "class1" -p input2.txt -a "class2" -p cc24 output.ti3
Note that in this last case there is no SSF provided and while the input text files might have RGB values, no RGB values can be generated for the built-in cc24, and the output will thus contain dummy values (zeroes) for the RGB triplets. That is to be used when making a profile you need to run it through again to re-generate RGB values with provided camera SSFs. For convenience the make-profile and test-profile commands support re-generation directly so you usually don't need to re-generate reference values separately with the make-target command.

If you are using Argyll source files it's preferred that you include spectra throughout the workflow so XYZ reference will be re-generated with the observer chosen in DCamProf. If not, I recommend using the 1964_10 observer in Argyll and choose that as well in DCamProf. If the XYZ reference values comes without spectra from a source you cannot control it's important to know which observer and illuminant that was used so you can later inform make-profile of that.

make-profile

  dcamprof make-profile [flags] <input-target.ti3> <output-profile.json>
Make a camera profile (in DCamProf's own native format, which can be converted later on) based on an Argyll .ti3 target file, either generated by Argyll from a raw test target photo, or by dcamprof make-target. The target file contains test patches with raw RGB values from the camera coupled with reference CIE XYZ coordinates of the patches, and possibly also the spectral reflectance of each patch.

Overview of flags:

  • -w, -W, weighting to control trade-off between smoothness and accuracy, described in a separate section below.
  • -M, ignore DE weights for matrix optimization, described in a separate section below.
  • -l <l,c>, LUT lightness and chromaticity relax parameters (default: 0,0). Coarse relax of the LUT stretching, which can be used as an alternative or complement to normal weighting (-w). A suitable starting point can be -l 0.1,0.1.
  • -d <distance>, minimum u'v' chromaticity distance between patches when optimizing LUT, default 0.02. Close patches will be grouped together and an average correction is made.
  • -o, observer, default 1931_2. If target XYZ values are not re-generated (that is the target lacks spectra) this must match the observer used when the XYZ values was originally generated. If not known the best guess is generally 1931_2, that is the default.
  • -c <ssf.json>, camera's spectral sensitivity functions, only needed if you want to regenerate camera raw RGB values from spectral information in the target file.
  • -i <calibration illuminant>, this is the illuminant the target was shot under, that is the illuminant the target file RGB values was generated for. Can be specified as an exif light-source name or number, xy coordinate, XYZ coordinate or a spectrum.json file. To allow any target value re-generation from spectra it must be a source with known spectrum. If camera SSF is provided (-c) RGB values will be re-generated.
  • -I specifies the illuminant for the XYZ reference values. Can be specified as an exif light-source name or number, xy coordinate, XYZ coordinate or a spectrum.json file. If spectral information is provided in the target the XYZ values will be re-generated according to chosen illuminant (and observer) when possible, and then this parameter is thus ignored. If there is no spectral information it's however important that the illuminant and observer matches what was used for the target.
  • -m, -f, pre-generated matrices if you want to skip the matrix finder step.
  • -s, run an alternate (much) slower matrix optimization algorithm which can find a little better result.
  • -r <dir>, directory to save informational reports and plots.

Illuminants

It's important that you get illuminants right in order to generate a correct profile. The .ti3 file format does not contain information on which illuminant that was used for the camera raw RGB or XYZ values. This means that you must keep track of that yourself and provide the information to DCamProf via the -i and -I parameters.

There are a few possible scenarios:

  • Target file has no spectral information, camera RGB values were created for the desired calibration illuminant, XYZ reference values for some other illuminant. Re-generation is not possible.
  • Target has spectral information, DCamProf knows the spectrum of the calibration illuminant, XYZ values in the files don't matter as DCamProf will re-generate from spectra. RGB values must still match calibration illuminant.
  • Target has spectral information, DCamProf has illuminant spectrum and you provide camera's SSFs. Both RGB and XYZ values will be re-generated from spectra.
For optimal results you want to avoid the first case. That is provide a target with spectral information, and a calibration illuminant with known spectrum. Then all XYZ values will be re-generated from spectra.

In the most flexible case you have the camera's SSFs too. In this case also the RGB values are regenerated for the calibration illuminant you choose.

If you lack camera SSFs the RGB values in the target file must match the calibration illuminant. That is if you have shot a test target in daylight similar to D65 and made a target file with Argyll you should set the calibration illuminant to D65.

If you lack reflectance spectra in the target file the specified XYZ illuminant must match the ones used in the target. The values could for example originate from a target manufacturer reference file, and is then often relative to D50 or D65. Make sure to look it up so you can provide the correct one.

If you have measured the XYZ reference values yourself using a spectrometer you should have spectra in the target file. If not they have probably disappeared along the way, look over the workflow and see if you can provide DCamProf with spectral information.

DCamProf will need XYZ values for both the calibration illuminant and the "profile connection space" which always is D50. A target file only contains XYZ values for one illuminant, and thus the other or both must be calculated. If there is no spectral information CIECAM02's chromatic transform CAT02 will be used, which does not provide as precise results as when calculating from spectra. If you can't get the spectra, it's best to have reference values relative to D50 as the LUT works in that space.

Weighting

Weighting is controlled with the -w parameter and is used to specify the importance of colors. There are two purposes, one is to inform the matrix optimizer so it will try to make better fit of one set of colors at the cost of another. The other is to specify a maximally accepted error (in Delta E) for each class of colors, this is then used in matrix optimization but more importantly to relax the LUT bending. A LUT can always stretch, compress and bend to match the target patches exactly, but that can result in sharp and even inverted bends causing ugly gradient transitions (typically most visible in photos with strong out-of-focus blur backgrounds when one color transitions into another). In this case it's better to relax the fitting, and the LUT optimizer will automatically relax in the best way based on the provided acceptable delta E levels.

If you create a matrix-only profile matrix fitting is obviously important, but if you make a LUT it's generally not worthwhile fine-tuning matrix weights, as the LUT will correct the residual error anyway. Feel free to experiment though.

The matrix optimization result is mostly affected by the actual patches provided. Re-weighting them will have some effect, but changing patch set has typically a much larger effect.

When using a LUT it can be better to let the matrix optimizer ignore the delta E relaxing weights, as it may provide a better starting point for the LUT. Use the -M parameter to control this.

To assign different weights to different groups of patches the target file must be split into "classes" (=groups of patches), specified through a "SAMPLE_CLASS" column in the file. The idea is that you can have a naming such as "skin", "forest_green", "textiles" etc and then for example assign greater importance to skin-tones.

Class names in the target file is a DCamProf concept and is not available in Argyll-generated files. By running an Argyll file through dcamprof make-target -p argyll.ti3 -a name out.ti3 you can add a class column, and then edit the text file manually and change names to split into more classes if you like. That way you can split even a 24 patch color checker into several classes. However, the main purpose of class-splitting is to be used when you have a number of distinct patch sets of different spectral types as you typically have when making a target directly with a camera's SSFs.

DCamProf makes a pre-weighting per default (the user weighting is added on top), this is to handle the situation when you combine several patch sets with different density. Some patch sets may have lots of patches concentrated around some specific color, and another may have few patches widely separated. To not cause the dense sets to totally dominate, there's a pre-weighting based on Delta E distances that normalizes all patches. This is generally a good thing, but if you really want "1 patch = 1 unit" you can disable this normalization by adding -W. This normalization only affects the matrix optimizer, the LUT optimizer only looks at the max acceptable delta E deviations.

If you don't have any class names (or all patches are in the same class) there's no value in providing a matrix weight. However the delta E relaxation can still be useful.

Finding the right weights is a trial-and-error process. Dump reports and plots (-r report_dir) and visualize the results, see the section on report directory files for more examples.

For each class you can assign up to five weights using the -w parameter, these are in order:

  1. Maximum acceptable DE deviation
  2. Matrix optimization weight
  3. CIE DE2000 kL
  4. CIE DE2000 kC
  5. CIE DE2000 kH

Here follows a few examples:

"-w skin 0,4 -w nature 6,1": the class "skin" should have full LUT correction (0 Delta E) and have 4 times the weight than ordinary patches during matrix optimization, while the class "nature" can be relaxed with up to 6 DE for a smoother LUT, matrix optimizer weight set to 1 (no change).

"-w all 3.5": "all" is a reserved name to point out all patches in the target file. Here the LUT optimizer is instructed to relax all patches up to 3.5 DE to make a smoother LUT.

"-w all 0,1,4,1,1": 0 delta E, unchanged weighting (1), and then three weighting parameters for delta E, which is the lightness, chroma and hue k weights for the CIEDE2000 algorithm. A higher value means less priority, so here we say we're less bothered with lightness than chroma (saturation) and hue. Lowering the importance of lightness is often a good idea, it's probably the least disturbing to have errors in.

Note that all this weighting stuff is only about matching the specific provided patches, the profiler can't magically make the camera work better. For example, if a camera is bad at separating green colors, it will still be bad even if the particular green patches in the target have been mapped correctly.

Color matrix and forward matrix

You may have noted that I have adopted the DNG Profile names of matrices also for the native DCamProf format. This is simply because the names are familiar and the concept is good. It doesn't lock the native format to DCP profiles.

The forward matrix operates in D50 XYZ space, and using D50 as a reference illuminant is not unique to DNG profiles, it's used for ICC profiles too. As the conversion from the calibration illuminant to D50 is a generic issue DCamProf has adopted the concept with using a forward matrix.

Profile-making tips

Do experiment! Learn how to use a plotting tool and plot results. To get a general feel of how profiling works in practice you can play around with one of the example camera SSFs, and then use the acquired knowledge and feel when you tune settings for your targeted camera (where you may not have SSFs).

What you will see is that there is no such thing as perfect result, and the farther from the white-point you get tougher it will be to compensate errors. While it can be fun to try to get a profile that works all the way out to the locus it will hurt performance of common colors. It's generally better to maximize performance for colors you're actually going to shoot. Pointer's gamut approximates the limit of how saturated real reflective colors can be, colors outside that need to be represented by emissive (or transmissive) light like lasers and diodes. It's generally not worth-wile trying to get a good match outside Pointer's gamut. If you have the camera's SSF you can plot and see how well the camera can actually separate colors, you will probably see that there are some issues when it comes to extremely saturated colors, and no camera profile can compensate for that.

Consider that a perfect match to a specific color checker does not mean that the color precision is perfect, not even for those colors. It's only perfect for the particular spectra the color checker has, somewhat compromised by various measurement errors throughout the profile making process. Therefore I suggest to always apply some LUT relaxing to smoothen profiles at least some. As true perfection cannot be had, it's better to make sure color transitions are smoothly rendered.

Using the -m and -f parameters you can experiment with using separate parameters for optimizing the matrix and the LUT. If the LUT seems to need a lot of strange stretching it may be because the matrices are no good, and in some cases it might be worthwhile to render them separately, perhaps with a different patch set even (which is feasible when you're using SSFs).

Examples

In all examples below I assume the target file has reflectance spectra. If not you need to specify the XYZ reference values illuminant using the -I parameter.

Example 1: basic profile making with default parameters, using calibration illuminant StdA (calibration illuminant = the light source the target was shot under):

  dcamprof make-profile -i StdA target.ti3 profile.json
Example 2: assuming we have a target with cc24 and pointer border, we make a smoother LUT by relaxing patch matching, 0.5 delta E on cc24 and 4 delta E on the pointer border. We still let the matrix make best match it can without relaxation (-M). We also reduce the importance of lightness matching through setting of the CIE DE2000 weights (4,1,1). By providing camera's SSF (-c) the RGB values will be re-generated for the given illuminant (D65). Plotting data is saved to the "dump" directory (-r).
  dcamprof make-profile -r dump -c ssf.json -i D65 -M -w cc24 0.5,1,4,1,1 -w pointer 4,1,4,1,1 target.ti3 profile.json
Example 3: make matrices using one target, and the LUT using another by running make-profile twice:
  dcamprof make-profile -i D65 target1.ti3 m.json
  dcamprof make-profile -i D65 -m m.json -f m.json target2.ti3 profile.json

test-profile

  dcamprof test-profile [flags] <target.ti3> <profile.json | profile.dcp>
The test profile command is used to test how well a profile can match a specific target.

It will print a text summary on the console, for deeper information you should use the -r parameter to dump text files and plots.

Overview of flags:

  • -o <observer>, used if patch values are re-generated, default 1931_2.
  • -c <ssf.json>, camera SSFs, used to re-generate target RGB values, or if you want to analyze the camera's color separation performance.
  • -i <test illuminant>, the illuminant the test is run under, which per default is the same as the profile's calibration illuminant.
  • -I <target XYZ reference values illuminant>, default is same as the test illuminant. Only required if the target lacks spectral data.
  • -w <r,g,b> | m<r,g,b>, provide camera WB as RGB levels or RGB multipliers. Per default white balance is derived from target, or when provided from the camera's SSFs.
  • -L, skip LUT. If the profile has a LUT but you want to test how it performs with only matrix correction enable this flag.
  • -r <dir>, directory to save informational reports and plots.
As always it's preferable with a target file which contains spectra so XYZ reference values can be re-generated rather than having to be converted using a chromatic adaptation transform.

White balance

Per default DCamProf will calculate the optimal white balance to match the target as well as possible. This is analogous to setting white balance in your raw converter with the white balance picker on the white patch on a color checker.

If you instead want to test how the profile will match colors when the camera is set to a different white balance (such as a camera preset) you can provide a custom white balance via the -w setting.

It's given as a balance between red, green and blue, or as channel multipliers. To find out what multipliers a camera is using you can use exiftool on a raw file. White balance can be stored in different ways depending on raw format, it's out of the scope of this documentation to cover it in full. Anyway, in most cases it's some sort of multipliers, and often green is repeated twice, like this:

  WB RGGB Levels Daylight : 15673 8192 8192 10727
And then you simply provide "-w m15673,8192,10727" to DCamProf, note the "m" which say that we provide white balance as multipliers rather than actual resulting balance between the channels which is 1/m.

When DCamProf prints a white balance it will show the balance normalized to 1.0, meaning that the above example is translated to 0.52,1,0.76.

Analyzing camera color separation performance

There is a special feature embedded in the test-profile command, which is that if you provide the camera's SSF you can get an analysis of the camera's color separation performance. This is a pure "hardware" test and has thus no relation to the profile so if you are only interested in this result you can provide any dummy profile.

To get a sane result you need a highly populated grid of patches to test with. I recommend to generate a locus grid, like this:

  dcamprof make-target -c cam-ssf.json -p locus-grid -g 0.01 locus-grid.ti3
This will take quite some time, but once generated you can reuse this grid with any camera since when you provide the SSF and illuminants the RGB and XYZ values will be regenerated from spectra:
  dcamprof test-profile -r dump1 -c cam-ssf.json -i D50 locus-grid.ti3 any-profile.json
To get the plot you need to provide the -r parameter, and then the file is named ssf-csep.dat. You can plot it for example with this gnuplot script:
  unset key
  set palette rgbformula 30,31,32
  set cbrange [0:300]
  plot 'gmt-locus.dat' using 1:2:4 w l lw 4 lc rgb var, \
    'ssf-csep.dat' pt 5 ps 2 lt palette, \
    'gmt-adobergb.dat' w l lc "red", \
    'gmt-pointer.dat' using 1:2:4 w l lw 2 lc rgb var
What you see is a heat-map in a u'v' chromaticity diagram, here limited to 300 max. Each dot shows how much the camera signal will change in 16 bits (65536 steps) for 1 delta E change in chromaticity (= change in hue and saturation with constant lightness). No current camera is really 16 bit, this is just used as a fixed reference to get a number in a comfortable-to-read range. For this type of test you should not worry about a camera's dynamic range and read noise, shot noise will be the limiting factor.

A black dot means that the signal change is zero and thus the camera cannot separate color at that chromaticity location and no profile can ever change that.

The test is run against the target provided and it expects a dense grid-like layout of patches, if your target is coarse there can be misleading results. The locus grid generated in this example makes reflectance spectra, so the colors tested are all related to the illuminant, the colors are as light as the illuminant allows for that chromaticity. This means more saturated colors are naturally bit darker and thus harder to separate. However it becomes harder for the eye too. Often cameras will show good separation capability in the purple range, and that is partly because the eye is relatively poor at it. As the values are related to Delta E they will be related to the eye's capability (as modeled by the observer's color matching functions).

The diagram always shows values relative to a D50 white point. You can test with a different illuminant using the -i parameter. You will see the result changing, but note that the coordinates are always remapped to D50 in the diagram.

Note that the generated locus grid will not go all the way to the edge of the line of purples. This is because the line of purples is actually black (as it's at the border of the eye's sensititivy) so moving in a bit we get saner colors. The spectral generator can still have some issues to reach all the way to the locus and line of purples so you may get some gaps.

This diagram in u'v' chromaticity coordinates shows the color separation capability of a Canon EOS 5D Mark II. The locus, Pointer's gamut and AdobeRGB gamut is shown as reference. Only points that have a patch in the provided target, so here you see some gaps at the borders where there are no test patches.

The unit of the heat map is how many 16 bit units (65536 steps) the camera raw signal changes if the color chromaticity changes with 1 CIEDE2000 unit. The test reflectance spectra is a generated grid related to a D50 illuminant, and is made as bright as possible for each chromaticity coordinate.

The darker heat (lower signal difference) the worse color separation, if it's zero the camera can differ at all. For complete information of limits you need to relate to photon shot noise too, which is out of the scope of this document. What we can see is that the camera gets problems towards the locus, mainly on the cyan side and towards the red corner. We also see it's good at purples, which is partly due to that the eye is not as good and thus it takes more distance to reach one delta E.

We can also see that the diagram is a bit "worried" and that we have a notable minima inside AdobeRGB towards the red corner on the purple side. Some odd minimas here and there and the messy look is typical, as the SSFs differs greatly from the observer's CMFs. We see a smoother behavior in the green area, this is because there all three SSFs are involved in producing the signal.

DCP vs native DCamProf profiles

You can test both DCPs and native profiles. DCPs will be rendered according to the DNG specification, but tone curve and baseline exposure is ignored. If a DCP contains both HueSatMap and LookTable only the HueSatMap is applied (as the DNG profile intention is such that HueSatMap should be about accuracy, LookTable about a subjective "look"). DCamProf will print information about this when run.

By design DCPs cannot represent colors outside the Prophoto gamut triangle, so if you're doing testing with extreme colors close to the locus you will see clipping to the Prophoto gamut edge. Otherwise a DCP should perform about the same as a native profile.

If you test a profile from Adobe or other commercial raw converter you will likely see rather large color errors. This is because those profiles are not designed to reproduce accurate colors, but rather to provide a subjective "look", like film.

Examples

Example 1: test how well profile.dcp matches target.ti3 under illuminant StdA, and write text files and plot data to the directory "dump" (it's assumed target.ti3 has spectra, if not you need to provide the -I parameter too):
  dcamprof test-profile -r dump -i StdA target.ti3 profile.dcp
Example 2: test how well the profile will match colors with a camera white balance preset (found out via exiftool for example):
  dcamprof test-profile -r dump -w m15673,8192,10727 -i D65 target.ti3 profile.json
Example 3: disable the profile's LUT and see how well the matrix matches the target (note that some DCPs designed with other tools are made such that the matrix is very far from correct color and the LUT is required to get close):
  dcamprof test-profile -r dump -L -i D65 target.ti3 profile.dcp

make-dcp

  dcamprof make-dcp [flags] <profile.json> <output.dcp>
Converts a profile in DCamProf native format to Adobe's DNG Camera Profile (DCP) which can be used directly in various raw converters. There's really not much to this command, generally you only run it with the -c flag to specify unique camera name.

Overview of flags:

  • -c <unique camera name>, must match what raw converters are expecting, provide within quotes.
  • -h <hdiv,sdiv>, hue and saturation divisions of DCP HSM (default: 90,30)
  • -F, skip forward matrix, will generate an old-style DNG profile without forward matrix, this is not recommended but may in some rare situations be necessary as some ancient software doesn't support forward matrices.
  • -L, skip LUT (= matrix-only profile)
  • -O, disable forward matrix whitepoint remapping. Most DCP software expects the forward matrix whitepoint to match 1931_2 D50, and if the profile was created using a different observer that will not match and then the profile may not be loaded. Therefore make-dcp will per default remap the whitepoint to match.
  • -r <dir>, directory to save informational reports and plots

HueSatMap LUT generation

The DNG HueSatMap is generated from the native 2.5D LUT in the DCamProf profile. This is done by sampling it at the hue and saturation divisions provided. The default is 90,30 (controlled with the -h parameter) which is a quite dense table and there's little reason to change that. If you'd want to change it it's probably to reduce the table size to get a smaller profile.

The DCamProf native LUT is spline-interpolated while a HueSatMap is linearly interpolated. This means that you may get smoother gradient transitions if you have a bit denser HueSatMap than needed for actual target matching. Therefore I think the 90,30 density is quite good to have even if the profile is based on very few patches.

If you dump plotting data with the -r parameter you will get data for the HueSatMap so you can visualize it. This is useful if you experiment with the table density.

Example plot for comparing native LUT with HSM LUT (useful to see if you should adjust HSM table size):

The plot shows a zoomed in section of the HSM LUT (blue dots) and the native LUT (beige grid).

  splot \
    'nve-lut.dat' w l lc "beige", \
    'hsm-lut.dat' pt 1 lc "blue", \
    'gmt-prophoto.dat' w l lc "red", \
    'gmt-locus.dat' w l lw 4 lc rgb var
The HSM LUT operates in linear Prophoto RGB space, converted to HSV. This means that in an u'v' coordinate system it looks very dense close to the white point, and then becomes gradually less dense.

Manual edits

When running the make-dcp command you can only specify the camera name. If you want to adjust other aspects such as copyright string and more you need to dot this manually by using the dcp2json and json2dcp commands:
  1. dcamprof dcp2json input.dcp dcp-profile.json
  2. edit dcp-profile.json using a text editor
  3. dcamprof json2dcp dcp-profile.json output.dcp
I don't make any specific claims on DCamProf-generated profiles so you can set any name and copyright you like and set a restrictive profile embed policy if you'd like and sell them. I surely like though if you let users use your profiles as freely as I allow people use my software.

Dual-illuminant DNG profiles

Currently DCamProf has no built-in support to directly generate a dual-illuminant profile. However you can do it manually by first creating two separate single-illuminant profiles and then manually merge them by using the dcp2json and json2dcp commands. A good choice of illuminant pair is StdA and D65.

To see how the format is, convert an existing dual-illuminant DCP (as provided by Adobe's DNG Converter for example) to JSON and use that as a guide when merging your own file.

Example

Basic conversion, this is what you will do most of the time (replace name with your specific camera name):
  dcamprof make-dcp -c "Canon EOS 5D mark II" profile.json profile.dcp

Report directory files

When DCamProf is run with the -r <report_dir> flag enabled it will write data files for plotting and report text files. The files are suitable to plot with gnuplot, but you can use any other plotting software if you like as it's just text files with the numbers in columns.

Text files

The report text files contain patch matching reports:
  • cm-patch-errors.txt, color matrix patch matching errors
  • fm-patch-errors.txt, forward matrix patch matching errors
  • lut-patch-errors.txt, LUT patch matching errors
A patch matching row looks like this:
  0 RGB 0.076 0.095 0.040 => XYZ 0.127 0.111 0.055 (0.126 0.109 0.052) DE 1.25 DE LCh 0.16 0.33 1.19 (dark brown)
First there's the patch index (0 in this example) then camera raw RGB values (0.0 - 1.0 range), then CIE XYZ reference values (0.0 - 1.0 range), and then within parentheses what XYZ values the conversion made, and then CIEDE2000 values for color difference between reference and converted value, related to the test illuminant.

The first delta E value is the total with 1,1,1 k weights, the following three is considering lightness (L) chroma (=saturation, C) and hue (h) separately. In the above example we see that most part of the color difference sits in hue (1.19 delta E). Finally there's a text name of the color. This text name is highly approximate and may not really be that correct, but it roughly points out the type of color in lightness (light, dark), chroma (grayish, strong, vivid etc) and hue.

Plot files

It varies a bit between commands and parameters used which files that are produced, but many will be same, for example if the command processes a target it will produce files related to the target.

Most files have u'v' chromaticity coordinates, and if there's lightness there's CIE Luv / CIE Lab lightness divided by 100. The division by 100 is there to make it about the same scale as u'v'. This is the same 3D space as the DCamProf LUT operates in and is roughly "perceptually uniform", that is moving a certain distance in the diagram makes up a certain color difference. However as the space is linear and lightness is normalized it's not as uniform it could be, especially towards the line of purples which in reality goes towards black and thus hard to differ for the eye.

Here's a list of data files you can find in the report directory after a run:

  • Spectra for observer, SSF and illuminant:
    • cmf-x.dat, cmf-y.dat, cmf-z.dat, the observer's color matching functions.
    • ssf-r.dat, ssf-g.dat, ssf-b.dat, the camera's spectral sensitivity functions.
    • illuminant.dat emissive spectrum for the illuminant.
    • illuminant-d50.dat emissive spectrum for the standard illuminant D50.
  • Common gamuts for u'v' chromaticity diagrams, these are flat but have three columns to be compatible in 3D plots:
    • gmt-srgb.dat, sRGB gamut
    • gmt-adobergb.dat, Adobe RGB gamut
    • gmt-prophoto.dat, ProPhoto gamut
    • gmt-pointer.dat, Pointer's gamut
    • gmt-locus.dat, spectral locus for the chosen observer
  • Files related to the test target, for 3D plots with u'v' and L/100 except the spectra which are 2D (see also the target files in the LUT section further down):
    • target-xyz.dat, XYZ reference values for the patches, usually for the calibration illuminant
    • target-spectra.dat, reflectance spectra for the patches
    • target-xyz-<classname>.dat, target-spectra-<classname>.dat, same as above split per target class name.
    • targetd50-*, D50 versions of above. Note that spectra are the same regardless of illuminant as it's the reflectance spectra.
    • Live update of spectra and patches during target generation, can be fun to plot via a gnuplot loop to see spectral generation progress graphically:
      • live-patches.dat XYZ reference values for the chosen illuminant.
      • live-spectra.dat reflectance spectra for the patches.
  • Files related to the LUTs, the target files are all relative to D50 as the LUT works in that space:
    • nve-lut.dat, native LUT stretching in u'v' difference (addition), plus the L multiplier shown as a 1/10th of the difference from 1.0. The reason for the strange L scale is that the LUT stretching on the L scale should be fairly perceptually equal to the chromaticity stretch. That is any bend on the surface should have equal perceptual effect regardless of axis.
    • nve-ref.dat, a plain grid showing a LUT with no correction factors, can be used to plot a reference to compare.
    • nve-lutv.dat, vectors that show the difference from nve-ref.dat to nve-lut.dat
    • hsm-lut.dat, hsm-lutv.dat, hsm-ref.dat, same as the nve-* files, but for the DCP LUT, HueSatMap.
    • target-mtx.dat, the target patches' XYZ positions after matrix-only correction.
    • target-nve-lut.dat, the target patches' XYZ positions after native LUT correction.
    • target-nve-lutvm.dat, vectors showing the difference between matrix-only correction and LUT correction.
    • target-nve-lutve.dat, vectors showing the difference between target reference values (targetd50-xyz.dat) and the profile's final values after LUT, that is the error vectors. For a perfect match these are all zero length.
    • target-hsm-lut.dat, target-hsm-lutvm.dat, target-hsm-lutve.dat, same as the target-nve-* files, but for the DCP LUT.
  • Other files

Example gnuplot scripts

As patch colors are often involved I recommend using gnuplot with a gray background rather than the default white. If you use the X11 terminal you do this by starting gnuplot with the following command gnuplot -background gray. All examples here are adapted for a gray background.

In gnuplot you do 2D plots with the plot command, and 3D plots with splot. It's often useful to view a 3D plot in 2D though, and thanks to gnuplot's isometric perspective viewing a 3D plot straight from above makes it perfectly 2D.

You can rotate a 3D plot using the mouse, and you can zoom in by right-clicking and drawing a zoom-in-box. Type reset and replot to return to the original view. It's not a quick thing to master gnuplot, but with the help of the example scripts here you should be able to get around and do the tasks necessary for visualizing DCamProf data.

You can label the axes etc, but I usually make it simple and just remove all labels with unset key.

Plotting SSF and observer CMF:
  plot \
    'cmf-x.dat' w l lc "pink", \
    'cmf-y.dat' w l lc "greenyellow", \
    'cmf-z.dat' w l lc "cyan", \
    'ssf-r.dat' w l lc "red", \
    'ssf-g.dat' w l lc "green", \
    'ssf-b.dat' w l lc "blue"
Basic plot for a test target, first the target spectra in 2D:
  plot 'target-spectra.dat' w l lc rgb var
The example shows cc24
...and then the target patches in 3D:
  set grid
  splot \
    'gmt-locus.dat' w l lw 4 lc rgb var, \
    'gmt-adobergb.dat' w l lc "red", \
    'gmt-pointer.dat' w l lw 2 lc rgb var, \
    'target-xyz.dat' pt 5 lc rgb var
A suitable plot after a make-profile or test-profile run with a target with relative few patches (such as a cc24):
  splot \
    'nve-lut.dat' w l lc "beige", \
    'gmt-locus.dat' w l lw 4 lc rgb var, \
    'gmt-adobergb.dat' w l lc "red", \
    'gmt-pointer.dat' w l lw 2 lc rgb var, \
    'target-nve-lutvm.dat' w vec lw 2 lc "black", \
    'targetd50-xyz.dat' pt 5 ps 2 lc rgb var
The image shows a zoomed in section, viewed directly from above, so we see a 2D chromaticity diagram with the LUT stretching in the chromaticity dimension. The black LUT vectors are only a little visible as the matrix alone makes a fair match.
A plot after a test-profile run with a dense target, such as a locus grid:
  splot \
    'nve-lut.dat' w l lc "beige", \
    'gmt-locus.dat' w l lw 4 lc rgb var, \
    'gmt-adobergb.dat' w l lc "red", \
    'gmt-prophoto.dat' w l lc "blue", \
    'gmt-pointer.dat' w l lw 2 lc rgb var, \
    'target-nve-lutve.dat' w vec lc "black"
Here we only plot the error vectors, the actual color (reference XYZ) is at the start of the arrow and where it ends up after profiling is at the end of the arrow. For a perfect profile on a perfect camera the vector length should thus be zero over the whole field. As we can see in the example to the right errors typically grow large towards the locus, the matrix even moves points outside the human gamut.
A plot after a test-profile run with a DCP profile:
  splot \
    'hsm-lutv.dat' w vec lc "beige", \
    'gmt-locus.dat' w l lw 4 lc rgb var, \
    'gmt-adobergb.dat' w l lc "red", \
    'gmt-prophoto.dat' w l lc "blue", \
    'gmt-pointer.dat' w l lw 2 lc rgb var, \
    'targetd50-xyz.dat' pt 5 ps 1.2 lc rgb var
Here we plot the DCP HSM LUT as vectors, it can't be plotted like a grid like the native LUT. The vectors show each table position at vector start and their shift in chromaticity and lightness at vector end. Note that a DCP HSM LUT actually changes values through multiplication in linear Prophoto RGB HSV space, that's why the LUT looks like a star fitted in the prohoto triangle with high density at the white-point. The lightness axis has been transformed to match the same scale as the native LUT so the LUTs can be compared directly.

Be careful to watch gnuplot's auto-scaling of axes. The lightness axis in a LUT often gets greatly exaggerated due to it's not plot at the same scale as chromaticity. You can control the scales with brackets, use the same scale range on all axes:

  splot [0:0.6] [0:0.6] [-0.3:0.3] \
    'nve-lut.dat' w l lc "beige", \
    'gmt-prophoto.dat' w l lc "red", \
    'gmt-locus.dat' w l lw 4 lc rgb var
With equal scale on the L axis a LUT typically looks very flat as L adjustments are generally minor.

More example scripts are found throughout the documentation.

Call for spectral databases and camera SSFs

DCamProf contains some built-in spectral data that has been retrieved from public sources. I'd like to have more. A database with spectral reflectance of human skin is currently the most desired, useful for rendering portrait profiles.

There are reference standard sets such as the ISO TR 16066, but those are not free and cannot be freely redistributed so I can't include that in DCamProf.

If you know of any database you think is useful for inclusion please let me know.

The other aspect is camera SSFs. It's quite complicated and/or costly to measure camera SSFs so most users will not be able to do that and thus have to rely on public sources. If you can provide camera SSFs or have links to sources I have missed please let me know.

Links to camera SSFs

Links to spectral databases

Acknowledgments

I'd like to thank those that have made camera SSFs and spectral databases available, without those DCamProf would not have been possible in its current form. Currently DCamProf has spectral databases from University of Eastern Finland and BabelColor, see the section with links to spectral databases for references.

Thanks to Mike Hutt for the Nelder-Mead simplex implementation which is used in DCamProf for solving various multi-variable complex optimization problems. I also want to thank Jarno Elonen for publishing a thin plate spline implementation which served as base for the DCamProf TPS used for getting a smooth LUT.

The copyright for the TPS source is required to be repeated in the documentation, so here it is:

  Copyright (C) 2003, 2004 by Jarno Elonen

  Permission to use, copy, modify, distribute and sell this
  software and its documentation for any purpose is hereby
  granted without fee, provided that the above copyright
  notice appear in all copies and that both that copyright
  notice and this permission notice appear in supporting
  documentation. The authors make no representations about
  the suitability of this software for any purpose. It is
  provided "as is" without express or implied warranty.



(c) Copyright 2015 - Anders Torger.