00001
00016 #include <iomanip>
00017 #include <dlrCommon/inputStream.h>
00018 #include <dlrComputerVision/cameraIntrinsicsPlumbBob.h>
00019 #include <dlrOptimization/optimizerBFGS.h>
00020 #include <dlrOptimization/optimizerNelderMead.h>
00021
00022 using namespace dlr::numeric;
00023 using namespace dlr::geometry;
00024
00025 namespace {
00026
00027
00028
00029 const double l_maximumReverseProjectionResidual = 0.01 * 0.01;
00030
00031 }
00032
00033
00034 namespace dlr {
00035
00036 namespace computerVision {
00037
00038
00039
00040
00041 CameraIntrinsicsPlumbBob::
00042 CameraIntrinsicsPlumbBob()
00043 : CameraIntrinsics(),
00044 m_centerU(50),
00045 m_centerV(50),
00046 m_kX(1.0),
00047 m_kY(1.0),
00048 m_numPixelsX(100),
00049 m_numPixelsY(100),
00050 m_radialCoefficient0(0.0),
00051 m_radialCoefficient1(0.0),
00052 m_radialCoefficient2(0.0),
00053 m_skewCoefficient(0.0),
00054 m_tangentialCoefficient0(0.0),
00055 m_tangentialCoefficient1(0.0)
00056 {
00057
00058 }
00059
00060
00061
00062
00063 CameraIntrinsicsPlumbBob::
00064 CameraIntrinsicsPlumbBob(size_t numPixelsX,
00065 size_t numPixelsY,
00066 double focalLengthX,
00067 double focalLengthY,
00068 double centerU,
00069 double centerV,
00070 double skewCoefficient,
00071 double radialCoefficient0,
00072 double radialCoefficient1,
00073 double radialCoefficient2,
00074 double tangentialCoefficient0,
00075 double tangentialCoefficient1)
00076 : CameraIntrinsics(),
00077 m_centerU(centerU),
00078 m_centerV(centerV),
00079 m_kX(focalLengthX),
00080 m_kY(focalLengthY),
00081 m_numPixelsX(numPixelsX),
00082 m_numPixelsY(numPixelsY),
00083 m_radialCoefficient0(radialCoefficient0),
00084 m_radialCoefficient1(radialCoefficient1),
00085 m_radialCoefficient2(radialCoefficient2),
00086 m_skewCoefficient(skewCoefficient),
00087 m_tangentialCoefficient0(tangentialCoefficient0),
00088 m_tangentialCoefficient1(tangentialCoefficient1)
00089 {
00090
00091 }
00092
00093
00094
00095
00096
00097 Vector2D
00098 CameraIntrinsicsPlumbBob::
00099 project(const Vector3D& point) const
00100 {
00101
00102 Vector2D normalizedPoint(point.x(), point.y(), point.z());
00103
00104 double xSquared = normalizedPoint.x() * normalizedPoint.x();
00105 double ySquared = normalizedPoint.y() * normalizedPoint.y();
00106 double rSquared = xSquared + ySquared;
00107 double rFourth = rSquared * rSquared;
00108 double rSixth = rSquared * rFourth;
00109
00110
00111 double radialDistortion = (1.0 + m_radialCoefficient0 * rSquared
00112 + m_radialCoefficient1 * rFourth
00113 + m_radialCoefficient2 * rSixth);
00114
00115 double crossTerm = normalizedPoint.x() * normalizedPoint.y();
00116 Vector2D tangentialDistortion(
00117 (2.0 * m_tangentialCoefficient0 * crossTerm
00118 + m_tangentialCoefficient1 * (rSquared + 2.0 * xSquared)),
00119 (m_tangentialCoefficient0 * (rSquared + 2.0 * ySquared)
00120 + 2.0 * m_tangentialCoefficient1 * crossTerm));
00121
00122
00123 Vector2D distortedPoint(
00124 radialDistortion * normalizedPoint + tangentialDistortion);
00125 Vector2D skewedPoint(
00126 distortedPoint.x() + m_skewCoefficient * distortedPoint.y(),
00127 distortedPoint.y());
00128
00129 return Vector2D(m_kX * skewedPoint.x() + m_centerU,
00130 m_kY * skewedPoint.y() + m_centerV);
00131 }
00132
00133
00134
00135
00136 std::istream&
00137 CameraIntrinsicsPlumbBob::
00138 readFromStream(std::istream& stream)
00139 {
00140
00141 if (!stream){
00142 return stream;
00143 }
00144
00145
00146
00147 InputStream inputStream(stream, InputStream::SKIP_WHITESPACE);
00148
00149 double centerU;
00150 double centerV;
00151 double kX;
00152 double kY;
00153 size_t numpixelsX;
00154 size_t numpixelsY;
00155 double radialCoefficient0;
00156 double radialCoefficient1;
00157 double radialCoefficient2;
00158 double skewCoefficient;
00159 double tangentialCoefficient0;
00160 double tangentialCoefficient1;
00161
00162 inputStream.expect("CameraIntrinsicsPlumbBob");
00163 inputStream.expect("{");
00164 stream >> numpixelsX;
00165 inputStream.expect(",");
00166 stream >> numpixelsY;
00167 inputStream.expect(",");
00168 stream >> kX;
00169 inputStream.expect(",");
00170 stream >> kY;
00171 inputStream.expect(",");
00172 stream >> centerU;
00173 inputStream.expect(",");
00174 stream >> centerV;
00175 inputStream.expect(",");
00176 stream >> skewCoefficient;
00177 inputStream.expect(",");
00178 stream >> radialCoefficient0;
00179 inputStream.expect(",");
00180 stream >> radialCoefficient1;
00181 inputStream.expect(",");
00182 stream >> radialCoefficient2;
00183 inputStream.expect(",");
00184 stream >> tangentialCoefficient0;
00185 inputStream.expect(",");
00186 stream >> tangentialCoefficient1;
00187 inputStream.expect("}");
00188
00189 if(stream) {
00190 m_centerU = centerU;
00191 m_centerV = centerV;
00192 m_kX = kX;
00193 m_kY = kY;
00194 m_numPixelsX = numpixelsX;
00195 m_numPixelsY = numpixelsY;
00196 m_radialCoefficient0 = radialCoefficient0;
00197 m_radialCoefficient1 = radialCoefficient1;
00198 m_radialCoefficient2 = radialCoefficient2;
00199 m_skewCoefficient = skewCoefficient;
00200 m_tangentialCoefficient0 = tangentialCoefficient0;
00201 m_tangentialCoefficient1 = tangentialCoefficient1;
00202 }
00203 return stream;
00204 }
00205
00206
00207
00208
00209
00210
00211 geometry::Ray3D
00212 CameraIntrinsicsPlumbBob::
00213 reverseProject(const Vector2D& pixelPosition,
00214 bool normalize) const
00215 {
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234 Array1D<double> startPoint(2);
00235 startPoint = 0.0;
00236
00237
00238 privateCode::PlumbBobObjective objective(*this, pixelPosition);
00239 OptimizerBFGS<privateCode::PlumbBobObjective> optimizer(objective);
00240 optimizer.setStartPoint(startPoint);
00241 Array1D<double> endPoint = optimizer.optimum();
00242 double residual = optimizer.optimalValue();
00243
00244
00245
00246
00247 if(residual
00248 > l_maximumReverseProjectionResidual + objective.getOffset()) {
00249 DLR_THROW(ValueException, "CameraIntrinsicsPlumbBob::reverseProject()",
00250 "Reverse projection failed to converge.");
00251 }
00252
00253 return Ray3D(Vector3D(0.0, 0.0, 0.0),
00254 Vector3D(endPoint[0], endPoint[1], 1.0, normalize));
00255 }
00256
00257
00258
00259
00260
00261 std::ostream&
00262 CameraIntrinsicsPlumbBob::
00263 writeToStream(std::ostream& stream) const
00264 {
00265 std::ios::fmtflags flags = stream.flags();
00266 std::streamsize precision = stream.precision();
00267 stream.precision(15);
00268 stream << "CameraIntrinsicsPlumbBob {"
00269 << std::fixed << std::setw(15)
00270 << m_numPixelsX << ", "
00271 << m_numPixelsY << ", "
00272 << m_kX << ", "
00273 << m_kY << ", "
00274 << m_centerU << ", "
00275 << m_centerV << ", "
00276 << m_skewCoefficient << ", "
00277 << m_radialCoefficient0 << ", "
00278 << m_radialCoefficient1 << ", "
00279 << m_radialCoefficient2 << ", "
00280 << m_tangentialCoefficient0 << ", "
00281 << m_tangentialCoefficient1 << "}";
00282 stream.precision(precision);
00283 stream.flags(flags);
00284 return stream;
00285 }
00286
00287
00288 void
00289 CameraIntrinsicsPlumbBob::
00290 projectWithPartialDerivatives(double xNorm,
00291 double yNorm,
00292 double& uValue,
00293 double& vValue,
00294 double& dUdX,
00295 double& dUdY,
00296 double& dVdX,
00297 double& dVdY) const
00298 {
00299 double xSquared = xNorm * xNorm;
00300 double ySquared = yNorm * yNorm;
00301 double rSquared = xSquared + ySquared;
00302 double rFourth = rSquared * rSquared;
00303 double rSixth = rSquared * rFourth;
00304
00305 double dRSquaredDX = 2.0 * xNorm;
00306 double dRSquaredDY = 2.0 * yNorm;
00307 double dRFourthDX = 2.0 * rSquared * dRSquaredDX;
00308 double dRFourthDY = 2.0 * rSquared * dRSquaredDY;
00309 double dRSixthDX = 3.0 * rFourth * dRSquaredDX;
00310 double dRSixthDY = 3.0 * rFourth * dRSquaredDY;
00311
00312
00313 double radialDistortion = (1.0 + m_radialCoefficient0 * rSquared
00314 + m_radialCoefficient1 * rFourth
00315 + m_radialCoefficient2 * rSixth);
00316 double dRadialDistortionDX =
00317 (m_radialCoefficient0 * dRSquaredDX
00318 + m_radialCoefficient1 * dRFourthDX
00319 + m_radialCoefficient2 * dRSixthDX);
00320 double dRadialDistortionDY =
00321 (m_radialCoefficient0 * dRSquaredDY
00322 + m_radialCoefficient1 * dRFourthDY
00323 + m_radialCoefficient2 * dRSixthDY);
00324
00325 double crossTerm = xNorm * yNorm;
00326 double dCrossTermDX = yNorm;
00327 double dCrossTermDY = xNorm;
00328
00329 Vector2D tangentialDistortion(
00330 (2.0 * m_tangentialCoefficient0 * crossTerm
00331 + m_tangentialCoefficient1 * (rSquared + 2.0 * xSquared)),
00332 (m_tangentialCoefficient0 * (rSquared + 2.0 * ySquared)
00333 + 2.0 * m_tangentialCoefficient1 * crossTerm));
00334 double dTangXDX =
00335 (2.0 * m_tangentialCoefficient0 * dCrossTermDX
00336 + m_tangentialCoefficient1 * (dRSquaredDX + 4.0 * xNorm));
00337 double dTangXDY =
00338 (2.0 * m_tangentialCoefficient0 * dCrossTermDY
00339 + m_tangentialCoefficient1 * dRSquaredDY);
00340 double dTangYDX =
00341 (m_tangentialCoefficient0 * dRSquaredDX
00342 + 2.0 * m_tangentialCoefficient1 * dCrossTermDX);
00343 double dTangYDY =
00344 (m_tangentialCoefficient0 * (dRSquaredDY + 4.0 * yNorm)
00345 + 2.0 * m_tangentialCoefficient1 * dCrossTermDY);
00346
00347
00348 Vector2D distortedPoint(
00349 radialDistortion * xNorm + tangentialDistortion.x(),
00350 radialDistortion * yNorm + tangentialDistortion.y());
00351
00352 double dDistortedXDX =
00353 dRadialDistortionDX * xNorm + radialDistortion + dTangXDX;
00354 double dDistortedXDY = dRadialDistortionDY * xNorm + dTangXDY;
00355 double dDistortedYDX = dRadialDistortionDX * yNorm + dTangYDX;
00356 double dDistortedYDY =
00357 dRadialDistortionDY * yNorm + radialDistortion + dTangYDY;
00358
00359 Vector2D skewedPoint(
00360 distortedPoint.x() + m_skewCoefficient * distortedPoint.y(),
00361 distortedPoint.y());
00362 double dSkewedXDX = dDistortedXDX + m_skewCoefficient * dDistortedYDX;
00363 double dSkewedXDY = dDistortedXDY + m_skewCoefficient * dDistortedYDY;
00364 double dSkewedYDX = dDistortedYDX;
00365 double dSkewedYDY = dDistortedYDY;
00366
00367 uValue = m_kX * skewedPoint.x() + m_centerU;
00368 vValue = m_kY * skewedPoint.y() + m_centerV;
00369 dUdX = m_kX * dSkewedXDX;
00370 dUdY = m_kX * dSkewedXDY;
00371 dVdX = m_kY * dSkewedYDX;
00372 dVdY = m_kY * dSkewedYDY;
00373 }
00374
00375
00376
00377 namespace privateCode {
00378
00379 PlumbBobObjective::
00380 PlumbBobObjective(const CameraIntrinsicsPlumbBob& intrinsics,
00381 const Vector2D& uvTarget)
00382 : m_intrinsics(intrinsics),
00383 m_offset(0.0001),
00384 m_uvTarget(uvTarget)
00385 {
00386
00387 }
00388
00389
00390 double
00391 PlumbBobObjective::
00392 operator()(const Array1D<double>& theta)
00393 {
00394 Vector3D candidate(theta[0], theta[1], 1.0);
00395 Vector2D projection = m_intrinsics.project(candidate);
00396
00397
00398
00399
00400
00401 double boundsPenalty = this->computeBoundsPenalty(projection);
00402
00403 return (magnitudeSquared(projection - m_uvTarget) + boundsPenalty
00404 + m_offset);
00405 }
00406
00407
00408 double
00409 PlumbBobObjective::
00410 getOffset()
00411 {
00412 return m_offset;
00413 }
00414
00415
00416 Array1D<double>
00417 PlumbBobObjective::
00418 gradient(const Array1D<double>& theta)
00419 {
00420 double uValue;
00421 double vValue;
00422 double dUdX;
00423 double dUdY;
00424 double dVdX;
00425 double dVdY;
00426 m_intrinsics.projectWithPartialDerivatives(
00427 theta[0], theta[1], uValue, vValue, dUdX, dUdY, dVdX, dVdY);
00428
00429 double twoTimesDeltaU = 2.0 * (uValue - m_uvTarget.x());
00430 double twoTimesDeltaV = 2.0 * (vValue - m_uvTarget.y());
00431
00432 Array1D<double> gradient(2);
00433 gradient[0] = twoTimesDeltaU * dUdX + twoTimesDeltaV * dVdX;
00434 gradient[1] = twoTimesDeltaU * dUdY + twoTimesDeltaV * dVdY;
00435
00436 double dPdX;
00437 double dPdY;
00438 this->computeBoundsPenaltyGradient(
00439 uValue, vValue, dUdX, dUdY, dVdX, dVdY, dPdX, dPdY);
00440 gradient[0] += dPdX;
00441 gradient[1] += dPdY;
00442
00443 return gradient;
00444 }
00445
00446
00447 double
00448 PlumbBobObjective::
00449 computeBoundsPenalty(const Vector2D& uvPosition)
00450 {
00451 double uViolation = 0.0;
00452 double scaleU = m_intrinsics.getNumPixelsX() / 10.0;
00453 if(uvPosition.x() < 0) {
00454 uViolation = -uvPosition.x() / scaleU;
00455 } else if(uvPosition.x() >= m_intrinsics.getNumPixelsX()) {
00456 uViolation = ((uvPosition.x() - m_intrinsics.getNumPixelsX())
00457 / scaleU);
00458 }
00459 double vViolation = 0.0;
00460 double scaleV = m_intrinsics.getNumPixelsY() / 10.0;
00461 if(uvPosition.y() < 0) {
00462 vViolation = -uvPosition.y() / scaleV;
00463 } else if(uvPosition.y() >= m_intrinsics.getNumPixelsY()) {
00464 vViolation = ((uvPosition.y() - m_intrinsics.getNumPixelsY())
00465 / scaleV);
00466 }
00467
00468
00469
00470 uViolation *= uViolation;
00471 uViolation *= uViolation;
00472 uViolation *= uViolation;
00473 vViolation *= vViolation;
00474 vViolation *= vViolation;
00475 vViolation *= vViolation;
00476 return uViolation + vViolation;
00477 }
00478
00479
00480 void
00481 PlumbBobObjective::
00482 computeBoundsPenaltyGradient(double uValue, double vValue,
00483 double dUdX, double dUdY,
00484 double dVdX, double dVdY,
00485 double& dPdX, double& dPdY)
00486 {
00487 double uViolation = 0.0;
00488 double scaleU = m_intrinsics.getNumPixelsX() / 10.0;
00489 double dUVdX = 0.0;
00490 double dUVdY = 0.0;
00491 if(uValue < 0) {
00492 uViolation = -uValue / scaleU;
00493 dUVdX = -dUdX / scaleU;
00494 dUVdY = -dUdY / scaleU;
00495 } else if(uValue >= m_intrinsics.getNumPixelsX()) {
00496 uViolation = (uValue - m_intrinsics.getNumPixelsX()) / scaleU;
00497 dUVdX = dUdX / scaleU;
00498 dUVdY = dUdY / scaleU;
00499 }
00500 double vViolation = 0.0;
00501 double scaleV = m_intrinsics.getNumPixelsY() / 10.0;
00502 double dVVdX = 0.0;
00503 double dVVdY = 0.0;
00504 if(vValue < 0) {
00505 vViolation = -vValue / scaleV;
00506 dVVdX = -dVdX / scaleV;
00507 dVVdY = -dVdY / scaleV;
00508 } else if(vValue >= m_intrinsics.getNumPixelsY()) {
00509 vViolation = (vValue - m_intrinsics.getNumPixelsY()) / scaleV;
00510 dVVdX = dVdX / scaleV;
00511 dVVdY = dVdY / scaleV;
00512 }
00513
00514
00515 double uViolationTo2 = uViolation * uViolation;
00516 double uViolationTo4 = uViolationTo2 * uViolationTo2;
00517 double uViolationTo7 = uViolationTo4 * uViolationTo2 * uViolation;
00518 double vViolationTo2 = vViolation * vViolation;
00519 double vViolationTo4 = vViolationTo2 * vViolationTo2;
00520 double vViolationTo7 = vViolationTo4 * vViolationTo2 * vViolation;
00521
00522 dPdX = 8.0 * uViolationTo7 * dUVdX + 8.0 * vViolationTo7 * dVVdX;
00523 dPdY = 8.0 * uViolationTo7 * dUVdY + 8.0 * vViolationTo7 * dVVdY;
00524 }
00525
00526
00527 }
00528
00529 }
00530
00531 }