OpenCVWidget
opencvviewer.cpp
Go to the documentation of this file.
00001 #include "opencvviewer.h"
00002 
00003 using namespace cv;
00004 
00005 OpenCVViewer::OpenCVViewer(QWidget *parent, QGLWidget *shareWidget)
00006     : QGLWidget(parent, shareWidget)
00007 {
00008 
00009 }
00010 
00011 OpenCVViewer::~OpenCVViewer()
00012 {
00013 }
00014 
00015 QSize OpenCVViewer::sizeHint() const
00016 {
00017     return QSize(400, 300);
00018 }
00019 
00020 Mat OpenCVViewer::getImage()
00021 {
00022     return displayImage;
00023 }
00024 
00025 void OpenCVViewer::setImage(Mat newImage)
00026 {
00027     displayImage = newImage;
00028 
00029     //Draw the scene
00030     glDraw();
00031 }
00032 
00033 void OpenCVViewer::initializeGL()
00034 {
00035     // Create the surface we will use for the texture:
00036     static const int coords[4][3] = { { +1, -1 }, { -1, -1 }, { -1, +1 }, { +1, +1 } };
00037     for (int j = 0; j < 4; ++j) {
00038         /* A note about texture coordinates:
00039           OpenCV uses a nice, sane coordinate system with origin in the upper left corner.
00040           Just like any other image processing tool (let's just forget the fact that math-wise
00041           that is silly).
00042           OpenGL, however, uses a math-inspired coordinate system with origin in the lower
00043           left.
00044           Right here, the texture is mapped, so the image is automatically flipped in the y-
00045           direction. Better do it here than actually flipping the image elsewhere.
00046         */
00047         texCoords.append(QVector2D(j == 0 || j == 3, j == 2 || j == 3));
00048         vertices.append(QVector2D(coords[j][0], coords[j][1]));
00049     }
00050 
00051     glEnable(GL_DEPTH_TEST);
00052     glEnable(GL_CULL_FACE);
00053     glEnable(GL_TEXTURE_2D);
00054 }
00055 
00056 void OpenCVViewer::paintGL()
00057 {
00058     if(displayImage.empty()) {
00059         displayImage = Mat::zeros(1, 1, CV_8UC3); // Paint a black background until we have something to show.
00060     }
00061 
00062     qglClearColor(Qt::black); // Create a nice, black background for the parts of the widget with no image.
00063     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
00064 
00065     glLoadIdentity();
00066 
00067     glVertexPointer(2, GL_FLOAT, 0, vertices.constData());
00068     glTexCoordPointer(2, GL_FLOAT, 0, texCoords.constData());
00069     glEnableClientState(GL_VERTEX_ARRAY);
00070     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
00071 
00072 
00073     // Below are three different methods of mapping the image as a texture. Use only one.
00074     // Basically, stick to method 3 unless you have very good reason for not doing that.
00075 
00076     // Method 1:
00077 /*
00078     This method binds the texture using mipmaps for smooth resizing. However, this means that a new
00079     mipmap must be calculated for each frame. This is slow (OpenGL is built for using static textures
00080     where the mipmaps are simply pre-calculated at the start of the program), so while it works it is
00081     so slow that I cannot recommend this approach.
00082 
00083     // Additions from http://www.nullterminator.net/gltexture.html:
00084     glGenTextures(1, &texture); // Allocate a texture name.
00085     glBindTexture(GL_TEXTURE_2D, texture); // Select our current texture.
00086     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Select modulate to mix texture with color for shading.
00087     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); // When texture area is small, use the closest mipmap.
00088     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // When texture area is large, use the first mipmap.
00089     gluBuild2DMipmaps( GL_TEXTURE_2D, 3, displayImage.cols, displayImage.rows, GL_BGR, GL_UNSIGNED_BYTE, displayImage.data); // Build our texture mipmaps from the raw OpenCV image data.
00090     // End of additions
00091 */
00092 
00093     // Method 2:
00094 /*
00095     Alternative way, going via a QPixmap. Do not use, it is slow.
00096     Remaining here to maintain my sanity, should the other solutions break.
00097     texture = bindTexture(QPixmap(QString("side1.png")), GL_TEXTURE_2D);
00098     glBindTexture(GL_TEXTURE_2D, texture);
00099 */
00100 
00101     // Method 3:
00102     // Non-mipmap way of mapping the texture (fast and clean):
00103     glGenTextures(1, &texture); // Allocate a texture name.
00104     glBindTexture(GL_TEXTURE_2D, texture); // Select our current texture.
00105     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // When the texture area is larger then the image, upscale using linear interpolation.
00106     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // When the texture area is smaller than the image, downsample using linear interpolation.
00107 
00108     if(displayImage.type() == CV_8UC3) {
00109         glTexImage2D(GL_TEXTURE_2D, 0, 3, displayImage.cols, displayImage.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, displayImage.data);
00110     } else if(displayImage.type() == CV_8UC1) {
00111         glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY8, displayImage.cols, displayImage.rows, 0, GL_RED, GL_UNSIGNED_BYTE, displayImage.data);
00112     }
00113 
00114     // End of different methods, the last few lines are common for all methods.
00115 
00116     glDrawArrays(GL_TRIANGLE_FAN, 0, 4); // Draw it!
00117 
00118     glDeleteTextures(1, &texture);
00119 
00120 }
00121 
00122 void OpenCVViewer::resizeGL(int width, int height)
00123 {
00124     // Make sure the image keeps its aspect ratio, regardless of widget size:
00125     // (also, center it in the widget)
00126     float imgRatio = (float)displayImage.cols/(float)displayImage.rows;
00127     float windowRatio = (float)width/(float)height;
00128     if(windowRatio < imgRatio) {
00129         glViewport(0, (height-width/imgRatio)/2, width, width/imgRatio);
00130     } else {
00131         glViewport((width-height*imgRatio)/2, 0, height*imgRatio, height);
00132     }
00133 
00134     glMatrixMode(GL_PROJECTION);
00135     glLoadIdentity();
00136     glOrtho(-1.0, +1.0, +1.0, -1.0, 0.0, 1.0);
00137 
00138     glMatrixMode(GL_MODELVIEW);
00139 }
00140 
00141 void OpenCVViewer::mousePressEvent(QMouseEvent* event)
00142 {
00143     emit mouseClicked(event);
00144 }
00145 
00146 void OpenCVViewer::mouseMoveEvent(QMouseEvent* event)
00147 {
00148     emit mouseMoved(event);
00149 }
00150 
00151 cv::Point OpenCVViewer::mapPoint(cv::Point widgetCoords)
00152 {
00153     if(displayImage.empty()) { // Bail out if we have no image to map to.
00154         return cv::Point(0,0);
00155     }
00156 
00157     cv::Point mappedPoint;
00158     float viewportRatio = (float)this->width()/(float)this->height();
00159     float imageRatio = (float)displayImage.cols/(float)displayImage.rows;
00160 
00161     mappedPoint.x = (int)((float)widgetCoords.x/(float)this->width()*(float)displayImage.cols);
00162     mappedPoint.y = (int)((float)widgetCoords.y/(float)this->height()*(float)displayImage.rows);
00163     if(viewportRatio > imageRatio) { // The viewport has black bars on the sides.
00164         int imageWidth = displayImage.cols*(float)this->height()/(float)displayImage.rows;
00165         int offset = (this->width()-imageWidth)/2;
00166         mappedPoint.x = displayImage.cols*((float)(widgetCoords.x-offset)/(float)imageWidth);
00167     } else if(viewportRatio < imageRatio) { // The viewport has black bars on top and bottom.
00168         int imageHeight = displayImage.rows*(float)this->width()/(float)displayImage.cols;
00169         int offset = (this->height()-imageHeight)/2;
00170         mappedPoint.y = displayImage.rows*((float)(widgetCoords.y-offset)/(float)imageHeight);
00171     }
00172 
00173     return mappedPoint;
00174 }
 All Classes Files Functions Variables