【问题标题】:C++ bitmap to base64C++ 位图到 base64
【发布时间】:2015-10-23 14:53:37
【问题描述】:

我有以下代码来创建位图:

//raw data
PBYTE firstPixel = (PBYTE)((PBYTE)AnsiBdbRecord) + sizeof(WINBIO_BDB_ANSI_381_RECORD);

// declare other bmp structures
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER info;
RGBQUAD rq[256];
// create the grayscale palette 
for (int i = 0; i<256; i++)
{
    rq[i].rgbBlue = i;
    rq[i].rgbGreen = i;
    rq[i].rgbRed = i;
    rq[i].rgbReserved = 0;
}
//RGBQUAD bl = { 0,0,0,0 };  //black color
//RGBQUAD wh = { 0xff,0xff,0xff,0xff }; // white color

// andinitialize them to zero
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));
memset(&info, 0, sizeof(BITMAPINFOHEADER));

// fill the fileheader with data
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM'
bmfh.bfReserved1 = 0;
bmfh.bfReserved2 = 0;
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // + padding;
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD);

// fill the infoheader
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = Width;
info.biHeight = Height;
info.biPlanes = 1; // we only have one bitplane
info.biBitCount = PixelDepth; // RGB mode is 24 bits
info.biCompression = BI_RGB;
info.biSizeImage = 0; // can be 0 for 24 bit images
info.biXPelsPerMeter = 0x0ec4; // paint and PSP use this values
info.biYPelsPerMeter = 0x0ec4;
info.biClrUsed = 0; // we are in RGB mode and have no palette
info.biClrImportant = 0; // all colors are importantenter code here

我保存如下:

    HANDLE file = CreateFile(bmpfile, GENERIC_WRITE, FILE_SHARE_READ,
    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == NULL)
{
    DWORD dw = GetLastError();
    CloseHandle(file);
}

// write file header
if (WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL) == false)
{
    DWORD dw = GetLastError();
    CloseHandle(file);
}
// write infoheader
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false)
{
    DWORD dw = GetLastError();
    CloseHandle(file);
}
//write rgbquad for black
if (WriteFile(file, &rq, sizeof(rq), &bwritten, NULL) == false)
{
    DWORD dw = GetLastError();
    CloseHandle(file);
}
// write image data
if (WriteFile(file, &firstPixel[0], imageSize, &bwritten, NULL) == false)
{
    DWORD dw = GetLastError();
    CloseHandle(file);
}

// and clean up
CloseHandle(file);

我认为以上是保存位图图像的标准方法。但是,我不希望保存图像,而是希望它以 BASE64 的形式提供并在 HTTP Post 中传递。因此,这个问题与this one 有关,但是我在将 bmp 结构转换为 BASE64 时遇到了很多困难。我从here 获取了BASE64 编码器,但我不知道如何将BMPFILEHEADER、BMPINFOHEADER、RGBQUAD 和原始数据结构作为参数传递给BASE64 编码器。

关于如何结合我收集的信息有什么想法或建议吗?

更新

感谢 Roman Pustylnikov,我已经走得更远了: 我正在创建一个这样的结构:

    struct ImageBuffer
{
    BITMAPFILEHEADER bfheader;
    BITMAPINFOHEADER infobmp;
    RGBQUAD rgb[256];
    PBYTE bitmap;
};

填写如下:

ImageBuffer capture;
capture.bfheader = bmfh;
capture.infobmp = info;
// create the grayscale palette 
for (int i = 0; i<256; i++)
{
    capture.rgb[i].rgbBlue = i;
    capture.rgb[i].rgbGreen = i;
    capture.rgb[i].rgbRed = i;
    capture.rgb[i].rgbReserved = 0;
}
capture.bitmap = firstPixel;

并将其转换如下:

int totalSize = sizeof(capture.bfheader) + sizeof(capture.infobmp) + sizeof(capture.rgb) + imageSize;
std::string encodedImage = base64_encode(reinterpret_cast<const unsigned char*>(&capture), totalSize);

但是,它给了我一个无效的位图。此外,当我从磁盘加载位图(使用 writefile 生成的位图)时,我得到一个不同的 base64 字符串。我使用 C# 代码比较两个 Base64 字符串:

            // generated base64 string
        string test = "Put base64string generated from C++ here";
        byte[] imageBytes = Convert.FromBase64String(test);

        // generate the same string based on the actual bmp
        byte[] data = File.ReadAllBytes(@"c:\successtest.bmp");
        string original = Convert.ToBase64String(data);

更新二:解决方案

可以在 Roman Pustylnikov 答案的最新更新中找到解决方案。

【问题讨论】:

    标签: c++ bitmap biometrics


    【解决方案1】:

    你可以不用file in the middle,使用下面的example作为编码器/解码器:

    第一种方法是创建连续内存并将其作为输入:

    int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + imageSize ;
    
       unsigned char * bmpBuff = new char[size];
       int i = 0;
       memcpy((void *)( &( bmpBuff[i] )), (void *) &bfheader,sizeof(BITMAPFILEHEADER) );
       i+=sizeof(BITMAPFILEHEADER);
       memcpy((void *)( &( bmpBuff[i] )), (void *) &infobmp,sizeof(BITMAPINFOHEADER) );
       i+=sizeof(BITMAPINFOHEADER);
       memcpy((void *)( &( bmpBuff[i] )), (void *) &rq,sizeof(RGBQUAD)*256 );
       i+=sizeof(RGBQUAD)*256;
       memcpy((void *)( &( bmpBuff[i] )), (void *) firstPixel, imageSize );
       std::string encodedImage = base64_encode(bmpBuff, size);
    

    这种方法的缺点是您需要复制内存。

    另一种方法是处理“丢失的三元组”。为此,我们需要定义一个结构:

    struct ScreenShotBuffer
    {
        BITMAPFILEHEADER bfheader;
        BITMAPINFO infobmp;
        RGBQUAD rgb[256];      
    };
    

    现在棘手的部分来了。由于编码处理字节的三元组,因此您需要处理两个缓冲区之间的边界(我尚未对此进行测试,因此它可能包含错误,只是一种通用方法)。

    int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256;
    
    
    unsigned char *info = einterpret_cast<const unsigned char*> &screenshotInfo;
    unsigned char *img = einterpret_cast<const unsigned char*> firstPixel;
    
    std::string encodedInfo = base64_encode(info , size);
    std::string encodedLostTriplet = "";
    
    int offset = size%3;
    
    unsigned char lostTriplet[3];
       if (offset) {
         lostTriplet[0]=info[size-offset];
         if (offset==2) 
             lostTriplet[1] = info[size-offset+1];
         else
             lostTriplet[1] = img[0];
         lostTriplet[2] = img[2-offset];
         encodedLostTriplet = base64_encode(lostTriplet, 3);
       }
       else {
          offset=3;
       }
    
    std::string encodedData = base64_encode(reinterpret_cast<const unsigned char*> &img[3-offset], imageSize - (3 - offset) );
    
    std::string encodedImage = encodedInfo + lostTriplet + encodedData;
    

    有点乱,但应该在相同的内存上工作。

    【讨论】:

    • 这似乎是朝着正确的方向发展。我需要先将 bitmapfileheader、infoheader、RGBQuad 和 rawdata 连接在一起吗?
    • 我相信您可以使用该结构并将标题与图像连接起来作为结果。由于标头应始终是恒定长度的(作为标头组合和编码的长度),其余部分将是图像数据。应该很容易解码。我已经更新了答案。
    • 我认为你已经接近了,尽管它仍然不起作用。有关详细信息,请参阅我的问题更新。
    • 我可能错了,但是 sizeof(PBYTE) 是 sizeof(char),而不是 imageSize。这就是我拆分编码的原因。除了标题之外,您必须对图像进行编码。位图不一定存储在结构旁边,因此 totalSize 是从未定义的位置而不是数据读取。
    • 长度看起来不错,如果我用 C# 反转保存的样本并将其与 C++ Base64 字符串的长度进行比较,它们的长度相同。我似乎只是在“encodedImage”变量中输入了错误的值..
    猜你喜欢
    • 2017-01-05
    • 2012-03-02
    • 1970-01-01
    • 2012-01-29
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 2013-09-20
    • 1970-01-01
    相关资源
    最近更新 更多