C++搭建框架,利用OpenGL、GDAL、Qt進行分塊顯示遙感影像
主要是利用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
目前支援小資料分塊顯示,後續將繼續更新大資料顯示