DevonDahon
DevonDahon

Reputation: 8360

ImageMagick: How to get a pixel from each area of a mask?

How to get a pixel from each area of a mask with ImageMagick ?

I'm currently able to get the centroid of the different areas this way:

enter image description here

convert circles.jpg -define connected-components:area-threshold=800 -define connected-components:verbose=true -connected-components 8 null: | sed '1,2d;$d' | awk '{print $1 $3}' > circles_centroids.txt

Which gives me these coordinates:

3994:668.1,373.8
660:145.2,161.5
7301:278.1,547.5
2973:666.8,372.6
49:143.6,156.0

By the way, why do I get five coordinates while I only have three circles ?

Then, I put a red point on each coordinate:

while IFS=: read -r n xy ; do
  mogrify -stroke red -strokewidth 3 -draw "stroke-linecap round line ${xy%,*},${xy#*,} ${xy%,*},$(bc<<<${xy#*,}+0.0001)" circles.jpg;
done < circles_centroids.txt

enter image description here

Is it possible, the same way, to get a list of coordinates with only one pixel for each area, instead of centroid ? If yes, how ?

=== EDIT ===

My goal is actually to get a mask image for each component of the image below, having the coordinates of a pixel for each area would allow me to use the filltoborder option in order to isolate each area and get a mask for each component. enter image description here

I also tried the "different grey level approach here, but I get the unxpected result below, where I can have different gray for one component. enter image description here

Upvotes: 0

Views: 337

Answers (2)

fmw42
fmw42

Reputation: 53164

You have a couple of issue that you need to fix in order to use connected components in Imagemagick.

First, your image is JPG and possibly antialiased and thus grayscale. (JPG is lossy compression and changes values). Thus it needs to be made binary or you will get too many regions for what you want to do.

Second, you need to use -define connected-components:mean-color=true in order to have the output image look like the input image and not have grayscale correspond to region ID numbers.

For example, if I do the following on your input image, I get lots of grayscale regions.

convert image.jpg \
-define connected-components:verbose=true \
-define connected-components:area-threshold=0 \
-define connected-components:mean-color=true \
-connected-components 4 \
null: 

  0: 818x687+0+0 420.4,339.2 393850 gray(255)
  6405: 213x232+560+260 667.8,373.2 38867 gray(255)
  1066: 212x197+46+61 145.7,161.0 32689 gray(255)
  11470: 197x189+180+453 278.4,547.6 27921 gray(255)
  4764: 259x279+536+236 667.8,372.1 14056 gray(0)
  86: 258x261+22+21 144.0,156.7 13604 gray(0)
  10418: 241x236+159+430 278.8,546.4 12060 gray(0)
  8450: 4x40+538+304 539.5,323.5 160 gray(2)
  14378: 2x57+158+496 158.5,523.8 113 gray(3)
  10247: 31x6+260+425 278.7,427.6 98 gray(255)
  5996: 32x5+106+257 121.2,259.2 89 gray(255)
  15035: 43x3+656+514 676.8,515.5 85 gray(0)
  14031: 41x4+659+490 678.3,492.3 81 gray(255)
  9942: 2x41+795+407 795.5,427.2 81 gray(4)
  3152: 2x32+22+144 22.5,159.5 64 gray(3)
  11234: 29x6+264+449 279.1,451.8 64 gray(0)
  15824: 4x16+378+568 379.5,575.5 64 gray(2)
  284: 28x5+127+33 140.6,35.3 61 gray(255)
  14375: 3x58+153+496 154.9,525.4 60 gray(254)
  14374: 1x56+154+496 154.0,523.5 56 gray(252)
  14377: 1x56+157+496 157.0,523.5 56 gray(0)
  14376: 1x56+156+496 156.0,523.5 56 gray(252)
  7795: 28x3+101+282 114.5,283.5 54 gray(0)
...


In the above full listing, there are more that 3 black regions.

But if I threshold at 50% to make the image purely black and white saving as PNG, then I get

convert image.jpg -threshold 50% -type bilevel \
-define connected-components:verbose=true \
-define connected-components:area-threshold=0 \
-define connected-components:mean-color=true \
-connected-components 4 \
null: 
Objects (id: bounding-box centroid area mean-color):
  0: 818x687+0+0 419.2,339.3 401926 gray(255)
  4: 215x236+560+258 668.0,373.6 41506 gray(255)
  2: 215x202+44+60 145.1,161.4 34718 gray(255)
  6: 197x192+180+452 278.1,547.3 29700 gray(255)
  3: 262x283+536+234 666.8,372.5 19261 gray(0)
  1: 262x265+20+20 143.2,155.8 18388 gray(0)
  5: 243x239+157+428 277.3,546.1 16467 gray(0)


Note that there are now only 3 gray(0), i.e. black regions. If there were some tiny spots of black, you could increase the area-threshold to remove them.

Now to extract your regions, I would do the following. I would extract the bounding box and centroid with an underscore between them and put in to an array. I would save the thresholded image as output of the connected-components (which can be done by using -define connected-components:mean-color=true. Then I would loop over the thresholded image, flood fill the regions (assuming they are closed) after extracting the centroid and then crop after extracting the bounding box.

Input: enter image description here

box_cent_Arr=(`convert image.jpg -threshold 50% -type bilevel \
-define connected-components:verbose=true \
-define connected-components:mean-color=true \
-connected-components 4 \
image_t50.png | grep "gray(0)" | awk 'BEGIN {OFS="_"} {print $2,$3}'`)
num=${#box_cent_Arr[*]}
for ((i=0; i<num; i++)); do
bbox=`echo "${box_cent_Arr[$i]}" | cut -d\_ -f1`
centroid=`echo "${box_cent_Arr[$i]}" | cut -d\_ -f2`
convert image_t50.png -fill black -draw "color $centroid floodfill" -alpha off -crop $bbox +repage image_$i.png
done


Thresholded Image: enter image description here

Three extracted filled regions:

enter image description here

enter image description here

enter image description here

With regard to your second image:

enter image description here

In this case, you do not need to fill them, so just use the bounding box. But you need to increase the area-threshold to remove the small spots next to the third row/second column region. So here I would do the following:

bboxArr=(`convert image.png -threshold 50% -type bilevel \
-define connected-components:verbose=true \
-define connected-components:area-threshold=1000 \
-define connected-components:mean-color=true \
-connected-components 4 \
image_t50.png | grep "gray(0)" | awk '{print $2}'`)
num=${#bboxArr[*]}
for ((i=0; i<num; i++)); do
bbox=`echo "${bboxArr[$i]}" | cut -d\_ -f1`
convert image_t50.png -crop $bbox +repage image_$i.png
done

The result are 22 regions extracted (that I numbered 0 to 21). For example, here are the first 3:

enter image description here

enter image description here

enter image description here

Upvotes: 1

Mark Setchell
Mark Setchell

Reputation: 207798

Updated Answer

Ah, I see the issue is somewhat more complicated than the initial example! I experimented with Fred Weinhaus's multicrop script and it seems to do the job rather well.

I would suggest you take up contact with Fred (@fmw42 on StackOverflow) or privately via his website to discuss suitability for your problem and licensing.

I ran:

./multicrop blobs.png result.png

and then montaged the results onto a red background:

magick montage -background red -geometry +10+10 result-*png montage.png

enter image description here

The positions and sizes of the various crop boxes are shown in the debug output of the script:

width=2990; height=4440; wg=299; hg=444; num=8

299 444 0 none
598 444 0 none
897 444 0 none
1196 444 0 none
1495 444 0 none
1794 444 0 none
2093 444 0 none
2392 444 0 none
2691 444 0 none
299 888 1 red
Processing Image 0
Initial Crop Box: 339x639+221+509

598 888 0 none
897 888 1 red
Processing Image 1
Initial Crop Box: 381x577+742+511

1196 888 0 none
1495 888 1 red
Processing Image 2
Initial Crop Box: 461x624+1314+437

1794 888 0 none
2093 888 0 none
2392 888 0 none
2691 888 1 red
Processing Image 3
Initial Crop Box: 326x939+2468+428

299 1332 0 none
598 1332 0 none
897 1332 0 none
1196 1332 0 none
1495 1332 0 none
1794 1332 0 none
2093 1332 0 none
2392 1332 0 none
2691 1332 0 none
299 1776 0 none
598 1776 0 none
897 1776 0 none
1196 1776 1 red
Processing Image 4
Initial Crop Box: 483x648+896+1374

1495 1776 0 none
1794 1776 1 red
Processing Image 5
Initial Crop Box: 536x837+1685+1218

2093 1776 0 none
2392 1776 0 none
2691 1776 0 none
299 2220 0 none
598 2220 0 none
897 2220 0 none
1196 2220 0 none
1495 2220 0 none
1794 2220 1 red
Processing Image 6
Initial Crop Box: 422x523+1578+2161

2093 2220 0 none
2392 2220 0 none
2691 2220 1 red
Processing Image 7
Initial Crop Box: 342x568+2432+2105

299 2664 0 none
598 2664 0 none
897 2664 0 none
1196 2664 0 none
1495 2664 0 none
1794 2664 0 none
2093 2664 0 none
2392 2664 0 none
2691 2664 0 none
299 3108 0 none
598 3108 0 none
897 3108 0 none
1196 3108 1 red
Processing Image 8
Initial Crop Box: 325x555+931+2784

1495 3108 0 none
1794 3108 1 red
Processing Image 9
Initial Crop Box: 426x528+1621+2820

2093 3108 0 none
2392 3108 0 none
2691 3108 1 red
Processing Image 10
Initial Crop Box: 305x549+2432+2781

299 3552 1 red
Processing Image 11
Initial Crop Box: 336x623+256+3413

598 3552 0 none
897 3552 0 none
1196 3552 1 red
Processing Image 12
Initial Crop Box: 381x617+934+3410

1495 3552 0 none
1794 3552 0 none
2093 3552 0 none
2392 3552 0 none
2691 3552 0 none
299 3996 0 none
598 3996 0 none
897 3996 0 none
1196 3996 0 none
1495 3996 0 none
1794 3996 0 none
2093 3996 0 none
2392 3996 0 none
2691 3996 0 none

If you run with -m option you will also get the results mask:

enter image description here

Original Answer

If you draw in the boxes it has found, you will immediately see the problem!

enter image description here

One solution might be to look for gray(255) to only get white areas, so you would do:

convert .. -connected-components ... | awk '/gray\(255\)/{print $1,$3}' > circles.txt

Upvotes: 0

Related Questions