Mat image conversion and BMP format images

I. Introduction

Format conversion is very common, in fact, I realized Mat I realized that imwrite interface can be directly saved as .bmp image data after Mat turn BMP, but are talking about following conversion is converted in memory, since the image is sent to identifying server Mat obviously can not be saved as .bmp file first, and then read the file in binary form to send identification service, but should complete its conversion directly in memory.

Two, Mat and BMP data structure

  1. Mat data structure

    Mat matrix data structure consists of a pointer pointing to the head and matrix data: Mat = + Matrix matrix data head pointer, the following code snippet Mat class, wherein uchar * data is common, UMatData * u GPU version of Mat, the Mat needs to be converted before UMat referred GPU image processing.

    int flags;
    //! the matrix dimensionality, >= 2
    int dims;
    //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
    int rows, cols;
    //! pointer to the data
    uchar* data;
    //! helper fields used in locateROI and adjustROI
    const uchar* datastart;
    const uchar* dataend;
    const uchar* datalimit;
    
    //! custom allocator
    MatAllocator* allocator;
    //! and the standard allocator
    static MatAllocator* getStdAllocator();
    static MatAllocator* getDefaultAllocator();
    static void setDefaultAllocator(MatAllocator* allocator);
    
    //! internal use method: updates the continuity flag
    void updateContinuityFlag();
    
    //! interaction with UMat
    UMatData* u;
    
    MatSize size;
    MatStep step;
  2. BMP data structure

    Windows Bitmap format is the basic display of the picture, the picture is displayed under Windows must first be converted to bitmaps and display. BitMap commonly known as a bitmap, without any compression, a relatively large image, other image formats are based on the corresponding compression algorithm is generated on the basis thereof.

    Bitmap = header information header + + + palette pixel data

    Header: BITMAPFILEHEADER, size of 14 bytes

    Header: BITMAPINFOHEADER, a size of 40 bytes

    Palette: RGBQUAD [n], of size 4 * n bytes, can be understood as an array, each element corresponds to one color, the pixel values ​​of the index color, palette, and gray-scale is generally divided into palettes color palette, gray scale palette name implies the use of a monochrome image, a color image using a color palette; palette primarily to reduce image size, only the palette needs only bitmap 1,4,8 , corresponding to the color type is bitmap 2,16,256.16,24,32 have enough space to compositions consisting of various colors, if the palette it will take up too much space, may be 16-bit RGB FIG. (556) or RGB (565), 24 is a bitmap RGB (888), 32 the bitmap is RGBA (8888).

    Pixel data: uchar * p.

Third, the realization of ideas Conversion

Mat clear data structure and BMP, and convert it very simple, the Mat in advance (that is, the relevant member variables Mat class) assigned to the BMP file header information and turn heads when Mat BMP, data pointer to the data copying the data portion of the BMP; BMP Mat turn is the same reason.

Four, Mat turn BMP

int CAlgorithm::Mat2Bmp(cv::Mat * pMat, uchar * & pBmp, ulong & size)
{
    if (!pMat)
    {
        return -1;
    }

    /////////////////////////////////创建bmp空白图片///////////////////////////
    int depth = pMat->depth();
    int channels = pMat->channels();
    int width = pMat->cols;
    int height = pMat->rows;

    // 获取图像每个像素的位数
    // depth 代表每个通道元素的宽度,0:CV_8U
    uint pixelSize = (8 << (depth / 2)) * channels;

    // bmp规定每一行的长度必须是4的整数倍, pMat->cols * pixelSize / 8 为每一行有效数据长度 
    // 其实OpenCV已经规定每行长度必须是4的整数倍,多以Mat也是
    //uint lineSize = ((width * pixelSize / 8) + 3) / 4 * 4;
    uint lineSize = width * pixelSize / 8;

    // 计算需要的调色板的大小,以便为bmp图像申请内存空间
    // 只有1、4、8位图才需要调色板(1->2, 4->16, 8->256),因为16、24、32位有足够的空间以便自由组合

    uint colorTableSize = 0;

    // 当前只支持8位图
    if (1 == pixelSize)
    {
        colorTableSize = 2 * sizeof(RGBQUAD);

        RGBQUAD* pColorTable = (RGBQUAD*)(&pBmp[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)]);
        for (int i = 0; i < 2; ++i)
        {
            // 灰阶调色板
            pColorTable[i].rgbRed = i;
            pColorTable[i].rgbGreen = i;
            pColorTable[i].rgbBlue = i;

            // 也可以创建彩色调色版
        }
    }
    else if (4 == pixelSize)
    {
        colorTableSize = 16 * sizeof(RGBQUAD);

        RGBQUAD* pColorTable = (RGBQUAD*)(&pBmp[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)]);
        for (int i = 0; i < 16; ++i)
        {
            // 灰阶调色板
            pColorTable[i].rgbRed = i;
            pColorTable[i].rgbGreen = i;
            pColorTable[i].rgbBlue = i;

            // 也可以创建彩色调色版
        }
    }
    if (8 == pixelSize)
    {
        colorTableSize = 256 * sizeof(RGBQUAD);

        RGBQUAD* pColorTable = (RGBQUAD*)(&pBmp[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)]);
        for (int i = 0; i < 256; ++i)
        {
            // 灰阶调色板
            pColorTable[i].rgbRed = i;
            pColorTable[i].rgbGreen = i;
            pColorTable[i].rgbBlue = i;

            // 也可以创建彩色调色版
        }
    }

    // bmp图片的大小, sizeof(BITMAPFILEHEADER) = 14, sizeof(BITMAPINFOHEADER) = 40
    size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize + height * lineSize;

    pBmp = (uchar*)malloc(size);
    if(!pBmp)
    {
        return -2;
    }
    memset(pBmp, 0, size);

    /////////////////////////////////为bmp图片的文件头赋值/////////////////////////////////
    BITMAPFILEHEADER * pFileHead = (BITMAPFILEHEADER *)pBmp;
    pFileHead->bfType = 0x4D42; // 0x4D42 代表 “BM”,位图标志
    pFileHead->bfSize = size;
    pFileHead->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize; // 图像数据偏移量

    /////////////////////////////////为bmp图片的信息头赋值/////////////////////////////////
    BITMAPINFOHEADER * pInfoHead = (BITMAPINFOHEADER *)(&pBmp[sizeof(BITMAPFILEHEADER)]);
    pInfoHead->biSize = 40;             // 信息头的大小
    pInfoHead->biWidth = width;
    pInfoHead->biHeight = height;
    pInfoHead->biPlanes = 1;            // 图像平面数,rgb为1?什么时候大于1?
    pInfoHead->biBitCount = pixelSize;  // 图像每个像素所占的位数
    pInfoHead->biCompression = 0;       // 0:不压缩,1:REL8, 2:REL4
    pInfoHead->biSizeImage = height * lineSize;     // 图像数据大小
    pInfoHead->biXPelsPerMeter = 0;     // 水平方向像素/米,分辨率
    pInfoHead->biYPelsPerMeter = 0;     // 垂直方向像素/米,分辨率
    pInfoHead->biClrUsed = 0;           // BMP图像使用的颜色,0:表示使用全部颜色
    pInfoHead->biClrImportant = 0;      // 重要的颜色数,0:所有的颜色都重要,当显卡不能够显示所有颜色时,辅助驱动程序显示颜色

    ///////////////////////////////////为bmp图片的调色板赋值/////////////////////////////////
    //// 8位图
    //if (8 == pixelSize)
    //{
    //  // 只有1、4、8位图才需要调色板(1->2, 4->16, 8->256),因为16、24、32位有足够的空间以便自由组合
    //  RGBQUAD* pColorTable = (RGBQUAD*)(&pBmp[sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)]);
    //  for (int i = 0; i < 256; ++i)
    //  {
    //      // 灰阶调色板
    //      pColorTable[i].rgbRed = i;
    //      pColorTable[i].rgbGreen = i;
    //      pColorTable[i].rgbBlue = i;

    //      // 也可以创建彩色调色版
    //  }
    //}

    /////////////////////////////////为bmp图片的图像数据赋值/////////////////////////////////
    // BMP 和 Mat 数据都是自左向右,但是BMP是自下而上,Mat是自上而下,故而在数据转换时需要颠倒数据上下位置

    //uchar * pBmpData = pBmp + pFileHead->bfOffBits;
    uchar * pBmpData = pBmp + pFileHead->bfOffBits + height * lineSize; // 最后一行尾地址
    uchar * pMatData = pMat->data;  // 第一行首地址

    // 将Mat从上往下一行一行拷给BMP
    for (int i = 0; i < height; ++i)
    {
        // 这里的 width 代表水平方向的像素个数,但是每个像素占1个字节,通过查表索引(RGB
        // 每次拷贝一行
        pBmpData -= lineSize;
        memcpy(pBmpData, pMatData, lineSize);
        pMatData += lineSize;
    }

    return 0;
}

Five, BMP turn Mat

int CAlgorithm::Bmp2Mat(uchar * pBmp, cv::Mat & mat)
{
    // 获取文件头信息
    if (!pBmp)
    {
        return -1;
    }

    BITMAPFILEHEADER * pFileHead = (BITMAPFILEHEADER *)pBmp;
    if (pFileHead->bfType != 0x4D42)
    {
        return -2;
    }

    BITMAPINFOHEADER* pInfoHead = (BITMAPINFOHEADER *)(pBmp + sizeof(BITMAPFILEHEADER));

    long height = pInfoHead->biHeight;
    long width = pInfoHead->biWidth;
    ulong dataSize = pInfoHead->biSizeImage;

    uchar * pMatData = (uchar *)malloc(dataSize);
    memset(pMatData, 0, dataSize);

    // bmp数据填充数据为至下而上、至左而右,mat为至上而下、至左而右
    uint lineSize = width * (pInfoHead->biBitCount) / 8;

    // 最后一行尾地址
    uchar * pBmpData = (uchar *)(pBmp + pFileHead->bfSize); // 每个像素占一个字节
    for (int h = 0; h < height; ++h)
    {
        pBmpData -= lineSize;
        memcpy(pMatData, pBmpData, lineSize);   // 复制整行
        pMatData += lineSize;
    }

    // Mat数据指针移到最前面
    pMatData -= dataSize;

    mat.create(height, width, CV_MAKETYPE(CV_8U, (pInfoHead->biBitCount) / 8));
    memcpy(mat.data, pMatData, dataSize);

    free(pMatData);

    return 0;
}

Sixth, little experience

A code base in the BMP Mat turn on improvements, BMP Mat turn their own to achieve, is not difficult to realize the function discovery, we spent a weekend time, learned a lot of interesting things Done, also during learned a lot.

Guess you like

Origin www.cnblogs.com/huluwa508/p/10983562.html