C++搭建框架,利用OpenGL、GDAL、Qt进行分块显示遥感影像

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yangfahe1/article/details/83958933

主要是利用C++搭建的框架,利用OpenGL、GDAL及Qt进行影像分块显示遥感影像,目前测试显示600M的资源3号卫星影像,仅仅需要15秒左右的时间。

此文章不对OpenGL以及GDAL做解释,如果对OpenGL和GDAL不熟悉,请自行查阅相应的文档。

利用OpenGL显示影像,那么第一步首先是要对OpenGL进行初始化。
void GeoGraphicsView::initializeGL()
{
      initializeOpenGLFunctions();
      glShadeModel(GL_SMOOTH); glClearDepth(1.0f);
      glClearColor(d_ptr->bgcolor().x, d_ptr->bgcolor().y,
      d_ptr->bgcolor().z, d_ptr->bgcolor().w);
      glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
      glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
      createShader(); getLocationId();
      QOpenGLWidget::initializeGL();
}

在initializeGL接口中d_ptr是GeoGraphicsView的一个类成员,仅仅保存了一些类的私有成员。这也符合Qt编程规范。

createShader接口是用来初始化着色器。
bool GeoGraphicsView::createShader()
{

       NApplication app; QString shader = app.currentShaderVertex();
       d_ptr->program()->addShaderFromSourceCode(QOpenGLShader::Vertex, shader);
       QString log = d_ptr->program()->log(); if(false == log.isEmpty())  return false;
       shader = app.currentShaderFragment();
       d_ptr->program()->addShaderFromSourceCode(QOpenGLShader::Fragment, shader);
       log = d_ptr->program()->log(); if(false == log.isEmpty()) return false;
       d_ptr->program()->link(); log = d_ptr->program()->log(); if(false == log.isEmpty()) return false;
       d_ptr->program()->bind(); log = d_ptr->program()->log(); if(false == log.isEmpty()) return false;

       return true;
}

getLocationId接口是用来获取在着色器中定义的变量。
void GeoGraphicsView::getLocationId()
{
     d_ptr->matrix(d_ptr->program()->uniformLocation("matrix"));
     d_ptr->sclaeid(d_ptr->program()->attributeLocation("scale"));
     d_ptr->posAttr(d_ptr->program()->attributeLocation("posAttr"));
     d_ptr->colAttr(d_ptr->program()->attributeLocation("colAttr"));
}
到此OpenGL初始化完成。

下一步就是渲染了。
void GeoGraphicsView::paintGL()
{
      modifyShaderVariable();
      renderData();
     QOpenGLWidget::paintGL();
}
在渲染接口中,首先是修改着色器中的变量值,因为我们可能对显示的影像进行缩放或者平移操作。这些操作都是需要在着色器中对变量进行修改。
void GeoGraphicsView::modifyShaderVariable()
{
      float halfWidth = d_ptr->winwidth() / 2.0f;
      float halfHeight = d_ptr->winheight() / 2.0f;
      glViewport(0, 0, d_ptr->winwidth(), d_ptr->winheight());
      QMatrix4x4 screenProj, rotatex, rotatey, rotate, scale, move;
      screenProj.ortho(-halfWidth, halfWidth, halfHeight, -halfHeight, -80.0f, 80.0f);
      rotatex.rotate(d_ptr->rotateangle().x, 0, 1); rotatey.rotate(d_ptr->rotateangle().y, 1, 0);
      rotate = rotatey *rotatex; screenProj = screenProj * rotate;
      screenProj.scale(d_ptr->scale());
      screenProj.translate(d_ptr->movedist().x(), d_ptr->movedist().y());
      d_ptr->program()->setUniformValue(d_ptr->matrix(), screenProj);
}
这个接口中都是使用OpenGL和Qt的接口,不懂的可以自行查阅文档。

接下来就是对数据进行渲染,而渲染又可以分为两种方式,一种是利用纹理进行贴图,第二种就是将影像数据进行按照实际的栅格格式进行渲染显示。显示DEM数据的时候就是利用纹理贴图的方式进行显示,而遥感影像是使用第二种方式。
void GeoGraphicsView::renderData()
{
     glClearColor(d_ptr->bgcolor().x, d_ptr->bgcolor().y,
     d_ptr->bgcolor().z, d_ptr->bgcolor().w);
     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
     for(int idx = 0; idx < d_ptr->block(); ++idx)
     {
           glEnableVertexAttribArray(d_ptr->colAttr());
           glEnableVertexAttribArray(d_ptr->posAttr());

           glBindBuffer(GL_ARRAY_BUFFER, d_ptr->colorid()[idx]);
           glVertexAttribPointer(d_ptr->colAttr(), 3, GL_FLOAT, GL_FALSE, sizeof(float3), 0);

           glBindBuffer(GL_ARRAY_BUFFER, d_ptr->dataid()[idx]);
           glVertexAttribPointer(d_ptr->posAttr(), 2, GL_FLOAT, GL_FALSE, sizeof(float2), 0);

           glDrawArrays(GL_POINTS, 0, d_ptr->verticeslen()[idx]);
           glBindBuffer(GL_ARRAY_BUFFER, 0);
           glBindBuffer(GL_ARRAY_BUFFER, 0);
           glDisableVertexAttribArray(d_ptr->posAttr());
           glDisableVertexAttribArray(d_ptr->colAttr());
     }
}

在打开软件的时候因为没有数据,所以d_ptr中的block为0,这个block也就是分块的总数。后面有介绍。到此OpenGL初始化以及渲染都完成。接下来就是组织数据,将数据传入着色器中。

前方高能,警报!!!警报!!!

打开影像,AdapterRaster类是对GDAL进行了封装,没有其他特别的。
bool GeoGraphicsView::openFile(const QString &file)
{
     if(true == file.isEmpty()) return false;
     AdapterRaster raster;
     if(false == raster.openImage(file, ReadOnly)) return false;
     if(raster->imageZSize() != 1) readImageRGB(raster);
     else readImageGray(raster);
     return true;
}
接下来就是读取影像。。。。(包括对数据进行分块)
void GeoGraphicsView::readImageRGB(const Object<IAdapterRaster> &raster)
{
     glClearColor(d_ptr->bgcolor().x, d_ptr->bgcolor().y,
     d_ptr->bgcolor().z, d_ptr->bgcolor().w);
     glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
     int bandMap[3] = { 3, 2, 1 };
     int w = raster.imageXSize(); d_ptr->width(w);
     int h = raster.imageYSize(); d_ptr->height(h);
     int xblock = w / 640, yblock = h / 640;
     xblock = (w + xblock + 639) / 640;
     yblock = (h + yblock + 639) / 640;
     int block = xblock * yblock;
     d_ptr->block(block);
     d_ptr->dataid() = new uint[block];
     d_ptr->colorid() = new uint[block];
     d_ptr->verticeslen() = new uint[block];
     double2 rmm, gmm, bmm;
     double rmin, rmax, gmin, gmax, bmin, bmax;
     raster.imageMinMax(bandMap[0], rmm.x, rmm.y);
     raster.imageMinMax(bandMap[1], gmm.x, gmm.y);
     raster.imageMinMax(bandMap[2], bmm.x, bmm.y);
     initializeProgressBar(S2Q("正在读取数据..."), 0, block - 1);
     for(int yb = 0; yb < yblock; ++yb)
     {
          int ybuf = yb == yblock - 1 ? h - yb * 640 : 640;
          for(int xb = 0; xb < xblock; ++xb)
          {
               int xbuf = xb == xblock - 1 ? w - xb * 640 : 640;
               SYPImageData data; int bk = yb * xblock + xb;
               setProgressBarPosition(bk);
               data.datatype = raster.imageDataType();
               float2 *verticespos = new float2[xbuf * ybuf];
               float3 *verticescolor = new float3[xbuf * ybuf];
               data.xpos = xb == 0 ? 0 : xb * 640 - xb;
               data.ypos = yb == 0 ? 0 : yb * 640 - yb;
               data.col = xbuf; data.row = ybuf; data.band = 3;
               data.allocator();
               raster.readImage(data, bandMap, BSQ);
               createRenderData(data, xbuf, ybuf, xb, yb, verticespos, verticescolor, rmm, gmm, bmm);
               beginRenderData(bk, xbuf * ybuf, verticespos, verticescolor);
               delete verticescolor; delete verticespos;   
          }
     }
     restoreProgressBar();
}

这里面需要解释的应该就是createRenderData和beginRenderData两个接口。
void GeoGraphicsView::createRenderData(const SYPImageData &data, int XBuf, int YBuf, int XBlock, int YBlock, float2 *VPos, float3 *VColor, const float2 &rmm, const float2 &gmm, const float2 &bmm)
{
     double rval, gval, bval; int bs = XBuf * YBuf;
     for(int row = 0; row < YBuf; ++row)
     {
        for(int col = 0; col < XBuf; ++col)
        {
               int index = row * XBuf + col;
               VPos[index].x = XBlock * 640 + col - d_ptr->width() / 2;
               VPos[index].y = YBlock * 640 + row - d_ptr->height() / 2;
               switch(data.datatype)
               {
                   case 1: valueFromPointer(((byte *)data.imageData()), index, bs, rval, gval, bval); break;
                   case 2: valueFromPointer(((ushort *)data.imageData()), index, bs, rval, gval, bval); break;
                   case 3: valueFromPointer(((short *)data.imageData()), index, bs, rval, gval, bval); break;
                   case 4: valueFromPointer(((ulong *)data.imageData()), index, bs, rval, gval, bval); break;
                   case 5: valueFromPointer(((long *)data.imageData()), index, bs, rval, gval, bval); break;
                   case 6: valueFromPointer(((float *)data.imageData()), index, bs, rval, gval, bval); break;
                   case 7: valueFromPointer(((double *)data.imageData()), index, bs, rval, gval, bval); break;
                   default: break;
              }
              rval = rval < rmm.x ? rmm.x : rval > rmm.y ? rmm.y : rval;
              VColor[index].x = double(rval - rmm.x) / (double)(rmm.y - rmm.x);
              gval = gval < gmm.x ? gmm.x : gval > gmm.y ? gmm.y : gval;
              VColor[index].y = double(gval - gmm.x) / (double)(gmm.y - gmm.x);
              bval = bval < bmm.x ? bmm.x : bval > bmm.y ? bmm.y : bval;
              VColor[index].z = double(bval - bmm.x) / (double)(bmm.y - bmm.x);
           }
       }
}
void GeoGraphicsView::beginRenderData(int bk, int bs, const float2 *VPos, const float3 *VColor)
{
     d_ptr->verticeslen()[bk] = bs; int len;

     glEnableVertexAttribArray(d_ptr->colAttr());
     glEnableVertexAttribArray(d_ptr->posAttr());

     glGenBuffers(1, &d_ptr->dataid()[bk]);
     glVertexAttribPointer(d_ptr->colAttr(), 3, GL_FLOAT, GL_FALSE, sizeof(float3), 0);
     glBindBuffer(GL_ARRAY_BUFFER, d_ptr->dataid()[bk]);
     len = bs * sizeof(float2);
     glBufferData(GL_ARRAY_BUFFER, len, VPos, GL_STATIC_DRAW);

     glGenBuffers(1, &d_ptr->colorid()[bk]);
     glVertexAttribPointer(d_ptr->posAttr(), 2, GL_FLOAT, GL_FALSE, sizeof(float2), 0);
     glBindBuffer(GL_ARRAY_BUFFER, d_ptr->colorid()[bk]);
     len = bs * sizeof(float3);
     glBufferData(GL_ARRAY_BUFFER, len, VColor, GL_STATIC_DRAW);

     glDrawArrays(GL_POINTS, 0, bs);
     glBindBuffer(GL_ARRAY_BUFFER, 0);
     glBindBuffer(GL_ARRAY_BUFFER, 0);
     glDisableVertexAttribArray(d_ptr->posAttr());
     glDisableVertexAttribArray(d_ptr->colAttr());
}

到此影像显示结束。

下面是显示效果:

放大之后的效果:

放大之后显示可以看出是一个一个小方块,实际上这并不是小方块,而是我们看到的错觉。再放大之后我们可以看到其实是一个个的点。

到此整个显示完成,后续将优化显示效果。

资源下载地址:https://download.csdn.net/download/yangfahe1/10781676

目前支持小数据分块显示,后续将继续更新大数据显示

猜你喜欢

转载自blog.csdn.net/yangfahe1/article/details/83958933