Reputation: 3166
In short, I want to find out co-ordinate of a pixel with predominant color.
To be specific, I want to achieve the following:
Find predominant color. By predominant, I mean a color that majority of the pixels in an image have (I have achieved it using histogram)
After getting this color (in my case its black), I want to find a pixel that is black and its surrounded by only black pixels. Basically, the center of most concentrated area of black.
Till now, I could only get the predominant color.
convert src.png -format %c histogram:info: > x.txt
cat x.txt | awk '{print $1}' | sed 's/://g' > x1.txt
h=$(sort -n x1.txt | tail -1)
cat x.txt | grep "$h"
rm -rf x.txt
Result:
169211: ( 0, 0, 0,255) #000000 black
Now, I can also get all the co-ordinates for black
convert src.png txt: | grep black
469,799: ( 0, 0, 0,255) #000000 black
470,799: ( 0, 0, 0,255) #000000 black
471,799: ( 0, 0, 0,255) #000000 black
472,799: ( 0, 0, 0,255) #000000 black
473,799: ( 0, 0, 0,255) #000000 black
474,799: ( 0, 0, 0,255) #000000 black
475,799: ( 0, 0, 0,255) #000000 black
476,799: ( 0, 0, 0,255) #000000 black
477,799: ( 0, 0, 0,255) #000000 black
478,799: ( 0, 0, 0,255) #000000 black
...
But I need a random co-ordinate of a black pixel that is located at a place where there is a only black pixels around it...
I am using Linux and Imagemagick version 6.6.5
Upvotes: 2
Views: 2847
Reputation: 207375
I like this approach using Euclidean distance morphology - it makes the wizard look rather demonic (!) and runs in under a second!
convert logo: -virtual-pixel none \
-morphology Distance Euclidean \
-auto-level -channel GB \
-threshold 85% 85.png
and thresholding at 95% and 99% gets you these:
And, if you want the text coordinates:
convert logo: -virtual-pixel none -morphology Distance Euclidean -auto-level -threshold 99% txt:- | grep white
151,328: (255,255,255,1) #FFFFFF white
152,328: (255,255,255,1) #FFFFFF white
Upvotes: 1
Reputation: 207375
Here is a different idea that runs in a fraction of a second...
If you are looking for large black areas, repeatedly tile the image into smaller and smaller tiles until you get a tile that is fully black, then choose the centre of that tile. Here is a little video showing the convergence. The process is looking at the semi-transparent red areas till one is found that only contains black.
Here is the code:
#!/bin/bash
# Set interesting pixels to black, others to white
convert logo: -fill black +opaque white -negate start.png
# Now tile, into 4,9,16,25 till we get a tile that is fully black
for i in $(seq 2 8); do
rm tmp*.png 2> /dev/null
convert -crop ${i}x${i}@ start.png tmp%d.png
for f in tmp*png; do
mean=$(identify -format "%[mean]" "$f")
if [ "$mean" = "0" ]; then
j=$((i*i))
echo "$f" of $j tiles
exit
fi
done
done
And the output is this:
tmp6.png of 9 tiles
Upvotes: 0
Reputation: 90203
First, find your predominant color. You already seem to know how to do this, so I skip this step. (I also didn't check or verify your code for this...)
Your code has some weaknesses:
You only clean up the x.txt
, but not your x1.txt
.
You should drop the | sed 's/://g'
part from your second command. It eliminates the :
colon from your variable, but this can lead to h=21
(instead of h=21:
) which leads to your grep "$h"
finding all such lines:
1: (221, 86, 77) #DD564D srgb(221,86,77)
1: (221,196,192) #DDC4C0 srgb(221,196,192)
1: (221,203,197) #DDCBC5 srgb(221,203,197)
[...]
21: (255,255,255) #FFFFFF white
If you keep it at h=21:
you'll find the one line you are looking for! (Example to verify: use the built-in rose:
image in place of your src.png
to see what I mean.)
Second, apply a very small amount of blur on the image, by averaging each pixel with its 8 surrounding pixels for each location: -blur 1x65535
(this operation uses a 3x3 square kernel). After that step, in the resulting image only those pixels will remain purely black, which were surrounded by only black pixels in the original image.
Third, make all non-black pixels white: by applying a -fill white +opaque black
-fill white -opaque black
-operation on the image. (See also "Morphology", esp. "Erosion".) This junks all other colors from the image by making all non-black colors white and simplifies your search for pure black pixels. (Note: this doesn't work for such src.png files which do not contain at least one 3x3 pixels area with pure black pixels...)
Fourth: We have to account for pixels which are on the border of the image (these don't have 8 neighbours!), and hence we assign the color 'none' to these with -virtual-pixel none
.
I'll use the ImageMagick built-in special picture named 'logo:' to demonstrate my approach:
convert logo: logo.png
As you can easily see, this image has white as its pre-dominant color. (So I switch the code for this example to make all white pixels black...)
Commandline so far:
convert \
logo: \
-virtual-pixel none \
$(for i in {1..2}; do echo " -blur 1x65535 "; done) \
-fill black \
-opaque white \
2_blur-logo-virtpixnone.png
Here are the 2 images side by side:
Fifth: Leather, rinse, repeat.
Now let's apply a few more iterations of this algorithm, like 100, 500, 1000 and 1300, and lets also apply an annotation to the result so we know which image is which:
for j in 100 500 1000 1300; do
convert \
logo: \
-virtual-pixel none \
$(for i in $(seq 1 ${j}); do echo " -blur 1x65535 "; done) \
-fill black \
-opaque white \
-gravity NorthEast -annotate +10+10 "$j iterations" \
${j}_blur-logo-virtpixnone.png
done
As you can clearly see, my algorithm makes the black area converge towards that spot which you'd have intuitively guessed as being the 'center' of the white colored areas when looking at the original logo.png
:
Once you arrive at an output image with no black spot left, you've iterated once too often. Go back one iteration. :-)
There should now be only a very limited number of candidate pixels which match your criteria.
Upvotes: 4
Reputation: 315
This is just a hunch and not a complete answer, but you could preprocess the image with a blur filter first. Then the pixels that are black in the blurred image must have had black neighborhoods in the original image.
This won't work for an arbitrary color, though.
Upvotes: 0