How to crop the difference of two images? - imagemagick

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.

Related

imagemagick: Remove all occurrences of a stray pixel surrounded by transparency

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.

ImageMagick and segmenting contiguous islands in a transparent background image

I'm trying to convert something that looks like this into a bunch of smaller image crops or "slices", where each slice might overlap with another, but should contain each island of non-transparent pixels. The long continuous strokes don't have to be converted, but most of the "spots" should be.
So effectively, I'm trying to automatically "slice" it to look some thing like this (rough manual slicing):
How do you use imagemagick to automatically crop the elements of such an image, and to also return the position, width/height that each sub-image was cropped from?
You can use "Connect Component Analysis". As your image contains no transparency it is hard to use, so I hacked it and coarsely made the chessboard areas transparent like this:
Then do something along these lines:
magick image.png -alpha off -colorspace gray -negate -threshold 10% \
-define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 8 -auto-level output.png
Sample Output
Objects (id: bounding-box centroid area mean-color):
1: 498x614+0+0 236.9,317.2 219166 srgb(0,0,0)
2: 112x583+112+0 177.0,268.6 16094 srgb(255,255,255)
27: 128x210+350+342 403.8,436.5 14195 srgb(255,255,255)
7: 104x148+338+85 390.0,160.4 10451 srgb(255,255,255) <--- this one
3: 103x145+200+0 250.9,70.9 10317 srgb(255,255,255)
25: 107x132+202+245 252.7,307.8 9227 srgb(255,255,255)
36: 117x139+213+428 268.3,497.2 9103 srgb(255,255,255)
12: 139x77+0+112 62.1,152.4 7162 srgb(255,255,255)
16: 73x93+414+161 459.0,213.4 4104 srgb(255,255,255)
37: 51x61+249+457 274.6,488.1 2224 srgb(0,0,0)
4: 71x36+338+0 382.0,19.6 1507 srgb(255,255,255)
26: 32x47+238+277 253.1,300.2 875 srgb(0,0,0)
43: 50x30+283+584 310.7,600.0 833 srgb(255,255,255)
13: 41x17+31+140 51.0,148.1 410 srgb(0,0,0)
9: 11x11+174+100 178.7,105.3 104 srgb(255,255,255)
Each line of output corresponds to a blob. There is a header at the start that tells you what each field is.
Let's look at the line:
7: 104x148+338+85 390.0,160.4 10451 srgb(255,255,255)
and draw that blob into your image in semi-transparent red:
magick image.png -fill "rgba(255,0,0,0.5)" -draw "rectangle 338,85 442,243" result.png
Note that I have actually discarded the alpha channel (with -alpha off) in my command and used the RGB channels converted to greyscale as items to detect. Depending how your image is created, you may be better of discarding the RGB channels and just using the alpha or its inverse (-alpha extract -negate).
Not sure ImageMagick can do that.
There is a script that does something similar for Gimp: ofn-extract-objects.
The technique it uses is fairly simple: select the background and convert the selection to "paths" (ie, vectors). The vectors will have one "stroke" per continuous border in the selection (this means that you have nested strokes if you have objects with holes, but this isn't too hard to detect and filter). Once you have the strokes you can use the coordinates of the anchors and tangents to determine their bounding box.
If you don't want to use Gimp, you can use potrace and process the output SVG in a similar way.

ImageMagick - Trim / Crop to contiguous objects

How do you do the equivalent of this step in Photoshop.
https://gyazo.com/180a507c0f3c9b342fe33ce218cd512e
Supposed there are two contiguous objects in an image, and you want to create exact sized crops around each one and output as two files. (Generalize to N files)
You can do that with "Connected Component Analysis" to find the contiguous blobs.
Start Image
convert shapes.png -colorspace gray -negate -threshold 10% \
-define connected-components:verbose=true \
-connected-components 8 -normalize output.png
Sample Output
Objects (id: bounding-box centroid area mean-color):
0: 416x310+0+0 212.3,145.2 76702 srgb(0,0,0)
1: 141x215+20+31 90.0,146.2 26129 srgb(255,255,255)
2: 141x215+241+75 311.0,190.2 26129 srgb(255,255,255)
Notice how each blob, or contiguous object, is "labelled" or identified with its own unique colour (shade of grey).
So there is a header line telling you what the fields are followed by 3 blobs, i.e. one per line of output. The first line is the entire image and not much use. The second one is 141 px wide and 215 px tall starting at +20+31 from the top-left corner. The third one is the same size (because I copied the shape) and starts as +241+75 from the top-left corner.
Now stroke red around the final indicated rectangle - bearing in mind that rectangle takes top-left and bottom-right corners rather than top-left corner plus width and height.
convert shapes.png -stroke red -fill none -draw "rectangle 241,75 382,290" z.png
And crop it:
convert shapes.png -crop 141x215+241+75 z.png
And here is the extracted part:
If you want to generalise, you can just pipe the ImageMagick output into awk and pick out the geometry field:
convert shapes.png -colorspace gray -negate -threshold 10% -define connected-components:verbose=true -connected-components 8 -normalize output.png | awk 'NR>2{print $2}'
Sample Output
141x215+20+31
141x215+241+75

I need to leave the biggest object in image

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:

Command line ImageMagick gutter and crop-marks

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

Resources