Subversion Repositories seema-scanner

Rev

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

Rev Author Line No. Line
1 jakw 1
#include "cvtools.h"
2
 
3
#ifdef _WIN32
4
#include <cstdint>
5
#endif
6
 
7
#include <stdio.h>
8
 
9
namespace cvtools{
10
 
34 jakw 11
// Function to solve the hand-eye (or eye-in-hand) calibration problem.
12
// Finds [Omega | tau], to minimize ||[R_mark | t_mark][Omega | tau] - [Omega | tau][R | t]||^2
13
// Algorithm according to Tsai, Lenz, A new technique for fully autonomous and efficient 3d robotics hand-eye calibration
14
// DTU, 2014, Jakob Wilm
15
void handEyeCalibrationTsai(const std::vector<cv::Matx33f> R, const std::vector<cv::Vec3f> t, const std::vector<cv::Matx33f> R_mark, const std::vector<cv::Vec3f> t_mark, cv::Matx33f &Omega, cv::Vec3f &tau){
16
// NOT DEBUGGED!
17
    int N = R.size();
18
    assert(N == R_mark.size());
19
    assert(N == t.size());
20
    assert(N == t_mark.size());
21
 
22
    // construct equations for rotation
23
    cv::Mat A(3*N, 3, CV_32F);
24
    cv::Mat b(3*N, 1, CV_32F);
25
    for(int i=0; i<N; i++){
26
        // angle axis representations
27
        cv::Vec3f rot;
28
        cv::Vec3f rot_mark;
29
        cv::Rodrigues(R[i], rot);
30
        cv::Rodrigues(R_mark[i], rot_mark);
31
 
32
        cv::Vec3f P = 2.0*sin(cv::norm(rot)/2.0)*cv::normalize(rot);
33
        cv::Vec3f P_mark = 2.0*sin(cv::norm(rot_mark)/2.0)*cv::normalize(rot_mark);
34
 
35
        cv::Vec3f sum = P+P_mark;
36
        cv::Mat crossProduct = (cv::Mat_<float>(3,3) << 0.0, -sum(2), sum(1), sum(2), 0.0, -sum(0), -sum(1), sum(0), 0.0);
37
 
38
        crossProduct.copyTo(A.rowRange(i*3, i*3+3));
39
 
40
        cv::Mat(P-P_mark).copyTo(b.rowRange(i*3, i*3+3));
41
    }
42
 
43
    // solve for rotation
44
    cv::Vec<float, 9> P_prime;
45
    cv::solve(A, b, P_prime);
46
    cv::Vec<float, 9> P = 2.0*P_prime/(cv::sqrt(1.0 + cv::norm(P_prime)*cv::norm(P_prime)));
47
    float nP = cv::norm(P);
48
    cv::Mat crossProduct = (cv::Mat_<float>(3,3) << 0.0, -P(2), P(1), P(2), 0.0, -P(0), -P(1), P(0), 0.0);
49
    cv::Mat OmegaMat = (1.0-nP*nP/2.0)*cv::Mat::eye(3,3,CV_32F) + 0.5*(cv::Mat(P)*cv::Mat(P).t() + cv::sqrt(4.0 - nP*nP)*crossProduct);
50
    Omega = cv::Matx33f(OmegaMat);
51
 
52
    // construct equations for translation
53
    A.setTo(0.0);
54
    b.setTo(0.0);
55
    for(int i=0; i<N; i++){
56
 
57
        cv::Mat diff = cv::Mat(R[i]) - cv::Mat::eye(3, 3, CV_32F);
58
        diff.copyTo(A.rowRange(i*3, i*3+3));
59
 
60
        cv::Mat diff_mark = cv::Mat(Omega*R_mark[i] - R[i]);
61
        diff_mark.copyTo(b.rowRange(i*3, i*3+3));
62
    }
63
 
64
    // solve for translation
65
    cv::solve(A, b, tau);
66
}
67
 
68
// Function to fit two sets of corresponding pose data.
69
// Finds [Omega | tau], to minimize ||[R_mark | t_mark] - [Omega | tau][R | t]||^2
31 jakw 70
// Algorithm and notation according to Mili Shah, Comparing two sets of corresponding six degree of freedom data, CVIU 2011.
71
// DTU, 2013, Oline V. Olesen, Jakob Wilm
72
void fitSixDofData(const std::vector<cv::Matx33f> R, const std::vector<cv::Vec3f> t, const std::vector<cv::Matx33f> R_mark, const std::vector<cv::Vec3f> t_mark, cv::Matx33f &Omega, cv::Vec3f &tau){
34 jakw 73
 
31 jakw 74
    int N = R.size();
75
    assert(N == R_mark.size());
76
    assert(N == t.size());
77
    assert(N == t_mark.size());
78
 
79
    // Mean translations
80
    cv::Vec3f t_mean;
81
    cv::Vec3f t_mark_mean;
82
    for(int i=0; i<N; i++){
83
        t_mean += 1.0/N * t[i];
84
        t_mark_mean += 1.0/N * t_mark[i];
85
    }
86
 
87
    // Data with mean adjusted translations
88
    cv::Mat X_bar(3, 4*N, CV_32F);
89
    cv::Mat X_mark_bar(3, 4*N, CV_32F);
90
    for(int i=0; i<N; i++){
33 jakw 91
        cv::Mat(R[i]).copyTo(X_bar.colRange(i*4,i*4+3));
92
        cv::Mat(t[i] - t_mean).copyTo(X_bar.col(i*4+3));
93
        cv::Mat(R_mark[i]).copyTo(X_mark_bar.colRange(i*4,i*4+3));
94
        cv::Mat(t_mark[i] - t_mark_mean).copyTo(X_mark_bar.col(i*4+3));
31 jakw 95
    }
33 jakw 96
    //std::cout << X_bar << std::endl;
31 jakw 97
    // SVD
33 jakw 98
    cv::Mat W, U, VT;
31 jakw 99
    cv::SVDecomp(X_bar*X_mark_bar.t(), W, U, VT);
100
 
101
    cv::Matx33f D = cv::Matx33f::eye();
102
    if(cv::determinant(VT*U) < 0)
103
        D(3,3) = -1;
104
 
105
    // Best rotation
33 jakw 106
    Omega = cv::Matx33f(cv::Mat(VT.t()))*D*cv::Matx33f(cv::Mat(U.t()));
31 jakw 107
 
108
    // Best translation
109
    tau = t_mark_mean - Omega*t_mean;
110
 
111
}
112
 
1 jakw 113
// Forward distortion of points. The inverse of the undistortion in cv::initUndistortRectifyMap().
114
// Inspired by Pascal Thomet, http://code.opencv.org/issues/1387#note-11
115
// Convention for distortion parameters: http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/parameters.html
116
void initDistortMap(const cv::Matx33f cameraMatrix, const cv::Vec<float, 5> distCoeffs, const cv::Size size, cv::Mat &map1, cv::Mat &map2){
117
 
118
    float fx = cameraMatrix(0,0);
119
    float fy = cameraMatrix(1,1);
120
    float ux = cameraMatrix(0,2);
121
    float uy = cameraMatrix(1,2);
122
 
123
    float k1 = distCoeffs[0];
124
    float k2 = distCoeffs[1];
125
    float p1 = distCoeffs[2];
126
    float p2 = distCoeffs[3];
127
    float k3 = distCoeffs[4];
128
 
129
    map1.create(size, CV_32F);
130
    map2.create(size, CV_32F);
131
 
132
    for(int col = 0; col < size.width; col++){
133
        for(int row = 0; row < size.height; row++){
134
 
135
            // move origo to principal point and convert using focal length
136
            float x = (col-ux)/fx;
137
            float y = (row-uy)/fy;
138
 
139
            float xCorrected, yCorrected;
140
 
141
            //Step 1 : correct distortion
142
            float r2 = x*x + y*y;
143
            //radial
144
            xCorrected = x * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
145
            yCorrected = y * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
146
            //tangential
147
            xCorrected = xCorrected + (2.*p1*x*y + p2*(r2+2.*x*x));
148
            yCorrected = yCorrected + (p1*(r2+2.*y*y) + 2.*p2*x*y);
149
 
150
            //convert back to pixel coordinates
151
            float col_displaced = xCorrected * fx + ux;
152
            float row_displaced = yCorrected * fy + uy;
153
 
154
            // correct the vector in the opposite direction
155
            map1.at<float>(row,col) = col+(col-col_displaced);
156
            map2.at<float>(row,col) = row +(row-row_displaced);
157
        }
158
    }
159
}
160
 
161
// Downsample a texture which was created in virtual column/row space for a diamond pixel array projector
162
cv::Mat diamondDownsample(cv::Mat &pattern){
163
 
164
    cv::Mat pattern_diamond(pattern.rows,pattern.cols/2,CV_8UC3);
165
 
166
    for(unsigned int col = 0; col < pattern_diamond.cols; col++){
167
        for(unsigned int row = 0; row < pattern_diamond.rows; row++){
168
 
169
            pattern_diamond.at<cv::Vec3b>(row,col)=pattern.at<cv::Vec3b>(row,col*2+row%2);
170
        }
171
    }
172
 
173
    return pattern_diamond;
174
 
175
}
176
 
177
 
178
void mouseCallback(int evt, int x, int y, int flags, void* param){
179
    cv::Mat *im = (cv::Mat*) param;
180
    if (evt == CV_EVENT_LBUTTONDOWN) {
181
        if(im->type() == CV_8UC3){
182
            printf("%d %d: %d, %d, %d\n",
183
                   x, y,
184
                   (int)(*im).at<cv::Vec3b>(y, x)[0],
185
                    (int)(*im).at<cv::Vec3b>(y, x)[1],
186
                    (int)(*im).at<cv::Vec3b>(y, x)[2]);
187
        } else if (im->type() == CV_32F) {
188
            printf("%d %d: %f\n",
189
                   x, y,
190
                   im->at<float>(y, x));
191
        }
192
    }
193
}
194
 
195
void imshow(const char *windowName, cv::Mat im, unsigned int x, unsigned int y){
196
 
197
    // Imshow
198
    if(!cvGetWindowHandle(windowName)){
199
        int windowFlags = CV_GUI_EXPANDED | CV_WINDOW_KEEPRATIO;
200
        cv::namedWindow(windowName, windowFlags);
201
        cv::moveWindow(windowName, x, y);
202
    }
203
    cv::imshow(windowName, im);
204
}
205
 
206
void imagesc(const char *windowName, cv::Mat im){
207
 
208
    // Imshow with scaled image
209
 
210
 
211
}
212
 
213
cv::Mat histimage(cv::Mat histogram){
214
 
215
    cv::Mat histImage(512, 640, CV_8UC3, cv::Scalar(0));
216
 
217
    // Normalize the result to [ 2, histImage.rows-2 ]
218
    cv::normalize(histogram, histogram, 2, histImage.rows-2, cv::NORM_MINMAX, -1, cv::Mat());
219
 
220
    float bin_w = (float)histImage.cols/(float)histogram.rows;
221
 
222
    // Draw main histogram
223
    for(int i = 1; i < histogram.rows-10; i++){
224
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
225
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
226
                 cv::Scalar(255, 255, 255), 2, 4);
227
    }
228
 
229
    // Draw red max
230
    for(int i = histogram.rows-10; i < histogram.rows; i++){
231
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
232
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
233
                 cv::Scalar(0, 0, 255), 2, 4);
234
    }
235
 
236
    return histImage;
237
}
238
 
239
void hist(const char *windowName, cv::Mat histogram, unsigned int x, unsigned int y){
240
 
241
    // Display
242
    imshow(windowName, histimage(histogram), x, y);
243
    cv::Point(1,2);
244
}
245
 
246
 
247
void writeMat(cv::Mat const& mat, const char* filename, const char* varName, bool bgr2rgb){
248
    /*!
249
         *  \author Philip G. Lee <rocketman768@gmail.com>
250
         *  Write \b mat into \b filename
251
         *  in uncompressed .mat format (Level 5 MATLAB) for Matlab.
252
         *  The variable name in matlab will be \b varName. If
253
         *  \b bgr2rgb is true and there are 3 channels, swaps 1st and 3rd
254
         *  channels in the output. This is needed because OpenCV matrices
255
         *  are bgr, while Matlab is rgb. This has been tested to work with
256
         *  3-channel single-precision floating point matrices, and I hope
257
         *  it works on other types/channels, but not exactly sure.
258
         *  Documentation at <http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf>
259
         */
260
    int textLen = 116;
261
    char* text;
262
    int subsysOffsetLen = 8;
263
    char* subsysOffset;
264
    int verLen = 2;
265
    char* ver;
266
    char flags;
267
    int bytes;
268
    int padBytes;
269
    int bytesPerElement;
270
    int i,j,k,k2;
271
    bool doBgrSwap;
272
    char mxClass;
273
    int32_t miClass;
274
    uchar const* rowPtr;
275
    uint32_t tmp32;
276
    float tmp;
277
    FILE* fp;
278
 
279
    // Matlab constants.
280
    const uint16_t MI = 0x4d49; // Contains "MI" in ascii.
281
    const int32_t miINT8 = 1;
282
    const int32_t miUINT8 = 2;
283
    const int32_t miINT16 = 3;
284
    const int32_t miUINT16 = 4;
285
    const int32_t miINT32 = 5;
286
    const int32_t miUINT32 = 6;
287
    const int32_t miSINGLE = 7;
288
    const int32_t miDOUBLE = 9;
289
    const int32_t miMATRIX = 14;
290
    const char mxDOUBLE_CLASS = 6;
291
    const char mxSINGLE_CLASS = 7;
292
    const char mxINT8_CLASS = 8;
293
    const char mxUINT8_CLASS = 9;
294
    const char mxINT16_CLASS = 10;
295
    const char mxUINT16_CLASS = 11;
296
    const char mxINT32_CLASS = 12;
297
    const char mxUINT32_CLASS = 13;
298
    const uint64_t zero = 0; // Used for padding.
299
 
300
    fp = fopen( filename, "wb" );
301
 
302
    if( fp == 0 )
303
        return;
304
 
305
    const int rows = mat.rows;
306
    const int cols = mat.cols;
307
    const int chans = mat.channels();
308
 
309
    doBgrSwap = (chans==3) && bgr2rgb;
310
 
311
    // I hope this mapping is right :-/
312
    switch( mat.depth() ){
313
    case CV_8U:
314
        mxClass = mxUINT8_CLASS;
315
        miClass = miUINT8;
316
        bytesPerElement = 1;
317
        break;
318
    case CV_8S:
319
        mxClass = mxINT8_CLASS;
320
        miClass = miINT8;
321
        bytesPerElement = 1;
322
        break;
323
    case CV_16U:
324
        mxClass = mxUINT16_CLASS;
325
        miClass = miUINT16;
326
        bytesPerElement = 2;
327
        break;
328
    case CV_16S:
329
        mxClass = mxINT16_CLASS;
330
        miClass = miINT16;
331
        bytesPerElement = 2;
332
        break;
333
    case CV_32S:
334
        mxClass = mxINT32_CLASS;
335
        miClass = miINT32;
336
        bytesPerElement = 4;
337
        break;
338
    case CV_32F:
339
        mxClass = mxSINGLE_CLASS;
340
        miClass = miSINGLE;
341
        bytesPerElement = 4;
342
        break;
343
    case CV_64F:
344
        mxClass = mxDOUBLE_CLASS;
345
        miClass = miDOUBLE;
346
        bytesPerElement = 8;
347
        break;
348
    default:
349
        return;
350
    }
351
 
352
    //==================Mat-file header (128 bytes, page 1-5)==================
353
    text = new char[textLen]; // Human-readable text.
354
    memset( text, ' ', textLen );
355
    text[textLen-1] = '\0';
356
    const char* t = "MATLAB 5.0 MAT-file, Platform: PCWIN";
357
    memcpy( text, t, strlen(t) );
358
 
359
    subsysOffset = new char[subsysOffsetLen]; // Zeros for us.
360
    memset( subsysOffset, 0x00, subsysOffsetLen );
361
    ver = new char[verLen];
362
    ver[0] = 0x00;
363
    ver[1] = 0x01;
364
 
365
    fwrite( text, 1, textLen, fp );
366
    fwrite( subsysOffset, 1, subsysOffsetLen, fp );
367
    fwrite( ver, 1, verLen, fp );
368
    // Endian indicator. MI will show up as "MI" on big-endian
369
    // systems and "IM" on little-endian systems.
370
    fwrite( &MI, 2, 1, fp );
371
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
372
 
373
    //===================Data element tag (8 bytes, page 1-8)==================
374
    bytes = 16 + 24 + (8 + strlen(varName) + (8-(strlen(varName)%8))%8)
375
            + (8 + rows*cols*chans*bytesPerElement);
376
    fwrite( &miMATRIX, 4, 1, fp ); // Data type.
377
    fwrite( &bytes, 4, 1, fp); // Data size in bytes.
378
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
379
 
380
    //====================Array flags (16 bytes, page 1-15)====================
381
    bytes = 8;
382
    fwrite( &miUINT32, 4, 1, fp );
383
    fwrite( &bytes, 4, 1, fp );
384
    flags = 0x00; // Complex, logical, and global flags all off.
385
 
386
    tmp32 = 0;
387
    tmp32 = (flags << 8 ) | (mxClass);
388
    fwrite( &tmp32, 4, 1, fp );
389
 
390
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
391
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
392
 
393
    //===============Dimensions subelement (24 bytes, page 1-17)===============
394
    bytes = 12;
395
    fwrite( &miINT32, 4, 1, fp );
396
    fwrite( &bytes, 4, 1, fp );
397
 
398
    fwrite( &rows, 4, 1, fp );
399
    fwrite( &cols, 4, 1, fp );
400
    fwrite( &chans, 4, 1, fp );
401
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
402
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
403
 
404
    //==Array name (8 + strlen(varName) + (8-(strlen(varName)%8))%8 bytes, page 1-17)==
405
    bytes = strlen(varName);
406
 
407
    fwrite( &miINT8, 4, 1, fp );
408
    fwrite( &bytes, 4, 1, fp );
409
    fwrite( varName, 1, bytes, fp );
410
 
411
    // Pad to nearest 64-bit boundary.
412
    padBytes = (8-(bytes%8))%8;
413
    fwrite( &zero, 1, padBytes, fp );
414
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
415
 
416
    //====Matrix data (rows*cols*chans*bytesPerElement+8 bytes, page 1-20)=====
417
    bytes = rows*cols*chans*bytesPerElement;
418
    fwrite( &miClass, 4, 1, fp );
419
    fwrite( &bytes, 4, 1, fp );
420
 
421
    for( k = 0; k < chans; ++k )
422
    {
423
        if( doBgrSwap )
424
        {
425
            k2 = (k==0)? 2 : ((k==2)? 0 : 1);
426
        }
427
        else
428
            k2 = k;
429
 
430
        for( j = 0; j < cols; ++j )
431
        {
432
            for( i = 0; i < rows; ++i )
433
            {
434
                rowPtr = mat.data + mat.step*i;
435
                fwrite( rowPtr + (chans*j + k2)*bytesPerElement, bytesPerElement, 1, fp );
436
            }
437
        }
438
    }
439
 
440
    // Pad to 64-bit boundary.
441
    padBytes = (8-(bytes%8))%8;
442
    fwrite( &zero, 1, padBytes, fp );
443
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
444
 
445
    fclose(fp);
446
    delete[] text;
447
    delete[] subsysOffset;
448
    delete[] ver;
449
}
450
 
451
 
452
 
453
 
454
 
455
}