Subversion Repositories seema-scanner

Rev

Rev 74 | Rev 82 | 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
 
49 jakw 11
// Lightly modified OpenCV function which accepts a line width argument
50 jakw 12
void drawChessboardCorners(cv::InputOutputArray _image, cv::Size patternSize, cv::InputArray _corners, bool patternWasFound, int line_width){
13
    cv::Mat corners = _corners.getMat();
49 jakw 14
    if( corners.empty() )
15
        return;
50 jakw 16
    cv::Mat image = _image.getMat(); CvMat c_image = _image.getMat();
49 jakw 17
    int nelems = corners.checkVector(2, CV_32F, true);
18
    CV_Assert(nelems >= 0);
19
    cvDrawChessboardCorners( &c_image, patternSize, (CvPoint2D32f*)corners.data,
20
                             nelems, patternWasFound, line_width);
21
}
22
 
50 jakw 23
void cvDrawChessboardCorners(CvArr* _image, CvSize pattern_size, CvPoint2D32f* corners, int count, int found, int line_width){
49 jakw 24
    const int shift = 0;
50 jakw 25
    const int radius = 12;
49 jakw 26
    const int r = radius*(1 << shift);
27
    int i;
28
    CvMat stub, *image;
29
    double scale = 1;
30
    int type, cn, line_type;
31
 
32
    image = cvGetMat( _image, &stub );
33
 
34
    type = CV_MAT_TYPE(image->type);
35
    cn = CV_MAT_CN(type);
36
    if( cn != 1 && cn != 3 && cn != 4 )
37
        CV_Error( CV_StsUnsupportedFormat, "Number of channels must be 1, 3 or 4" );
38
 
39
    switch( CV_MAT_DEPTH(image->type) )
40
    {
41
    case CV_8U:
42
        scale = 1;
43
        break;
44
    case CV_16U:
45
        scale = 256;
46
        break;
47
    case CV_32F:
48
        scale = 1./255;
49
        break;
50
    default:
51
        CV_Error( CV_StsUnsupportedFormat,
52
            "Only 8-bit, 16-bit or floating-point 32-bit images are supported" );
53
    }
54
 
55
    line_type = type == CV_8UC1 || type == CV_8UC3 ? CV_AA : 8;
56
 
57
    if( !found )
58
    {
59
        CvScalar color = {{0,0,255}};
60
        if( cn == 1 )
61
            color = cvScalarAll(200);
62
        color.val[0] *= scale;
63
        color.val[1] *= scale;
64
        color.val[2] *= scale;
65
        color.val[3] *= scale;
66
 
67
        for( i = 0; i < count; i++ )
68
        {
69
            CvPoint pt;
70
            pt.x = cvRound(corners[i].x*(1 << shift));
71
            pt.y = cvRound(corners[i].y*(1 << shift));
72
            cvLine( image, cvPoint( pt.x - r, pt.y - r ),
50 jakw 73
                    cvPoint( pt.x + r, pt.y + r ), color, line_width, line_type, shift );
49 jakw 74
            cvLine( image, cvPoint( pt.x - r, pt.y + r),
50 jakw 75
                    cvPoint( pt.x + r, pt.y - r), color, line_width, line_type, shift );
76
            cvCircle( image, pt, r+(1<<shift), color, line_width, line_type, shift );
49 jakw 77
        }
78
    }
79
    else
80
    {
81
        int x, y;
82
        CvPoint prev_pt = {0, 0};
83
        const int line_max = 7;
84
        static const CvScalar line_colors[line_max] =
85
        {
86
            {{0,0,255}},
87
            {{0,128,255}},
88
            {{0,200,200}},
89
            {{0,255,0}},
90
            {{200,200,0}},
91
            {{255,0,0}},
92
            {{255,0,255}}
93
        };
94
 
95
        for( y = 0, i = 0; y < pattern_size.height; y++ )
96
        {
97
            CvScalar color = line_colors[y % line_max];
98
            if( cn == 1 )
99
                color = cvScalarAll(200);
100
            color.val[0] *= scale;
101
            color.val[1] *= scale;
102
            color.val[2] *= scale;
103
            color.val[3] *= scale;
104
 
105
            for( x = 0; x < pattern_size.width; x++, i++ )
106
            {
107
                CvPoint pt;
108
                pt.x = cvRound(corners[i].x*(1 << shift));
109
                pt.y = cvRound(corners[i].y*(1 << shift));
110
 
111
                if( i != 0 )
112
                    cvLine( image, prev_pt, pt, color, 1, line_type, shift );
113
 
114
                cvLine( image, cvPoint(pt.x - r, pt.y - r),
50 jakw 115
                        cvPoint(pt.x + r, pt.y + r), color, line_width, line_type, shift );
49 jakw 116
                cvLine( image, cvPoint(pt.x - r, pt.y + r),
50 jakw 117
                        cvPoint(pt.x + r, pt.y - r), color, line_width, line_type, shift );
118
                cvCircle( image, pt, r+(1<<shift), color, line_width, line_type, shift );
49 jakw 119
                prev_pt = pt;
120
            }
121
        }
122
    }
123
}
124
 
74 jakw 125
// Returns the result of mod(mat(x,y), moduli) for each matrix element
126
cv::Mat modulo(const cv::Mat &mat, float n){
127
 
128
    cv::Mat ret(mat.rows, mat.cols, mat.type());
129
 
130
    for(int row=0; row<ret.rows; row++){
131
        for(int col=0; col<ret.cols; col++){
132
            float val = mat.at<float>(row, col);
133
            // note: std::fmod calculates the remainder, not arithmetic modulo
134
            ret.at<float>(row, col) = val - n * std::floor(val / n);
135
        }
136
    }
137
 
138
    return ret;
139
}
140
 
42 jakw 141
// Convert a 3xN matrix to a vector of Point3fs.
142
void matToPoints3f(const cv::Mat &mat, std::vector<cv::Point3f> &points){
143
 
144
    unsigned int nPoints = mat.cols;
145
    points.resize(nPoints);
146
 
147
    for(unsigned int i=0; i<nPoints; i++)
148
        points[i] = cv::Point3f(mat.at<float>(0, i), mat.at<float>(1, i), mat.at<float>(2, i));
149
}
150
 
151
// Convert a (Dim+1)xN matrix of homogenous points to a DimxN matrix of points in non-homogenous coordinates.
152
void convertMatFromHomogeneous(cv::Mat &src, cv::Mat &dst){
153
    unsigned int N = src.cols;
154
    unsigned int Dim = src.rows-1;
155
    dst.create(Dim, N, src.type());
156
    for(unsigned int i=0; i<N; i++){
157
        for(unsigned int j=0; j<Dim; j++)
158
            dst.at<float>(j,i) = src.at<float>(j,i)/src.at<float>(Dim,i);
159
    }
160
 
161
}
162
 
34 jakw 163
// Function to solve the hand-eye (or eye-in-hand) calibration problem.
164
// Finds [Omega | tau], to minimize ||[R_mark | t_mark][Omega | tau] - [Omega | tau][R | t]||^2
165
// Algorithm according to Tsai, Lenz, A new technique for fully autonomous and efficient 3d robotics hand-eye calibration
166
// DTU, 2014, Jakob Wilm
167
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){
42 jakw 168
 
34 jakw 169
    int N = R.size();
170
    assert(N == R_mark.size());
171
    assert(N == t.size());
172
    assert(N == t_mark.size());
173
 
174
    // construct equations for rotation
175
    cv::Mat A(3*N, 3, CV_32F);
176
    cv::Mat b(3*N, 1, CV_32F);
177
    for(int i=0; i<N; i++){
178
        // angle axis representations
179
        cv::Vec3f rot;
180
        cv::Vec3f rot_mark;
181
        cv::Rodrigues(R[i], rot);
182
        cv::Rodrigues(R_mark[i], rot_mark);
183
 
184
        cv::Vec3f P = 2.0*sin(cv::norm(rot)/2.0)*cv::normalize(rot);
36 jakw 185
//std::cout << "P: " << std::endl << P << std::endl;
34 jakw 186
        cv::Vec3f P_mark = 2.0*sin(cv::norm(rot_mark)/2.0)*cv::normalize(rot_mark);
36 jakw 187
//std::cout << "P_mark: " << std::endl << P_mark << std::endl;
34 jakw 188
        cv::Vec3f sum = P+P_mark;
189
        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);
36 jakw 190
//std::cout << "crossProduct: " << std::endl << crossProduct << std::endl;
34 jakw 191
        crossProduct.copyTo(A.rowRange(i*3, i*3+3));
192
 
193
        cv::Mat(P-P_mark).copyTo(b.rowRange(i*3, i*3+3));
194
    }
195
 
196
    // solve for rotation
36 jakw 197
    cv::Vec3f P_prime;
198
    cv::solve(A, b, P_prime, cv::DECOMP_SVD);
199
    cv::Vec3f P = 2.0*P_prime/(cv::sqrt(1.0 + cv::norm(P_prime)*cv::norm(P_prime)));
34 jakw 200
    float nP = cv::norm(P);
201
    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);
202
    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);
203
    Omega = cv::Matx33f(OmegaMat);
204
 
205
    // construct equations for translation
206
    A.setTo(0.0);
207
    b.setTo(0.0);
208
    for(int i=0; i<N; i++){
209
 
36 jakw 210
        cv::Mat diff = cv::Mat(R_mark[i]) - cv::Mat::eye(3, 3, CV_32F);
34 jakw 211
        diff.copyTo(A.rowRange(i*3, i*3+3));
212
 
36 jakw 213
        cv::Mat diff_mark = cv::Mat(Omega*t[i] - t_mark[i]);
34 jakw 214
        diff_mark.copyTo(b.rowRange(i*3, i*3+3));
215
    }
216
 
217
    // solve for translation
36 jakw 218
    cv::solve(A, b, tau, cv::DECOMP_SVD);
81 jakw 219
 
220
    cv::Mat err_tau = b - (A*cv::Mat(tau));
221
    std::cout << err_tau << std::endl;
34 jakw 222
}
223
 
224
// Function to fit two sets of corresponding pose data.
225
// Finds [Omega | tau], to minimize ||[R_mark | t_mark] - [Omega | tau][R | t]||^2
31 jakw 226
// Algorithm and notation according to Mili Shah, Comparing two sets of corresponding six degree of freedom data, CVIU 2011.
227
// DTU, 2013, Oline V. Olesen, Jakob Wilm
228
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 229
 
31 jakw 230
    int N = R.size();
231
    assert(N == R_mark.size());
232
    assert(N == t.size());
233
    assert(N == t_mark.size());
234
 
235
    // Mean translations
236
    cv::Vec3f t_mean;
237
    cv::Vec3f t_mark_mean;
238
    for(int i=0; i<N; i++){
239
        t_mean += 1.0/N * t[i];
240
        t_mark_mean += 1.0/N * t_mark[i];
241
    }
242
 
243
    // Data with mean adjusted translations
244
    cv::Mat X_bar(3, 4*N, CV_32F);
245
    cv::Mat X_mark_bar(3, 4*N, CV_32F);
246
    for(int i=0; i<N; i++){
33 jakw 247
        cv::Mat(R[i]).copyTo(X_bar.colRange(i*4,i*4+3));
248
        cv::Mat(t[i] - t_mean).copyTo(X_bar.col(i*4+3));
249
        cv::Mat(R_mark[i]).copyTo(X_mark_bar.colRange(i*4,i*4+3));
250
        cv::Mat(t_mark[i] - t_mark_mean).copyTo(X_mark_bar.col(i*4+3));
31 jakw 251
    }
33 jakw 252
    //std::cout << X_bar << std::endl;
31 jakw 253
    // SVD
33 jakw 254
    cv::Mat W, U, VT;
31 jakw 255
    cv::SVDecomp(X_bar*X_mark_bar.t(), W, U, VT);
256
 
257
    cv::Matx33f D = cv::Matx33f::eye();
258
    if(cv::determinant(VT*U) < 0)
259
        D(3,3) = -1;
260
 
261
    // Best rotation
33 jakw 262
    Omega = cv::Matx33f(cv::Mat(VT.t()))*D*cv::Matx33f(cv::Mat(U.t()));
31 jakw 263
 
264
    // Best translation
265
    tau = t_mark_mean - Omega*t_mean;
266
 
267
}
268
 
1 jakw 269
// Forward distortion of points. The inverse of the undistortion in cv::initUndistortRectifyMap().
270
// Inspired by Pascal Thomet, http://code.opencv.org/issues/1387#note-11
271
// Convention for distortion parameters: http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/parameters.html
272
void initDistortMap(const cv::Matx33f cameraMatrix, const cv::Vec<float, 5> distCoeffs, const cv::Size size, cv::Mat &map1, cv::Mat &map2){
273
 
274
    float fx = cameraMatrix(0,0);
275
    float fy = cameraMatrix(1,1);
276
    float ux = cameraMatrix(0,2);
277
    float uy = cameraMatrix(1,2);
278
 
279
    float k1 = distCoeffs[0];
280
    float k2 = distCoeffs[1];
281
    float p1 = distCoeffs[2];
282
    float p2 = distCoeffs[3];
283
    float k3 = distCoeffs[4];
284
 
285
    map1.create(size, CV_32F);
286
    map2.create(size, CV_32F);
287
 
288
    for(int col = 0; col < size.width; col++){
289
        for(int row = 0; row < size.height; row++){
290
 
291
            // move origo to principal point and convert using focal length
292
            float x = (col-ux)/fx;
293
            float y = (row-uy)/fy;
294
 
295
            float xCorrected, yCorrected;
296
 
297
            //Step 1 : correct distortion
298
            float r2 = x*x + y*y;
299
            //radial
300
            xCorrected = x * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
301
            yCorrected = y * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
302
            //tangential
303
            xCorrected = xCorrected + (2.*p1*x*y + p2*(r2+2.*x*x));
304
            yCorrected = yCorrected + (p1*(r2+2.*y*y) + 2.*p2*x*y);
305
 
306
            //convert back to pixel coordinates
307
            float col_displaced = xCorrected * fx + ux;
308
            float row_displaced = yCorrected * fy + uy;
309
 
310
            // correct the vector in the opposite direction
311
            map1.at<float>(row,col) = col+(col-col_displaced);
312
            map2.at<float>(row,col) = row +(row-row_displaced);
313
        }
314
    }
315
}
316
 
317
// Downsample a texture which was created in virtual column/row space for a diamond pixel array projector
318
cv::Mat diamondDownsample(cv::Mat &pattern){
319
 
320
    cv::Mat pattern_diamond(pattern.rows,pattern.cols/2,CV_8UC3);
321
 
322
    for(unsigned int col = 0; col < pattern_diamond.cols; col++){
323
        for(unsigned int row = 0; row < pattern_diamond.rows; row++){
324
 
325
            pattern_diamond.at<cv::Vec3b>(row,col)=pattern.at<cv::Vec3b>(row,col*2+row%2);
326
        }
327
    }
328
 
329
    return pattern_diamond;
330
 
331
}
332
 
333
 
334
void mouseCallback(int evt, int x, int y, int flags, void* param){
335
    cv::Mat *im = (cv::Mat*) param;
336
    if (evt == CV_EVENT_LBUTTONDOWN) {
337
        if(im->type() == CV_8UC3){
338
            printf("%d %d: %d, %d, %d\n",
339
                   x, y,
340
                   (int)(*im).at<cv::Vec3b>(y, x)[0],
341
                    (int)(*im).at<cv::Vec3b>(y, x)[1],
342
                    (int)(*im).at<cv::Vec3b>(y, x)[2]);
343
        } else if (im->type() == CV_32F) {
344
            printf("%d %d: %f\n",
345
                   x, y,
346
                   im->at<float>(y, x));
347
        }
348
    }
349
}
350
 
351
void imshow(const char *windowName, cv::Mat im, unsigned int x, unsigned int y){
352
 
353
    // Imshow
354
    if(!cvGetWindowHandle(windowName)){
355
        int windowFlags = CV_GUI_EXPANDED | CV_WINDOW_KEEPRATIO;
356
        cv::namedWindow(windowName, windowFlags);
357
        cv::moveWindow(windowName, x, y);
358
    }
359
    cv::imshow(windowName, im);
360
}
361
 
362
void imagesc(const char *windowName, cv::Mat im){
363
 
364
    // Imshow with scaled image
365
 
366
 
367
}
368
 
369
cv::Mat histimage(cv::Mat histogram){
370
 
371
    cv::Mat histImage(512, 640, CV_8UC3, cv::Scalar(0));
372
 
373
    // Normalize the result to [ 2, histImage.rows-2 ]
374
    cv::normalize(histogram, histogram, 2, histImage.rows-2, cv::NORM_MINMAX, -1, cv::Mat());
375
 
376
    float bin_w = (float)histImage.cols/(float)histogram.rows;
377
 
378
    // Draw main histogram
379
    for(int i = 1; i < histogram.rows-10; i++){
380
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
381
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
382
                 cv::Scalar(255, 255, 255), 2, 4);
383
    }
384
 
385
    // Draw red max
386
    for(int i = histogram.rows-10; i < histogram.rows; i++){
387
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
388
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
389
                 cv::Scalar(0, 0, 255), 2, 4);
390
    }
391
 
392
    return histImage;
393
}
394
 
395
void hist(const char *windowName, cv::Mat histogram, unsigned int x, unsigned int y){
396
 
397
    // Display
398
    imshow(windowName, histimage(histogram), x, y);
399
    cv::Point(1,2);
400
}
401
 
402
 
403
void writeMat(cv::Mat const& mat, const char* filename, const char* varName, bool bgr2rgb){
404
    /*!
405
         *  \author Philip G. Lee <rocketman768@gmail.com>
406
         *  Write \b mat into \b filename
407
         *  in uncompressed .mat format (Level 5 MATLAB) for Matlab.
408
         *  The variable name in matlab will be \b varName. If
409
         *  \b bgr2rgb is true and there are 3 channels, swaps 1st and 3rd
410
         *  channels in the output. This is needed because OpenCV matrices
411
         *  are bgr, while Matlab is rgb. This has been tested to work with
412
         *  3-channel single-precision floating point matrices, and I hope
413
         *  it works on other types/channels, but not exactly sure.
414
         *  Documentation at <http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf>
415
         */
416
    int textLen = 116;
417
    char* text;
418
    int subsysOffsetLen = 8;
419
    char* subsysOffset;
420
    int verLen = 2;
421
    char* ver;
422
    char flags;
423
    int bytes;
424
    int padBytes;
425
    int bytesPerElement;
426
    int i,j,k,k2;
427
    bool doBgrSwap;
428
    char mxClass;
429
    int32_t miClass;
430
    uchar const* rowPtr;
431
    uint32_t tmp32;
432
    float tmp;
433
    FILE* fp;
434
 
435
    // Matlab constants.
436
    const uint16_t MI = 0x4d49; // Contains "MI" in ascii.
437
    const int32_t miINT8 = 1;
438
    const int32_t miUINT8 = 2;
439
    const int32_t miINT16 = 3;
440
    const int32_t miUINT16 = 4;
441
    const int32_t miINT32 = 5;
442
    const int32_t miUINT32 = 6;
443
    const int32_t miSINGLE = 7;
444
    const int32_t miDOUBLE = 9;
445
    const int32_t miMATRIX = 14;
446
    const char mxDOUBLE_CLASS = 6;
447
    const char mxSINGLE_CLASS = 7;
448
    const char mxINT8_CLASS = 8;
449
    const char mxUINT8_CLASS = 9;
450
    const char mxINT16_CLASS = 10;
451
    const char mxUINT16_CLASS = 11;
452
    const char mxINT32_CLASS = 12;
453
    const char mxUINT32_CLASS = 13;
454
    const uint64_t zero = 0; // Used for padding.
455
 
456
    fp = fopen( filename, "wb" );
457
 
458
    if( fp == 0 )
459
        return;
460
 
461
    const int rows = mat.rows;
462
    const int cols = mat.cols;
463
    const int chans = mat.channels();
464
 
465
    doBgrSwap = (chans==3) && bgr2rgb;
466
 
467
    // I hope this mapping is right :-/
468
    switch( mat.depth() ){
469
    case CV_8U:
470
        mxClass = mxUINT8_CLASS;
471
        miClass = miUINT8;
472
        bytesPerElement = 1;
473
        break;
474
    case CV_8S:
475
        mxClass = mxINT8_CLASS;
476
        miClass = miINT8;
477
        bytesPerElement = 1;
478
        break;
479
    case CV_16U:
480
        mxClass = mxUINT16_CLASS;
481
        miClass = miUINT16;
482
        bytesPerElement = 2;
483
        break;
484
    case CV_16S:
485
        mxClass = mxINT16_CLASS;
486
        miClass = miINT16;
487
        bytesPerElement = 2;
488
        break;
489
    case CV_32S:
490
        mxClass = mxINT32_CLASS;
491
        miClass = miINT32;
492
        bytesPerElement = 4;
493
        break;
494
    case CV_32F:
495
        mxClass = mxSINGLE_CLASS;
496
        miClass = miSINGLE;
497
        bytesPerElement = 4;
498
        break;
499
    case CV_64F:
500
        mxClass = mxDOUBLE_CLASS;
501
        miClass = miDOUBLE;
502
        bytesPerElement = 8;
503
        break;
504
    default:
505
        return;
506
    }
507
 
508
    //==================Mat-file header (128 bytes, page 1-5)==================
509
    text = new char[textLen]; // Human-readable text.
510
    memset( text, ' ', textLen );
511
    text[textLen-1] = '\0';
512
    const char* t = "MATLAB 5.0 MAT-file, Platform: PCWIN";
513
    memcpy( text, t, strlen(t) );
514
 
515
    subsysOffset = new char[subsysOffsetLen]; // Zeros for us.
516
    memset( subsysOffset, 0x00, subsysOffsetLen );
517
    ver = new char[verLen];
518
    ver[0] = 0x00;
519
    ver[1] = 0x01;
520
 
521
    fwrite( text, 1, textLen, fp );
522
    fwrite( subsysOffset, 1, subsysOffsetLen, fp );
523
    fwrite( ver, 1, verLen, fp );
524
    // Endian indicator. MI will show up as "MI" on big-endian
525
    // systems and "IM" on little-endian systems.
526
    fwrite( &MI, 2, 1, fp );
527
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
528
 
529
    //===================Data element tag (8 bytes, page 1-8)==================
530
    bytes = 16 + 24 + (8 + strlen(varName) + (8-(strlen(varName)%8))%8)
531
            + (8 + rows*cols*chans*bytesPerElement);
532
    fwrite( &miMATRIX, 4, 1, fp ); // Data type.
533
    fwrite( &bytes, 4, 1, fp); // Data size in bytes.
534
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
535
 
536
    //====================Array flags (16 bytes, page 1-15)====================
537
    bytes = 8;
538
    fwrite( &miUINT32, 4, 1, fp );
539
    fwrite( &bytes, 4, 1, fp );
540
    flags = 0x00; // Complex, logical, and global flags all off.
541
 
542
    tmp32 = 0;
543
    tmp32 = (flags << 8 ) | (mxClass);
544
    fwrite( &tmp32, 4, 1, fp );
545
 
546
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
547
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
548
 
549
    //===============Dimensions subelement (24 bytes, page 1-17)===============
550
    bytes = 12;
551
    fwrite( &miINT32, 4, 1, fp );
552
    fwrite( &bytes, 4, 1, fp );
553
 
554
    fwrite( &rows, 4, 1, fp );
555
    fwrite( &cols, 4, 1, fp );
556
    fwrite( &chans, 4, 1, fp );
557
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
558
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
559
 
560
    //==Array name (8 + strlen(varName) + (8-(strlen(varName)%8))%8 bytes, page 1-17)==
561
    bytes = strlen(varName);
562
 
563
    fwrite( &miINT8, 4, 1, fp );
564
    fwrite( &bytes, 4, 1, fp );
565
    fwrite( varName, 1, bytes, fp );
566
 
567
    // Pad to nearest 64-bit boundary.
568
    padBytes = (8-(bytes%8))%8;
569
    fwrite( &zero, 1, padBytes, fp );
570
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
571
 
572
    //====Matrix data (rows*cols*chans*bytesPerElement+8 bytes, page 1-20)=====
573
    bytes = rows*cols*chans*bytesPerElement;
574
    fwrite( &miClass, 4, 1, fp );
575
    fwrite( &bytes, 4, 1, fp );
576
 
577
    for( k = 0; k < chans; ++k )
578
    {
579
        if( doBgrSwap )
580
        {
581
            k2 = (k==0)? 2 : ((k==2)? 0 : 1);
582
        }
583
        else
584
            k2 = k;
585
 
586
        for( j = 0; j < cols; ++j )
587
        {
588
            for( i = 0; i < rows; ++i )
589
            {
590
                rowPtr = mat.data + mat.step*i;
591
                fwrite( rowPtr + (chans*j + k2)*bytesPerElement, bytesPerElement, 1, fp );
592
            }
593
        }
594
    }
595
 
596
    // Pad to 64-bit boundary.
597
    padBytes = (8-(bytes%8))%8;
598
    fwrite( &zero, 1, padBytes, fp );
599
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
600
 
601
    fclose(fp);
602
    delete[] text;
603
    delete[] subsysOffset;
604
    delete[] ver;
605
}
606
 
607
 
608
 
609
 
610
 
611
}