Skip to content

Instantly share code, notes, and snippets.

@Hermann-SW
Last active April 29, 2024 21:47
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hermann-SW/e6049fe1a24fc2b5a53c654e0e9f6b9c to your computer and use it in GitHub Desktop.
Save Hermann-SW/e6049fe1a24fc2b5a53c654e0e9f6b9c to your computer and use it in GitHub Desktop.
tool for playing with Raspberry Pi Global Shutter Camera crop values
#!/bin/bash
# shellcheck disable=SC2154
# (silence shellcheck wrt $cam1 environment variable)
if [[ $# -lt 4 ]]; then echo "Format: [narrow=1] [cam1=1] $0 width height framerate ms [us]"; exit; fi
export SHTR=""; if [[ $# -gt 4 ]]; then SHTR="--shutter"; fi
export workaround=""; if [[ "" != "$(grep '=bookworm' /etc/os-release)" ]]; then workaround="--no-raw"; fi
export d=10; if [[ "" != "$(grep "Revision.*: ...17.$" /proc/cpuinfo)" ]]; then if [[ "$cam1" == "" ]]; then d=6; else d=4; fi; fi
for((m=0; m<=5; ++m))
do
if media-ctl -d "/dev/media$m" --set-v4l2 "'imx296 $d-001a':0 [fmt:SBGGR10_1X10/${1}x$2 crop:($(( (1440 - $1) / 2 )),$(( (1088 - $2) / 2 )))/${1}x$2]" >/dev/null; then echo -e "/dev/media$m\n"; break; fi
done
libcamera-hello --list-cameras ;echo
rm -f /dev/shm/tst.pts
if [[ "" != "$(grep "Revision.*: ...17.$" /proc/cpuinfo)" ]]
then
rpicam-vid "$workaround" ${cam1:+--camera 1} --width "$1" --height "$2" --denoise cdn_off --framerate "$3" -t "$4" "$SHTR" "$5" -o /dev/shm/tst${cam1:+1}.mp4 -n ;echo
~/venv/bin/python ~/rpicam-apps/utils/timestamp.py --plot ${narrow:+--narrow} /dev/shm/tst${cam1:+1}.mp4
else
libcamera-vid "$workaround" --width "$1" --height "$2" --denoise cdn_off --framerate "$3" --save-pts /dev/shm/tst.pts -t "$4" "$SHTR" "$5" -o /dev/shm/tst.h264 -n ;echo
rm -f tstamps.csv && ptsanalyze /dev/shm/tst.pts
fi
@Hermann-SW
Copy link
Author

Hermann-SW commented Mar 16, 2023

Related forum posting:
https://forums.raspberrypi.com/viewtopic.php?p=2090640#p2090640

Script is "shellcheck" clean.

Needs ptsanalyze tool installed somewhere on $PATH:
https://github.com/Hermann-SW/userland/blob/master/tools/ptsanalyze

  • determines the /dev/mediaX device GS camera is connected to, in order to configure crop
  • does "--list-cameras" to confirm crop setting was successful
  • captures video with "libcamera-vid"
  • does frame delay and skip analysis with "ptsanalyze"

Usage:

pi@raspberrypi4B2:~ $ ./GScrop 
Format: ./GScrop width height framerate ms
pi@raspberrypi4B2:~ $ 

Example capturing with 532fps requested, 128x96 crop, for 3000ms — with proof of success and no frame skips:

pi@raspberrypi4B2:~ $ ./GScrop 128 96 532 3000
/dev/media3

Available cameras
-----------------
0 : imx296 [1456x1088] (/base/soc/i2c0mux/i2c@1/imx296@1a)
    Modes: 'SRGGB10_CSI2P' : 1456x1088 [535.91 fps - (0, 0)/128x96 crop]

[9:21:43.834461391] [120326]  INFO Camera camera_manager.cpp:299 libcamera v0.0.4+22-923f5d70
[9:21:43.875920693] [120327]  INFO RPI raspberrypi.cpp:1476 Registered camera /base/soc/i2c0mux/i2c@1/imx296@1a to Unicam device /dev/media3 and ISP device /dev/media0
Mode selection:
    SRGGB10_CSI2P 1456x1088 - Score: 3581.84
[9:21:43.879168888] [120326]  INFO Camera camera.cpp:1028 configuring streams: (0) 128x96-YUV420 (1) 1456x1088-SBGGR10_CSI2P
[9:21:43.879389255] [120327]  INFO RPI raspberrypi.cpp:851 Sensor: /base/soc/i2c0mux/i2c@1/imx296@1a - Selected sensor format: 128x96-SBGGR10_1X10 - Selected unicam format: 128x96-pBAA
Halting: reached timeout of 3000 milliseconds.

creating tstamps.csv
1591 frames were captured
majority framerate 535fps
  frame delta time[us] distribution
      1 1859
      2 1861
      4 1862
     13 1863
     28 1864
     90 1865
    485 1866
    751 1867
    156 1868
     38 1869
     11 1870
      6 1871
      2 1872
      1 1874
> after skip frame indices (middle column)
0 frame skips (0%)
average framerate 536fps
pi@raspberrypi4B2:~ $ 

Slowed down 10× of captured 536fps video (analog clock second hand):

@Hermann-SW
Copy link
Author

Hermann-SW commented Mar 17, 2023

Updated GScrop allows to pass optional shutter time (in microseconds) after video length (in milliseconds), to be used for libcamera-vid video capturing. Also now crop is centered in sensor area (see "...(656, 496)/128x96 crop]" further below):

pi@raspberrypi4B2:~ $ ./GScrop 
Format: ./GScrop width height framerate ms [us]
pi@raspberrypi4B2:~ $ 
pi@raspberrypi4B2:~ $ shellcheck GScrop 
pi@raspberrypi4B2:~ $ 

Sample execution with 29us shutter time, for capturing fast rotating (22,500rpm) RC airplane propeller.
https://forums.raspberrypi.com/viewtopic.php?p=2091003#p2091003
22500/60/536 = 0.70 rotations between successive frames (propeller rotates clockwise, follow one side central reflective marker).
1fps animated .gif shows 20 successive frames from recorded video:

pi@raspberrypi4B2:~ $ ./GScrop 128 96 534 2000 29
/dev/media3

Available cameras
-----------------
0 : imx296 [1456x1088] (/base/soc/i2c0mux/i2c@1/imx296@1a)
    Modes: 'SRGGB10_CSI2P' : 1456x1088 [535.91 fps - (656, 496)/128x96 crop]

[28:22:05.521087746] [277565]  INFO Camera camera_manager.cpp:299 libcamera v0.0.4+22-923f5d70
[28:22:05.562791538] [277566]  INFO RPI raspberrypi.cpp:1476 Registered camera /base/soc/i2c0mux/i2c@1/imx296@1a to Unicam device /dev/media3 and ISP device /dev/media0
Mode selection:
    SRGGB10_CSI2P 1456x1088 - Score: 3581.84
[28:22:05.565814553] [277565]  INFO Camera camera.cpp:1028 configuring streams: (0) 128x96-YUV420 (1) 1456x1088-SBGGR10_CSI2P
[28:22:05.566060087] [277566]  INFO RPI raspberrypi.cpp:851 Sensor: /base/soc/i2c0mux/i2c@1/imx296@1a - Selected sensor format: 128x96-SBGGR10_1X10 - Selected unicam format: 128x96-pBAA
Halting: reached timeout of 2000 milliseconds.

creating tstamps.csv
1054 frames were captured
majority framerate 535fps
  frame delta time[us] distribution
      1 1861
      2 1862
      5 1864
     17 1865
    358 1866
    618 1867
     40 1868
      4 1869
      3 1870
      1 1871
      1 1872
      1 1873
> after skip frame indices (middle column)
0 frame skips (0%)
average framerate 536fps
pi@raspberrypi4B2:~ $ 

@Hermann-SW
Copy link
Author

Added @naushir's workaround to make it work for Bookworm:
https://forums.raspberrypi.com/viewtopic.php?p=2156818#p2156818

@Hermann-SW
Copy link
Author

Make shellcheck clean:

$ shellcheck GScrop
$

Once /dev/media0 was used for camera, allow to find media0 as well.

@Hermann-SW
Copy link
Author

Hermann-SW commented Dec 22, 2023

Updated GScrop is able to work with new Raspberry Pi5 as well.
Microsecond precision timestamps are inside .mp4 with Pi5.
For Pi5 GScrop uses supported tool for frame delta analysis:
https://github.com/Hermann-SW/rpicam-apps/blob/main/utils/timestamp.py

Pull request ...
raspberrypi/rpicam-apps#624
... for new "--narrow" option:
https://github.com/Hermann-SW/rpicam-apps/commit/dced74761c38257f7dda7db48a977278091dd23d

This is plot for 10s 688x136@400fps GS camera video with few frameskips:

pi@raspberrypi5:~ $ ~/venv/bin/python ~/rpicam-apps/utils/timestamp.py --plot --narrow test.mp4
Total: 3988 frames (3987 samples)
Average: 2.499 ms / 400.180 fps
Minimum: 2.471 ms at frame 1030
Maximum: 12.444 ms at frame 953
Outliers: 4 (100%) 9 (10.0%) 10 (1.0%) 3986 (0.1%)

@Hermann-SW
Copy link
Author

Hermann-SW commented Jan 6, 2024

Pull request got accepted, then I did delete my fork.
So tool with "--narrow" option can be found in official repo:
https://github.com/raspberrypi/rpicam-apps/blob/main/utils/timestamp.py

"--narrow" help text got improved in the process:

pi@raspberrypi5:~ $ ~/venv/bin/python ~/rpicam-apps/utils/timestamp.py -h 
usage: timestamp.py [-h] [--plot] [--narrow] filename

rpicam-apps timestamp analysis tool
...
options:
  -h, --help  show this help message and exit
  --plot      Plot timestamp graph
  --narrow    Narrow the y-axis by hiding outliers
pi@raspberrypi5:~ $ 

 
 
For a 60s duration 688x136@400fps "center of analog clock" recording ...
https://stamm-wilbrandt.de/videos/688x136.400fps_60s.mp4
https://forums.raspberrypi.com/download/file.php?id=64737
 
... with a single frameskip ...

pi@raspberrypi5:~ $ wget 2>/dev/null https://stamm-wilbrandt.de/videos/688x136.400fps_60s.mp4
pi@raspberrypi5:~ $ ~/venv/bin/python ~/rpicam-apps/utils/timestamp.py --plot --narrow 688x136.400fps_60s.mp4
Total: 24092 frames (24091 samples)
Average: 2.489 ms / 401.768 fps
Minimum: 2.475 ms at frame 11943
Maximum: 4.977 ms at frame 14847
Outliers: 0 (100%) 1 (10.0%) 1 (1.0%) 369 (0.1%)

 
...the average line is now in vertical middle:
https://forums.raspberrypi.com/viewtopic.php?p=2175722#p2175722
https://forums.raspberrypi.com/download/file.php?id=64736

@Hermann-SW
Copy link
Author

Hermann-SW commented Jan 19, 2024

The longer Pi5 is up, the more frameskips are likely to happen.
Simple countermeasure:
reboot before final recording

In order to get better timestamp.py analysis with "--narrow" option in case frameskips are likely, I added "narrow" environment variable handling to GScrop:

pi@raspberrypi5:~ $ uptime
 21:11:06 up 4 days, 19:22,  2 users,  load average: 0.60, 0.57, 0.38
pi@raspberrypi5:~ $ 
pi@raspberrypi5:~ $ narrow=1 GScrop 224 96 536 2000
/dev/media2
...
[libx264 @ 0x555666219830] kb/s:607.83

Total: 1054 frames (1053 samples)
Average: 1.870 ms / 534.697 fps
Minimum: 1.861 ms at frame 146
Maximum: 3.737 ms at frame 129
Outliers: 0 (100%) 2 (10.0%) 2 (1.0%) 1022 (0.1%)

@ddbaron
Copy link

ddbaron commented Feb 26, 2024

I'd like to suggest incorporating the detection of the bus used by the camera before calling media-ctl.

I use this command to parse out the definition:
media-ctl -d /dev/media0 --print-dot | grep -o "imx296 [0-9]-001a" | uniq

@Hermann-SW
Copy link
Author

Hermann-SW commented Feb 26, 2024

Hi,
I will receive my 2nd GS camera tomorrow ;-)
Then I can verify and will incorporate your suggestion.
I want to see whether Pi5 can record both cameras @536fps concurrently — have you already tested that?

@ddbaron
Copy link

ddbaron commented Feb 26, 2024

Updated 28/feb/2024: A bit more testing. I have two GS cameras but they're wired in synchronous mode so I needed to hack gscrop to handle it.

@liyangyuan
Copy link

This is a fantastic tool! Thank you.
By the way, I'm not sure if I'm an outlier or not, but I want to provide some information. I've discovered that I need to use d=6. Additionally, the conditional statement for determination of d doesn't seem to work for my Raspberry 5 8GB.
image
image

@Hermann-SW
Copy link
Author

Hermann-SW commented Mar 9, 2024

Latest revision added dual camera handling for Pi5:

pi@raspberrypi5:~ $ GScrop 
Format: [narrow=1] [cam1=1] /usr/local/bin/GScrop width height framerate ms [us]
pi@raspberrypi5:~ $ 

Default is to use camera 0:

pi@raspberrypi5:~ $ narrow=1 GScrop 688 136 402 1000
/dev/media0
...
[libx264 @ 0x5556652764a0] kb/s:1641.32

Total: 383 frames (382 samples)
Average: 2.500 ms / 400.004 fps
Minimum: 2.469 ms at frame 125
Maximum: 9.895 ms at frame 246
Outliers: 1 (100%) 2 (10.0%) 314 (1.0%) 382 (0.1%)
pi@raspberrypi5:~ $

Here camera 1 is used:

pi@raspberrypi5:~ $ cam1=foobar GScrop 688 136 402 1000
/dev/media1
...
[libx264 @ 0x5555dc266710] kb/s:1369.74

Total: 390 frames (389 samples)
Average: 2.474 ms / 404.186 fps
Minimum: 2.468 ms at frame 139
Maximum: 2.481 ms at frame 140
Outliers: 0 (100%) 0 (10.0%) 0 (1.0%) 5 (0.1%)
pi@raspberrypi5:~ $ 

@Hermann-SW
Copy link
Author

Hermann-SW commented Mar 9, 2024

Revision 11 clean again:

pi@raspberrypi5:~ $ shellcheck /usr/local/bin/GScrop 
pi@raspberrypi5:~ $ 

@Hermann-SW
Copy link
Author

Hermann-SW commented Apr 29, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment