// geopix.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include #include #include #include #include #include #include "matrix3.h" using std::vector; #define RAD2DEG 57.29578f #define DEG2RAD 0.017453293f struct LCM_param { float a,b,c,d; int parity; }; struct GeoPoint : Vector3 { static float radius_h,radius_v; const char *fscanf(FILE *); }; static void backtrans(const Vector3& input, const Vector3& north, const Vector3& east, const Vector3& up, Vector3& output) { for (unsigned i=0; i<3; ++i) output.value[i]= north.value[i]*input.value[0]+ east .value[i]*input.value[1]+ up .value[i]*input.value[2]; } const char *GeoPoint::fscanf(FILE *f) { float latd,lond,alt; if (::fscanf(f,"%f%f%f",&latd,&lond,&alt)<3) return "Lat/lon/alt read failed"; set_unit(latd*DEG2RAD,lond*DEG2RAD); operator*=(1+alt/radius_v); return 0; } float GeoPoint::radius_h=0,GeoPoint::radius_v=0; struct PixPoint { float pixX,pixY,azim,elev,h_dist2; GeoPoint location; Vector3 sight; char *name; const char *fscanf(FILE *); const char *fscanf(FILE *, float&); const char *scan_pix_name(FILE *); void set_sight(const Vector3&, const Vector3&, const Vector3&, const Vector3&); void set_sight(const Vector3&, Vector3, Vector3, const LCM_param&, float, float, float); void set_refrac(); PixPoint() {name=0;} PixPoint(const PixPoint& p) : location(p.location),sight(p.sight), pixX(p.pixX),pixY(p.pixY),azim(p.azim),elev(p.elev),h_dist2(p.h_dist2), name(strdup(p.name)) {} ~PixPoint() {delete[] name;} }; const char *PixPoint::scan_pix_name(FILE *f) { char buf[64],c; if (::fscanf(f,"%f%f",&pixX,&pixY)<2) return strerror(errno); while (isspace(c=fgetc(f))); if (c==EOF) return "No name for point"; ungetc(c,f); fgets(buf,sizeof buf,f); char *nl=strchr(buf,'\n'); if (!nl) return "Background point name too long or not terminated"; *nl='\0'; delete[] name; // just in case there was already something there name=new char[strlen(buf)+1]; strcpy(name,buf); return 0; } const char *PixPoint::fscanf(FILE *f) { const char *error=location.fscanf(f); if (error) return error; return scan_pix_name(f); } const char *PixPoint::fscanf(FILE *f, float& dist) { if (::fscanf(f,"%f",&dist)<1) return strerror(errno); dist/=GeoPoint::radius_h; return scan_pix_name(f); } void PixPoint::set_refrac() { h_dist2=sight.value[0]*sight.value[0]+sight.value[1]*sight.value[1]; } void PixPoint::set_sight(const Vector3& photo_point, const Vector3& up, const Vector3& north, const Vector3& east) { // compute the sight vector Vector3 sight_ECEF=location; sight_ECEF-=photo_point; sight.value[0]=sight_ECEF*north; sight.value[1]=sight_ECEF*east; sight.value[2]=sight_ECEF*up; set_refrac(); } void PixPoint::set_sight(const Vector3& photo_axis, Vector3 photo_up, Vector3 photo_cross, const LCM_param& lcmp, float camres2i, float dist, float rf_curv) { // compute azimuth and elevation angle of pixel coordinates float dx=pixX-lcmp.c,dy=pixY-lcmp.d; azim=( dx*lcmp.a+lcmp.parity*dy*lcmp.b)*camres2i; elev=(-dx*lcmp.b+lcmp.parity*dy*lcmp.a)*camres2i; // compute sight vector based on pixel coordinates and distance photo_up*=elev; photo_cross*=azim; sight=photo_axis; sight+=photo_up; sight+=photo_cross; sight*=dist/(float)sqrt(sight*sight); set_refrac(); sight.value[2]-=h_dist2*rf_curv; } static float mod_diff(float z, const Vector3& y) { Vector3 xy=y; xy.value[2]+=z; float D=z+(float)sqrt(xy*xy); xy.value[2]+=z; return (xy*y)/D; } static int LCM(float ax, float ay, float ax2, float ay2, float au, float av, float aux, float auy, float avx, float avy, LCM_param& param) { float Di=1/(ax2+ay2); int p_rec=aux*avy>auy*avx?1:-1; if (param.parity==0) param.parity=p_rec; param.a=(aux+param.parity*avy)*Di; param.b=(-auy+param.parity*avx)*Di; param.c=au-param.a*ax+param.b*ay; param.d=av-param.parity*(param.a*ay+param.b*ax); return p_rec; } struct Parameter { unsigned n; float *x_deriv,*y_deriv,a_deriv,b_deriv,sx,sy; Parameter() : n(0),x_deriv(0) {} ~Parameter() {delete[] x_deriv;} void init(unsigned n_) {n=n_; x_deriv=new float[2*n_]; y_deriv=x_deriv+n_; sx=sy=0;} void set_xy_deriv(unsigned i, float x, float y) {sx+=x_deriv[i]=x; sy+=y_deriv[i]=y;} void subtract_avg(); void set_ab_deriv(const vector&, float, float, float, float, const LCM_param&, float); void predict_deriv(unsigned, const LCM_param&, float, float, float&, float&) const; float Q_deriv(const vector&, float, float, float, float, const LCM_param&); }; void Parameter::subtract_avg() { float ax=sx/n,ay=sy/n; for (unsigned i=0; i& bgpts, float au, float av, float ax, float ay, const LCM_param& lcmp, float D) { float du,dv,dx,dy,ca=0,cb=0,c0=0; for (unsigned i=0; i& bgpts, float au, float av, float ax, float ay, const LCM_param& lcmp) { float du,dv,dx,dy,Qd=0; for (unsigned i=0; i&, float, float, float, float, const LCM_param&, float); float Q_deriv(const Parameter&, const Parameter&, const vector&, float, float, float, float, const LCM_param&); }; void ParamPair::set_ab_deriv(const Parameter& p1, const Parameter& p2, const vector& bgpts, float au, float av, float ax, float ay, const LCM_param& lcmp, float D) { float du,dv,dx,dy,ca=0,cb=0,c0=0,c1=0,c2=0; for (unsigned i=0; i& bgpts, float au, float av, float ax, float ay, const LCM_param& lcmp) { float du,dv,dx,dy,Qd=0; for (unsigned i=0; i=0; --i) { float& y=x[i]; for (j=i+1; j<3; ++j) { y-=a[j][i]*x[j]; } y/=a[i][i]; } } static void solve(const float a[2][2], const float b[2], float x[2], float D) { x[0]=(b[0]*a[1][1]-a[0][1]*b[1])/D; x[1]=(b[1]*a[0][0]-a[1][0]*b[0])/D; } static void transpose(float a[3][3]) { unsigned j,k; float y; for (j=1; j<3; ++j) { for (k=0; k"); gets(fname); #endif return 1; } } // open the file f=fopen(fname,"r"); if (!f) { perror(fname); #ifdef NDEBUG printf("\nPress "); gets(fname); #endif return 2; } float ax=0,ay=0,ax2=0,ay2=0,axy=0, axa=0,axe=0,aya=0,aye=0,aa=0,ae=0,aa2=0,ae2=0,aae=0, sig_aa,sig_ab,sig_bb,sig_azaz,sig_azel,sig_elel, da,de,dx,dy,ni,dist,rf_curv,camres,camresi,camres2i,camaz,camel, h_disti,a2,ab,b2; GeoPoint photo_point; Parameter cam_pos[3],pa_param[2],rf_param; ParamPair cam_pos2[3][3],pa_param2[2][2],rf_param2; float d1Qcp[3],d2Qcp[3][3],d1Qpa[2],d2Qpa[2][2],d1Qrf,d2Qrf; Vector3 photo_axis,photo_up,photo_cross,up,east,north,vec1; unsigned i,nbgpt=0,j,k,n_opt_par; int p_rec,nrdf; LCM_param lcmp; vector bgpts; PixPoint test; // read global parameters if (fscanf(f,"%f%f%f%d%d", &GeoPoint::radius_h,&GeoPoint::radius_v,&rf_curv, &lcmp.parity,&n_opt_par)<5) { error=strerror(errno); goto exit; } rf_curv*=0.5; // read camera axis if (fscanf(f,"%f%f",&camaz,&camel)<2) { error=strerror(errno); goto exit; } // read photo point if (error=photo_point.fscanf(f)) goto exit; up=photo_point; up.normalize(); east.value[0]=-photo_point.value[1]; east.value[1]=photo_point.value[0]; east.value[2]=0; east.normalize(); north.cross(up,east); photo_axis.value[0]=photo_axis.value[1]=photo_axis.value[2]=0; while (c=fgetc(f),c!='#') { ungetc(c,f); // read BG point bgpts.resize(++nbgpt); PixPoint& pixp=bgpts.back(); if (error=pixp.fscanf(f)) goto exit; pixp.set_sight(photo_point,up,north,east); // sum pixel positions for statistics analysis ax+=pixp.pixX; ay+=pixp.pixY; // sum the normalized sight vector into the initial photo axis vec1=pixp.sight; vec1.normalize(); photo_axis+=vec1; } for (j=0; j<3; ++j) { cam_pos[j].init(nbgpt); for (k=0; k<3; ++k) cam_pos2[j][k].init(nbgpt); } for (j=0; j<2; ++j) { pa_param[j].init(nbgpt); for (k=0; k<2; ++k) pa_param2[j][k].init(nbgpt); } rf_param.init(nbgpt); rf_param2.init(nbgpt); ni=1.0f/nbgpt; ax*=ni; ay*=ni; if (camaz<0) { // user wants the photo axis to be the sight vector average photo_axis.normalize(); } else { // user specifies photo axis photo_axis.set_unit(camel*DEG2RAD,camaz*DEG2RAD); } photo_up.value[0]=photo_up.value[1]=0; photo_up.value[2]=1; photo_up.orthog(photo_axis); photo_cross.cross(photo_up,photo_axis); // collect sums for statistics analysis for (i=0; i0 ? RAD2DEG*sqrt(a2*sig_bb+b2*sig_aa-2*ab*sig_ab)*camres2i : -1, DEG2RAD*camres, nrdf>0 ? DEG2RAD*sqrt(a2*sig_aa+b2*sig_bb+2*ab*sig_ab)*camresi : -1); printf("Pixel coordinates of photo axis: %.1f %.1f\n",lcmp.c,lcmp.d); printf("Image X-axis increases %s\n",lcmp.a>0?"right":"left"); printf("Image Y-axis increases %s\n",lcmp.a*lcmp.parity>0?"up":"down"); printf("Parity: "); if (lcmp.parity==p_rec) printf("%d\n",lcmp.parity); else printf("%d recommended, %d used\n",p_rec,lcmp.parity); if (nrdf>0) { printf("\nPARAMETER CORRECTIONS (%d redundant degrees of freedom)\n",nrdf); printf("Camera position:"); // compute camera position gradients for (j=0; j<3; ++j) { cam_pos[j].set_ab_deriv(bgpts,ax,ay,aa,ae,lcmp,aa2+ae2); d1Qcp[j]=cam_pos[j].Q_deriv(bgpts,ax,ay,aa,ae,lcmp); } // compute camera position hessian for (j=0; j<3; ++j) { for (k=0; k<3; ++k) { cam_pos2[j][k].set_ab_deriv(cam_pos[j],cam_pos[k], bgpts,ax,ay,aa,ae,lcmp,aa2+ae2); d2Qcp[j][k]=cam_pos2[j][k]. Q_deriv(cam_pos[j],cam_pos[k],bgpts,ax,ay,aa,ae,lcmp); } } if (cholesky(d2Qcp)) { // Camera position hessian is positive definite. // Proceed with correction & error analysis. float cam_shift[3],cam_error[3][3],Gff[3][3],fj,fk,gj,gk; Vector3 photo_shift=photo_point; solve(d2Qcp,d1Qcp,cam_shift); for (j=0; j<3; ++j) { const Parameter& pj=cam_pos[j]; for (k=0; k<3; ++k) { const Parameter& pk=cam_pos[k]; float& x=cam_error[j][k]; x=0; for (i=0; i0 && d2Qpa[0][0]>0) { // Photo axis hessian is positive definite. // Proceed with correction & error analysis. float pa_shift[2],pa_error[2][2],Gff[2][2],fj,fk,gj,gk; solve(d2Qpa,d1Qpa,pa_shift,D); pa_shift[0]*=-1; pa_shift[1]*=-1; for (j=0; j<2; ++j) { const Parameter& pj=pa_param[j]; for (k=0; k<2; ++k) { const Parameter& pk=pa_param[k]; float& x=pa_error[j][k]; x=0; for (i=0; i0) { // Refractive curvature second derivative is positive. // Proceed with correction & error analysis. float rf_shift=-d1Qrf/d2Qrf,rf_error=0,f,g; for (i=0; i0 ? sqrt(ba_ae*ba_ae*sig_aa+aa_be*aa_be*sig_bb-2*ba_ae*aa_be*sig_ab +sig_elel*(1+ni)) * GeoPoint::radius_v*dist*camres2i : -1, GeoPoint::radius_v*dist*camresi, test.name); } if (c=='P') { // compute prospective points printf("\nPROSPECTIVE POINTS\n"); printf(" ElevAngle\n" " Latitude Longitude Elevation Azimuth Actul Aprnt Pix Res Name\n" "========= ========== ========= ======= ===== ===== ======= ================\n"); while (c=fgetc(f),c!='#' && c!=EOF) { ungetc(c,f); if (error=test.fscanf(f,dist)) goto exit; test.h_dist2=dist*dist; test.set_sight(photo_axis,photo_up,photo_cross,lcmp, camres2i,dist,rf_curv); backtrans(test.sight,north,east,up,test.location); test.location+=photo_point; h_disti=1/(float)sqrt(test.h_dist2); printf("%9.5f %10.5f %5.0f %6.2f %5.2f %5.2f %#6.3g %s\n", RAD2DEG*test.location.theta(),RAD2DEG*test.location.phi(), GeoPoint::radius_v*(sqrt(test.location*test.location)-1), RAD2DEG*test.sight.phi_positive(), RAD2DEG*atan(test.sight.value[2]*h_disti), RAD2DEG*atan((test.sight.value[2]+test.h_dist2*rf_curv)*h_disti), GeoPoint::radius_v*dist*camresi,test.name); } } exit: fclose(f); if (error) { fprintf(stderr,"%s\n",error); #ifdef NDEBUG printf("\nPress "); gets(fname); #endif return 3; } #ifdef NDEBUG printf("\nPress "); gets(fname); #endif return 0; }