Autotrim white border from scanned image with ImageMagick? - imagemagick

I have ~200 scanned photos which I want to crop the white space out of. See example:
Can someone provide me with the appropriate command line code to do this?... I have been trying to sort out the -trim and -fuzz options with no luck. NOT ALL images are same size (i.e. 4x6, 5x7, etc). All images were scanned/saved as jpg
Ideal scenario is a script where new trimmed photos are saved in one subdirectory.
Thanks in advance!

I would suggest using -morphology to remove the scan artifacts, trim, and then capture the resulting paging.
PAGE_OFFSET=$(convert TrmkF.jpg -morphology Dilate:3 Diamond:3,5 -fuzz 10% -trim -format '%wx%h%O' info:-)
The $PAGE_OFFSET variable should now have the rough location of the scanned photo. We can apply that value with the -crop command.
convert TrmkF.jpg -crop $PAGE_OFFSET output.jpg
[![output][1]][1]
Edit
A (powershell) batch script may look as simple as...
Get-ChildItem "C:\path\to\photos" -Filter *.jpg |
Foreach-Object {
$pageOffset = magick $_.FullName -morphology Dilate:3 Diamond:3,5 -fuzz 10% -trim -format '%xx%h%O' info:- | Out-String
$output = $_.FullName + ".output.jpg"
magick $_.FullName -crop $pageOffset +repage $output
}
ymmv
[1]: https://i.stack.imgur.com/u8bSs.png

I've found that the above gives bad results, I think the formatting is different on MacOS or something so sharing the success story here. I have exactly this same issue - hundreds of scanned photos with some blotches in the white ruining the auto trim function.
I just modified parameters from the other individual's answer and got amazing results using this:
cd into your folder of images
mkdir ../done
v
echo "$f";\
size=$(magick "$f" -bordercolor White -border 10x10 \
-morphology Dilate:5 Diamond:5,7 -fuzz 5% -trim \
-format "%wx%h%O" info:-); \
echo $size; \
magick "$f" -bordercolor White -border 10x10 -crop $size +repage "../done/$f"; done;

Related

Combined Multiple Commands in Imagemagick

I'n very new to imagemagick and i need to learn how could i combined these two commands to make it work.
I need to add background image to a transparent image. I tried to combine these two commands but was not successful.
magick mogrify -path Output_Path -trim -filter Triangle -define filter:support=2 -thumbnail 450x450 -gravity center -extent 500x500 -unsharp 0.25x0.25+8+0.065 -dither None -posterize 136 -quality 82 -define jpeg:fancy-upsampling=off -define png:compression-filter=5 -define png:compression-level=9 -define png:compression-strategy=1 -define png:exclude-chunk=all -interlace none -colorspace sRGB -strip Transparent_Image_Path
magick Background_Image.png Transparent_Image.png -composite output.jpg
Result Should Be Like This :
Image Reference
Thanks in advance!
There are several possible syntaxes, depending on how your brain likes to work ;-)
What you need to bear in mind when trying to understand this is that all processing operations, e.g. -crop or -resize apply to all loaded images. So, you either need to load images that need processing and process them before loading images you don't want affected, or you need to restrict processing to certain images only... using parenthesised "aside" operations.
You can proceed like this:
load transparent foreground image first and process it before loading the background image, then
load background image, then
exchange the order and composite
That looks like this:
magick FOREGROUND.PNG -resize ... -crop ... \
BACKGROUND.PNG \
+swap -composite RESULT.JPG
Alternatively, you could:
load the background image, then
load the foreground image and process it "aside" within parentheses so that the background image is not affected
composite
That looks like this:
magick BACKGROUND.PNG \
\( FOREGROUND.PNG -resize ... -crop ... \) \
-composite RESULT.JPG
If you need to independently process both background and foreground images prior to compositing, use:
magick \
\( BACKGROUND.PNG -resize ... -crop ... \) \
\( FOREGROUND.PNG -resize ... -crop ... \) \
-composite RESULT.JPG
Note that if you use Windows:
the backslashes at line-ends become carets (^) and may not have trailing spaces after them,
the backslashes preceding opening and closing parentheses must be omitted,
most single quotes probably need replacing with double quotes - probably not applicable to this specific question.
Note that your command is probably unnecessarily complicated, I would recommend omitting quite a lot of it and seeing how you get on. For example, all the define png:XXX=YYY settings are irrelevant if you aren't creating a PNG as output.
You can carry forward the JPEG-related parameters (quality, interlace, upsampling) and put them in anywhere you like, probably at the start like this, but put the colorspace and strip at the end to ensure both input files are stripped:
magick -quality 82 -define jpeg:fancy-upsampling=off \
BACKGROUND.PNG ... \
FOREGROUND.PNG ... \
-composite -colorspace sRGB -strip RESULT.JPG

How to pass Imagemagick montage output and image properties to the convert command without using any temporary files?

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

Using Imagemagick, save its variables per use

History: I try to apply a watermark on a series of images in a directory, at linux, putting that watemark on an extra created space / border below the image. Both Watermark and the extra space is resized / created depending on the height size on the image they are applied.
I do various calculations based on the height of the image that is processed each time that they are saved on some variables. However, I do not know how to a) Make a script to be applied to all the images on the directory, using the saved height calculations PER IMAGE, OR b) to be applied these calculations PER IMAGE, as "single terminal command" e.g. "for pic in DSC*.*; do height calculations....... and saved at that variable to be used for the next batch terminal command"
The variables of imagemagick. How I saved them to be used for the next use, per image?
height=`convert DSC__12.jpg -format "%w" info:`
yoff_p50=`convert xc: -format "%[fx:$height/50]" info:`
yoff_p100=`convert xc: -format "%[fx:$height/100]" info:`
yoff_p200=`convert xc: -format "%[fx:$height/200]" info:`
I tried the following linux terminal command: Is there a better elegant (shorter?) way?
for pic in DSC*.*;
do height=`convert "$pic" -format "%w" info:`;
yoff_p50=`convert xc: -format "%[fx:$height/50]" info:`;
yoff_p100=`convert xc: -format "%[fx:$height/100]" info:`;
yoff_p200=`convert xc: -format "%[fx:$height/200]" info:`;
convert -background black -gravity NorthWest -extent 0%x0%+0+${yoff_p50} -pointsize ${yoff_p100} -fill white -undercolor '#00000080' -gravity SouthWest -annotate +${yoff_p100}+${yoff_p200} "$(stat -c '%y' "$pic")" "$pic" "${pic//.*}-d.jpg";
done;
for pic in DSC*-d.*;
do height=`convert "$pic" -format "%w" info:`;
yoff_p50=`convert xc: -format "%[fx:$height/50]" info:`;
yoff_p100=`convert xc: -format "%[fx:$height/100]" info:`;
yoff_p200=`convert xc: -format "%[fx:$height/200]" info:`;
convert /home/elias/Data/PHOTOS/watermark_0.png -resize ${yoff_p50}x${yoff_p50}^ /home/elias/Data/PHOTOS/res_watermark_0.png;
composite -dissolve 100% -gravity SouthEast /home/elias/Data/PHOTOS/res_watermark_0.png "$pic" "${pic//.*}-marked.jpg";
done;
ps. How I can check what values are saved per imagemagick variable?
i found out how to check the variable value in linux terminal: echo ${height}
UPDATE: Solution: The solution by Mark Setchell worked.
Finally, i run: ..and it did the job from linux terminal very fine.
for pic in DSC*.* ; do
#Save either the date of File Creation or Modification on filename
exiftool "-FileName<CreateDate" -d "${pic//.*}_%Y%m%d_%H%M%S.jpg" "$pic" && exiftool "-FileName<FileModifyDate" -d "${pic//.*}_%Y%m%d_%H%M%S.jpg" "$pic" ;
done;
for pic in DSC*.* ; do
# Determine offsets and sizes
read w y1 y2 y3 < <(identify -format "%w %[fx:w/50] %[fx:w/100] %[fx:w/200]" "$pic");
ts=$(stat -c '%y' "$pic");
convert -size ${w}x${y1} xc:black -gravity SouthWest \
-pointsize ${y2} -fill white -undercolor '#00000080' -annotate +${y2}+${y3} "$ts" \
\( /home/elias/Data/PHOTOS/res_watermark_0.png -resize "${y1}x${y1}^" \) -gravity East -composite \
"$pic" +swap -append "${pic//.*}-marked.jpg";
done
It can probably be improved further, but I would go with something more like this:
#!/bin/bash
for f in DSC* ; do
# Determine offsets and sizes
read w y1 y2 y3 < <(identify -format "%w %[fx:w/50] %[fx:w/100] %[fx:w/200]" "$f")
# Get timestamp - different on macOS. You want: ts=$(stat -c '%y' "$f")
ts=$(stat -f "%Sm%n" "$f")
convert -size ${w}x${y1} xc:black -gravity SouthWest \
-pointsize ${y2} -fill white -undercolor '#00000080' -annotate +${y2}+${y3} "$ts" \
\( watermark.png -resize "${y1}x${y1}^" \) -gravity East -composite \
"$f" +swap -append watermarked-"$f".jpg
done
Things to note:
The line beginning read w y1... gets all the calculated values in a single go - this will be quicker.
The line beginning ts=$(...) gets the timestamp. I abstracted it out because I am on macOS and it is different. You can re-integrate it, if you want.
The final line is where the action is. I create a black annotation bar the right size first, then write in the timestamp in white on the left. Then, on the next line I load the watermark and resize it and splat it into the right corner of the black annotation bar. Then, on the next line, I load the main image, and swap the order so the annotation bar is at the bottom and the image is at the top before appending.
Remember when debugging ImageMagick scripts, you can add in -write "debug.png" at any position to see how something looks after resizing , or before compositing or wherever.
Another way to save variables in one ImageMagick command is using declare.
declare `convert xc: -format "yoff_p50=%[fx:$height/50]\nyoff_p100=%[fx:$height/50]\nyoff_p200=%[fx:$height/200]\n" info:`
Example:
declare `convert rose: -format "ww=%[fx:w]\nhh=%[fx:h]\n" info:` echo "ww=$ww; hh=$hh;"
ww=70; hh=46;

Adding border to multiple images using gimp?

I have around 100 pictures that i want to add white border to it all at once.
I use Linux and also use gimp ,.. please suggest me something to do so online of offline.
and one more thing that i have tried convert option on imagemagick but nothing happen.
If you want to do 100 all at once you will be best off using ImageMagick's mogrify command like this to add a 10 pixel white border around all images:
mogrify -mattecolor white -frame 10x10 image*.jpg
If the images are not all in a single directory, you can do the following which will do the same thing across all subdirectories of the one you are currently in:
find . -name \*.jpg -exec convert "{}" -mattecolor white -frame 10x10 "{}" \;
Obviously you can change the 10 to a different number of pixels if you wish.
Please make a backup before using this as I may have misunderstood your needs.
Updated
If you want a drop shadow, you really need to be working with PNG rather than JPG since the former supports transparency and the latter doesn't - but IM can convert your JPEGs to PNGs anyway. I use the following command for drop shadows:
convert image.jpg \( -clone 0 -background black -shadow 80x3+0+8 \) -reverse -background none -layers merge +repage image.png
So, I would apply that to a pile of images like this:
#!/bin/bash
for f in *.jpg; do
new=${f%%jpg}png # Work out new name = original name minus "jpg" + "png"
echo Processing $f into $new
convert "$f" \( -clone 0 -background black -shadow 80x3+0+8 \) -reverse -background none -layers merge +repage "$new"
done

Image magick to put images in row and then generate reflection

I was wondering if I can use imagemagick to produce a result image like this
My initial image will be like this
I am able to create the dark image to be put in the background like this -
convert -brightness-contrast -20x10 GARDENS-ILLUSTRATED_JAN-14.jpg out_lighter2.jpg
But I do not know how to put them in line like that and then generate a reflection.
I have got something close, but have no more time, so maybe you can fiddle with it and get it to what you want:
#!/bin/bash
input=input.png
# Calculate width and height
w=$(convert $input -ping -format "%w" info:)
h=$(convert $input -ping -format "%h" info:)
# Calculate how much to chop off bottom and height of reflection
chop=$(echo $h*60/100|bc)
refl=$(echo $h*20/100|bc)
convert $input -alpha on \
\( +clone -flip -size ${w}x${refl} gradient:gray40-black -alpha off -compose CopyOpacity -composite \) \
-append -background white -compose Over -flatten -gravity South -chop 0x${chop} output1.png
# Darken and reduce for second layer
convert output1.png -brightness-contrast -25x8 -resize 90% output2.png
# Darken and reduce for third layer
convert output2.png -brightness-contrast -25x8 -resize 90% output3.png
# Make big new output canvas
neww=$(echo $w*1.6|bc)
newh=$(echo $h*1.6|bc)
# Splat images onto it with offsets
convert -size ${neww}x${newh} xc:transparent -page +0+80 output3.png \
-page +40+40 output2.png \
-page +80+0 output1.png -flatten output.png
My basic idea is to generate the bright, frontmost image first in output1.png, then darken and reduce it into output2.png and again into output3.png. Then composite them all together into output.png with a little offset.

Resources