Subversion Repositories seema-scanner

Rev

Rev 245 | 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++){
255 - 113
        if(frames0[i].depth() == CV_8U)
114
            cv::cvtColor(frames0[i], frames0Gray[i], CV_BayerBG2GRAY);
115
        else if(frames0[i].channels() == 3)
116
            cv::cvtColor(frames0[i], frames0Gray[i], CV_RGB2GRAY);
117
        else
118
            frames0Gray[i] = frames0[i];
119
        if(frames1[i].depth() == CV_8U)
120
            cv::cvtColor(frames1[i], frames1Gray[i], CV_BayerBG2GRAY);
121
        else if(frames1[i].channels() == 3)
122
            cv::cvtColor(frames1[i], frames1Gray[i], CV_RGB2GRAY);
123
        else
124
            frames1Gray[i] = frames1[i];
200 jakw 125
    }
126
 
127
    // Decode camera0 (horizontal)
128
    std::vector<cv::Mat> frames0PrimaryHorz(frames0Gray.begin()+2, frames0Gray.begin()+2+nStepsPrimary);
129
    cv::Mat up0Primary, amplitude0PrimaryHorz, energy0PrimaryHorz;
130
    decodePhaseShiftingSequence(frames0PrimaryHorz, up0Primary, amplitude0PrimaryHorz, energy0PrimaryHorz);
131
 
132
    std::vector<cv::Mat> frames0SecondaryHorz(frames0Gray.begin()+2+nStepsPrimary, frames0Gray.end());
133
    cv::Mat up0Secondary, amplitude0SecondaryHorz, energy0SecondaryHorz;
134
    decodePhaseShiftingSequence(frames0SecondaryHorz, up0Secondary, amplitude0SecondaryHorz, energy0SecondaryHorz);
135
 
136
    cv::Mat up0;
137
    unwrapHeterodynePhases(up0Primary, up0Secondary, up0);
138
    up0 *= screenCols/(2.0*CV_PI);
139
 
140
    #ifdef QT_DEBUG
141
        cvtools::writeMat(up0, "up0.mat", "up0");
142
    #endif
143
 
144
    // Decode camera0 (vertical)
145
    std::vector<cv::Mat> frames0PrimaryVert(frames0Gray.begin()+2+nStepsPrimary+nStepsSecondary, frames0Gray.end()-nStepsSecondary);
146
    cv::Mat vp0Primary, amplitude0PrimaryVert, energy0PrimaryVert;
147
    decodePhaseShiftingSequence(frames0PrimaryVert, vp0Primary, amplitude0PrimaryVert, energy0PrimaryVert);
148
 
149
    std::vector<cv::Mat> frames0SecondaryVert(frames0Gray.end()-nStepsSecondary, frames0Gray.end());
150
    cv::Mat vp0Secondary, amplitude0SecondaryVert, energy0SecondaryVert;
151
    decodePhaseShiftingSequence(frames0SecondaryVert, vp0Secondary, amplitude0SecondaryVert, energy0SecondaryVert);
152
 
153
    cv::Mat vp0;
154
    unwrapHeterodynePhases(vp0Primary, vp0Secondary, vp0);
155
    vp0 *= screenCols/(2.0*CV_PI);
156
 
157
    #ifdef QT_DEBUG
158
        cvtools::writeMat(vp0, "vp0.mat", "vp0");
159
    #endif
160
 
161
 
162
    // Decode camera1 (horizontal)
163
    std::vector<cv::Mat> frames1PrimaryHorz(frames1Gray.begin()+2, frames1Gray.begin()+2+nStepsPrimary);
164
    cv::Mat up1Primary, amplitude1PrimaryHorz, energy1PrimaryHorz;
165
    decodePhaseShiftingSequence(frames1PrimaryHorz, up1Primary, amplitude1PrimaryHorz, energy1PrimaryHorz);
166
 
167
    std::vector<cv::Mat> frames1SecondaryHorz(frames1Gray.begin()+2+nStepsPrimary, frames1Gray.end());
168
    cv::Mat up1Secondary, amplitude1SecondaryHorz, energy1SecondaryHorz;
169
    decodePhaseShiftingSequence(frames1SecondaryHorz, up1Secondary, amplitude1SecondaryHorz, energy1SecondaryHorz);
170
 
171
    cv::Mat up1;
172
    unwrapHeterodynePhases(up1Primary, up1Secondary, up1);
173
    up1 *= screenCols/(2.0*CV_PI);
174
 
175
    #ifdef QT_DEBUG
176
        cvtools::writeMat(up1, "up1.mat", "up1");
177
    #endif
178
 
179
    // Decode camera1 (vertical)
180
    std::vector<cv::Mat> frames1PrimaryVert(frames1Gray.begin()+2+nStepsPrimary+nStepsSecondary, frames1Gray.end()-nStepsSecondary);
181
    cv::Mat vp1Primary, amplitude1PrimaryVert, energy1PrimaryVert;
182
    decodePhaseShiftingSequence(frames1PrimaryVert, vp1Primary, amplitude1PrimaryVert, energy1PrimaryVert);
183
 
184
    std::vector<cv::Mat> frames1SecondaryVert(frames1Gray.end()-nStepsSecondary, frames1Gray.end());
185
    cv::Mat vp1Secondary, amplitude1SecondaryVert, energy1SecondaryVert;
186
    decodePhaseShiftingSequence(frames1SecondaryVert, vp1Secondary, amplitude1SecondaryVert, energy1SecondaryVert);
187
 
188
    cv::Mat vp1;
189
    unwrapHeterodynePhases(vp1Primary, vp1Secondary, vp1);
190
    vp1 *= screenCols/(2.0*CV_PI);
191
 
192
    #ifdef QT_DEBUG
193
        cvtools::writeMat(vp1, "vp1.mat", "vp1");
194
    #endif
195
 
196
    // color debayer and remap
197
    cv::Mat color0, color1;
255 - 198
    if(frames0[0].depth() == CV_8U)
199
        cv::cvtColor(frames0[0], color0, CV_BayerBG2RGB);
200
    else
201
        color0 = frames0[0];
202
    if(frames1[0].depth() == CV_8U)
203
        cv::cvtColor(frames1[0], color1, CV_BayerBG2GRAY);
204
    else
205
        color1 = frames1[0];
200 jakw 206
 
207
    #ifdef QT_DEBUG
208
        cvtools::writeMat(color0, "color0.mat", "color0");
209
        cvtools::writeMat(color1, "color1.mat", "color1");
210
    #endif
211
 
212
    // Occlusion masks
213
    cv::Mat occlusion0, occlusion1;
214
    cv::subtract(frames0Gray[0], frames0Gray[1], occlusion0);
215
    occlusion0 = (occlusion0 > 25) & (occlusion0 < 250);
216
    cv::subtract(frames1Gray[0], frames1Gray[1], occlusion1);
217
    occlusion1 = (occlusion1 > 25) & (occlusion1 < 250);
218
 
219
    // Threshold on energy at primary frequency
220
    occlusion0 = occlusion0 & (amplitude0PrimaryHorz > 5.0*nStepsPrimary);
221
    occlusion1 = occlusion1 & (amplitude1PrimaryHorz > 5.0*nStepsPrimary);
222
 
223
    // Threshold on energy ratios
224
    occlusion0 = occlusion0 & (amplitude0PrimaryHorz > 0.85*energy1PrimaryHorz);
225
    occlusion0 = occlusion0 & (amplitude0SecondaryHorz > 0.85*energy1SecondaryHorz);
226
 
227
    occlusion1 = occlusion1 & (amplitude1PrimaryHorz > 0.85*energy1PrimaryHorz);
228
    occlusion1 = occlusion1 & (amplitude1SecondaryHorz > 0.85*energy1SecondaryHorz);
229
 
230
//    // Erode occlusion masks
231
//    cv::Mat strel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5,5));
232
//    cv::erode(occlusion0, occlusion0, strel);
233
//    cv::erode(occlusion1, occlusion1, strel);
234
 
235
    // Threshold on gradient of phase
236
    cv::Mat edges0;
237
    cv::Sobel(up0, edges0, -1, 1, 1, 5);
238
    occlusion0 = occlusion0 & (abs(edges0) < 10);
239
    cv::Sobel(vp0, edges0, -1, 1, 1, 5);
240
    occlusion0 = occlusion0 & (abs(edges0) < 10);
241
 
242
    cv::Mat edges1;
243
    cv::Sobel(up1, edges1, -1, 1, 1, 5);
244
    occlusion1 = occlusion1 & (abs(edges1) < 10);
245
    cv::Sobel(vp1, edges1, -1, 1, 1, 5);
246
    occlusion1 = occlusion1 & (abs(edges1) < 10);
247
 
248
    #ifdef QT_DEBUG
249
        cvtools::writeMat(edges0, "edges0.mat", "edges0");
250
        cvtools::writeMat(edges1, "edges1.mat", "edges1");
251
        cvtools::writeMat(occlusion0, "occlusion0.mat", "occlusion0");
252
        cvtools::writeMat(occlusion1, "occlusion1.mat", "occlusion1");
253
    #endif
254
 
255
    // Match phase maps
256
 
257
    // camera0 against camera1
258
    std::vector<cv::Vec2f> q0, q1;
259
    for(int row=0; row<frameRows; row++){
260
        for(int col=0; col<frameCols; col++){
261
 
262
            if(!occlusion0.at<char>(row,col))
263
                continue;
264
 
265
            float up0i = up0.at<float>(row,col);
266
            float vp0i = vp0.at<float>(row,col);
267
 
268
            for(int row1=0; row1<vp1.rows-1; row1++){
269
                for(int col1=0; col1<up1.cols-1; col1++){
270
 
271
                    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))
272
                        continue;
273
 
274
                    float up1Left = up1.at<float>(row1,col1);
275
                    float up1Right = up1.at<float>(row1,col1+1);
276
                    float vp1Up = vp1.at<float>(row1,col1);
277
                    float vp1Down = vp1.at<float>(row1+1,col1);
278
 
279
                    if((up1Left <= up0i) && (up0i <= up1Right) && (up0i-up1Left < 1.0) && (up1Right-up0i < 1.0) && (up1Right-up1Left > 0.1) &&
280
                       (vp1Up <= vp0i) && (vp0i <= vp1Down) && (vp0i-vp1Up < 1.0) && (vp1Down-vp0i < 1.0) && (vp1Down-vp1Up > 0.1)){
281
 
282
                        float col1i = col1 + (up0i-up1Left)/(up1Right-up1Left);
283
                        float row1i = row1 + (vp0i-vp1Up)/(vp1Up-vp1Down);
284
 
285
                        q0.push_back(cv::Point2f(col, row));
286
                        q1.push_back(cv::Point2f(col1i, row1i));
287
 
288
                        break;
289
                    }
290
                }
291
            }
292
        }
293
    }
294
 
295
 
296
    int nMatches = q0.size();
297
 
298
    if(nMatches < 1){
299
        Q.resize(0);
300
        color.resize(0);
301
 
302
        return;
303
    }
304
 
305
    // Retrieve color information
306
    color.resize(nMatches);
307
    for(int i=0; i<nMatches; i++){
308
 
309
        cv::Vec3b c0 = color0.at<cv::Vec3b>(q0[i][1], q0[i][0]);
310
        cv::Vec3b c1 = color1.at<cv::Vec3b>(q1[i][1], q1[i][0]);
311
 
312
        color[i] = 0.5*c0 + 0.5*c1;
313
    }
314
 
315
    // Correct for lens distortion
316
    cv::undistortPoints(q0, q0, calibration.K0, calibration.k0, cv::noArray(), calibration.K0);
317
    cv::undistortPoints(q1, q1, calibration.K1, calibration.k1, cv::noArray(), calibration.K1);
318
    cv::correctMatches(calibration.F, q0, q1, q0, q1);
319
 
320
    // Triangulate points
321
    cv::Mat P0(3, 4, CV_32F, cv::Scalar(0.0));
322
    cv::Mat(calibration.K0).copyTo(P0.colRange(0, 3));
323
 
324
    cv::Mat P1(3, 4, CV_32F), temp(3,4,CV_32F);
325
    cv::Mat(calibration.R1).copyTo(temp(cv::Range(0,3), cv::Range(0,3)));
326
    cv::Mat(calibration.T1).copyTo(temp(cv::Range(0,3), cv::Range(3,4)));
327
    P1 = cv::Mat(calibration.K1) * temp;
328
 
329
    cv::Mat QMatHomogenous, QMat;
330
    cv::triangulatePoints(P0, P1, q0, q1, QMatHomogenous);
331
 
332
    cvtools::convertMatFromHomogeneous(QMatHomogenous, QMat);
333
    cvtools::matToPoints3f(QMat, Q);
334
 
335
}