I'm building an AR art installation, and need to track a person as they move through a room.
To do this I've built a head piece that has several infrared lights (with diffusers) and have a camera (a USB webcam) that has an optical filter to remove most/all visible light from the image, as well as a few tweaks to the image that basically leave me with white dots on a black background.
Getting the webcam set up in such a way as to capture the boundaries of the room was pretty easy, but I'm unsure as to how to go about then processing the black and white image to get the x,y coordinates of each dot.
Example image output: (This is a mock-up as I don't have one on me this second, and also keep in mind that the data will come from what is effectively a video)
Any ideas?
I can think of three ways to do this with ImageMagick, which has node
bindings, and is installed on most Linux distros and is available for OSX and Windows.
First, at the command line, quite simply type this:
identify -precision 5 -define identify:locate=maximum -define identify:limit=3 image.png
Channel maximum locations:
Gray: 65535 (1) 146,164 147,164 148,164
and that shows you the three brightest pixels are 146 pixels across from the top left corner and 164 pixels down from the top left corner, and the two beside it to its right.
Alternatively, if you are interested in the area and/or centroid of the dot, you can do a Connected Components Analysis with ImageMagick, which goes like this:
convert image.png \
-colorspace gray -threshold 10% \
-define connected-components:verbose=true \
-connected-components 8 output.png
Objects (id: bounding-box centroid area mean-color):
0: 818x502+0+0 408.6,250.5 410539 srgb(0,0,0)
1: 11x11+143+164 148.0,169.0 97 srgb(255,255,255)
This shows you (in the last line of output) that the white blob is 11 pixels x 11 pixels and is located 143 pixels across the image from the left edge and 164 pixels down from the top. Its centroid is at 148,169 and its area is 97 pixels and its colour is white.
The first object found (in the second to last line of output) is the entire image and you can discount that as its colour is black, i.e. rgb(0,0,0).
I can explain the parameters a little too... I convert to grayscale because Coonected Component Analysis traditionally looks for white objects on a black background in a b&w image. I then threshold to get pure white and pure black - you may need a median filter here on your real system to get rid of noise -median 3
, for example. The verbose=true
means that the command should print a list of all the blobs it finds, and the 8
means to consider pixels that are 8-connected to be parts of the same blob, i.e. a pixel touching another one at its NE, SE, SW, or NW corner is considered part of the same blob - if you set that to 4
, pixels have to be directly beside or above/below one another to be considered neighbours.
If you want to "box in" the area that it has found, you can do that like this:
convert image.png -stroke red -fill none -strokewidth 2 -draw "rectangle 143,164 154,175" output.png
The third method is slower, and it involves converting the image to text and then searching for the word "white". So. let's start simple, and just convert the image to text like this:
convert image.png -threshold 50% txt:
# ImageMagick pixel enumeration: 818,502,255,srgb
0,0: (0,0,0) #000000 black
1,0: (0,0,0) #000000 black
2,0: (0,0,0) #000000 black
3,0: (0,0,0) #000000 black
...
... 410,000 lines later
...
813,501: (0,0,0) #000000 black
814,501: (0,0,0) #000000 black
815,501: (0,0,0) #000000 black
816,501: (0,0,0) #000000 black
817,501: (0,0,0) #000000 black
Now, let's refine that, and look for white pixels only (on Windows you would use FINDSTR
rather than grep
):
convert image.png -threshold 50% txt: | grep white
146,164: (255,255,255) #FFFFFF white
147,164: (255,255,255) #FFFFFF white
148,164: (255,255,255) #FFFFFF white
149,164: (255,255,255) #FFFFFF white
150,164: (255,255,255) #FFFFFF white
145,165: (255,255,255) #FFFFFF white
146,165: (255,255,255) #FFFFFF white
147,165: (255,255,255) #FFFFFF white
148,165: (255,255,255) #FFFFFF white
149,165: (255,255,255) #FFFFFF white
150,165: (255,255,255) #FFFFFF white
151,165: (255,255,255) #FFFFFF white
144,166: (255,255,255) #FFFFFF white
145,166: (255,255,255) #FFFFFF white
146,166: (255,255,255) #FFFFFF white
147,166: (255,255,255) #FFFFFF white
148,166: (255,255,255) #FFFFFF white
149,166: (255,255,255) #FFFFFF white
150,166: (255,255,255) #FFFFFF white
151,166: (255,255,255) #FFFFFF white
152,166: (255,255,255) #FFFFFF white
143,167: (255,255,255) #FFFFFF white
144,167: (255,255,255) #FFFFFF white
145,167: (255,255,255) #FFFFFF white
146,167: (255,255,255) #FFFFFF white
147,167: (255,255,255) #FFFFFF white
148,167: (255,255,255) #FFFFFF white
149,167: (255,255,255) #FFFFFF white
150,167: (255,255,255) #FFFFFF white
151,167: (255,255,255) #FFFFFF white
152,167: (255,255,255) #FFFFFF white
153,167: (255,255,255) #FFFFFF white
143,168: (255,255,255) #FFFFFF white
144,168: (255,255,255) #FFFFFF white
145,168: (255,255,255) #FFFFFF white
146,168: (255,255,255) #FFFFFF white
147,168: (255,255,255) #FFFFFF white
148,168: (255,255,255) #FFFFFF white
149,168: (255,255,255) #FFFFFF white
150,168: (255,255,255) #FFFFFF white
151,168: (255,255,255) #FFFFFF white
152,168: (255,255,255) #FFFFFF white
153,168: (255,255,255) #FFFFFF white
143,169: (255,255,255) #FFFFFF white
144,169: (255,255,255) #FFFFFF white
145,169: (255,255,255) #FFFFFF white
146,169: (255,255,255) #FFFFFF white
147,169: (255,255,255) #FFFFFF white
148,169: (255,255,255) #FFFFFF white
149,169: (255,255,255) #FFFFFF white
150,169: (255,255,255) #FFFFFF white
151,169: (255,255,255) #FFFFFF white
152,169: (255,255,255) #FFFFFF white
153,169: (255,255,255) #FFFFFF white
143,170: (255,255,255) #FFFFFF white
144,170: (255,255,255) #FFFFFF white
145,170: (255,255,255) #FFFFFF white
146,170: (255,255,255) #FFFFFF white
147,170: (255,255,255) #FFFFFF white
148,170: (255,255,255) #FFFFFF white
149,170: (255,255,255) #FFFFFF white
150,170: (255,255,255) #FFFFFF white
151,170: (255,255,255) #FFFFFF white
152,170: (255,255,255) #FFFFFF white
153,170: (255,255,255) #FFFFFF white
143,171: (255,255,255) #FFFFFF white
144,171: (255,255,255) #FFFFFF white
145,171: (255,255,255) #FFFFFF white
146,171: (255,255,255) #FFFFFF white
147,171: (255,255,255) #FFFFFF white
148,171: (255,255,255) #FFFFFF white
149,171: (255,255,255) #FFFFFF white
150,171: (255,255,255) #FFFFFF white
151,171: (255,255,255) #FFFFFF white
152,171: (255,255,255) #FFFFFF white
153,171: (255,255,255) #FFFFFF white
144,172: (255,255,255) #FFFFFF white
145,172: (255,255,255) #FFFFFF white
146,172: (255,255,255) #FFFFFF white
147,172: (255,255,255) #FFFFFF white
148,172: (255,255,255) #FFFFFF white
149,172: (255,255,255) #FFFFFF white
150,172: (255,255,255) #FFFFFF white
151,172: (255,255,255) #FFFFFF white
152,172: (255,255,255) #FFFFFF white
145,173: (255,255,255) #FFFFFF white
146,173: (255,255,255) #FFFFFF white
147,173: (255,255,255) #FFFFFF white
148,173: (255,255,255) #FFFFFF white
149,173: (255,255,255) #FFFFFF white
150,173: (255,255,255) #FFFFFF white
151,173: (255,255,255) #FFFFFF white
146,174: (255,255,255) #FFFFFF white
147,174: (255,255,255) #FFFFFF white
148,174: (255,255,255) #FFFFFF white
149,174: (255,255,255) #FFFFFF white
150,174: (255,255,255) #FFFFFF white
As regards a node
version, I am really not very good at node
but I can point you to my answer here which does another ImageMagick process through node
and hope you can adapt that if you try the above at the command line and find it works for you well enough that you want to use ImageMagick.