GDAL是处理地理信息的有力工具。从这篇博客开始,我分3篇来介绍如何利用GDAL来保存bmp格式的灰度图。
第一篇是一个最简单的例子:从TCP端接收数据,然后通过GDAL保存为本地的bmp灰度文件。这个例子有个缺点:GDAL要求数据每行字节数被4整除,这个程序没有对这个要求加保护措施。这个问题将在第二篇里修正。
1)准备数据
在photoshop里新建一个灰度文件,文件的宽度应该是4的倍数(正如前面所说,程序没有加保护,所以文件宽度要被四整除)。
注:创建的灰度图,每个像素占一个字节。但是CSDN似乎不支持上传这种单字节格式的bmp文件。我只能传一个png文件示意。
2)撰写一个TCP类,用来从TCP端口读取二进制数据:
#include "ClientThread.h"
#include "ClientTCP.h"
#include <wingdi.h>
#include <qdebug.h>
#if !defined(IMG_BUF_SIZE)
#define IMG_BUF_SIZE (1024 * 1024 * 10)
#endif
#if !defined(TCP_BUF_SIZE)
#define TCP_BUF_SIZE (1024 * 1024)
#endif
int MFCconnect(SOCKET skt, const sockaddr * pAddr, int iLen)
{
return connect(skt, pAddr, iLen);
}
ClientThread * pInstance = NULL;
ClientThread::ClientThread() : QThread()
{
m_pData = new unsigned char[TCP_BUF_SIZE];
m_pImg = new unsigned char[IMG_BUF_SIZE];
m_iBufPtr = 0;
pInstance = this;
}
ClientThread::~ClientThread()
{
terminate();
delete [] m_pData;
delete [] m_pImg;
}
void ClientThread::run()
{
WSAData wsaData;
int iRet = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (0 == iRet)
{
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET != m_socket)
{
sockaddr_in saServer;
saServer.sin_family = AF_INET; //地址家族
saServer.sin_port = htons(5050); //注意转化为网络节序
saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (SOCKET_ERROR != MFCconnect(m_socket, (const sockaddr *)&saServer, (int)sizeof(saServer)))
{
ClientTCP * pDlg = ClientTCP::pGetInstance();
pDlg->setReadyMsg();
while (true)
{
int iRet = recv(m_socket, (char *)m_pData, TCP_BUF_SIZE, 0);
if (SOCKET_ERROR == iRet)
{
int nError = WSAGetLastError();
pDlg->setErrMsg();
break;
}
else if (0 == iRet)//链接断开
{
int nError = WSAGetLastError();
pDlg->setErrMsg();
break;
}
else
{
static bool static_bFlag = false;
static BITMAPFILEHEADER stFileBMP;
static BITMAPINFOHEADER stInfoBMP;
if(!static_bFlag)
{
memcpy(&stFileBMP, m_pData, sizeof(stFileBMP));
memcpy(&stInfoBMP, m_pData + sizeof(BITMAPFILEHEADER), sizeof(BITMAPINFOHEADER));
static_bFlag = true;
}
memcpy(m_pImg + m_iBufPtr, m_pData, iRet);
m_iBufPtr += iRet;
if(m_iBufPtr == stFileBMP.bfSize)
{
bool bRet = bWriteMemToFile_GrayScale(m_pImg + stFileBMP.bfOffBits,
stInfoBMP.biWidth, stInfoBMP.biHeight,
"hehe.bmp");
}
qDebug()<<m_iBufPtr;
}
}
WSACleanup();
}
}
}
}
ClientThread * ClientThread::pGetInstance()
{
return pInstance;
}
3) 撰写一个专门用来保存灰度图的函数:
bool bWriteMemToFile_GrayScale(unsigned char * pData, int iWidth, int iHeight,
const char * arrFileName)
{
char *GType = NULL;
GType = pFindImageTypeGDAL((char *)arrFileName);
if (GType == NULL) { return false; }
GDALDriver *pDriver = NULL;
pDriver = GetGDALDriverManager()->GetDriverByName((const char *)GType);
if( pDriver == NULL ) { return false; }
GDALDataset * pDataSet = pDriver->Create(arrFileName, iWidth, iHeight, 1, GDT_Byte, NULL);
GDALRasterBand * pBand = pDataSet->GetRasterBand(1);// from 1 to GetRasterCount()
pBand->RasterIO(GF_Write,
0,
0,
iWidth,
iHeight,
pData ,
iWidth,
iHeight,
GDT_Byte,
0,
0,
NULL);//用法见 https://www.gdal.org/classGDALRasterBand.html#a30786c81246455321e96d73047b8edf1
GDALClose((GDALDatasetH) pDataSet);//用法示例见https://blog.csdn.net/godenlove007/article/details/8864763 https://blog.csdn.net/liminlu0314/article/details/19633849
}
char * pFindImageTypeGDAL( char *pDstImgFileName)
{
char *dstExtension = strlwr(strrchr(pDstImgFileName,'.') + 1);
char *Gtype = NULL;
if (0 == strcmp(dstExtension,"bmp")) Gtype = "BMP";
else if (0 == strcmp(dstExtension,"jpg")) Gtype = "JPEG";
else if (0 == strcmp(dstExtension,"png")) Gtype = "PNG";
else if (0 == strcmp(dstExtension,"tif")) Gtype = "GTiff";
else if (0 == strcmp(dstExtension,"gif")) Gtype = "GIF";
else Gtype = NULL;
return Gtype;
}
4)在main.cpp里初始化GDAL驱动:
#include "ClientTCP.h"
#include <QtWidgets/QApplication>
#include "ClientThread.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ClientThread thrd;
ClientTCP w;
w.show();
GDALAllRegister();
return a.exec();
}
5)通过TCP助手向你的程序发送你刚刚制作的bmp文件:
6) 发送完毕后,你会看到hehe.bmp保存在sln同级的目录内:
7)注意,bmp文件把内存的第一行作为图像最底一行。所以网络助手发出的数据其实是从底下往上发。但是GDAL保存文件时,仍然按照正常顺序,内存第一行也是图片第一行。所以最后的文件hehe.bmp变成upside-down
源代码请在我的资源https://download.csdn.net/my中下载。