How do I use Imagemagick to replace a gray gradient background - background

I have several photos for which I need to remove the background (replace it with #ffffff)
I have tried several ways, but none that gives a good result.
eg. convert "$i" -fuzz 10% -fill '#ffffff' -opaque white $i-new.png
This makes the result whiter and creates a rather harsh line between the white color and the shadow.
More sample images

I think you want a flood fill using the colour from the top-left corner:
convert cauli.jpg -fuzz 10% -fill red -draw 'color 0,0 floodfill' result.png
Probably use a white fill though :-)

Related

ImageMagick convert to png8 (paletted PNG with binary alpha) using background color for partially transparent pixels

I have a 32-bit PNG image like this:
Displayed here with a checkerboard background for visibility, but instead of the checkerboard the image is actually transparent (for your reference: the original 32-bit image). As you can see, around the edges and towards the right, the red pixels are gradually fading out from opaque to transparent.
If I would display this 32-bit image on top of a blue background, the pixels around the edges and towards the right would gradually fade from red to blue.
Now I need to convert this image to an 8-bit PNG with binary alpha. I want the fully transparent area to remain fully transparent, but the partially transparent pixels to gradually blend with the intended background/matte color, in this example blue.
So something like this:
However, I can't seem to figure out how to do this with ImageMagick (using ImageMagick 7.0.8-26 on Mac). I tried as follows:
magick original.png -background 'rgba(0,0,255,0)' png8:output.png
But this results in:
It seems to ignore the blue background color altogether, and just convert the transparency to binary (probably taking fully opaque for ≥128 or fully transparent for <128).
Also if I specifiy rgb(0,0,255) as background color (i.e. no alpha) the result is the same.
Does ImageMagick have some smart 'background blending for partially transparent pixels' option that I don't know about?
Otherwise, I'm guessing I should somehow extract a binary bitmap where the original image is fully transparent (i.e. has alpha value or opacity 0), then flatten the image on a background of my choosing, and then re-apply the boolean alpha bitmap again. But not quite sure how to do this.
I can think of two techniques, but I'm sure folks have better answers.
First Option
Composite the blue background, and then copy the alpha channel from the original source.
magick original.png \( \
+clone \( +clone -fill BLUE -draw 'color 0,0 reset' \) \
-compose DstOver -composite \) \
-swap 0,1 -compose CopyAlpha -composite PNG8:output.png
Second Option
Replace transparent with blue color, but then call -transparent without any -fuzz operation.
magick original.png \( \
+clone -fill BLUE -draw 'color 0,0 reset' \) \
-compose DstOver -composite -transparent BLUE \
PNG8:output.png
Again, I'm sure the above examples can be improved on. YMMV
One way to do that with ImageMagick is to extract the alpha channel, colorize essentially everything except the totally transparent pixels, and composite the original image over the result.
convert input.png \( +clone -alpha extract -transparent black -fill blue \
-channel RGB -colorize 100 +channel \) +insert -composite output.png
That reads the input image, clones it inside the parentheses, and extracts the alpha channel to a black and white mask.
The pure black is made transparent with "-transparent black".
The white, everything except pure black, is changed to blue with "-colorize 100". Setting "-channel RGB" applies that fill color to only the white part of the mask and ignores the transparent.
Then after the parentheses the "+insert" moves that blue-on-transparent image to the back, and "-composite" places the input image over that blue and transparent one.
If you're using ImageMagick version 7, use "magick" instead of "convert". If you're running on Windows, change the end of line backslash "\" to a caret "^", and remove the backslashes that escape the parentheses "\(...\)" to simply "(...)".
Here is one more way in ImageMagick.
convert img.png \
\( -clone 0 -alpha extract -channel rgba \
\( xc:none xc:blue +append \) -clut +channel \
-channel alpha -threshold 0 +channel \) \
+swap -compose over -composite PNG8:result.png
OR FOR Imagemgick 7
magick img.png \
\( -clone 0 -alpha extract -alpha copy -channel rgba \
\( xc:none xc:blue +append \) -clut \
-channel alpha -threshold 0 +channel \) \
+swap -compose over -composite PNG8:result.png
Read the image.
Clone it, extract the alpha channel, create transparent blue color map and apply to the alpha channel, then threshold the alpha channel.
Swap the two images and composite the original over the processed alpha channel.
Save the output.
Thanks to the posted suggestions, however the problem is I don't want to mask any particular color (like black or blue) as transparent. That exact same color may also appear in the non-transparent parts.
After some tweaking, I got the exact result I need:
magick original.png -write MPR:foo -background 'rgb(0,0,255)' -flatten \( MPR:foo -alpha extract -threshold 0 \) -compose copy_opacity -composite png8:result.png
Explanation:
I start with original.png and use -write MPR:foo to save a temporary copy in memory called 'foo'
Then I flatten the image on a blue background, giving the correct color values everywhere (including blended with the background for partially transparent pixels) but losing the opacity data.
Then I side-process a 2nd layer, opening the same original input image (restoring from memory using MPR:foo) and extract its alpha channel which now becomes a grayscale image, containing the original alpha data.
I apply -threshold 0 on that layer, which changes all channel values that were >0 to the maximum (i.e. alpha 255 or fully opaque) and ≤0 remains 0 (fully transparent). So this is now my 'binary alpha' channel.
Then finally I merge these two layers by using a copy_opacity composite which just copies the 2nd layer (the binary alpha) and writes that as the resulting layer's alpha data.
Saving the end result gives me the exact image I need.

Imagemagick maximum colors and scaling

I am trying to convert 80x80 images to 56x56 images in greyscale in 2bpp.
The 80x80 images are colored and probably have up to 16 colors in them. They also have a transparent background.
I need them greyscaled with 4 colors in them, with white being the lightest and black being the darkest.
Each image has multiple colors in it, but each color has a palette of 3 colors, a dark one, medium one, and light one.
I need to convert all the dark ones to a dark grey, the medium ones to a light grey, and the light ones to white, while maintaining the black that is already in the image.
I can successfully convert the image to greyscale, trim the canvas, and fill the background with this command
convert input.png +dither -flatten -trim -set colorspace Gray -
separate -average output.png
Now I need to limit the colors, but it's not converting the right ones. The light colors are getting converted to light grey instead of white. When I change -level option, it only works with some of the images.
-auto-levels does not do what I want either.
Is there anyway to set the colors in the midrange to automatically level themselves to fit my requirements? I'm sorry if I'm not explaining this enough.
This is the code I've been tampering with but it only works on a few images. Messing with the gamma option can make it work for more images but breaks the original working images.
convert "$f" +dither -flatten -trim -set colorspace Gray -separate -
average -gamma 1.37 -level 25%,75% -colors 4 -adaptive-resize 56x56\>
-background white -gravity center -extent 56x56 -remap nido.png
"${f%.png}".png2
I can't provide an expected image, but an image something similar to what is expected. Here is original image https://img.pokemondb.net/sprites/black-white/normal/charizard.png and here is desired output format image https://img.pokemondb.net/sprites/red-blue/normal/charizard.png
Here is what I've got so far
https://www.pokecommunity.com/showthread.php?p=9692599#post9692599
convert "$f" +dither -background white -flatten -trim -adaptive-resize
56x56\> "${f%.png}".png2
convert "${f%.png}".png2 +dither -colorspace gray -separate -average
"${f%.png}".png2
convert "${f%.png}".png2 +dither -gamma 3.0 -black-threshold 70%
"${f%.png}".png2
convert "${f%.png}".png2 +dither -gamma 0.45 -white-threshold 90%
"${f%.png}".png2
convert "${f%.png}".png2 +dither -remap nidoking.png -background white
-gravity center -extent 56x56 "${f%.png}".png2
Btw, ^ is in a for loop, hence the variables. Changing the gamme and black/white theshold values are getting me closer, but it is extremely tedious and when I get one image to correctly convert another breaks. nidoking.png is my remap file. remap works perfect, it's just before the remap the colors are being separated or filtered properly.
Solved, thanks to Mark Setchell
This is what I ended up doing
#!/bin/bash
rm x*
rm colors*
cd images
rm *.png2
rm *.txt
for f in *.png
do
#Fitting canvas to image, setting background color, and removing transparency
convert "$f" +dither -background white -flatten -trim "${f%.png}".png2
#Converting image to greyscale
convert "${f%.png}".png2 +dither -colorspace gray -separate -average "${f%.png}".png2
#Resizing without blurring/adding pixels
convert "${f%.png}.png2" +dither -interpolate Nearest -interpolative-resize 56x56\> "${f%.png}".png2
#Grabbing every color used in image and putting it in a text file
convert "${f%.png}.png2" txt: | sed '1d' | cut -f 4 -d " " | sort -u > "${f%.png}".txt
done
#Putting all colors into one file
cat *.txt >> ../colors
cd ../
#One last clean up of file/sorting
cat colors | tr " " "\n" | sort -u > colors.txt
rm colors
#Splitting the hex codes into four files for each desired color
file=colors.txt
lines=$(wc -l <${file})
((lpp = (lines + 4 - 1) / 4))
split --lines=${lpp} ${file}
#Going back to images directory
cd images
for f in *.png
do
#Each while loop reads everyone line of the specified file and puts it in variable $i, then I use $i to convert to one of the desired 4 colors.
cat ../xaa | while read i
do
convert "${f%.png}".png2 +dither -fuzz 0% -fill "#000000" -opaque "${i}" "${f%.png}".png2
done
cat ../xab | while read i
do
convert "${f%.png}".png2 +dither -fuzz 0% -fill "#555555" -opaque "${i}" "${f%.png}".png2
done
cat ../xac | while read i
do
convert "${f%.png}".png2 +dither -fuzz 0% -fill "#AAAAAA" -opaque "${i}" "${f%.png}".png2
done
cat ../xad | while read i
do
convert "${f%.png}".png2 +dither -fuzz 0% -fill "#FFFFFF" -opaque "${i}" "${f%.png}".png2
done
mv "${f%.png}".png2 ../finished/"${f}"
done
This script turned this
Into this
Basically, the idea would be to do a reduction to 4 colours in the RGB colourspace (rather than in greyscale colourspace) to get the best four colours. Then get the lightness of each of those and map the darkest one to black, the next lighter to dark grey, the next lighter to light grey and the lightest to white.
Here it is mapped to the 4 best colours in RGB colourspace:
The code, without much error checking or corner-case handling, looks like this:
#!/bin/bash -x
# Do a colour reduction to get best 4 colours in RGB colourspace rather than in grey colourspace
magick pokething.png -alpha off +dither -colors 5 -unique-colors unique.png
# Get hex colours into array "hexcolours[]"
hexcolours=( $(convert unique.png txt: | awk 'NR>1{print $3}') )
echo DEBUG: hexcolours=${hexcolours[#]}
# Get lightness of each colour into array "lightness[]", i.e. H of HSL
# Note ggrep is just GNU grep
lightness=( $(convert unique.png -colorspace HSL -separate -delete 0,1 txt: | ggrep -Po "\d+(?=\)$)") )
echo DEBUG: lightness=${lightness[#]}
# Sort the colours by their lightness
fourshades=( $(for ((i=0;i<4;i++)) ;do
echo ${lightness[i]} ${hexcolours[i]}
done | sort -n | awk '{print $2}') )
echo DEBUG: fourshades=${fourshades[#]}
# Now change those colours in original image
magick pokething.png -alpha off +dither -colors 5 -fuzz 10% \
-fill black -opaque "${fourshades[0]}" \
-fill gray25 -opaque "${fourshades[1]}" \
-fill gray75 -opaque "${fourshades[2]}" \
-fill white -opaque "${fourshades[3]}" \
result.png
The output is as follows:
DEBUG: hexcolours=#000000 #094152 #A95244 #EF9E3C
DEBUG: lightness=0 46 119 150
DEBUG: fourshades=#000000 #094152 #A95244 #EF9E3C
That results in this being executed:
magick pokething.png -alpha off +dither -colors 5 -fuzz 10% \
-fill black -opaque '#000000' \
-fill gray25 -opaque '#094152' \
-fill gray75 -opaque '#A95244' \
-fill white -opaque '#EF9E3C' result.png
So, basically I am replacing #094152 with dark grey because 46 is the second darkest colour present. Then I am replacing #A95244 with light grey because 119 is the next lighter colour, then replacing #EF9E3C with white because that is the lightest colour.
You might be able to get your desired result by using pretty much only "-remap". This example will make a four color map from white, 65% gray, 35% gray, and black, and write that map to temporary memory. Next it reads your input image, sets the background to white, and flattens the image. Then it turns off dithering, remaps the input image to the map you created, and returns the result.
convert xc:white xc:gray65 xc:gray35 xc:black -append -write mpr:map +delete \
input.png -background white -flatten +dither -remap mpr:map output.png
You should do any resizing, cropping, etc., before the remap, of course. You probably don't need to turn it to grayscale first because the map has no color.

convert all colors of a pdf file to blue using imagemagick

How can I convert all colors no matter what to blue color using imagemagick? or any other simple and fast solution?
I am out of black ink and want to convert a pdf that is in grayscale to only blue color and then print it.
My printer has limited options.
Not certain of what you mean/expect, but try this:
convert input.pdf +level-colors blue, blueResult.pdf
It should convert all shades of black to their equivalent shades of blue. So, if you start with this:
you will get this as a result:
If your original document is colour, you may need to desaturate first:
convert input.pdf -modulate 100,0 +level-colors blue, blueResult.pdf
convert image.pdf -fill blue -colorize 100 image.pdf
I am not sure this is what you want. The result will be totally blue everywhere. Do you want something else? You say all colors, but does that include white and black and shades of gray?
If you want every color but white to be blue, then
convert image.pdf -fill blue +opaque white image.pdf
However, IM is not a good tool to use to go from vector to vector format. IM will rasterize the vector image and then put a vector shell about it.

Imagemagick unwanted black background on rotated transparent images

I have a website that generates polaroid-like images stacked on eachother at different angles.
Up until now everything worked well, but now i've started getting some black background around my transparent .png's.
You can see the problem here. The images in the last album are all messed up.
I'm using imagemagick (6.5.4.7-3.fc12).
my commands look something like this:
the first one is contained whitin a foreach and generates a bunch of pngs rotated at different angles
convert '{$sf}' -auto-orient -thumbnail 120x120 -gravity center -bordercolor snow -background black -polaroid {$angle} {$i}.png
the second command takes the previously generated images and stacks them toghater
convert '*.png' -background transparent -alpha on -gravity center -layers merge -extent 190x190 +repage -thumbnail 115x115 -gravity center -extent 120x120 'result.png'
As far as I got with the debuging, the black background is already present in the images generated with the first command and they only appear when I rotate the images. If I only use -polaroid 0 instead of the +polaroid, then the resulting images are ok.
My guess is that the problem is not with the code itself, but rather ImageMagick or something else got upgraded on my server and that started this whole mess.
I also tried all kinds of combinations with setting -alpha and everything else i could find in the imagemagick docs that is even just slightly related to transparency, but nothing seems to work.
After all sorts of testing I finally got to the conclusion that the problem was not with my convert commands.
The solution to my problem was to reinstall/update ImageMagick.
//It romove the unwanted black/white background and make it transparent backgraund.
ImageInfo info1 = new ImageInfo(
"/opt/apache-tomcat-6.0.18/webapps/newcpclient_branch/upload/sample/ATT00003.jpg");
MagickImage blankImage = new MagickImage(info1);
**blankImage.setBackgroundColor(PixelPacket.queryColorDatabase("#FFFF8800"));**
blankImage = blankImage.rotateImage(250.0);
blankImage.setFileName("/opt/apache-tomcat-6.0.18/webapps/newcpclient_branch/upload/sample/transparent.png");
blankImage.writeImage(info1);
You have -background set to 'black' in your first line. That means you don't get transparency. Does it work if you set it to 'none'?
Edit:
import os
import random as ra
for i in range(10):
image = 'convert C:/image.png -auto-orient -thumbnail 120x120 -gravity center -bordercolor snow -background none -polaroid '+str(ra.uniform(0,360))+' C:/test/image_polaroid_'+str(i)+'.png'
os.system(image)
image = 'convert -size 500x500 xc:transparent C:/test/result.png'
os.system(image)
for i in range(10):
image = 'composite -gravity center C:/test/image_polaroid_'+str(i)+'.png C:/test/result.png C:/test/result.png'
os.system(image)
Edit 2:
import os
import random as ra
for i in range(10):
image = 'convert C:/image.png -background none -polaroid 0 C:/test/image_polaroid_'+str(i)+'.png'
os.system(image)
image = 'mogrify -rotate '+str(ra.randint(0,360))+' -background none C:/test/image_polaroid_'+str(i)+'.png'
os.system(image)

How to remove an image's solid background using ImageMagick convert command

I'm finding a way to make the solid background of an image become as transparent as possible (with little halo). ImageMagick is a powerful tool however it's not easy to use. I tried one command
convert image.jpg \
-bordercolor white \
-border 1x1 \
-alpha set \
-channel RGBA \
-fuzz 20% \
-fill none \
-floodfill +0+0 white \
-shave 1x1 \
image.png
, which works kinda well. However, this requires me to know the exact color of the solid background while I need something to work with any background color. Anyone could help me with this? Th
As a heuristic, you could ask for the color of the pixels at the corners (http://studio.imagemagick.org/pipermail/magick-users/2005-March/015034.html) and then see which color is used the most. Not exactly fool-proof, though...