Unable to create NDVI image using ImageMagick - imagemagick

I have four separate images - 2-projected.tif, 3-projected.tif, 4-projected.tif and 5-projected.tif. These are four Landsat images. Image 2-projected.tif corresponds to blue channel, image 3-projected.tif - to green channel, image 4-projected.tif - to red channel, and 5-projected.tif - to infrared. Now I want to create NDVI image. To do this, I first create a combined RGB image, using ImageMagic:
$ convert 4-projected.tif 3-projected.tif 2-projected.tif -combine RGB.tif
So far, so good. And then I try to follow a command from this tutorial, which is supposed to create NDVI image. I do it like so:
$ convert 5-projected.tif RGB.tif -channel RGB -fx '(u.r-v.r)/(u.r+v.r+0.001)' -normalize NDVI.tif
But as a result, I get these error messages:
convert: unable to parse expression (u.r-1.0*v.r)' # error/fx.c/FxGetSymbol/183
1.
convert: divide by zero'(u.r-1.0*v.r)/(u.r+v.r+0.001)'' # error/fx.c/FxEvaluat
eSubexpression/2159.
I'm not sure how can I fix it.

The two bands of interest are the red and the NIR and the formula for NDVI is:
NDVI = (NIR-red)/(NIR+red)
You have two options. First off, if you have the red and the NIR in two separate, single channel images, you can do:
convert red.tif NIR.tif -fx '(u.r-v.r)/(u.r+v.r+0.001)' -normalize -compress lzw NDVI.tif
Here, I am using u.r to refer to the first channel of the first image and v.r to refer to the first channel of the second image.
Alternatively, if the red and NIR are the first two channels in an RGB image (i.e. ImageMagick would call them the red and green channels):
convert RGB.tif -fx '(u.r-u.g)/(u.r+u.g+0.001)' -normalize -compress lzw NDVI.tif
Here I am using u.r to refer to the first channel of the first image and u.g to refer to the second channel of the first image.
The -fx method is extremely powerful, but notoriously slow. This method below should give you the same answer, but I have not checked it too thoroughly:
convert 4-projected.tif -write MPC:red +delete \
5-projected.tif -write MPC:NIR +delete \
\( mpc:red mpc:NIR -evaluate-sequence subtract \) \
\( mpc:red mpc:NIR -evaluate-sequence add \) \
-evaluate-sequence divide -normalize -compress lzw NDVI.tif
If you want to colourise the image with false colour, you could generate a Colour Lookup Table (CLUT) and map the grayscale values in the NDVI image to those colours. So, let's say you wanted to map the darkest blacks in your NDVI image to black, the quite dark values to red, the quite bright values to orange and the very brightest values to green, you could make a CLUT like this:
convert xc:black xc:red xc:orange xc:lime +append clut.png
and apply it the greyscale result from above like this:
convert NDVI.tif -normalize clut.png -clut falsecolour.jpg
If you want to make the orange and green tones longer (more prevalent), you can alter their lengths to make them longer in the CLUT:
convert -size 30x1 xc:black -size 40x1 xc:red -size 80x1 xc:orange -size 100x1 xc:lime +append clut.png
Then re-apply the CLUT:
convert NDVI.tif -normalize clut.png -clut result.jpg

Related

imagemagick mean image sequence and ignore alpha channel

I want to get the mean of a sequence of images by using Imagemagick. Therefore I use the following command:
convert *.png -evaluate-sequence mean MEAN.png
Each of my images does contain an alpha channel. What I want is: Combine all the images by ignoring the alpha channel.
When I combine the images, the alpha channel is considered in the "mean" method and my final image has transparency. That isn't what I want.
Result:
I tried to add the parameter -alpha off, but then Imagemagick converts the alpha channel to black.
convert *.png -alpha off -evaluate-sequence mean MEAN.png
Result:
Photoshop does it right. I load all images in a stack and create a smart object. When I use the "mean" method in Photoshop, the alpha channel is not considdered in the final result.
Result that I want with Imagemagick:
Does someone have an idea how to do that with Imagemagick?
What you need to do is to use the alpha channels as weighting images for each image. The total fraction of white values at each pixel from all the alpha channels would be the weight to use for the average. So something like this should do what you want or at least be close.
First, cd to your directory of images. Then run the following.
convert *.png -evaluate-sequence mean \
\( -clone 0 -alpha off \) \
\( -clone 0 -alpha extract \) \
-delete 0 +swap -compose divide -composite result.png
This will work if there is some image texture at each pixel coming from al least one image. That is at a given pixel all images are not totally black (transparent).
compare -metric rmse result.png mean_photoshop.png null:
125.167 (0.00190993)
So this shows that there is about 0.2% difference between my result and what you got from photoshop
Maybe this way of working will help you get there - or at least explain the problem:
convert xc:"rgba(255,0,0,1)" xc:"rgba(0,0,0,1)" xc:"rgba(0,0,0,0)" -depth 8 -evaluate-sequence mean txt:
Output
# ImageMagick pixel enumeration: 1,1,65535,srgba
0,0: (21845,0,0,43690) #550000AA srgba(85,0,0,0.666667)
Using IM 6.8.9.4 Q16 or IM 7.0.5.5 Q16 Mac OSX Sierra, this seems to work fine for me:
Make transparent image
convert logo: -transparent white logot.png
Get mean
convert logot.png logot.png logot.png -alpha off -evaluate-sequence mean result.png
magick logot.png logot.png logot.png -alpha off -evaluate-sequence mean result.png
This also seems to work:
convert logot.png logot.png logot.png -channel rgb -evaluate-sequence mean -alpha off result.png
So perhaps you need to upgrade your ImageMagick (and/or libpng?)
Can you post a zip file of some of your input images, so we can test with your images?
One problem that I see is that the PNG images that you provided have black under the transparent areas and not image texture. So when you disable alpha as in my commands above, you see black and the black gets averaged into the final result. Did you use these same PNG images in Photoshop or did you have Photoshop PSD images or some other images that you used and then exported to PNG, which may have put black under the transparent areas. Have you tried using the same PNG images in Photoshop to do the average?
In fact, you have 8-bit color (palette) images, which have one color (black) assigned to be the transparent color.

Imagemagick: How does RGB work with this software?

I want to change all colors #FF00FF in an image to #0000FF while keeping shades, ideally. So I figured I should at least get it to change colors to begin with to see if the software is even capable of doing things like that.
However its only changing a bit of the color to white and only with a high Fuzz. So it's obvious that RGB in Imagemagick doesn't work like it does anywhere else and I can't find anything to explain how it works.
It seems to replace some off-white with pure white.
Using PHP I do:
exec("convert ".$dir."".$file." -channel RGB -fuzz 30% -opaque rgb\(255,0,255\) -fill rgb\(0,0,255\) ".$dir."".$file);
I am not 100% certain what you mean as you haven't provided a sample of what other software does, but I'll have a try and see if we can get there.
So, if we make a starting image, including your presumed shades of magenta on the left and some test colours on the right:
convert -size 256x256 gradient:black-magenta -size 50x256 \
xc:black xc:white xc:red xc:lime xc:blue +append start.png
And, you want to change magenta shades into blue. I would call that a hue modulation, so I would want to find out the hue angle between blue and magenta, so I would create a 2x1 image with one magenta and one blue pixel and get their HSI values:
convert xc:magenta xc:blue -append -colorspace hsi txt:
Output
# ImageMagick pixel enumeration: 1,2,65535,hsi
0,0: (54612.5,65535,43690) #D555FFFFAAAA hsi(300,100%,66.6667%)
0,1: (43690,65535,21845) #AAAAFFFF5555 hsi(240,100%,33.3333%)
And I can see their hues are 60 degrees apart (300-240). So I would use the -modulate operator, which takes a Brightness, Saturation and Hue, leave the first two unchanged at 100%, and modify the Hue by 60 degrees:
convert start.png -modulate 100,100,60 result.png
Or maybe that is not what you mean? Maybe you only mean to affect specific colour. If so, it gets harder... but not that hard :-)
First, extract the Hue, Saturation and Brightness layers to separate files:
convert start.png -colorspace HSL -separate -colorspace gray HSL-%d.png
That will give us the Hue as a single channel greyscale image in HSL-0.png, the Saturation in HSL-1.png and the Lightness in HSL-2.png.
Now we want to make a new LUT (Lookup Table) for the Hue channel, so we make a 360 pixel long LUT that maps 1:1, i.e. everything maps to normal.
convert -size 1x360 gradient: -rotate 90 greyscale.png
Then we want to dink with the lookups around magenta (300) and make them blue (240). So we want to subtract 60 degrees (which is 0.16 if you scale 0-360 degrees onto the range 0-1) from all pixels in the range 280-320 so there is some tolerance:
convert -size 1x360 gradient: -rotate 90 -colorspace gray -fx "i<280||i>320?u:u-0.16" hueCLUT.png
Now apply that LUT to the Hue of the original image and rebuild it...
convert HSL-0.png -colorspace gray hueCLUT.png -clut HSL-1.png HSL-2.png -set colorspace HSL -combine -colorspace RGB result.png
So, as a simpler script, that might become:
#!/bin/bash
# Make a hue CLUT, transforming magenta hues to blue
convert -size 1x360 gradient: -rotate 90 -colorspace gray -fx "i<295||i>305?u:u-0.16" -resize 256x1! hueclut.png
# Apply to the hue channel
convert start.png -colorspace HSL -write MPR:HSL \
-channel R -separate hueclut.png -clut \
\( MPR:HSL -channel G -separate \) \
\( MPR:HSL -channel B -separate \) \
-set colorspace HSL -combine -colorspace RGB result.png

How to select all grayscale colors?

In ImageMagick convert, I can select a specific color with e.g. -opaque blue. How can I select all grayscale colors (e.g. #000000, #707070, #ffffff)?
Not sure what you are trying to do, but this may help. The greyscale pixels will have a saturation of zero, so that is probably the easiest way to identify them.
First, make a funky sample image:
convert -size 400x100 gradient:black-white -bordercolor red -border 80 image.png
Now make all grey areas (those with very low saturation) transparent:
convert image.png -alpha on -channel A -fx "saturation<0.01?0:1" result.png
Note
Note that the -fx operator is extremely powerful but notoriously slow because it is actually interpolated for each and every pixel. If your images are large, the following technique may be more appropriate.
Basically, I clone the image and convert the whole thing to HSL colorspace and separate the channels. Then I discard the Hue and Lightness channels so I am left with just the Saturation. I then threshold that and copy that back to the original image as the alpha channel. On a 2000x2000 pixel image, this method will run in under a second whereas the -fx method will require 5-6 seconds.
convert image.png \( +clone -colorspace hsl -separate -delete 0,2 -threshold 1% \) -compose copy-opacity -composite result.png

Removing background using imagemagick, on a white product

Here's the original image I'm trying to remove background from:
I am trying to use imagemagick to remove the background from an image. When the image has a white product, my script doesn't work well. It removes the white from inside the product also. In brief I'm trying to do the following
create a mask image (replace background pixels with white with fuzz and threshold)
apply the mask over the original image to generate the output
If I use a fuzz factor of 0, like shown below, i get the background removed, but it creates a nasty halo around it. What can be done here?
I would take advantage of HSL colorspace, and create an alpha mask from the lightness channel.
convert tshirt.jpg \( \
+clone -colorspace HSL -separate \
-delete 0,1 -fx 'u>0.975?0:1' \) \
-compose CopyOpacity -composite \
out.png
I would go for a simple threshold to pick out the white and then some sort of filtration to remove the noise/ragged edges. So, for example
convert shirt.jpg -threshold 99.99% -negate result.jpg
which gives this:
Then apply some median filtering to smooth it:
convert shirt.jpg -threshold 99.99% -median 5 -negate result.jpg
or maybe a bigger filter:
convert shirt.jpg -threshold 99.99% -median 11 -negate result.jpg
which gives this
Alternatively, you may get on better with an erosion and a dilation...
convert shirt.jpg -threshold 99.99% -negate \
-morphology erode diamond:3 \
-morphology dilate diamond:3 result.jpg
You may like to use Anthony Thyssen's flicker_compare to flicker between the input and result image to see what you have got, see here.
./flickercompare -o flick.gif shirt.jpg result.jpg

ImageMagick: write pixels in BGR order

I want to convert (a lot of) JPEG images to the Sun Raster format (see here for instance), because my program can only use this format.
The issue is that my program only allows images of type Old
or Standard i.e. with pixel written in the BGR order. (I should change that but don't have the time).
By default, ImageMagick generates files in the RGB format.
I have found a trick to swap the color planes, but as the file is style written as RGB I get fancy colors.
This is probably a simple option to pass to ImageMagick, but I didn't find it.
Do you have an idea?
Another way to swap colors is to use the -color-matrix option. For example, to convert RGB to BGR:
convert input.png -color-matrix '0 0 1
0 1 0
1 0 0' output.png
Reference: http://www.imagemagick.org/Usage/color_mods/
In Imagemagick 6, you can do that easily with -swap command. Lets swap the red and blue channels in the logo image.
convert logo.png -separate +channel \
-swap 0,2 -combine -colorspace sRGB logo_bgr.png
For very old versions of Imagemagick use RGB rather than sRGB.
In Imagemagick 7, use magick rather than convert.
I have an idea how to swap Red and Blue colors in RGB image:
convert ( image_RGB.bmp -channel B -separate ) \
( image_RGB.bmp -channel G -separate ) \
( image_RGB.bmp -channel R -separate ) -channel RGB -combine image_BGR.bmp
You just cut channels form original image and write them in reverse order.
I would have thought that in Imagemagick
convert image.jpg image.sun
or
convert image.jpg SUN:image.sun
would convert to BGR format if that is the standard for the sun raster format. See imagemagick.org/script/formats.php.
Have you tried that?
If that does not work, then use my -swap command but write to .sun file format rather than png, which I used above. That is
convert logo.png -separate +channel -swap 0,2 -combine -colorspace sRGB logo_bgr.sun

Resources