Subversion Repositories seema-scanner

Rev

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

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