Subversion Repositories seema-scanner

Rev

Rev 240 | Rev 243 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 240 Rev 241
1
function MSE = alignSubScansMarkers(calibrationFileName, alnFileName)
1
function MSE = alignSubScansMarkers(calibrationFileName, alnFileName)
2
%ALIGNSUBSCANSMARKERS Determines an exact alignment of sub scans (scans
2
%ALIGNSUBSCANSMARKERS Determines an exact alignment of sub scans (scans
3
% from e.g. one revolution of the rotation stage). 
3
% from e.g. one revolution of the rotation stage). 
4
% The method searches for circular white markers of a specific diameter.
4
% The method searches for circular white markers of a specific diameter.
5
% White frames corresponding to each sub scan must be available.
5
% White frames corresponding to each sub scan must be available.
6
% A coarse alignment in the form of an aln-file must be provided. 
6
% A coarse alignment in the form of an aln-file must be provided. 
7
%
7
%
8
% 2017 Jakob Wilm, DTU
8
% 2017 Jakob Wilm, DTU
9
 
9
 
10
initialAlign = readMeshLabALN(alnFileName);
10
initialAlign = readMeshLabALN(alnFileName);
11
[alnFilePath, ~, ~] = fileparts(alnFileName);
11
[alnFilePath, ~, ~] = fileparts(alnFileName);
12
 
12
 
13
calibration = readOpenCVXML(calibrationFileName);
13
calibration = readOpenCVXML(calibrationFileName);
14
 
14
 
15
% correct for Matlab 1-indexing in principle point coordinates
15
% correct for Matlab 1-indexing in principle point coordinates
16
calibration.K0(1:2, 3) = calibration.K0(1:2, 3)+1;
16
calibration.K0(1:2, 3) = calibration.K0(1:2, 3)+1;
17
calibration.K1(1:2, 3) = calibration.K1(1:2, 3)+1;
17
calibration.K1(1:2, 3) = calibration.K1(1:2, 3)+1;
18
 
18
 
19
% full projection matrices in Matlab convention
19
% full projection matrices in Matlab convention
20
P0 = transpose(calibration.K0*[eye(3) zeros(3,1)]);
20
P0 = transpose(calibration.K0*[eye(3) zeros(3,1)]);
21
P1 = transpose(calibration.K1*[calibration.R1 calibration.T1']);
21
P1 = transpose(calibration.K1*[calibration.R1 calibration.T1']);
22
 
22
 
23
% matlab cam params for undistortion
23
% matlab cam params for undistortion
24
camParams0 = cameraParameters('IntrinsicMatrix', calibration.K0', 'RadialDistortion', calibration.k0([1 2 5]), 'TangentialDistortion', calibration.k0([3 4]));
24
camParams0 = cameraParameters('IntrinsicMatrix', calibration.K0', 'RadialDistortion', calibration.k0([1 2 5]), 'TangentialDistortion', calibration.k0([3 4]));
25
camParams1 = cameraParameters('IntrinsicMatrix', calibration.K1', 'RadialDistortion', calibration.k1([1 2 5]), 'TangentialDistortion', calibration.k1([3 4]));
25
camParams1 = cameraParameters('IntrinsicMatrix', calibration.K1', 'RadialDistortion', calibration.k1([1 2 5]), 'TangentialDistortion', calibration.k1([3 4]));
26
 
26
 
27
% matlab struct for triangulation
27
% matlab struct for triangulation
28
camStereoParams = stereoParameters(camParams0, camParams1, calibration.R1', calibration.T1');
28
camStereoParams = stereoParameters(camParams0, camParams1, calibration.R1', calibration.T1');
29
 
29
 
30
nSubScans = length(initialAlign);
30
nSubScans = length(initialAlign);
-
 
31
%nSubScans = 5;
31
 
32
 
32
% 3D coordinates of markers in local camera frame
33
% 3D coordinates of markers in local camera frame
33
E = cell(nSubScans, 1);
34
E = cell(nSubScans, 1);
34
 
35
 
35
% 3D coordinates of markers in global initial alignment
36
% 3D coordinates of markers in global initial alignment
36
Eg = cell(size(E));
37
Eg = cell(size(E));
37
 
38
 
38
% find 3D markers coordinates 
39
% find 3D markers coordinates 
39
for i=1:nSubScans
40
for i=1:nSubScans
40
%for i=5:5
-
 
41
    % load point cloud
41
    % load point cloud
42
    pcFileName = fullfile(alnFilePath, initialAlign(i).FileName);
42
    pcFileName = fullfile(alnFilePath, initialAlign(i).FileName);
43
    pcFilePath = fileparts(pcFileName);
43
    pcFilePath = fileparts(pcFileName);
44
    pc = pcread(pcFileName);
44
    pc = pcread(pcFileName);
45
    Q = pc.Location;
45
    Q = pc.Location;
46
    idString = strsplit(initialAlign(i).FileName, {'.ply', '_'});
46
    idString = strsplit(initialAlign(i).FileName, {'.ply', '_'});
47
    idString = idString{end-1};
47
    idString = idString{end-1};
48
    
48
    
49
    % load white frames
49
    % load white frames
50
    frame0 = imread(fullfile(pcFilePath, ['sequence_' idString], 'frames0_0.png'));
50
    frame0 = imread(fullfile(pcFilePath, ['sequence_' idString], 'frames0_0.png'));
51
    frame1 = imread(fullfile(pcFilePath, ['sequence_' idString], 'frames1_0.png'));
51
    frame1 = imread(fullfile(pcFilePath, ['sequence_' idString], 'frames1_0.png'));
52
 
52
 
53
    e0Coords = autoDetectMarkers(frame0);
53
    e0Coords = autoDetectMarkers(frame0);
54
    e1Coords = autoDetectMarkers(frame1);
54
    e1Coords = autoDetectMarkers(frame1);
55
    
55
    
56
    %e0Coords = manuallyDetectMarkers(frame0);
56
    %e0Coords = manuallyDetectMarkers(frame0);
57
    %e1Coords = manuallyDetectMarkers(frame1);
57
    %e1Coords = manuallyDetectMarkers(frame1);
58
    
58
    
59
    %[e0Coords, conf0] = detectMarkersSubpix(frame0, e0Coords, P0, Q);
59
    %[e0Coords, conf0] = detectMarkersSubpix(frame0, e0Coords, P0, Q);
60
    %[e1Coords, conf1] = detectMarkersSubpix(frame1, e1Coords, P1, Q);
60
    %[e1Coords, conf1] = detectMarkersSubpix(frame1, e1Coords, P1, Q);
61
 
61
 
62
    if(length(e0Coords) < 1 || length(e1Coords) < 1)
62
    if(length(e0Coords) < 1 || length(e1Coords) < 1)
63
        continue;
63
        continue;
64
    end
64
    end
65
    
65
    
66
%     figure; 
66
%     figure; 
67
%     subplot(1,2,1);
67
%     subplot(1,2,1);
68
%     imshow(frame0);
68
%     imshow(frame0);
69
%     hold('on');
69
%     hold('on');
70
%     plot(e0Coords(:,1), e0Coords(:,2), 'rx', 'MarkerSize', 15);
70
%     plot(e0Coords(:,1), e0Coords(:,2), 'rx', 'MarkerSize', 15);
71
%     subplot(1,2,2);
71
%     subplot(1,2,2);
72
%     imshow(frame1);
72
%     imshow(frame1);
73
%     hold('on');
73
%     hold('on');
74
%     plot(e1Coords(:,1), e1Coords(:,2), 'rx', 'MarkerSize', 15);
74
%     plot(e1Coords(:,1), e1Coords(:,2), 'rx', 'MarkerSize', 15);
75
%     drawnow;
75
%     drawnow;
76
    
76
    
77
    e0Coords = undistortPointsFast(e0Coords, camParams0);
77
    e0CoordsUndistort = undistortPointsFast(e0Coords, camParams0);
78
    e1Coords = undistortPointsFast(e1Coords, camParams1);
78
    e1CoordsUndistort = undistortPointsFast(e1Coords, camParams1);
79
 
79
 
80
    % match ellipse candidates between cameras based on projection
80
    % match ellipse candidates between cameras based on projection
81
    E0 = projectOntoPointCloud(e0Coords, P0, Q);
81
    E0 = projectOntoPointCloud(e0CoordsUndistort, P0, Q);
82
    E1 = projectOntoPointCloud(e1Coords, P1, Q);
82
    E1 = projectOntoPointCloud(e1CoordsUndistort, P1, Q);
83
 
83
 
84
    matchedPairs = nan(size(E0, 1), 2);
84
    matchedPairs = nan(size(E0, 1), 2);
85
    nMatchedPairs = 0;
85
    nMatchedPairs = 0;
86
    for j=1:size(E0, 1)
86
    for j=1:size(E0, 1)
87
        
87
        
88
        % should use pdist2 instead..
88
        % should use pdist2 instead..
89
        sqDists = sum((E1 - repmat(E0(j,:), size(E1, 1), 1)).^2, 2);
89
        sqDists = sum((E1 - repmat(E0(j,:), size(E1, 1), 1)).^2, 2);
90
        
90
        
91
        [minSqDist, minSqDistIdx] = min(sqDists);
91
        [minSqDist, minSqDistIdx] = min(sqDists);
92
 
92
 
93
        if(minSqDist < 1^2)
93
        if(minSqDist < 2^2)
94
            nMatchedPairs = nMatchedPairs + 1;
94
            nMatchedPairs = nMatchedPairs + 1;
95
            matchedPairs(nMatchedPairs, :) = [j, minSqDistIdx];
95
            matchedPairs(nMatchedPairs, :) = [j, minSqDistIdx];
96
        end
96
        end
97
    end
97
    end
98
    matchedPairs = matchedPairs(1:nMatchedPairs, :);
98
    matchedPairs = matchedPairs(1:nMatchedPairs, :);
99
    
99
    
100
    figure; 
100
    figure; 
101
    subplot(1,2,1);
101
    subplot(1,2,1);
102
    imshow(frame0);
102
    imshow(frame0);
103
    hold('on');
103
    hold('on');
104
    plot(e0Coords(matchedPairs(:, 1),1), e0Coords(matchedPairs(:, 1),2), 'rx', 'MarkerSize', 15);
104
    plot(e0Coords(matchedPairs(:, 1),1), e0Coords(matchedPairs(:, 1),2), 'rx', 'MarkerSize', 15);
105
    subplot(1,2,2);
105
    subplot(1,2,2);
106
    imshow(frame1);
106
    imshow(frame1);
107
    hold('on');
107
    hold('on');
108
    plot(e1Coords(matchedPairs(:, 2),1), e1Coords(matchedPairs(:, 2),2), 'rx', 'MarkerSize', 15);
108
    plot(e1Coords(matchedPairs(:, 2),1), e1Coords(matchedPairs(:, 2),2), 'rx', 'MarkerSize', 15);
109
    drawnow;
109
    drawnow;
110
    
110
    
111
%     % triangulate marker centers (lens correction has been performed)
111
%     % triangulate marker centers (lens correction has been performed)
112
%     [E{i}, e] = triangulate(e0Coords(matchedPairs(:, 1),:), e1Coords(matchedPairs(:, 2),:), camStereoParams);
112
%     [E{i}, e] = triangulate(e0Coords(matchedPairs(:, 1),:), e1Coords(matchedPairs(:, 2),:), camStereoParams);
113
%     E{i} = E{i}(e<3.0, :);
113
%     E{i} = E{i}(e<3.0, :);
114
%     display(e);
114
%     display(e);
115
    
115
    
116
    [E{i}, e] = detectMarkersStereoSubpix(frame0, frame1, E0(matchedPairs(:, 1), :), camStereoParams, pc);
116
    [E{i}, e] = detectMarkersStereoSubpix(frame0, frame1, E0(matchedPairs(:, 1), :), camStereoParams, pc);
117
    display(e);
117
    display(e);
118
    
118
    
119
    % write point cloud with marker (debugging)
119
    % write point cloud with marker (debugging)
120
    pcDebug = pointCloud([pc.Location; E{i}], 'Color', [pc.Color; repmat([255, 0, 0], size(E{i}, 1), 1)]);
120
    %pcDebug = pointCloud([pc.Location; E{i}], 'Color', [pc.Color; repmat([255, 0, 0], size(E{i}, 1), 1)]);
121
    pcwrite(pcDebug, 'pcDebug.ply');
121
    %pcwrite(pcDebug, 'pcDebug.ply');
122
    
122
    
123
    % bring markers into initial alignment
123
    % bring markers into initial alignment
124
    [U,~,V] = svd(initialAlign(i).Rotation);
124
    [U,~,V] = svd(initialAlign(i).Rotation);
125
    Ri = U*V';
125
    Ri = U*V';
126
    Ti = initialAlign(i).Translation;
126
    Ti = initialAlign(i).Translation;
127
    
127
    
128
    Eg{i} = E{i}*Ri' + repmat(Ti', size(E{i}, 1), 1);
128
    Eg{i} = E{i}*Ri' + repmat(Ti', size(E{i}, 1), 1);
129
end
129
end
130
 
130
 
131
% show found markers in initial alignment
131
% show found markers in initial alignment
132
figure;
132
figure;
133
hold('on');
133
hold('on');
134
for i=1:nSubScans
134
for i=1:nSubScans
135
    % fix Ri to be orthogonal
135
    % fix Ri to be orthogonal
136
    [U,~,V] = svd(initialAlign(i).Rotation);
136
    [U,~,V] = svd(initialAlign(i).Rotation);
137
    Ri = U*V';
137
    Ri = U*V';
-
 
138
    initialAlign(i).Rotation = Ri;
138
    
139
    
139
    % bring point cloud into initial alignment
140
    % bring point cloud into initial alignment
140
    pcFileName = fullfile(alnFilePath, initialAlign(i).FileName);
141
    pcFileName = fullfile(alnFilePath, initialAlign(i).FileName);
141
    pc = pcread(pcFileName);
142
    pc = pcread(pcFileName);
142
    tform = affine3d([Ri' [0;0;0]; initialAlign(i).Translation' 1]);
143
    tform = affine3d([Ri' [0;0;0]; initialAlign(i).Translation' 1]);
143
    pcg = pctransform(pc, tform);
144
    pcg = pctransform(pc, tform);
144
   
145
   
145
    pcshow(pcg);
146
    pcshow(pcg);
146
    xlabel('x');
147
    xlabel('x');
147
    ylabel('y');
148
    ylabel('y');
148
    zlabel('z');
149
    zlabel('z');
149
    
150
    
150
    plot3(Eg{i}(:,1), Eg{i}(:,2), Eg{i}(:,3), '.', 'MarkerSize', 15);
151
    plot3(Eg{i}(:,1), Eg{i}(:,2), Eg{i}(:,3), '.', 'MarkerSize', 15);
151
    title('Initial Alignment');
152
    title('Initial Alignment');
152
end
153
end
153
 
154
 
154
% match markers between poses using initial alignment
155
% match markers between poses using initial alignment
155
Pg = {};
156
Pg = {};
156
P = {};
157
P = {};
157
for i=1:nSubScans
158
for i=1:nSubScans
158
    for j=1:size(Eg{i}, 1)
159
    for j=1:size(Eg{i}, 1)
159
        pg = Eg{i}(j,:);
160
        pg = Eg{i}(j,:);
160
        p = E{i}(j,:);
161
        p = E{i}(j,:);
161
        matched = false;
162
        matched = false;
162
        for k=1:size(Pg, 2)
163
        for k=1:size(Pg, 2)
163
            clusterCenter = mean(cat(1, Pg{:,k}), 1);
164
            clusterCenter = mean(cat(1, Pg{:,k}), 1);
164
            if(sum((pg - clusterCenter).^2) < 3^2)
165
            if(sum((pg - clusterCenter).^2) < 3^2)
165
                % store in global frame
166
                % store in global frame
166
                Pg{i,k} = pg;
167
                Pg{i,k} = pg;
167
                % store in local frame
168
                % store in local frame
168
                P{i,k} = p;
169
                P{i,k} = p;
169
                matched = true;
170
                matched = true;
170
                break;
171
                break;
171
            end
172
            end
172
        end
173
        end
173
        % create new cluster
174
        % create new cluster
174
        if(not(matched))
175
        if(not(matched))
175
            Pg{i,end+1} = pg;
176
            Pg{i,end+1} = pg;
176
            P{i,end+1} = p;
177
            P{i,end+1} = p;
177
        end 
178
        end 
178
    end
179
    end
179
end
180
end
180
 
181
 
181
% run optimization
182
% run optimization
182
alignment = groupwiseOrthogonalProcrustes(P, initialAlign);
183
alignment = groupwiseOrthogonalProcrustes(P, initialAlign);
183
 
184
 
184
% show found markers in optimized alignment
185
% show found markers in optimized alignment
185
figure;
186
figure;
186
hold('on');
187
hold('on');
187
for i=1:nSubScans
188
for i=1:nSubScans
188
    % fix Ri to be orthogonal
189
    % fix Ri to be orthogonal
189
    [U,~,V] = svd(alignment(i).Rotation);
190
    %[U,~,V] = svd(alignment(i).Rotation);
190
    Ri = U*V';
191
    %Ri = U*V';
-
 
192
    Ri = alignment(i).Rotation;
191
    Ti = alignment(i).Translation;
193
    Ti = alignment(i).Translation;
192
    
194
    
193
    Ea = E{i}*Ri' + repmat(Ti', size(E{i}, 1), 1);
195
    Ea = E{i}*Ri' + repmat(Ti', size(E{i}, 1), 1);
194
    
196
    
195
    % bring point cloud into optimized alignment
197
    % bring point cloud into optimized alignment
-
 
198
    pcFileName = fullfile(alnFilePath, initialAlign(i).FileName);
196
    pc = pcread(initialAlign(i).FileName);
199
    pc = pcread(pcFileName);
197
    tform = affine3d([Ri' [0;0;0]; initialAlign(i).Translation' 1]);
200
    tform = affine3d([Ri' [0;0;0]; initialAlign(i).Translation' 1]);
198
    pcg = pctransform(pc, tform);
201
    pcg = pctransform(pc, tform);
199
   
202
   
200
    pcshow(pcg);
203
    pcshow(pcg);
201
    xlabel('x');
204
    xlabel('x');
202
    ylabel('y');
205
    ylabel('y');
203
    zlabel('z');
206
    zlabel('z');
204
    
207
    
205
    plot3(Ea(:,1), Ea(:,2), Ea(:,3), '.', 'MarkerSize', 15);
208
    plot3(Ea(:,1), Ea(:,2), Ea(:,3), '.', 'MarkerSize', 15);
206
    title('Optimized Alignment');
209
    title('Optimized Alignment');
207
end
210
end
208
 
211
 
209
% write to ALN file
212
% write to ALN file
210
for i=1:length(alignment)
213
for i=1:length(alignment)
211
    alignment(i).FileName = initialAlign(i).FileName;
214
    alignment(i).FileName = initialAlign(i).FileName;
212
end
215
end
213
 
216
 
214
writeMeshLabALN(alignment, strrep(alnFileName, '.aln', 'Optimized.aln'));
217
writeMeshLabALN(alignment, strrep(alnFileName, '.aln', 'Optimized.aln'));
215
 
218
 
216
end
219
end
217
 
220
 
218
function e = autoDetectMarkers(frame, P, pointCloud)
221
function e = autoDetectMarkers(frame, P, pointCloud)
219
 
222
 
220
    % create mask based on morphology
223
    % create mask based on morphology
221
    g = rgb2gray(frame);
224
    g = rgb2gray(frame);
222
    % g(g>254) = 0;
225
    % g(g>254) = 0;
223
    % bw = imbinarize(g, 'adaptive', 'Sensitivity', 10^(-50));
226
    % bw = imbinarize(g, 'adaptive', 'Sensitivity', 10^(-50));
224
    bw = imbinarize(g, 0.10);
227
    bw = imbinarize(g, 0.10);
225
    cc = bwconncomp(bw);
228
    cc = bwconncomp(bw);
226
    rp = regionprops(cc, 'Area', 'Solidity', 'Eccentricity', 'Centroid');
229
    rp = regionprops(cc, 'Area', 'Solidity', 'Eccentricity', 'Centroid');
227
    idx = ([rp.Area] > 100 & [rp.Area] < 1000 & [rp.Solidity] > 0.9);
230
    idx = ([rp.Area] > 100 & [rp.Area] < 1000 & [rp.Solidity] > 0.9);
228
    
231
    
229
    e = cat(1, rp(idx).Centroid);
232
    e = cat(1, rp(idx).Centroid);
230
 
233
 
231
end
234
end
232
 
235
 
233
function e = manuallyDetectMarkers(frame, P, pointCloud)
236
function e = manuallyDetectMarkers(frame, P, pointCloud)
234
    
237
    
235
    e = [];
238
    e = [];
236
	%edges = edge(rgb2gray(frame), 'Canny', [0.08 0.1], 2);
239
	%edges = edge(rgb2gray(frame), 'Canny', [0.08 0.1], 2);
237
 
240
 
238
    figure; 
241
    figure; 
239
    hold('on');
242
    hold('on');
240
    imshow(frame);
243
    imshow(frame);
241
    title('Close figure to end.');
244
    title('Close figure to end.');
242
    set(gcf, 'pointer', 'crosshair'); 
245
    set(gcf, 'pointer', 'crosshair'); 
243
    set(gcf, 'WindowButtonDownFcn', @clickCallback);
246
    set(gcf, 'WindowButtonDownFcn', @clickCallback);
244
    
247
    
245
    uiwait;
248
    uiwait;
246
 
249
 
247
    function clickCallback(caller, ~)
250
    function clickCallback(caller, ~)
248
        
251
        
249
        p = get(gca, 'CurrentPoint'); 
252
        p = get(gca, 'CurrentPoint'); 
250
        p = p(1, 1:2);
253
        p = p(1, 1:2);
251
 
254
 
252
        e = [e; p(:, 1:2)];
255
        e = [e; p(:, 1:2)];
253
        
256
        
254
        if(not(isempty(el)))
257
        if(not(isempty(el)))
255
            figure(caller);
258
            figure(caller);
256
            hold('on');
259
            hold('on');
257
            plot(el(1), el(2), 'rx', 'MarkerSize', 15);
260
            plot(el(1), el(2), 'rx', 'MarkerSize', 15);
258
        end
261
        end
259
    end
262
    end
260
    
263
    
261
end
264
end
262
 
265
 
263
function [e, conf] = detectMarkersSubpix(frame, initGuesses, P, Q)
266
function [e, conf] = detectMarkersSubpix(frame, initGuesses, P, Q)
264
% Detect a marker in a single frame by rectifying to the image and
267
% Detect a marker in a single frame by rectifying to the image and
265
% performing image registration.
268
% performing image registration.
266
 
269
 
267
    % create mask based on morphology
270
    % create mask based on morphology
268
    g = rgb2gray(frame);
271
    g = rgb2gray(frame);
269
    g(g>254) = 0;
272
    g(g>254) = 0;
270
    bw = imbinarize(g);
273
    bw = imbinarize(g);
271
    cc = bwconncomp(bw);
274
    cc = bwconncomp(bw);
272
    labels = labelmatrix(cc);
275
    labels = labelmatrix(cc);
273
 
276
 
274
    % project point cloud into image
277
    % project point cloud into image
275
    q = [Q ones(size(Q,1),1)]*P;
278
    q = [Q ones(size(Q,1),1)]*P;
276
    q = q./[q(:,3) q(:,3) q(:,3)];
279
    q = q./[q(:,3) q(:,3) q(:,3)];
277
    
280
    
278
    e = zeros(size(initGuesses));
281
    e = zeros(size(initGuesses));
279
    conf = zeros(size(initGuesses, 1), 1);
282
    conf = zeros(size(initGuesses, 1), 1);
280
    
283
    
281
    nMarkersFound = 0;
284
    nMarkersFound = 0;
282
    
285
    
283
    for i=1:size(initGuesses, 1)
286
    for i=1:size(initGuesses, 1)
284
        
287
        
285
        labelId = labels(round(initGuesses(i,2)), round(initGuesses(i,1)));
288
        labelId = labels(round(initGuesses(i,2)), round(initGuesses(i,1)));
286
        labelMask = (labels == labelId);
289
        labelMask = (labels == labelId);
287
        labelMask = imdilate(labelMask, strel('disk', 3, 0));
290
        labelMask = imdilate(labelMask, strel('disk', 3, 0));
288
        
291
        
289
        if(sum(sum(labelMask)) < 10 || sum(sum(labelMask)) > 1000)
292
        if(sum(sum(labelMask)) < 10 || sum(sum(labelMask)) > 1000)
290
            continue;
293
            continue;
291
        end
294
        end
292
        
295
        
293
        % determine 3D points that are part of the marker
296
        % determine 3D points that are part of the marker
294
        % note: we should probably undistort labelMask
297
        % note: we should probably undistort labelMask
295
        pointMask = false(size(q, 1), 1);
298
        pointMask = false(size(q, 1), 1);
296
        for j=1:size(q,1)
299
        for j=1:size(q,1)
297
            if(round(q(j,2)) > size(labelMask, 1) || round(q(j,1)) > size(labelMask, 2) || round(q(j,2)) < 1 || round(q(j,1)) < 1)
300
            if(round(q(j,2)) > size(labelMask, 1) || round(q(j,1)) > size(labelMask, 2) || round(q(j,2)) < 1 || round(q(j,1)) < 1)
298
                continue;
301
                continue;
299
            end
302
            end
300
            
303
            
301
            if(labelMask(round(q(j,2)), round(q(j,1))))
304
            if(labelMask(round(q(j,2)), round(q(j,1))))
302
                pointMask(j) = true;
305
                pointMask(j) = true;
303
            end
306
            end
304
        end
307
        end
305
        
308
        
306
        if(sum(pointMask)) < 10
309
        if(sum(pointMask)) < 10
307
            continue;
310
            continue;
308
        end
311
        end
309
        
312
        
310
        % project 3D points onto local plane
313
        % project 3D points onto local plane
311
        [~, sc, ~] = pca(Q(pointMask, :));
314
        [~, sc, ~] = pca(Q(pointMask, :));
312
        Qlocal = sc(:, 1:2);
315
        Qlocal = sc(:, 1:2);
313
        
316
        
314
        % synthetic marker in high res. space
317
        % synthetic marker in high res. space
315
        m = zeros(151, 151);
318
        m = zeros(151, 151);
316
        [x, y] = meshgrid(1:151, 1:151);
319
        [x, y] = meshgrid(1:151, 1:151);
317
        m((x(:)-76).^2 + (y(:)-76).^2 <= 50^2) = 1.0;
320
        m((x(:)-76).^2 + (y(:)-76).^2 <= 50^2) = 1.0;
318
        
321
        
319
        % relation between marker space (px) and true marker/local plane(mm)
322
        % relation between marker space (px) and true marker/local plane(mm)
320
        % true marker diameter is 1.75mm
323
        % true marker diameter is 1.75mm
321
        mScale = 101/1.4; %px/mm
324
        mScale = 101/1.4; %px/mm
322
        mShift = 76; %px
325
        mShift = 76; %px
323
        
326
        
324
        % build homography from image to marker space
327
        % build homography from image to marker space
325
        H = fitgeotrans(q(pointMask, 1:2), mScale*Qlocal+mShift,  'projective');
328
        H = fitgeotrans(q(pointMask, 1:2), mScale*Qlocal+mShift,  'projective');
326
        %Hdlt = Hest_DLT([mScale*Qlocal+mShift, ones(size(Qlocal, 1), 1)]', q(pointMask,:)');
329
        %Hdlt = Hest_DLT([mScale*Qlocal+mShift, ones(size(Qlocal, 1), 1)]', q(pointMask,:)');
327
        %H = projective2d(Hdlt');
330
        %H = projective2d(Hdlt');
328
        
331
        
329
        % bring image of marker into marker space
332
        % bring image of marker into marker space
330
        imMarkerSpace = imwarp(frame, H, 'OutputView', imref2d(size(m)));
333
        imMarkerSpace = imwarp(frame, H, 'OutputView', imref2d(size(m)));
331
        imMarkerSpace = rgb2gray(im2double(imMarkerSpace));
334
        imMarkerSpace = rgb2gray(im2double(imMarkerSpace));
332
        
335
        
333
        %figure; imshowpair(imMarkerSpace, m);
336
        %figure; imshowpair(imMarkerSpace, m);
334
        
337
        
335
        % perform image registration
338
        % perform image registration
336
        % might be better off using subpixel image correlation
339
        % might be better off using subpixel image correlation
337
        [opt, met] = imregconfig('multimodal');
340
        [opt, met] = imregconfig('multimodal');
338
        T = imregtform(m, imMarkerSpace, 'translation', opt, met, 'DisplayOptimization', false);
341
        T = imregtform(m, imMarkerSpace, 'translation', opt, met, 'DisplayOptimization', false);
339
        
342
        
340
        rege = imwarp(m, T, 'OutputView', imref2d(size(m)));
343
        rege = imwarp(m, T, 'OutputView', imref2d(size(m)));
341
        %figure; imshowpair(imMarkerSpace, rege);
344
        %figure; imshowpair(imMarkerSpace, rege);
342
        
345
        
343
        % measure of correlation
346
        % measure of correlation
344
        confI = sum(sum(imMarkerSpace .* rege))/sqrt(sum(sum(imMarkerSpace) * sum(sum(rege))));
347
        confI = sum(sum(imMarkerSpace .* rege))/sqrt(sum(sum(imMarkerSpace) * sum(sum(rege))));
345
        %confI = 1.0;
348
        %confI = 1.0;
346
        
349
        
347
        if confI<0.4
350
        if confI<0.4
348
            continue;
351
            continue;
349
        end
352
        end
350
        
353
        
351
        fprintf('Found marker with confidence: %f\n', confI);
354
        fprintf('Found marker with confidence: %f\n', confI);
352
            
355
            
353
        % transform marker space coordinates (76,76) to frame space
356
        % transform marker space coordinates (76,76) to frame space
354
        el = T.transformPointsForward([76, 76]);
357
        el = T.transformPointsForward([76, 76]);
355
        el = H.transformPointsInverse(el);
358
        el = H.transformPointsInverse(el);
356
        
359
        
357
        nMarkersFound = nMarkersFound+1;
360
        nMarkersFound = nMarkersFound+1;
358
        e(nMarkersFound,:) = el;
361
        e(nMarkersFound,:) = el;
359
        conf(nMarkersFound) = confI;
362
        conf(nMarkersFound) = confI;
360
    end
363
    end
361
    
364
    
362
    e = e(1:nMarkersFound, :);
365
    e = e(1:nMarkersFound, :);
363
    conf = conf(1:nMarkersFound);
366
    conf = conf(1:nMarkersFound);
364
end
367
end
365
 
368
 
366
function [E, conf] = detectMarkersStereoSubpix(frame0, frame1, initGuesses, camStereoParams, pc)
369
function [E, conf] = detectMarkersStereoSubpix(frame0, frame1, initGuesses, camStereoParams, pc)
367
% Detect a marker in stereo frame set by minimizing the difference to
370
% Detect a marker in stereo frame set by minimizing the difference to
368
% projected images of 3d marker
371
% projected images of 3d marker
369
    
372
    
370
    normals = pcnormals(pc, 6);
373
    normals = pcnormals(pc, 6);
371
    
374
    
372
    frame0 = rgb2gray(frame0);
375
    frame0 = rgb2gray(frame0);
373
    frame1 = rgb2gray(frame1);
376
    frame1 = rgb2gray(frame1);
374
 
377
 
375
    nMarkers = size(initGuesses, 2);
378
    nMarkers = size(initGuesses, 1);
376
    
379
    
377
    E = zeros(nMarkers, 3);
380
    E = zeros(nMarkers, 3);
378
    conf = zeros(nMarkers, 1);
381
    conf = zeros(nMarkers, 1);
379
    
382
    
380
    for i=1:nMarkers
383
    for i=1:nMarkers
381
        
384
        
382
        pI = initGuesses(i,:);
385
        pI = initGuesses(i,:);
383
    	
386
    	
384
        e0 = camStereoParams.CameraParameters1.worldToImage(eye(3,3), zeros(3,1), pI);
387
        e0 = camStereoParams.CameraParameters1.worldToImage(eye(3,3), zeros(3,1), pI);
385
        e1 = camStereoParams.CameraParameters2.worldToImage(camStereoParams.RotationOfCamera2, camStereoParams.TranslationOfCamera2, pI);
388
        e1 = camStereoParams.CameraParameters2.worldToImage(camStereoParams.RotationOfCamera2, camStereoParams.TranslationOfCamera2, pI);
386
        
389
        
387
        % center of support region
390
        % center of support region
388
        e0Center = round(e0);
391
        e0Center = round(e0);
389
        e1Center = round(e1);
392
        e1Center = round(e1);
390
        
393
        
391
        % find initial normal
394
        % find initial normal
392
        idx = pc.findNearestNeighbors(pI, 1);
395
        idx = pc.findNearestNeighbors(pI, 1);
393
        nI = double(normals(idx, :));
396
        nI = double(normals(idx, :));
394
        
397
        
395
        margin = 25;
398
        margin = 25;
396
        
399
        
397
        [x,y] = meshgrid(e0Center(1)-margin:e0Center(1)+margin, e0Center(2)-margin:e0Center(2)+margin);
400
        [x,y] = meshgrid(e0Center(1)-margin:e0Center(1)+margin, e0Center(2)-margin:e0Center(2)+margin);
398
        e0ImCoords = [x(:), y(:)];
401
        e0ImCoords = [x(:), y(:)];
399
       
402
       
400
        [x,y] = meshgrid(e1Center(1)-margin:e1Center(1)+margin, e1Center(2)-margin:e1Center(2)+margin);
403
        [x,y] = meshgrid(e1Center(1)-margin:e1Center(1)+margin, e1Center(2)-margin:e1Center(2)+margin);
401
        e1ImCoords = [x(:), y(:)];
404
        e1ImCoords = [x(:), y(:)];
402
        
405
        
-
 
406
        nCoords = length(x(:));
-
 
407
        
403
        e0UndistImCoords = undistortPointsFast(e0ImCoords, camStereoParams.CameraParameters1);
408
        e0UndistImCoords = undistortPointsFast(e0ImCoords, camStereoParams.CameraParameters1);
404
        e0NormImCoords = camStereoParams.CameraParameters1.pointsToWorld(eye(3,3), [0, 0, 1], e0UndistImCoords);
409
        e0NormImCoords = camStereoParams.CameraParameters1.pointsToWorld(eye(3,3), [0, 0, 1], e0UndistImCoords);
-
 
410
        e0Hom = [e0NormImCoords, ones(nCoords, 1)];
405
        e1UndistImCoords = undistortPointsFast(e1ImCoords, camStereoParams.CameraParameters2);
411
        e1UndistImCoords = undistortPointsFast(e1ImCoords, camStereoParams.CameraParameters2);
406
        e1NormImCoords = camStereoParams.CameraParameters2.pointsToWorld(eye(3,3), [0, 0, 1], e1UndistImCoords);
412
        e1NormImCoords = camStereoParams.CameraParameters2.pointsToWorld(eye(3,3), [0, 0, 1], e1UndistImCoords);
-
 
413
        e1Hom = [e1NormImCoords, ones(nCoords, 1)];
407
 
414
 
-
 
415
        imVals0 = double(frame0(sub2ind(size(frame0), e0ImCoords(:,2), e0ImCoords(:,1))));
-
 
416
        imVals1 = double(frame1(sub2ind(size(frame1), e1ImCoords(:,2), e1ImCoords(:,1))));
-
 
417
                
408
        x0 = [pI nI(1:2)/nI(3) 70.0 70.0];
418
        x0 = [pI nI(1:2)/nI(3) 70.0 70.0];
409
        
419
        
410
        r = circleResiduals(x0);
420
%         r = circleResiduals(x0);
411
        figure; 
421
%         figure; 
412
        subplot(2,2,1);
422
%         subplot(2,2,1);
413
        imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
423
%         imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
414
        subplot(2,2,2);
424
%         subplot(2,2,2);
415
        imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
425
%         imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
416
        drawnow;
426
%         drawnow;
417
 
427
 
418
        options = optimset('Algorithm', 'levenberg-marquardt', 'Display', 'iter-detailed', 'OutputFcn', @out, 'MaxIter', 30, 'TolFun', 10^(-5), 'TolX', 0);
428
        %options = optimset('Algorithm', 'levenberg-marquardt', 'Display', 'final', 'OutputFcn', @out, 'MaxIter', 30, 'TolFun', 10^(-6), 'TolX', 0);
-
 
429
        options = optimoptions('lsqnonlin', 'Display', 'final');
-
 
430
        
419
        [x, conf(i), ~] = lsqnonlin(@circleResiduals, x0, [], [], options);
431
        [x, conf(i), ~] = lsqnonlin(@circleResiduals, x0, [], [], options);
420
        
432
        
421
        r = circleResiduals(x);
433
%         r = circleResiduals(x);
422
        subplot(2,2,3);
434
%         subplot(2,2,3);
423
        imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
435
%         imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
424
        subplot(2,2,4);
436
%         subplot(2,2,4);
425
        imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
437
%         imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
426
        drawnow;
438
%         drawnow;
427
        
439
        
428
        E(i,:) = x(1:3);
440
        E(i,:) = x(1:3);
429
 
441
 
430
    end
442
    end
431
    
443
    
432
    function stop = out(x, optimValues, state)
444
    function stop = out(x, optimValues, state)
433
        
445
        
434
%         r = optimValues.residual;
446
%         r = optimValues.residual;
435
%         
447
%         
436
%         figure; 
448
%         figure; 
437
%         subplot(1,2,1);
449
%         subplot(1,2,1);
438
%         imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
450
%         imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
439
%         subplot(1,2,2);
451
%         subplot(1,2,2);
440
%         imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
452
%         imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
441
%         drawnow;
453
%         drawnow;
442
%         
454
%         
443
%         display(x);
455
%         display(x);
444
        
456
        
445
        stop = false;
457
        stop = false;
446
    end
458
    end
447
 
459
 
448
    function r = circleResiduals(x)
460
    function r = circleResiduals(x)
449
        
461
        
450
        p0 = x(1:3);
462
        p0 = x(1:3);
451
        p1 = x(1:3) * camStereoParams.RotationOfCamera2 + camStereoParams.TranslationOfCamera2;
463
        p1 = x(1:3) * camStereoParams.RotationOfCamera2 + camStereoParams.TranslationOfCamera2;
452
        n0 = [x(4:5) 1.0];
464
        n0 = [x(4:5) 1.0];
453
        n0 = 1.0/norm(n0) * n0;
465
        n0 = 1.0/norm(n0) * n0;
454
        n1 = n0 * camStereoParams.RotationOfCamera2;
466
        n1 = n0 * camStereoParams.RotationOfCamera2;
455
        
467
        
456
        r = zeros(length(e0NormImCoords) + length(e1NormImCoords), 1);
468
        r = zeros(length(e0NormImCoords) + length(e1NormImCoords), 1);
457
        
469
        
458
        % norminal radius of markers
470
        % norminal radius of markers
459
        markerRadius = 1.4/2.0; %mm
471
        markerRadius = 1.4/2.0; %mm
460
        
472
        
461
        % half-width of ramp
473
        % half-width of ramp
462
        w = 0.3; %mm
474
        w = 0.1; %mm
-
 
475
      
-
 
476
        % camera 0
-
 
477
        t = (p0*n0')./(e0Hom*n0');
463
 
478
 
464
        p0n0 = (p0*n0');
479
        dVec = repmat(p0, nCoords, 1) - t.*e0Hom;
465
        p1n1 = (p1*n1');
-
 
466
        
-
 
467
        for k=1:length(e0NormImCoords)
480
        d = sqrt(sum(dVec.*dVec, 2));
468
 
481
 
469
            % dermine homogenous coordinate on the hypothesized plane
-
 
470
            t = p0n0/([e0NormImCoords(k,:), 1]*n0');
-
 
471
            
-
 
472
            d = norm(p0 - t*[e0NormImCoords(k,:), 1.0]);
-
 
473
            
-
 
474
            imVal = double(frame0(e0ImCoords(k,2), e0ImCoords(k,1)));
-
 
475
            
-
 
476
            % "saturated" ramp function for marker disc shape
482
        % "saturated" ramp function for marker disc shape
477
            weight = max(min(1.0, -1.0/(2*w)*(d-markerRadius)+0.5), 0.0);
483
        weights = max(min(1.0, -1.0/(2*w)*(d-markerRadius)+0.5), 0.0);
478
            
-
 
479
            r(k) = x(6)*weight - imVal;
-
 
480
            
-
 
481
        end
-
 
482
        
-
 
483
        for k=1:length(e1NormImCoords)
-
 
484
 
484
 
485
            % dermine z coordinate on the hypothesized plane
-
 
486
            t = p1n1/([e1NormImCoords(k,:), 1]*n1');
-
 
487
            
-
 
488
            d = norm(p1 - t*[e1NormImCoords(k,:), 1.0]);
485
        r(1:nCoords) = x(6)*weights - imVals0;
489
            
486
            
490
            imVal = double(frame1(e1ImCoords(k,2), e1ImCoords(k,1)));
-
 
491
            
487
        % camera 1
492
            % "saturated" ramp function for marker disc shape
-
 
493
            weight = max(min(1.0, -1.0/(2*w)*(d-markerRadius)+0.5), 0.0);
-
 
494
            
-
 
495
            r(length(e0NormImCoords)+k) = x(7)*weight - imVal;
488
        t = (p1*n1')./(e1Hom*n1');
496
 
489
 
497
        end
490
        dVec = repmat(p1, nCoords, 1) - t.*e1Hom;
498
        
491
        d = sqrt(sum(dVec.*dVec, 2));
-
 
492
 
-
 
493
        % "saturated" ramp function for marker disc shape
-
 
494
        weights = max(min(1.0, -1.0/(2*w)*(d-markerRadius)+0.5), 0.0);
-
 
495
 
-
 
496
        r(nCoords+1:end) = x(7)*weights - imVals1;
499
        
497
        
500
%         figure; 
498
%         figure; 
501
%         subplot(1,2,1);
499
%         subplot(1,2,1);
502
%         imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
500
%         imagesc(reshape(r(1:length(e0NormImCoords)), 2*margin+1, 2*margin+1), [-50 50]);
503
%         subplot(1,2,2);
501
%         subplot(1,2,2);
504
%         imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
502
%         imagesc(reshape(r(length(e0NormImCoords)+1:end), 2*margin+1, 2*margin+1), [-50 50]);
505
%         drawnow;
503
%         drawnow;
506
        
504
        
507
    end
505
    end
508
    
506
    
509
end
507
end
510
 
508
 
511
function E = projectOntoPointCloud(e, P, pointCloud)
509
function E = projectOntoPointCloud(e, P, pointCloud)
512
% Project 2d point coordinates onto pointCloud to find corresponding 3d
510
% Project 2d point coordinates onto pointCloud to find corresponding 3d
513
% point coordinates.
511
% point coordinates.
514
 
512
 
515
    q = [pointCloud ones(size(pointCloud,1),1)]*P;
513
    q = [pointCloud ones(size(pointCloud,1),1)]*P;
516
    q = q(:,1:2)./[q(:,3) q(:,3)];
514
    q = q(:,1:2)./[q(:,3) q(:,3)];
517
 
515
 
518
    E = nan(size(e,1), 3);
516
    E = nan(size(e,1), 3);
519
    
517
    
520
    for i=1:size(e, 1)
518
    for i=1:size(e, 1)
521
        sqDists = sum((q - repmat(e(i,:), size(q, 1), 1)).^2, 2);
519
        sqDists = sum((q - repmat(e(i,:), size(q, 1), 1)).^2, 2);
522
        
520
        
523
        [sqDistsSorted, sortIdx] = sort(sqDists);
521
        [sqDistsSorted, sortIdx] = sort(sqDists);
524
        
522
        
-
 
523
        % neighbors are points that project to within 10px
525
        neighbors = (sqDistsSorted < 4.0^2);
524
        neighbors = (sqDistsSorted < 10.0^2);
526
        
525
        
527
        distsSorted = sqrt(sqDistsSorted(neighbors));
526
        distsSorted = sqrt(sqDistsSorted(neighbors));
528
        invDistsSorted = 1.0/distsSorted;
527
        invDistsSorted = 1.0./distsSorted;
529
        sortIdx = sortIdx(neighbors);
528
        sortIdx = sortIdx(neighbors);
530
        
529
        
531
        nNeighbors = sum(neighbors);
530
        nNeighbors = sum(neighbors);
532
        
531
        
533
        if(nNeighbors >= 2)
532
        if(nNeighbors >= 2)
534
            E(i, :) = 0;
533
            E(i, :) = 0;
535
            for j=1:nNeighbors
534
            for j=1:nNeighbors
536
                E(i, :) = E(i, :) + invDistsSorted(j)/sum(invDistsSorted) * pointCloud(sortIdx(j), :);
535
                E(i, :) = E(i, :) + invDistsSorted(j)/sum(invDistsSorted) * pointCloud(sortIdx(j), :);
537
            end
536
            end
538
        end
537
        end
539
            
538
            
540
    end    
539
    end    
541
end
540
end
542
 
541
 
543
function H = Hest_DLT(q1, q2)
542
function H = Hest_DLT(q1, q2)
544
    % Estimate the homography between a set of point correspondences using the 
543
    % Estimate the homography between a set of point correspondences using the 
545
    % direct linear transform algorithm.
544
    % direct linear transform algorithm.
546
    %
545
    %
547
    % Input:
546
    % Input:
548
    %           q1: 3xN matrix of homogenous point coordinates from camera 1. 
547
    %           q1: 3xN matrix of homogenous point coordinates from camera 1. 
549
    %           q2: 3xN matrix of corresponding points from camera 2.
548
    %           q2: 3xN matrix of corresponding points from camera 2.
550
    % Output:
549
    % Output:
551
    %           H: 3x3 matrix. The Fundamental Matrix estimate. 
550
    %           H: 3x3 matrix. The Fundamental Matrix estimate. 
552
    %
551
    %
553
    % Note that N must be at least 4.
552
    % Note that N must be at least 4.
554
    % See derivation in Aanaes, Lecture Notes on Computer Vision, 2011
553
    % See derivation in Aanaes, Lecture Notes on Computer Vision, 2011
555
 
554
 
556
    % Normalize points
555
    % Normalize points
557
    [T1,invT1] = normalizationMat(q1);
556
    [T1,invT1] = normalizationMat(q1);
558
    q1_tilde = T1*q1;
557
    q1_tilde = T1*q1;
559
 
558
 
560
    T2 = normalizationMat(q2);
559
    T2 = normalizationMat(q2);
561
    q2_tilde = T2*q2;
560
    q2_tilde = T2*q2;
562
 
561
 
563
    % DLT estimation
562
    % DLT estimation
564
    N = size(q1_tilde,2);
563
    N = size(q1_tilde,2);
565
    assert(size(q2_tilde,2)==N);
564
    assert(size(q2_tilde,2)==N);
566
 
565
 
567
    B = zeros(3*N,9);
566
    B = zeros(3*N,9);
568
 
567
 
569
    for i=1:N
568
    for i=1:N
570
        q1i = q1_tilde(:,i);
569
        q1i = q1_tilde(:,i);
571
        q2i = q2_tilde(:,i);
570
        q2i = q2_tilde(:,i);
572
        q1_x = [0 -q1i(3) q1i(2); q1i(3) 0 -q1i(1); -q1i(2) q1i(1) 0];
571
        q1_x = [0 -q1i(3) q1i(2); q1i(3) 0 -q1i(1); -q1i(2) q1i(1) 0];
573
        biT = kron(q2i', q1_x); 
572
        biT = kron(q2i', q1_x); 
574
        B(3*(i-1)+1:3*i, :) = biT;
573
        B(3*(i-1)+1:3*i, :) = biT;
575
    end
574
    end
576
 
575
 
577
    [U,S,~] = svd(B');
576
    [U,S,~] = svd(B');
578
 
577
 
579
    [~,idx] = min(diag(S));
578
    [~,idx] = min(diag(S));
580
    h = U(:,idx);
579
    h = U(:,idx);
581
 
580
 
582
    H_tilde = reshape(h, 3, 3);
581
    H_tilde = reshape(h, 3, 3);
583
 
582
 
584
    % Unnormalize H
583
    % Unnormalize H
585
    H = invT1*H_tilde*T2;
584
    H = invT1*H_tilde*T2;
586
 
585
 
587
    % Arbitrarily chose scale
586
    % Arbitrarily chose scale
588
    H = H * 1/H(3,3);
587
    H = H * 1/H(3,3);
589
end
588
end
590
 
589
 
591
function [T,invT] = normalizationMat(q)
590
function [T,invT] = normalizationMat(q)
592
    % Gives a normalization matrix for homogeneous coordinates
591
    % Gives a normalization matrix for homogeneous coordinates
593
    % such that T*q will have zero mean and unit variance.
592
    % such that T*q will have zero mean and unit variance.
594
    % See Aanaes, Computer Vision Lecture Notes 2.8.2
593
    % See Aanaes, Computer Vision Lecture Notes 2.8.2
595
    %
594
    %
596
    % q: (M+1)xN matrix of N MD points in homogenous coordinates
595
    % q: (M+1)xN matrix of N MD points in homogenous coordinates
597
    %
596
    %
598
    % Extended to also efficiently compute the inverse matrix
597
    % Extended to also efficiently compute the inverse matrix
599
    % DTU, 2013, Jakob Wilm
598
    % DTU, 2013, Jakob Wilm
600
 
599
 
601
    [M,N] = size(q);
600
    [M,N] = size(q);
602
    M = M-1;
601
    M = M-1;
603
 
602
 
604
    mu = mean(q(1:M,:),2);
603
    mu = mean(q(1:M,:),2);
605
 
604
 
606
    q_bar = q(1:M,:)-repmat(mu,1,N);
605
    q_bar = q(1:M,:)-repmat(mu,1,N);
607
 
606
 
608
    s = mean(sqrt(diag(q_bar'*q_bar)))/sqrt(2);
607
    s = mean(sqrt(diag(q_bar'*q_bar)))/sqrt(2);
609
 
608
 
610
    T = [eye(M)/s, -mu/s; zeros(1,M) 1];
609
    T = [eye(M)/s, -mu/s; zeros(1,M) 1];
611
 
610
 
612
    invT = [eye(M)*s, mu; zeros(1,M) 1];
611
    invT = [eye(M)*s, mu; zeros(1,M) 1];
613
end
612
end
614
 
613
 
615

Generated by GNU Enscript 1.6.6.
614

Generated by GNU Enscript 1.6.6.
616
 
615
 
617
 
616
 
618
 
617