Introduction
These days, there are many range finder devices which are used almost everywhere from robotics to country's defence system. All these devices work well, but there are portability and cost concerns for general purpose robotics applications. From my previous articles, I've been creating cool programs related to webcam and now I've come up with a laser-web cam range finder. It could tell how far an object is by just using a very cheap laser pen and an ordinary web cam.
How It Works
First, we need to arrange the laser pen exactly below the web cam so that it's parallel to the optical axis of the camera's lens. Practically, it's not possible even a minor error would give a huge error at greater distances, but this technique works good at short distances (~2 meters). Let's see why we arranged it in this way, check out the ray diagram below:
When the laser beam is projected at some surface, it makes a laser dot on it and then image of that laser dot is formed at focal plane of web cam. Assuming that angle of view of camera is parallel, then by applying laws of similar triangles, we get:
h' / h = f' / D
h': height of laser dot(image) from center(image height)
h: height of laser beam below camera(actual height)
f': focus of camera, distance of lens to focal plane
D: we want this distance(range)
so D = (f' * h) / h'
Since focus of the camera doesn't change much at small distances, laser is fixed and it's height won't change so everything is constant here except h'(image height). We get our relation that D is inversely proportional to h'.
We know that a pixel is a unit of measurement so we want to re-define h' then we could say that h' is directly proportional to number pixels of laser dot from center of image or mathematically:
h' = k * (pixels of laser dot from center)
where k is some constant depends on type of web cam and computer used. Now our final equation to find distance has reduced to a simple equation below:
D = K / (pixels of laser dot from center)
To find constant K we could choose experimental approach. We use known values to calculate constant of proportionality. It's just needed once and I've included this option in my program. All you need is to hold a piece of paper at a known distance, let's say 10cm from webcam and simply press a button and the computer will calculate the constant of proportionality.
Image Processing
Before reading further, please take a look at my previous article for "how to include webcam within your C# application".
Now we know that distance is inversely proportional to pixels of laser dot from the center of image. But first of all, we need to extract the laser dot from image. Well it's fairly easy using a computer vision library called AForge. In my case, laser dot is a highly red blob, so it could be extracted using color filter:
Bitmap image = (Bitmap)eventArgs.Frame.Clone();
ColorFiltering colr_filter = new ColorFiltering();
colr_filter.Red.Min = color_data.RedDown;
colr_filter.Red.Max = color_data.RedUp;
colr_filter.Blue.Min = color_data.BlueDown;
colr_filter.Blue.Max = color_data.BlueUp;
colr_filter.Green.Min = color_data.GreenDown;
colr_filter.Green.Max = color_data.GreenUp;
Bitmap gray_image = colr_filter.Apply(image);
Note: These RedDown
, RedUp
, BlueDown
, etc. are pre-defined values within range of 0-255. Consider the following color filter code:
ColorFiltering colorFilter = new ColorFiltering( );
colorFilter.Red = new IntRange( 0, 100 );
colorFilter.Green = new IntRange( 0, 200 );
colorFilter.Blue = new IntRange( 150, 255 );
Bitmap objectImage = colorFilter.Apply( image );
And when this code is applied on the image above, the resultant would be:
Similarly, once we isolate the laser dot, we convert image to 8bpp gray scale for using other filters.
IFilter gray_filter = new GrayscaleBT709();
gray_image = gray_filter.Apply(gray_image);
Further, we apply threshold filter to retain only bright objects such as laser dot.
Threshold th_filter = new Threshold(color_data.threshold);
th_filter.ApplyInPlace(gray_image);
Then we apply erosion filter to remove unwanted and small pixels and then finally apply dilation filter to make laser blob more prominent.
Erosion3x3 err_filter = new Erosion3x3();
err_filter.ApplyInPlace(gray_image);
Dilatation3x3 dil_filter = new Dilatation3x3();
dil_filter.ApplyInPlace(gray_image);
I've included a color tune form where you could play around with filters and visually see how changing values of certain filters could change the original image. You may find this option in Filters>>Edit.
Once we have a white blob on laser dot, we use blob counter class to detect its location and geometric center. See the code below:
BlobCounter bc = new BlobCounter();
bc.ObjectsOrder = ObjectsOrder.Area;
bc.MinHeight = 15;
bc.MaxWidth = 15;
bc.ProcessImage(gray_image);
gray_image.Dispose();
To find the geometric center of the blob, we use the following code:
Blob[] b = bc.GetObjectsInformation();
IntPoint center_blob = b[0].CenterOfGravity;
Once we have the location of laser dot, then we could easily calculate the number of pixels from center of image by simple code:
Math.Abs((float)(previous_image.Height/2) - y_cord)
where y_cord
is y coordinate of laser dot.
Now we are left with one more thing that is constant of calibration, we find this value experimentally. We choose a surface which is at a known distance from the camera and then we flash laser. Computer detects that laser light, finds the pixels from center and multiplies it with known distance and we get a constant. I've included this feature in the program, you may find it in Tools>>calibrate.
That's it! We are done building our laser range finder.
Using the Program
You may first need to adjust the color filter to detect the laser light. When you are adjusting the settings of filter, remember that the white region is considered as a blob and black is rejected. Also you need to calibrate, it's pretty simple just go to Tools>>Calibrate and after that, the program will instruct you accordingly.
You could also change the source code to detect the edges of moving object. Basically, edges mean sudden change of range and we could also build some kind of robots that would follow some object by detecting its edges. It may also be used in autonomous robotics. For now, have fun with it. If you like it or found any error or need to give any suggestion, then share it with me though comments.
Update
Just updated the program, I found that sometimes it's not able to detect laser dot or at least not with full precision. So I added a manual mode to define laser dot on its own. You can download it from the link at the top of this article.
References
This article is greatly based on work and article written by Todd Danko. Make sure you read his article.