Subversion Repositories seema-scanner

Rev

Rev 208 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
200 jakw 1
//
2
// Two Frequency Phase Shifting using the Heterodyne Principle with horizontal and vertical encoding.
3
//
4
// This implementation is based on "AlgorithmPhaseShiftTwoFreq", but uses horizontal and vertial fringes, which adds some encoding redundancy,
5
// but avoids interpolation effects from rectifying homographies.
6
 
7
#include "AlgorithmPhaseShiftTwoFreqHorzVert.h"
8
#include <math.h>
9
 
10
#include "cvtools.h"
11
#include "algorithmtools.h"
12
 
13
static unsigned int nStepsPrimary = 16; // number of shifts/steps in primary
14
static unsigned int nStepsSecondary = 8; // number of shifts/steps in secondary
15
static float nPeriodsPrimary = 40; // primary period
16
 
17
AlgorithmPhaseShiftTwoFreqHorzVert::AlgorithmPhaseShiftTwoFreqHorzVert(unsigned int _screenCols, unsigned int _screenRows) : Algorithm(_screenCols, _screenRows){
18
 
19
    // Set N
20
    N = 2+2*(nStepsPrimary+nStepsSecondary);
21
 
22
    // Determine the secondary (wider) period to fulfill the heterodyne condition
23
    float nPeriodsSecondary = nPeriodsPrimary + 1;
24
 
25
    // all on pattern
26
    cv::Mat allOn(1, screenCols, CV_8UC3, cv::Scalar::all(255));
27
    patterns.push_back(allOn);
28
 
29
    // all off pattern
30
    cv::Mat allOff(1, screenCols, CV_8UC3, cv::Scalar::all(0));
31
    patterns.push_back(allOff);
32
 
33
    // Primary encoding patterns (horizontal)
34
    for(unsigned int i=0; i<nStepsPrimary; i++){
35
        float phase = 2.0*CV_PI/nStepsPrimary * i;
36
        float pitch = screenCols/nPeriodsPrimary;
37
        cv::Mat patternI(1,1,CV_8U);
38
        patternI = computePhaseVector(screenCols, phase, pitch);
39
        patterns.push_back(patternI.t());
40
    }
41
 
42
    // Secondary encoding patterns (horizontal)
43
    for(unsigned int i=0; i<nStepsSecondary; i++){
44
        float phase = 2.0*CV_PI/nStepsSecondary * i;
45
        float pitch = screenCols/nPeriodsSecondary;
46
        cv::Mat patternI(1,1,CV_8U);
47
        patternI = computePhaseVector(screenCols, phase, pitch);
48
        patterns.push_back(patternI.t());
49
    }
50
 
51
    // Primary encoding patterns (vertical)
52
    for(unsigned int i=0; i<nStepsPrimary; i++){
53
        float phase = 2.0*CV_PI/nStepsPrimary * i;
54
        float pitch = screenRows/nPeriodsPrimary;
55
        cv::Mat patternI(1,1,CV_8U);
56
        patternI = computePhaseVector(screenRows, phase, pitch);
57
        patterns.push_back(patternI);
58
    }
59
 
60
    // Secondary encoding patterns (vertical)
61
    for(unsigned int i=0; i<nStepsSecondary; i++){
62
        float phase = 2.0*CV_PI/nStepsSecondary * i;
63
        float pitch = screenRows/nPeriodsSecondary;
64
        cv::Mat patternI(1,1,CV_8U);
65
        patternI = computePhaseVector(screenRows, phase, pitch);
66
        patterns.push_back(patternI);
67
    }
68
 
69
}
70
 
71
cv::Mat AlgorithmPhaseShiftTwoFreqHorzVert::getEncodingPattern(unsigned int depth){
72
    return patterns[depth];
73
}
74
 
75
static void decodePhaseShiftingSequence(const std::vector<cv::Mat> &frames, cv::Mat &up, cv::Mat &amplitude, cv::Mat &energy){
76
 
77
    std::vector<cv::Mat> F = getDFTComponents(frames);
78
    cv::phase(F[2], -F[3], up);
79
 
80
    // Collected signal energy at higher frequencies
81
    energy.create(frames[0].rows, frames[0].cols, CV_32F);
82
    energy.setTo(0.0);
83
    for(unsigned int i=0; i<frames.size()-1; i++){
84
        cv::Mat magnitude;
85
        cv::magnitude(F[i*2 + 2], F[i*2 + 3], magnitude);
86
        cv::add(energy, magnitude, energy, cv::noArray(), CV_32F);
87
    }
88
 
89
    cv::magnitude(F[2], -F[3], amplitude);
90
}
91
 
92
static void unwrapHeterodynePhases(const cv::Mat &upPrimary, const cv::Mat &upSecondary, cv::Mat &up){
93
 
94
    // Determine equivalent phase
95
    cv::Mat upEquivalent = upSecondary - upPrimary;
96
    upEquivalent = cvtools::modulo(upEquivalent, 2.0*CV_PI);
97
    up = unwrapWithCue(upPrimary, upEquivalent, nPeriodsPrimary);
98
}
99
 
100
 
245 jakw 101
void AlgorithmPhaseShiftTwoFreqHorzVert::get3DPoints(const SMCalibrationParameters &calibration, const std::vector<cv::Mat>& frames0, const std::vector<cv::Mat>& frames1, std::vector<cv::Point3f>& Q, std::vector<cv::Vec3f> &color){
200 jakw 102
 
103
    assert(frames0.size() == N);
104
    assert(frames1.size() == N);
105
 
106
    int frameRows = frames0[0].rows;
107
    int frameCols = frames0[0].cols;
108
 
109
    // Gray-scale
110
    std::vector<cv::Mat> frames0Gray(N);
111
    std::vector<cv::Mat> frames1Gray(N);
112
    for(unsigned int i=0; i<N; i++){
113
        cv::cvtColor(frames0[i], frames0Gray[i], CV_BayerBG2GRAY);
114
        cv::cvtColor(frames1[i], frames1Gray[i], CV_BayerBG2GRAY);
115
    }
116
 
117
    // Decode camera0 (horizontal)
118
    std::vector<cv::Mat> frames0PrimaryHorz(frames0Gray.begin()+2, frames0Gray.begin()+2+nStepsPrimary);
119
    cv::Mat up0Primary, amplitude0PrimaryHorz, energy0PrimaryHorz;
120
    decodePhaseShiftingSequence(frames0PrimaryHorz, up0Primary, amplitude0PrimaryHorz, energy0PrimaryHorz);
121
 
122
    std::vector<cv::Mat> frames0SecondaryHorz(frames0Gray.begin()+2+nStepsPrimary, frames0Gray.end());
123
    cv::Mat up0Secondary, amplitude0SecondaryHorz, energy0SecondaryHorz;
124
    decodePhaseShiftingSequence(frames0SecondaryHorz, up0Secondary, amplitude0SecondaryHorz, energy0SecondaryHorz);
125
 
126
    cv::Mat up0;
127
    unwrapHeterodynePhases(up0Primary, up0Secondary, up0);
128
    up0 *= screenCols/(2.0*CV_PI);
129
 
130
    #ifdef QT_DEBUG
131
        cvtools::writeMat(up0, "up0.mat", "up0");
132
    #endif
133
 
134
    // Decode camera0 (vertical)
135
    std::vector<cv::Mat> frames0PrimaryVert(frames0Gray.begin()+2+nStepsPrimary+nStepsSecondary, frames0Gray.end()-nStepsSecondary);
136
    cv::Mat vp0Primary, amplitude0PrimaryVert, energy0PrimaryVert;
137
    decodePhaseShiftingSequence(frames0PrimaryVert, vp0Primary, amplitude0PrimaryVert, energy0PrimaryVert);
138
 
139
    std::vector<cv::Mat> frames0SecondaryVert(frames0Gray.end()-nStepsSecondary, frames0Gray.end());
140
    cv::Mat vp0Secondary, amplitude0SecondaryVert, energy0SecondaryVert;
141
    decodePhaseShiftingSequence(frames0SecondaryVert, vp0Secondary, amplitude0SecondaryVert, energy0SecondaryVert);
142
 
143
    cv::Mat vp0;
144
    unwrapHeterodynePhases(vp0Primary, vp0Secondary, vp0);
145
    vp0 *= screenCols/(2.0*CV_PI);
146
 
147
    #ifdef QT_DEBUG
148
        cvtools::writeMat(vp0, "vp0.mat", "vp0");
149
    #endif
150
 
151
 
152
    // Decode camera1 (horizontal)
153
    std::vector<cv::Mat> frames1PrimaryHorz(frames1Gray.begin()+2, frames1Gray.begin()+2+nStepsPrimary);
154
    cv::Mat up1Primary, amplitude1PrimaryHorz, energy1PrimaryHorz;
155
    decodePhaseShiftingSequence(frames1PrimaryHorz, up1Primary, amplitude1PrimaryHorz, energy1PrimaryHorz);
156
 
157
    std::vector<cv::Mat> frames1SecondaryHorz(frames1Gray.begin()+2+nStepsPrimary, frames1Gray.end());
158
    cv::Mat up1Secondary, amplitude1SecondaryHorz, energy1SecondaryHorz;
159
    decodePhaseShiftingSequence(frames1SecondaryHorz, up1Secondary, amplitude1SecondaryHorz, energy1SecondaryHorz);
160
 
161
    cv::Mat up1;
162
    unwrapHeterodynePhases(up1Primary, up1Secondary, up1);
163
    up1 *= screenCols/(2.0*CV_PI);
164
 
165
    #ifdef QT_DEBUG
166
        cvtools::writeMat(up1, "up1.mat", "up1");
167
    #endif
168
 
169
    // Decode camera1 (vertical)
170
    std::vector<cv::Mat> frames1PrimaryVert(frames1Gray.begin()+2+nStepsPrimary+nStepsSecondary, frames1Gray.end()-nStepsSecondary);
171
    cv::Mat vp1Primary, amplitude1PrimaryVert, energy1PrimaryVert;
172
    decodePhaseShiftingSequence(frames1PrimaryVert, vp1Primary, amplitude1PrimaryVert, energy1PrimaryVert);
173
 
174
    std::vector<cv::Mat> frames1SecondaryVert(frames1Gray.end()-nStepsSecondary, frames1Gray.end());
175
    cv::Mat vp1Secondary, amplitude1SecondaryVert, energy1SecondaryVert;
176
    decodePhaseShiftingSequence(frames1SecondaryVert, vp1Secondary, amplitude1SecondaryVert, energy1SecondaryVert);
177
 
178
    cv::Mat vp1;
179
    unwrapHeterodynePhases(vp1Primary, vp1Secondary, vp1);
180
    vp1 *= screenCols/(2.0*CV_PI);
181
 
182
    #ifdef QT_DEBUG
183
        cvtools::writeMat(vp1, "vp1.mat", "vp1");
184
    #endif
185
 
186
    // color debayer and remap
187
    cv::Mat color0, color1;
188
    cv::cvtColor(frames0[0], color0, CV_BayerBG2RGB);
189
    cv::cvtColor(frames1[0], color1, CV_BayerBG2RGB);
190
 
191
    #ifdef QT_DEBUG
192
        cvtools::writeMat(color0, "color0.mat", "color0");
193
        cvtools::writeMat(color1, "color1.mat", "color1");
194
    #endif
195
 
196
    // Occlusion masks
197
    cv::Mat occlusion0, occlusion1;
198
    cv::subtract(frames0Gray[0], frames0Gray[1], occlusion0);
199
    occlusion0 = (occlusion0 > 25) & (occlusion0 < 250);
200
    cv::subtract(frames1Gray[0], frames1Gray[1], occlusion1);
201
    occlusion1 = (occlusion1 > 25) & (occlusion1 < 250);
202
 
203
    // Threshold on energy at primary frequency
204
    occlusion0 = occlusion0 & (amplitude0PrimaryHorz > 5.0*nStepsPrimary);
205
    occlusion1 = occlusion1 & (amplitude1PrimaryHorz > 5.0*nStepsPrimary);
206
 
207
    // Threshold on energy ratios
208
    occlusion0 = occlusion0 & (amplitude0PrimaryHorz > 0.85*energy1PrimaryHorz);
209
    occlusion0 = occlusion0 & (amplitude0SecondaryHorz > 0.85*energy1SecondaryHorz);
210
 
211
    occlusion1 = occlusion1 & (amplitude1PrimaryHorz > 0.85*energy1PrimaryHorz);
212
    occlusion1 = occlusion1 & (amplitude1SecondaryHorz > 0.85*energy1SecondaryHorz);
213
 
214
//    // Erode occlusion masks
215
//    cv::Mat strel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5,5));
216
//    cv::erode(occlusion0, occlusion0, strel);
217
//    cv::erode(occlusion1, occlusion1, strel);
218
 
219
    // Threshold on gradient of phase
220
    cv::Mat edges0;
221
    cv::Sobel(up0, edges0, -1, 1, 1, 5);
222
    occlusion0 = occlusion0 & (abs(edges0) < 10);
223
    cv::Sobel(vp0, edges0, -1, 1, 1, 5);
224
    occlusion0 = occlusion0 & (abs(edges0) < 10);
225
 
226
    cv::Mat edges1;
227
    cv::Sobel(up1, edges1, -1, 1, 1, 5);
228
    occlusion1 = occlusion1 & (abs(edges1) < 10);
229
    cv::Sobel(vp1, edges1, -1, 1, 1, 5);
230
    occlusion1 = occlusion1 & (abs(edges1) < 10);
231
 
232
    #ifdef QT_DEBUG
233
        cvtools::writeMat(edges0, "edges0.mat", "edges0");
234
        cvtools::writeMat(edges1, "edges1.mat", "edges1");
235
        cvtools::writeMat(occlusion0, "occlusion0.mat", "occlusion0");
236
        cvtools::writeMat(occlusion1, "occlusion1.mat", "occlusion1");
237
    #endif
238
 
239
    // Match phase maps
240
 
241
    // camera0 against camera1
242
    std::vector<cv::Vec2f> q0, q1;
243
    for(int row=0; row<frameRows; row++){
244
        for(int col=0; col<frameCols; col++){
245
 
246
            if(!occlusion0.at<char>(row,col))
247
                continue;
248
 
249
            float up0i = up0.at<float>(row,col);
250
            float vp0i = vp0.at<float>(row,col);
251
 
252
            for(int row1=0; row1<vp1.rows-1; row1++){
253
                for(int col1=0; col1<up1.cols-1; col1++){
254
 
255
                    if(!occlusion1.at<char>(row1,col1) || !occlusion1.at<char>(row1,col1+1) || !occlusion1.at<char>(row1+1,col1) || !occlusion1.at<char>(row1+1,col1+1))
256
                        continue;
257
 
258
                    float up1Left = up1.at<float>(row1,col1);
259
                    float up1Right = up1.at<float>(row1,col1+1);
260
                    float vp1Up = vp1.at<float>(row1,col1);
261
                    float vp1Down = vp1.at<float>(row1+1,col1);
262
 
263
                    if((up1Left <= up0i) && (up0i <= up1Right) && (up0i-up1Left < 1.0) && (up1Right-up0i < 1.0) && (up1Right-up1Left > 0.1) &&
264
                       (vp1Up <= vp0i) && (vp0i <= vp1Down) && (vp0i-vp1Up < 1.0) && (vp1Down-vp0i < 1.0) && (vp1Down-vp1Up > 0.1)){
265
 
266
                        float col1i = col1 + (up0i-up1Left)/(up1Right-up1Left);
267
                        float row1i = row1 + (vp0i-vp1Up)/(vp1Up-vp1Down);
268
 
269
                        q0.push_back(cv::Point2f(col, row));
270
                        q1.push_back(cv::Point2f(col1i, row1i));
271
 
272
                        break;
273
                    }
274
                }
275
            }
276
        }
277
    }
278
 
279
 
280
    int nMatches = q0.size();
281
 
282
    if(nMatches < 1){
283
        Q.resize(0);
284
        color.resize(0);
285
 
286
        return;
287
    }
288
 
289
    // Retrieve color information
290
    color.resize(nMatches);
291
    for(int i=0; i<nMatches; i++){
292
 
293
        cv::Vec3b c0 = color0.at<cv::Vec3b>(q0[i][1], q0[i][0]);
294
        cv::Vec3b c1 = color1.at<cv::Vec3b>(q1[i][1], q1[i][0]);
295
 
296
        color[i] = 0.5*c0 + 0.5*c1;
297
    }
298
 
299
    // Correct for lens distortion
300
    cv::undistortPoints(q0, q0, calibration.K0, calibration.k0, cv::noArray(), calibration.K0);
301
    cv::undistortPoints(q1, q1, calibration.K1, calibration.k1, cv::noArray(), calibration.K1);
302
    cv::correctMatches(calibration.F, q0, q1, q0, q1);
303
 
304
    // Triangulate points
305
    cv::Mat P0(3, 4, CV_32F, cv::Scalar(0.0));
306
    cv::Mat(calibration.K0).copyTo(P0.colRange(0, 3));
307
 
308
    cv::Mat P1(3, 4, CV_32F), temp(3,4,CV_32F);
309
    cv::Mat(calibration.R1).copyTo(temp(cv::Range(0,3), cv::Range(0,3)));
310
    cv::Mat(calibration.T1).copyTo(temp(cv::Range(0,3), cv::Range(3,4)));
311
    P1 = cv::Mat(calibration.K1) * temp;
312
 
313
    cv::Mat QMatHomogenous, QMat;
314
    cv::triangulatePoints(P0, P1, q0, q1, QMatHomogenous);
315
 
316
    cvtools::convertMatFromHomogeneous(QMatHomogenous, QMat);
317
    cvtools::matToPoints3f(QMat, Q);
318
 
319
}