Code covered by the BSD License  

Highlights from
Consistent imaging with consumer cameras

image thumbnail

Consistent imaging with consumer cameras

by

 

08 Jul 2013 (Updated )

Accurate color capture using consumer cameras.

demo.m
%
% demo.m - RUN THIS SCRIPT CELL BY CELL TO SEE HOW THE FUNCTIONS IN THE
% BUNDLE ARE USED.
%
% The examples here are given for a Macbeth ColorChecker, but they can easily
% be extended to other color charts. See individual files for each
% script for details of input and output arguments.
% 
% ************************************************************************
% If you use this code, please cite the following paper:
%
% Akkaynak et al
% Use of commercial off-the-shelf (COTS) digital cameras for scientific data acquisition  % and scene-specific color calibration, Journal of the Optical Society of America A, Vol. % 31, No.2, February 2014
% 
%
% ************************************************************************
% For questions, comments and bug reports, please use the interface at
% Matlab Central/ File Exchange. See paper above for details.
% ************************************************************************

clear all; close all;clc; warning off

%% LOAD AN IMAGE THAT HAS A CALIBRATION TARGET IN THE SCENE
% In this example, the input image a linear TIFF image, which was converted from .dng format using Adobe DNG Converter. 
% You may skip of modify this step if you already have a linear RGB image.
% If you know you don't have a linear image, but know which format your
% image is in, i.e. sRGB, proPhotoRGB etc, you may undo the gamma
% correction and use this code.

I = im2double(imread('testImage.tiff'));

s = size(I);
figure;imshow(I)
title('Linear image','fontsize',20)


%% LOAD COLOR CHART DATA
% This example is given for a Macbeth ColorChecker - you can load other charts data saved in the
% same format. To see the format, open MacbethColorChecker.m.

load MacbethColorCheckerData.mat

%% MAKE MASKS FOR THE PATCHES OF THE COLOR CHART
% This script works for a Macbeth chart but it can be modified to work for other charts.
% It places the masks for each patch on the image, and waits for the user to drag each mask over the correct patch.
% Once each mask is aligned its corresponding patch, the user should double click the first patch in the chart
% -- that will accept the masks for all patches. In a Macbeth ColorChecker, the first patch is the Dark Skin.

masks = makeChartMask(I,chart,colors);

%% CHECK (AND CORRECT FOR) LINEARITY OF CAMERA RESPONSE 
% - ONLY UNCOMMENT IF YOUR IMAGE IS NOT LINEAR RGB
% 
% If you started with a raw images (i.e, used Adobe DNG converter to
% demosaic it) your image is linear. skip this cell.if not, uncomment it.
%

% The location and Y values for gray neutral (gray) patches given here are for those in the last row of a Macbeth ColorChecker.
% Modify these to work for your color chart, if different than a Macbeth
% ColorChecker.

% neutralPatches = [4 1; 4 2; 4 3; 4 4; 4 5; 4 6]; % location of patches
% neutralTarget = [89.57 57.76 35.15 19.44 9.08 3.43]./100; % Y value of patches, published or measured
% neutralValues = getPatchValues(I,masks,neutralPatches,colors);
% linFactors = getlincam(neutralTarget,neutralValues);
% Ilinearized = linearizeRGB(linFactors,I);
% plotLinearization(neutralValues,neutralTarget,linFactors);
% figure;imshow(Ilinearized)
% title('Linearized image','fontsize',20)

%% APPLY WHITE BALANCE
% The locatio of the WS and DS are for a Macbeth chart, modify as needed.
Ilinearized = I;
whitePatch = [4 1];
darkPatch = [4 6];
Iwhitebalanced = whiteBalance(Ilinearized,masks.(colors{whitePatch(1),whitePatch(2)}).mask,masks.(colors{darkPatch(1),darkPatch(2)}).mask,0.8);
figure;imshow(Iwhitebalanced)
title('white balanced image','fontsize',20)
%% DERIVE THE TRANSFORMATION MATRIX T
% This matrix is derived from the published XYZ values for the Macbeth ColorChecker patches, and the RGB values the camera 
% recorded in the image I. To use with habitat chart, derive the RGB and XYZ values from spectra of
% features from the habitat. See spectra2XYZ.m to get tri-stimulus values.

RGB = getChartRGBvalues(Iwhitebalanced,masks,colors);
XYZ = getChartXYZvalues(chart,colors);
T = XYZ' * pinv(RGB)';

%% APPLY T TO A NOVEL IMAGE

Ixyz = reshape((T*reshape(Iwhitebalanced,[s(1)*s(2) 3])')',[s(1) s(2) 3]);
Irgb = XYZ2ProPhoto(Ixyz); % ProPhoto is a wide gamut RGB space that won't clip most colors.
figure;imshow(Irgb);title('Color transformed image','fontsize',20)

%% EXAMPLE OF OBTAINING TRI-STIMULUS VALUES FROM SPECTRA, i.e. to perform scene-specific color calibration
clear all; close all;clc; warning off
% use reflectance spectra from a Macbeth ColorChecker
% use CIE D50 light profile
% use human CMF
load MacbethColorCheckerData.mat
load CIED50.mat
load humanCMF.mat
%
scolors = size(colors);
XYZfromSpectra = zeros(scolors(1)*scolors(2),3);
% The light spectra length should be adjusted to match that of the reflectance
D50 = interp1(wlD50,D50,chart.wavelength);
% CMF spectra length should be adjusted to match that of the reflectance
CMF(1,:) = interp1(StdObsWL,xbar,chart.wavelength);
CMF(2,:) = interp1(StdObsWL,ybar,chart.wavelength);
CMF(3,:) = interp1(StdObsWL,zbar,chart.wavelength);

n = 1;
for row = 1:scolors(1)
    for col = 1:scolors(2)
    curColor = colors{row,col};
    reflectance = chart.(curColor).reflectance;
    XYZfromSpectra(n,:) = spectra2XYZ(reflectance,D50,CMF);
    n = n+1;
    end
end

% now visualize the XYZ values in approximate RGB colors to check if the
% conversion worked and compare it to published XYZ values for a Macbeth
% ColorChecker
XYZMacbeth = getChartXYZvalues(chart,colors);

figure
visualizeXYZ(XYZfromSpectra)
title('colors obtained from spectra','fontsize',20)

figure
visualizeXYZ(XYZMacbeth)
title('colors obtained from published XYZ','fontsize',20)

Contact us