File: CreatePseudoGrayLUT.m

package info (click to toggle)
psychtoolbox-3 3.0.19.14.dfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 86,796 kB
  • sloc: ansic: 176,245; cpp: 20,103; objc: 5,393; sh: 2,753; python: 1,397; php: 384; makefile: 193; java: 113
file content (137 lines) | stat: -rw-r--r-- 5,742 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
function [lut, numuniquelevels] = CreatePseudoGrayLUT(nativeBpc)
% [lut, numuniquelevels] = CreatePseudoGrayLUT(nativeBpc)
%
% Create and return a 3 rows by 2^(nativeBpc + 4) columns RGB16 uint16
% color lookup table 'lut', suitable as lut for the generic LUT based
% luminance output formatter of the Psychtoolbox imaging pipeline. This
% is a helper function for PsychImaging(), usually not directly called by
% user code.
%
% This will setup the pipeline for output of luminance images with about
% 1786 different levels of perceived luminance on a standard 8 bits per
% color channel RGB framebuffer of a standard graphics card. Also supports
% more levels on a 10 bpc or 12 bpc framebuffer.
%
% In general, under optimal conditions, for a suitable and well calibrated
% display, a luminance precision increase of up to 2.8 bits on top of the
% native output precision of the framebuffer and display device should be
% possible. However, this has not been verified yet, especially for the higher
% bit depths modes.
%
% Usage:
%
% At the beginning of your script, replace the standard ...
%
% win = Screen('OpenWindow', screenid, ...);
%
% ... call by the following commands:
%
% PsychImaging('PrepareConfiguration');
% PsychImaging('AddTask', 'General', 'EnablePseudoGrayOutput');
% win = PsychImaging('OpenWindow', screenid, ....);
%
%
% Further explanation:
%
% The technique to represent 10.7 bits (log2(1786)) of luminance on a 8 bit
% display is called "Pseudo Gray" or "Bit-Stealing". It adds small delta
% values to the different color channels to "tilt" the RGB color vector of
% each pixel a bit away from the "pure luminance' axis. This slight tilt,
% combined with the different emission characteristics of red, green and
% blue monitor phospor and the different sensitivity of the human eye for the
% three different colors, will create the perception (or illusion) of extra
% luminance values. So far the theory. In practice you'll need a well
% calibrated and gamma corrected color monitor for this to work, and there
% may be some smallish color artifacts in your stimuli.
%
% Anyway, this is the "poor man's solution" to high bitdepths luminance
% output, mostly here for illustration and demo purposes. If you need well
% controlled high quality high resolution luminance output, check out our
% different drivers for different high precision display devices like video
% attenuators, the VideoSwitcher, CRS Bits++ box and the AMD/ATI 10 bit
% framebuffer driver. See "help PsychImaging" for an overview and usage.
%
% The LUT encoding used here is based solely on the algorithm described at
% this webpage by Richard W. Franzen:
%
% <http://r0k.us/graphics/pseudoGrey.html>
%
% The webpage refers to multiple different sources of this type of
% algorithm, as apparently the principle was described by multiple independent
% authors. There is also a reference to the "Bit stealing" technique by:
%
% Tyler C.W., Chan H., Liu L., McBride B. & Kontsevich L.L. (1992)
% "Bit-stealing: How to get 1786 or more grey levels from an 8-bit color
% monitor", Proc. SPIE #1666, pp 351-364.
%
% However, i haven't ever read that article, so i don't know if it proposes
% exactly the same procedure although readers of that article told me that
% it is "basically the same".
%

% History:
% 06/01/08  mk  Written.

if nativeBpc < 1
    warning('CreatePseudoGrayLUT: Bogus 0 bpc native framebuffer depths provided! Aborting...');
    lut = [];
    return;
end

if nativeBpc == 16
    warning('CreatePseudoGrayLUT: Our PseudoGray implementation can not handle 16 bpc native framebuffers. Assuming this is actually a 12 bpc one, and proceeding accordingly.');
    nativeBpc = 12;
elseif nativeBpc > 12
    warning('CreatePseudoGrayLUT: Our PseudoGray implementation can not handle more than 12 bpc native framebuffers! Aborting...');
    lut = [];
    return;
end

global foo

% "Boost table" btable maps 4 LSB's to boost values for red, green and blue channel:
btable = (zeros(3,16));
% These slots are boosted by one in red channel:
btable(1, 1+[5:8, 14:15]) = 1;
% These slots are boosted by one in green channel:
btable(2, 1+[9:15]) = 1;
% These slots are boosted by one in blue channel:
btable(3, 1+[2:4, 7:8, 11:13]) = 1;

% Allocate uint8 lut for 3 channels RGB, with 1786 different levels of
% pseudo-gray, but distributed over 4096 slots -- Oversampled to simplify
% lut generation in code below:
lut = uint16(zeros(3, 2^(nativeBpc + 4)));
foo = [];
% Initialize LUT with standard pseudo-gray encoding:
for i=0:length(lut)-1
    % Initialize slot 'i':
    
    % Compute 4 bit LSB index into table of color boost values:
    boostidx = mod(i,16) + 1;

    % Read from table:
    boost = btable(:, boostidx);

    % Base color value is just the 8 MSB in the normal 0-255 range:
    base = (bitshift(i, -4));

    % Add boost value to each base value to get final value, then bitshift,
    % so that the maximum value for any given nativeBpc fills out the nativeBpc
    % most significant bits of the uint16 16 bit entry.
    lut(:, i+1) = uint16(bitshift(([base; base; base] + boost), 16 - nativeBpc));
    foo(end+1) = norm(double(lut(:, i+1)));
end

% Debug code: Compute number of unique color triplets, aka displayable
% levels of pseudo-gray in LUT: This comes out as the expected 1786 levels:
dlut = double(lut);
hashlut = dlut(1,:) + 65536 * (dlut(2,:) + 65536 * dlut(3,:));
numuniquelevels = length(unique(hashlut));

fprintf('CreatePseudoGrayLUT: The LUT has %i unique potential luminance encodings, for up to %f bpc simulated luminance precision.\n',...
        numuniquelevels, log2(numuniquelevels));

% Done. Return the 'lut', which is ready for use with the generic
% attenuator driver of PsychImaging():
return;