EquirectangularToFovVideo.cu 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // EquirectangularToFovVideo.cu
  2. #include "EquirectangularToFovVideo.h"
  3. #include "Util.h"
  4. #include <iostream>
  5. #include <cstdlib>
  6. #include <algorithm>
  7. #include <cmath>
  8. #define PI 3.14159265
  9. using namespace std;
  10. // PUBLIC:
  11. EquirectangularToFovVideo::EquirectangularToFovVideo(Arff* pArff) : EquirectangularToFovBase(pArff), eq_d{nullptr}, fov_d{nullptr}, m_pInfo{new ConvertionInfo()}
  12. {
  13. }
  14. /*virtual*/ EquirectangularToFovVideo::~EquirectangularToFovVideo()
  15. {
  16. if (eq_d != nullptr)
  17. cudaFree(eq_d);
  18. if (fov_d != nullptr)
  19. cudaFree(fov_d);
  20. delete m_pInfo;
  21. }
  22. // The following functions are the same as the ones provided from the base class
  23. // but specialized to run on the device (GPU)
  24. __device__ void EquirectangularToSpherical_d(unsigned int xEq, unsigned int yEq, unsigned int widthPx, unsigned int heightPx, double *horRads, double *verRads)
  25. {
  26. *horRads = (xEq * 2.0 * PI) / widthPx;
  27. *verRads = (yEq * PI) / heightPx;
  28. }
  29. __device__ void SphericalToEquirectangular_d(double horRads, double verRads, unsigned int widthPx, unsigned int heightPx, unsigned int *xEq, unsigned int *yEq)
  30. {
  31. int x = (int)((horRads / (2.0 * PI)) * widthPx + 0.5); // round double to closer int
  32. int y = (int)((verRads / PI) * heightPx + 0.5);
  33. // make sure returned values are within the video
  34. if (x < 0)
  35. x = widthPx + x - 1;
  36. else if (x >= (int)widthPx)
  37. x -= widthPx;
  38. *xEq = (unsigned int) x;
  39. if (y < 0)
  40. y = heightPx + y - 1;
  41. if (y >= (int)heightPx)
  42. y = 2 * heightPx - y - 1;
  43. *yEq = (unsigned int) y;
  44. }
  45. __device__ void SphericalToCartesian_d(double horRads, double verRads, Vec3 *cart)
  46. {
  47. cart->x = sin(verRads)*cos(horRads);
  48. cart->y = cos(verRads);
  49. cart->z = sin(verRads)*sin(horRads);
  50. }
  51. __device__ void CartesianToSpherical_d(Vec3 cart, double *horRads, double *verRads)
  52. {
  53. *horRads = atan2(cart.z, cart.x);
  54. *verRads = acos(cart.y);
  55. }
  56. __device__ Vec3 RotatePoint_d(Matrix33 rot, Vec3 v)
  57. {
  58. //Vec3 res(0,0,0);
  59. Vec3 res = v; // Avoid __device__ constructor by using copy constructor
  60. res.x = rot.mat[0][0]*v.x + rot.mat[0][1]*v.y + rot.mat[0][2]*v.z;
  61. res.y = rot.mat[1][0]*v.x + rot.mat[1][1]*v.y + rot.mat[1][2]*v.z;
  62. res.z = rot.mat[2][0]*v.x + rot.mat[2][1]*v.y + rot.mat[2][2]*v.z;
  63. return res;
  64. }
  65. __global__ void GPUCalculation(const uchar *eq_d, uchar *fov_d, ConvertionInfo *info)
  66. {
  67. double fovWidth_rads = (info->fovWidth_deg * PI / 180);
  68. double fovHeight_rads = (info->fovHeight_deg * PI / 180);
  69. double horRads, verRads;
  70. double vidHorRads, vidVerRads;
  71. unsigned int xEq, yEq;
  72. int idx = blockIdx.x*blockDim.x + threadIdx.x;
  73. int idy = blockIdx.y*blockDim.y + threadIdx.y;
  74. int x = idx;
  75. int y = idy;
  76. if (x >= info->fovWidth_px || y >= info->fovHeight_px)
  77. return;
  78. horRads = x * fovWidth_rads / info->fovWidth_px - fovWidth_rads / 2.0;
  79. verRads = y * fovHeight_rads / info->fovHeight_px - fovHeight_rads / 2.0;
  80. // make it point towards center of equirectangular projection
  81. horRads += PI;
  82. verRads += PI/2;
  83. //Vec3 pixelVec(0,0,0);
  84. Vec3 pixelVec = info->tmpVec; // Avoid __device__ constructor by using copy constructor
  85. SphericalToCartesian_d(horRads, verRads, &pixelVec);
  86. Vec3 vidPixelVec = RotatePoint_d(info->rot, pixelVec);
  87. CartesianToSpherical_d(vidPixelVec, &vidHorRads, &vidVerRads);
  88. SphericalToEquirectangular_d(vidHorRads, vidVerRads, info->eqWidth_px, info->eqHeight_px, &xEq, &yEq);
  89. int posEq = yEq*info->eqWidth_px*4 + xEq*4;
  90. int posFov = y*info->fovWidth_px*4 + x*4;
  91. *(fov_d + posFov) = *(eq_d + posEq);
  92. *(fov_d + posFov + 1) = *(eq_d + posEq + 1);
  93. *(fov_d + posFov + 2) = *(eq_d + posEq + 2);
  94. *(fov_d + posFov + 3) = *(eq_d + posEq + 3);
  95. }
  96. bool EquirectangularToFovVideo::Convert(const QImage *eqImage, long int time, QImage *fovImage)
  97. {
  98. double xEqHead, yEqHead, tiltHead;
  99. GetHeadPos(time, &xEqHead, &yEqHead, &tiltHead);
  100. double horHeadRads, verHeadRads;
  101. EquirectangularToSpherical(xEqHead, yEqHead, m_pArff->WidthPx(), m_pArff->HeightPx(), &horHeadRads, &verHeadRads);
  102. Vec3 headVec(0,0,0);
  103. SphericalToCartesian(horHeadRads, verHeadRads, &headVec);
  104. Vec3 vidVec(-1,0,0); // pointing to the middle of equirectangular projection
  105. double headTiltRads = tiltHead * PI / 180;
  106. Matrix33 rot = HeadToVideoRotation(headVec, headTiltRads, vidVec);
  107. const uchar *eqImageBits = eqImage->bits();
  108. uchar *fovImageBits = fovImage->bits();
  109. GenerateSampling(fovImage);
  110. // Set up GPU for calculation
  111. ConvertionInfo *info = new ConvertionInfo();
  112. ConvertionInfo *info_d;
  113. m_pInfo->rot = rot;
  114. m_pInfo->fovWidth_deg = m_fovWidthDeg;
  115. m_pInfo->fovHeight_deg = m_fovHeightDeg;
  116. m_pInfo->fovWidth_px = fovImage->width();
  117. m_pInfo->fovHeight_px = fovImage->height();
  118. m_pInfo->eqWidth_px = eqImage->width();
  119. m_pInfo->eqHeight_px = eqImage->height();
  120. if (eq_d == nullptr)
  121. cudaMalloc((void**)&eq_d, eqImage->byteCount());
  122. if (fov_d == nullptr)
  123. cudaMalloc((void**)&fov_d, fovImage->byteCount());
  124. cudaMalloc((void**)&info_d, sizeof(ConvertionInfo));
  125. cudaMemcpy(eq_d, eqImageBits, eqImage->byteCount(), cudaMemcpyHostToDevice);
  126. cudaMemcpy(fov_d, fovImageBits, fovImage->byteCount(), cudaMemcpyHostToDevice);
  127. cudaMemcpy(info_d, m_pInfo, sizeof(ConvertionInfo), cudaMemcpyHostToDevice);
  128. int device;
  129. cudaGetDevice(&device);
  130. cudaDeviceProp devProp;
  131. cudaGetDeviceProperties(&devProp, device);
  132. int maxThreads = devProp.maxThreadsPerBlock;
  133. int maxThreadsDim = floor(sqrt(maxThreads));
  134. dim3 dimBlock(maxThreadsDim, maxThreadsDim);
  135. dim3 dimGrid(fovImage->width()/dimBlock.x + 1, fovImage->height()/dimBlock.y + 1);
  136. GPUCalculation<<<dimGrid,dimBlock>>>(eq_d, fov_d, info_d);
  137. cudaMemcpy(fovImageBits, fov_d, fovImage->byteCount(), cudaMemcpyDeviceToHost);
  138. cudaFree(info_d);
  139. // *** Placeholder
  140. return true;
  141. }
  142. double EquirectangularToFovVideo::GetAspectRatio()
  143. {
  144. return (double)m_fovWidthPx/m_fovHeightPx;
  145. }
  146. // PRIVATE:
  147. void EquirectangularToFovVideo::GenerateSampling(const QImage *image)
  148. {
  149. if ((int)m_vHorSampling.size() == image->width() && (int)m_vVerSampling.size() == image->height())
  150. return;
  151. m_vHorSampling.resize(image->width());
  152. m_vVerSampling.resize(image->height());
  153. double fovWidthRads = (m_fovWidthDeg * PI / 180);
  154. double fovHeightRads = (m_fovHeightDeg * PI / 180);
  155. Generate1DSampling(fovWidthRads, &m_vHorSampling);
  156. Generate1DSampling(fovHeightRads, &m_vVerSampling);
  157. }
  158. void EquirectangularToFovVideo::Generate1DSampling(double fovRads, vector<double> *samples)
  159. {
  160. for (size_t i=0; i<samples->size(); i++)
  161. (*samples)[i] = i * fovRads / samples->size() - fovRads / 2.0;
  162. }