Subversion Repositories seema-scanner

Rev

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

Rev 121 Rev 139
1
#include "cvtools.h"
1
#include "cvtools.h"
2
 
2
 
3
#ifdef _WIN32
3
#ifdef _WIN32
4
#include <cstdint>
4
#include <cstdint>
5
#endif
5
#endif
6
 
6
 
7
#include <stdio.h>
7
#include <stdio.h>
8
 
8
 
9
namespace cvtools{
9
namespace cvtools{
10
 
10
 
-
 
11
// Convert an BGR 3-channel image back into a Bayered image
-
 
12
void cvtColorBGRToBayerBG(const cv::Mat &imBGR, cv::Mat &imBayerBG){
-
 
13
 
-
 
14
    imBayerBG.create(imBGR.size(), CV_8UC1);
-
 
15
 
-
 
16
 
-
 
17
    for(int r=0; r<imBGR.rows; r++){
-
 
18
        for(int c=0; c<imBGR.cols; c++){
-
 
19
 
-
 
20
            bool evenRow = r % 2;
-
 
21
            bool evenCol = c % 2;
-
 
22
 
-
 
23
            if(evenRow & evenCol)
-
 
24
                imBayerBG.at<uchar>(r,c) = imBGR.at<cv::Vec3b>(r,c)[0];
-
 
25
            else if(!evenRow & !evenCol)
-
 
26
                imBayerBG.at<uchar>(r,c) = imBGR.at<cv::Vec3b>(r,c)[2];
-
 
27
            else
-
 
28
                imBayerBG.at<uchar>(r,c) = imBGR.at<cv::Vec3b>(r,c)[1];
-
 
29
 
-
 
30
        }
-
 
31
 
-
 
32
    }
-
 
33
 
-
 
34
 
-
 
35
}
-
 
36
 
-
 
37
 
11
// Removes matches not satisfying the epipolar constraint.
38
// Removes matches not satisfying the epipolar constraint.
12
// F is the fundamental matrix.
39
// F is the fundamental matrix.
13
// Works like cv::correctMatches(), except it removes matches with an error distance greater than maxD.
40
// Works like cv::correctMatches(), except it removes matches with an error distance greater than maxD.
14
void removeIncorrectMatches(const cv::Mat F, const std::vector<cv::Point2f> &q0, const std::vector<cv::Point2f> &q1, const float maxD,
41
void removeIncorrectMatches(const cv::Mat F, const std::vector<cv::Point2f> &q0, const std::vector<cv::Point2f> &q1, const float maxD,
15
                                std::vector<cv::Point2f> q0Correct, std::vector<cv::Point2f> q1Correct){
42
                                std::vector<cv::Point2f> q0Correct, std::vector<cv::Point2f> q1Correct){
16
 
43
 
17
    int n0 = (int)q0.size(), n1 = (int)q1.size();
44
    int n0 = (int)q0.size(), n1 = (int)q1.size();
18
    q0Correct.reserve(n0);
45
    q0Correct.reserve(n0);
19
    q1Correct.reserve(n1);
46
    q1Correct.reserve(n1);
20
 
47
 
21
    // Point to line distance
48
    // Point to line distance
22
//    for( int i = 0; i < n1; i++ ){
49
//    for( int i = 0; i < n1; i++ ){
23
//        cv::Vec3f p0 = cv::Vec3f(q0[i].x, q0[i].y, 1.0);
50
//        cv::Vec3f p0 = cv::Vec3f(q0[i].x, q0[i].y, 1.0);
24
//        // Epipolar line defined by p0
51
//        // Epipolar line defined by p0
25
//        cv::Vec3f l = F*p0;
52
//        cv::Vec3f l = F*p0;
26
//        l /= sqrt(l(0)*l(0) + l(1)*l(1));
53
//        l /= sqrt(l(0)*l(0) + l(1)*l(1));
27
//        for( int j = 0; j < n2; j++ ){
54
//        for( int j = 0; j < n2; j++ ){
28
//            cv::Vec3f p1 = cv::Vec3f(q1[i].x, q1[i].y, 1.0);
55
//            cv::Vec3f p1 = cv::Vec3f(q1[i].x, q1[i].y, 1.0);
29
//            // Signed distance to line
56
//            // Signed distance to line
30
//            float d = l.dot(p1);
57
//            float d = l.dot(p1);
31
//            if(d < maxD){
58
//            if(d < maxD){
32
//                q0Correct.push_back(q0[i]);
59
//                q0Correct.push_back(q0[i]);
33
//                q1Correct.push_back(q1[i]);
60
//                q1Correct.push_back(q1[i]);
34
//            }
61
//            }
35
//        }
62
//        }
36
//    }
63
//    }
37
 
64
 
38
    // Symmetric epipolar distance
65
    // Symmetric epipolar distance
39
    std::vector<cv::Point3f> l0, l1;
66
    std::vector<cv::Point3f> l0, l1;
40
    cv::computeCorrespondEpilines(q0, 1, F, l0);
67
    cv::computeCorrespondEpilines(q0, 1, F, l0);
41
    cv::computeCorrespondEpilines(q1, 2, F, l1);
68
    cv::computeCorrespondEpilines(q1, 2, F, l1);
42
 
69
 
43
    for(int i = 0; i < n0; i++){
70
    for(int i = 0; i < n0; i++){
44
        cv::Vec3f p0 = cv::Vec3f(q0[i].x, q0[i].y, 1.0);
71
        cv::Vec3f p0 = cv::Vec3f(q0[i].x, q0[i].y, 1.0);
45
        for(int j = 0; j < n1; j++){
72
        for(int j = 0; j < n1; j++){
46
            cv::Vec3f p1 = cv::Vec3f(q1[j].x, q1[j].y, 1.0);
73
            cv::Vec3f p1 = cv::Vec3f(q1[j].x, q1[j].y, 1.0);
47
            float d01 = l0[i].dot(p1);
74
            float d01 = l0[i].dot(p1);
48
            float d10 = l1[j].dot(p0);
75
            float d10 = l1[j].dot(p0);
49
            float d = d01*d01 + d10*d10;
76
            float d = d01*d01 + d10*d10;
50
            if(d < maxD){
77
            if(d < maxD){
51
                q0Correct.push_back(q0[i]);
78
                q0Correct.push_back(q0[i]);
52
                q1Correct.push_back(q1[i]);
79
                q1Correct.push_back(q1[i]);
53
            }
80
            }
54
        }
81
        }
55
    }
82
    }
56
 
83
 
57
//    // Sampson Error (H&Z, p. 287) (expensive...)
84
//    // Sampson Error (H&Z, p. 287) (expensive...)
58
//    std::vector<cv::Point3f> p0, p1;
85
//    std::vector<cv::Point3f> p0, p1;
59
//    cv::convertPointsToHomogeneous(q0, p0);
86
//    cv::convertPointsToHomogeneous(q0, p0);
60
//    cv::convertPointsToHomogeneous(q1, p1);
87
//    cv::convertPointsToHomogeneous(q1, p1);
61
//    cv::Mat Fp0Mat = cv::Mat(F)*cv::Mat(p0).reshape(1).t();
88
//    cv::Mat Fp0Mat = cv::Mat(F)*cv::Mat(p0).reshape(1).t();
62
//    cv::Mat FTp1Mat = cv::Mat(F.t())*cv::Mat(p1).reshape(1).t();
89
//    cv::Mat FTp1Mat = cv::Mat(F.t())*cv::Mat(p1).reshape(1).t();
63
//    for( int i = 0; i < n1; i++ ){
90
//    for( int i = 0; i < n1; i++ ){
64
//        cv::Vec3f Fp0 = Fp0Mat.col(i);
91
//        cv::Vec3f Fp0 = Fp0Mat.col(i);
65
//        for( int j = 0; j < n2; j++ ){
92
//        for( int j = 0; j < n2; j++ ){
66
//            cv::Vec3f FTp1 = FTp1Mat.col(j);
93
//            cv::Vec3f FTp1 = FTp1Mat.col(j);
67
//            cv::Matx<float,1,1> p1TFp0 = cv::Matx31f(p1[j]).t()*F*cv::Matx31f(p0[i]);
94
//            cv::Matx<float,1,1> p1TFp0 = cv::Matx31f(p1[j]).t()*F*cv::Matx31f(p0[i]);
68
//            float d = p1TFp0(0)*p1TFp0(0) / (Fp0(0)*Fp0(0) + Fp0(1)*Fp0(1) + FTp1(0)*FTp1(0) + FTp1(1)*FTp1(1));
95
//            float d = p1TFp0(0)*p1TFp0(0) / (Fp0(0)*Fp0(0) + Fp0(1)*Fp0(1) + FTp1(0)*FTp1(0) + FTp1(1)*FTp1(1));
69
//            if(d < maxD){
96
//            if(d < maxD){
70
//                q0Correct.push_back(q0[i]);
97
//                q0Correct.push_back(q0[i]);
71
//                q1Correct.push_back(q1[i]);
98
//                q1Correct.push_back(q1[i]);
72
//            }
99
//            }
73
//        }
100
//        }
74
//    }
101
//    }
75
 
102
 
76
    return;
103
    return;
77
}
104
}
78
 
105
 
79
// Lightly modified OpenCV function which accepts a line width argument
106
// Lightly modified OpenCV function which accepts a line width argument
80
void drawChessboardCorners(cv::InputOutputArray _image, cv::Size patternSize, cv::InputArray _corners, bool patternWasFound, int line_width){
107
void drawChessboardCorners(cv::InputOutputArray _image, cv::Size patternSize, cv::InputArray _corners, bool patternWasFound, int line_width){
81
    cv::Mat corners = _corners.getMat();
108
    cv::Mat corners = _corners.getMat();
82
    if( corners.empty() )
109
    if( corners.empty() )
83
        return;
110
        return;
84
    cv::Mat image = _image.getMat(); CvMat c_image = _image.getMat();
111
    cv::Mat image = _image.getMat(); CvMat c_image = _image.getMat();
85
    int nelems = corners.checkVector(2, CV_32F, true);
112
    int nelems = corners.checkVector(2, CV_32F, true);
86
    CV_Assert(nelems >= 0);
113
    CV_Assert(nelems >= 0);
87
    cvDrawChessboardCorners( &c_image, patternSize, (CvPoint2D32f*)corners.data,
114
    cvDrawChessboardCorners( &c_image, patternSize, (CvPoint2D32f*)corners.data,
88
                             nelems, patternWasFound, line_width);
115
                             nelems, patternWasFound, line_width);
89
}
116
}
90
 
117
 
91
void rshift(cv::Mat& I, unsigned int shift){
118
void rshift(cv::Mat& I, unsigned int shift){
92
 
119
 
93
    int nRows = I.rows;
120
    int nRows = I.rows;
94
    int nCols = I.cols;
121
    int nCols = I.cols;
95
 
122
 
96
    if (I.isContinuous()){
123
    if (I.isContinuous()){
97
        nCols *= nRows;
124
        nCols *= nRows;
98
        nRows = 1;
125
        nRows = 1;
99
    }
126
    }
100
 
127
 
101
    int i,j;
128
    int i,j;
102
    unsigned short* p;
129
    unsigned short* p;
103
    for( i = 0; i < nRows; ++i){
130
    for( i = 0; i < nRows; ++i){
104
        p = I.ptr<unsigned short>(i);
131
        p = I.ptr<unsigned short>(i);
105
        for ( j = 0; j < nCols; ++j){
132
        for ( j = 0; j < nCols; ++j){
106
            p[j] = p[j]>>shift;
133
            p[j] = p[j]>>shift;
107
        }
134
        }
108
    }
135
    }
109
}
136
}
110
 
137
 
111
void cvDrawChessboardCorners(CvArr* _image, CvSize pattern_size, CvPoint2D32f* corners, int count, int found, int line_width){
138
void cvDrawChessboardCorners(CvArr* _image, CvSize pattern_size, CvPoint2D32f* corners, int count, int found, int line_width){
112
    const int shift = 0;
139
    const int shift = 0;
113
    const int radius = 12;
140
    const int radius = 12;
114
    const int r = radius*(1 << shift);
141
    const int r = radius*(1 << shift);
115
    int i;
142
    int i;
116
    CvMat stub, *image;
143
    CvMat stub, *image;
117
    double scale = 1;
144
    double scale = 1;
118
    int type, cn, line_type;
145
    int type, cn, line_type;
119
 
146
 
120
    image = cvGetMat( _image, &stub );
147
    image = cvGetMat( _image, &stub );
121
 
148
 
122
    type = CV_MAT_TYPE(image->type);
149
    type = CV_MAT_TYPE(image->type);
123
    cn = CV_MAT_CN(type);
150
    cn = CV_MAT_CN(type);
124
    if( cn != 1 && cn != 3 && cn != 4 )
151
    if( cn != 1 && cn != 3 && cn != 4 )
125
        CV_Error( CV_StsUnsupportedFormat, "Number of channels must be 1, 3 or 4" );
152
        CV_Error( CV_StsUnsupportedFormat, "Number of channels must be 1, 3 or 4" );
126
 
153
 
127
    switch( CV_MAT_DEPTH(image->type) )
154
    switch( CV_MAT_DEPTH(image->type) )
128
    {
155
    {
129
    case CV_8U:
156
    case CV_8U:
130
        scale = 1;
157
        scale = 1;
131
        break;
158
        break;
132
    case CV_16U:
159
    case CV_16U:
133
        scale = 256;
160
        scale = 256;
134
        break;
161
        break;
135
    case CV_32F:
162
    case CV_32F:
136
        scale = 1./255;
163
        scale = 1./255;
137
        break;
164
        break;
138
    default:
165
    default:
139
        CV_Error( CV_StsUnsupportedFormat,
166
        CV_Error( CV_StsUnsupportedFormat,
140
            "Only 8-bit, 16-bit or floating-point 32-bit images are supported" );
167
            "Only 8-bit, 16-bit or floating-point 32-bit images are supported" );
141
    }
168
    }
142
 
169
 
143
    line_type = type == CV_8UC1 || type == CV_8UC3 ? CV_AA : 8;
170
    line_type = type == CV_8UC1 || type == CV_8UC3 ? CV_AA : 8;
144
 
171
 
145
    if( !found )
172
    if( !found )
146
    {
173
    {
147
        CvScalar color = {{0,0,255}};
174
        CvScalar color = {{0,0,255}};
148
        if( cn == 1 )
175
        if( cn == 1 )
149
            color = cvScalarAll(200);
176
            color = cvScalarAll(200);
150
        color.val[0] *= scale;
177
        color.val[0] *= scale;
151
        color.val[1] *= scale;
178
        color.val[1] *= scale;
152
        color.val[2] *= scale;
179
        color.val[2] *= scale;
153
        color.val[3] *= scale;
180
        color.val[3] *= scale;
154
 
181
 
155
        for( i = 0; i < count; i++ )
182
        for( i = 0; i < count; i++ )
156
        {
183
        {
157
            CvPoint pt;
184
            CvPoint pt;
158
            pt.x = cvRound(corners[i].x*(1 << shift));
185
            pt.x = cvRound(corners[i].x*(1 << shift));
159
            pt.y = cvRound(corners[i].y*(1 << shift));
186
            pt.y = cvRound(corners[i].y*(1 << shift));
160
            cvLine( image, cvPoint( pt.x - r, pt.y - r ),
187
            cvLine( image, cvPoint( pt.x - r, pt.y - r ),
161
                    cvPoint( pt.x + r, pt.y + r ), color, line_width, line_type, shift );
188
                    cvPoint( pt.x + r, pt.y + r ), color, line_width, line_type, shift );
162
            cvLine( image, cvPoint( pt.x - r, pt.y + r),
189
            cvLine( image, cvPoint( pt.x - r, pt.y + r),
163
                    cvPoint( pt.x + r, pt.y - r), color, line_width, line_type, shift );
190
                    cvPoint( pt.x + r, pt.y - r), color, line_width, line_type, shift );
164
            cvCircle( image, pt, r+(1<<shift), color, line_width, line_type, shift );
191
            cvCircle( image, pt, r+(1<<shift), color, line_width, line_type, shift );
165
        }
192
        }
166
    }
193
    }
167
    else
194
    else
168
    {
195
    {
169
        int x, y;
196
        int x, y;
170
        CvPoint prev_pt = {0, 0};
197
        CvPoint prev_pt = {0, 0};
171
        const int line_max = 7;
198
        const int line_max = 7;
172
        static const CvScalar line_colors[line_max] =
199
        static const CvScalar line_colors[line_max] =
173
        {
200
        {
174
            {{0,0,255}},
201
            {{0,0,255}},
175
            {{0,128,255}},
202
            {{0,128,255}},
176
            {{0,200,200}},
203
            {{0,200,200}},
177
            {{0,255,0}},
204
            {{0,255,0}},
178
            {{200,200,0}},
205
            {{200,200,0}},
179
            {{255,0,0}},
206
            {{255,0,0}},
180
            {{255,0,255}}
207
            {{255,0,255}}
181
        };
208
        };
182
 
209
 
183
        for( y = 0, i = 0; y < pattern_size.height; y++ )
210
        for( y = 0, i = 0; y < pattern_size.height; y++ )
184
        {
211
        {
185
            CvScalar color = line_colors[y % line_max];
212
            CvScalar color = line_colors[y % line_max];
186
            if( cn == 1 )
213
            if( cn == 1 )
187
                color = cvScalarAll(200);
214
                color = cvScalarAll(200);
188
            color.val[0] *= scale;
215
            color.val[0] *= scale;
189
            color.val[1] *= scale;
216
            color.val[1] *= scale;
190
            color.val[2] *= scale;
217
            color.val[2] *= scale;
191
            color.val[3] *= scale;
218
            color.val[3] *= scale;
192
 
219
 
193
            for( x = 0; x < pattern_size.width; x++, i++ )
220
            for( x = 0; x < pattern_size.width; x++, i++ )
194
            {
221
            {
195
                CvPoint pt;
222
                CvPoint pt;
196
                pt.x = cvRound(corners[i].x*(1 << shift));
223
                pt.x = cvRound(corners[i].x*(1 << shift));
197
                pt.y = cvRound(corners[i].y*(1 << shift));
224
                pt.y = cvRound(corners[i].y*(1 << shift));
198
 
225
 
199
                if( i != 0 )
226
                if( i != 0 )
200
                    cvLine( image, prev_pt, pt, color, 1, line_type, shift );
227
                    cvLine( image, prev_pt, pt, color, 1, line_type, shift );
201
 
228
 
202
                cvLine( image, cvPoint(pt.x - r, pt.y - r),
229
                cvLine( image, cvPoint(pt.x - r, pt.y - r),
203
                        cvPoint(pt.x + r, pt.y + r), color, line_width, line_type, shift );
230
                        cvPoint(pt.x + r, pt.y + r), color, line_width, line_type, shift );
204
                cvLine( image, cvPoint(pt.x - r, pt.y + r),
231
                cvLine( image, cvPoint(pt.x - r, pt.y + r),
205
                        cvPoint(pt.x + r, pt.y - r), color, line_width, line_type, shift );
232
                        cvPoint(pt.x + r, pt.y - r), color, line_width, line_type, shift );
206
                cvCircle( image, pt, r+(1<<shift), color, line_width, line_type, shift );
233
                cvCircle( image, pt, r+(1<<shift), color, line_width, line_type, shift );
207
                prev_pt = pt;
234
                prev_pt = pt;
208
            }
235
            }
209
        }
236
        }
210
    }
237
    }
211
}
238
}
212
 
239
 
213
// Returns the result of mod(mat(x,y), moduli) for each matrix element
240
// Returns the result of mod(mat(x,y), moduli) for each matrix element
214
cv::Mat modulo(const cv::Mat &mat, float n){
241
cv::Mat modulo(const cv::Mat &mat, float n){
215
 
242
 
216
    cv::Mat ret(mat.rows, mat.cols, mat.type());
243
    cv::Mat ret(mat.rows, mat.cols, mat.type());
217
 
244
 
218
    for(int row=0; row<ret.rows; row++){
245
    for(int row=0; row<ret.rows; row++){
219
        for(int col=0; col<ret.cols; col++){
246
        for(int col=0; col<ret.cols; col++){
220
            float val = mat.at<float>(row, col);
247
            float val = mat.at<float>(row, col);
221
            // note: std::fmod calculates the remainder, not arithmetic modulo
248
            // note: std::fmod calculates the remainder, not arithmetic modulo
222
            ret.at<float>(row, col) = val - n * std::floor(val / n);
249
            ret.at<float>(row, col) = val - n * std::floor(val / n);
223
        }
250
        }
224
    }
251
    }
225
 
252
 
226
    return ret;
253
    return ret;
227
}
254
}
228
 
255
 
229
// Convert a 3xN matrix to a vector of Point3fs.
256
// Convert a 3xN matrix to a vector of Point3fs.
230
void matToPoints3f(const cv::Mat &mat, std::vector<cv::Point3f> &points){
257
void matToPoints3f(const cv::Mat &mat, std::vector<cv::Point3f> &points){
231
 
258
 
232
    unsigned int nPoints = mat.cols;
259
    unsigned int nPoints = mat.cols;
233
    points.resize(nPoints);
260
    points.resize(nPoints);
234
 
261
 
235
    for(unsigned int i=0; i<nPoints; i++)
262
    for(unsigned int i=0; i<nPoints; i++)
236
        points[i] = cv::Point3f(mat.at<float>(0, i), mat.at<float>(1, i), mat.at<float>(2, i));
263
        points[i] = cv::Point3f(mat.at<float>(0, i), mat.at<float>(1, i), mat.at<float>(2, i));
237
}
264
}
238
 
265
 
239
// Convert a (Dim+1)xN matrix of homogenous points to a DimxN matrix of points in non-homogenous coordinates.
266
// Convert a (Dim+1)xN matrix of homogenous points to a DimxN matrix of points in non-homogenous coordinates.
240
void convertMatFromHomogeneous(cv::Mat &src, cv::Mat &dst){
267
void convertMatFromHomogeneous(cv::Mat &src, cv::Mat &dst){
241
    unsigned int N = src.cols;
268
    unsigned int N = src.cols;
242
    unsigned int Dim = src.rows-1;
269
    unsigned int Dim = src.rows-1;
243
    dst.create(Dim, N, src.type());
270
    dst.create(Dim, N, src.type());
244
    for(unsigned int i=0; i<N; i++){
271
    for(unsigned int i=0; i<N; i++){
245
        for(unsigned int j=0; j<Dim; j++)
272
        for(unsigned int j=0; j<Dim; j++)
246
            dst.at<float>(j,i) = src.at<float>(j,i)/src.at<float>(Dim,i);
273
            dst.at<float>(j,i) = src.at<float>(j,i)/src.at<float>(Dim,i);
247
    }
274
    }
248
 
275
 
249
}
276
}
250
 
277
 
251
// Function to solve the hand-eye (or eye-in-hand) calibration problem.
278
// Function to solve the hand-eye (or eye-in-hand) calibration problem.
252
// Finds [Omega | tau], to minimize ||[R_mark | t_mark][Omega | tau] - [Omega | tau][R | t]||^2
279
// Finds [Omega | tau], to minimize ||[R_mark | t_mark][Omega | tau] - [Omega | tau][R | t]||^2
253
// Algorithm according to Tsai, Lenz, A new technique for fully autonomous and efficient 3d robotics hand-eye calibration
280
// Algorithm according to Tsai, Lenz, A new technique for fully autonomous and efficient 3d robotics hand-eye calibration
254
// DTU, 2014, Jakob Wilm
281
// DTU, 2014, Jakob Wilm
255
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){
282
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){
256
 
283
 
257
    int N = R.size();
284
    int N = R.size();
258
    assert(N == R_mark.size());
285
    assert(N == R_mark.size());
259
    assert(N == t.size());
286
    assert(N == t.size());
260
    assert(N == t_mark.size());
287
    assert(N == t_mark.size());
261
 
288
 
262
    // construct equations for rotation
289
    // construct equations for rotation
263
    cv::Mat A(3*N, 3, CV_32F);
290
    cv::Mat A(3*N, 3, CV_32F);
264
    cv::Mat b(3*N, 1, CV_32F);
291
    cv::Mat b(3*N, 1, CV_32F);
265
    for(int i=0; i<N; i++){
292
    for(int i=0; i<N; i++){
266
        // angle axis representations
293
        // angle axis representations
267
        cv::Vec3f rot;
294
        cv::Vec3f rot;
268
        cv::Vec3f rot_mark;
295
        cv::Vec3f rot_mark;
269
        cv::Rodrigues(R[i], rot);
296
        cv::Rodrigues(R[i], rot);
270
        cv::Rodrigues(R_mark[i], rot_mark);
297
        cv::Rodrigues(R_mark[i], rot_mark);
271
 
298
 
272
        cv::Vec3f P = 2.0*sin(cv::norm(rot)/2.0)*cv::normalize(rot);
299
        cv::Vec3f P = 2.0*sin(cv::norm(rot)/2.0)*cv::normalize(rot);
273
//std::cout << "P: " << std::endl << P << std::endl;
300
//std::cout << "P: " << std::endl << P << std::endl;
274
        cv::Vec3f P_mark = 2.0*sin(cv::norm(rot_mark)/2.0)*cv::normalize(rot_mark);
301
        cv::Vec3f P_mark = 2.0*sin(cv::norm(rot_mark)/2.0)*cv::normalize(rot_mark);
275
//std::cout << "P_mark: " << std::endl << P_mark << std::endl;
302
//std::cout << "P_mark: " << std::endl << P_mark << std::endl;
276
        cv::Vec3f sum = P+P_mark;
303
        cv::Vec3f sum = P+P_mark;
277
        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);
304
        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);
278
//std::cout << "crossProduct: " << std::endl << crossProduct << std::endl;
305
//std::cout << "crossProduct: " << std::endl << crossProduct << std::endl;
279
        crossProduct.copyTo(A.rowRange(i*3, i*3+3));
306
        crossProduct.copyTo(A.rowRange(i*3, i*3+3));
280
 
307
 
281
        cv::Mat(P-P_mark).copyTo(b.rowRange(i*3, i*3+3));
308
        cv::Mat(P-P_mark).copyTo(b.rowRange(i*3, i*3+3));
282
    }
309
    }
283
 
310
 
284
    // solve for rotation
311
    // solve for rotation
285
    cv::Vec3f P_prime;
312
    cv::Vec3f P_prime;
286
    cv::solve(A, b, P_prime, cv::DECOMP_SVD);
313
    cv::solve(A, b, P_prime, cv::DECOMP_SVD);
287
    cv::Vec3f P = 2.0*P_prime/(cv::sqrt(1.0 + cv::norm(P_prime)*cv::norm(P_prime)));
314
    cv::Vec3f P = 2.0*P_prime/(cv::sqrt(1.0 + cv::norm(P_prime)*cv::norm(P_prime)));
288
    float nP = cv::norm(P);
315
    float nP = cv::norm(P);
289
    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);
316
    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);
290
    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);
317
    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);
291
    Omega = cv::Matx33f(OmegaMat);
318
    Omega = cv::Matx33f(OmegaMat);
292
 
319
 
293
    // construct equations for translation
320
    // construct equations for translation
294
    A.setTo(0.0);
321
    A.setTo(0.0);
295
    b.setTo(0.0);
322
    b.setTo(0.0);
296
    for(int i=0; i<N; i++){
323
    for(int i=0; i<N; i++){
297
 
324
 
298
        cv::Mat diff = cv::Mat(R_mark[i]) - cv::Mat::eye(3, 3, CV_32F);
325
        cv::Mat diff = cv::Mat(R_mark[i]) - cv::Mat::eye(3, 3, CV_32F);
299
        diff.copyTo(A.rowRange(i*3, i*3+3));
326
        diff.copyTo(A.rowRange(i*3, i*3+3));
300
 
327
 
301
        cv::Mat diff_mark = cv::Mat(Omega*t[i] - t_mark[i]);
328
        cv::Mat diff_mark = cv::Mat(Omega*t[i] - t_mark[i]);
302
        diff_mark.copyTo(b.rowRange(i*3, i*3+3));
329
        diff_mark.copyTo(b.rowRange(i*3, i*3+3));
303
    }
330
    }
304
 
331
 
305
    // solve for translation
332
    // solve for translation
306
    cv::solve(A, b, tau, cv::DECOMP_SVD);
333
    cv::solve(A, b, tau, cv::DECOMP_SVD);
307
 
334
 
308
    cv::Mat err_tau = b - (A*cv::Mat(tau));
335
    cv::Mat err_tau = b - (A*cv::Mat(tau));
309
    std::cout << err_tau << std::endl;
336
    std::cout << err_tau << std::endl;
310
}
337
}
311
 
338
 
312
// Function to solve for the rotation axis from sets of 3D point coordinates of flat pattern feature points
339
// Function to solve for the rotation axis from sets of 3D point coordinates of flat pattern feature points
313
// Algorithm according to Chen et al., Rotation axis calibration of a turntable using constrained global optimization, Optik 2014
340
// Algorithm according to Chen et al., Rotation axis calibration of a turntable using constrained global optimization, Optik 2014
314
// DTU, 2014, Jakob Wilm
341
// DTU, 2014, Jakob Wilm
315
void rotationAxisCalibration(const std::vector< std::vector<cv::Point3f> > Qcam, const std::vector<cv::Point3f> Qobj, cv::Vec3f &axis, cv::Vec3f &point){
342
void rotationAxisCalibration(const std::vector< std::vector<cv::Point3f> > Qcam, const std::vector<cv::Point3f> Qobj, cv::Vec3f &axis, cv::Vec3f &point){
316
 
343
 
317
    // number of frames (points on each arch)
344
    // number of frames (points on each arch)
318
    int l = Qcam.size();
345
    int l = Qcam.size();
319
 
346
 
320
    // number of points in each frame
347
    // number of points in each frame
321
    int mn = Qobj.size();
348
    int mn = Qobj.size();
322
 
349
 
323
    assert(mn == Qcam[0].size());
350
    assert(mn == Qcam[0].size());
324
 
351
 
325
    // construct matrix for axis determination
352
    // construct matrix for axis determination
326
    cv::Mat M(6, 6, CV_32F, cv::Scalar(0));
353
    cv::Mat M(6, 6, CV_32F, cv::Scalar(0));
327
 
354
 
328
    for(int k=0; k<l; k++){
355
    for(int k=0; k<l; k++){
329
        for(int idx=0; idx<mn; idx++){
356
        for(int idx=0; idx<mn; idx++){
330
 
357
 
331
//            float i = Qobj[idx].x+4;
358
//            float i = Qobj[idx].x+4;
332
//            float j = Qobj[idx].y+4;
359
//            float j = Qobj[idx].y+4;
333
            float i = Qobj[idx].x;
360
            float i = Qobj[idx].x;
334
            float j = Qobj[idx].y;
361
            float j = Qobj[idx].y;
335
 
362
 
336
            float x = Qcam[k][idx].x;
363
            float x = Qcam[k][idx].x;
337
            float y = Qcam[k][idx].y;
364
            float y = Qcam[k][idx].y;
338
            float z = Qcam[k][idx].z;
365
            float z = Qcam[k][idx].z;
339
 
366
 
340
            M += (cv::Mat_<float>(6,6) << x*x, x*y, x*z, x, i*x, j*x,
367
            M += (cv::Mat_<float>(6,6) << x*x, x*y, x*z, x, i*x, j*x,
341
                                            0, y*y, y*z, y, i*y, j*y,
368
                                            0, y*y, y*z, y, i*y, j*y,
342
                                            0,   0, z*z, z, i*z, j*z,
369
                                            0,   0, z*z, z, i*z, j*z,
343
                                            0,   0,   0, 1,   i,   j,
370
                                            0,   0,   0, 1,   i,   j,
344
                                            0,   0,   0, 0, i*i, i*j,
371
                                            0,   0,   0, 0, i*i, i*j,
345
                                            0,   0,   0, 0,   0, j*j);
372
                                            0,   0,   0, 0,   0, j*j);
346
 
373
 
347
        }
374
        }
348
    }
375
    }
349
 
376
 
350
    cv::completeSymm(M, false);
377
    cv::completeSymm(M, false);
351
 
378
 
352
    // solve for axis
379
    // solve for axis
353
    std::vector<float> lambda;
380
    std::vector<float> lambda;
354
    cv::Mat u;
381
    cv::Mat u;
355
    cv::eigen(M, lambda, u);
382
    cv::eigen(M, lambda, u);
356
 
383
 
357
    float minLambda = abs(lambda[0]);
384
    float minLambda = abs(lambda[0]);
358
    int idx = 0;
385
    int idx = 0;
359
    for(int i=1; i<lambda.size(); i++){
386
    for(int i=1; i<lambda.size(); i++){
360
        if(abs(lambda[i]) < minLambda){
387
        if(abs(lambda[i]) < minLambda){
361
            minLambda = lambda[i];
388
            minLambda = lambda[i];
362
            idx = i;
389
            idx = i;
363
        }
390
        }
364
    }
391
    }
365
 
392
 
366
    axis = u.row(idx).colRange(0, 3);
393
    axis = u.row(idx).colRange(0, 3);
367
    axis = cv::normalize(axis);
394
    axis = cv::normalize(axis);
368
 
395
 
369
    float nx = u.at<float>(idx, 0);
396
    float nx = u.at<float>(idx, 0);
370
    float ny = u.at<float>(idx, 1);
397
    float ny = u.at<float>(idx, 1);
371
    float nz = u.at<float>(idx, 2);
398
    float nz = u.at<float>(idx, 2);
372
    float d  = u.at<float>(idx, 3);
399
    float d  = u.at<float>(idx, 3);
373
    float dh = u.at<float>(idx, 4);
400
    float dh = u.at<float>(idx, 4);
374
    float dv = u.at<float>(idx, 5);
401
    float dv = u.at<float>(idx, 5);
375
 
402
 
376
//    // Paper version: c is initially eliminated
403
//    // Paper version: c is initially eliminated
377
//    cv::Mat A(l*mn, mn+2, CV_32F, cv::Scalar(0.0));
404
//    cv::Mat A(l*mn, mn+2, CV_32F, cv::Scalar(0.0));
378
//    cv::Mat bb(l*mn, 1, CV_32F);
405
//    cv::Mat bb(l*mn, 1, CV_32F);
379
 
406
 
380
//    for(int k=0; k<l; k++){
407
//    for(int k=0; k<l; k++){
381
//        for(int idx=0; idx<mn; idx++){
408
//        for(int idx=0; idx<mn; idx++){
382
 
409
 
383
//            float i = Qobj[idx].x;
410
//            float i = Qobj[idx].x;
384
//            float j = Qobj[idx].y;
411
//            float j = Qobj[idx].y;
385
 
412
 
386
//            float x = Qcam[k][idx].x;
413
//            float x = Qcam[k][idx].x;
387
//            float y = Qcam[k][idx].y;
414
//            float y = Qcam[k][idx].y;
388
//            float z = Qcam[k][idx].z;
415
//            float z = Qcam[k][idx].z;
389
 
416
 
390
//            float f = x*x + y*y + z*z + (2*x*nx + 2*y*ny + 2*z*nz)*(i*dh + j*dv);
417
//            float f = x*x + y*y + z*z + (2*x*nx + 2*y*ny + 2*z*nz)*(i*dh + j*dv);
391
 
418
 
392
//            int row = k*mn+idx;
419
//            int row = k*mn+idx;
393
//            A.at<float>(row, 0) = 2*x - (2*z*nx)/nz;
420
//            A.at<float>(row, 0) = 2*x - (2*z*nx)/nz;
394
//            A.at<float>(row, 1) = 2*y - (2*z*ny)/nz;
421
//            A.at<float>(row, 1) = 2*y - (2*z*ny)/nz;
395
//            A.at<float>(row, idx+2) = 1.0;
422
//            A.at<float>(row, idx+2) = 1.0;
396
 
423
 
397
//            bb.at<float>(row, 0) = f + (2*z*d)/nz;
424
//            bb.at<float>(row, 0) = f + (2*z*d)/nz;
398
//        }
425
//        }
399
//    }
426
//    }
400
 
427
 
401
//    // solve for point
428
//    // solve for point
402
//    cv::Mat abe;
429
//    cv::Mat abe;
403
//    cv::solve(A, bb, abe, cv::DECOMP_SVD);
430
//    cv::solve(A, bb, abe, cv::DECOMP_SVD);
404
 
431
 
405
//    float a = abe.at<float>(0, 0);
432
//    float a = abe.at<float>(0, 0);
406
//    float b = abe.at<float>(1, 0);
433
//    float b = abe.at<float>(1, 0);
407
//    float c = -(nx*a+ny*b+d)/nz;
434
//    float c = -(nx*a+ny*b+d)/nz;
408
 
435
 
409
    // Our version: solve simultanously for a,b,c
436
    // Our version: solve simultanously for a,b,c
410
    cv::Mat A(l*mn, mn+3, CV_32F, cv::Scalar(0.0));
437
    cv::Mat A(l*mn, mn+3, CV_32F, cv::Scalar(0.0));
411
    cv::Mat bb(l*mn, 1, CV_32F);
438
    cv::Mat bb(l*mn, 1, CV_32F);
412
 
439
 
413
    for(int k=0; k<l; k++){
440
    for(int k=0; k<l; k++){
414
        for(int idx=0; idx<mn; idx++){
441
        for(int idx=0; idx<mn; idx++){
415
 
442
 
416
            float i = Qobj[idx].x;
443
            float i = Qobj[idx].x;
417
            float j = Qobj[idx].y;
444
            float j = Qobj[idx].y;
418
 
445
 
419
            float x = Qcam[k][idx].x;
446
            float x = Qcam[k][idx].x;
420
            float y = Qcam[k][idx].y;
447
            float y = Qcam[k][idx].y;
421
            float z = Qcam[k][idx].z;
448
            float z = Qcam[k][idx].z;
422
 
449
 
423
            float f = x*x + y*y + z*z + (2*x*nx + 2*y*ny + 2*z*nz)*(i*dh + j*dv);
450
            float f = x*x + y*y + z*z + (2*x*nx + 2*y*ny + 2*z*nz)*(i*dh + j*dv);
424
 
451
 
425
            int row = k*mn+idx;
452
            int row = k*mn+idx;
426
            A.at<float>(row, 0) = 2*x;
453
            A.at<float>(row, 0) = 2*x;
427
            A.at<float>(row, 1) = 2*y;
454
            A.at<float>(row, 1) = 2*y;
428
            A.at<float>(row, 2) = 2*z;
455
            A.at<float>(row, 2) = 2*z;
429
            A.at<float>(row, idx+3) = 1.0;
456
            A.at<float>(row, idx+3) = 1.0;
430
 
457
 
431
            bb.at<float>(row, 0) = f;
458
            bb.at<float>(row, 0) = f;
432
        }
459
        }
433
    }
460
    }
434
 
461
 
435
    // solve for point
462
    // solve for point
436
    cv::Mat abe;
463
    cv::Mat abe;
437
    cv::solve(A, bb, abe, cv::DECOMP_SVD);
464
    cv::solve(A, bb, abe, cv::DECOMP_SVD);
438
 
465
 
439
    float a = abe.at<float>(0, 0);
466
    float a = abe.at<float>(0, 0);
440
    float b = abe.at<float>(1, 0);
467
    float b = abe.at<float>(1, 0);
441
    float c = abe.at<float>(2, 0);
468
    float c = abe.at<float>(2, 0);
442
 
469
 
443
    point[0] = a;
470
    point[0] = a;
444
    point[1] = b;
471
    point[1] = b;
445
    point[2] = c;
472
    point[2] = c;
446
 
473
 
447
}
474
}
448
 
475
 
449
// Function to fit two sets of corresponding pose data.
476
// Function to fit two sets of corresponding pose data.
450
// Finds [Omega | tau], to minimize ||[R_mark | t_mark] - [Omega | tau][R | t]||^2
477
// Finds [Omega | tau], to minimize ||[R_mark | t_mark] - [Omega | tau][R | t]||^2
451
// Algorithm and notation according to Mili Shah, Comparing two sets of corresponding six degree of freedom data, CVIU 2011.
478
// Algorithm and notation according to Mili Shah, Comparing two sets of corresponding six degree of freedom data, CVIU 2011.
452
// DTU, 2013, Oline V. Olesen, Jakob Wilm
479
// DTU, 2013, Oline V. Olesen, Jakob Wilm
453
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){
480
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){
454
 
481
 
455
    int N = R.size();
482
    int N = R.size();
456
    assert(N == R_mark.size());
483
    assert(N == R_mark.size());
457
    assert(N == t.size());
484
    assert(N == t.size());
458
    assert(N == t_mark.size());
485
    assert(N == t_mark.size());
459
 
486
 
460
    // Mean translations
487
    // Mean translations
461
    cv::Vec3f t_mean;
488
    cv::Vec3f t_mean;
462
    cv::Vec3f t_mark_mean;
489
    cv::Vec3f t_mark_mean;
463
    for(int i=0; i<N; i++){
490
    for(int i=0; i<N; i++){
464
        t_mean += 1.0/N * t[i];
491
        t_mean += 1.0/N * t[i];
465
        t_mark_mean += 1.0/N * t_mark[i];
492
        t_mark_mean += 1.0/N * t_mark[i];
466
    }
493
    }
467
 
494
 
468
    // Data with mean adjusted translations
495
    // Data with mean adjusted translations
469
    cv::Mat X_bar(3, 4*N, CV_32F);
496
    cv::Mat X_bar(3, 4*N, CV_32F);
470
    cv::Mat X_mark_bar(3, 4*N, CV_32F);
497
    cv::Mat X_mark_bar(3, 4*N, CV_32F);
471
    for(int i=0; i<N; i++){
498
    for(int i=0; i<N; i++){
472
        cv::Mat(R[i]).copyTo(X_bar.colRange(i*4,i*4+3));
499
        cv::Mat(R[i]).copyTo(X_bar.colRange(i*4,i*4+3));
473
        cv::Mat(t[i] - t_mean).copyTo(X_bar.col(i*4+3));
500
        cv::Mat(t[i] - t_mean).copyTo(X_bar.col(i*4+3));
474
        cv::Mat(R_mark[i]).copyTo(X_mark_bar.colRange(i*4,i*4+3));
501
        cv::Mat(R_mark[i]).copyTo(X_mark_bar.colRange(i*4,i*4+3));
475
        cv::Mat(t_mark[i] - t_mark_mean).copyTo(X_mark_bar.col(i*4+3));
502
        cv::Mat(t_mark[i] - t_mark_mean).copyTo(X_mark_bar.col(i*4+3));
476
    }
503
    }
477
    //std::cout << X_bar << std::endl;
504
    //std::cout << X_bar << std::endl;
478
    // SVD
505
    // SVD
479
    cv::Mat W, U, VT;
506
    cv::Mat W, U, VT;
480
    cv::SVDecomp(X_bar*X_mark_bar.t(), W, U, VT);
507
    cv::SVDecomp(X_bar*X_mark_bar.t(), W, U, VT);
481
 
508
 
482
    cv::Matx33f D = cv::Matx33f::eye();
509
    cv::Matx33f D = cv::Matx33f::eye();
483
    if(cv::determinant(VT*U) < 0)
510
    if(cv::determinant(VT*U) < 0)
484
        D(3,3) = -1;
511
        D(3,3) = -1;
485
 
512
 
486
    // Best rotation
513
    // Best rotation
487
    Omega = cv::Matx33f(cv::Mat(VT.t()))*D*cv::Matx33f(cv::Mat(U.t()));
514
    Omega = cv::Matx33f(cv::Mat(VT.t()))*D*cv::Matx33f(cv::Mat(U.t()));
488
 
515
 
489
    // Best translation
516
    // Best translation
490
    tau = t_mark_mean - Omega*t_mean;
517
    tau = t_mark_mean - Omega*t_mean;
491
 
518
 
492
}
519
}
493
 
520
 
494
// Forward distortion of points. The inverse of the undistortion in cv::initUndistortRectifyMap().
521
// Forward distortion of points. The inverse of the undistortion in cv::initUndistortRectifyMap().
495
// Inspired by Pascal Thomet, http://code.opencv.org/issues/1387#note-11
522
// Inspired by Pascal Thomet, http://code.opencv.org/issues/1387#note-11
496
// Convention for distortion parameters: http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/parameters.html
523
// Convention for distortion parameters: http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/parameters.html
497
void initDistortMap(const cv::Matx33f cameraMatrix, const cv::Vec<float, 5> distCoeffs, const cv::Size size, cv::Mat &map1, cv::Mat &map2){
524
void initDistortMap(const cv::Matx33f cameraMatrix, const cv::Vec<float, 5> distCoeffs, const cv::Size size, cv::Mat &map1, cv::Mat &map2){
498
 
525
 
499
    float fx = cameraMatrix(0,0);
526
    float fx = cameraMatrix(0,0);
500
    float fy = cameraMatrix(1,1);
527
    float fy = cameraMatrix(1,1);
501
    float ux = cameraMatrix(0,2);
528
    float ux = cameraMatrix(0,2);
502
    float uy = cameraMatrix(1,2);
529
    float uy = cameraMatrix(1,2);
503
 
530
 
504
    float k1 = distCoeffs[0];
531
    float k1 = distCoeffs[0];
505
    float k2 = distCoeffs[1];
532
    float k2 = distCoeffs[1];
506
    float p1 = distCoeffs[2];
533
    float p1 = distCoeffs[2];
507
    float p2 = distCoeffs[3];
534
    float p2 = distCoeffs[3];
508
    float k3 = distCoeffs[4];
535
    float k3 = distCoeffs[4];
509
 
536
 
510
    map1.create(size, CV_32F);
537
    map1.create(size, CV_32F);
511
    map2.create(size, CV_32F);
538
    map2.create(size, CV_32F);
512
 
539
 
513
    for(int col = 0; col < size.width; col++){
540
    for(int col = 0; col < size.width; col++){
514
        for(int row = 0; row < size.height; row++){
541
        for(int row = 0; row < size.height; row++){
515
 
542
 
516
            // move origo to principal point and convert using focal length
543
            // move origo to principal point and convert using focal length
517
            float x = (col-ux)/fx;
544
            float x = (col-ux)/fx;
518
            float y = (row-uy)/fy;
545
            float y = (row-uy)/fy;
519
 
546
 
520
            float xCorrected, yCorrected;
547
            float xCorrected, yCorrected;
521
 
548
 
522
            //Step 1 : correct distortion
549
            //Step 1 : correct distortion
523
            float r2 = x*x + y*y;
550
            float r2 = x*x + y*y;
524
            //radial
551
            //radial
525
            xCorrected = x * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
552
            xCorrected = x * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
526
            yCorrected = y * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
553
            yCorrected = y * (1. + k1*r2 + k2*r2*r2 + k3*r2*r2*r2);
527
            //tangential
554
            //tangential
528
            xCorrected = xCorrected + (2.*p1*x*y + p2*(r2+2.*x*x));
555
            xCorrected = xCorrected + (2.*p1*x*y + p2*(r2+2.*x*x));
529
            yCorrected = yCorrected + (p1*(r2+2.*y*y) + 2.*p2*x*y);
556
            yCorrected = yCorrected + (p1*(r2+2.*y*y) + 2.*p2*x*y);
530
 
557
 
531
            //convert back to pixel coordinates
558
            //convert back to pixel coordinates
532
            float col_displaced = xCorrected * fx + ux;
559
            float col_displaced = xCorrected * fx + ux;
533
            float row_displaced = yCorrected * fy + uy;
560
            float row_displaced = yCorrected * fy + uy;
534
 
561
 
535
            // correct the vector in the opposite direction
562
            // correct the vector in the opposite direction
536
            map1.at<float>(row,col) = col+(col-col_displaced);
563
            map1.at<float>(row,col) = col+(col-col_displaced);
537
            map2.at<float>(row,col) = row +(row-row_displaced);
564
            map2.at<float>(row,col) = row +(row-row_displaced);
538
        }
565
        }
539
    }
566
    }
540
}
567
}
541
 
568
 
542
// Downsample a texture which was created in virtual column/row space for a diamond pixel array projector
569
// Downsample a texture which was created in virtual column/row space for a diamond pixel array projector
543
cv::Mat diamondDownsample(cv::Mat &pattern){
570
cv::Mat diamondDownsample(cv::Mat &pattern){
544
 
571
 
545
    cv::Mat pattern_diamond(pattern.rows,pattern.cols/2,CV_8UC3);
572
    cv::Mat pattern_diamond(pattern.rows,pattern.cols/2,CV_8UC3);
546
 
573
 
547
    for(unsigned int col = 0; col < pattern_diamond.cols; col++){
574
    for(unsigned int col = 0; col < pattern_diamond.cols; col++){
548
        for(unsigned int row = 0; row < pattern_diamond.rows; row++){
575
        for(unsigned int row = 0; row < pattern_diamond.rows; row++){
549
 
576
 
550
            pattern_diamond.at<cv::Vec3b>(row,col)=pattern.at<cv::Vec3b>(row,col*2+row%2);
577
            pattern_diamond.at<cv::Vec3b>(row,col)=pattern.at<cv::Vec3b>(row,col*2+row%2);
551
        }
578
        }
552
    }
579
    }
553
 
580
 
554
    return pattern_diamond;
581
    return pattern_diamond;
555
 
582
 
556
}
583
}
557
 
584
 
558
 
585
 
559
void mouseCallback(int evt, int x, int y, int flags, void* param){
586
void mouseCallback(int evt, int x, int y, int flags, void* param){
560
    cv::Mat *im = (cv::Mat*) param;
587
    cv::Mat *im = (cv::Mat*) param;
561
    if (evt == CV_EVENT_LBUTTONDOWN) {
588
    if (evt == CV_EVENT_LBUTTONDOWN) {
562
        if(im->type() == CV_8UC3){
589
        if(im->type() == CV_8UC3){
563
            printf("%d %d: %d, %d, %d\n",
590
            printf("%d %d: %d, %d, %d\n",
564
                   x, y,
591
                   x, y,
565
                   (int)(*im).at<cv::Vec3b>(y, x)[0],
592
                   (int)(*im).at<cv::Vec3b>(y, x)[0],
566
                    (int)(*im).at<cv::Vec3b>(y, x)[1],
593
                    (int)(*im).at<cv::Vec3b>(y, x)[1],
567
                    (int)(*im).at<cv::Vec3b>(y, x)[2]);
594
                    (int)(*im).at<cv::Vec3b>(y, x)[2]);
568
        } else if (im->type() == CV_32F) {
595
        } else if (im->type() == CV_32F) {
569
            printf("%d %d: %f\n",
596
            printf("%d %d: %f\n",
570
                   x, y,
597
                   x, y,
571
                   im->at<float>(y, x));
598
                   im->at<float>(y, x));
572
        }
599
        }
573
    }
600
    }
574
}
601
}
575
 
602
 
576
void imshow(const char *windowName, cv::Mat im, unsigned int x, unsigned int y){
603
void imshow(const char *windowName, cv::Mat im, unsigned int x, unsigned int y){
577
 
604
 
578
    // Imshow
605
    // Imshow
579
    if(!cvGetWindowHandle(windowName)){
606
    if(!cvGetWindowHandle(windowName)){
580
        int windowFlags = CV_GUI_EXPANDED | CV_WINDOW_KEEPRATIO;
607
        int windowFlags = CV_GUI_EXPANDED | CV_WINDOW_KEEPRATIO;
581
        cv::namedWindow(windowName, windowFlags);
608
        cv::namedWindow(windowName, windowFlags);
582
        cv::moveWindow(windowName, x, y);
609
        cv::moveWindow(windowName, x, y);
583
    }
610
    }
584
    cv::imshow(windowName, im);
611
    cv::imshow(windowName, im);
585
}
612
}
586
 
613
 
587
void imagesc(const char *windowName, cv::Mat im){
614
void imagesc(const char *windowName, cv::Mat im){
588
 
615
 
589
    // Imshow with scaled image
616
    // Imshow with scaled image
590
 
617
 
591
 
618
 
592
}
619
}
593
 
620
 
594
cv::Mat histimage(cv::Mat histogram){
621
cv::Mat histimage(cv::Mat histogram){
595
 
622
 
596
    cv::Mat histImage(512, 640, CV_8UC3, cv::Scalar(0));
623
    cv::Mat histImage(512, 640, CV_8UC3, cv::Scalar(0));
597
 
624
 
598
    // Normalize the result to [ 2, histImage.rows-2 ]
625
    // Normalize the result to [ 2, histImage.rows-2 ]
599
    cv::normalize(histogram, histogram, 2, histImage.rows-2, cv::NORM_MINMAX, -1, cv::Mat());
626
    cv::normalize(histogram, histogram, 2, histImage.rows-2, cv::NORM_MINMAX, -1, cv::Mat());
600
 
627
 
601
    float bin_w = (float)histImage.cols/(float)histogram.rows;
628
    float bin_w = (float)histImage.cols/(float)histogram.rows;
602
 
629
 
603
    // Draw main histogram
630
    // Draw main histogram
604
    for(int i = 1; i < histogram.rows-10; i++){
631
    for(int i = 1; i < histogram.rows-10; i++){
605
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
632
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
606
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
633
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
607
                 cv::Scalar(255, 255, 255), 2, 4);
634
                 cv::Scalar(255, 255, 255), 2, 4);
608
    }
635
    }
609
 
636
 
610
    // Draw red max
637
    // Draw red max
611
    for(int i = histogram.rows-10; i < histogram.rows; i++){
638
    for(int i = histogram.rows-10; i < histogram.rows; i++){
612
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
639
        cv::line(histImage, cv::Point( bin_w*(i-1), histImage.rows - cvRound(histogram.at<float>(i-1)) ),
613
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
640
                 cv::Point( bin_w*(i), histImage.rows - cvRound(histogram.at<float>(i)) ),
614
                 cv::Scalar(0, 0, 255), 2, 4);
641
                 cv::Scalar(0, 0, 255), 2, 4);
615
    }
642
    }
616
 
643
 
617
    return histImage;
644
    return histImage;
618
}
645
}
619
 
646
 
620
void hist(const char *windowName, cv::Mat histogram, unsigned int x, unsigned int y){
647
void hist(const char *windowName, cv::Mat histogram, unsigned int x, unsigned int y){
621
 
648
 
622
    // Display
649
    // Display
623
    imshow(windowName, histimage(histogram), x, y);
650
    imshow(windowName, histimage(histogram), x, y);
624
    cv::Point(1,2);
651
    cv::Point(1,2);
625
}
652
}
626
 
653
 
627
 
654
 
628
void writeMat(cv::Mat const& mat, const char* filename, const char* varName, bool bgr2rgb){
655
void writeMat(cv::Mat const& mat, const char* filename, const char* varName, bool bgr2rgb){
629
    /*!
656
    /*!
630
         *  \author Philip G. Lee <rocketman768@gmail.com>
657
         *  \author Philip G. Lee <rocketman768@gmail.com>
631
         *  Write \b mat into \b filename
658
         *  Write \b mat into \b filename
632
         *  in uncompressed .mat format (Level 5 MATLAB) for Matlab.
659
         *  in uncompressed .mat format (Level 5 MATLAB) for Matlab.
633
         *  The variable name in matlab will be \b varName. If
660
         *  The variable name in matlab will be \b varName. If
634
         *  \b bgr2rgb is true and there are 3 channels, swaps 1st and 3rd
661
         *  \b bgr2rgb is true and there are 3 channels, swaps 1st and 3rd
635
         *  channels in the output. This is needed because OpenCV matrices
662
         *  channels in the output. This is needed because OpenCV matrices
636
         *  are bgr, while Matlab is rgb. This has been tested to work with
663
         *  are bgr, while Matlab is rgb. This has been tested to work with
637
         *  3-channel single-precision floating point matrices, and I hope
664
         *  3-channel single-precision floating point matrices, and I hope
638
         *  it works on other types/channels, but not exactly sure.
665
         *  it works on other types/channels, but not exactly sure.
639
         *  Documentation at <http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf>
666
         *  Documentation at <http://www.mathworks.com/help/pdf_doc/matlab/matfile_format.pdf>
640
         */
667
         */
641
    int textLen = 116;
668
    int textLen = 116;
642
    char* text;
669
    char* text;
643
    int subsysOffsetLen = 8;
670
    int subsysOffsetLen = 8;
644
    char* subsysOffset;
671
    char* subsysOffset;
645
    int verLen = 2;
672
    int verLen = 2;
646
    char* ver;
673
    char* ver;
647
    char flags;
674
    char flags;
648
    int bytes;
675
    int bytes;
649
    int padBytes;
676
    int padBytes;
650
    int bytesPerElement;
677
    int bytesPerElement;
651
    int i,j,k,k2;
678
    int i,j,k,k2;
652
    bool doBgrSwap;
679
    bool doBgrSwap;
653
    char mxClass;
680
    char mxClass;
654
    int32_t miClass;
681
    int32_t miClass;
655
    uchar const* rowPtr;
682
    uchar const* rowPtr;
656
    uint32_t tmp32;
683
    uint32_t tmp32;
657
    float tmp;
684
    float tmp;
658
    FILE* fp;
685
    FILE* fp;
659
 
686
 
660
    // Matlab constants.
687
    // Matlab constants.
661
    const uint16_t MI = 0x4d49; // Contains "MI" in ascii.
688
    const uint16_t MI = 0x4d49; // Contains "MI" in ascii.
662
    const int32_t miINT8 = 1;
689
    const int32_t miINT8 = 1;
663
    const int32_t miUINT8 = 2;
690
    const int32_t miUINT8 = 2;
664
    const int32_t miINT16 = 3;
691
    const int32_t miINT16 = 3;
665
    const int32_t miUINT16 = 4;
692
    const int32_t miUINT16 = 4;
666
    const int32_t miINT32 = 5;
693
    const int32_t miINT32 = 5;
667
    const int32_t miUINT32 = 6;
694
    const int32_t miUINT32 = 6;
668
    const int32_t miSINGLE = 7;
695
    const int32_t miSINGLE = 7;
669
    const int32_t miDOUBLE = 9;
696
    const int32_t miDOUBLE = 9;
670
    const int32_t miMATRIX = 14;
697
    const int32_t miMATRIX = 14;
671
    const char mxDOUBLE_CLASS = 6;
698
    const char mxDOUBLE_CLASS = 6;
672
    const char mxSINGLE_CLASS = 7;
699
    const char mxSINGLE_CLASS = 7;
673
    const char mxINT8_CLASS = 8;
700
    const char mxINT8_CLASS = 8;
674
    const char mxUINT8_CLASS = 9;
701
    const char mxUINT8_CLASS = 9;
675
    const char mxINT16_CLASS = 10;
702
    const char mxINT16_CLASS = 10;
676
    const char mxUINT16_CLASS = 11;
703
    const char mxUINT16_CLASS = 11;
677
    const char mxINT32_CLASS = 12;
704
    const char mxINT32_CLASS = 12;
678
    const char mxUINT32_CLASS = 13;
705
    const char mxUINT32_CLASS = 13;
679
    const uint64_t zero = 0; // Used for padding.
706
    const uint64_t zero = 0; // Used for padding.
680
 
707
 
681
    fp = fopen( filename, "wb" );
708
    fp = fopen( filename, "wb" );
682
 
709
 
683
    if( fp == 0 )
710
    if( fp == 0 )
684
        return;
711
        return;
685
 
712
 
686
    const int rows = mat.rows;
713
    const int rows = mat.rows;
687
    const int cols = mat.cols;
714
    const int cols = mat.cols;
688
    const int chans = mat.channels();
715
    const int chans = mat.channels();
689
 
716
 
690
    doBgrSwap = (chans==3) && bgr2rgb;
717
    doBgrSwap = (chans==3) && bgr2rgb;
691
 
718
 
692
    // I hope this mapping is right :-/
719
    // I hope this mapping is right :-/
693
    switch( mat.depth() ){
720
    switch( mat.depth() ){
694
    case CV_8U:
721
    case CV_8U:
695
        mxClass = mxUINT8_CLASS;
722
        mxClass = mxUINT8_CLASS;
696
        miClass = miUINT8;
723
        miClass = miUINT8;
697
        bytesPerElement = 1;
724
        bytesPerElement = 1;
698
        break;
725
        break;
699
    case CV_8S:
726
    case CV_8S:
700
        mxClass = mxINT8_CLASS;
727
        mxClass = mxINT8_CLASS;
701
        miClass = miINT8;
728
        miClass = miINT8;
702
        bytesPerElement = 1;
729
        bytesPerElement = 1;
703
        break;
730
        break;
704
    case CV_16U:
731
    case CV_16U:
705
        mxClass = mxUINT16_CLASS;
732
        mxClass = mxUINT16_CLASS;
706
        miClass = miUINT16;
733
        miClass = miUINT16;
707
        bytesPerElement = 2;
734
        bytesPerElement = 2;
708
        break;
735
        break;
709
    case CV_16S:
736
    case CV_16S:
710
        mxClass = mxINT16_CLASS;
737
        mxClass = mxINT16_CLASS;
711
        miClass = miINT16;
738
        miClass = miINT16;
712
        bytesPerElement = 2;
739
        bytesPerElement = 2;
713
        break;
740
        break;
714
    case CV_32S:
741
    case CV_32S:
715
        mxClass = mxINT32_CLASS;
742
        mxClass = mxINT32_CLASS;
716
        miClass = miINT32;
743
        miClass = miINT32;
717
        bytesPerElement = 4;
744
        bytesPerElement = 4;
718
        break;
745
        break;
719
    case CV_32F:
746
    case CV_32F:
720
        mxClass = mxSINGLE_CLASS;
747
        mxClass = mxSINGLE_CLASS;
721
        miClass = miSINGLE;
748
        miClass = miSINGLE;
722
        bytesPerElement = 4;
749
        bytesPerElement = 4;
723
        break;
750
        break;
724
    case CV_64F:
751
    case CV_64F:
725
        mxClass = mxDOUBLE_CLASS;
752
        mxClass = mxDOUBLE_CLASS;
726
        miClass = miDOUBLE;
753
        miClass = miDOUBLE;
727
        bytesPerElement = 8;
754
        bytesPerElement = 8;
728
        break;
755
        break;
729
    default:
756
    default:
730
        return;
757
        return;
731
    }
758
    }
732
 
759
 
733
    //==================Mat-file header (128 bytes, page 1-5)==================
760
    //==================Mat-file header (128 bytes, page 1-5)==================
734
    text = new char[textLen]; // Human-readable text.
761
    text = new char[textLen]; // Human-readable text.
735
    memset( text, ' ', textLen );
762
    memset( text, ' ', textLen );
736
    text[textLen-1] = '\0';
763
    text[textLen-1] = '\0';
737
    const char* t = "MATLAB 5.0 MAT-file, Platform: PCWIN";
764
    const char* t = "MATLAB 5.0 MAT-file, Platform: PCWIN";
738
    memcpy( text, t, strlen(t) );
765
    memcpy( text, t, strlen(t) );
739
 
766
 
740
    subsysOffset = new char[subsysOffsetLen]; // Zeros for us.
767
    subsysOffset = new char[subsysOffsetLen]; // Zeros for us.
741
    memset( subsysOffset, 0x00, subsysOffsetLen );
768
    memset( subsysOffset, 0x00, subsysOffsetLen );
742
    ver = new char[verLen];
769
    ver = new char[verLen];
743
    ver[0] = 0x00;
770
    ver[0] = 0x00;
744
    ver[1] = 0x01;
771
    ver[1] = 0x01;
745
 
772
 
746
    fwrite( text, 1, textLen, fp );
773
    fwrite( text, 1, textLen, fp );
747
    fwrite( subsysOffset, 1, subsysOffsetLen, fp );
774
    fwrite( subsysOffset, 1, subsysOffsetLen, fp );
748
    fwrite( ver, 1, verLen, fp );
775
    fwrite( ver, 1, verLen, fp );
749
    // Endian indicator. MI will show up as "MI" on big-endian
776
    // Endian indicator. MI will show up as "MI" on big-endian
750
    // systems and "IM" on little-endian systems.
777
    // systems and "IM" on little-endian systems.
751
    fwrite( &MI, 2, 1, fp );
778
    fwrite( &MI, 2, 1, fp );
752
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
779
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
753
 
780
 
754
    //===================Data element tag (8 bytes, page 1-8)==================
781
    //===================Data element tag (8 bytes, page 1-8)==================
755
    bytes = 16 + 24 + (8 + strlen(varName) + (8-(strlen(varName)%8))%8)
782
    bytes = 16 + 24 + (8 + strlen(varName) + (8-(strlen(varName)%8))%8)
756
            + (8 + rows*cols*chans*bytesPerElement);
783
            + (8 + rows*cols*chans*bytesPerElement);
757
    fwrite( &miMATRIX, 4, 1, fp ); // Data type.
784
    fwrite( &miMATRIX, 4, 1, fp ); // Data type.
758
    fwrite( &bytes, 4, 1, fp); // Data size in bytes.
785
    fwrite( &bytes, 4, 1, fp); // Data size in bytes.
759
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
786
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
760
 
787
 
761
    //====================Array flags (16 bytes, page 1-15)====================
788
    //====================Array flags (16 bytes, page 1-15)====================
762
    bytes = 8;
789
    bytes = 8;
763
    fwrite( &miUINT32, 4, 1, fp );
790
    fwrite( &miUINT32, 4, 1, fp );
764
    fwrite( &bytes, 4, 1, fp );
791
    fwrite( &bytes, 4, 1, fp );
765
    flags = 0x00; // Complex, logical, and global flags all off.
792
    flags = 0x00; // Complex, logical, and global flags all off.
766
 
793
 
767
    tmp32 = 0;
794
    tmp32 = 0;
768
    tmp32 = (flags << 8 ) | (mxClass);
795
    tmp32 = (flags << 8 ) | (mxClass);
769
    fwrite( &tmp32, 4, 1, fp );
796
    fwrite( &tmp32, 4, 1, fp );
770
 
797
 
771
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
798
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
772
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
799
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
773
 
800
 
774
    //===============Dimensions subelement (24 bytes, page 1-17)===============
801
    //===============Dimensions subelement (24 bytes, page 1-17)===============
775
    bytes = 12;
802
    bytes = 12;
776
    fwrite( &miINT32, 4, 1, fp );
803
    fwrite( &miINT32, 4, 1, fp );
777
    fwrite( &bytes, 4, 1, fp );
804
    fwrite( &bytes, 4, 1, fp );
778
 
805
 
779
    fwrite( &rows, 4, 1, fp );
806
    fwrite( &rows, 4, 1, fp );
780
    fwrite( &cols, 4, 1, fp );
807
    fwrite( &cols, 4, 1, fp );
781
    fwrite( &chans, 4, 1, fp );
808
    fwrite( &chans, 4, 1, fp );
782
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
809
    fwrite( &zero, 4, 1, fp ); // Padding to 64-bit boundary.
783
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
810
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
784
 
811
 
785
    //==Array name (8 + strlen(varName) + (8-(strlen(varName)%8))%8 bytes, page 1-17)==
812
    //==Array name (8 + strlen(varName) + (8-(strlen(varName)%8))%8 bytes, page 1-17)==
786
    bytes = strlen(varName);
813
    bytes = strlen(varName);
787
 
814
 
788
    fwrite( &miINT8, 4, 1, fp );
815
    fwrite( &miINT8, 4, 1, fp );
789
    fwrite( &bytes, 4, 1, fp );
816
    fwrite( &bytes, 4, 1, fp );
790
    fwrite( varName, 1, bytes, fp );
817
    fwrite( varName, 1, bytes, fp );
791
 
818
 
792
    // Pad to nearest 64-bit boundary.
819
    // Pad to nearest 64-bit boundary.
793
    padBytes = (8-(bytes%8))%8;
820
    padBytes = (8-(bytes%8))%8;
794
    fwrite( &zero, 1, padBytes, fp );
821
    fwrite( &zero, 1, padBytes, fp );
795
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
822
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
796
 
823
 
797
    //====Matrix data (rows*cols*chans*bytesPerElement+8 bytes, page 1-20)=====
824
    //====Matrix data (rows*cols*chans*bytesPerElement+8 bytes, page 1-20)=====
798
    bytes = rows*cols*chans*bytesPerElement;
825
    bytes = rows*cols*chans*bytesPerElement;
799
    fwrite( &miClass, 4, 1, fp );
826
    fwrite( &miClass, 4, 1, fp );
800
    fwrite( &bytes, 4, 1, fp );
827
    fwrite( &bytes, 4, 1, fp );
801
 
828
 
802
    for( k = 0; k < chans; ++k )
829
    for( k = 0; k < chans; ++k )
803
    {
830
    {
804
        if( doBgrSwap )
831
        if( doBgrSwap )
805
        {
832
        {
806
            k2 = (k==0)? 2 : ((k==2)? 0 : 1);
833
            k2 = (k==0)? 2 : ((k==2)? 0 : 1);
807
        }
834
        }
808
        else
835
        else
809
            k2 = k;
836
            k2 = k;
810
 
837
 
811
        for( j = 0; j < cols; ++j )
838
        for( j = 0; j < cols; ++j )
812
        {
839
        {
813
            for( i = 0; i < rows; ++i )
840
            for( i = 0; i < rows; ++i )
814
            {
841
            {
815
                rowPtr = mat.data + mat.step*i;
842
                rowPtr = mat.data + mat.step*i;
816
                fwrite( rowPtr + (chans*j + k2)*bytesPerElement, bytesPerElement, 1, fp );
843
                fwrite( rowPtr + (chans*j + k2)*bytesPerElement, bytesPerElement, 1, fp );
817
            }
844
            }
818
        }
845
        }
819
    }
846
    }
820
 
847
 
821
    // Pad to 64-bit boundary.
848
    // Pad to 64-bit boundary.
822
    padBytes = (8-(bytes%8))%8;
849
    padBytes = (8-(bytes%8))%8;
823
    fwrite( &zero, 1, padBytes, fp );
850
    fwrite( &zero, 1, padBytes, fp );
824
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
851
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
825
 
852
 
826
    fclose(fp);
853
    fclose(fp);
827
    delete[] text;
854
    delete[] text;
828
    delete[] subsysOffset;
855
    delete[] subsysOffset;
829
    delete[] ver;
856
    delete[] ver;
830
}
857
}
831
 
858
 
832
 
859
 
833
 
860
 
834
 
861
 
835
 
862
 
836
}
863
}
837
 
864