I am trying to rewrite an Imagemagick script into Imagick. I am struck at a point where I want to write the following code piece to Imagick.
-compose mathematics
Could someone check whether there is an option? I don't find a -compose option like
imagick::COMPOSITE_MATHEMATICS in PHP
In Imagemagick command line, if you have two input images A and B
convert A B -define compose:args="a,b,c,d" -compose mathematics result
then
that means
a*A*B + b*B + c*A +d
so
a*A*B is
convert A B -compose multiply -composite -evaluate multiply a tmp1
b*B is
convert B -evaluate multiply b tmp2
c*A is
convert A -evaluate multiply c tmp3
a*A*B + b*B + c*A +d
convert tmp1 tmp2 -compose plus -composite tmp3 -compose plus -composite -evaluate add 100*d% result
where 100*d must be evaluated ahead of time
You will need to get the Imagick equivalents.
Not all uses of -compose mathematics will be separable, if any involve values outside the Quantum Range of your compile. So the be sure you need to use HDRI compile of Imagemagick such as with Imagemagick 7 default compile or recompile IM 6 with HDRI enabled.
If you cannot use HDRI, then if the separate operations do not work, then you will need to use -fx to do the equivalent and it will be considerably slower
Related
i have a folder of images with names foo<bar>.png where <bar> ranges from 0 to 100.
When i create a gif of these images with
convert -dispose previous -delay 10 -resize 25% -loop 0 *.png myimage.gif
it creates a gif with images in order like 0,1,2,3...
Is there a command to select images in random order?
If your PNG images are all the same dimensions, and if the number of images can be evenly divided into a grid, like 72 images would make a grid of 8 x 9 images, and if your images are small enough to read them all into a single ImageMagick command, here is a command that will randomize the order of a group of 72 input images...
convert *.png -virtual-pixel tile +append -crop 9x1# -append +repage ^
-crop 1x9# -distort affine "0,0 %[fx:floor(rand()*8)*(w/8)],0" -append +repage ^
-crop 8x1# -distort affine "0,0 0,%[fx:floor(rand()*9)*(h/9)]" +append +repage ^
-crop 8x9# +repage -set delay 10 -loop 0 image.gif
It basically makes a grid, randomly rolls all the rows, then randomly rolls all the columns. The shuffle scatters the images pretty well, but if you want a deeper shuffle, copy and paste those two "-crop ... -distort" lines, and add them below the first two...
convert *.png -virtual-pixel tile +append -crop 9x1# -append +repage ^
-crop 1x9# -distort affine "0,0 %[fx:floor(rand()*8)*(w/8)],0" -append +repage ^
-crop 8x1# -distort affine "0,0 0,%[fx:floor(rand()*9)*(h/9)]" +append +repage ^
-crop 1x9# -distort affine "0,0 %[fx:floor(rand()*8)*(w/8)],0" -append +repage ^
-crop 8x1# -distort affine "0,0 0,%[fx:floor(rand()*9)*(h/9)]" +append +repage ^
-crop 8x9# +repage -set delay 10 -loop 0 image.gif
Carefully replace the "8"s and "9"s with the width and height of the grid that holds the number of images you're using. I use it in a script that shuffles a deck of playing card images, 13 rows and 4 columns.
This uses ImageMagick v6 in Windows CMD syntax. In a BAT script double all the percent signs "%%". It would probably work on a *nix OS by changing all the continued-line carets "^" to backslashes "\". For ImageMagick v7 use "magick" instead of "convert".
I can tell you 2/3 of the answer on Windows, and hopefully some other kind soul can add the rest for you. Nobody said answers have to be complete.
Create a text file containing the names of the PNGs you want to animate. I believe that is:
DIR /B *.png > filelist.txt
Shuffle the lines in that file. I don't know how to do that in Windows. In Linux and macOS it is shuf. Here's a little Python equivalent:
python -c "import random, sys; lines = open(sys.argv[1]).readlines(); random.shuffle(lines); print ''.join(lines)," filelist.txt > shuffled.txt
Pass that file to ImageMagick like this:
convert -loop 0 -delay 80 #filelist.txt animation.gif
If you get that working, you can avoid the need for a temporary file by using this syntax:
DIR /B *.png | SHUFFLELINES | convert -loop 0 -delay 80 #- animation.gif
I have a set of images, and I can use the Imagemagick montage command on them to produce a montage image file with transparent background (let's call this fgimg). Now I have another existing image (let's call this bgimg) that I'd like to use (after some special processing with the convert command) as the background for fgimg, which can be achieved within the same convert command. At this point it seems trivial to avoid writing the temporary fgimg to disk, simply by piping the standard output of montage to the standard input of convert.
My problem is that the special processing I'm applying to bgimg will require some knowledge of the image properties of fgimg (e.g., resizing bgimg to have the same size as fgimg), which I don't know in advance. How can this information be retrieved and used in the convert command?
Note: I'm using Imagemagick version 6.9.7-4 on Linux.
I'll include some commands below to further illustrate the problem in detail.
The following command produces the montage image fgimg from a set of input images. The output is in the 'special' miff format (which seems best for temporary output to be worked on later), and has transparent background so that the actual background can be applied later. Most of the other options here are not important, but the point is that the output size (dimensions) cannot be determined in advance.
montage input_*.jpg -tile 5x -border 2 -geometry '200x200>+20+20' \
-gravity center -set label '%f\n%G' -background none -fill white \
-title 'Sample Title' miff:fgimg
Next, I have another input image bgimg.jpg. I want to perform some processing on it before using it as background to fgimg. The processing can be quite complex in general, but in this example, I want to:
resize bgimg.jpg to fit inside the dimensions of fgimg without any cropping;
apply a fade-to-black effect around the edges;
make it the same size as fgimg, with a black background;
combine this with fgimg to produce the final output.
Notice that I need the size of fgimg in two places. I can first extract this into a shell variable:
size=$(identify -format '%G' miff:fgimg)
Then I can do all the steps above in one convert command (note that $size is used twice):
convert "bgimg.jpg[$size]" -gravity center \
\( +clone -fill white -colorize 100% -bordercolor black \
-shave 20 -border 20 -blur 0x20 \) -compose multiply -composite \
-background black -compose copy -extent $size \
miff:fgimg -compose over -composite final_out.jpg
Now here is the problem: I want to avoid writing the temporary file fgimg to disk.
I could replace miff:fgimg with miff:- in both the montage and convert commands and then just pipe one to the other: montage ... | convert .... But how do I deal with the $size?
I tried to use file descriptors (miff:fd:3) but this does not seem to work, which is confirmed by the comments to this question.
Is there a way to do this (in Imagemagick v6) without creating a temporary file?
This example command uses ImageMagick v6 on a bash shell. Instead of "montage" it starts by using "convert" to create a "logo:", one of IM's built-in sample images, then pipes it out as a MIFF and into the "convert" command that follows. You can pipe the output of "montage" just as easily. And it uses another IM built-in image "rose:" as your "bgimg.jpg"...
convert logo: miff:- | convert - rose: \
+distort SRT "%[fx:t?min(u.w/v.w,u.h/v.h):1] 0" \
-shave 1 +repage -gravity center -bordercolor black \
\( -clone 1 -fill white -colorize 100 -shave 6 -border 6 \
-blur 0x6 -clone 1 -compose multiply -composite \) -delete 1 \
\( -clone 0 -alpha off -fill black -colorize 100 \
-clone 1 -compose over -composite \) -delete 1 \
+swap -composite final_out.jpg
That reads the piped image "-" and the background image "rose:".
Then it uses "+distort" with an FX expression to scale "rose:" to the maximum dimensions that still fit within the original piped input image. That operation adds a pixel all around so we use "-shave 1" to get rid of that.
Next inside parentheses it clones that re-scaled background image, makes an edge blur mask, and composites them to make the fade-to-black edge on the background image. Right after the parentheses it deletes the non-edged background image.
In the next parentheses it clones the input image, makes it black, clones the modified background image, and composites it centered over the black one. Again the non-extended background image is discarded after the parentheses with "-delete 1".
Finally the modified background and the input image are put in the proper order with "+swap" and composited for the final output. Run this command without the last "-composite" to see the two images that result from the prior parts of the command.
Both the main input image and background image can be any size, any dimensions, and any aspect ratio. This works for me on v6.8.9 on a bash shell. It should work on any ImageMagick newer. It should work on Windows by removing all the backslashes that escape parentheses and changing the continued-line backslashes "\" to carets "^".
EDITED TO ADD:
You can use that FX expression to find the scaling amount, save it as a variable, then isolate the background image inside parentheses and use that variable to do the scaling and shaving there. That way it only affects the background image. There may be a rounding error with that, but the main image, which determines the exact final output dimensions, will be unaffected. Note the difference in the first few lines of this command...
convert logo: miff:- | convert - rose: \
-set option:v1 "%[fx:min(u.w/v.w,u.h/v.h)]" \
\( -clone 1 +distort SRT "%[v1] 0" -shave 1 \) -delete 1 \
+repage -gravity center -bordercolor black \
\( -clone 1 -fill white -colorize 100 -shave 6 -border 6 \
-blur 0x6 -clone 1 -compose multiply -composite \) -delete 1 \
\( -clone 0 -alpha off -fill black -colorize 100 \
-clone 1 -compose over -composite \) -delete 1 \
+swap final_out.jpg
I have the following image:
What I want is to preserve only red color and desaturate
every other color into grayscale. Resulting in this:
How can I do that with Imagemagick command line?
I tried this but failed:
convert original.png \( -clone 0 -transparent red -alpha extract -transparent black \) redonly.png
Here is one way, though not perfect, using ImageMagick. I specify hue=0 deg for red and tolerance=25 deg both in range 0 to 360.
Input:
hue=0
tolerance=25
toler=`convert xc: -format "%[fx:(100*$tolerance/360)]" info:`
hueval=`convert xc: -format "%[fx:50-$hue]" info:`
thresh=`convert xc: -format "%[fx:100-$tolerance]" info:`
convert tomato.jpg \
\( -clone 0 -colorspace gray -colorspace sRGB \) \
\( -clone 0 -colorspace HSL -channel 0 -separate +channel \
-evaluate AddModulus ${hueval}% \
-solarize 50% -level 0x50% \
-threshold $thresh% \) \
-swap 0,1 -alpha off -compose over -composite \
result.jpg
Also not perfect, but fairly easy to understand. Basically, you could use the fx operator to inspect the Hue of each pixel and, depending on its Hue/colour, return either the original pixel or its greyscale equivalent.
So, as a first stab, you might do this to replace all pixels exhibiting a high Hue value with their greyscale (lightness) equivalent:
magick ripe.jpg -fx "(u.hue<0.1)? u : u.lightness" result.jpg
Then you might realise that red Hues wrap around at 0/360 degrees on the Hue circle, so you could do:
magick ripe.jpg -fx "(u.hue<0.1)||(u.hue>0.9)? u : u.lightness" result.jpg
Explanation
There are a couple of things going on here. Firstly, the -fx operator is a very low-level, extremely powerful (and sadly rather slow because it is interpreted) way of running a piece of "code" on every pixel in the image. Secondly, I am running a ternary operator with the format:
condition? valueA : valueB
so I am testing a condition for every pixel, and if true I return valueA, and if false I return valueB. When I refer to u and u.hue and u.lightness, the u means the first image in my command - I could load two images and use features of the first to select features of the second, then I would use u and v to differentiate. Finally, the values are scaled on the range [0,1] so I don't test for "Hue>350 in the range [0,360]", instead I test for Hue>0.9 as a sloppy equivalent - I guess I could have used Hue>(350/360). Note that you can make the expression arbitrarily complicated and also put it in a separate file to re-use it like this:
magick ripe.jpg -fx #DeSatNonRed.fx result.jpg
DeSatNonRed.fx might look something like this:
hueMin=350/360;
hueMax=20/360;
(hue>hueMin) || (hue<hueMax) ? u : u.lightness
Note that, in the general case, you should also really consider Saturation when looking at Hues, which you can add in above, but which I omitted for clarity, and because your image is almost fully saturated anyway.
Keywords: Image processing, ImageMagick, low-level fx operator, ternary, pixel-by-pixel, evaluate, expression.
I'm trying to use this dataset to do Exposure Meging (Fusion) in Python. Each image in the dataset has an OpenEXR file that can be downloaded (i don't have much experience with this file format).
I want to extract different samples (jpg or png) from the OpenEXR file with different exposures .
I managed to do that in Darktable :
Open the OpenEXR file (image)
Change the Exposure
Save as jpg
redo for each exposure value (-3EV, -2EV, -1EV, 0EV, 1EV, 2EV, 3EV).
The problem : I have 100 images and i want to automate this process. any idea on how to do that ?
Thank you in advance
Since each increment of EV ("Exposure Value") corresponds to doubling the exposure, and EXR files are in linear light (not gamma-encoded), you would expect that you can double the pixel values in an EXR file to add 1EV and halve them to do -1EV...
So, I downloaded the Luxo EXR file from here. Then I went into Photoshop and clicked:
Image -> Mode -> 8-bits/channel
and selected Method = Exposure and Gamma and set exposure=+1 and saved the resulting file as a JPEG with +1 in its name. I repeated that for EV-3, EV-2, EV+0, EV+1, EV+2, EV+3.
I then looked at the resulting files with ImageMagick using commands like the following in the Terminal to examine the mean value of the combined RGB image:
magick identify -verbose image-EV+2.jpg
I then went about producing those same mean values, and found that the following works:
# To increase 1 EV
magick input.exr -evaluate multiply 2 result.jpg
# To increase 2 EV
magick input.exr -evaluate multiply 4 result.jpg
# To increase 3 EV
magick input.exr -evaluate multiply 8 result.jpg
And so on...
So, I wrote a bash script to do that as follows, which you could save in your HOME directory as adjust.sh:
#!/bin/bash
# Default file, if none specified
file=${1:-/Users/mark/Desktop/LuxoDoubleChecker.exr}
# Default EV of +1, if none specified
EV=${2:-1}
# Strip extension
base="${file%.*}"
# Apply given EV to file and save with new name
new="${base}EV${EV}.jpg"
echo "Applying EV $EV to $file, saving as $new"
magick "$file" -evaluate multiply $(bc -l <<< "2^$EV") "$new"
Then, just necessary once, make it executable:
chmod +x $HOME/adjust.sh
And then you run it like this to add +3EV to SomeImage.exr:
~/adjust.sh SomeImage.exr 3
Sample Output
Applying EV 3 to SomeImage.exr, saving as SomeImageEV3.jpg
Alternatively, if you save this script as allEVs.sh, it will load the specified image just once and generate all 7 exposures in one go without re-reading the input EXR file 7 times:
#!/bin/bash
# Default file, if none specified
file=${1:-/Users/mark/Desktop/LuxoDoubleChecker.exr}
# Strip extension to get base without extension
base="${file%.*}"
magick "$file" \
\( +clone -evaluate multiply 0.125 -write "${base}EV-3.jpg" +delete \) \
\( +clone -evaluate multiply 0.25 -write "${base}EV-2.jpg" +delete \) \
\( +clone -evaluate multiply 0.5 -write "${base}EV-1.jpg" +delete \) \
\( +clone -evaluate multiply 1 -write "${base}EV-0.jpg" +delete \) \
\( +clone -evaluate multiply 2 -write "${base}EV+1.jpg" +delete \) \
\( +clone -evaluate multiply 4 -write "${base}EV+2.jpg" +delete \) \
-evaluate multiply 8 "${base}EV+3.jpg"
Please check carefully that this works correctly for you before basing a lifetime's analysis on it...
Keywords: Image processing, HDR, High Dynamic Range, EXR, EV, Exposure Value, f-stop, stop, stops, exposure, increase, decrease, tone map, ImageMagick.
I know exactly what I want to do, and could do it with python, scipy, and PIL.
I want to use imagemagick, since it is designed specifically for these actions.
T is highest legal intensity (0, of course is lowest)
Input image into temporary MPC named I
Gaussian blur I and store into temporary MPC named G
Subtract and divide D = (I - G) / G
Get maximum M = max (T * abs(D))
Offset, normalize, and scale O = T * (D + M) / (2 * M)
Output O into file name output.png
I can't figure out how to do this from the online documentation.
Imagemagick documentation vocabulary seems to be for
image manipulation professionals and it is beyond my understanding.
I have tried to reproduce your commands in Imagemagick, but I am not sure of the result or about whether T and M should be in range 0 to 1 or 0 to Quantumrange (0 to 65535 for Q16 HDRI IM compile). I tested on the Imagemagick logo: image using Imagemagick 7.0.7.21 Q16 HDRI.
T="65000"
sigma=5
magick logo: I.mpc
magick I.mpc -blur 0x$sigma G.mpc
magick I.mpc G.mpc +swap -compose minus -composite G.mpc +swap -compose divide -composite D.mpc
M=`magick D.mpc D.mpc -compose multiply -composite -evaluate pow 0.5 -evaluate multiply $T -format "%[fx:maxima]" info:`
M2=`magick xc: -format "%[fx:2*$M]" info:`
magick D.mpc -evaluate add $M -evaluate divide $M2 -evaluate multiply $T output.png
Line 1: Set T=65000 (range 0 to 65355)
Line 2: Set gaussian blur sigma to 5
Line 3: Read the input into I.mpc
Line 4: Apply gaussian blur to I.mpc to create G.mpc
Line 5: Create D=(I-G)/G (requires HDRI IM 7 compile to keep negative values)
Line 6: Compute M=T*Max(sqrt(D*D)) as a single number variable in the range 0 to 65535 (Quantumrange for 16-bit IM compile)
Line 7: Compute 2*M as variable M2
Line 8: Compute output O = T * (D + M) / (2 * M)
If this is not correct (does not match your python, etc, approach, then please post and input and output example and then I might be able to correct any false assumptions or mistakes and make it work the same.
If wanting to use Imagemagick 6, then one would have to compile or get a version that is Q16 HDRI compiled. Then in the above commands, just change magick to convert.
Here is the corrected method as one long command line using Imagemagick 7.0.7.21 Q16 HDRI.
T="65000"
sigma=5
magick \
\( logo: -write mpr:imgI +delete \) \
\( mpr:imgI -blur 0x$sigma -write mpr:imgG +delete \) \
\( mpr:imgI mpr:imgG +swap -define compose:clamp=off -compose minus -composite mpr:imgG +swap -define compose:clamp=off -compose divide -composite -write mpr:imgD +delete \) \
\( mpr:imgD mpr:imgD -define compose:clamp=off -compose multiply -composite -evaluate pow 0.5 -evaluate multiply $T -write mpr:imgT +delete \) \
mpr:imgT -set option:mm "%[fx:maxima]" -set option:nn "%[fx:2*maxima]" +delete mpr:imgD -evaluate add "%[mm]" -evaluate divide "%[nn]" -evaluate multiply $T output3.png
Here is a bash script for handling
Any 0-255 image file recognized by convert.
I tried it on some sample files of my own.
It works.
#!/bin/bash
echo $#
if [ $# -ne 1 ]
then
echo "Usage: ${0} {imagefilename}"
elif [ -f "$1" ]
then
T="255"
sigma=5
convert ${1} ${1}.I.mpc
convert ${1}.I.mpc -blur 0x$sigma ${1}.G.mpc
convert ${1}.I.mpc ${1}.G.mpc +swap -compose minus -composite ${1}.G.mpc +swap -compose divide -composite ${1}.D.mpc
M=`convert ${1}.D.mpc ${1}.D.mpc -compose multiply -composite -evaluate pow 0.5 -evaluate multiply $T -format "%[fx:maxima]" info:`
M2=`convert xc: -format "%[fx:2*$M]" info:`
convert ${1}.D.mpc -evaluate add $M -evaluate divide $M2 -evaluate multiply $T ${1}.mdif.png
else
echo "Usage: ${0} {imagefilename}\n!exists ${1}"
fi
I had hoped the imagemagick commands would have been
easier to read and easier to understand but fmw42 achieved the goal.
CORRECTION:
I made a mistake in my previous posts. When using HDRI and needing to keep negative values or values outside the range 0 to quantumrange, any composite operations will by default clamp and give results as if HDRI was not enabled. So one needs to add defines before the composite operations to keep from clamping (clipping) to normal non-HDRI dynamic range limits. So the correct code would be as follows:
T="65000"
sigma=5
magick logo: I.mpc
magick I.mpc -blur 0x$sigma G.mpc
magick I.mpc G.mpc +swap -define compose:clamp=off -compose minus -composite G.mpc +swap -define compose:clamp=off -compose divide -composite D.mpc
M=`magick D.mpc D.mpc -define compose:clamp=off -compose multiply -composite -evaluate pow 0.5 -evaluate multiply $T -format "%[fx:maxima]" info:`
M2=`magick xc: -format "%[fx:2*$M]" info:`
magick D.mpc -evaluate add $M -evaluate divide $M2 -evaluate multiply $T output.png
The result is a bit different from my earlier results.