Imagemagick resized pictures are different using a single command or two commands - imagemagick

I can't understand why those two scripts seem to produce a different result, given that the second one is like the first one but separated into two commands.
First script:
convert lena_std.tif -compress None -resize 160x160 -compress None -resize 32x32 test1.bmp
Second script:
convert lena_std.tif -compress None -resize 160x160 test2.bmp
convert test2.bmp -compress None -resize 32x32 test3.bmp
I use the following command to check the difference between the results:
convert test1.bmp test3.bmp -metric AE -compare diff.bmp
I use Imagemagick on Ubuntu 22.04. My convert -version indicates: Version: ImageMagick 6.9.11-60 Q16 x86_64 2021-01-25.

Because when you scale you interpolate pixels.
Roughly, the code considers the pixel at (x,y) in the result, and computes where it comes from in the source. This is usually not an exact pixel, more like an area, when you scale down, or part of a pixel, when you scale up. So to make up the color of the pixel at (x,y) some math is applied: if you scale down, some averaging of the source area, and if you scale up, something that depends on how close the source is to the edge of the pixel and how different the color of neighboring pixels are.
This math can be very simple (the color of the closest pixel), simple (some linear average), a bit more complex (bi-cubic interpolation) or plain magic (sinc/Lanczos), the more complex forms giving the better results.
So, in one case, you obtain a result directly from the source to the pixel you want, and in the other you obtain the final result from an approximation of what the image would look at the intermediate size.
Another way to see it is that each interpolation has a spatial frequency response (like a filter in acoustics), and in one case you apply a single filter and in the other one you compose two filters.

Related

Stitching images using GraphicsMagick with Blending

I have to stitch number of tiles using GraphicsMagick to create one single image. I am currently using -convert with -mosaic with some overlap to stitch tiles. But the stitched image has border where the overlap is done.
Following is the command I am using:
gm convert -background transparent
-page "+0+0" "E:/Images/Scan 001_TileScan_001_s00_ch00.tif"
-page "+0+948" "E:/Images/Scan 001_TileScan_001_s01_ch00.tif"
-page "+0+1896" "E:/Images/Scan 001_TileScan_001_s02_ch00.tif"
-page "+0+2844" "E:/Images/Scan 001_TileScan_001_s03_ch00.tif"
-mosaic "E:/Output/temp/0.png"
The final image looks like this:
How to stitch and Blend without Border?
I've been part of several projects to make seamless image mosaics. There are a couple of other factors you might like to consider:
Flatfielding. Take a shot of a piece of white card with your lens and lighting setup, then use that to flatten out the image lightness. I don't know if GM has a thing to do this, #fmw42 would know. A flatfield image is specific to a lighting setup, lens aperture setting, focus setting and zoom setting, so you need to lock focus/aperture/zoom after taking one. You'll need to do this correction in linear light.
Lens distortion. Some lenses, especially wide-angle ones, will introduce significant geometric distortion. Take a shot of a piece of graph paper and check that the lines are all parallel. It's possible to use a graph-paper shot to automatically generate a lens model you can use to remove geometric errors, but simply choosing a lens with low distortion is easier.
Scatter. Are you moving the object or the camera? Is the lighting moving too? You can have problems with scatter if you shift the object: bright parts of the object will scatter light into dark areas when they move under a light. You need to model and remove this or you'll see seams in darker areas.
Rotation. You can get small amounts of rotation, depending on how your translation stage works and how carefully you've set the camera up. You can also get the focus changing across the field. You might find you need to correct for this too.
libvips has a package of functions for making seamless image mosaics, including all of the above features. I made an example for you: with these source images (near IR images of painting underdrawing):
Entering:
$ vips mosaic cd1.1.jpg cd1.2.jpg join.jpg horizontal 531 0 100 0
Makes a horizontal join to the file join.jpg. The numbers give a guessed overlap of 100 pixels -- the mosaic program will do a search and find the exact position for you. It then does a feathered join using a raised cosine to make:
Although the images have been flatfielded, you can see a join. This is because the camera sensitivity has changed as the object has moved. The libvips globalbalance operation will automatically take the mosaic apart, calculate a set of weightings for each frame that minimise average join error, and reassemble it.
For this pair I get:
nip2, the libvips GUI, has all this with a GUI interface. There's a chapter in the manual (press F1 to view) about assembling large image mosaics:
https://github.com/jcupitt/nip2/releases
Global balance won't work from the CLI, unfortunately, but it will work from any of the libvips language bindings (C#, Python, Ruby, JavaScript, C, C++, Go, Rust, PHP etc. etc.). For example, in pyvips you can write:
import pyvips
left = pyvips.Image.new_from_file("cd1.1.jpg")
right = pyvips.Image.new_from_file("cd1.2.jpg")
join = left.mosaic(right, "horizontal", 531, 0, 100, 0)
balance = join.globalbalance()
balance.write_to_file("x.jpg")
Here is an example using ImageMagick. But since colors are different, you will only mitigate the sharp edge with a ramped blend. The closer the colors are and the more gradual the blend (i.e. over a larger area), the less it will show.
1) Create red and blue images
convert -size 500x500 xc:red top.png
convert -size 500x500 xc:blue btm.png
2) Create mask that is solid white for most and a gradient where you want to overlap them. Here I have 100 pixels gradient for 100 pixel overlap
convert -size 500x100 gradient: -size 500x400 xc:black -append -negate mask_btm.png
convert mask_btm.png -flip mask_top.png
3) Put masks into the alpha channels of each image
convert top.png mask_top.png -alpha off -compose copy_opacity -composite top2.png
convert btm.png mask_btm.png -alpha off -compose copy_opacity -composite btm2.png
4) Mosaic the two images one above the other with an overlap of 100
convert -page +0+0 top2.png -page +0+400 btm2.png -background none -mosaic result.png
See also my tidbit about shaping the gradient at http://www.fmwconcepts.com/imagemagick/tidbits/image.php#composite1. But I would use a linear gradient for such work (as shown here), because as you overlap linear gradients they sum to a constant white, so the result will be fully opaque where they overlap.
One other thing to consider is trying to match the colors of the images to some common color map. This can be done by a number of methods. For example, histogram matching or mean/std (brightness/contrast) matching. See for example, my scripts: histmatch, matchimage and redist at http://www.fmwconcepts.com/imagemagick/index.php and ImageMagick -remap at https://www.imagemagick.org/Usage/quantize/#remap

Defining image mask based on image values G'MIC

I am using the inpainting command in GMIC, which takes in both an image and a mask which indicates which part of that image to inpaint. Values that are 255 on the mask are then filled in.
http://gmic.eu/reference.shtml
The input images I am using have huge black portions (the value of the pixels are 0 here). I want to define the mask to be exactly the pixels of the original image which are black.
Of course, I can preprocess all these masks in matlab, python, etc, but this will take a long time as I am processing on the order of 1 million images. GMIC has a fast piping interface which does everything in memory, and a mathematical interpreter, so I should be able to do this all with the GMIC command line and save a lot of time.
The answer I need does this entirely in GMIC using it's mathematical interpreter. Thanks in advance!
Something like this probably :
$ gmic input.png --select_color 0,0,0,0 -inpaint[0] [1],.... -keep[0] -o output.png
(where you must set your inpaint parameters according to your needs).

Compare scanned image (label) with original

There is an original high quality label. After it's been printed we scan a sample and want to compare it with original to find errors in printed text for example. Original and scanned images are almost of the same size (but a bit different).
ImageMagic can do it great but not with scanned image (I suppose it compares it bitwise but scanned image contains to much "noise").
Is there an utility that can so such a comparison? Or may be an algorithm (implemented or easy to implement) - like the one that uses Cauchy–Schwarz inequality in signal processing?
Adding sample pics.
Original:-
Scanned:-
Further Thoughts
As I explained in the comments, I think the registration of the original and scanned images is going to be important as your scans are not exactly horizontal nor the same size. To do a crude registration, you could find some points of high-contrast that are hopefully unique in the original image. So, say I wanted one on the top-left (called tl.jpg), one in the top-right (tr.jpg), one in the bottom-left (bl.jpg) and one in the bottom-right (br.jpg). I might choose these:
[]
[]3
I can now find these in the original image and in the scanned image using a sub-image search, for example:
compare -metric RMSE -subimage-search original.jpg tl.jpg a.png b.png
1148.27 (0.0175214) # 168,103
That shows me where the sub-image has been found, and the second (greyish) image shows me a white peak where the image is actually located. It also tells me that the sub image is at coordinates [168,103] in the original image.
compare -metric RMSE -subimage-search scanned.jpg tl.jpg a.png b.png
7343.29 (0.112051) # 173,102
And now I know that same point is at coordinates [173,102] in the scanned image. So I need to transform [173,102] to [168,103].
I then need to do that for the other sub images:
compare -metric RMSE -subimage-search scanned.jpg br.jpg result.png
8058.29 (0.122962) # 577,592
Ok, so we can get 4 points, one near each corner in the original image, and their corresponding locations in the scanned image. Then we need to do an affine transformation - which I may, or may not do in the future. There are notes on how to do it here.
Original Answer
It would help if you were able to supply some sample images to show what sort of problems you are expecting with the labels. However, let's assume you have these:
label.png
unhappy.png
unhappy2.png
I have only put a red border around them so you can see the edges on this white background.
If you use Fred Weinhaus's script similar from his superb website, you can now compute a normalised cross correlation between the original image and the unhappy ones. So, taking the original label and the one with one track of white across it, they come out pretty similar (96%)
./similar label.png unhappy.png
Similarity Metric: 0.960718
If we now try the more unhappy one with two tracks across it, they are less similar (92%):
./similar label.png unhappy2.png
Similarity Metric: 0.921804
Ok, that seems to work. We now need to deal with the shifted and differently sized scan, so I will attempt to trim them to only get the important stuff and blur them to lose any noise and resize to a standardised size for comparison using a little script.
#!/bin/bash
image1=$1
image2=$2
fuzz="10%"
filtration="-median 5x5"
resize="-resize 500x300"
echo DEBUG: Preparing $image1 and $image2...
# Get cropbox from blurred image
cropbox=$(convert "$image1" -fuzz $fuzz $filtration -format %# info:)
# Now crop original unblurred image and resize to standard size
convert "$image1" -crop "$cropbox" $resize +repage im1.png
# Get cropbox from blurred image
cropbox=$(convert "$image2" -fuzz $fuzz $filtration -format %# info:)
# Now crop original unblurred image and resize to standard size
convert "$image2" -crop "$cropbox" $resize +repage im2.png
# Now compare using Fred's script
./similar im1.png im2.png
We can now compare the original label with a new image called unhappy-shifted.png
./prepare label.png unhappy-shifted.png
DEBUG: Preparing label.png and unhappy-shifted.png...
Similarity Metric: 1
And we can see they compare the same despite being shifted. Obviously I cannot see your images, how noisy they are, what sort of background you have, how big they are, what colour they are and so on - so you may need to adjust the preparation where I have just done a median filter. Maybe you need a blur and/or a threshold. Maybe you need to go to greyscale.

Image conversion to PNG vs JPG using ImageMagick

I'm trying to convert some images into either PNG or JPG and trying to find out which format will result in the smaller file size. With most of the cases PNG will give me the best compression but some odd images I get better compression out of JPG. I have two questions:
What characteristics of image will cause it to give better results?
Is there a way to pre-determine which format will give me better
results without converting them first?
This photo gives better compression result using PNG
This photo provides substantially better compression using JPG
I have absolutely no time to develop this line of thought further but the image entropy is probably a good discriminant for selecting JPEG or PNG - see my earlier comment on your question.
If you use ImageMagick, you can calculate the Entropy easily like this:
identify -verbose -features 1 image.jpg | grep -i -A1 entropy
Your top image gives output like this:
identify -verbose -features 1 t.jpg | grep -i -A1 entropy
Sum Entropy:
0.703064, 0.723437, 0.733147, 0.733015, 0.723166
Entropy:
1.01034, 1.12974, 1.14983, 1.15122, 1.11028
Difference Entropy:
0.433414, 0.647495, 0.665738, 0.671079, 0.604431
and your bottom image gives output like this:
identify -verbose -features 1 b.jpg | grep -i -A1 entropy
Sum Entropy:
1.60934, 1.62512, 1.65567, 1.65315, 1.63582
Entropy:
2.19687, 2.33206, 2.44111, 2.43816, 2.35205
Difference Entropy:
0.737134, 0.879926, 0.980157, 0.979763, 0.894245
I suspect images with a higher entropy will compress better as JPEGs and those with a lower entropy will fare better as PNGs - but I have to dash now :-)
There are 5 values for each type of entropy - horizontal, vertical, left diag, right diag and overall. I think the last value is the only one you need consider.
Updated
Ok, I have had a little more time to spend on this now. I do not have a pile of sample images to test my theory on, so I did it a different way. I made a little script to calculate the following for a given input file:
ratio of JPEG size to PNG size
entropy
Here it is:
#!/bin/bash
f="$1"
jsize=$(convert "$f" -strip JPG:- | wc -c)
psize=$(convert "$f" PNG:- | wc -c)
jpratio=$(echo $jsize*100/$psize | bc)
# Make greyscale version for entropy calculation
rm temp*.jpg 2> /dev/null
convert "$f" -colorspace gray temp.jpg
entropy=$(identify -verbose -features 1 temp.jpg | grep -A1 " Entropy:" | tail -n 1 | awk -F, '{print $5}')
echo $jpratio:$entropy
So, for a given image, you would do this:
./go image.jpg
8:3.3 # JPEG is 8x bigger than PNG and entropy is 3.3
Then I took your image and added different amounts of noise to it to increase its entropy, like this
for i in {1..99}; do convert bottom.jpg +noise Gaussian -evaluate add ${i}% xx${i}.jpg;done
that gives me files called xx1.jpg with 1% noise, xx2.jpg with 2% noise and so on, up to xx99.jpg with 99% noise.
Then I ran each of the files through the first script, like this:
for f in xx*.jpg; do ./go $f;done > data.txt
to give me data.txt.
Then I created the following gnuplot command file plot.cmd:
set title 'Plotted with Gnuplot'
set ylabel 'Entropy'
set xlabel 'JPEG size/PNG Size'
set grid
set terminal postscript color landscape dashed enhanced 'Times-Roman'
set output 'file.eps'
plot 'data.txt'
and ran it with
gnuplot plot.cmd
And I got the following plot which shows that as ImageMagick's entropy number increases, the ratio of JPEG size to PNG size improves in favour of JPEG... not very scientific, but something at least. Maybe you could run the script against the type of images you normally use and see what you get.
That depends very much on your use case.
1) JPG is usually not as good for text because the artifacts tend to "smear" or blur the image. For photos, this is usually not a problem; also for high-resolution textual images the problem will be much less pronounced (because the blur radius is smaller relative to image size).
Note that PNG is usually used to losslessly compress images, while JPG is inherently lossy. With a higher compression ratio, JPG files will be much smaller, but the artifacts will be more pronounced. Note also that there are programs that are able to do lossy compression in PNG (which could well beat JPG compression in some cases).
In short: PNGs will work well with computer-generated images, because those tend to be quite regular and thus easy to deflate. JPGs will fare better with photos which tend to have more jitter, which is hard to compress. When you move away from ImageMagick and libpng, there are other possibilities.
2) While it would be possible to train a neural network to decide whether JPG or PNG would compress better, it would probably take longer and be less exact than just trying both and looking at the output. Note also that there are some approximative measurements that can tell you if an image is too blurry (which may help you setting the correct compression level if you want to tune further).
One big difference is that PNG allows for alpha transparencies so that you can see parts of what is behind the image. Jpg will block out a rectangle.

Can ImageMagick find a scaled down match of a large image?

I'm working on a project where we need to match original hi-resolution photos to their scaled down counterparts. For example the original may be 2000px x 2000px, and the scaled down version might be 500px x 500px.
In researching how to do this I've found mention that ImageMagick's compare operation can be used to compare larger and smaller images, but that it behaves as though the smaller image has been cropped from the larger--and as a result it performs a very intensive scan (http://www.imagemagick.org/discourse-server/viewtopic.php?f=2&t=16781#p61937).
Is there an option or flag that I can use to indicate that I only want a match if the smaller image has been scaled (not cropped) from the larger image?
You can temporarily scale the larger image down to the size of the smaller image and then compare the resized version to the thumbnails, as described by Marc Maurice on his blog.
convert bigimage.png -resize 500x500 MIFF:- | \
compare - -metric AE -fuzz '10%' smallimage.png null:
Because the resize algorithm is probably different from the original resize algorithm, this will introduce differences, but if the smaller images are only scaled and not changed otherwise, the similarities should be sufficient to do the matching. You'll have to find a suitable metric and threshold though.
If you don't now the thumbnail sizes or if they differ, you may want to downsize both images to a safe size below the minimum of all thumbnail sizes or you grab the thumbnail sizes with
identify -format "%w,%h" smallimage.png

Resources