blob: e5b31ae3d1e014ba63e55c88738072f23d04f302 [file] [log] [blame]
// Copyright 2006 Google Inc.
// All Rights Reserved.
// Author: <renn@google.com> (Marius Renn)
// local includes
#include "debugging.h"
#include "mask.h"
#include "mathfunctions.h"
#include "surface.h"
// C includes
#include <stdlib.h>
using namespace helium;
// Helper functions ------------------------------------------------------------
inline float XProject(int x, int y, float* c) {
return (c[0] * x + c[1] * y + c[2]) / (c[6] * x + c[7] * y + 1.0);
}
inline float YProject(int x, int y, float* c) {
return (c[3] * x + c[4] * y + c[5]) / (c[6] * x + c[7] * y + 1.0);
}
// Surface implementation ------------------------------------------------------
const int kMargin = 8;
Surface::Surface() : top_left_(0, 0),
top_right_(0, 0),
bottom_left_(0, 0),
bottom_right_(0, 0) {
}
Surface::Surface(Point top_left,
Point top_right,
Point bottom_left,
Point bottom_right) : top_left_(top_left),
top_right_(top_right),
bottom_left_(bottom_left),
bottom_right_(bottom_right) {
}
void Surface::CalculateDestinationRect(unsigned& width, unsigned& height) {
// Calculate middle points (for width / height evaluation)
Point mid_left = Middle(top_left_, bottom_left_);
Point mid_right = Middle(top_right_, bottom_right_);
Point mid_top = Middle(top_left_, top_right_);
Point mid_bottom = Middle(bottom_left_, bottom_right_);
// Calculate width and height (without perspective)
float w = Distance(mid_left, mid_right);
float h = Distance(mid_top, mid_bottom);
// Add perspective factors to width and height
float dis_left = Distance(top_left_, bottom_left_);
float dis_right = Distance(top_right_, bottom_right_);
w *= (dis_left > dis_right)
? (dis_left / dis_right)
: (dis_right / dis_left);
float dis_top = Distance(top_left_, top_right_);
float dis_bottom = Distance(bottom_left_, bottom_right_);
h *= (dis_top > dis_bottom)
? (dis_top / dis_bottom)
: (dis_bottom / dis_top);
width = static_cast<unsigned>(w);
height = static_cast<unsigned>(h);
}
Mask Surface::Dewarp(const Mask& mask) {
unsigned width, height;
// Calculate destination rectangle
CalculateDestinationRect(width, height);
// Dewarp to it
return DewarpMaskTo(mask, width, height);
}
Mask Surface::DewarpMaskTo(const Mask& mask, unsigned width, unsigned height) {
// Heavily inspired by the perspective projection code in Leptonica.
// Thanks to Dan Bloomberg for the help!
Mask dest(width + kMargin * 2, height + kMargin * 2);
dest.Clear();
bool* dest_ptr = dest.Access(kMargin, kMargin);
// Find the coefficients for the inverse projection (dest -> source)
float coeffs[8];
if (!CalcProjectionCoefficients(width, height, coeffs)) {
// If we can't dewarp, then return the input in hopes that it might still
// get recognized.
bool* mask_ptr = mask.data();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) *(dest_ptr++) = *(mask_ptr++);
dest_ptr += kMargin * 2;
}
return dest;
}
int src_x = 0;
int src_y = 0;
int max_x = mask.width() - 1;
int max_y = mask.height() - 1;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Calculate source coordinates
src_x = static_cast<int>(XProject(x, y, coeffs));
src_y = static_cast<int>(YProject(x, y, coeffs));
// Project onto flat surface
*dest_ptr = ((src_x >= 0) && (src_y >= 0)
&& (src_x <= max_x) && (src_y <= max_y))
? mask.ValueAt(src_x, src_y)
: 0;
++dest_ptr;
}
dest_ptr += kMargin * 2;
}
return dest;
}
bool Surface::CalcProjectionCoefficients(unsigned width,
unsigned height,
float* coeffs) {
// Convert geometrical values to float and give them shorter names
float x1 = top_left_.x;
float y1 = top_left_.y;
float x2 = top_right_.x;
float y2 = top_right_.y;
float x3 = bottom_right_.x;
float y3 = bottom_right_.y;
float x4 = bottom_left_.x;
float y4 = bottom_left_.y;
float w = width;
float h = height;
// Given is the transformation A*C = B, where B is a vector of the
// destination points and the C's are the unknown coefficients.
// This is the 8x8 matrix A:
float transform_matrix[8 * 8] = {
0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 ,
0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 ,
w, 0.0, 1.0, 0.0, 0.0, 0.0, -w * x2, 0.0 ,
0.0, 0.0, 0.0, w, 0.0, 1.0, -w * y2, 0.0 ,
w, h, 1.0, 0.0, 0.0, 0.0, -w * x3, -h * x3,
0.0, 0.0, 0.0, w, h, 1.0, -w * y3, -h * y2,
0.0, h, 1.0, 0.0, 0.0, 0.0, 0.0, -h * x4,
0.0, 0.0, 0.0, 0.0, h, 1.0, 0.0, -h * y4
};
// These are the destination coordinates B
float dest[8] = { x1, y1, x2, y2, x3, y3, x4, y4 };
// Run Gauss to solve the linear system
return Gauss(transform_matrix, dest, coeffs, 8);
}