1. 程式人生 > >利用GDAL儲存bmp格式的灰度圖(一)

利用GDAL儲存bmp格式的灰度圖(一)

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中下載。