Calculate the center of an Aruco marker in an image

I'm not shy to say that the Web as become a big pile of garbage. If I need to search information there, I try as much as I can to stay away from blogs and forums, and stick to official documentation. Yet, it happens too I lazily copy/paste code from SO or elsewhere. I did, and I've got bitten for doing so. Now I must redeem myself.

Aruco markers are squared black and white markers created by S.Garrido-Jurado et al. in Automatic generation and detection of highly reliable fiducial markers under occlusion. They are used, for example, in computer vision for camera pose estimation. The library OpenCV contains functions to detect them in an image and make calculation using them.

After detecting the marker in the image, I needed the coordinates of the center of the marker. OpenCV returns the coordinates of the corners of the marker, but surprisingly doesn't provide a way to get the center. You have to do it yourself. Searching on Google for "get center aruco image", among the 9 results on the first result page (1, 2, 3, 4, 5, 6, 7, 8, 9), four links (including the top one) point to a "the center is the average of the corners" answer, one link points to a "the center is calculated using the moments of the corner" answer, and four links are irrelevant. Then, I used the average of the corners coordinates to calculate the center coordinates. Which is incorrect. The correct answer is "the center is the intersection of lines joining opposite corners".

Here is an Aruco marker generated using OpenCV:

I print it on paper and take a picture with my smartphone:

With the script below, I calculate the center using the average, the moments, and the diagonal methods then I print and visualise the results.

The blue dot (diagonal method) gives the correct coordinates, the red and green dot (average and moments methods) gives incorrect coordinates.

The bias results from the angle at which the camera is looking at the marker. If the camera were to be right on top of the center of the marker, looking at it, all three methods would give the correct coordinates. Also, even with the incorrect methods, the result won't be biased by very much. Added to the fact that the marker is often very small in the image, that there is bias anyway in the corners coordinates to start with, that the posterior algorithms using these coordinates are often robust to noise, in general all three methods will probably give acceptable results. But if the accuracy of the result is critical to your application, cause you need it to work even at steep angles of view and other calculations rely on the accuracy of these coordinates, you'd better use the correct method.

About the diagonal method, if you want to check the calculation in the script, here is how I got it.

Knowing the coordinates of A, B, C, D, we search the coordinates of the intersection of line (AD) and line (CB). (AD) and (CB) can be expressed as: $$ AD(t)=\vec{A}+t\vec{AD} $$ $$ CB(t)=\vec{C}+t\vec{CB} $$ The lines intersect at \(t_1,t_2\) such as \(AD(t_1)=CB(t_2)\). This gives the system of linear equations: $$ \left\lbrace \begin{array}{l} A_x+t_1AD_x=C_x+t_2CB_x\\ A_y+t_1AD_x=C_y+t_2CB_y\\ \end{array} \right. $$ equivalent to: $$ \left\lbrace \begin{array}{l} t_1AD_x-t_2CB_x=C_x-A_x\\ t_1AD_y-t_2CB_y=C_y-A_y\\ \end{array} \right. $$ From the second equation of the system \(t_2\) can be written in function of \(t_1\) as follow: $$ t_2=\frac{C_y-A_y-t_1AD_y}{-CB_y} $$ From the second equation of the system \(t_1\) can be written in function of \(t_2\) as follow: $$ t_1=\frac{C_y-A_y+t_2CB_y}{AD_y} $$ Then, using either one depending on \(CB_y\ne0.0\) or \(AD_y\ne0.0\), \(t_1\) or \(t_2\) can be calculated from the first equation of the system. $$ \begin{array}{l} t_1AD_x-\frac{C_y-A_y-t_1AD_y}{-CB_y}CB_x=C_x-A_x\\ t_1(AD_x-\frac{AD_yCB_x}{CB_y})=C_x-A_x-\frac{(C_y-A_y)CB_x}{CB_y}\\ t_1=(C_x-A_x-\frac{(C_y-A_y)CB_x}{CB_y})/(AD_x-\frac{AD_yCB_x}{CB_y})\\ \end{array} $$ or $$ \begin{array}{l} \frac{C_y-A_y+t_2CB_y}{AD_y}AD_x-t_2CB_x=C_x-A_x\\ t_2(\frac{CB_yAD_x}{AD_y}-CB_x)=C_x-A_x-(\frac{(C_y-A_y)AD_x}{AD_y})\\ t_2=(C_x-A_x-(\frac{(C_y-A_y)AD_x}{AD_y}))/(\frac{CB_yAD_x}{AD_y}-CB_x)\\ \end{array} $$ Knowing \(t_1\) or \(t_2\), the coordinates of the intersection can be calculated using either \(AD(t_1)=\vec{A}+t_1\vec{AD}\) or \(CB(t_2)=\vec{C}+t_2\vec{CB}\).