FormerLurker / Octolapse

Stabilized timelapses for Octoprint
GNU Affero General Public License v3.0
634 stars 99 forks source link

[Request] or Suggestion: Update DSLR wiki instructions to use deferred process forks #904

Open Fibonacci- opened 1 year ago

Fibonacci- commented 1 year ago

If this is a feature request describe it here

Currently, the instructions and scripts for DSLR Deferred Download (https://github.com/FormerLurker/Octolapse/wiki/V0.4---Configuring-a-DSLR-With-Deferred-Download) take advantage of Octolapse's pre-print and pre-render script hooks to pull down all images on the camera and create a timelapse from any/all images on the camera's SD card at the end of a timelapse. We can take advantage of the process forking capabilities in Linux to handle transferring image files in the background, eliminating the need for the non-snapshot script triggers.

My camera and gphoto2 have some problems that prevent gphoto2 from listing the files on the camera properly, so I'm not able to get a good script that separates the capture from the deferred file transfer. This script could be improved by running the capture command in the foreground, parsing the output, and backgrounding the file transfer task. However, this script should work as far as triggering a capture via gphoto2 and transferring the file to the snapshot directory with the appropriate name without interrupting the print, eliminating the need for the pre-process script that deletes all files on the camera and the post-print image transfer. This script executes in 0m0.015s on my rpi 3b.

This script requires the at binary, which is not generally included by default, but can be easily installed via the package manager with sudo apt install at on debian-based systems.

#!/bin/sh
# Camera Capture Script - Trigger/Download In Background
# Requires no camera initialization script
# Written by: Formerlurker@pm.me
# Modified by: Fibonacci-
# Put the arguments sent by Octolapse into variables for easy use
SNAPSHOT_NUMBER=$1
DELAY_SECONDS=$2
DATA_DIRECTORY=$3
SNAPSHOT_DIRECTORY=$4
SNAPSHOT_FILENAME=$5
SNAPSHOT_FULL_PATH=$6

if [ ! -d "${SNAPSHOT_DIRECTORY}" ];
then
  echo "Creating directory: ${SNAPSHOT_DIRECTORY}"
  mkdir -p "${SNAPSHOT_DIRECTORY}"
fi

# trigger the camera background process and exit immediately
# note: at's stderr is suppressed because at always writes a warning about default shells to stderr
# which makes octolapse think the script failed
echo "gphoto2 --capture-image-and-download --filename \"${SNAPSHOT_FULL_PATH}\"" | at now 2>/dev/null

# this entire script will execute in a few milliseconds, depending on 
# whether it needs to create the snapshot directory and how fast that can be done
# even though it may take a few more fractions of a second 
# for at to trigger gphoto2, and for gphoto2 to capture an image, 
# depending on focus times/light levels/etc.
# that means there's a chance octolapse will consider the camera "done" before the photo is taken 
# and move on with the print, leading to blurry/inconsistent timelapse frames
# if that happens, uncomment and adjust this sleep command 
# sleep 0.5

at logs its jobs to mail of the user that triggered it, which is less than ideal from a logging/monitoring/debugging perspective, but unfortunately I couldn't get setsid or nohup to play nicely with how Octolapse handles process/timeout monitoring.

TL;DR Take the non-deferred DSLR script and make it do the time-intensive operations in the background.

Version of Octolapse

Octolapse Version: 0.4.2

Version of OctoPrint

OctoPrint Version: 1.8.7

Operating System running OctoPrint and Octolapse

OS Name: Linux Os Version: Raspbian GNU/Linux 10 (buster)

Link to octoprint.log

Link to octoprint.log: n/a

Please consider becoming a patron

If you like this project, please support my work by becoming a patron, and consider adding a 'star' to the repository. It takes a lot of time and effort to maintain the project and respond to issues. The cost of test prints, software, cameras, printer parts, etc. can quickly add up, so every bit helps.

You can find various videos and tutorials by subscribing to my Youtube channel. You can also follow me on Twitter.

FormerLurker commented 1 year ago

Interesting! Any idea what happens if two snapshots trigger in close proximity, such that the previous transfer is still in progress when the next snapshot is taken? This is obviously an edge case, but I JUST debugged some gcode recently where two snapshots were taken back-to-back by user generated commands.

As long as it handles this gracefully, I think this is a good one to add. Would you be willing to create the markdown for the wiki?

Fibonacci- commented 1 year ago

As written, if a snapshot is triggered while a transfer is still in progress, the newest snapshot would fail because two instances of gphoto2 are trying to access the same camera (the usual "something else is using this" message of ...Could not claim interface 0 (Device or resource busy)... will be logged to the triggering user's mailbox).

What behavior would you consider correct? I can add a quick while loop before the script triggers at to block until the at queue is empty:

while ! [[ -z $(atq) ]]
do
    echo "atq indicates jobs still running!"
    sleep 0.2
done

That may cause a snapshot timeout in Octolapse depending on the user's timeout settings, but that timeout would (obviously) be visible to Octolapse and handled/logged by Octolapse like any other timeout.

And sure, I'm happy to write the markdown for the wiki! Let me know if there's a style guide or anything I should reference.

FormerLurker commented 1 year ago

I think having it fail as quickly as possible is fine! If you want to add some flag to cause it to wait until the device is ready, that would be fine too.

Regarding a style sheet, I wish I were that organized, lol. Feel free to use the existing guides as a base, and THANK you for such an interesting contribution!

Fibonacci- commented 1 year ago

Here's the markdown I came up with. File:

DSLR-with-Background-Capture-and-Download.md

File contents:

Note: These instructions are in beta. Please notify me with any issues or corrections, or just to say things worked for you.

Before Using This Guide

This guide assumes you have read and performed all of the steps in the Configuring an External Camera tutorial. If you have not done so, complete the guide at least up to and including step 4.

Backgrounding Image Download

These instructions assume you are running a Debian-based OS (incl. OctoPi and Raspbian).

It is possible to have a background process handle the capture and image transfer to Octolapse to significantly reduce the time necessary to capture a snapshot. There are many ways to trigger non-child processes or fork and disown processes in Linux - for simplicity, we will use the at daemon.

Step 1 - Installing Required Software

First, update your Raspberri PI and install GPhoto if you have not already done so.

Install the at daemon and its associated programs by running the following command:

sudo apt install at

This installs a lightweight service that can schedule execution of commands at a specified time. Luckily for us, "now" is a valid time that can be specified.

Step 2 - Configuring DSLR Settings

This snapshot method does not wait for your DSLR to finish taking its picture before allowing the print to continue. Therefore, to avoid blurry photos, it's important to pre-configure your DSLR so that the time between photo trigger and photo capture is as small as possible. Any extra work or analysis your DSLR has to do before it takes the photo will increase the risk of poor-quality photos.

Make the following changes to your camera settings where possible (specific instructions will vary based on your camera model/manufacturer):

Step 2 - Create Snapshot Background Script

Next we have to add a new script, which we'll call our 'Background Snapshot Script'. This script will tell the at daemon to take a snapshot as soon as it can, then return control to Octolapse so the print can continue.

I put my script in /home/pi/scripts by navigating to the following directory:

cd /home/pi/scripts

Next, create a new script file with the nano editor like so:

nano take-background-photo.sh

This will open the nano editor. Copy the following script and paste it into the nano editor by pressing the right mouse key.

IMPORTANT NOTE: The very first line #!/bin/bash is important, and must be included. This script uses bash-specific syntax that is NOT COMPATIBLE with sh.

#!/bin/bash
# Camera Capture Script - Trigger/Download In Background
# Requires no camera initialization script
# Written by: Fibonacci- (Github)
# Modified from Formerlurker@pm.me's Camera Capture Script
# Put the arguments sent by Octolapse into variables for easy use
SNAPSHOT_NUMBER=$1
DELAY_SECONDS=$2
DATA_DIRECTORY=$3
SNAPSHOT_DIRECTORY=$4
SNAPSHOT_FILENAME=$5
SNAPSHOT_FULL_PATH=$6

if [ ! -d "${SNAPSHOT_DIRECTORY}" ];
then
  echo "Creating directory: ${SNAPSHOT_DIRECTORY}"
  mkdir -p "${SNAPSHOT_DIRECTORY}"
fi

# ask the at daemon if previous jobs are still transferring and fail if so
# note: if you've used the at daemon in other places, change this and the trigger to use a specific at queue
if [[ $(atq) ]];
then
    echo "Background snapshot: atq indicates a previous snapshot is still running! Cannot take a snapshot!" >/dev/stderr
    exit
fi

# as an alternative to failing if other jobs are still running, uncomment the below while loop
# and comment the above if statement to make octolapse wait until the other jobs have finished
# before triggering a new snapshot

#while ! [[ -z $(atq) ]]
#do
#   echo "Background snapshot: atq indicates jobs still running! Waiting..."
#   sleep 0.2
#done

# trigger the camera background process and exit immediately
# note: at's stderr is suppressed because at always writes a warning about default shells to stderr
# which makes octolapse think the script failed
echo "gphoto2 --capture-image-and-download --filename \"${SNAPSHOT_FULL_PATH}\"" | at now 2>/dev/null

# this entire script will execute in a few milliseconds, depending on
# whether it needs to create the snapshot directory and how fast that can be done
# even though it may take a few more fractions of a second
# for at to trigger gphoto2, and for gphoto2 to capture an image,
# depending on focus times/light levels/etc.
# that means there's a chance octolapse will consider the camera "done" before the photo is taken
# and move on with the print, leading to blurry/inconsistent timelapse frames
# if that happens, uncomment and adjust this sleep command
# sleep 0.5

Now we need to save the script. Press ctrl + o and then Enter to save. Then press ctrl + x to exit.

Now we need to add execute permission to the script with the following command

chmod +x take-background-photo.sh

Note that at's output, therefore gphoto2's output, is sent to the mailbox of the user running Octoprint (usually pi). To see the output of at's run of the gphoto2 command, type mail and type the the mail item's number (usually the lowest item in the list). Here's an example of viewing an at log when the camera was not connected:

Fibonacci@pi:~ $ mail
"/var/mail/Fibonacci": 8 messages 8 unread
>U   1 Fibonacci        Sun May 14 21:13  18/546   Output from your job      612
 U   2 Fibonacci        Sun May 14 21:16  17/520   Output from your job      613
 U   3 Fibonacci        Sun May 14 21:19  17/520   Output from your job      614
 U   4 Fibonacci        Sun May 14 21:33  27/1107  Output from your job      615
 U   5 Fibonacci        Sun May 14 21:33  27/1107  Output from your job      616
 U   6 Fibonacci        Sun May 14 21:33  27/1107  Output from your job      617
 U   7 Fibonacci        Sun May 14 21:33  27/1107  Output from your job      618
 U   8 Fibonacci        Sun May 14 21:33  27/1107  Output from your job      619
? 8
Return-path: <Fibonacci@pi>
Envelope-to: Fibonacci@pi
Delivery-date: Sun, 14 May 2023 21:33:13 -0400
Received: from Fibonacci by pi with local (Exim 4.92)
    (envelope-from <Fibonacci@pi>)
    id 1pyN5N-0007ug-Mm
    for Fibonacci@pi; Sun, 14 May 2023 21:33:13 -0400
Subject: Output from your job      619
To: Fibonacci@pi
Message-Id: <E1pyN5N-0007ug-Mm@pi>
From: Fibonacci  <Fibonacci@pi>
Date: Sun, 14 May 2023 21:33:13 -0400
Status: O
X-UID: 8

*** Error (-52: 'Could not find the requested device on the USB port') ***
...[remainder of gphoto2 error removed]...

?

Step 3 - Configure Octolapse

Finally, we will need to configure Octolapse to use this new script. Open OctoPrint and navigate to the Octolapse tab. Depending on your OctoPrint settings you may have to sign in before you can change Octolapse settings, so do that now if necessary. Edit your 'Camera Profile' and change the following settings:

First, ensure that the camera profile is enabled by checking the 'enabled' checkbox at the top.

Second, make sure the 'Camera Type' drop down box at the top of the page from 'Webcam' to 'External Camera - Script'

Next, type in the following for the 'Snapshot Acquire Script' setting:

/home/pi/scripts/take-background-photo.sh

Set the 'Snapshot Delay' setting to 0. This will reduce the total snapshot time since it is unlikely that you will need a delay before attempting to take a snapshot when using a DSLR.

Set the 'Snapshot Timeout' to 10000 (10 seconds). This can be reduced later, but it's useful for testing.

Make sure all of the other custom script fields are empty.

Press the "Test Print" button. You should get a yellow "Partial Success" pop-up indicating that the script ran successfully but no snapshot was detected - that's expected, since the snapshot will be written to Octolapse's directory a few seconds after the script finishes running.

Press the "Test Print" button a few times in rapid succession. You should get one yellow "Partial Success" pop-up and several "Camera Script Failed" pop-ups. In the Octolapse log at ~/.octolapse/logs/plugin_octolapse.log, you should see a few messages similar to:

2023-05-14 21:18:42,199 - octolapse.script - ERROR - Error output (stderr) for 'Sony - Snapshot Camera Script':
    Background snapshot: atq indicates a previous snapshot is still running! Cannot take a snapshot!

This means the mechanism to prevent several snapshots happening in parallel is working as expected.

Save your camera profile changes.

Finally, edit your current rendering profile settings and make sure the 'Timelapse Generation' option is checked. If you have multiple processors on your server (for example, the Pi3 B+ has 4 cores), you may want to adjust the 'Rendering Thread Count' under 'Advanced Rendering Settings'. This setting can greatly reduce the amount of time required to render a video. This is really nice because rendering a high res video can take a very long time on a pi! Be careful, however, since setting the available thread count higher than the number of available cores will HURT performance. If you are using a Pi3 B+ it is pretty safe to set the rendering thread count to 3, but NOT more than 4.

Step 6 - Test Print

That should be it as far as configuration goes! Now run a short test print. Change your 'debug profile' within Octolapse to Test Print - Full Diagnostic. This option will cause a diagnostic lot to be recorded. Additionally, test mode will prevent your printer from heating up (bed and extruder) and will strip off all extrusion commands, so you won't waste any time or plastic. I strongly recommend you unload your filament, though, just in case there is a bug or glitch in the test mode code.

Octolapse should now run as usual, but should acquire an image from your DSLR instead of the usual webcam, and it will only pause for snapshots a few fractions of a second. You will NOT see any preview snapshots in the Octolapse tab, but this is normal (the images are given to Octolapse after the preview render occurs). It should take substantially less time to acquire images now that the images aren't being downloaded before the print continues. You should hear your camera snapping away during the snapshot phase. When the test print is completed, Octolapse will run the timelapse. The resulting timelapse will be available within the Octolapse tab as usual. Note that it can take a REALLY long time to render a video depending on the length and settings. Be very patient.

If the timelapse is blurry, it's likely because Octolapse is continuing the print before/while your camera is taking the photo. If this issue occurs, edit the script by typing nano take-background-photo.sh and uncommenting the last line:

Change

# sleep 0.5

to

sleep 0.5

This will make the script pause for half a second before returning control to Octolapse. Run another test print and adjust the sleep time as needed - it should be as small of a value as possible without seeing blurriness and quality issues with the snapshots.

Troubleshooting

If you are having trouble you might consider opening an issue here. Be sure to submit plugin_octolapse.log and your settings.json file, as is requested in the standard issue template. In addition, please submit the latest associated mail log with any personal information removed. Please note any deviations from the above instructions or anything else you feel is relevant. Before creating an issue please re-read the guide to make sure nothing obvious is wrong, look through Octolapse Issues page and check the gphoto2 website and issues on the gphoto2 github page. It's possible that workarounds already exist!

FormerLurker commented 1 year ago

Fantastic!! I will proofread and upload it ASAP. Thank you for all the efforts, and i am excited to try this out!