QT中使用GDAL多线程读取遥感图像到QImage

GDAL 是一个很强大的可以读取很多格式 的带有GIS信息的栅格型图像。前阵子项目中需要读取遥感图像,并显示到QT界面,由于遥感图像一般很大,所以采取新开一个读图线程的方式来读取,防止界面假死。下面是代码共享,测试通过读取500MB的24000*24000像素GeoTiff图并在QT的QGraphicsView中显示。

环境:VS2005+SP1, Qt 4.6.0, GDAL 1.6.2

文件:commontoolfunctions.h, gdalimagereaderthread.h, gdalimagereaderthread.cpp

1. commontoolfunctions.h文件:

/* * 工具函数:网上找来的根据两点的经纬度坐标计算两个点的大地距离。 */ 
#ifndef COMMONTOOLFUNCTIONS_H  
#define COMMONTOOLFUNCTIONS_H  
#include <math.h>  
#define EARTH_RADIUS  6378.137  
#define MY_PI 3.14159265358979  
#define LB_CONVERT_TO_RAD(d) (d * MY_PI / 180.0)  
/* * 输入经纬度坐标,单位'度'。 * 返回值单位为公里。 * code from google maps. 近似值. */ 
double CalcDistanceByLB(double lat1, double lng1, double lat2, double lng2)  
{  double radLat1 = LB_CONVERT_TO_RAD(lat1);  double radLat2 = LB_CONVERT_TO_RAD(lat2);  double a = radLat1 - radLat2;  double b = LB_CONVERT_TO_RAD(lng1) - LB_CONVERT_TO_RAD(lng2);  double s = 2 * asin(sqrt(pow(sin(a/2),2) + cos(radLat1)*cos(radLat2)*pow(sin(b/2),2)));  s = s * EARTH_RADIUS;  return s;  
}  
#endif 

2. gdalimagereaderthread.h文件

/* * QT线程类:使用GDAL读取图像文件并转为QImage的线程。 * 创建:MulinB * 日期:2010-1-8 * 上次修改: 2010-06-20 * HUST-IPRAI-CVL */ 
#ifndef GDALIMAGEREADERTHREAD_H  
#define GDALIMAGEREADERTHREAD_H  #include <QThread>  
#include <gdal_priv.h> //for GDAL  class QImage;  
class RSI_MapCanvas;  class RSI_ImageReaderThread : public QThread  
{  Q_OBJECT  public:  RSI_ImageReaderThread(RSI_MapCanvas* pCanvas);  ~RSI_ImageReaderThread();  public:  void ReadFile(const QString& imageFileName); //使用该函数读取文件  protected:  void run(); //线程函数  private:  RSI_MapCanvas*  m_pCanvas;  QString         m_imageFileName;  private:  //GDAL读取图像文件时的临时变量  GDALRasterBand* m_pBand_GDAL_;  int             m_nXSize_GDAL_;  int             m_nYSize_GDAL_;  void*           m_pDataBufStart_GDAL_;  int             m_nBufXSize_GDAL_;  int             m_nBufYSize_GDAL_;  GDALDataType    m_eBufDataType_GDAL_;  int             m_nBufPixSpace_GDAL_;  int             m_nBufLineSpace_GDAL_;  private:  enum IMG_OPEN_DRIVER_T  {  RSI_IMG_DRIVER_UNDEF = 0, //未定义的打开方式  RSI_USE_OPENCV, //使用opencv  RSI_USE_GDAL, //使用GDAL  };  private:  //将图像宽度字节数32位对齐  inline int Get32bitAlignedWidthBytes(int nImgPixWidth, int nBytesPerPix);  //GDAL buffer 转 QImage  QImage*  CvtGDALBufferToGrayQImage(uchar* &pafScanblock, int nImgSizeX, int nImgSizeY, GDALDataType pixType);  //将非8位的灰度图转换为8位灰度图  template <class T>  void CvtImgDataTo8bitGrayImg(T* pOrigData, int nImgSizeX, int nImgSizeXAligned32bit, int nImgSizeY, uchar* pNew8bitImgBuf, int nNewImgSizeXAligned32bit, T maxVal, T minVal);  
};  #endif // GDALIMAGEREADERTHREAD_H

3. gdalimagereaderthread.cpp文件

/* * QT线程类:使用GDAL读取图像文件并转为QImage的线程。 * 创建:MulinB * 日期:2010-1-8 * 上次修改: 2010-06-20 * TODO: 1.图像分辨率转换计算貌似有问题... 2.代码比较乱,没时间整理...囧... * HUST-IPRAI-CVL */ 
#include "GDALImageReaderThread.h"  
#include "iplimagetoqimage.h" //for IplImage converting to QImage  
#include "commontoolfunctions.h" //for calculate distance by LB  
#include <cv.h> //for OpenCV  
#include <highgui.h>  //for OpenCV  #include <limits.h> //for INT_MAX, INT_MIN, ...  
#include <float.h> //for FLT_MAX, FLT_MIN, ...  QMutex G_imageReadMutex;  
QWaitCondition G_imageReadComplete;  RSI_ImageReaderThread::RSI_ImageReaderThread(RSI_MapCanvas* pCanvas)  : m_pCanvas(pCanvas)  
{  }  RSI_ImageReaderThread::~RSI_ImageReaderThread()  
{  }  //线程函数  
void RSI_ImageReaderThread::run()  
{  //这里只将GDAL最费时的RasterIO()函数作为新线程  /* GDALRasterBand::RasterIO() parameters: eRWFlag  Either GF_Read to read a region of data, or GT_Write to write a region of data.  nXOff  The pixel offset to the top left corner of the region of the band to be accessed. This would be zero to start from the left side.  nYOff  The line offset to the top left corner of the region of the band to be accessed. This would be zero to start from the top.  nXSize  The width of the region of the band to be accessed in pixels.  nYSize  The height of the region of the band to be accessed in lines.  pData  The buffer into which the data should be read, or from which it should be written. This buffer must contain at least nBufXSize * nBufYSize words of type eBufType. It is organized in left to right, top to bottom pixel order. Spacing is controlled by the nPixelSpace, and nLineSpace parameters.  nBufXSize  the width of the buffer image into which the desired region is to be read, or from which it is to be written.  nBufYSize  the height of the buffer image into which the desired region is to be read, or from which it is to be written.  eBufType  the type of the pixel values in the pData data buffer. The pixel values will automatically be translated to/from the GDALRasterBand data type as needed.  nPixelSpace  The byte offset from the start of one pixel value in pData to the start of the next pixel value within a scanline. If defaulted (0) the size of the datatype eBufType is used.  nLineSpace  The byte offset from the start of one scanline in pData to the start of the next. If defaulted the size of the datatype eBufType * nBufXSize is used.  */ G_imageReadMutex.lock();  m_pBand_GDAL_->RasterIO(GF_Read, 0, 0,  m_nXSize_GDAL_, m_nYSize_GDAL_,   m_pDataBufStart_GDAL_,   m_nBufXSize_GDAL_, m_nBufYSize_GDAL_,   m_eBufDataType_GDAL_,   m_nBufPixSpace_GDAL_,  m_nBufLineSpace_GDAL_);  //G_imageReadComplete.wakeAll();  G_imageReadMutex.unlock();  
}  //读图函数:使用GDAL和OpenCV读取图像文件  
void RSI_ImageReaderThread::ReadFile(const QString& imageFileName)  
{  m_imageFileName = imageFileName;  if (m_imageFileName.isEmpty() || m_pCanvas == NULL)  return;  //选择打开图像的驱动方式: GDAL or OpenCV  const QString strOpenCV = tr("OpenCV");  const QString strGDAL = tr("GDAL");  IMG_OPEN_DRIVER_T  eOpenDriverType;  QStringList items; //for input dialog  items << strOpenCV << strGDAL;  int  curDriverItemIdx = 0;  //先根据扩展名推荐打开方式  QString fileExtName = m_imageFileName.right(3).toLower();  if (fileExtName == QString("bmp")   || fileExtName == QString("png")  || fileExtName == QString("jpg")  || fileExtName == QString("peg")  || fileExtName == QString("jpe")  || fileExtName == QString("ppm")  || fileExtName == QString("pbm")  || fileExtName == QString("pgm"))  {  //如果是常用格式,推荐使用OpenCV打开: (*.bmp *.png *.jpg *.jpeg *.jpe *.ppm *.pgm *.pbm)  eOpenDriverType = RSI_USE_OPENCV; //根据扩展名来推荐使用哪种打开方式  curDriverItemIdx = 0;  }  else {  eOpenDriverType = RSI_USE_GDAL;  curDriverItemIdx = 1;  }  //弹出对话框让用户选择使用哪种驱动方式打开图像  bool dlgOk;  QString itemSel = QInputDialog::getItem(m_pCanvas, tr("Please select..."),  tr("Driver for image opener:"), items, curDriverItemIdx, false, &dlgOk);  if (dlgOk && !itemSel.isEmpty())  {  if (itemSel == strOpenCV)  {  eOpenDriverType = RSI_USE_OPENCV;  }  else if (itemSel == strGDAL)  {  eOpenDriverType = RSI_USE_GDAL;  }  }  else {  return;  }  //进度条  int progressBarValue = 2;  QProgressDialog progress(tr("Reading image file..."), tr("Abort"), 0, 100, m_pCanvas);  QProgressBar prgBar(&progress);  prgBar.setTextVisible(false); //不显示百分比  progress.setBar(&prgBar);  progress.setWindowModality(Qt::ApplicationModal);  progress.setCancelButton(NULL);  progress.setWindowTitle(tr("Please wait..."));  // 标题  progress.setValue(progressBarValue);  //use GDAL and OpenCV open image files  if (eOpenDriverType == RSI_USE_OPENCV)  {  //打开图像文件  IplImage* pIplImg = NULL; //声明IplImage指针  if ((pIplImg = cvLoadImage(m_imageFileName.toStdString().c_str(), 1)) == 0) //使用OpenCV highgui打开图像文件  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed!"), tr("OK"));  return;  }  //读取图像信息  QString rsiFileInfo; //图像信息  QString tempStr;  tempStr = tr("Size is %1 x %2 x [%3(bits) x %4(channels)]/n")  .arg(pIplImg->width)  .arg(pIplImg->height)  .arg(pIplImg->depth)  .arg(pIplImg->nChannels);  rsiFileInfo += tempStr;  //显示  uchar* imageDataBuffer = NULL;  QImage* pQImage = IplImageToQImage(pIplImg, &imageDataBuffer); //转换成QImage  //创建图层  RSI_ImageLayer* pImageLayer = new RSI_ImageLayer(*pQImage);  pImageLayer->setLayerName(m_imageFileName);  pImageLayer->setImageLayerInfo(rsiFileInfo);  pImageLayer->setBufferToBeFree(imageDataBuffer); //记录到m_bufferToBeFree中,待关闭图像时释放buffer内存  pImageLayer->setVisible(false); //默认不显示  pImageLayer->setFlag(QGraphicsItem::ItemIsMovable, false);  m_pCanvas->AddImageLayer(pImageLayer);  //释放临时内存  cvReleaseImage(&pIplImg);  delete pQImage;  goto _READ_IMAGE_FILE_OVER; //读取完毕直接返回  }  else if (eOpenDriverType == RSI_USE_GDAL)  {  //如果是遥感图像格式,则使用GDAL打开  GDALAllRegister();  GDALDataset* poDataset;  //GDAL数据集  poDataset = (GDALDataset*)GDALOpen(m_imageFileName.toStdString().c_str(), GA_ReadOnly);  if( poDataset == NULL )  {  //QMessageBox::warning(NULL, tr("Failed"), tr("GDAL Open Image File Failed!"), tr("OK"));  return;  }  //读取遥感图像信息  QString rsiFileInfo; //遥感图像信息  QString tempStr;  tempStr = tr("GDAL Driver: %1/%2/n")  .arg(QString(poDataset->GetDriver()->GetDescription()))  .arg(QString(poDataset->GetDriver()->GetMetadataItem(GDAL_DMD_LONGNAME)));  rsiFileInfo += tempStr;  tempStr = tr("Size is %1 x %2 x %3(bands)/n")  .arg(poDataset->GetRasterXSize())  .arg(poDataset->GetRasterYSize())  .arg(poDataset->GetRasterCount());  rsiFileInfo += tempStr;  /* 地理参考坐标系统: The returned string defines the projection coordinate system of the image in OpenGIS WKT format. */ double adfGeoTransform[6];  QString prjRefStr(poDataset->GetProjectionRef());  if (prjRefStr.isEmpty())  {  prjRefStr = tr("none"); //如果此字段为空,那么投影方式可能是GCPs  }  else //非空时才读取仿射变换信息  {  tempStr = tr("Projection(WKT format) is '%1'/n").arg(prjRefStr);  rsiFileInfo += tempStr;  //非空时才读取仿射变换信息  /* 仿射地理变换信息: adfGeoTransform[0] // top left x  adfGeoTransform[1] // w-e pixel resolution  adfGeoTransform[2] // rotation, 0 if image is "north up"  adfGeoTransform[3] // top left y  adfGeoTransform[4] // rotation, 0 if image is "north up"  adfGeoTransform[5] // n-s pixel resolution  计算某一点的地理坐标可以如下计算: Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2) Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5) */ poDataset->GetGeoTransform( adfGeoTransform );  tempStr = tr("Origin Cords = (%1,%2)/n")  .arg(adfGeoTransform[0], 0, 'f', 6)  .arg(adfGeoTransform[3], 0, 'f', 6);  rsiFileInfo += tempStr;  tempStr = tr("Pixel Size(degrees) = (%1,%2)/n")  .arg(adfGeoTransform[1], 0, 'f', 6)  .arg(adfGeoTransform[5], 0, 'f', 6);  rsiFileInfo += tempStr;  }  //读取GCPs投影方式  QString prjGCPStr(poDataset->GetGCPProjection());  if (prjGCPStr.isEmpty())  {  prjGCPStr = tr("none");  }  else {  tempStr = tr("GCPs Prj is '%1'/n").arg(prjGCPStr);  rsiFileInfo += tempStr;  int nGCPs = poDataset->GetGCPCount(); //获得GCP控制点的个数  const GDAL_GCP* pGCPs = poDataset->GetGCPs(); //获得GCP控制点  tempStr = tr("Number of GCPs: %1/n").arg(nGCPs);  rsiFileInfo += tempStr;  //由GCPs获得仿射变换参数  GDALGCPsToGeoTransform( nGCPs, pGCPs, adfGeoTransform, TRUE );  tempStr = tr("Origin Cords = (%1,%2)/n")  .arg(adfGeoTransform[0], 0, 'f', 6)  .arg(adfGeoTransform[3], 0, 'f', 6);  rsiFileInfo += tempStr;  tempStr = tr("Pixel Size(degrees) = (%1,%2)/n")  .arg(adfGeoTransform[1], 0, 'f', 6)  .arg(adfGeoTransform[5], 0, 'f', 6);  rsiFileInfo += tempStr;  }  //根据adfGeoTransform计算每个像素代表多少米距离  double oriCordL = adfGeoTransform[0];  double oriCordB = adfGeoTransform[3];  double tempL, tempB;  GDALApplyGeoTransform(adfGeoTransform, 100, 0, &tempL, &tempB); //计算(100,0)像素处的L,B坐标  double tempDist = CalcDistanceByLB(oriCordL, oriCordB, tempL, tempB);  double xMeterPerPix = tempDist * 1000.0 / 100.0; //单位为米  GDALApplyGeoTransform(adfGeoTransform, 0, 100, &tempL, &tempB); //计算(0,100)像素处的L,B坐标  tempDist = CalcDistanceByLB(oriCordL, oriCordB, tempL, tempB);  double yMeterPerPix = tempDist * 1000.0 / 100.0; //单位为米  tempStr = tr("Pixel Size(meters) = (%1,%2)/n")  .arg(xMeterPerPix, 0, 'f', 6)  .arg(yMeterPerPix, 0, 'f', 6);  rsiFileInfo += tempStr;  //读取图像大小  int nBandCount = poDataset->GetRasterCount();  int nImgSizeX = poDataset->GetRasterXSize();  int nImgSizeY = poDataset->GetRasterYSize();  //读取图像数据点类型  GDALRasterBand* preBand = poDataset->GetRasterBand(1); //预读取遥感的第一个波段  GDALDataType prePixType = preBand->GetRasterDataType();  int prePixSize = GDALGetDataTypeSize(prePixType) / 8; //GDALGetDataTypeSize得到的是bit  if (nBandCount >= 3 && prePixSize == 1) //多于3个波段的图像给用户选择是否合并前三个波段用RGB显示  {  //QMessageBox::StandardButton userAnswer = QMessageBox::Yes;  QMessageBox::StandardButton userAnswer = QMessageBox::question(m_pCanvas,   tr("Choose image show format"),   tr("%1 bands detected!/nWill you show it in RGB color format using the first three bands?").arg(nBandCount),   QMessageBox::Yes|QMessageBox::No);  if (userAnswer == QMessageBox::Yes)  {  //--------将前三个波段组合成RGB颜色显示, 使用QImage::Format_RGB32格式显示---------  uchar* pafScanblock = (uchar*)malloc(4*(nImgSizeX)*(nImgSizeY)); //Format_RGB32每个像素4个字节: RGBA  //assert(pafScanblock != NULL);  if (pafScanblock == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER;  }  memset(pafScanblock, 0x00, 4*(nImgSizeX)*(nImgSizeY));  //读取图像  for (int nB=0; nB<nBandCount; nB++)  {  GDALRasterBand* poBand = poDataset->GetRasterBand(nB+1); //遥感的一个波段  assert(poBand != NULL);  GDALDataType pixType = poBand->GetRasterDataType();  int pixSize = GDALGetDataTypeSize(pixType) / 8; //GDALGetDataTypeSize得到的是bit  assert(pixSize == 1); //彩色图的每个通道的size应该是1个字节  //用RasterIO()读取图像数据  //poBand->RasterIO(GF_Read, 0, 0, nImgSizeX, nImgSizeY,  //  pafScanblock+nB, nImgSizeX, nImgSizeY, pixType, 4, 0);  G_imageReadMutex.lock();  m_pBand_GDAL_ = poBand;  m_nXSize_GDAL_ = nImgSizeX;  m_nYSize_GDAL_ = nImgSizeY;  m_pDataBufStart_GDAL_ = pafScanblock+nB; //注意!  m_nBufXSize_GDAL_ = nImgSizeX;  m_nBufYSize_GDAL_ = nImgSizeY;  m_eBufDataType_GDAL_ = pixType;  m_nBufPixSpace_GDAL_ = 4; //注意!  m_nBufLineSpace_GDAL_ = 0;  start(); //开始新线程  G_imageReadMutex.unlock();  while (this->isRunning())  {  progressBarValue += 8;  progress.setValue(progressBarValue % 100);  QThread::msleep(1000); //注意:这里是让主线程sleep,而不是新线程  }  //G_imageReadComplete.wait(&G_imageReadMutex);  //progressBarValue += 20;  //progress.setValue(progressBarValue % 100);  //G_imageReadMutex.unlock();  }  //组合成彩色QImage  QImage* pQImage = NULL;  pQImage = new QImage(pafScanblock, nImgSizeX, nImgSizeY, QImage::Format_RGB32); //注意:QImage: each scanline of data in the image must also be 32-bit aligned  //assert(pQImage != NULL);  if (pQImage == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  free(pafScanblock);  delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER;  }  //创建图层  RSI_ImageLayer* pImageLayer = new RSI_ImageLayer(*pQImage);  if (pImageLayer == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  free(pafScanblock);  delete pQImage;   delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER;  }  pImageLayer->setLayerName(tr("3 bands composited color image") + QString("(") + m_imageFileName + QString(")"));  pImageLayer->setImageLayerInfo(rsiFileInfo);  pImageLayer->setGeoTransform(adfGeoTransform);  pImageLayer->setResolutionX(xMeterPerPix);  pImageLayer->setResolutionY(yMeterPerPix);  pImageLayer->setBufferToBeFree(pafScanblock); //记录到m_bufferToBeFree中,待关闭图像时释放buffer内存  pImageLayer->setVisible(false); //默认不显示  pImageLayer->setFlag(QGraphicsItem::ItemIsMovable, false);  m_pCanvas->AddImageLayer(pImageLayer);  //释放临时对象  delete pQImage;   delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER; //读取完毕直接返回  }  }  //------------按照波段分别读取,每个波段显示成一个灰度图-------------  //读取图像  for (int nB=0; nB<nBandCount; nB++)  {  GDALRasterBand* poBand = poDataset->GetRasterBand(nB+1); //遥感的一个波段  assert(poBand != NULL);  GDALDataType pixType = poBand->GetRasterDataType();  int pixSize = GDALGetDataTypeSize(pixType) / 8; //GDALGetDataTypeSize得到的是bit  //注意:QImage: each scanline of data in the image must also be 32-bit aligned  int nImgSizeXAligned32bit = Get32bitAlignedWidthBytes(nImgSizeX, pixSize) / pixSize; //对齐后的像素宽度  uchar* pafScanblock = (uchar*)malloc(pixSize*(nImgSizeXAligned32bit)*(nImgSizeY));  //assert(pafScanblock != NULL);  if (pafScanblock == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER;  }  //用RasterIO()读取图像数据  //poBand->RasterIO(GF_Read, 0, 0, nImgSizeX, nImgSizeY,  //  pafScanblock, nImgSizeXAligned32bit, nImgSizeY, pixType, 0, 0);  G_imageReadMutex.lock();  m_pBand_GDAL_ = poBand;  m_nXSize_GDAL_ = nImgSizeX;  m_nYSize_GDAL_ = nImgSizeY;  m_pDataBufStart_GDAL_ = pafScanblock;  m_nBufXSize_GDAL_ = nImgSizeXAligned32bit;  m_nBufYSize_GDAL_ = nImgSizeY;  m_eBufDataType_GDAL_ = pixType;  m_nBufPixSpace_GDAL_ = 0;  m_nBufLineSpace_GDAL_ = 0;  start(); //开始新线程  G_imageReadMutex.unlock();  while (this->isRunning())  {  progressBarValue += 8;  progress.setValue(progressBarValue % 100);  QThread::msleep(1000); //注意:这里是让主线程sleep,而不是新线程  }  //G_imageReadComplete.wait(&G_imageReadMutex);  //progressBarValue += 20;  //progress.setValue(progressBarValue % 100);  //G_imageReadMutex.unlock();  //转化为QImage  QImage* pQImage = CvtGDALBufferToGrayQImage(pafScanblock, nImgSizeX, nImgSizeY, pixType);  if (pQImage == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  free(pafScanblock);  delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER;  }  //创建图层  RSI_ImageLayer* pImageLayer = new RSI_ImageLayer(*pQImage);  if (pImageLayer == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  free(pafScanblock);  delete pQImage;   delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER;  }  pImageLayer->setLayerName(tr("band") + QString("%1").arg(nB+1) + tr(": pixel type = ") + QString(GDALGetDataTypeName(pixType)) + QString("(") + QString("%1").arg(pixSize) + tr(" bytes)"));  pImageLayer->setImageLayerInfo(rsiFileInfo);  pImageLayer->setGeoTransform(adfGeoTransform);  pImageLayer->setResolutionX(xMeterPerPix);  pImageLayer->setResolutionY(yMeterPerPix);  pImageLayer->setBufferToBeFree(pafScanblock); //记录到m_bufferToBeFree中,待关闭图像时释放buffer内存  pImageLayer->setVisible(false); //默认不显示  pImageLayer->setFlag(QGraphicsItem::ItemIsMovable, false);  m_pCanvas->AddImageLayer(pImageLayer);  //释放临时对象  delete pQImage;   }  delete poDataset; //关闭数据集  goto _READ_IMAGE_FILE_OVER; //读取完毕直接返回  } //end if eOpenDriverType  _READ_IMAGE_FILE_OVER:  return;  
}  //将图像宽度字节数32位对齐,返回对齐后的字节数  
int RSI_ImageReaderThread::Get32bitAlignedWidthBytes(int nImgPixWidth, int nBytesPerPix)  
{  return ((nImgPixWidth*nBytesPerPix/4) + (((nImgPixWidth*nBytesPerPix)%4)==0 ? 0 : 1)) * 4;  
}  //GDAL buffer 转 QImage  
QImage*  RSI_ImageReaderThread::CvtGDALBufferToGrayQImage(uchar* &pafScanblock, int nImgSizeX, int nImgSizeY, GDALDataType pixType)  
{  //准备颜色表  QVector<QRgb> vcolorTable;  for (int i = 0; i < 256; i++)  {  vcolorTable.push_back(qRgb(i, i, i));  }  int pixSize = GDALGetDataTypeSize(pixType) / 8;  int nImgSizeXAligned32bit = Get32bitAlignedWidthBytes(nImgSizeX, pixSize) / pixSize; //对齐后的像素宽度  int nNewImgSizeXAligned32bit = Get32bitAlignedWidthBytes(nImgSizeX, 1);  //根据pixType转换  if (pixType == GDT_Byte)  {  //do nothing  }  else //add by baolc, 2010-06-10  {  //将16位数据转为8位灰度显示  uchar* pNew_pafScanblock_8bit = (uchar*)malloc(nNewImgSizeXAligned32bit * nImgSizeY);  if (pNew_pafScanblock_8bit == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  return NULL;  }  if (pixType == GDT_UInt16)  {  CvtImgDataTo8bitGrayImg<ushort>((ushort*)pafScanblock, nImgSizeX, nImgSizeXAligned32bit, nImgSizeY,   pNew_pafScanblock_8bit, nNewImgSizeXAligned32bit, USHRT_MAX, 0);  }  else if (pixType == GDT_Int16)  {  CvtImgDataTo8bitGrayImg<short>((short*)pafScanblock, nImgSizeX, nImgSizeXAligned32bit, nImgSizeY,   pNew_pafScanblock_8bit, nNewImgSizeXAligned32bit, SHRT_MAX, SHRT_MIN);  }  else if (pixType == GDT_UInt32)  {  CvtImgDataTo8bitGrayImg<int>((int*)pafScanblock, nImgSizeX, nImgSizeXAligned32bit, nImgSizeY,   pNew_pafScanblock_8bit, nNewImgSizeXAligned32bit, UINT_MAX, 0);  }  else if (pixType == GDT_Int32)  {  CvtImgDataTo8bitGrayImg<unsigned int>((unsigned int*)pafScanblock, nImgSizeX, nImgSizeXAligned32bit, nImgSizeY,   pNew_pafScanblock_8bit, nNewImgSizeXAligned32bit, INT_MAX, INT_MIN);  }  else if (pixType == GDT_Float32)  {  CvtImgDataTo8bitGrayImg<float>((float*)pafScanblock, nImgSizeX, nImgSizeXAligned32bit, nImgSizeY,   pNew_pafScanblock_8bit, nNewImgSizeXAligned32bit, FLT_MAX, FLT_MIN);  }  else if (pixType == GDT_Float64)  {  CvtImgDataTo8bitGrayImg<double>((double*)pafScanblock, nImgSizeX, nImgSizeXAligned32bit, nImgSizeY,   pNew_pafScanblock_8bit, nNewImgSizeXAligned32bit, DBL_MAX, DBL_MIN);  }  free(pafScanblock); //释放原来的内存  pafScanblock = pNew_pafScanblock_8bit; //指向新内存,注意,因为参数传进来是引用,所以可以改变其指向  }  QImage* pQImage = new QImage(pafScanblock, nImgSizeX, nImgSizeY, QImage::Format_Indexed8); //注意:QImage: each scanline of data in the image must also be 32-bit aligned  if (pQImage == NULL)  {  QMessageBox::warning(m_pCanvas, tr("Failed"), tr("Open Image File Failed! Out of memory!"), tr("OK"));  }  else {  pQImage->setColorTable(vcolorTable);  }  return pQImage;  
}  //将非8位的灰度图转换为8位灰度图  
template <class T>  
void RSI_ImageReaderThread::CvtImgDataTo8bitGrayImg(T* pOrigData, int nImgSizeX, int nImgSizeXAligned32bit, int nImgSizeY, uchar* pNew8bitImgBuf, int nNewImgSizeXAligned32bit, T maxVal, T minVal)  
{  int oldIdxPos = 0;  int newIdxPos = 0;  T minPixVal = maxVal;  T maxPixVal = minVal;  T curPixVal;  //找到最大值和最小值  for (int niY=0; niY<nImgSizeY; niY++)  {  for (int niX=0; niX<nImgSizeX; niX++)  {  oldIdxPos = niY * nImgSizeXAligned32bit + niX;  curPixVal = pOrigData[oldIdxPos];  if (curPixVal > maxPixVal)  maxPixVal = curPixVal;  if (curPixVal < minPixVal)  minPixVal = curPixVal;  }  }  //根据最大值最小值进行归一化  double pixValRange = maxPixVal - minPixVal;  if (pixValRange == 0)  pixValRange = 1;  for (int niY=0; niY<nImgSizeY; niY++)  {  for (int niX=0; niX<nImgSizeX; niX++)  {  oldIdxPos = niY * nImgSizeXAligned32bit + niX;  newIdxPos = niY * nNewImgSizeXAligned32bit + niX;  curPixVal = pOrigData[oldIdxPos];  pNew8bitImgBuf[newIdxPos] = (uchar)((curPixVal - minPixVal) / pixValRange * 255);  }  }  
} 

4. 结果显示

在我的机器上读取的一张遥感图像里,将上面读取到的imgFileInfo经过翻译后显示出来如下所示:

GDAL Driver: GTiff/GeoTIFF  
图像大小: 24000 X 24000 X 1(波段)  
GCPs投影方式: GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.2572235630016,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]]  
GCP个数: 4  
图像左上角坐标: (116.906446, 36.204771)  
像素跨度(经纬度): (0.000031, -0.000022)  
像素跨度(米): (3.486195, 1.279424)

转载链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/760095.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密

x-zse-96,android端,伪dex加固,so加固,白盒AES,字符串加密 上一篇某招聘软件的sig及sp参数被和谐掉了,所以懂得都懂啊! 因为web的api没有那么全,所以来看了下app的,ios的防护几乎没有,纸糊的一样,android端的有点复杂了,到最后我也没能完整的实现整个加密过程,我也只复现到DF…

Hadoop安装部署-单机版

Apache Hadoop是一个使用HDFS&#xff08;Hadoop Distributed File System&#xff09;分布式文件系统执行可靠的、规模化的分布式计算的开源项目&#xff0c;Hadoop是使用Java语言开发&#xff0c;其运行在Linux操作系统上集群规模最大支持几千个分布式节点&#xff0c;本文主…

【嵌入式——QT】QThread创建多线程

【嵌入式——QT】QThread创建多线程 概述主要函数图示代码示例 概述 QThread类提供不依赖于平台的管理线程的方法&#xff0c;一个QThread类的对象管理一个线程&#xff0c;一般从QThread继承一个自定义类&#xff0c;并重定义虚函数run()&#xff0c;在run()函数里实现线程需…

基于java的健身房管理系统的设计与实现论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本健身房管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&…

富格林:谨记可信计策安全做单

富格林悉知&#xff0c;现货黄金由于活跃的行情给投资者带来不少的盈利的机会&#xff0c;吸引着众多的投资者进场做单。但在黄金投资市场中一定要掌握可信的投资方法&#xff0c;提前布局好策略&#xff0c;这样才能增加安全获利的机会。不建议直接进入市场做单&#xff0c;因…

【MySQL】数据库的基础概念

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习计网、mysql和算法 ✈️专栏&#xff1a;MySQL学习 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac…

Redis 更新开源许可证 - 不再支持云供应商提供商业化的 Redis

原文&#xff1a;Rowan Trollope - 2024.03.20 未来的 Redis 版本将继续在 RSALv2 和 SSPLv1 双许可证下提供源代码的免费和宽松使用&#xff1b;这些版本将整合先前仅在 Redis Stack 中可用的高级数据类型和处理引擎。 从今天开始&#xff0c;所有未来的 Redis 版本都将以开…

Vue.js前端开发零基础教学(二)

目录 前言 2.1 单文件组件 2.2 数据绑定 2.2.2 响应式数据绑定 2.3 指令 2.3.1 内容渲染指令 2.3.2 属性绑定指令 ​编辑 2.3.3 事件绑定指令 2.3.4 双向数据绑定指令 2.3.5 条件渲染指令 2.3.6 列表渲染指令 2.4 事件对象 2.5 事件修饰符 学习目标&am…

优化您的部署:Docker 镜像最佳实践

介绍 在快速发展的软件开发和部署领域&#xff0c;Docker 已成为容器化的强大工具&#xff0c;为打包、分发和运行应用程序提供了一种标准化的高效方式。Docker 镜像在这一过程中发挥着至关重要的作用&#xff0c;是容器化应用程序的基础。为确保最佳性能、可扩展性和安全性&a…

sentinel使用控制台实现

1、添加依赖 <!--整合控制台--><dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-transport-simple-http</artifactId> <version>1.8.0</version></dependency> 此项方法&#xff0…

HarmonyOS入门学习

HarmonyOS入门学习 前言快速入门ArkTS组件基础组件Image组件Text组件TextInput 文本输入框Buttonslider 滑动组件 页面布局循环控制ForEach循环创建组件 List自定义组件创建自定义组件Builder 自定义函数 状态管理Prop和LinkProvide和ConsumeObjectLink和Observed ArkUI页面路由…

JVM第八讲:GC - Java 垃圾回收基础知识

GC - Java 垃圾回收基础知识 本文是JVM第八讲&#xff0c; Java 垃圾回收基础知识。垃圾收集主要是针对堆和方法区进行&#xff1b;程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的&#xff0c;只存在于线程的生命周期内&#xff0c;线程结束之后也会消失&#xff0…

蓝桥杯day6刷题日记-航班时间-完全二叉树的权值-砝码称重

P8665 [蓝桥杯 2018 省 A] 航班时间 注意输出形式和读取数据的形式 #include <iostream> #include <algorithm> using namespace std; int n,h1,m1,s1,h2,m2,s2; int q[1000];int gettime() {int day 0;scanf("%d:%d:%d %d:%d:%d", &h1, &m1,…

单片机第四季-第二课:uCos2源码-BSP

1&#xff0c;初始uCos2 文件中uC开头的为uCos相关的。 2&#xff0c;uCos2源码工程建立 建立Source Insight工程 寻找main函数 (1)RTOS其实就是一个大的裸机程序&#xff0c;也是从main开始运行的 (2)main之前也是有一个汇编的启动文件的 (3)main中调用了很多初始化函数 bsp部…

html2canvas实现原理

html2canvas 是一个 JavaScript 库&#xff0c;可以将 HTML 元素转换为 Canvas 元素&#xff0c;进而实现对网页内容的截图功能。它的实现原理主要包括以下几个步骤&#xff1a; 1、解析和构建 DOM 树&#xff1a;html2canvas 首先会解析网页上指定的 HTML 元素&#xff0c;包…

开机动画结束正常进入桌面

systemUI 添加壁纸 1128 I am_proc_start: [0,1485,10019,com.android.systemui,service,{com.android.systemui/com.android.systemui.ImageWallpaper}] Line 87550: 03-08 17:29:15.680346 947 1128 I ActivityManager: Start proc 1485:com.android.systemui/u0a19 …

Linux信号补充——信号发送和保存

三、信号的发送与保存 3.1信号的发送 ​ 必须有操作系统来保存信号&#xff0c;因为他是管理者&#xff1b; ​ 信号给进程的task_struct发送信号&#xff0c;在task_struct中维护了一个整数signal有0-31位&#xff0c;共32个bit位&#xff1b;对于信号的管理使用的是位图结…

Java常见的垃圾回收器GC

本节讲解一下常见的垃圾回收器。需要特别注意的是&#xff0c;每一种垃圾回收器都会存在用户线程&#xff08;即用户程序&#xff09;暂停的问题&#xff0c;只不过每种回收器用户线程暂停的时长优化程度不一样。在启动JVM时&#xff0c;可以通过“指定参数-xx:垃圾回收器名称”…

Android静默安装一(Root版)

近期开发上线一个常驻app&#xff0c;项目已上线&#xff0c;今天随笔记录一下静默安装相关内容。我分三篇静默安装&#xff08;root版&#xff09;、静默安装&#xff08;无障碍版&#xff09;、监听系统更新、卸载、安装。先说说我的项目需求&#xff1a;要求app一直运行&…

贵价茶叶产区成谜,竹叶青茶是“行业黑马”还是“韭菜镰刀”?

撰稿|行星 来源|贝多财经 近日&#xff0c;央视“315”晚会曝光了自诩“高端商务白酒”的听花酒。这款白酒号称拥有提升免疫力、改善睡眠、保障男性功能等多种保健功能&#xff0c;标准装售价5860元&#xff0c;精品装售价更是达到5.86万元&#xff0c;昂贵程度令人瞠目结舌。…