// Profilometer.cpp : This file contains the 'main' function. Program execution begins and ends there. // //Program flow: // Initialize PMeter // Set geometry constants - sigma, dist, radius // Set camera constants - pixel dimensions, fov angles // Precalculate other constants - trig expressions and such // ---Precalculate curvature correction--- // Nevermind, this varies with horizontal position and therefore both x and y, would need full screen 2d array // Maybe there's an approximation that's fixed in one of those and varies in the other? // Per frame: // Preprocessing - Calculate threshold for identifying intensity spikes // Build incidence pixel (IP) array // Laser likely centered, may not extend across whole fov -> start in middle of x range, not edge // Prioritize neighborhood search to cut down number of operations // Option 1: maximal intensity pixel is IP // Option 2: find groups of pixels above threshold, use weighted average of the segment as IP // Calculate real space depth of IPs // Apply curvature correction // Return depth array // #define _USE_MATH_DEFINES #include #include #include class PMeter { public: int height, width; // camera pixel dimensions double alpha, beta; // camera angle dimensions // full fov is 2* this angle, vertical and horizontal respectively // 0 < alpha < sigma // alpha >= sigma causes phi singularity // 0 < beta < pi/2 double sigma; // angle between laser plane and camera center line // 0 < sigma < pi/2 double dist; // distance between camera and laser double tan_alpha; double tan_beta; double* tan_phi_arr; double* tan_theta_arr; double sin_sigma; double cos_sigma; double p_h; void fastinit(int h, int w, double a, double b, double s, double d) { // class variable initializer height = h; width = w; alpha = a; beta = b; sigma = s; dist = d; trig_precalc(); } void trig_precalc() { tan_alpha = tan(alpha); tan_beta = tan(beta); tan_phi_arr = (double*)malloc(sizeof(double) * height); for (int i = 0; i < height; i++) { //tan_phi_arr[i] = tan_alpha / height * (2 * i + 1 - height); tan_phi_arr[i] = tan_alpha / height * (2 * i - height); } tan_theta_arr = (double*)malloc(sizeof(double) * width); for (int i = 0; i < width; i++) { //tan_theta_arr[i] = -tan_beta / width * (2 * i - 1 - width); tan_theta_arr[i] = -tan_beta / width * (2 * i - width); } sin_sigma = sin(sigma); cos_sigma = cos(sigma); p_h = 2 * dist / height * tan_alpha / sin_sigma; } void print_conf() { std::cout << "Height: " << height << "\n"; std::cout << "Width: " << width << "\n"; std::cout << "Alpha: " << alpha << "\n"; std::cout << "Beta: " << beta << "\n"; std::cout << "Sigma: " << sigma << "\n"; std::cout << "Dist: " << dist << "\n"; //std::cout << "Phi: " << phi << "\n"; //std::cout << "Theta: " << theta << "\n"; } double get_phi(int y) { // phi is angle below camera center line of given y coordinate - above center is negative // returns phi at pixel center, removing the +1 shifts this to low edge // alpha >= phi >= -alpha double phi = atan(tan_phi_arr[y]); return phi; } double get_theta(int x) { // theta is horizontal angle between center line and given x coordinate // returns theta at pixel center, removing the -1 shifts this to low edge // beta >= theta >= -beta // if viewed from above, positive theta means pixel vector is to the left of center, i.e. x < width/2 double theta = atan(tan_theta_arr[x]); return theta; } double sin_sp(int y) { // sin( sigma + phi) return sin_sigma + cos_sigma * tan_phi_arr[y] / sqrt(1 + tan_phi_arr[y]); } double cos_sp(int y) { // cos( sigma + phi) return cos_sigma + sin_sigma * tan_phi_arr[y] / sqrt(1 + tan_phi_arr[y]); } double pixel_delta_approx(int y) { // returns approximation of change in elevation from y to y+1 // assumes that epsilon=0 //double delta = 2 * dist * tan(alpha) * cos(phi) / (height * sin(sigma + phi) * sin(sigma + phi)); double delta = p_h * sin_sigma / (sin_sigma + cos_sigma * tan_phi_arr[y]); return delta; } double pixel_delta_exact(int y) { // returns exact change in elevation from y to y+1 //double tan_eps = 2 * tan(alpha) * cos(phi) * cos(phi) / (height + 2 * tan(alpha) * sin(phi) * cos(phi)); double tan_phi = tan_phi_arr[y]; double tan_eps = 2 * tan_alpha / (height * (1 + tan_phi * tan_phi) + 2 * tan_alpha * tan_phi); std::cout << "Tan(eps) at phi=" << get_phi(y) << ": " << tan_eps << "\n"; //double delta = 2 * dist * tan(alpha) * cos(phi) * (cos(phi) - sin(phi) * tan_eps) / (height * sin(sigma + phi) * (sin(sigma + phi) + cos(sigma + phi) * tan_eps)); //double delta = dist * tan_eps / (sin_sp(y) * (sin_sp(y) + tan_eps * cos_sp(y))); double delta = 2 * dist * tan_alpha * (1 - tan_phi_arr[y] * tan_eps) / (height * sin_sp(y) * (sin_sp(y) + cos_sp(y) * tan_eps)); return delta; } double get_elevation(int y) { // elevation above horizontal camera plane // singularity if phi = -sigma // flat projection surface -> x coordinate irrelevant //double e = dist / tan(phi + sigma); double e = dist * cos_sp(y) / sin_sp(y); return e; } double get_horizontal_dist(int x, int y) { // get horizontal distance from center line, same sign convention as theta // generally depends on both x and y //double f = dist * tan(theta) / sin(sigma + phi); double f = dist * tan_theta_arr[x] / sin_sp(y); return f; } /* void approx_test_phi(double new_phi) { // print exact and approximate delta and the error at given phi // changes phi phi = new_phi; double da = pixel_delta_approx(); std::cout << "Delta approx.: " << da << "\n"; double de = pixel_delta_exact(); std::cout << "Delta exact: " << de << "\n"; std::cout << "Error absolute: " << (de - da) << "\n"; std::cout << "Error relative: " << (de - da)/de << "\n"; } */ void approx_test(int y) { double da = pixel_delta_approx(y); std::cout << "Delta approx.: " << da << "\n"; double de = pixel_delta_exact(y); std::cout << "Delta exact: " << de << "\n"; std::cout << "Error absolute: " << (de - da) << "\n"; std::cout << "Error relative: " << (de - da) / de << "\n"; } /* void edge_test() { // run approx_test_phi at bottom edge, phi=0, and top edge // preserves phi double old_phi = phi; std::cout << "\nMax phi\n"; approx_test_phi(alpha); std::cout << "\nZero phi\n"; approx_test_phi(0.0); std::cout << "\nMin phi\n"; approx_test_phi(-1*alpha); phi = old_phi; } */ void edge_test() { // run approx_test_phi at y = {0, height/2, height-1} std::cout << "\ny = h - 1\n"; approx_test(height - 1); std::cout << "\ny = h/2\n"; approx_test(height/2); std::cout << "\ny = 0\n"; approx_test(0); } }; int main() { PMeter PM; // fastinit(int h, int w, double a, double b, double s, double d) PM.fastinit(3120, 4208, M_PI / 180 * 21.5, M_PI / 180 * 43, M_PI / 180 * 51, 0.05); PM.print_conf(); std::cout << "phi at y=50 is " << PM.get_phi(50) << "\n"; PM.edge_test(); ////cv::Mat image = cv::imread("./PM_test_0.bmp"); //cv::Mat image = cv::imread("./PM_test_1.png"); ////cv::Mat image = cv::imread("./PM_test_2.png"); // //cv::String windowName = "imtest"; //cv::namedWindow(windowName); //cv::imshow(windowName, image); //cv::waitKey(0); std::cout << "\n===\nEnd of main()\n===\n"; }