PageRenderTime 47ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/luminance-hdr-2.3.0/src/Filter/pfspanoramic.cpp

#
C++ | 620 lines | 409 code | 154 blank | 57 comment | 58 complexity | 7536537493d9e5e68f66a4f71ed28827 MD5 | raw file
Possible License(s): GPL-2.0
  1. /**
  2. * @brief Perform projective transformations of spherical images
  3. *
  4. * This file is a part of LuminanceHDR package (based on pfstool's code).
  5. * ----------------------------------------------------------------------
  6. * Copyright (C) 2003,2004 Rafal Mantiuk and Grzegorz Krawczyk
  7. * Copyright (C) 2008 Giuseppe Rota
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22. * ----------------------------------------------------------------------
  23. *
  24. * @author Miloslaw Smyk, <thorgal@wfmh.org.pl>
  25. *
  26. * $Id: pfspanoramic.cpp,v 1.2 2006/11/20 17:52:15 rafm Exp $
  27. */
  28. #include <string.h>
  29. #include "pfspanoramic.h"
  30. ProjectionFactory ProjectionFactory::singleton(true);
  31. PolarProjection PolarProjection::singleton(true);
  32. CylindricalProjection CylindricalProjection::singleton(true);
  33. AngularProjection AngularProjection::singleton(true);
  34. MirrorBallProjection MirrorBallProjection::singleton(true);
  35. const double EPSILON=1e-7;
  36. class Vector3D
  37. {
  38. public:
  39. double x, y, z;
  40. Vector3D(double phi, double theta)
  41. {
  42. x = cos(phi) * sin(theta);
  43. y = sin(phi) * sin(theta);
  44. z = cos(theta);
  45. }
  46. Vector3D(double x, double y, double z)
  47. {
  48. this->x = x;
  49. this->y = y;
  50. this->z = z;
  51. normalize();
  52. }
  53. double magnitude(void)
  54. {
  55. return sqrt( x * x + y * y + z * z );
  56. }
  57. void normalize(void)
  58. {
  59. double len = magnitude();
  60. x = x / len;
  61. y = y / len;
  62. z = z / len;
  63. }
  64. double dot(Vector3D *v)
  65. {
  66. return x * v->x + y * v->y + z * v->z;
  67. }
  68. //TODO: optimize rotations by precomputing sines and cosines
  69. void rotateX(double angle)
  70. {
  71. angle *= (M_PI / 180);
  72. double c = cos(angle);
  73. double s = sin(angle);
  74. double y2 = c * y + -s * z;
  75. double z2 = s * y + c * z;
  76. y = y2;
  77. z = z2;
  78. }
  79. void rotateY(double angle)
  80. {
  81. angle *= (M_PI / 180);
  82. double c = cos(angle);
  83. double s = sin(angle);
  84. double x2 = c * x + s * z;
  85. double z2 = -s * x + c * z;
  86. x = x2;
  87. z = z2;
  88. }
  89. void rotateZ(double angle)
  90. {
  91. angle *= (M_PI / 180);
  92. double c = cos(angle);
  93. double s = sin(angle);
  94. double x2 = c * x + -s * y;
  95. double y2 = s * x + c * y;
  96. x = x2;
  97. y = y2;
  98. }
  99. };
  100. class Point2D
  101. {
  102. public:
  103. double x, y;
  104. Point2D(double x, double y)
  105. {
  106. this->x = x;
  107. this->y = y;
  108. }
  109. };
  110. ///PROJECTIONFACTORY
  111. ProjectionFactory::ProjectionFactory(bool ) {
  112. }
  113. void ProjectionFactory::registerProjection(const char *name, ProjectionCreator ptr) {
  114. singleton.projections[ string( name ) ] = ptr;
  115. }
  116. // TODO: check this function
  117. Projection *ProjectionFactory::getProjection(char *name) {
  118. char *opts;
  119. Projection *projection = NULL;
  120. if( (opts = strchr(name, '/')) )
  121. {
  122. *opts++ = '\0';
  123. }
  124. ProjectionCreator projectionCreator = singleton.projections.find(string(name))->second;
  125. if(projectionCreator != NULL)
  126. {
  127. projection = projectionCreator();
  128. if(opts != NULL)
  129. projection->setOptions(opts);
  130. }
  131. return projection;
  132. }
  133. //FIXME: Lame. Should return an iterator over the names. No time for this now. :/
  134. void ProjectionFactory::listProjectionNames(void) {
  135. map<string, ProjectionCreator>::iterator i = singleton.projections.begin();
  136. while(i != singleton.projections.end())
  137. {
  138. fprintf( stderr, "%s\n", (*i).first.c_str());
  139. i++;
  140. }
  141. }
  142. ///END PROJECTIONFACTORY
  143. ///MIRRORBALL
  144. MirrorBallProjection::MirrorBallProjection(bool initialization) {
  145. name = "mirrorball";
  146. if(initialization)
  147. ProjectionFactory::registerProjection(name, this->create);
  148. }
  149. Projection* MirrorBallProjection::create() {
  150. return new MirrorBallProjection(false);
  151. }
  152. const char *MirrorBallProjection::getName(void) {
  153. return name;
  154. }
  155. double MirrorBallProjection::getSizeRatio(void) {
  156. return 1;
  157. }
  158. bool MirrorBallProjection::isValidPixel(double u, double v) {
  159. // check if we are not in a boundary region (outside a circle)
  160. if((u - 0.5) * (u - 0.5) + (v - 0.5) * (v - 0.5) > 0.25)
  161. return false;
  162. else
  163. return true;
  164. }
  165. Vector3D* MirrorBallProjection::uvToDirection(double u, double v) {
  166. u = 2 * u - 1;
  167. v = 2 * v - 1;
  168. double phi = atan2( v, u );
  169. double theta = 2 * asin( sqrt( u * u + v * v ) );
  170. Vector3D *direction = new Vector3D(phi, theta);
  171. // double t;
  172. direction->y = -direction->y;
  173. return direction;
  174. }
  175. Point2D* MirrorBallProjection::directionToUV(Vector3D *direction) {
  176. double u, v;
  177. direction->y = -direction->y;
  178. if(fabs(direction->x) > 0 || fabs(direction->y) > 0)
  179. {
  180. double distance = sqrt(direction->x * direction->x + direction->y * direction->y);
  181. double r = 0.5 * (sin(acos(direction->z) / 2)) / distance;
  182. u = direction->x * r + 0.5;
  183. v = direction->y * r + 0.5;
  184. }
  185. else
  186. {
  187. u = v = 0.5;
  188. }
  189. return new Point2D(u, v);
  190. }
  191. ///END MIRRORBALL
  192. ///ANGULAR
  193. AngularProjection::AngularProjection(bool initialization) {
  194. name = "angular";
  195. totalAngle = 360;
  196. if(initialization)
  197. ProjectionFactory::registerProjection(name, this->create);
  198. }
  199. Projection* AngularProjection::create() {
  200. AngularProjection *p = new AngularProjection(false);
  201. p->totalAngle = 360;
  202. return (Projection *)p;
  203. }
  204. void AngularProjection::setOptions(char *opts) {
  205. char *delimiter;
  206. static const char *OPTION_ANGLE = "angle";
  207. while(*opts)
  208. {
  209. //fprintf(stderr,"option: %s\n", opts);
  210. //if(delimiter = strchr(name, '/'))
  211. //*delimiter++ = '\0';
  212. if(strncmp(opts, OPTION_ANGLE, strlen(OPTION_ANGLE)) == 0)
  213. {
  214. totalAngle = strtod(opts + strlen(OPTION_ANGLE) + 1, &delimiter);
  215. // fprintf(stderr,"angle: %g\n", totalAngle);
  216. if(0 >= totalAngle || totalAngle > 360)
  217. {
  218. throw "error: angular projection: angle must be in (0,360] degrees range.\n";
  219. }
  220. }
  221. else
  222. {
  223. throw " error: angular projection: unknown option.\n";
  224. }
  225. opts = delimiter + 1;
  226. }
  227. }
  228. const char *AngularProjection::getName(void) {
  229. return name;
  230. }
  231. double AngularProjection::getSizeRatio(void) {
  232. return 1;
  233. }
  234. bool AngularProjection::isValidPixel(double u, double v) {
  235. // check if we are not in a boundary region (outside a circle)
  236. if((u - 0.5) * (u - 0.5) + (v - 0.5) * (v - 0.5) > 0.25)
  237. return false;
  238. else
  239. return true;
  240. }
  241. Vector3D* AngularProjection::uvToDirection(double u, double v) {
  242. u = 2 * u - 1;
  243. v = 2 * v - 1;
  244. u *= totalAngle / 360;
  245. v *= totalAngle / 360;
  246. double phi = atan2( v, u );
  247. double theta = M_PI * sqrt( u * u + v * v );
  248. Vector3D *direction = new Vector3D(phi, theta);
  249. // double t;
  250. direction->y = -direction->y;
  251. return direction;
  252. }
  253. Point2D* AngularProjection::directionToUV(Vector3D *direction) {
  254. double u, v;
  255. direction->y = -direction->y;
  256. if(fabs(direction->x) > 0 || fabs(direction->y) > 0)
  257. {
  258. double distance = sqrt(direction->x * direction->x + direction->y * direction->y);
  259. double r = (1 / (2 * M_PI)) * acos(direction->z) / distance;
  260. u = direction->x * r + 0.5;
  261. v = direction->y * r + 0.5;
  262. }
  263. else
  264. {
  265. u = v = 0.5;
  266. }
  267. return new Point2D(u, v);
  268. }
  269. ///END ANGULAR
  270. ///CYLINDRICAL
  271. CylindricalProjection::CylindricalProjection(bool initialization) {
  272. name = "cylindrical";
  273. if(initialization)
  274. ProjectionFactory::registerProjection(name, this->create);
  275. pole = new Vector3D(0, 1, 0);
  276. equator = new Vector3D(0, 0, -1);
  277. cross = new Vector3D(1, 0, 0);
  278. }
  279. Projection* CylindricalProjection::create() {
  280. return new CylindricalProjection(false);
  281. }
  282. CylindricalProjection::~CylindricalProjection() {
  283. delete pole;
  284. delete equator;
  285. delete cross;
  286. }
  287. double CylindricalProjection::getSizeRatio(void) {
  288. return 2;
  289. }
  290. bool CylindricalProjection::isValidPixel(double /*u*/, double /*v*/) {
  291. return true;
  292. }
  293. Vector3D* CylindricalProjection::uvToDirection(double u, double v) {
  294. u = 0.75 - u;
  295. u *= M_PI * 2;
  296. v = acos( 1 - 2 * v );
  297. Vector3D *direction = new Vector3D(u, v);
  298. double temp = direction->z;
  299. direction->z = direction->y;
  300. direction->y = temp;
  301. return direction;
  302. }
  303. Point2D* CylindricalProjection::directionToUV(Vector3D *direction) {
  304. double u, v;
  305. double lat = direction->dot(pole);
  306. v = ( 1 - lat ) / 2;
  307. if(v < EPSILON || fabs(1 - v) < EPSILON)
  308. u = 0;
  309. else
  310. {
  311. double ratio = equator->dot( direction ) / sin( acos( lat ) );
  312. if(ratio < -1)
  313. ratio = -1;
  314. else
  315. if(ratio > 1)
  316. ratio = 1;
  317. double lon = acos(ratio) / (2 * M_PI);
  318. if(cross->dot(direction) < 0)
  319. u = lon;
  320. else
  321. u = 1 - lon;
  322. if(u == 1)
  323. u = 0;
  324. if(v == 1)
  325. v = 0;
  326. }
  327. // if ( 0 > v || v >= 1 ) fprintf(stderr, "u: %f (%f,%f,%f)\n", v, direction->x, direction->y, direction->z);
  328. // assert ( -0. <= u && u < 1 );
  329. // assert ( -0. <= v && v < 1 );
  330. return new Point2D(u, v);
  331. }
  332. ///END CYLINDRICAL
  333. ///POLAR
  334. PolarProjection::PolarProjection(bool initialization) {
  335. name = "polar";
  336. if(initialization)
  337. ProjectionFactory::registerProjection(name, this->create);
  338. pole = new Vector3D(0, 1, 0);
  339. equator = new Vector3D(0, 0, -1);
  340. cross = new Vector3D(1, 0, 0);
  341. }
  342. Projection* PolarProjection::create() {
  343. return new PolarProjection(false);
  344. }
  345. PolarProjection::~PolarProjection() {
  346. delete pole;
  347. delete equator;
  348. delete cross;
  349. }
  350. double PolarProjection::getSizeRatio(void) {
  351. return 2;
  352. }
  353. bool PolarProjection::isValidPixel(double /*u*/, double /*v*/) {
  354. return true;
  355. }
  356. Vector3D* PolarProjection::uvToDirection(double u, double v) {
  357. u = 0.75 - u;
  358. u *= M_PI * 2;
  359. v *= M_PI;
  360. Vector3D *direction = new Vector3D(u, v);
  361. double temp = direction->z;
  362. direction->z = direction->y;
  363. direction->y = temp;
  364. return direction;
  365. }
  366. Point2D* PolarProjection::directionToUV(Vector3D *direction) {
  367. double u, v;
  368. double lat = acos(direction->dot(pole));
  369. v = lat * M_1_PI;
  370. if(v < EPSILON || fabs(1 - v) < EPSILON)
  371. u = 0;
  372. else
  373. {
  374. double ratio = equator->dot(direction) / sin(lat);
  375. if(ratio < -1)
  376. ratio = -1;
  377. else
  378. if(ratio > 1)
  379. ratio = 1;
  380. double lon = acos(ratio) / (2 * M_PI);
  381. if(cross->dot(direction) < 0)
  382. u = lon;
  383. else
  384. u = 1 - lon;
  385. if(u == 1)
  386. u = 0;
  387. if(v == 1)
  388. v = 0;
  389. }
  390. // if ( 0 > v || v >= 1 ) fprintf(stderr, "u: %f (%f,%f,%f)\n", v, direction->x, direction->y, direction->z);
  391. // assert ( -0. <= u && u < 1 );
  392. // assert ( -0. <= v && v < 1 );
  393. return new Point2D(u, v);
  394. }
  395. ///END POLAR
  396. void transformArray( const pfs::Array2D *in, pfs::Array2D *out, TransformInfo *transformInfo)
  397. {
  398. const double delta = 1. / transformInfo->oversampleFactor;
  399. const double offset = 0.5 / transformInfo->oversampleFactor;
  400. const double scaler = 1. / ( transformInfo->oversampleFactor * transformInfo->oversampleFactor );
  401. const int outRows = out->getRows();
  402. const int outCols = out->getCols();
  403. const int inRows = in->getRows();
  404. const int inCols = in->getCols();
  405. for( int y = 0; y < outRows; y++ )
  406. for( int x = 0; x < outCols; x++ ) {
  407. double pixVal = 0;
  408. if( transformInfo->dstProjection->isValidPixel(( x + 0.5 ) / outCols, ( y + 0.5 ) / outCols ) == true )
  409. {
  410. for( double sy = 0, oy = 0; oy < transformInfo->oversampleFactor; sy += delta, oy++ )
  411. for( double sx = 0, ox = 0; ox < transformInfo->oversampleFactor; sx += delta, ox++ )
  412. {
  413. Vector3D *direction = transformInfo->dstProjection->uvToDirection(
  414. ( x + offset + sx ) / outCols, ( y + offset + sy ) / outRows );
  415. if(direction == NULL)
  416. continue;
  417. // angles below are negated, because we want to rotate
  418. // the environment around us, not us within the environment.
  419. if( transformInfo->xRotate != 0 )
  420. direction->rotateX( -transformInfo->xRotate );
  421. if( transformInfo->yRotate != 0 )
  422. direction->rotateY( -transformInfo->yRotate );
  423. if( transformInfo->zRotate != 0 )
  424. direction->rotateZ( -transformInfo->zRotate );
  425. Point2D *p = transformInfo->srcProjection->directionToUV( direction );
  426. p->x *= inCols;
  427. p->y *= inRows;
  428. if( transformInfo->interpolate == true )
  429. {
  430. int ix = (int)floor( p->x );
  431. int iy = (int)floor( p->y );
  432. double i = p->x - ix;
  433. double j = p->y - iy;
  434. // compute pixel weights for interpolation
  435. double w1 = i * j;
  436. double w2 = (1 - i) * j;
  437. double w3 = (1 - i) * (1 - j);
  438. double w4 = i * (1 - j);
  439. int dx = ix + 1;
  440. if(dx >= inCols)
  441. dx = inCols - 1;
  442. int dy = iy + 1;
  443. if(dy >= inRows)
  444. dy = inRows - 1;
  445. pixVal += w3 * (*in)(ix, iy) +
  446. w4 * (*in)(dx, iy) +
  447. w1 * (*in)(dx, dy) +
  448. w2 * (*in)(ix, dy);
  449. }
  450. else
  451. {
  452. int ix = (int)floor(p->x + 0.5);
  453. int iy = (int)floor(p->y + 0.5);
  454. if(ix >= inCols)
  455. ix = inCols - 1;
  456. if(iy >= inRows)
  457. iy = inRows - 1;
  458. pixVal += (*in)(ix, iy);
  459. }
  460. delete direction;
  461. delete p;
  462. (*out)(x,y) = pixVal * scaler;
  463. }
  464. }
  465. }
  466. }