Hello! I'd like to remove all occurrences of stray pixels from a transparent image.
Below is an example image, enlarged for convenience:
Following is that same image but how I desire it to look after processing:
The best way I can think to describe what I'm looking to achieve is that every pixel whose surrounding pixels are fully transparent should be removed. Think of the selector as a 3x3 grid, with the middle of the grid being the pixel operated on.
I took a look at Morphology in the IM manual, but it doesn't appear to provide a fine enough method for this.
Is this possible with ImageMagick? Is there any other command line software that could achieve this if not?
In Imagemagick, you can use -connected-components to remove those isolated blocks of pixels. They seem to be 5x5 pixels on a side. So we threshold the area to keep at 26. We remove those blocks in the alpha channel and then replace that in the image. (Note we need to use 8-connected vs 4-connected region detection to preserve your other regions).
Since you say the your image was enlarged, so I presume your isolated regions were 1x1 pixels. So change the area-threshold to 2, to remove single pixel regions.
Input:
Unix Syntax:
convert img.png \
\( -clone 0 -alpha extract -type bilevel \
-define connected-components:mean-color=true \
-define connected-components:area-threshold=26 \
-connected-components 8 \) \
-alpha off -compose copy_opacity -composite \
result.png
Windows Syntax:
convert img.png ^
( -clone 0 -alpha extract -type bilevel ^
-define connected-components:mean-color=true ^
-define connected-components:area-threshold=26 ^
-connected-components 8 ) ^
-alpha off -compose copy_opacity -composite ^
result.png
See -connected-components
ADDITION:
If you only want to remove the small isolated color pixels and not any transparent pixels inside the color ones, then there is no trivial way to do that. That is an enhancement I would like to have. However, it can be done.
Here is your image modified so that the top left red block has a single transparent center pixel. I added a red line to its right to be sure it was larger than 25 pixels when the center was turned transparent and so that you could see which pixel has the transparent center. You will have to download and zoom in on this image to see the missing pixel.
4x Zoom:
The method is to find all white regions in the alpha channel and then make a list of all regions that are less than 26 pixels. Then reprocess the image to remove those regions by ID.
Get ID List
id_list=""
OLDIFS=$IFS
IFS=$'\n'
arr=(`convert img2.png -alpha extract -type bilevel \
-define connected-components:mean-color=true \
-define connected-components:verbose=true \
-connected-components 8 null: | grep "gray(255)" | sed 's/^[ ]*//'`)
echo "${arr[*]}"
num=${#arr[*]}
IFS=$OLDIFS
for ((i=0; i<num; i++)); do
id=`echo "${arr[$i]}" | cut -d' ' -f1 | sed 's/[:]*$//'`
count=`echo "${arr[$i]}" | cut -d' ' -f4`
if [ $count -lt 26 ]; then
id_list="$id_list $id"
fi
done
echo "$id_list"
Here is what is printed
12: 5x5+120+70 122.0,72.0 25 gray(255)
14: 5x5+30+85 32.0,87.0 25 gray(255)
15: 5x5+110+85 112.0,87.0 25 gray(255)
16: 5x5+75+90 77.0,92.0 25 gray(255)
17: 5x5+40+100 42.0,102.0 25 gray(255)
18: 5x5+110+110 112.0,112.0 25 gray(255)
19: 5x5+140+110 142.0,112.0 25 gray(255)
21: 5x5+15+130 17.0,132.0 25 gray(255)
22: 5x5+40+140 42.0,142.0 25 gray(255)
23: 5x5+85+140 87.0,142.0 25 gray(255)
24: 5x5+120+140 122.0,142.0 25 gray(255)
2: 5x5+55+5 57.0,7.0 25 gray(255)
5: 5x5+100+20 102.0,22.0 25 gray(255)
7: 5x5+65+30 67.0,32.0 25 gray(255)
8: 5x5+125+30 127.0,32.0 25 gray(255)
9: 5x5+105+50 107.0,52.0 25 gray(255)
11: 5x5+25+65 27.0,67.0 25 gray(255)
12 14 15 16 17 18 19 21 22 23 24 2 5 7 8 9 11
Reprocess to remove regions by ID
convert img2.png \
\( -clone 0 -alpha extract -type bilevel \
-define connected-components:mean-color=true \
-define connected-components:remove="$id_list" \
-connected-components 8 -background black -flatten +write tmp.png \) \
-alpha off -compose copy_opacity -composite \
result2.png
4x Zoom:
fmw42's excellent answer uses connected regions, but I think it is possible with just a morphology. Use:
0 0 0
0 1 0
0 0 0
As the structuring element with erode and it'll detect 8-way connected isolated pixels. Now EOR that with your alpha and it'll make those pixels fully transparent (ie. remove them).
I don't know IM well enough to make you a command to do this :-( But with the libvips command-line it would be this to make a test image:
size=256
# sparse speckles for the alpha
vips gaussnoise t1.v $size $size
vips relational_const t1.v a.v more 200
# RGB noise
vips gaussnoise r.v $size $size
vips gaussnoise g.v $size $size
vips gaussnoise b.v $size $size
# assemble and save
vips bandjoin "r.v g.v b.v a.v" x.png
Then to remove your stray pixels:
# make mask.mor, a file containing our structuring element
cat >mask.mor <<EOF
3 3
0 0 0
0 255 0
0 0 0
EOF
# pull out the alpha channel
vips extract_band x.png a.v 3
# find isolated pixels
vips morph a.v t1.v mask.mor erode
# EOR with alpha to zap those pixels
vips boolean t1.v a.v t2.v eor
# extract rgb from original, then attach our modified alpha
vips extract_band x.png rgb.v 0 --n 3
vips bandjoin "rgb.v t2.v" x2.png
Here's before and after:
The libvips CLI is fast but a bit clumsy. It's neater if you use something like Python to script it.
Related
Is there a way that I can use the convert from imagemagick command to create a bleed margin in a PDF or even view one?
Bleed is a very common print technique, and ImageMagick offers the concept of "Virtual Pixels" which are the pixels located on the plane outside of "Authentic Pixels".
For example...
Take the following image
convert rose: rose.png
Set new viewport, and bleed virtual pixel across edge.
convert rose.png -set option:distort:viewport 90x66-10-10 \
-virtual-pixel Edge -filter point \
-distort SRT 0 +repage output.png
I bit more creativity (and maths) to include trim lines.
convert \( \
-size 110x86 xc:white -fill none -strokewidth 1 -stroke black \
-draw "path 'M 20 0 V 85 M 89 0 V 85 M 0 20 H 110 M 0 65 H 110'" \) \
\( output.png -repage +10+10 \) -layers merge \
output_with_trim.png
I need to detect the biggest object from image with ImageMagick. It can be bigger or smaller, or can be in other location. It's always black, and background always white.
Like this with Connected Component Analysis
convert objects.png -define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 8 -auto-level output.png
Objects (id: bounding-box centroid area mean-color):
0: 595x842+0+0 296.7,420.0 499414 gray(255)
7: 37x30+342+632 360.0,646.5 1110 gray(0)
3: 12x15+465+375 470.5,382.0 180 gray(0)
1: 23x12+439+332 447.9,335.4 150 gray(0)
6: 13x16+451+425 456.6,430.6 136 gray(0)
The first object listed (the first line) is a white object, because the mean-color is gray(255), and is therefore the background, so I ignore that. The second one is the largest (area=1110) and I can draw a red rectangle around it like this
convert objects.png -stroke red -strokewidth 5 -fill none -draw "rectangle 342,632 379,662" out.png
If you want to mask out all objects outside the bounding box of the largest object, you can do that like this:
convert objects.png -alpha on \
\( +clone \
-evaluate set 0 \
-fill white \
-draw "rectangle 342,632 379,662" \
-alpha off \
\) -compose copy-opacity -composite result.png
Basically the part inside the parentheses copies the original image (+clone), fills it with black (-evaluate set 0), then draws a white box over the bounding box of the biggest shape, then uses that black and white mask to set the opacity of the original image that we started off with. That leaves you with this:
For almost 4 days I searched for a command how I could add a crop-mark to my image.
I have installed ImageMagick software as well. I tried so many commands but am not able to crop with adding proper gutter and a crop mark.
My image should look like this after the conversion:
https://alphagraphicslisle.files.wordpress.com/2011/12/gutter-2.png?w=540
Assuming you would know the dimensions of the crop area, you would use ImageMagick's Draw commands to add trim/crop lines.
If your source image does not have a bleed region, you can generate one with an Edge virtual pixel.
convert rose: -set option:distort:viewport 90x66-10-10 \
-virtual-pixel Edge -filter point -distort SRT 0 \
+repage rose_with_bleed.png
(Note: The rose: input image is a special name, using a built-in demo image that's present in every ImageMagick installation.)
Simplest/fastest way I can think of a solution would be to..
Draw trim-lead lines first
Composite image over lead
Draw crop rectangle over image
Example:
convert -size 110x86 xc:white \
-strokewidth 1 -stroke gray \
-draw 'line 20 0 20 86 line 90 0 90 86' \
-draw 'line 0 20 110 20 line 0 66 110 66' \
rose_with_bleed.png \
-geometry 90x66+10+10 -composite \
-stroke red -fill transparent \
-draw 'rectangle 20 20 90 66' \
out.png
I'd like to take these 2 images:
And essentially produce this:
I've gotten as far as using compare with fuzz to define the part that has changed. Is it possible to then get the bounding box of this area and crop the second frame?
I would do something along these lines:
convert a.jpg b.jpg -colorspace gray -blur 0x2 \
-compose difference -composite \
-threshold 20% out.jpg
Convert to greyscale and blur a little to hide small differences, then calculate the difference and threshold to make a binarised image like this:
Then I would go for a Connected Components Analysis to find the biggest object in the image, like this:
convert a.jpg b.jpg -colorspace gray -blur 0x2 \
-compose difference -composite -threshold 20% \
-define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 8 out.jpg
Objects (id: bounding-box centroid area mean-color):
0: 1029x1079+0+0 515.0,538.4 1102870 srgb(0,0,0)
17: 76x147+326+564 366.5,641.4 5827 srgb(252,252,252)
22: 18x50+358+612 365.1,635.3 568 srgb(0,0,0)
11: 34x31+810+345 825.5,361.1 317 srgb(255,255,255)
16: 57x97+25+539 52.3,587.2 286 srgb(255,255,255)
14: 46x65+120+414 144.0,444.3 203 srgb(255,255,255)
18: 27x49+23+579 36.9,601.0 118 srgb(255,255,255)
24: 16x8+703+641 710.6,644.5 102 srgb(255,255,255)
The -define connected-components:verbose=true causes the individual blobs to be output as text for parsing.
The -define connected-components:area-threshold=100 says to only output blobs larger than 100 pixels in area.
The -connected-components 8 says to allow pixels that are 8-connected to be considered as belonging to the same object. 8-connected means North-East, South-East, South-West and North-East neighbours in addition to the normal North, East, South and West. By default, ImageMagick only considers 4-connected pixels as belonging to the same object - it's faster ;-)
And your player is item id 17 - the second row and you can see the bounding box, and cut that out of the orignal with
convert b.jpg -crop 76x147+326+564 player.jpg
Note: You will need ImageMagick 6.8.9-10 or better for Connected Components Analysis.
im getting the most existing colors of an image and display it with the "histogram" funktion like
convert image.jpg -scale 100x100 -gravity \
center -crop 50% -dither None -remap color_map.gif \
-format %c histogram:info:
22: ( 0, 0, 0) #000000 black
881: (119,133,142) #77858E rgb(119,133,142)
268: (186, 84, 29) #BA541D rgb(186,84,29)
662: (212,212,212) #D4D4D4 grey83
20: (244,203, 98) #F4CB62 rgb(244,203,98)
647: (255,255,255) #FFFFFF white
Ho can i work now with this output? i want to save the most existing color in my database, but i dont know how to get now only the color with the number 881.
Can any one help me?
If you want to do this purely from the shell (assuming Bash-like Unix environment), something like this would work:
convert image.jpg -scale 100x100 -gravity center \
-crop 50% -dither None -remap color_map.gif \
-format %c histogram:info: | sort | tail -n1 | \
sed -e 's/.*\(#[0-9A-F]\+\).*/\1/'
That takes the output from ImageMagick, sorts it so the largest color count is on the bottom, takes just that line, then extracts the color hex from the line. You can tweak the sed regex if your goal is to get the decimal rgb values instead of the hex.
So for your example image, the output should be just:
#77858E