ImageMagick: write pixels in BGR order - imagemagick

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

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.

Unable to create NDVI image using 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

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

Changing exposure of jpeg

Given a jpeg, what is the formula to change the exposure of that jpeg by +/-1 stop or as known as 1 EV? I want to simulate this exposure change. Is there a formula/ method to do so?
I can demonstrate that using ImageMagick, which is included in most Linux distros and available for OSX and Windows from here.
First, at the Terminal command line create an image:
convert -size 512x512 gradient:black-yellow gradient.png
Now, the way to effect +1 stop exposure increase is to composite the image with itself using the Screen blending mode - it is available in Photoshop and ImageMagick and is described here.
So, the formula to composite image A with image B is:
1-stop brighter image = 1-(1-A)(1-B)
but as we are compositing the image with itself, A and B are the same, so we effectively have
1-(1-A)(1-A)
ImageMagick refers to the pixels of an image using p rather than A, so we can do a 1-stop increase like this:
convert gradient.png -colorspace RGB -fx "(1-(1-p)(1-p))" result.png
Note that the Wikipedia article, and ImageMagick's -fx both assume your pixel intensities vary between 0 and 1.0. If you are using 8-bit images, you should calculate with 255 in place of 1, namely
+1 stop brighter image = 255-(255-A)(255-A)
or if using 16-bit values
+1 stop brighter image = 65535-(65535-A)(65535-A)
The above fx-based method is however, very slow because the -fx is interpreted rather than compiled, so a faster way to do it is:
convert gradient.png gradient.png -colorspace RGB -compose screen -composite screen.png
Just for fun, another way of looking at that is that we take the inverse of A, that is 1-A, and square it, and then take the inverse, so it can be done like this:
convert gradient.png -colorspace RGB -negate -evaluate pow 2 -negate result.png
The equivalent of -1 stop exposure decrease is to composite the image with itself using the Multiply blend mode, the formula being
1-stop darker image = A x B
which you would do faster with
convert gradient.png gradient.png -colorspace RGB -compose multiply -composite result.png
or even faster, by using memory-to-memory cloning rather than reading from disk twice, with
convert gradient.png -colorspace RGB +clone -compose multiply -composite result.png
but could do equally with
convert gradient.png -colorspace RGB -evaluate pow 2 result.png

Convert RGB to Grayscale in ImageMagick command-line

How do I convert a RGB image (3 channels) to a grayscale one, using the (r+g+b)/3 method?
I look through an examples page: http://www.imagemagick.org/Usage/color_mods/#grayscale
but the desired method:
convert test.png -fx '(r+g+b)/3' gray_fx_average.png
gave me a wrong result - the resulted image has still 3 channels.
You can check this by running a command: identify -format "%[colorspace] <== %f\n" *.png.
convert <img_in> -set colorspace Gray -separate -average <img_out> gives the best result for any image for me.
Using the (r+g+b)/3 method will apply the effects of grayscale, but the image will remain in sRGB (which is the expected behavior for this method). You'll need to specify the desired colorspace along with the -fx command.
convert test.png -fx '(r+g+b)/3' -colorspace Gray gray_fx_average.png
Verify with identify -format "%[colorspace] <== %f\n" gray_fx_average.png
Gray <== gray_fx_average.png
To batch convert images in Fish shell:
for file in *.jpg; convert -colorspace Gray $file $file; end;
A few ways to that in Imagemagick command line are:
convert test.png -grayscale average gray_average.png
or
convert test.png -colorspace OHTA -channel r -separate +channel gray_average.png
or
convert test.png -intensity average -colorspace gray gray_average.png
or
convert test.png -colorspace HSI -channel blue -separate +channel gray_average.png
See
https://imagemagick.org/script/command-line-options.php#grayscale
https://imagemagick.org/script/command-line-options.php#intensity
https://imagemagick.org/script/command-line-options.php#colorspace
Seems like you are taking the red channel to do that, on
convert test.png -colorspace OHTA -channel r -separate +channel gray_average.png
i prefer the green channel (i heard that way works on tv sice ancient days, maybe the best)
I use convert mostly to convert colour pictures of documents into grey-scale pdf documents in order to perform OCR. My best results are using Rec709Luminance. So I recommend
convert colourpicture.png -grayscale Rec709Luminance greyscalepicture.png
Short command, nice outputs.
I use this with good result for gray-scale images (I convert from PNG):
ls ./*.png | xargs -L1 -I {} convert {} -strip -interlace JPEG -sampling-factor 4:2:0 -gaussian-blur 0.05 -colorspace Gray -quality 20 {}.jpg
I use this for scanned B&W pages get them to gray-scale images (the extra arguments cleans shadows from previous pages):
ls ./*.png | xargs -L1 -I {} convert {} -strip -interlace JPEG -sampling-factor 4:2:0 -gaussian-blur 0.05 -colorspace Gray -quality 20 -density 300 -fill white -fuzz 40% +opaque "#000000" -density 300 {}.jpg
I had an issue to convert an sRGB colorspace to a Gray colorspace. I had to delete Alpha channel manually before a conversion. In other case, the image will stay sRGB.
convert image_original.tga -alpha off -set colorspace Gray image_converted.tga

Resources