Playing with conic sections in homogeneous coordinates

Luca Magri. Credits: Prof. Giacomo Boracchi

Contents

Preparation

First, we create an empty image and display it (will be our "canvas")

figure(1)
im=zeros(500,500);
imshow(im, []);
hold on;

Data input: enter five points (then enter)

disp('click 5 points, then enter');
[x, y]=getpts;
scatter(x,y,100,'filled');
click 5 points, then enter

x and y are now column vectors.

x
y
x =

  184.0000
  291.0000
  327.0000
  267.0000
  174.0000


y =

  161.0000
  148.0000
  228.0000
  325.0000
  253.0000

Estimation of conic parameters

using the technique shown in "Multiple View Geometry", page 9. this is equivalent to requiring that the conic passes trhough all the six points

A=[x.^2 x.*y y.^2 x y ones(size(x))]

% A is 5x6: we find the parameter vector (containing [a b c d e f]) as the
% right null space of A.
% This returns a vector N such that A*N=0.  Note that this expresses the
% fact that the conic passes through all the points we inserted.
N = null(A);
% alternatively you can use the svd
[~,~,V] = svd(A);
N = V(:,end);
A =

   1.0e+05 *

    0.3386    0.2962    0.2592    0.0018    0.0016    0.0000
    0.8468    0.4307    0.2190    0.0029    0.0015    0.0000
    1.0693    0.7456    0.5198    0.0033    0.0023    0.0000
    0.7129    0.8677    1.0562    0.0027    0.0032    0.0000
    0.3028    0.4402    0.6401    0.0017    0.0025    0.0000

Let's assign the values to variables and build the 3x3 conic matrix (which is symmetrical)

cc = N(:, 1);
% change the name of variables
[a b c d e f] = deal(cc(1),cc(2),cc(3),cc(4),cc(5),cc(6));
% here is the matrix of the conic: off-diagonal elements must be divided
% by two
C=[a b/2 d/2; b/2 c e/2; d/2 e/2 f]

% Remark: since the right null space has dimension one,
% when the points are taken in general position
% the system admits an infinite number of solutions
% however, these can be expressed as lambda * n, where n
% lambda \in R and n = null(A).
% thus, they all corresponds to the same conic.
C =

    0.0000   -0.0000   -0.0029
   -0.0000    0.0000   -0.0016
   -0.0029   -0.0016    1.0000

Draw the conic

We pick every pixel, and compute the incidence relation.

im=zeros(500,500);
for i=1:500
    for j=1:500
        im(i,j)=[j i 1]*C*[j i 1]'; % this is an algebraic error
    end
end
figure; imshow(im); title('Algebraic error');

Let's draw black for pixels less than zero, white for pixels greater than 0.

figure(1)
imshow(im > 0);
scatter(x,y, 100, 'filled');

What happen when A is not full rank?

Instead of taking 5 points take just 4 N contains in each column a generator of the null space

N = null(A(1 : 4, :));

% then, any vector living in the null space give rise to a conic
% passing throguh those 4 points. Since the nullspace has dimension two
% we can find two conic passing through the four points

Let's assign the values to variables and build the 3x3 conic matrix (which is symmetrical) Let's start with a first solution, the first vector of the nullspace

cc = N(:, 1);
% cc = N(:, 1) + N(:, 2); % another example, any linear combination is fine
[a, b, c, d, e, f]=deal(cc(1),cc(2),cc(3),cc(4),cc(5),cc(6));
% here is the matrix of the conic
C1_four =[a b/2 d/2; b/2 c e/2; d/2 e/2 f]
% Let's consider a second solution, the second  vector of the nullspace
cc = N(:, 2);
% cc = N(:, 1) + 2*N(:, 2); % another example, any linear combination is fine
[a, b, c, d, e, f]=deal(cc(1),cc(2),cc(3),cc(4),cc(5),cc(6));
% here is the matrix of the conic
C2_four =[a b/2 d/2; b/2 c e/2; d/2 e/2 f]
C1_four =

    0.0024   -0.0015   -0.3198
   -0.0015    0.0002    0.3844
   -0.3198    0.3844   -0.0023


C2_four =

    0.0000   -0.0000   -0.0032
   -0.0000    0.0000   -0.0012
   -0.0032   -0.0012    1.0000

Draw the conic

We pick every pixel, and compute the incidence relation.

im_four=zeros(500,500);
for i=1:500
    for j=1:500
        im_four1(i,j)=[j i 1]*C1_four*[j i 1]';
        im_four2(i,j)=[j i 1]*C2_four*[j i 1]';
    end
end

figure(2)
subplot(1,2,1)
imshow(im_four1>0);
hold on;
scatter(x(1 : 4), y(1 : 4), 100, 'filled','cyan');
scatter(x(5), y(5), 100, 'filled','red');
title('1st nullspace col ')
subplot(1,2,2)
imshow(im_four2>0);
hold on
scatter(x(1 : 4), y(1 : 4), 100, 'filled','cyan');
scatter(x(5), y(5), 100, 'filled','red');
title('2n nullspace col ')
hold off

Any linear combination of N1 and N2 is a solution

we can draw a pencil of conics

for lambda = [10, 200, 3000, 100000]

    cc = 1*N(:,1)+ lambda*N(:,2);
    [a, b, c, d, e, f]=deal(cc(1),cc(2),cc(3),cc(4),cc(5),cc(6));
    C3_four =[a b/2 d/2; b/2 c e/2; d/2 e/2 f];
    % We pick every pixel, and compute the incidence relation.
    im_four=zeros(500,500);
    for i=1:500
        for j=1:500
            im_four3(i,j)=[j i 1]*C3_four*[j i 1]';
        end
    end
    figure(3)
    imshow(im_four3>0);
    hold on;
    scatter(x(1 : 4), y(1 : 4), 100, 'filled','cyan');
    scatter(x(5), y(5), 100, 'filled','red');
    title('linear combination of nullspace cols ')
    pause;
end

Tangent lines

Let's draw the tangents to our points: the tangent line in homogeneous coordinates is l the line tangent to C at the line x is defined as l = C*x

% this is the line tangent to the first point
l1 = C * [x(1); y(1); 1];
% here I compute all the tangent lines simultaneously
% stacking all the vectors in the columns of the matrix l
l=C*[x y ones(size(x))]'
l =

   -0.0007    0.0006    0.0010    0.0001   -0.0010
   -0.0005   -0.0007   -0.0001    0.0008    0.0003
    0.2114   -0.0796   -0.3101   -0.2878    0.0960

in order to draw it we find its angle, then plot a line of length 200 around it. In order to write less lines of code, I'm doing a bit of hacks with matrices and vectors here: theta will be a column vector of angles, and with a single plot command I'll plot all the lines. Check out the documentation of plot to understand what's happening.

theta=atan2(-l(1,:),l(2,:));
theta = theta';
figure(1), hold on
plot([x-cos(theta)*100 x+cos(theta)*100]',...
    [y-sin(theta)*100 y+sin(theta)*100]','b-','LineWidth',5);

Let's compute the dual conic and check that all the tangent lines we computed previously actually satisfy the relation (up to numeric precision issues).

Cstar=inv(C);
l(:,1)'*Cstar*l(:,1)
l(:,2)'*Cstar*l(:,2)
l(:,3)'*Cstar*l(:,3)
l(:,4)'*Cstar*l(:,4)
l(:,5)'*Cstar*l(:,5)
ans =

   2.7756e-16


ans =

   5.8287e-16


ans =

   3.3307e-16


ans =

  -4.4409e-16


ans =

  -4.9960e-16