Processing.py issues with saveFrame(): bug or a mistake on my end?

Hi all,

I’ve been making some really simple, noobish programs and haven’t had any issues until writing one that uses the P2D renderer. I’ve been searching online and have found folks having similar issues, but I can’t seem to resolve it in processing.py. I am having two main issues: issue #1 is that when I am using a keyPressed() function to saveFrame(), when I hit the specified key, it exits the display window instead of saving the frame. Sometimes copying and pasting all of my code into a new file seems to fix the issue, and the frames will save, but then I wind up with issue #2.

Issue #2 is that when saveFrame() is “working”, the frames are outputting as a black image, instead of the actual frame. Once it starts putting out a black frame, I can’t get anything else. I have been running similar sketches that don’t use the P2D renderer, and I have zero issues.

Here is my code:

rstroke = 12

def setup():
    size(800,800, P2D)
   
def draw():
    size(800,800, P2D)
    noLoop()
    img = loadImage("starpattern.JPG")
    background(255)
    
    def gn():
        return random(100,600)
    
    a = gn()
    b = gn()
    c = gn()
    d = gn()

    beginShape()
    texture(img)
    vertex(a,b,a,b)
    vertex(c,b,c,b)
    vertex(c,d,c,d)
    vertex(a,d,a,d)
    endShape(CLOSE)
    
    strokeWeight(random(rstroke))
    noFill()
    circle(gn(),gn(),gn())
    
    strokeWeight(random(rstroke))
    line(gn(),gn(),gn(),gn())
    
    strokeWeight(random(rstroke))
    line(gn(),gn(),gn(),gn())

def mouseClicked():
    loop()

def keyPressed():
    if key == 's':
        saveFrame("design-####.png")

If anyone has some insight as to what I might be doing wrong, or whether there is some sort of workaround, I would be super grateful!

1 Like

Welcome @ginataka

Firstly, there’s no need for a size() function in your draw() block:

def draw():
    size(800, 800, P2D)  # delete this line
    ...

I suspect the issue has something to do with your loadImage() path. If you comment out the following two lines, will the sketch run properly?

def draw():
    ...
    #img = loadImage("starpattern.JPG")
    ...
    #texture(img)

If it works now, double-check that you’ve referenced your image correctly (it must be named starpattern.JPG, reside the sketch’s root or data sub-directory, etc.)

1 Like

Welcome @ginataka !

Thanks for taking the time to explore Processing.py, for not giving up when problems arise and asking an insightful question.

As @tabreturn mentioned you should use size only inside setup (when using the setup/draw structure). I think loadImage() expects the image file to be in a folder named data inside your sketch folder.

I had some issues with the P2D renderer (needed for the textured shape, I suppose) on my Linux machine with an onboard video card. If this is your case, perhaps you could try this at the beggining of your sketch:

from java.lang import System 
System.setProperty("jogl.disable.openglcore", "false") 
1 Like

Thank you both for getting back to me so quickly, @tabreturn and @villares!

Shoot, I knew I should have double-checked my code before posting it on here. I did a quick check at the last second to see if moving my size() into draw() would have any effect (got desperate, haha). So that’s why I had my size() in two places.

So far, commenting out the loadImage() and texture() doesn’t solve the issue, and I definitely have the image stored in a file called “data”. I also tried the System import you suggested, @villares, and it seems to not be affecting the output.

Not sure if it is worth noting, but earlier I was trying to run the same program with Processing 3.2, and I couldn’t get the display window to load at all. After updating to a later version of Processing (3.5), the window is loading but with the saveFrame() issue. The strangest thing is that initially it was working, and it seems like it became progressively more buggy.

Thank you both again for the input, I’m gonna keep fiddling with it and I’ll report back if I find any solutions!

1 Like

My monitor is small so I reduced the size, added the jogl.disable I need to use P2D with my video card, and tested first image() to see if the image was loading (because I usually type the file names wrong). Also, I usually need to close the Processing IDE and open it again if I forget to add the jogl.disable workaround.

A good practice is to load the image once on setup() only, using a global variable img by anouncing global img beforehand.

So I tested this code :
(UPDATE: This is not working for saving, see other answers below)

from java.lang import System 
System.setProperty("jogl.disable.openglcore", "false") 

rstroke = 12

def setup():
    global img
    size(400,400, P2D)
    img = loadImage("starpattern.JPG")

   
def draw():
    noLoop()
    background(255)
    # image(img, 0, 0)  # to test if it loaded the image
    def gn():
        return random(100,600)
    
    a = gn(),
    b = gn()
    c = gn()
    d = gn()

    beginShape()
    texture(img)
    vertex(a,b,a,b)
    vertex(c,b,c,b)
    vertex(c,d,c,d)
    vertex(a,d,a,d)
    endShape(CLOSE)
    
    strokeWeight(random(rstroke))
    noFill()
    circle(gn(),gn(),gn())
    
    strokeWeight(random(rstroke))
    line(gn(),gn(),gn(),gn())
    
    strokeWeight(random(rstroke))
    line(gn(),gn(),gn(),gn())

def mouseClicked():
    loop()

def keyPressed():
    if key == 's':
        saveFrame("design-####.png")

Using this image as starpattern.JPG:

The result was this:

1 Like

Nice! Very similar output (and starpattern, haha). Thank you for the tip on including global img in the setup(), I had a feeling I was missing some of the best practices there.

Does your version allow you to save frames correctly?

I have definitely narrowed it down to the P2D renderer. If I don’t use it, everything works (and saves) just fine, it just means I can’t use the pattern texture for that one shape. Alternatively, I can take screenshots with ctrl+prt sc to get images, it’s just more labor-intensive than I was hoping.

I’ll take a look at how P2D is interacting with my video card as well and see if that changes anything.

Thanks again!

Ugh! Not saving! Black PNGs, ugh! I will try to find a workaround!
I think it has to do with the noLoop(), because you have to have draw running to use saveFrame(), and this will create a very hard problem because you can’t save the image you are seeing :frowning:

UPDATE: fixing the saving “the image you saw” issue with the answer from @GoToLoop below!

rstroke = 12
save_now = False

def setup():
    global img
    size(400,400, P2D)
    img = loadImage("starpattern.JPG")
    noLoop()
   
def draw():
    if save_now:
        saveFrame("design-####.png")

    background(255)  # or whatever background cleaning method you prefer
    # drawing goes here!     

def mouseClicked():
    redraw()

def keyPressed():    
    global save_now
    if key == 's':
        save_now = True
        redraw()

Now one could avoid having the save event create a new drawing by doing this:

    if save_now:
        saveFrame("design-####.png")
    else:
        return

The PGraphics based approach next didn’t work :confused:

Sorry, amost, but not quite (no texture :frowning: )

from java.lang import System 
System.setProperty("jogl.disable.openglcore", "false") 

rstroke = 12
save_now = False

def setup():
    global img, pgraphics
    size(400,400, P2D)
    img = loadImage("starpattern.JPG")    
    noLoop()

   
def draw():
    global pgraphics
    # pgraphics = createGraphics(width, height, P2D)  # doesn't work :(
    pgraphics = createGraphics(width, height)  # works without texture
    beginRecord(pgraphics)
    background(255)
    # image(img, 0, 0)  # to test if it loaded the image
    def gn():
        return random(100,600)
    
    a = gn()
    b = gn()
    c = gn()
    d = gn()

    beginShape()
    texture(img)
    vertex(a,b,a,b)
    vertex(c,b,c,b)
    vertex(c,d,c,d)
    vertex(a,d,a,d)
    endShape(CLOSE)
    
    strokeWeight(random(rstroke))
    noFill()
    circle(gn(),gn(),gn())
    
    strokeWeight(random(rstroke))
    line(gn(),gn(),gn(),gn())
    
    strokeWeight(random(rstroke))
    line(gn(),gn(),gn(),gn())
    
    endRecord()  # fishes recording the pgraphics

def mouseClicked():
    redraw()

def keyPressed():    
    if key == 's':
        pgraphics.save("design-{:04}.png".format(frameCount))

I have also tried a third known approach that is to draw with the PGraphics object methods, like this, but it didn’t work either (I think the .texture() method was supposed to be there but it wasn’t):

    global pg
    pg = createGraphics(width, height, P2D)  # creates a PGraphics object
    pg.beginDraw()
    pg.background(255)

    def gn():
        return random(100,600)
    
    a = gn()
    b = gn()
    c = gn()
    d = gn()

    pg.beginShape()
    pg.texture(img)
    pg.vertex(a,b,a,b)
    pg.vertex(c,b,c,b)
    pg.vertex(c,d,c,d)
    pg.vertex(a,d,a,d)
    pg.endShape(CLOSE)
    
    pg.strokeWeight(random(rstroke))
    pg.noFill()
    pg.circle(gn(),gn(),gn())
    
    pg.strokeWeight(random(rstroke))
    pg.line(gn(),gn(),gn(),gn())
    
    pg.strokeWeight(random(rstroke))
    pg.line(gn(),gn(),gn(),gn())
    
    pg.endDraw()  # finishes drawing the PGraphics
    image(pg, 0, 0)
    

This is my workaround attempt. See if it works for you folks too: :innocent:

"""
 Texture Save Frame (v1.0.1)
 by Ginataka (2021-Jan-08)
 mod GoToLoop

 https://Discourse.Processing.org/t/
 processing-py-issues-with-saveframe-bug-or-a-mistake-on-my-end/26863/9
"""

from java.lang.System import getProperty, setProperty

DISABLE_OPENGL, TRUE, FALSE = 'jogl.disable.openglcore', 'true', 'false'
getProperty(DISABLE_OPENGL) == TRUE and setProperty(DISABLE_OPENGL, FALSE)

SAVE_KEY = ord('S')
MAX_STROKE_WEIGHT = 12

takeShot = False

def setup(IMAGE_FILENAME = 'starpattern.JPG'):
    size(800, 600, P2D)
    smooth(8); noLoop(); noFill()

    global img
    img = loadImage(IMAGE_FILENAME)


def draw(
    FRAME_FILENAME = 'shots/design-%.4d.png',
    TITLE = 'frame: %.4d',

    gn = lambda: random(100, height),
    sw = lambda: strokeWeight(random(MAX_STROKE_WEIGHT))):

    global takeShot
    if takeShot:
        takeShot = False
        saveFrame(this.dataPath(FRAME_FILENAME % (frameCount - 1)))

    this.surface.title = TITLE % frameCount
    background(int(random(PImage.ALPHA_MASK)))

    a, b, c, d = gn(), gn(), gn(), gn()

    with beginClosedShape():
        texture(img)

        vertex(a, b, a, b)
        vertex(c, b, c, b)
        vertex(c, d, c, d)
        vertex(a, d, a, d)

    sw()
    circle(gn(), gn(), gn())

    sw()
    line(gn(), gn(), gn(), gn())

    sw()
    line(gn(), gn(), gn(), gn())


def mousePressed(): redraw()


def keyPressed():
    global takeShot
    takeShot = keyCode is SAVE_KEY
    takeShot and redraw()
2 Likes

AMAZING! @GoToLoop always to the rescue with the most amazing knowledge! Thank you!

Please, what is this def setup(IMAGE_FILENAME = 'starpattern.JPG'):? Looks like a default argument on setup()… why are you doing this? are you able to call the sketch from the command line with a different argument?

Just to confirm, the most important thing, to fix my example, is to save the frame, if the flag is on, before drawing the current frame this is so simple, yet it eluded me for years.

Thanks again!