LadybugVirtualCamera.cpp
Go to the documentation of this file.
00001 /*
00002  * LadybugVirtualCamera.cpp
00003  *
00004  *  Created on: Aug 5, 2010
00005  *      Author: petrito1@cmp.felk.cvut.cz
00006  */
00007 
00008 #include "LadybugVirtualCamera.h"
00009 
00010 #include <fstream>
00011 #include <GL/glu.h>
00012 #include <GL/glut.h>
00013 #include <opencv2/highgui/highgui.hpp>
00014 
00015 #include "CameraException.h"
00016 
00017 namespace nifti {
00018 namespace ladybug {
00019 
00020 LadybugVirtualCamera::LadybugVirtualCamera() :
00021   viewportWidth(640), viewportHeight(480), pan(0.0), tilt(0.0), horizontalFov(60.0), verticalFov(45.0), meshCols(0),
00022       meshRows(0), alphamaskAvailable(false), initialized(false), offScreenRendering(false), frameBuffer(0),
00023       colorRenderBuffer(0), depthRenderBuffer(0) {
00024 
00025   // TODO: Remove...?
00026   for (int i = 0; i < NUM_CAMERAS; i++) {
00027     mesh[i] = NULL;
00028 
00029     alphaMaskWidths[i] = 0;
00030     alphaMaskHeights[i] = 0;
00031     alphamasks[i] = NULL;
00032 
00033     imageWidths[i] = 0;
00034     imageHeights[i] = 0;
00035 
00036     glTextureWidths[i] = 0;
00037     glTextureHeights[i] = 0;
00038     glTextures[i] = (GLuint) 0;
00039   }
00040 }
00041 
00042 LadybugVirtualCamera::~LadybugVirtualCamera() {
00043   clearMesh();
00044   clearAlphamask();
00045 }
00046 
00047 void LadybugVirtualCamera::initializeTextures() {
00048   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
00049   glEnable(GL_TEXTURE_2D);
00050   glShadeModel(GL_FLAT);
00051   // Generate and setup textures and blending.
00052   glGenTextures(6, glTextures);
00053 }
00054 
00055 void LadybugVirtualCamera::initializeFramebuffers() {
00056   glGenFramebuffers(1, &frameBuffer);
00057   glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
00058 
00059   // Create and attach a color buffer.
00060   glGenRenderbuffers(1, &colorRenderBuffer);
00061   // We must bind colorRenderBuffer before we call glRenderbufferStorage.
00062   glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
00063   // TODO: The storage has to be set according to viewport size.
00064   glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, viewportWidth, viewportHeight);
00065   // Attach color buffer to FBO.
00066   glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderBuffer);
00067 
00068   glGenRenderbuffers(1, &depthRenderBuffer);
00069   glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBuffer);
00070   // TODO: The storage has to be set according to viewport size.
00071   glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, viewportWidth, viewportHeight);
00072   // Attach depth buffer to FBO.
00073   glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderBuffer);
00074 
00075   // Does the GPU support current FBO configuration?
00076   GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
00077   switch (status) {
00078   case GL_FRAMEBUFFER_COMPLETE:
00079     break;
00080   default:
00081     deleteBuffers();
00082     throw std::runtime_error("Could not setup frame buffer.\n");
00083   }
00084 }
00085 
00086 void LadybugVirtualCamera::initialize(bool offScreenRendering) {
00087   this->offScreenRendering = offScreenRendering;
00088 
00089   if (this->offScreenRendering) {
00090     int glutWin = glutCreateWindow("temp");
00091     GLenum glew = glewInit();
00092     if (GLEW_OK != glew) {
00093       glutDestroyWindow(glutWin);
00094       throw std::runtime_error((char*) glewGetErrorString(glew));
00095     }
00096 
00097     initializeFramebuffers();
00098   }
00099 
00100   initializeTextures();
00101   this->initialized = true;
00102 }
00103 
00104 void LadybugVirtualCamera::updateBuffers() {
00105   glBindRenderbuffer(GL_RENDERBUFFER, colorRenderBuffer);
00106   glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, viewportWidth, viewportHeight);
00107 
00108   glBindRenderbuffer(GL_RENDERBUFFER, depthRenderBuffer);
00109   glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, viewportWidth, viewportHeight);
00110 }
00111 
00112 void LadybugVirtualCamera::updateViewport() {
00113 
00114   glViewport(0, 0, viewportWidth, viewportHeight);
00115 }
00116 
00117 void LadybugVirtualCamera::updateProjection() {
00118 
00119   glMatrixMode(GL_PROJECTION);
00120   glLoadIdentity();
00121   gluPerspective((GLdouble) verticalFov, (GLdouble) horizontalFov / (GLdouble) verticalFov, 0.1, 100.0);
00122 }
00123 
00124 void LadybugVirtualCamera::updateModelView() {
00125 
00126   glMatrixMode(GL_MODELVIEW);
00127   glLoadIdentity();
00128   glRotated(-tilt, 1.0, 0.0, 0.0); // Tilt the view.
00129   glRotated(-pan, 0.0, 1.0, 0.0); // Pan the view.
00130   // TODO: Do not rotate in case of portrait images?
00131   glRotated(-90.0, 1.0, 0.0, 0.0); // Convert Ladybug 3D coordinate system to OpenGL's one.
00132 }
00133 
00134 void LadybugVirtualCamera::clearBuffers() {
00135 
00136   // Clear the buffers to transparent black at "infinity".
00137   glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
00138   glClearDepth(1.0);
00139   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00140 }
00141 
00142 void LadybugVirtualCamera::render() {
00143   glMatrixMode(GL_TEXTURE);
00144   glLoadIdentity();
00145 
00146 //  printf("Mesh rows: %d, cols: %d.\n", meshRows, meshCols); // TODO: Remove line.
00147 
00148   // TODO: Avoid rendering textures outside the frustum?
00149   for (int iCamera = 0; iCamera < NUM_CAMERAS; iCamera++) // for each camera
00150   {
00151 //    printf("Rendering cam %d.\n", iCamera); // TODO: Remove line.
00152     double validTextureWidth = (double) imageWidths[iCamera] / glTextureWidths[iCamera];
00153     double validTextureHeight = (double) imageHeights[iCamera] / glTextureHeights[iCamera];
00154 
00155     glBindTexture(GL_TEXTURE_2D, glTextures[iCamera]);
00156     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00157     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00158 
00159     for (int iRow = 0; iRow < meshRows - 1; iRow++) // for each row
00160     {
00161       glBegin(GL_TRIANGLE_STRIP);
00162       for (int iCol = 0; iCol < meshCols; iCol++) // for each column
00163       {
00164         // Texture coordinates.
00165         double p1 = (double) iCol / (meshCols - 1) * validTextureWidth;
00166         double q1 = (double) iRow / (meshRows - 1) * validTextureHeight;
00167         double q2 = (double) (iRow + 1.0) / (meshRows - 1) * validTextureHeight;
00168 
00169         // 3-D world coordinates.
00170         int ptr1 = iRow * meshCols + iCol;
00171         double x1 = mesh[iCamera][ptr1 * 3 + 0];
00172         double y1 = mesh[iCamera][ptr1 * 3 + 1];
00173         double z1 = mesh[iCamera][ptr1 * 3 + 2];
00174 
00175         int ptr2 = (iRow + 1) * meshCols + iCol;
00176         double x2 = mesh[iCamera][ptr2 * 3 + 0];
00177         double y2 = mesh[iCamera][ptr2 * 3 + 1];
00178         double z2 = mesh[iCamera][ptr2 * 3 + 2];
00179 
00180         glTexCoord2d(p1, q1);
00181         glVertex3d(x1, y1, z1);
00182 
00183         glTexCoord2d(p1, q2);
00184         glVertex3d(x2, y2, z2);
00185       }
00186       glEnd();
00187     }
00188   }
00189 }
00190 
00191 void LadybugVirtualCamera::renderView(cv::Mat& image) {
00192 
00193   clearBuffers();
00194   updateViewport();
00195   updateProjection();
00196   updateModelView();
00197   render();
00198 
00199   // Copy pixels to main memory.
00200   cv::Mat flippedImage(viewportHeight, viewportWidth, CV_8UC4);
00201   glReadPixels(0, 0, viewportWidth, viewportHeight, GL_BGRA, GL_UNSIGNED_BYTE, flippedImage.data);
00202 
00203   // OpenGL y coordinate rises towards top of the image... flip the image.
00204   cv::flip(flippedImage, image, 0);
00205 }
00206 
00207 void LadybugVirtualCamera::deleteBuffers() {
00208   if (!this->offScreenRendering) {
00209     // Nothing to delete.
00210     return;
00211   }
00212 
00213   if (colorRenderBuffer > 0) {
00214     glDeleteRenderbuffers(1, &colorRenderBuffer);
00215   }
00216   if (depthRenderBuffer > 0) {
00217     glDeleteRenderbuffers(1, &depthRenderBuffer);
00218   }
00219   // Bind 0 framebuffer, which means render to back buffer.
00220   // As a result, our framebuffer is unbound.
00221   glBindFramebuffer(GL_FRAMEBUFFER, 0);
00222   if (frameBuffer > 0) {
00223     glDeleteFramebuffers(1, &frameBuffer);
00224   }
00225 }
00226 
00227 void LadybugVirtualCamera::getViewport(int* viewportWidth, int* viewportHeight) {
00228   *viewportWidth = this->viewportWidth;
00229   *viewportHeight = this->viewportHeight;
00230 }
00231 void LadybugVirtualCamera::setViewport(int viewportWidth, int viewportHeight) {
00232   // Regenerate the graphics buffers only when necessary.
00233   if (this->viewportWidth != viewportWidth || this->viewportHeight != viewportHeight) {
00234 
00235     this->viewportWidth = viewportWidth;
00236     this->viewportHeight = viewportHeight;
00237 
00238     if (this->offScreenRendering) {
00239       updateBuffers();
00240     }
00241   }
00242 }
00243 
00244 double LadybugVirtualCamera::getPan() {
00245   return this->pan;
00246 }
00247 void LadybugVirtualCamera::setPan(double pan) {
00248   this->pan = pan;
00249 }
00250 double LadybugVirtualCamera::getTilt() {
00251   return this->tilt;
00252 }
00253 void LadybugVirtualCamera::setTilt(double tilt) {
00254   this->tilt = tilt;
00255 }
00256 double LadybugVirtualCamera::getHorizontalFov() {
00257   return this->horizontalFov;
00258 }
00259 void LadybugVirtualCamera::setHorizontalFov(double horizontalFov) {
00260   this->horizontalFov = horizontalFov;
00261 }
00262 double LadybugVirtualCamera::getVerticalFov() {
00263   return this->verticalFov;
00264 }
00265 void LadybugVirtualCamera::setVerticalFov(double verticalFov) {
00266   this->verticalFov = verticalFov;
00267 }
00268 
00269 void LadybugVirtualCamera::loadMesh(const std::string& meshFile) {
00270   printf("Loading mesh from %s...\n", meshFile.data());
00271   // TODO: Use temp buffer until we know that no exception will occur.
00272   clearMesh();
00273 
00274   FILE *fp = fopen(meshFile.data(), "r");
00275   if (fp == NULL) {
00276     throw std::runtime_error("Opening mesh file failed: " + meshFile);
00277   }
00278 
00279   if (fscanf(fp, "cols %d rows %d\n", &meshCols, &meshRows) != 2) {
00280     fclose(fp);
00281     throw std::runtime_error("Reading mesh file header failed.");
00282   }
00283   printf("Header: rows: %d, columns: %d\n", meshRows, meshCols);
00284 
00285   for (int iImage = 0; iImage < NUM_CAMERAS; iImage++) {
00286     mesh[iImage] = new double[meshCols * meshRows * 3];
00287 
00288     for (int iRow = 0; iRow < meshRows; iRow++) {
00289       for (int iCol = 0; iCol < meshCols; iCol++) {
00290         double x, y, z;
00291         int scanned;
00292         if ((scanned = fscanf(fp, "%lf, %lf, %lf", &x, &y, &z)) != 3) {
00293           fclose(fp);
00294           printf("Image %d, row %d, column %d. Values scanned: %d\n", iImage, iRow, iCol, scanned);
00295           printf("%lf\n", x);
00296           throw std::runtime_error("Reading mesh file failed.");
00297         }
00298         mesh[iImage][(iRow * meshCols + iCol) * 3 + 0] = x;
00299         mesh[iImage][(iRow * meshCols + iCol) * 3 + 1] = y;
00300         mesh[iImage][(iRow * meshCols + iCol) * 3 + 2] = z;
00301       }
00302     }
00303   }
00304 
00305   fclose(fp);
00306 }
00307 
00308 void LadybugVirtualCamera::clearMesh() {
00309   printf("Clearing mesh...\n");
00310   for (int i = 0; i < NUM_CAMERAS; i++) {
00311     delete[] mesh[i];
00312     mesh[i] = NULL;
00313   }
00314 }
00315 
00316 void LadybugVirtualCamera::loadAlphamask(const std::string& alphamaskFilePrefix) {
00317   clearAlphamask();
00318   printf("Loading alphamask using prefix %s...\n", alphamaskFilePrefix.data());
00319   alphamaskAvailable = true;
00320   for (int iCamera = 0; iCamera < NUM_CAMERAS; iCamera++) {
00321     char pgmPath[1024];
00322     sprintf(pgmPath, "%s%d.pgm", alphamaskFilePrefix.data(), iCamera);
00323 
00324     // Read alpha mask and set it to the 4th (U) byte of the BGRU buffer while scaling.
00325     cv::Mat alphamaskMat = cv::imread(pgmPath, 0);
00326     // TODO: Check elem size?
00327     //printf("Alphamask element size: %lu.", static_cast<long unsigned int> (alphamaskMat.elemSize()));
00328     if (alphamaskMat.elemSize() > 1) {
00329       alphamaskAvailable = false;
00330       throw std::runtime_error("Invalid alphamask type.");
00331     }
00332     alphaMaskWidths[iCamera] = alphamaskMat.cols;
00333     alphaMaskHeights[iCamera] = alphamaskMat.rows;
00334     int alphamaskSize = alphamaskMat.cols * alphamaskMat.rows * alphamaskMat.elemSize();
00335     alphamasks[iCamera] = new unsigned char[alphamaskSize];
00336     memcpy(alphamasks[iCamera], alphamaskMat.data, alphamaskSize);
00337 
00338     // Enable blending.
00339 //    glEnable(GL_BLEND);
00340 //    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00341   }
00342 }
00343 
00344 void LadybugVirtualCamera::clearAlphamask() {
00345   printf("Clearing alphamask...\n");
00346   for (int iCamera = 0; iCamera < NUM_CAMERAS; iCamera++) {
00347     //    free(alphamasks[iCamera]);
00348     delete[] alphamasks[iCamera];
00349     alphamasks[iCamera] = NULL;
00350   }
00351   alphamaskAvailable = false;
00352   glDisable(GL_BLEND);
00353 }
00354 
00355 void LadybugVirtualCamera::setImage(const int& imageIndex, const cv::Mat& image) {
00356 
00357   imageWidths[imageIndex] = image.cols;
00358   imageHeights[imageIndex] = image.rows;
00359 
00360   // 3-channel BGR image is assumed.
00361   int channels = image.channels();
00362   if (channels != 3) {
00363     throw std::runtime_error("Image must have 3 channels.");
00364   }
00365 
00366   unsigned char* bgraBuffer = new unsigned char[imageWidths[imageIndex] * imageHeights[imageIndex] * 4];
00367   // Copy color bands from the BGR buffer to the BGRA buffer.
00368   for (int iData = 0; iData < imageWidths[imageIndex] * imageHeights[imageIndex] * channels; iData++) {
00369     // TODO: Is data a pointer to ROI or whole data?
00370     bgraBuffer[iData + (iData / channels)] = image.data[iData];
00371   }
00372 
00373   if (alphamaskAvailable) {
00374     for (int y = 0; y < imageHeights[imageIndex]; y++) {
00375       for (int x = 0; x < imageWidths[imageIndex]; x++) {
00376         int alphamaskX = x * alphaMaskWidths[imageIndex] / imageWidths[imageIndex];
00377         int alphamaskY = y * alphaMaskHeights[imageIndex] / imageHeights[imageIndex];
00378         bgraBuffer[(y * imageWidths[imageIndex] + x) * 4 + 3] = alphamasks[imageIndex][alphamaskY
00379             * alphaMaskWidths[imageIndex] + alphamaskX];
00380       }
00381     }
00382   }
00383 
00384   glBindTexture(GL_TEXTURE_2D, glTextures[imageIndex]);
00385 
00386   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
00387   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
00388   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00389   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00390 
00391   // Make a texture from the PPM image.
00392   // Texture size is set to the minimum of power of two which can contain the PPM
00393   // so that this program works on lower OpenGL versions.
00394   glTextureWidths[imageIndex] = getMinimumPowerOfTwo(imageWidths[imageIndex]);
00395   glTextureHeights[imageIndex] = getMinimumPowerOfTwo(imageHeights[imageIndex]);
00396 
00397   // Copy PPM image pixels to the valid region of the texture.
00398   int textureElems = glTextureWidths[imageIndex] * glTextureHeights[imageIndex] * 4;
00399   unsigned char* textureBuffer = new unsigned char[textureElems];
00400 //  memset(textureBuffer, 0, textureElems);
00401 
00402   for (int y = 0; y < imageHeights[imageIndex]; y++) {
00403     for (int x = 0; x < imageWidths[imageIndex]; x++) {
00404       textureBuffer[(y * glTextureWidths[imageIndex] + x) * 4 + 0] = bgraBuffer[(y * imageWidths[imageIndex] + x) * 4
00405           + 0]; // B
00406       textureBuffer[(y * glTextureWidths[imageIndex] + x) * 4 + 1] = bgraBuffer[(y * imageWidths[imageIndex] + x) * 4
00407           + 1]; // G
00408       textureBuffer[(y * glTextureWidths[imageIndex] + x) * 4 + 2] = bgraBuffer[(y * imageWidths[imageIndex] + x) * 4
00409           + 2]; // R
00410       textureBuffer[(y * glTextureWidths[imageIndex] + x) * 4 + 3] = bgraBuffer[(y * imageWidths[imageIndex] + x) * 4
00411           + 3]; // A
00412     }
00413   }
00414 
00415   // Transfer the RGB buffer to graphics card.
00416 //  printf("Calling glTexImage2D.\n");
00417   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, glTextureWidths[imageIndex], glTextureHeights[imageIndex], 0, GL_BGRA_EXT,
00418       GL_UNSIGNED_BYTE, textureBuffer);
00419 //  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTextureWidths[imageIndex], glTextureHeights[imageIndex], 0, GL_RGBA,
00420 //        GL_UNSIGNED_BYTE, textureBuffer);
00421 //  printf("Calling gluBuild2DMipmaps.\n");
00422 //  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA,
00423 //        glTextureWidths[imageIndex], glTextureHeights[imageIndex], GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, textureBuffer);
00424 //  printf("GL error: %s.\n", glGetString(glGetError()));
00425 
00426   // TODO: Reset after every texture update?
00427   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
00428   glEnable(GL_TEXTURE_2D);
00429   glShadeModel(GL_FLAT);
00430   glEnable(GL_BLEND);
00431   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00432 
00433   // Now the RGB texture is transferred to graphics, we won't need these.
00434   delete[] bgraBuffer;
00435   delete[] textureBuffer;
00436 }
00437 
00438 void LadybugVirtualCamera::setImages(const cv::Mat(&images)[NUM_CAMERAS]) {
00439   for (int iImage = 0; iImage < NUM_CAMERAS; iImage++) {
00440     setImage(iImage, images[iImage]);
00441   }
00442 }
00443 
00444 void LadybugVirtualCamera::setImages(const cv::Mat& compositeImage) {
00445   int imageHeight = compositeImage.rows / NUM_CAMERAS;
00446   cv::Mat images[NUM_CAMERAS];
00447   for (int iImage = 0; iImage < NUM_CAMERAS; iImage++) {
00448     images[iImage] = compositeImage.rowRange(iImage * imageHeight, (iImage + 1) * imageHeight);
00449   }
00450   setImages(images);
00451 }
00452 
00453 int LadybugVirtualCamera::getMinimumPowerOfTwo(int n) {
00454   int i = 1;
00455   while (i < n) {
00456     i *= 2;
00457   }
00458   return i;
00459 }
00460 
00461 }
00462 }
 All Classes Namespaces Files Functions Variables Typedefs Defines


omnicamera
Author(s): Tomas Petricek / petrito1@cmp.felk.cvut.cz
autogenerated on Tue Dec 10 2013 14:26:53