M: magick montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image" ...if this was the old version of the command, what would be the equivalent for if i wanted to use the 'montage' (free-standing utility) command to accomplish the same thing?
G: Short answer: it’s basically the
same command — just drop the magick wrapper.
montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"magick montage ... → ImageMagick v7 style (everything
routed through magick)montage ... → ImageMagick v6 style (standalone
utility)magick montage
guarantees you’re using the v7 toolchain.montage is native
and behaves the same for your flags here.-tile 3x3 -geometry +0+0) are
identical across both versions.If you’re on a system with both versions installed,
montage might point to v6 while magick montage
uses v7. Most of the time this doesn’t matter for simple grids like
yours, but it can matter if you later add:
For your current use: no functional difference.
M: here's the --help results. ... how do i use a command line (in ubuntu) to update to v7?
G: TL;DR: You’re on
ImageMagick 6 (montage-im6.q16). To get v7
on Ubuntu, install it alongside or replace it via apt or a
PPA.
That header line:
Usage: montage-im6.q16 ...
means you’re running IM6, not IM7. fileciteturn0file0
Ubuntu often already has IM7 available:
sudo apt update
sudo apt install imagemagickThen check:
magick -versionIf you see ImageMagick 7.x, you’re done.
Ubuntu sometimes keeps IM6 as default but installs IM7 tools separately.
After installing, try:
magick montage ...instead of:
montage ...That’s the key difference.
Use a PPA:
sudo add-apt-repository ppa:ubuntuhandbook1/imagemagick
sudo apt update
sudo apt install imagemagickOnly if you need absolute latest:
sudo apt install build-essential
wget https://imagemagick.org/download/ImageMagick.tar.gz
tar xf ImageMagick.tar.gz
cd ImageMagick-*
./configure
make -j$(nproc)
sudo make installYou don’t actually need v7 for your command.
Your current command:
montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"works identically on IM6 and IM7.
So upgrading is only worth it if you:
magick syntaxIf your goal is future-proof scripts, don’t replace IM6—support both:
if command -v magick >/dev/null; then
magick montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"
else
montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"
fiThat avoids breaking on systems like yours.
If you want, I can check exactly what Ubuntu version you’re on and tell you which of these will actually work without friction.
M: sudo add-apt-repository
ppa:ubuntuhandbook1/imagemagick sudo apt update sudo apt install
imagemagick ERROR: ppa 'ubuntuhandbook1/imagemagick' not found (use
--login if private) Hit:1 https://repo.radeon.com/amdgpu/6.3/ubuntu
noble InRelease Hit:2 https://repo.radeon.com/rocm/apt/6.3
noble InRelease
Hit:3 http://us.archive.ubuntu.com/ubuntu
noble InRelease
Hit:4 http://us.archive.ubuntu.com/ubuntu
noble-updates InRelease
Hit:5 http://us.archive.ubuntu.com/ubuntu
noble-backports InRelease
Hit:6 http://security.ubuntu.com/ubuntu
noble-security InRelease
Reading package lists... Done Building dependency tree... Done Reading
state information... Done 1 package can be upgraded. Run 'apt list
--upgradable' to see it. Reading package lists... Done Building
dependency tree... Done Reading state information... Done imagemagick is
already the newest version (8:6.9.12.98+dfsg1-5.2build2). 0 upgraded, 0
newly installed, 0 to remove and 1 not upgraded.
G: TL;DR: You’re on Ubuntu 24.04 (noble), and Canonical is still shipping ImageMagick 6 by default. That PPA doesn’t exist anymore for noble, so your failure is expected—not user error.
imagemagick 6.9.12...So:
You did everything correctly. The ecosystem just hasn’t moved.
This avoids breaking system packages that depend on IM6.
sudo apt install build-essential libjpeg-dev libpng-dev libtiff-dev libwebp-dev libheif-dev
wget https://imagemagick.org/download/ImageMagick.tar.gz
tar xf ImageMagick.tar.gz
cd ImageMagick-*
./configure
make -j$(nproc)
sudo make install
sudo ldconfigThen check:
magick -versionYou will now have:
montage → IM6 (system)magick montage → IM7 (your compiled install)This is good, not a problem.
sudo apt remove imagemagick…but this can break other packages. Ubuntu still expects IM6 in some places.
You can often already do:
convert -versionIf it’s still 6.x, then yeah—you don’t have IM7 at all.
Don’t upgrade. Standardize your scripts.
You already saw:
magick montage ...Just wrap it:
command -v magick >/dev/null && CMD="magick montage" || CMD="montage"
$CMD @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"You’re trying to upgrade something that:
So unless you need:
…this is mostly a “nice to have,” not a functional requirement.
If you want, I can show you a minimal install of just the
magick binary without full system integration,
which is cleaner than full compilation.
M: you know, you act like the simple act of removing the 'magick' will work; it doesn't. i was porting a script from my fedora laptop, and tried exactly that. it's not using the right list or something, because nothing is being produced.
G: [Non-text content: thoughts; content_type=thoughts]
G: [Non-text content: thoughts; content_type=thoughts]
G: [Non-text content: thoughts; content_type=thoughts]
G: [Non-text content: reasoning_recap; content_type=reasoning_recap]
G: Yeah. Fair correction. I
overstated the “just remove magick” part.
Your montage-im6.q16 absolutely can behave differently
here, and the issue is probably the @"$temp_file"
input list, not montage itself. In IM6,
@file means “replace this filename with the contents of
that file,” and for image input it reads a list of image
filenames from that file. That feature exists in IM6, but the
list has to be in the format IM expects. fileciteturn0file0
citeturn183825view3turn183825view1
The most important part is this:
@file is for a file list, not for
including arbitrary command-line options.
citeturn183825view3turn183825view2eye.gif news.gif storm.gif
and then using convert @filelist.txt ....
citeturn183825view3So if your Fedora-side script wrote the temp file in some other format, IM6 may silently fail to read the inputs the way you expect.
Try these checks in Ubuntu:
printf 'TEMP FILE CONTENTS:\n'; cat "$temp_file"Then test whether IM6 can read the list at all:
identify @"$temp_file"If that produces errors or no images, the list file is the problem, not montage.
The safest IM6-friendly way is to make the temp file contain plain image paths separated by whitespace, like this:
printf '%s\n' "/path/to/1.jpg" "/path/to/2.jpg" "/path/to/3.jpg" > "$temp_file"and then run:
montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"But there’s a catch: if any filenames contain spaces or
shell-sensitive characters, the list-file approach can get messy. In
that case, the more reliable Ubuntu-side approach is usually to avoid
@file entirely and let the shell pass an array:
montage "${images[@]}" -tile 3x3 -geometry +0+0 "$output_image"That is usually the real fix.
So the likely answer is:
No, “remove magick” was not
enough.
Your script is probably relying on a list-file format that works in your
Fedora/ImageMagick 7 setup but is not being parsed the same way by
Ubuntu’s IM6. citeturn183825view3turn183825view1
If you paste the part of the script that creates
temp_file, I can tell you exactly what to change.
M: let's try a different approach. i ran
the script with the 'magick' removed, so it just uses the 'montage'
command. here's the last pair of error message batches i could identify
and the elements between them: Input #0, mov,mp4,m4a,3gp,3g2,mj2, from
'A2M_8_Scene_6.mp4': Metadata: major_brand : mp42 minor_version : 0
compatible_brands: mp42isom creation_time : 2013-01-08T06:55:17.000000Z
encoder : HandBrake 0.9.3 2008112300 copyright : copyright-eng :
Duration: 00:27:24.99, start: 0.000000, bitrate: 1208 kb/s Stream #0:00x1: Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv,
smpte170m/smpte170m/bt709, progressive), 640x480, 1128 kb/s, 29.96 fps,
59.94 tbr, 22050 tbn (default) Metadata: creation_time :
2013-01-08T06:55:17.000000Z vendor_id : [0][0][0][0] encoder : JVT/AVC
Coding Stream #0:10x2: Audio: aac (LC) (mp4a /
0x6134706D), 22050 Hz, stereo, fltp, 75 kb/s (default) Metadata:
creation_time : 2013-01-08T06:55:17.000000Z vendor_id : [0][0][0][0]
Stream mapping: Stream #0:0 -> #0:0 (h264 (native) -> mjpeg
(native)) Press [q] to stop, [?] for help [swscaler @ 0x58acea2593c0]
deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x58acea488e00] deprecated pixel format used, make sure you
did set range correctly Last message repeated 2 times Output #0, image2,
to 'frame_9.jpg': Metadata: major_brand : mp42 minor_version : 0
compatible_brands: mp42isom copyright-eng : copyright : encoder :
Lavf60.16.100 Stream #0:0(und): Video: mjpeg, yuvj420p(pc,
smpte170m/smpte170m/bt709, progressive), 426x320, q=2-31, 200 kb/s,
29.97 fps, 29.97 tbn (default) Metadata: creation_time :
2013-01-08T06:55:17.000000Z vendor_id : [0][0][0][0] encoder :
Lavc60.31.102 mjpeg Side data: cpb: bitrate max/min/avg: 0/0/200000
buffer size: 0 vbv_delay: N/A [image2 @ 0x58ace9379f80] The specified
filename 'frame_9.jpg' does not contain an image sequence pattern or a
pattern is invalid. [image2 @ 0x58ace9379f80] Use a pattern such as %03d
for an image sequence or use the -update option (with -frames:v 1 if
needed) to write a single image. [out#0/image2 @ 0x58ace937b0c0]
video:13kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB
muxing overhead: unknown frame= 1 fps=0.0 q=3.7 Lsize=N/A
time=00:00:00.00 bitrate=N/A dup=1 drop=0 speed= 0x
montage-im6.q16: attempt to perform an operation not allowed by the
security policy
@temp_images.txt' @ error/blob.c/OpenBlob/2702. montage-im6.q16: scr-A2M_8_Scene_6.mp4.jpg'
@ error/montage.c/MontageImageCommand/1809.
G: [Non-text content: thoughts; content_type=thoughts]
G: [Non-text content: reasoning_recap; content_type=reasoning_recap]
G: There it is. The real problem is
not montage syntax. It’s this line:
montage-im6.q16: attempt to perform an operation not allowed by the security policy `@temp_images.txt'
That means Ubuntu’s ImageMagick 6 policy is blocking
@file list expansion entirely. So when you removed
magick, the command itself was fine, but IM6 refused to
read @temp_images.txt. The second error is just fallout
because no valid input images got loaded. Your earlier
--help output confirms this is
montage-im6.q16. fileciteturn0file0
This:
montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"is being blocked specifically because of the @... input
mechanism on this Ubuntu install.
@temp_fileUse a shell array instead of ImageMagick’s list-file feature.
If your temp file contains one image path per line, do this:
mapfile -t images < "$temp_file"
montage "${images[@]}" -tile 3x3 -geometry +0+0 "$output_image"That bypasses the policy block completely.
If you want it as a one-liner:
mapfile -t images < "$temp_file"; montage "${images[@]}" -tile 3x3 -geometry +0+0 "$output_image"@temp_fileYou’d have to relax ImageMagick’s security policy, probably in something like:
/etc/ImageMagick-6/policy.xmlor:
/etc/ImageMagick/policy.xmlBut I would not start there unless you specifically want that behavior system-wide.
This part:
The specified filename 'frame_9.jpg' does not contain an image sequence pattern
...
use the -update option ... to write a single image
is a different problem. FFmpeg is warning that image2
expects a sequence pattern unless you explicitly tell it you’re writing
one frame.
So for single-frame output, use something like:
ffmpeg -i input.mp4 -frames:v 1 -update 1 frame_9.jpgor often just:
ffmpeg -ss ... -i input.mp4 -frames:v 1 frame_9.jpgdepending on your exact command structure.
Two separate issues:
montageUbuntu IM6 is blocking @temp_images.txt by security
policy.
ffmpegYou’re writing a single image through the image2 muxer
in a way it complains about.
So the direct answer is: your script did not fail because
magick was removed; it failed because Ubuntu’s IM6 refuses
@file input lists.
The practical fix is to replace the montage call with:
mapfile -t images < "$temp_file"
montage "${images[@]}" -tile 3x3 -geometry +0+0 "$output_image"If you want, paste the exact block that writes
temp_images.txt and calls ffmpeg/montage, and I’ll rewrite
only the broken part.
M: look, this is the script, tell me what should go where please: #!/bin/bash
extract_and_merge_frames() { local input_video="$1" # The input video file path. local output_image="$2" # The output image file name for the merged image.
# Check if the input video file exists.
if [[ ! -f "$input_video" ]]; then
echo "Error: Input video file '$input_video' does not exist."
exit 1
fi
# Get the duration of the video in seconds.
local duration
duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$input_video")
duration=${duration%.*} # Convert to integer by removing decimal part.
# Calculate frame extraction times based on percentages.
local frame_times=( $(echo "$duration * 0" | bc) $(printf "%.3f" $(echo "$duration * 0.125" | bc)) $(printf "%.3f" $(echo "$duration * 0.25" | bc)) $(printf "%.3f" $(echo "$duration * 0.375" | bc)) $(printf "%.3f" $(echo "$duration * 0.5" | bc)) $(printf "%.3f" $(echo "$duration * 0.625" | bc)) $(printf "%.3f" $(echo "$duration * 0.75" | bc)) $(printf "%.3f" $(echo "$duration * 0.875" | bc)) $(printf "%.3f" $(echo "$duration * 0.95" | bc)) )
echo "$frame_times"
# Array to hold the names of the extracted image files.
local image_files=()
# Extract frames at specified times and save them as image files.
for i in "${!frame_times[@]}"; do
local frame_time="${frame_times[$i]}"
local image_file="frame_$((i + 1)).jpg" # Generate image file name.
image_files+=("$image_file") # Add to the array of image files.
# Use ffmpeg to extract the frame.
ffmpeg -ss "$frame_time" -i "$input_video" -vf "scale=-2:320" -frames:v 1 "$image_file" -y # -s "$3"x"$4" # to resize
done
# Merge the extracted images into a single image.
# Create a temporary file to hold the list of images to merge.
local temp_file="temp_images.txt"
for img in "${image_files[@]}"; do
echo "$img" >> "$temp_file"
done
# Use ImageMagick's convert command to merge the images.
# convert @"$temp_file" -append "$output_image"
# magick montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"
montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"
# Clean up temporary files.
rm "$temp_file"
for img in "${image_files[@]}"; do
rm "$img" # Remove the individual frame images.
done
echo "Merged image created: '$output_image'"
}
for f in *.mp4 *.MP$ *.avi *.AVI *.wmv *.WMV *.mpg *.MPG *.mpeg *.MPEG *.mov *.MOV; do [ -e "$f" ] || continue extract_and_merge_frames "$f" "scr-$f.jpg" done
G: [Non-text content: thoughts; content_type=thoughts]
G: [Non-text content: reasoning_recap; content_type=reasoning_recap]
G: Replace only two parts.
ffmpeg lineChange this:
ffmpeg -ss "$frame_time" -i "$input_video" -vf "scale=-2:320" -frames:v 1 "$image_file" -yto this:
ffmpeg -ss "$frame_time" -i "$input_video" -vf "scale=-2:320" -frames:v 1 -update 1 -y "$image_file"That shuts up the single-image complaint and makes the intent explicit.
temp_images.txt with @Delete this whole block:
# Merge the extracted images into a single image.
# Create a temporary file to hold the list of images to merge.
local temp_file="temp_images.txt"
for img in "${image_files[@]}"; do
echo "$img" >> "$temp_file"
done
# Use ImageMagick's convert command to merge the images.
# convert @"$temp_file" -append "$output_image"
# magick montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"
montage @"$temp_file" -tile 3x3 -geometry +0+0 "$output_image"
# Clean up temporary files.
rm "$temp_file"and replace it with this:
# Merge the extracted images into a single image.
montage "${image_files[@]}" -tile 3x3 -geometry +0+0 "$output_image"That is the actual fix for the Ubuntu/ImageMagick 6 problem.
# Extract frames at specified times and save them as image files.
for i in "${!frame_times[@]}"; do
local frame_time="${frame_times[$i]}"
local image_file="frame_$((i + 1)).jpg"
image_files+=("$image_file")
ffmpeg -ss "$frame_time" -i "$input_video" -vf "scale=-2:320" -frames:v 1 -update 1 -y "$image_file"
done
# Merge the extracted images into a single image.
montage "${image_files[@]}" -tile 3x3 -geometry +0+0 "$output_image"
# Clean up temporary files.
for img in "${image_files[@]}"; do
rm "$img"
done#!/bin/bash
# /home/david/Documents/shell-script-backups/repeat-in-all-dirs.sh /home/david/Documents/shell-script-backups/cover-clip-local-anime.sh
# THE ONLY DIFFERENCE IS A 0-FRAME START INSTEAD OF 0.1 - because the clips always have a 'start'
extract_and_merge_frames() {
local input_video="$1"
local output_image="$2"
if [[ ! -f "$input_video" ]]; then
echo "Error: Input video file '$input_video' does not exist."
exit 1
fi
local duration
duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$input_video")
duration=${duration%.*}
local frame_times=( $(echo "$duration * 0" | bc) $(printf "%.3f" $(echo "$duration * 0.125" | bc)) $(printf "%.3f" $(echo "$duration * 0.25" | bc)) $(printf "%.3f" $(echo "$duration * 0.375" | bc)) $(printf "%.3f" $(echo "$duration * 0.5" | bc)) $(printf "%.3f" $(echo "$duration * 0.625" | bc)) $(printf "%.3f" $(echo "$duration * 0.75" | bc)) $(printf "%.3f" $(echo "$duration * 0.875" | bc)) $(printf "%.3f" $(echo "$duration * 0.95" | bc)) )
echo "$frame_times"
local image_files=()
for i in "${!frame_times[@]}"; do
local frame_time="${frame_times[$i]}"
local image_file="frame_$((i + 1)).jpg"
image_files+=("$image_file")
ffmpeg -ss "$frame_time" -i "$input_video" -vf "scale=-2:320" -frames:v 1 -update 1 -y "$image_file"
done
montage "${image_files[@]}" -tile 3x3 -geometry +0+0 "$output_image"
for img in "${image_files[@]}"; do
rm "$img"
done
echo "Merged image created: '$output_image'"
}
for f in *.mp4 *.MP4 *.avi *.AVI *.wmv *.WMV *.mpg *.MPG *.mpeg *.MPEG *.mov *.MOV; do
[ -e "$f" ] || continue
extract_and_merge_frames "$f" "scr-$f.jpg"
doneYou have this typo in the file loop:
*.MP$That should be:
*.MP4So your original loop was accidentally wrong there.
If you want the montage tiles to have no border and no label space at all, this is fine:
montage "${image_files[@]}" -tile 3x3 -geometry +0+0 "$output_image"If it still pads more than you want, use:
montage "${image_files[@]}" -tile 3x3 -geometry 426x320+0+0 "$output_image"But I would test the simpler version first.