8 #include <cvd/image_io.h> 9 #include <cvd/vector_image_ref.h> 10 #include <cvd/vision.h> 20 static int DEBUG_ESM = 0;
67 double RMSE()
const {
return std::sqrt(error / std::max(1, pixels)); }
70 inline std::ostream& operator<<(std::ostream& out,
const ESMResult& r)
81 inline TooN::Vector<2> getBounds(
const TooN::Vector<2>& rect,
const TooN::Vector<3>& A,
const TooN::Vector<3>& B);
82 inline std::vector<TooN::Vector<2, int>> getBounds(
const TooN::Vector<2>& rect,
const ImageRef& in,
const TooN::Matrix<3>& H);
83 inline std::vector<TooN::Vector<2, int>> erode(
const std::vector<TooN::Vector<2, int>>& in);
87 inline std::vector<TooN::Vector<2, int>> transform_perspective(
BasicImage<T>& out,
const BasicImage<T>& in,
const TooN::Matrix<3>& H);
90 inline std::vector<TooN::Vector<2, int>> transform_affine(
BasicImage<T>& out,
const BasicImage<T>& in,
const TooN::Matrix<3>& H);
94 inline std::vector<TooN::Vector<2, int>> transform_translation(
BasicImage<T>& out,
const BasicImage<T>& in,
const TooN::Matrix<3>& H);
101 template <
typename GRADIENT,
typename IMAGE>
105 template <
typename GRADIENT,
typename IMAGE>
120 template <
typename TRANSFORM,
typename APPEARANCE,
typename IMAGE,
typename GRADIENT>
136 template <
int PARAMS>
140 static const int dimensions = PARAMS;
147 template <
int R,
int C,
typename P,
typename B>
153 const TooN::Matrix<3>& get_matrix()
const {
return H; }
154 TooN::Matrix<3>& get_matrix() {
return H; }
155 const TooN::Vector<PARAMS>& get_jacobian(
const TooN::Vector<2>& p,
const TooN::Vector<2>& grad)
const 161 J[7] = -p[1] * (grad * p);
164 J[6] = -p[0] * (grad * p);
167 J[5] = grad[0] * p[1] + grad[1] * p[0];
170 J[4] = grad[0] * p[0] - grad[1] * p[1];
173 J[3] = 3 * (grad * p);
176 J[2] = -grad[0] * p[1] + grad[1] * p[0];
188 void update(
const TooN::Vector<PARAMS>& v)
190 TooN::Matrix<3> G = TooN::Zeros;
206 G(2, 2) += -2 * v[3];
218 H = H * TooN::exp(-G);
223 mutable TooN::Vector<PARAMS> J;
232 template <
int PARAMS>
236 static const int dimensions = PARAMS;
239 : Pre(TooN::Identity)
243 template <
int R,
int C,
typename P,
typename B>
249 template <
int R,
int C,
typename P,
typename B,
int R2,
int C2,
typename P2,
typename B2>
250 HomographyPrefix(
const TooN::Matrix<R, C, P, B>& h,
const TooN::Matrix<R2, C2, P2, B2>& p)
256 const TooN::Matrix<3> get_matrix()
const {
return H * Pre; }
258 template <
int R,
int C,
typename P,
typename B>
259 void set_prefix(
const TooN::Matrix<R, C, P, B>& p)
262 const TooN::Matrix<3>
id = TooN::Identity;
263 H = TooN::gaussian_elimination(Pre,
id);
264 PreGrad = H.T().template slice<0, 0, 2, 2>();
267 const TooN::Vector<PARAMS>& get_jacobian(
const TooN::Vector<2>& x,
const TooN::Vector<2>& grad)
const 271 const TooN::Vector<2> p = TooN::project(Pre * TooN::unproject(x));
280 TooN::Matrix<2> PreGrad;
290 static const int dimensions = 3;
294 template <
typename P,
typename B>
295 CameraRotation(
const TooN::Vector<4, P, B>& camera_params,
const TooN::SO3<>& init = TooN::SO3<>())
298 set_camera(camera_params);
301 template <
typename P,
typename B>
302 void set_camera(
const TooN::Vector<4, P, B>& camera_params)
306 K(0, 0) = camera_params[0];
307 K(1, 1) = camera_params[1];
308 K(0, 2) = camera_params[2];
309 K(1, 2) = camera_params[3];
311 kinv = TooN::makeVector(1 / camera_params[0], 1 / camera_params[1], -camera_params[2] / camera_params[0], -camera_params[3] / camera_params[1]);
312 Kinv = TooN::Identity;
313 Kinv(0, 0) = kinv[0];
314 Kinv(1, 1) = kinv[1];
315 Kinv(0, 2) = kinv[2];
316 Kinv(1, 2) = kinv[3];
319 const TooN::Matrix<3> get_matrix()
const {
return K * R * Kinv; }
321 const TooN::Vector<dimensions>& get_jacobian(
const TooN::Vector<2>& p,
const TooN::Vector<2>& grad)
const 324 const TooN::Vector<3> pW = TooN::makeVector(kinv[0] * p[0] + kinv[2], kinv[1] * p[1] + kinv[3], 1);
325 const TooN::Vector<3> J_pK = TooN::makeVector(grad[0] * k[0], grad[1] * k[1], grad * (k.slice<2, 2>() - p));
326 J[0] = J_pK * TooN::SO3<>::generator_field(0, pW);
327 J[1] = J_pK * TooN::SO3<>::generator_field(1, pW);
328 J[2] = J_pK * TooN::SO3<>::generator_field(2, pW);
332 void update(
const TooN::Vector<dimensions>& v)
334 R = R * TooN::SO3<>::exp(-v);
337 const TooN::SO3<>& get_rotation()
const {
return R; }
338 TooN::SO3<>& get_rotation() {
return R; }
344 mutable TooN::Vector<dimensions>
J;
352 static const int dimensions = 0;
353 template <
typename PIXEL>
354 double difference(
const PIXEL& warped,
const PIXEL& templatePixel)
const 356 return warped - templatePixel;
358 template <
typename PIXEL>
359 std::pair<double, TooN::Vector<dimensions>> difference_jacobian(
const PIXEL& warped,
const PIXEL& templatePixel)
const 361 return std::make_pair(difference(warped, templatePixel), TooN::Vector<dimensions>());
363 TooN::Vector<2> image_jacobian(
const TooN::Vector<2>& gradWarped,
const TooN::Vector<2>& gradTemplate)
const 365 return (gradWarped + gradTemplate) * 0.25;
367 void update(
const TooN::Vector<dimensions>& d) { }
376 static const int dimensions = 1;
377 template <
typename PIXEL>
378 double difference(
const PIXEL& warped,
const PIXEL& templatePixel)
const 380 return warped - templatePixel - offset;
382 template <
typename PIXEL>
383 std::pair<double, TooN::Vector<dimensions>> difference_jacobian(
const PIXEL& warped,
const PIXEL& templatePixel)
const 385 return std::make_pair(difference(warped, templatePixel), TooN::makeVector(1));
387 TooN::Vector<2> image_jacobian(
const TooN::Vector<2>& gradWarped,
const TooN::Vector<2>& gradTemplate)
const 389 return (gradWarped + gradTemplate) * 0.25;
391 void update(
const TooN::Vector<dimensions>& d)
410 static const int dimensions = 0;
411 template <
typename PIXEL>
412 double difference(
const PIXEL& warped,
const PIXEL& templatePixel)
const 414 return warped - templatePixel;
416 template <
typename PIXEL>
417 std::pair<double, TooN::Vector<dimensions>> difference_jacobian(
const PIXEL& warped,
const PIXEL& templatePixel)
const 419 return std::make_pair(difference(warped, templatePixel), TooN::Vector<dimensions>());
421 TooN::Vector<2> image_jacobian(
const TooN::Vector<2>& gradWarped,
const TooN::Vector<2>& gradTemplate)
const 423 return (gradWarped + 0.5 * gradTemplate) * 0.25;
425 void update(
const TooN::Vector<dimensions>& d)
432 class LinearAppearance {
433 static const int dimensions = 2;
435 template <
typename PIXEL>
double difference(
const PIXEL & templatePixel,
const PIXEL & warped )
const {
return warped - templatePixel*scale - offset; }
436 Vector<2> image_jacobian(
const Vector<2> & gradTemplate,
const Vector<2> & gradImage )
const {
return gradTemplate + gradImage; }
437 Vector<dimensions> get_jacobian(
const Vector<2> & point ) {
return TooN::makeVector(1.0); }
438 void update(
const Vector<dimensions> & d ) { offset += d[0]; }
445 template <
typename P>
446 inline TooN::Matrix<3, 3, P> scaleHomography(
const TooN::Matrix<3, 3, P>& H,
const P& f)
448 const TooN::Vector<3, P> s = TooN::makeVector<P>(1 / f, 1 / f, 1);
449 const TooN::Vector<3, P> is = TooN::makeVector<P>(f, f, 1);
450 return is.as_diagonal() * H * s.as_diagonal();
457 template<
int PARAMS,
typename APPEARANCE,
typename IMAGE>
459 TooN::Matrix<3> prefix = TooN::Identity;
461 prefix(0,2) = -from.
size().x/2;
462 prefix(1,2) = -from.
size().y/2;
464 prefix(0,0) = 2.0/from.
size().x;
465 prefix(1,1) = 2.0/from.
size().y;
468 cout << prefix << endl;
472 APPEARANCE appearance;
473 ESMTransform<HomographyPrefix<PARAMS>, APPEARANCE> T(H, appearance);
479 return T.transform.get_matrix();
487 template <typename TRANSFORM, typename APPEARANCE, typename IMAGE = CVD::byte, typename GRADIENT = TooN::Vector<2, typename Pixel::traits<IMAGE>::wider_type>>
492 APPEARANCE appearance;
523 templGradient = Internal::gradient<IMAGE, GRADIENT>(templ);
531 templGradient = Internal::gradient<IMAGE, GRADIENT>(templ);
544 templGradient = Internal::gradient<GRADIENT>(templ);
556 transform = TRANSFORM();
557 appearance = APPEARANCE();
562 return result =
Internal::esm_opt(transform, appearance, templ, templGradient, to, max_iterations, min_delta, max_RMSE);
568 return optimize(from, fromGradient, to);
573 return result =
Internal::esm_opt(transform, appearance, from, fromGradient, to, max_iterations, min_delta, max_RMSE);
576 const ESMResult& get_result()
const {
return result; }
581 template <typename APPEARANCE, typename IMAGE = CVD::byte, typename GRADIENT = TooN::Vector<2, typename Pixel::traits<IMAGE>::wider_type>>
588 RotationEstimator(
const TooN::Vector<4>& cam_params,
const TooN::SO3<>& init = TooN::SO3<>())
590 transform.set_camera(cam_params);
591 transform.get_rotation() = init;
596 transform.get_rotation() = TooN::SO3<>();
597 appearance = APPEARANCE();
607 inline TooN::Vector<2> getBounds(
const TooN::Vector<2>& rect,
const TooN::Vector<3>& A,
const TooN::Vector<3>& B)
609 const TooN::Vector<3> dir = A - B;
610 TooN::Vector<2> interval = TooN::makeVector(0, 1);
612 TooN::Matrix<4, 3> lines;
613 lines[0] = TooN::makeVector(1, 0, 0);
614 lines[1] = TooN::makeVector(-1, 0, rect[0]);
615 lines[2] = TooN::makeVector(0, 1, 0);
616 lines[3] = TooN::makeVector(0, -1, rect[1]);
618 const TooN::Vector<4> hitA = lines * A;
619 const TooN::Vector<4> hitB = lines * B;
621 for(
int i = 0; i < 4; ++i)
623 if(hitA[i] <= 0 && hitB[i] <= 0)
624 return TooN::makeVector(0, 0);
625 if(hitA[i] * hitB[i] < 0)
627 const double t = hitA[i] / (lines[i] * dir);
630 interval[0] = std::max(t, interval[0]);
634 interval[1] = std::min(t, interval[1]);
639 if(interval[0] > interval[1])
640 return TooN::makeVector(0, 0);
644 inline std::vector<TooN::Vector<2, int>> getBounds(
const TooN::Vector<2>& rect,
const ImageRef& in,
const TooN::Matrix<3>& H)
646 std::vector<TooN::Vector<2, int>> bounds(in.
y);
647 for(
int y = 0; y < in.
y; ++y)
650 const TooN::Vector<3> A = H * TooN::makeVector(0, y, 1);
651 const TooN::Vector<3> B = H * TooN::makeVector(in.
x - 1, y, 1);
653 const TooN::Vector<2> b = getBounds(rect, A, B) * (in.
x - 1);
654 bounds[y] = TooN::makeVector<int>(ceil(b[0]), floor(b[1]) + 1);
659 inline std::vector<TooN::Vector<2, int>> erode(
const std::vector<TooN::Vector<2, int>>& in)
663 std::vector<TooN::Vector<2, int>> out(in.size());
664 out.front() = out.back() = TooN::makeVector(0, 0);
665 for(
unsigned i = 1; i < in.size() - 1; ++i)
667 out[i][0] = max(in[i - 1][0], max(in[i][0] + 1, in[i + 1][0]));
668 out[i][1] = min(in[i - 1][1], min(in[i][1] - 1, in[i + 1][1]));
669 if(out[i][0] > out[i][1])
670 out[i][0] = out[i][1] = 0;
676 template <
typename T>
677 inline std::vector<TooN::Vector<2, int>> transform_perspective(
BasicImage<T>& out,
const BasicImage<T>& in,
const TooN::Matrix<3>& H)
681 const TooN::Vector<3> across = H.T()[0];
682 const TooN::Vector<3> down = H.T()[1];
683 TooN::Vector<3> base = H.T()[2];
685 const std::vector<TooN::Vector<2, int>> bounds = getBounds(insize, size, H);
687 for(
int y = 0; y < size.
y; ++y)
689 TooN::Vector<3> X = base + bounds[y][0] * across;
690 T* data = out[y] + bounds[y][0];
691 for(
int x = bounds[y][0]; x < bounds[y][1]; ++x, X += across, ++data)
693 const TooN::DefaultPrecision inv = 1 / X[2];
694 sample(in, X[0] * inv, X[1] * inv, *data);
703 template <
typename T>
704 inline std::vector<TooN::Vector<2, int>> transform_affine(
BasicImage<T>& out,
const BasicImage<T>& in,
const TooN::Matrix<3>& H)
708 const TooN::Vector<2> across = H.T()[0].slice<0, 2>();
709 const TooN::Vector<2> down = H.T()[1].slice<0, 2>();
710 TooN::Vector<2> base = H.T()[2].slice<0, 2>();
712 const std::vector<TooN::Vector<2, int>> bounds = getBounds(insize, size, H);
714 for(
int y = 0; y < size.
y; ++y)
716 TooN::Vector<2> X = base + bounds[y][0] * across;
717 T* data = out[y] + bounds[y][0];
718 for(
int x = bounds[y][0]; x < bounds[y][1]; ++x, X += across, ++data)
720 sample(in, X[0], X[1], *data);
730 template <
typename T>
731 inline std::vector<TooN::Vector<2, int>> transform_translation(
BasicImage<T>& out,
const BasicImage<T>& in,
const TooN::Matrix<3>& H)
735 const TooN::Vector<2> across = H.T()[0].slice<0, 2>();
736 const TooN::Vector<2> down = H.T()[1].slice<0, 2>();
737 TooN::Vector<2> base = H.T()[2].slice<0, 2>();
739 std::vector<TooN::Vector<2, int>> bounds = getBounds(insize, size, H);
741 const double fx = H(0, 2) - floor(H(0, 2));
742 const double fy = H(1, 2) - floor(H(1, 2));
743 const double ul = fx * fy;
744 const double ur = (1 - fx) * fy;
745 const double ll = fx * (1 - fy);
746 const double lr = (1 - fx) * (1 - fy);
748 for(
int y = 0; y < size.
y; ++y)
750 const TooN::Vector<2>& interval = bounds[y];
752 T* data = out[y] + x;
753 TooN::Vector<2> X = base + x * across;
754 for(; x < interval[1]; ++x, ++data, X += across)
756 sample(in, X[0], X[1], *data);
764 template <
typename T>
767 const double perspective = H(2, 0) * H(2, 0) + H(2, 1) * H(2, 1);
768 if(perspective < 1e-10)
769 return transform_affine(out, in, H);
770 return transform_perspective(out, in, H);
773 template <
typename GRADIENT,
typename IMAGE>
776 assert(bounds.size() == unsigned(img.
size().y));
778 for(
int y = 1; y < img.
size().y - 1; ++y)
780 for(
int x = bounds[y][0]; x < bounds[y][1]; ++x)
782 grad[y][x][0] = img[y][x + 1] - img[y][x - 1];
783 grad[y][x][1] = img[y + 1][x] - img[y - 1][x];
789 template <
typename GRADIENT,
typename IMAGE>
793 for(
int y = 1; y < img.
size().y - 1; ++y)
795 for(
int x = 1; x < img.
size().x - 1; ++x)
797 grad[y][x][0] = img[y][x + 1] - img[y][x - 1];
798 grad[y][x][1] = img[y + 1][x] - img[y - 1][x];
804 template <
typename TRANSFORM,
typename APPEARANCE,
typename IMAGE,
typename GRADIENT>
807 assert(templateImage.
size() == templateGradient.
size());
809 const int dimensions = TRANSFORM::dimensions + APPEARANCE::dimensions;
813 TooN::WLS<dimensions> wls;
815 ESMResult result = { 1e100, 1e100, 0, 0 };
818 std::vector<TooN::Vector<2, int>> mask = Internal::transform(warped, target, T.get_matrix());
819 mask = Internal::erode(mask);
824 Image<GRADIENT> grad_warped = Internal::gradient<GRADIENT>(warped, mask);
828 wls.add_prior(1e-10);
829 TooN::Vector<dimensions> J;
830 for(
unsigned y = 0; y < mask.size(); ++y)
832 for(
int x = mask[y][0]; x < mask[y][1]; ++x)
834 std::pair<double, TooN::Vector<APPEARANCE::dimensions>> da = A.difference_jacobian(warped[y][x], templateImage[y][x]);
835 J.template slice<0, TRANSFORM::dimensions>() = T.get_jacobian(TooN::makeVector(x, y), A.image_jacobian(grad_warped[y][x], templateGradient[y][x]));
836 J.template slice<TRANSFORM::dimensions, APPEARANCE::dimensions>() = da.second;
837 wls.add_mJ(da.first, J);
843 T.update(wls.get_mu().template slice<0, TRANSFORM::dimensions>());
844 A.update(wls.get_mu().template slice<TRANSFORM::dimensions, APPEARANCE::dimensions>());
848 mask = Internal::transform(warped, target, T.get_matrix());
849 mask = Internal::erode(mask);
855 for(
unsigned y = 0; y < mask.size(); ++y)
857 result.
pixels += mask[y][1] - mask[y][0];
858 for(
int x = mask[y][0]; x < mask[y][1]; ++x)
860 const double d = A.difference(warped[y][x], templateImage[y][x]);
861 result.
error += d * d;
866 result.
delta = norm(wls.get_mu());
870 std::ostringstream sout;
871 sout <<
"debug_" << std::setw(4) << std::setfill(
'0') << DEBUG_ESM <<
"_" << std::setw(4) << std::setfill(
'0') << result.
iterations <<
".jpg";
874 Internal::transform(warped, target, T.get_matrix());
876 std::cout << wls.get_mu() <<
"\t" << T << std::endl;
877 std::cout << wls.get_C_inv() << std::endl;
878 std::cout << wls.get_vector() << std::endl;
879 std::cout << result << std::endl;
883 }
while(result.
iterations < max_iterations && result.
delta > min_delta && result.
RMSE() > max_RMSE);
890 template <
int PARAMS>
891 inline std::ostream& operator<<(std::ostream& out, const Homography<PARAMS>& t)
893 out << t.get_matrix()[0] <<
" " << t.get_matrix()[1] <<
" " << t.get_matrix()[2];
897 template <
int PARAMS>
898 inline std::ostream& operator<<(std::ostream& out, const HomographyPrefix<PARAMS>& t)
900 out << t.get_matrix()[0] <<
" " << t.get_matrix()[1] <<
" " << t.get_matrix()[2];
904 inline std::ostream& operator<<(std::ostream& out,
const StaticAppearance& t)
909 inline std::ostream& operator<<(std::ostream& out,
const OffsetAppearance& t)
915 inline std::ostream& operator<<(std::ostream& out,
const BlurAppearance& t)
Blur appearance model that assumes that the input image was subject to blur.
Definition: esm.h:407
void gradient(const BasicImage< S > &im, BasicImage< T > &out)
computes the gradient image from an image.
Definition: vision.h:348
Basic appearance model implementing no change in the image.
Definition: esm.h:349
int pixels
common pixels in last iteration
Definition: esm.h:63
a specialization of ESMEstimator for pure camera rotation only.
Definition: esm.h:582
All classes and functions are within the CVD namespace.
Definition: argb.h:6
ImageRef size() const
What is the size of this image?
Definition: image.h:557
a generic implementation for 2D homography-based transformations parameterized by a 3x3 matrix with d...
Definition: esm.h:137
double error
final total squared error
Definition: esm.h:61
TooN::Vector< dimensions > J
internal temporary to avoid multiple re-initialisations
Definition: esm.h:344
TooN::Vector< 2 > vec(const ImageRef &ir)
Convert an image co-ordinate into a Vector.
Definition: vector_image_ref.h:13
a special implementation for 2D homography-based transformations described as a camera rotating aroun...
Definition: esm.h:287
The main class for the ESM module.
Definition: esm.h:488
void img_save(const BasicImage< PixelType > &im, std::ostream &o, ImageType::ImageType t, const std::map< std::string, Parameter<>> &p=std::map< std::string, Parameter<>>())
Save an image to a stream.
Definition: image_io.h:280
TooN::SO3 R
the transformation, a rotation of the camera around its centre
Definition: esm.h:343
Result class storing some information about the optimization.
Definition: esm.h:59
TooN::Matrix< 3 > Kinv
linear camera matrix and its inverse
Definition: esm.h:341
ESMResult esm_opt(TRANSFORM &T, APPEARANCE &A, const BasicImage< IMAGE > &templateImage, const BasicImage< GRADIENT > &templateGradient, const BasicImage< IMAGE > &target, const int max_iterations=40, const double min_delta=1e-8, const double max_RMSE=1.0)
a full ESM optimization function.
Definition: esm.h:805
Simple appearance model that assumes a constant offset in the intensities of the image vs...
Definition: esm.h:373
int x
The x co-ordinate.
Definition: image_ref.h:172
double RMSE() const
returns RMSE error computed from error and
Definition: esm.h:67
Definition: image_ref.h:29
void fill(const T d)
Set all the pixels in the image to a value.
Definition: image.h:574
A generic image class to manage a block of arbitrarily padded data as an image.
Definition: image.h:273
int iterations
number of iterations performed
Definition: esm.h:64
int y
The y co-ordinate.
Definition: image_ref.h:173
TooN::Vector< 4 > kinv
same parameters in vector form for convenience
Definition: esm.h:342
double delta
norm of the last update
Definition: esm.h:62
This class provides a generic 2D homography transformation, but also applies a fixed transformation o...
Definition: esm.h:233