OpenCVWidget
|
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 }