Drawing multiple anti-aliased lines

Have there been any further suggestions for how to draw several anti-aliased lines (like psychtoolbox’s DrawLines) since this discussion: https://groups.google.com/forum/#!searchin/psychopy-users/drawlines|sort:relevance/psychopy-users/77zgZC_wJhw/RGaDriQqcsUJ ?
This is my current attempt:

visual.ElementArrayStim(win, nElements=nLines, elementTex='sqr',
elementMask='None', 
sizes=np.c_[np.ones(nLines) * lineWidthDeg, np.ones(nLines) * lineLengthDeg], 
oris=oris, xys=xys, colors=colors)

nLines is just a number of lines and lineWidthDeg is the width of the lines in degrees, lineLengthDeg is the line length in degrees.
With this I still get aliasing even though interpolate=True is the default.

Perhaps a simpler question is how to get just a single anti-aliased line using gratingStim, i.e.

visual.GratingStim(win, tex='sqr', mask='None', 
size=[lineWidthDeg, lineLengthDeg], interpolate=True)

Or alternately, if someone knows a way to do this with Line, that would work to!

Thank you for all your help!

Hello,

Are you using a window where useFBO=True? There is a known bug that causes anti-aliasing to fail.

I am not, this is just running on my Mac laptop for testing right now. But doesn’t seem to make a difference whether I set it to True or False.
Just for the record, this is my call to Window

sizePix = [1440, 900]
win = visual.Window(sizePix, monitor='testMonitor', units='deg', fullscr=True, useFBO=False)

That is an archaelogical post from before PsychoPy actually had simple geometric stimuli, so I would discount that completely…

You should be using the built-in shape stimuli, as described here:
http://www.psychopy.org/api/visual.html

rather than the grating stimuli.

These should appear anti-aliased by default. Paradoxically, they don’t (currently) look so great on a retina-level display, though.

Check out the shapes.py Coder demo from the demos menu. It will show you how to work with these sorts of stimuli.

Hmm I’m not sure shape is exactly what I’m looking for, I’d like to be able to draw to draw something like this (but without the aliasing):


It seems like shape only allows for connected lines (without some sort of hack where you draw a bunch of lines that are the same color as the background). Is this correct? If so is there a better way to do something like this image?

On the page above, you’ll see a list showing that ShapeStim is a parent class with a variety of sub-classes, including LineStim:

http://www.psychopy.org/api/visual/line.html#psychopy.visual.Line

Anti-aliasing seems to work for me in the below example - and does not work with useFBO=True, as mentioned by @mdc.

import numpy as np

import psychopy.visual
import psychopy.event

win = psychopy.visual.Window(
    size=(600, 600),
    fullscr=False,
    units="pix",
    useFBO=False
)

for _ in range(10):

    p0 = np.random.uniform(-200, 200, 2)

    line = psychopy.visual.Line(
        win=win,
        start=p0,
        end=p0 + 40,
        lineColor=np.random.choice([-1, 1])
    )

    line.draw()

win.flip()

cap = np.array(win.getMovieFrame())[..., 0]

print len(np.unique(cap))

psychopy.event.waitKeys()

win.close()
1 Like

@mdc @djmannion Thank you for all your help so far. I was aware of the Line function but I would like to be able to present up to 200 lines (not just 10) at once and flicker them. It seems like the only way to present multiple lines using Line is in a for loop (like @djmannion suggested) but with 200 lines it ends up dropping several frames. This was my original reason for trying to use ElementArrayStim but I ran into the anti-aliasing issue. Is there any way to create an array of lines and present it without a for loop? Or is there any other way to get something that works like DrawLines in psychtoolbox (http://docs.psychtoolbox.org/DrawLines)?

You could write some custom OpenGL code to get functionality like PTB’s DrawLines. But, perhaps your ElementArrayStim approach could work sufficiently if you do the anti-aliasing beforehand? For example (using skimage):

import numpy as np

import psychopy.visual
import psychopy.event

import skimage.draw

win = psychopy.visual.Window(
    size=(600, 600),
    fullscr=False,
    units="pix"
)

line = np.ones((64, 64)) * -1.0

(i_rows, i_cols, line_vals) = skimage.draw.line_aa(4, 4, 60, 60)

line[i_rows, i_cols] = (line_vals * 2.0) - 1.0

lines = psychopy.visual.ElementArrayStim(
    win=win,
    nElements=200,
    xys=np.random.uniform(-200, 200, (200, 2)),
    elementTex=np.ones((4, 4)),
    elementMask=line,
    contrs=np.random.choice([-1, 1], 200),
    sizes=(64, 64)
)

lines.draw()

win.flip()

psychopy.event.waitKeys()

win.close()

Or you could render a Line offscreen first (using BufferImageStim) and then use that in an ElementArrayStim:

import numpy as np

import psychopy.visual
import psychopy.event

win = psychopy.visual.Window(
    size=(600, 600),
    fullscr=False,
    units="pix"
)

line = psychopy.visual.Line(
    win=win,
    start=(-20, -20),
    end=(20, 20)
)

buf = psychopy.visual.BufferImageStim(
    win=win,
    stim=[line],
    rect=[-64 / 600.0, 64 / 600.0, 64 / 600.0, -64 / 600.0]
)

line_tex = np.flipud(np.array(buf.image)[..., 0]) - 127
line_tex = line_tex.astype("float") / line_tex.max() * 2.0 - 1.0

lines = psychopy.visual.ElementArrayStim(
    win=win,
    nElements=200,
    xys=np.random.uniform(-200, 200, (200, 2)),
    elementTex=np.ones((4, 4)),
    elementMask=line_tex,
    contrs=np.random.choice([-1, 1], 200),
    sizes=(64, 64)
)

lines.draw()

win.flip()

psychopy.event.waitKeys()

win.close()

Hello,

If you are using the newest version of PsychoPy with pyglet, you might be able to switch on MSAA to get anti-aliasing. Use the multiSample=True and numSamples=8 or 16 when starting your window (make sure useFBO=False). Keep in mind MSAA results in a performance hit on older graphics hardware and is a bit different visibly than the usual alpha blending technique.

I recommend trying this if all else fails.

1 Like

Thanks @mdc, I had a similar problem and your solution works!