I just encountered unexpected behavior in ImageMagick, which I'm hoping someone can explain to me.
Version numbers
$ convert --version
Version: ImageMagick 6.7.7-10 2013-02-25 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2012 ImageMagick Studio LLC
Features: OpenMP
Running on Linux Mint 15 Olivia (based on Ubuntu 13.04 "Raring Ringtail").
Executive summary
Running the same operation with two images that should be quite similar, except that one is a GIF while the other is a JPEG, the resulting output is entirely different. The GIF comes out black, the JPEG comes out white.
To reproduce:
Go to http://karenswhimsy.com/public-domain-images/animal-silhouettes/animal-silhouettes-1.shtm and download the elephant silhouette as elephant.jpg
Download http://www.arthursclipart.org/silhouettes/animals/DUCK1.gif as DUCK1.gif
convert elephant.jpg -negate -alpha shape output-elephant.png
convert DUCK1.gif -negate -alpha shape output-duck.png
Compare the output-elephant.png and output-duck.png images
Can anyone explain why these two output images are different? Why the GIF duck ends up being black after conversion, while the JPEG elephant becomes white after conversion?
Long-winded explanation
The situation is this: I want to take some black-and-white images, turn the background transparent, and turn the foreground different colors. For example, starting with the elephant silhouette at http://karenswhimsy.com/public-domain-images/animal-silhouettes/images/animal-silhouettes-1.jpg (I saved the image as elephant.jpg), I want to produce a .png with a transparent background and an elephant that's green, red, yellow, or whatever color I want.
The command I'm using to do this is:
convert elephant.jpg -negate -alpha shape +level-colors ,green green-elephant.png
This does exactly what I want. First it inverts the image so that the background is black and the elephant is white, because -alpha shape expects an alpha mask where black = fully transparent and white = fully opaque. Then -alpha shape does its magic and produces a white elephant against a transparent background. Then +level-colors ,green kicks in, transforming black-and-white gradients into the two colors specified as parameters to +level-colors; here, the first color is omitted so it would remain black, and the second color is what white turns into. This produces a green elephant with smooth borders -- exactly the result I want -- and I'm quite happy with it.
Next, I tried running the same command against one of the images from http://www.arthursclipart.org/silhouettes/animals.htm (I used DUCK1.gif). The input source is the same -- a black silhouette against a white background -- so I expected the same result, a green duck against a transparent background. But it didn't work. A little research showed me that -alpha shape was behaving differently. Where with the JPEG elephant it was producing a white elephant against a transparent background, with the GIF duck the same command was producing a black duck against a transparent background. In other words, -alpha shape was inverting the result with a GIF image source, but not with a JPEG image source. So to color the animal properly, I needed to rewrite the +level-colors parameters to put "green" before the comma instead of after.
Tweaking my script is no problem at all, but I'd love to understand why this is happening, and so far I'm clueless. Can anyone explain to me why ImageMagick is treating GIFs and JPEGs so differently in the -alpha shape operation?
OK, I think here is the story. With GIF, a background color might be explicitly defined, and I think that is the case with DUCK1.GIF. Not so with JPEG; here the background is, I think, assumed to be "white".
Whatever the case is, converting the DUCK1.GIF to DUCK1.JPG results in a similar image to the elephant. Moreover, you can make sure that the same background color is used with the apropriate Imagemagick option. The two commands below produce similar output:
convert DUCK1.jpg -background Black -negate -alpha shape output-duck.png
convert elephant.jpg -background Black -negate -alpha shape output-elephant.png
Related
Suppose I have some image a.jpg and some other image b.jpg.
The desired output out.jpg should be obtained by copying all the pixels from b.jpg that are not black onto a.jpg, all other pixels shall remain untouched.
I tried using composite but had no success whatsoever.
EDITED TO ADD: A solution here can be quite simple and generic, but going forward, please remember to always include your version of ImageMagick and which OS or platform you're working on. There are some syntax differences that can make that important.
At the very simplest, using ImageMagick v6, you should be able to do something like this...
convert b.jpg -background none -transparent black a.jpg +swap -composite out.jpg
That reads in the B image, changes all the pure black pixels to transparent, then reads in the A image, swaps the images so they're in the right order, then composites the modified B image over the A image and writes the output.
You can add a fuzz value like "-fuzz 5%" ahead of the "-transparent" operation to expand the selection to include near-black pixels, also.
To use with IMv7 change "convert" to "magick".
I am trying to extract a region from an image that is already marked with a certain color. In the picture below
I would like to extract only the pixels which belong to the sidewalk, that is, all pixels that belong to the black blob that is connected to the mid-lower part of the image. There are black dots outside that blob which I am not interested in. So if I could get roughly the region shown below
it would be perfect. Does anyone know of some common algorithms that can do this? Morphology? Region growing using a kind of flooding algorithm?
Thanks,
You can do that quite easily with a flood fill. If I use ImageMagick to demonstrate at the command line because it is installed on most Linux distros and is available for macOS and Windows.
So, bearing in mind that the pixel you identified as your seed is at around 440,520 in the image you supplied that includes the axes, we can floodfill all pixels that match that colour and touch the seed with cyan using:
convert scene.png -fill cyan -draw 'color 440,520 floodfill' result.png
Or, we can make a mask by changing the non-cyan pixels to white and the cyan pixels to black:
convert scene.png -fill cyan -draw 'color 440,520 floodfill' -fill white +opaque cyan -fill black -opaque cyan z.png
There are a thousand other things you can simply do from the command line to take this further... fill small holes in the mask, make a transparency layer from the mask - just ask more questions if you need a hand.
If you want to close the holes in your image, you probably want to use morphological functions. I am away from any computers with ImageMagick for a week so I can only tell you in general terms. Start with the pure black and white (no grey) picture above and try:
convert image.png -morphology open disk:3 result.jpg
Try replacing the word open above with close, erode or dilate. Experiment with disk, disk:3 disk:7 and so on.
ImageMagick is premultiplying transparent pixels. This causes a gray outline to appear during subsequent transformations.
For example:
$ convert -size 1085x558 xc:"rgba(0,0,0,0)" PNG32:temp.png
$ composite -gravity center samples/logo_white.png temp.png PNG32:temp.png
Here are the source and resulting images.
Here is a video showing that the temp.png image has had its transparent pixels turned from white to black.
Is there a way to force ImageMagick to leave fully transparent pixels alone rather than changing them to black?
Just a quickie.
I have a series of images min*.png that I want to animate into a gif.
They are each fully transparent, except for some white dots on the area I want filled in for that frame.
Is there some way to create an animation from these such that the background is black (so that the whtie dots show up?)
I am interested in both:
black background, and paste each successive image on top of the previous ones (so frame i is the black background plus all of the dots up to image i)
each frame consists of just (image i on a black background)
I think for 1. I need to use -dispose none and for 2 I use -dispose background or -dispose previous, but various attempts at actually setting the background to black have failed (I have spent a lot of time reading this imagemagick page but am still learning).
e.g.
convert -background black -dispose background min*.png out.gif
various attempts with -background and -dispose have invariably produced a gif of my min*.png with a transparent background, not a black one. I think I'm close, but not sure.
This may be useful for the black background problem: starting from ImageMagick 6.7.5 you can remove transparency and replace it with a static color; you can read more about this command here
Hope this helps, unfortunately I have an older version of Imagemagick, so i can't try it myself
Example from ImageMagick documentation:
convert moon.png -background tan -alpha remove alpha_remove.png
The color "tan" replaces the transparent areas of the picture
Comment from mathematical.coffee
Using the above answer, I was able to generate the animations I wanted.
1: successive buildup of dots, all on a black background. Turned out to be as simple as creating a black background picture to put at the start of the animation, and using -coalesce:
# where bg.png is a black png of the appropriate size:
convert bg.png min*.png -coalesce out.gif
# in the below the first line creates the black background
# image, same size as my first min00.png image, for me:
convert min00.png -alpha Opaque +level-colors black \
min*.png -coalesce out.gif
2. use the method mentioned above:
convert min000*.png -background black -alpha remove out.gif
In both I was using imagemagick 6.7.something.
I have an image in .jpg format with white background color. I want to remove the white background color to transparent in Imagemagick. I tried many ways but still the white background can not be removed. Can some one help me to solve this.
You cannot have transparent background colors in your JPEGs. The JPEG file format doesn't support transparency.
If you need transparent background, you need to convert the JPEG to
either PNG (high quality, filesize possibly larger than JPEG)
or GIF (in case you can tolerate low quality and a range of maximally 255 colors).
Example command:
convert your.jpg -transparent white your.png
First, you need to convert the image format from .jpg to .png format, because JPEG does not support transparency. Then use this command:
convert image1.png -fuzz 20% -transparent white result.png
The -fuzz option allows the specified percentage deviation from the pure white colour to be converted to transparent as well. This is useful, for example, when your image contains noise or subtle gradients.
I just found a very neat thing!
magicwand 1,1 -t 20 -f image -r outside -m overlay -o 0 image.jpg imgOutput.png
It is a Fred Weinhaus bash script that can be downloaded from here (for non commercial use only). Also there has about 250 scripts!! and this one is amazing! it did exactly the trick, to remove all background while keeping the inner image dots untouched!
At his page, there are several images as examples so you pick what you need to put on the command line!
The initial position 1,1 is a general guesser saying all the contour is background.
Pay attention that the output must be ".png"
This is my solution without magicwand (replace magick by convert for im < 7.0):
magick img.png -fuzz 20% -fill none -draw "alpha 1x1 floodfill" result.png
Get the background automatically and remove it :
bg=$(convert input.png -format "%[pixel:p{0,0}]" info:)
convert input.png -fuzz 20% -transparent "$bg" output.png