Search code examples
matlabalphapsychtoolbox

How do I make a PNG file display in Psychtoolbox with a transparent background?


I have two images I need to display concurrently on screen, using the Screen('DrawTexture') function. One image is a scene image, and second is an object, in which the background is transparent. I want to display the object on top of the scene image. However, when I try this, the object appears to have a black background.

There's definitely no problem with the object image; when called with [object,map,alpha] = imread(objectimage.png), the alpha value returns an appropriately-sized matrix. I have also successfully displayed these images, one on top of the other, using Python. However, due to various research reasons, this project cannot be written in Python.

I've tried looking for solutions, but the only solutions I can find relate to figures or plots, not Screen. I suspect I need to do something with alpha blending (possibly something quite basic), but I've not been able to find any beginner-friendly guides.

My test code currently looks like this:

% screen setup
PsychDefaultSetup(2); Screen('Preference', 'SkipSyncTests', 1);
screenNum = max(Screen('Screens')); % set screen 
Screen('Preference','VisualDebugLevel',3);
[w,rect] = Screen('OpenWindow',screenNum);
% Activate for alpha
Screen('BlendFunction', w, 'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA');

% image presentation rectangles
bigImSq = [0 0 500 500];
[bigIm, xOffsetsigB, yOffsetsigB] = CenterRect(bigImSq, rect);
smImSq = [0 0 250 250];
[smallIm, xOffsetsigS, yOffsetsigS] = CenterRect(smImSq, rect);

% IMAGES
sceneIm = 'scene.png'; 
objIm = 'object.png';
sceneLoad = imread(sceneIm); 
[objLoad,map,alpha] = imread(objIm);

% final textures for display
scene = Screen('MakeTexture',w,sceneLoad); 
object = Screen('MakeTexture',w,objLoad); 

% Image presentation
grey = [100 100 100];

Screen('FillRect',w,grey); 
Screen('Flip',w); 
WaitSecs(0.5);

Screen('FillRect',w,grey);
Screen('DrawTexture', w, scene,[],bigIm); % draw the scene 
Screen('DrawTexture', w, object,[],smallIm); % draw the object 
Screen('Flip',w); 
WaitSecs(3);

Screen('CloseAll');

Any advice would be appreciated!


Solution

  • I think all you need to do is to include the alpha channel in your image during MakeTexture.

    % slightly modified boilerplate -- non-fullscreen by default,
    % and set the background color (no need for all the FillRects)
    PsychDefaultSetup(2); 
    Screen('Preference', 'SkipSyncTests', 1);
    screenNum = max(Screen('Screens')); % set screen 
    Screen('Preference', 'VisualDebugLevel', 3);
    [w, rect] = Screen('OpenWindow', screenNum, [100 100 100], [0 0 400 400]);
    
    Screen('BlendFunction', w, 'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA');
    
    % image presentation rectangles
    smImSq = [0 0 250 250];
    [smallIm, xOffsetsigS, yOffsetsigS] = CenterRect(smImSq, rect);
    

    Here's an image with a transparent background (for reproducibility).

    % from http://pngimg.com/upload/cat_PNG100.png
    [img, ~, alpha] = imread('cat.png');
    size(img)
    %% 2557 x 1993 x 3 (rgb)
    

    We'll make one texture without the alpha channel, and one with.

    texture1 = Screen('MakeTexture', w, img);
    img(:, :, 4) = alpha;
    texture2 = Screen('MakeTexture', w, img);
    

    First, the image without the alpha channel.

    Screen('DrawTexture', w, texture1, [], smallIm);
    Screen('Flip', w);
    

    Then, the RGBA texture.

    Screen('DrawTexture', w, texture2, [], smallIm);
    Screen('Flip', w);
    sca;