This guide is part of a series on VPixx Technologies’ specialty high bit depth video modes. Here we will discuss how to create high bit depth colour using C48 mode. C48 is compatible with the following VPixx hardware:
- the first generation DATAPixx video I/O hub and a CRT monitor
- our VIEWPixx and VIEWPixx /3D CRT replacement monitors, and
- our PROPixx projector
For a general introduction to high bit depth, please see the first guide in our series, on M16 grayscale mode.
C48: 16 bits per colour, halved horizontal resolution
In C24, which is our standard operating mode, the red, green and blue components of a pixel are assigned 8 bits each. This means that each colour has 28 or 256 levels of intensity (0-255). Most commercial displays are 8 bits per colour (bpc).
In C48, data from horizontally adjacent pixels are combined so that each colour channel receives 16 bits instead. The leftmost pixel of the pair contributes the 8 most significant bits, and the rightmost pixel contributes the 8 least significant bits.
At 16 bpc, your red, green and blue colour channels now have 216 or 65,536 levels of intensity (0-65535). However, as a consequence of this pixel combination your image’s horizontal resolution is halved. So if your 8 bpc stimuli were drawn in full HD (1920 x 1080 pixels), your 16 bpc image will be reduced to 960 x 1080 pixels.
Our hardware will present this 16 bpc image data full screen, full resolution on the display. The image data is scaled to double-width to get the final display output.
This means that if you do nothing to adjust your stimuli in C48, they will appear horizontally stretched on your display.
If you are working in MATLAB or Octave, there are several PsychImaging functions to simplify working in C48 mode. These functions allow you to draw your images directly in 16 bpc, so you don’t need to worry about combining 8 bpc pixels yourself. The backend graphics management will then handle formatting the image data for our hardware.
These PsychImaging functions also offer some different options for rescaling your stimuli automatically so they don’t appear stretched. For more on scaling options, see our C48 examples below.
The major advantage of C48 is having access to the full spectrum of 16-bit colours on each video frame. This is a great mode for rich multicoloured images, complex gradients or colour wheels, and colour- or luminance-based visual search tasks where a fine degree of control is needed.
More levels of colour intensity does not mean a larger colour space. The range of colours a display can show (called the “gamut”) is determined by the hardware and remains the same across video modes. Adding more levels of a colour simply means you can divide your colour space into finer step sizes, leading to smoother gradients and more precise control over your output.
16 bpc refers to the characteristics of video signal. The final output of your display depends on the screen hardware. For instance, our PROPixx and VIEWPixx displays can show up to 12 bpc, while the VIEWPixx /3D can show 10 bpc. Some CRT monitors can show all the way up to 16 bpc.
In cases where the video signal bit depth exceeds what your display can show, the least significant bits (i.e., the smallest steps) are ignored.
Example 1: Drawing a simple luminance patch in C48
In this example, we enable drawing in 16 bpc and C48 mode. Our stimulus is a luminance patch with a randomly selected 16 bpc gray level on a black background. We would like our output to be square, so we manually scale our patch width to half the height. This ensures the stimulus will be stretched to the desired dimensions on our display.
%Establish the correct colour and video settings AssertOpenGL; PsychImaging('PrepareConfiguration'); %Draw stimuli in 16 bits per colour PsychImaging('AddTask', 'General', 'FloatingPoint32Bit'); %Enable C48 and set framebuffer to half the display resolution (no automatic scaling) PsychImaging('AddTask', 'General', 'EnableDataPixxC48Output', 0); %Open a full screen window with a black background screenNumber = max(Screen('Screens')); [win, rect] = PsychImaging('OpenWindow', screenNumber, [0,0,0]); %Set up some position parameters center = [rect(3)/2, rect(4)/2]; patchHeight = 200; patchWidth = patchHeight/2; %rescale to half the height, so our final stretched image appears square patchRect = [center(1) - patchWidth/2, center(2) - patchHeight/2, ... center(1) + patchWidth/2, center(2) + patchHeight/2]; %Let's pick an random gray level between 0-65535, which is our maximum value at 16 bpc. This must %be expressed as a value from 0-1. maxIntensity = (2^16)-1; colourValue = randi(maxIntensity)/maxIntensity; %Draw our patch and flip Screen('FillRect', win, colourValue, patchRect); Screen('Flip', win); WaitSecs(4); %Close window Screen('Closeall');
Example 2: Drawing a gradient of dots in C48 mode, using automatic scaling
In this example, we are using the Screen(‘DrawDots’) command to draw a gradient of dots in a ring around the middle of the display. Like the previous example, we will draw our stimuli directly in 16 bpc.
DrawDots does not allow you to manually scale dots to half width. Instead, we will use one of the automatic scaling modes available with PsychImaging(‘AddTask’, ‘General’, ‘EnableDataPixxC48Output’, modes). This will ensure our dots appear as circles on the display, rather than stretched ovals.
As of April 2022, there are three scaling modes to choose from:
0 – Image framebuffer is set to half the display resolution. This preserves 100% of whatever you draw, but if you do not manually scale your stimuli (as in example 1) they will appear stretched on the final full resolution display.
1 – Image framebuffer is set to full display resolution. When the half-resolution image data is generated to send to the display, odd pixels in the framebuffer are ignored. Stimuli appear with the correct dimensions on the final full resolution display.
2 – Image framebuffer is set to full display resolution. When the half-resolution image data is generated to send to the display, the 16 bpc values of adjacent pixels are averaged to a single value. Stimuli appear with the correct dimensions on the final full resolution display.
Which of these modes you use depends on your stimuli and what you are trying to achieve. Here we will use mode 2, as the pixel averaging creates some nice antialiasing around the edges of our dots and makes them look smoother.
%Establish the correct color and video settings AssertOpenGL; PsychImaging('PrepareConfiguration'); PsychImaging('AddTask', 'General', 'FloatingPoint32Bit'); %Our stimuli are circular and hard to scale manually, so we will use mode 2, which averages adjacent %pixels to scale the image to half-resolution for us. You can also use mode 1, but mode 2 gives some %nice antialiasing around the edges of circular stimuli. PsychImaging('AddTask', 'General', 'EnableDataPixxC48Output', 2); %Open a full screen window with a black background screenNumber = max(Screen('Screens')); [win, rect] = PsychImaging('OpenWindow', screenNumber, [0,0,0]); %Set up some parameters maxIntensity = (2^16)-1; numDots = 100; radius = 400; %pixels radiusCenter = [rect(3)/2, rect(4)/2]; dotSize = 10; theta = (pi*2)/numDots; %Initialize dot parameters positions = nan(2,numDots); colours = nan(3,numDots); %A simple loop to assign dot positions and create an evenly-spaced red gradient for k=1:numDots positions(1, k) = radius * cos(theta*k); positions(2, k) = radius * sin(theta*k); colours(:, k) = [((maxIntensity/numDots)*k)/maxIntensity,0,0]; end %Draw all of our dots and flip Screen('DrawDots', win, positions, dotSize, colours, radiusCenter, 1); Screen('Flip', win); WaitSecs(4); %Close window Screen('Closeall');
Not sure if you are actually in C48 mode? Psychtoolbox prints detailed information to the command line whenever a new window is opened; under “PTB – Info” you should see C48 and your scaling mode identified. You can also check in PyPixx under “Video Mode Configuration” (while the window is open), or by disabling any scaling and confirming your stimuli are horizontally stretched.
These methods will confirm what video mode the hardware is in. Only an external measurement tool like a colorimeter or spectrophotometer can externally verify your display colour/luminance output.
Cite this guide
Fraser, L., (2022, April 11). High Bit Depth Colour with C48 Video Mode. Retrieved [Month, Day, Year], from https://vpixx.com/vocal/C48