Simple Optical Tracking

DIY camera tracking of objects that are moving on the ground, with just a webcam and a few lines of code

The setup that will be covered here, allows you to track an object that moves on a specified plane (e.g. on even ground) using just one webcam and a few lines of Python code. The camera has to be in an elevated position, observing the plane and the object you want to track.

The foundation for the tracking algorithm is the pinhole camera model. This model allows you to get the real-world position [XW,YW,ZW]T of the Object you want to track using pixel data from the image. For this, you need to identify 3 things:

  1. The image coordinates [XI,YI]T of the object
  2. The intrinsic parameters of the camera
  3. The Rotation RWC and translation tWC from World Coordinates to Camera Coordinates:
    [XC,YC,ZC]T=RWC[XW,YW,ZW]T+tWC

Intrinsic Parameters

The first step of determining the image coordinates (the actual pixel positions of the object) can be done in various ways. You can use segmentation or object detection algorithms, for example. Choosing a suitable method here depends on the situation and is out of the scope of this post. For simplicity's sake, let's just assume that the image coordinates are known already.

This brings us to the next step, the intrinsic parameters of the camera. In a perfect world, The camera we use behaves like a pinhole camera. In such a scenario, you could just draw a light ray from each pixel in the image, through the pinhole, pointing exactly to the corresponding object in the real world

Olaf Peters (OlafTheScientist), CC BY-SA 4.0, via Wikimedia Commons

This means, that the image coordinates are connected directly to the real-world object by the projection rays. The origin of the camera coordinate system is the center of the projection O (aka. the pinhole). Computing the 3D reconstruction from image coordinates is like following those rays from the known pixel position, through the pinhole until you reach the object. Understanding the geometry behind this is easier if we first follow that ray in the opposite direction (from known world coordinates to the pixel positions) and then turn that process upside down. Let's say, the world coordinates are known and transformed into the camera coordinate system. To make everything easier, those transformed coordinates are also scaled down so that they are exactly one unit away from the center of the projection.

  • X^C=XCZC
  • Y^C=YCZC
  • Z^C=ZCZC=1

These scaled-down coordinates (emphasized by the ^ symbol) can be projected onto the pixel coordinate system using the principal point [cx,cy] and the focal length [fx,fy]. For this, the parameters are placed inside the camera matrix K that projects the normalized coordinates [X^C,Y^C,1]T onto the image plane to get the actual pixel positions.

  • K=[fx0cx0fycy001]
  • [XI,YI,1]T=K[X^C,Y^C,1]T

All the equations above assume that the camera in use follows the pinhole camera model. However, this model is idealized and doesn't quite represent reality. In reality, camera lenses are used instead of a pinhole. Those lenses are essentially doing the same thing but add some additional distortions to the image so that the pixel/image coordinates aren't quite at the same place as they would be with a pinhole.

Determining the lens distortion parameters as well as the camera matrix can be done with openly accessible libraries like opencv, or with tools like the Matlab Camera Calibrator. The details of that process can be found on those sites and aren't discussed here for that reason.

Rotation and Translation from World to Camera-Coordinates

The third thing you need to determine for the 3D reconstruction is the relation between the camera- and the world coordinates. It is expressed using the rotation RWC and the translation tWC. These parameters are also called extrinsic parameters and can be determined using a perspective-n-point transformation. The transformation requires at least 4 markers that are placed on the plane, you want to observe with the camera. The markers cannot be colinear and you have to know their exact position in world coordinates. Once you have placed the markers and know their exact locations, you can take a picture with your camera and determine the corresponding points in image coordinates

Source

Let's say we have the following corresponding point pairs:

Real World Point Image Point
PW(0)=[XW(0),YW(0),ZW(0)] PI(0)=[XI(0),YI(0)]
PW(1)=[XW(1),YW(1),ZW(1)] PI(1)=[XI(1),YI(1)]
PW(2)=[XW(2),YW(2),ZW(2)] PI(2)=[XI(2),YI(2)]
PW(3)=[XW(3),YW(3),ZW(3)] PI(3)=[XI(3),YI(3)]

With those corresponding points, the camera matrix K, and the distoriton params d you can now use OpenCVs solvePnP(...) function to get the rotation matrix RWC and the translation vector tWC.

  • solvePnP([PW(0),,PW(3)],[PI(0),,PI(3)],K,d)[RWC,tWC]

Putting everything together

We determined the camera matrix K and the distortion parameters d by calibrating the camera. Using at least 4 known real-world markers, we also determined the extrinsic parameters RWC and tWC using solvePnP(...). With all this information, it's now possible to transform any image point [XI,YI] into the world coordinate system [XW,YW,ZW] if ZW is known. Because this whole tracking setup is constrained to objects moving on a plane, the ZW value can simply be measured beforehand. It is the height of the marker that should be measured with respect to the plane with the four fixed markers on it. The first step in this process is turning the image coordinates into normalized camera coordinates using the inverse camera matrix K1.

  • [X^C,Y^C,1]T=K1[XI,YI,1]T

The next step is scaling the normalized coordinates by ZC to get the actual point in the camera coordinate system.

  • [XC,YC,ZC]T=[X^C,Y^C,1]TZC

Unfortunately, we don't know the value of ZC. All we know is ZW so we need to calculate it first. To do that, the relationship between world and camera coordinates can be used. It is described using extrinsic parameters RWC and tWC.

  • RWC=[r00r01r02r10r11r12r20r21r22]
  • tWC=[xtytzt]
  • [XC,YC,ZC]T=RWC[XW,YW,ZW]T+tWC

Let's first turn this equation around so that the world coordinates are alone on one side. The inverse rotation matrix can be obtained by transposing it and the inverse translation is just the negative value of the vector:

  • [XW,YW,ZW]T=(RWC)T([XC,YC,ZC]TtWC)

Now replace the camera coordinates with the scaled normalized camera coordinates:

  • [XW,YW,ZW]T=(RWC)T([X^C,Y^C,1]TZCtWC)

The desired result can now be calculated by simplifying the equation and just looking at the Z-value of the 3D coordinates here:

  • r2=[r02,r12,r22] (the last row of the inverse rotation matrix)
  • ZW=r2([X^C,Y^C,1]TZCtWC)
  • ZW=r2[X^C,Y^C,1]TZCr2tWC
  • ZC=ZW+r2tWCr2[X^C,Y^C,1]T

Now, the value of ZC is known, we can go back to the previous equation to calculate the position in the world coordinate system.

Example Implementation

I've also created a Jupyter Notebook example that shows how to actually code everything that was described above: