对于影像文件的读取,借鉴了已有的资料,网址忘了,侵权告知即删
耗时3天,直接提供我测试的代码吧,
头文件内容
#ifndef SHOWBIGPICWIDGET_H
#define SHOWBIGPICWIDGET_H
#include <QWidget>
#include <QLabel>
#include<gdal_priv.h>
class ShowBigPicWidget : public QWidget
{
Q_OBJECT
public:
explicit ShowBigPicWidget(QWidget *parent = nullptr);
~ShowBigPicWidget();
private:
//读取图像
void readImage(QString filePath);
//
void sectionHandle();
//波段处理
void ShowBand( GDALRasterBand* band );
//显示图片
void ShowImg( QList<GDALRasterBand*>*imgBand );
unsigned char* ImgSketch( float* buffer ,GDALRasterBand* currentBand, int size, double noValue );
//根据图像的全部宽高,去获取当前显示时读取的图像宽高和缩放宽高
void scaleShow(int &imgX,int &imgY, int &imgW,int &imgH,int &scaleW,int &scaleH);
/**
* @auther 贺华
* @date 2024/09/04
* @brief 图像显示,首次显示,默认全部显示 即 显示全景
*/
void picShowNormal();
//针对放大、缩小功能,实际就是计算下列的控制值的过程
/**
* @auther 贺华
* @date 2024/09/05
* @brief 放大
*/
void magnification();
/**
* @auther 贺华
* @date 2024/09/05
* @brief 缩小
*/
void reduce();
private:
QString m_filePath = "";
//文件读取对象
GDALDataset *poDataset = nullptr;
//图像的全部像素宽高
int m_totalW = 0;
int m_totalH = 0;
//绘制的图像
QLabel *m_picLabel = nullptr;
/// <summary>
/// 判断是否显示RGB彩色图像
/// </summary>
bool m_showColor;
//缩放因子 默认1.0 大于1.0为放大 小于1.0为缩小 最多保留两位小数
//按照
float m_scaleFac = 1.0;
/**
* @auther 贺华
* @date 2024/09/04
* @brief 下列字段用来控制获取当前显示图像,移动和缩放都会更新相应值
*/
//左上角 定位获取图像数据的像素点
int m_locationX = 0;
int m_locationY = 0;
//获取图像的实际像素的长和宽
int m_realW = 0;
int m_realH = 0;
//显示在label上的长和宽,一般来说都是label的长宽
int m_showW = 0;
int m_showH = 0;
//鼠标按下的坐标
QPoint lastEventCursorPos;
protected:
void wheelEvent( QWheelEvent *event );
void mousePressEvent( QMouseEvent *event );
void mouseMoveEvent( QMouseEvent *event );
void mouseReleaseEvent( QMouseEvent *event);
};
#endif // SHOWBIGPICWIDGET_H
cpp文件
#include "ShowBigPicWidget.h"
#include "ui_ShowBigPicDialog.h"
#include <QFile>
#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QMouseEvent>
#include <qglobal.h>
#include "MapCanvas.h"
ShowBigPicWidget::ShowBigPicWidget(QWidget *parent) :
QWidget(parent)
{
this->setAttribute(Qt::WA_DeleteOnClose);
QHBoxLayout *lay = new QHBoxLayout();
m_picLabel = new QLabel(this);
m_picLabel->setFixedSize(600,500);
m_picLabel->setAlignment(Qt::AlignCenter);
lay->addWidget(m_picLabel);
lay->setContentsMargins(0,0,0,0);
this->setLayout(lay);
m_filePath = "C:/Users/dev36/Desktop/HH/Demos/dllConsole/GF2_PMS2_E85.0_N44.6_20160527_L1A0001606970/GF2_PMS2_E85.0_N44.6_20160527_L1A0001606970-PAN2.tiff";
this->readImage(m_filePath);
}
ShowBigPicWidget::~ShowBigPicWidget()
{
qDebug()<<"~~delete ShowBigPicWidget";
if(poDataset){
GDALClose( poDataset );
poDataset = nullptr;
GDALDestroyDriverManager();
}
}
void ShowBigPicWidget::readImage(QString filePath)
{
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO" );
//设置缓存64M
// GDALSetCacheMax(64);
poDataset = ( GDALDataset* )GDALOpen(filePath.toStdString().c_str(), GA_ReadOnly );
if ( poDataset == NULL )
{
qDebug()<<tr( "Can not open file %1" ).arg( filePath );
return;
}
// ShowFileList( filePath );
// ShowImgInfor( filePath );
int imgWidth = poDataset->GetRasterBand(1)->GetXSize();
int imgHeight = poDataset->GetRasterBand(1)->GetYSize();
m_totalH = imgHeight;
m_totalW = imgWidth;
//解决大图像移动、缩放 很慢的问题 会在图像的相同目录下新建ovr文件
if(poDataset->GetRasterBand(1)->GetOverviewCount() == 0) {
//金字塔信息会缓存 如果已有金字塔信息就不需要再创建
int iPixelNum = imgHeight * imgWidth; //图像中的总像元个数
int iTopNum = 4096; //顶层金字塔大小,64*64
int iCurNum = iPixelNum / 4;
int anLevels[1024] = { 0 };
int nLevelCount = 0; //金字塔级数
do //计算金字塔级数,从第二级到顶层
{
anLevels[nLevelCount] = static_cast<int>(pow(2.0, nLevelCount+2));
nLevelCount ++;
iCurNum /= 4;
} while (iCurNum > iTopNum);
const char *pszResampling = "nearest"; //采样方式
/* -------------------------------------------------------------------- */
/* Generate overviews. */
/* -------------------------------------------------------------------- */
CPLErr err = GDALBuildOverviews( poDataset,pszResampling, nLevelCount, anLevels,
0, NULL, NULL, NULL );
}
this->picShowNormal();
this->sectionHandle();
}
void ShowBigPicWidget::sectionHandle()
{
// 如果图像文件并非三个波段,则默认只显示第一波段灰度图像
if ( poDataset->GetRasterCount() != 3 )
{
m_showColor = false;
ShowBand( poDataset->GetRasterBand(1) );
}
// 如果图像正好三个波段,则默认以RGB的顺序显示彩色图
else
{
m_showColor = true;
QList<GDALRasterBand*> bandList;
bandList.append(poDataset->GetRasterBand( 1 ) );
bandList.append(poDataset->GetRasterBand( 2 ) );
bandList.append(poDataset->GetRasterBand( 3 ) );
ShowImg( &bandList );
}
}
void ShowBigPicWidget::ShowBand(GDALRasterBand *band)
{
if ( band == NULL )
{
return;
}
QList<GDALRasterBand*> myBand;
myBand.append( band );
myBand.append( band );
myBand.append( band );
ShowImg( &myBand );
}
void ShowBigPicWidget::ShowImg(QList<GDALRasterBand *> *imgBand)
{
if ( imgBand->size() != 3 )
{
return;
}
int imgWidth = m_realW;
int imgHeight = m_realH;
int iScaleWidth = m_showW;
int iScaleHeight = m_showH;
int x = m_locationX;
int y = m_locationY;
// this->scaleShow(x,y,imgWidth,imgHeight,iScaleWidth,iScaleHeight);
// GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();
// 首先分别读取RGB三个波段
float* rBand = new float[iScaleWidth *iScaleHeight];
float* gBand = new float[iScaleWidth *iScaleHeight];
float* bBand = new float[iScaleWidth *iScaleHeight];
unsigned char *rBandUC, *gBandUC, *bBandUC;
// 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取
if ( m_showColor == true )
{
imgBand->at( 0 )->RasterIO(GF_Read, x, y, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 1 )->RasterIO(GF_Read, x, y, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 2 )->RasterIO(GF_Read, x, y, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
// 分别拉伸每个波段并将Float转换为unsigned char
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );
bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );
}
else
{
imgBand->at( 0 )->RasterIO(GF_Read, x, y, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = rBandUC;
bBandUC = rBandUC;
}
// 将三个波段组合起来
int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;
unsigned char* allBandUC = new unsigned char[bytePerLine * iScaleHeight * 3];//可能不需要乘3
for( int h = 0; h < iScaleHeight; h++ )
{
for( int w = 0; w < iScaleWidth; w++)
{
allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];
}
}
//显示图片
m_picLabel->setPixmap(QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888)));
//释放内存
delete [] rBand;
delete [] gBand;
delete [] bBand;
if ( m_showColor == true ){
delete []rBandUC;
delete []gBandUC;
delete []bBandUC;
}else{
delete []rBandUC;
}
delete [] allBandUC;
//还是不清除缓存了,清除之后遇到大文件缩放、移动
// if(poDataset){
// //清除框架内部缓存,否则大图移动缩放,内存会一直上涨
// GDALFlushCache(poDataset);
// }
}
///<summary>
/// 图像线性拉伸
///</summary>
///<param name="buffer">图像缓存</param>
///<param name="currentBand">当前波段</param>
///<param name="size">The size.</param>
///<param name="noValue">图像中的异常值</param>
///<returns>经过拉伸的8位图像缓存</returns>
unsigned char *ShowBigPicWidget::ImgSketch(float *buffer, GDALRasterBand *currentBand, int bandSize, double noValue)
{
unsigned char* resBuffer = new unsigned char[bandSize];
double max, min;
double minmax[2];
currentBand->ComputeRasterMinMax( 1,minmax );
min = minmax[0];
max = minmax[1];
if( min <= noValue && noValue<= max )
{
min = 0;
}
for ( int i = 0; i < bandSize; i++ )
{
if ( buffer[i] > max )
{
resBuffer[i] = 255;
}
else if ( buffer[i] <= max&& buffer[i] >= min )
{
resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );
}
else
{
resBuffer[i] = 0;
}
}
return resBuffer;
}
//根据图像的全部宽高,去获取当前显示时读取的图像宽高和缩放宽高
void ShowBigPicWidget::scaleShow(int &imgX,int &imgY,int &imgW, int &imgH, int &scaleW, int &scaleH)
{
int labelW = this->width();
int labelH = this->height();
scaleW = labelW;
scaleH = labelH;
imgX = m_locationX;
imgY = m_locationY;
imgW = labelW;
imgH = labelH;
if(imgX + imgW > m_totalW && imgX == 0){
imgW = m_totalW;
scaleW = m_totalW;
}
if(imgY + imgH > m_totalH && imgY == 0){
imgH = m_totalH;
scaleH = m_totalH;
}
}
/**
* @auther 贺华
* @date 2024/09/04
* @brief 图像显示,首次显示,默认全部显示,
*/
void ShowBigPicWidget::picShowNormal()
{
m_locationX = 0;
m_locationY = 0;
m_realW = m_totalW;
m_realH = m_totalH;
QSize labelSize = m_picLabel->size();
if(labelSize.width() >= m_totalW){
if(labelSize.height() >= m_totalH){
//这种情况就要不改变分辨率的情况下显示
m_showW = m_totalW;
m_showH = m_totalH;
}else{
m_showW = m_totalH*labelSize.width()/m_totalW;
m_showH = m_totalH;
}
}else{
if(labelSize.height() >= m_totalH){
m_showW = m_totalW;
m_showH = m_totalW*labelSize.height()/m_totalH;
}else{
//需要看宽度、高度占比更高
if(labelSize.width()/labelSize.height() >= m_totalW/m_totalH){
//高度占比更高
m_showH = labelSize.height();
m_showW = labelSize.height()*m_totalW/m_totalH;
}else{
//宽度占比更高
m_showW = labelSize.width();
m_showH = labelSize.width()*m_totalH/m_totalW;
}
}
}
}
/**
* @auther 贺华
* @date 2024/09/05
* @brief 放大
*/
void ShowBigPicWidget::magnification()
{
if(m_realH <= 2 || m_realW <= 2){
//如果实际显示像素低于2个像素,不支持继续放大
return;
}
if(m_locationX > 0){
if(m_locationY > 0){
//图像的显示肯定是已经超过了当前的视图,那么继续放大 当前宽高的0.05
m_locationX = m_locationX + (((int)m_realW*0.05)/2);
m_locationY = m_locationY + (((int)m_realH*0.05)/2);
m_realW = m_realW*0.95;
m_realH = m_realH*0.95;
}else{
//这种情况需要计算,判断放大后会不会超过视图高度
if(m_showH * 1.05 <= m_picLabel->size().height()){
//等于的话
m_locationX = m_locationX + (((int)m_realW*0.05)/2);
m_locationY = 0;
m_realW = m_realW*0.95;
if(m_showH * 1.05 < m_picLabel->size().height()){
m_showH = m_showH*1.05;
}else{
m_showH = m_picLabel->size().height();
}
}else{
//宽度肯定是大于了
m_locationX = m_locationX + (((int)m_realW*0.05)/2);
m_locationY = ((int)m_realH*0.05)/2;
m_realW = m_realW*0.95;
m_realH = m_realH*0.95;
m_showH = m_picLabel->size().height();
}
}
}else{
if(m_locationY > 0){
//这种情况需要计算,判断放大后会不会超过视图宽度
if(m_showW * 1.05 <= m_picLabel->size().width()){
//等于的话
m_locationY = m_locationY + (((int)m_realH*0.05)/2);
m_locationX = 0;
m_realH = m_realH*0.95;
if(m_showW * 1.05 < m_picLabel->size().width()){
m_showW = m_showW*1.05;
}else{
m_showW = m_picLabel->size().width();
}
}else{
//宽度肯定是大于了
m_locationY = m_locationY + (((int)m_realH*0.05)/2);
m_locationX = ((int)m_realW*0.05)/2;
m_realH = m_realH*0.95;
m_realW = m_realW*0.95;
m_showW = m_picLabel->size().width();
}
}else{
//那不得判断放大后是否都超过了视图区域?
bool xOut = m_showW * 1.05 > m_picLabel->size().width();
bool yOut = m_showH * 1.05 > m_picLabel->size().height();
if(xOut){
if(yOut){
//都超出了
m_locationX = ((int)m_realW*0.05)/2;
m_locationY = ((int)m_realH*0.05)/2;
m_showH = m_picLabel->size().height();
m_showW = m_picLabel->size().width();
m_realW = m_realW*1.05 - ((m_showW*1.05-m_picLabel->size().width())*m_realW/m_showW);
m_realH = m_realH*1.05 - ((m_showH*1.05-m_picLabel->size().height())*m_realH/m_showH);
}else{
//宽度超出
m_locationY = 0;
m_locationX = ((int)m_realW*0.05)/2;
m_showH = m_showH*1.05;
m_showW = m_picLabel->size().width();
m_realH = m_totalH;
m_realW = m_totalW-(2*m_locationX);
}
}else{
if(yOut){
//高度超出
m_locationX = 0;
m_locationY = ((int)m_realH*0.05)/2;
m_showW = m_showW*1.05;
m_showH = m_picLabel->size().height();
m_realW = m_totalW;
m_realH = m_totalH-(2*m_locationY);
}else{
//都未超出
m_locationX = 0;
m_locationY = 0;
//需要向上取整,否则可能放大不了了
m_showH = ceil(m_showH*1.05);
m_showW = ceil(m_showW*1.05);
m_realW = m_totalW;
m_realH = m_totalH;
}
}
}
}
if(m_locationX < 0){
m_locationX = 0;
}
if(m_locationY < 0){
m_locationY = 0;
}
this->sectionHandle();
// this->readImage(m_filePath);
}
/**
* @auther 贺华
* @date 2024/09/05
* @brief 缩小 注意*******缩小还要考虑在边缘时缩小
*/
void ShowBigPicWidget::reduce()
{
if(m_showH <= 5 || m_showW <= 5){
//如果显示区域低于5像素,不支持缩小
return;
}
//先判断缩小之后需要显示的内容是否已经超过了图像的最大尺寸
bool xAll = m_realW*1.05 >= m_totalW;
bool yAll = m_realH*1.05 >= m_totalH;
if(xAll){
if(yAll){
//都超过了最大尺寸 宽高都显示全部内容
if(m_realW < m_totalW){
if(m_realH < m_totalH){
//需要看宽度、高度占比更高
if(m_picLabel->size().width()/m_picLabel->size().height() >= m_totalW/m_totalH){
//高度占比更高
m_showH = m_picLabel->size().height();
m_showW = m_picLabel->size().height()*m_totalW/m_totalH;
}else{
//宽度占比更高
m_showW = m_picLabel->size().width();
m_showH = m_picLabel->size().width()*m_totalH/m_totalW;
}
}else{
//说明高度在未缩小之前就已全部显示
m_showH = m_showH*0.95;
m_showW = m_showH*m_totalW/m_totalH;
}
}else{
if(m_realH < m_totalH){
//说明宽度在未缩小之前就已全部显示
m_showW = m_showW*0.95;
m_showH = m_showW*m_totalH/m_totalW;
}else{
m_showW = m_showW*0.95;
m_showH = m_showH*0.95;
}
}
m_locationX = 0;
m_locationY = 0;
m_realW = m_totalW;
m_realH = m_totalH;
}else{
//在缩小后宽度全部显示,而高度显示部分
m_locationY = m_locationY-(m_realH*0.05/2);
if(m_locationY < 0){
m_locationY = 0;
}else if(m_locationY + (m_realH *1.05) > m_totalH){
m_locationY = m_totalH - (m_realH *1.05);
}
if(m_realW < m_totalW){
//宽度在缩小之前未全部显示 根据比例计算
m_showW = m_showH*m_totalW/(m_realH*1.05);
}else{
//宽度在缩小之前已全部显示
m_showW = m_showW*0.95;
}
m_locationX = 0;
m_realW = m_totalW;
m_realH = m_realH*1.05;
m_showH = m_showH * 1;
}
}else{
if(yAll){
//在缩小后高度全部显示,而宽度显示部分
m_locationX = m_locationX-(m_realW*0.05/2);
if(m_locationX < 0){
m_locationX = 0;
}else if(m_locationX + (m_realW *1.05) > m_totalW){
m_locationX = m_totalW - (m_realW *1.05);
}
if(m_realH < m_totalH){
//高度在缩小之前未全部显示 根据比例计算
m_showH = m_showW*m_totalH/(m_realW*1.05);
}else{
//高度在缩小之前已全部显示
m_showH = m_showH*0.95;
}
m_locationY = 0;
m_realH = m_totalH;
m_realW = m_realW*1.05;
m_showW = m_showW * 1;
}else{
//这种情况就是缩小之后 视图显示宽高都不是全部的 //需要向上取整,否则可能缩小不了
m_locationX = m_locationX-(ceil(m_realW*0.05)/2);
if(m_locationX < 0){
m_locationX = 0;
}else if(m_locationX + (ceil(m_realW *1.05)) > m_totalW){
m_locationX = m_totalW - (ceil(m_realW *1.05));
}
m_locationY = m_locationY-(ceil(m_realH *0.05)/2);
if(m_locationY < 0){
m_locationY = 0;
}else if(m_locationY + ceil(m_realH *1.05) > m_totalH){
m_locationY = m_totalH - ceil(m_realH *1.05);
}
m_realW = ceil(m_realW*1.05);
m_realH = ceil(m_realH*1.05);
m_showW = m_showW*1;
m_showH = m_showH*1;
}
}
this->sectionHandle();
// this->readImage(m_filePath);
}
/**
* @auther 贺华
* @date 2024/09/05
* @brief 图像的放大缩小功能 0.05的当前显示区域变化
*/
void ShowBigPicWidget::wheelEvent(QWheelEvent *event)
{
//缩放
if(event->angleDelta().y() > 0){
//放大
this->magnification();
}else{
//缩小
this->reduce();
}
}
void ShowBigPicWidget::mousePressEvent(QMouseEvent *event)
{
if ( event->button() == Qt::LeftButton ){
lastEventCursorPos = event->pos();
}
}
void ShowBigPicWidget::mouseMoveEvent(QMouseEvent *event)
{
QPoint p = event->pos();
int xPadding = p.x()-lastEventCursorPos.x();
int yPadding = p.y()-lastEventCursorPos.y();
//获取移动比例,在不同尺寸下,移动的距离跟随变化,更加流程
xPadding = xPadding*m_realW/m_picLabel->size().width();
yPadding = yPadding*m_realW/m_picLabel->size().width();
//计算什么时候是不做处理的?
if(m_locationX == 0 && m_locationY == 0 && xPadding > 0 && yPadding > 0){
//第一种 已经到了左上角,不能继续滑动
return;
}
if(m_locationX + m_realW >= m_totalW && m_locationY+m_realH >= m_totalH
&& xPadding < 0 && yPadding < 0){
//第二种 已经到了右下角,不能继续滑动
return;
}
m_locationX = m_locationX-xPadding;
m_locationY = m_locationY-yPadding;
//确保不会超出边界
if(m_locationX + m_realW > m_totalW){
m_locationX = m_totalW-m_realW;
}
if(m_locationY + m_realH > m_totalH){
m_locationY = m_totalH-m_realH;
}
if(m_locationX < 0){
m_locationX = 0;
}
if(m_locationY < 0){
m_locationY = 0;
}
this->sectionHandle();
// this->readImage(m_filePath);
lastEventCursorPos = event->pos();
}
void ShowBigPicWidget::mouseReleaseEvent(QMouseEvent *event)
{
}
感觉对于放大、缩小写的还是有点复杂,但是效果还好
使用金字塔的API解决了图像文件过大操作卡死的现象