目的:通过构建bitmap header将图像unsigned char*纯数据数组图像保存为位图
环境:
系统:Win10 x64
环境:win10/centos 7.5
vs2017/gcc
问题分析:
有朋友在c环境下,使用cv::Mat.data保存了图片中的纯unsigned char*数据。然后需要在另外的环境使用这部分数据还原出图片来。在不使用任何三方库的情况下,最简单的做法莫过于使用自己构建的bmp header来生成bitmap位图。
参考博客连接:
bitmap构建header保存
https://blog.csdn.net/xiajun07061225/article/details/6633938
图像X轴翻转
https://blog.csdn.net/tounaobun/article/details/17377777?utm_medium=distribute.pc_relevant_download.none-task-blog-BlogCommendFromBaidu-3.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-BlogCommendFromBaidu-3.nonecas
RGB转BGR
https://bbs.csdn.net/topics/392366646
解决方案:
直接上代码,代码较随意,看构建过程即可
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
//BMP文件头(14字节)
typedef struct /**** BMP file header structure ****/
{
unsigned int bfSize; /* Size of file */
unsigned short bfReserved1; /* Reserved */
unsigned short bfReserved2; /* ... */
unsigned int bfOffBits; /* Offset to bitmap data */
} MyBITMAPFILEHEADER;
//位图信息头(40字节)
typedef struct /**** BMP file info structure ****/
{
unsigned int biSize; /* Size of info header */
int biWidth; /* Width of image */
int biHeight; /* Height of image */
unsigned short biPlanes; /* Number of color planes */
unsigned short biBitCount; /* Number of bits per pixel */
unsigned int biCompression; /* Type of compression to use */
unsigned int biSizeImage; /* Size of image data */
int biXPelsPerMeter; /* X pixels per meter */
int biYPelsPerMeter; /* Y pixels per meter */
unsigned int biClrUsed; /* Number of colors used */
unsigned int biClrImportant; /* Number of important colors */
} MyBITMAPINFOHEADER;
// 保存为bmp文件
void MySaveBmp(const char *filename, unsigned char *rgbbuf, int width, int height)
{
MyBITMAPFILEHEADER bfh;
MyBITMAPINFOHEADER bih;
/* Magic number for file. It does not fit in the header structure due to alignment requirements, so put it outside */
unsigned short bfType = 0x4d42;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfSize = 2 + sizeof(MyBITMAPFILEHEADER) + sizeof(MyBITMAPINFOHEADER) + width * height * 2;
bfh.bfOffBits = 0x36;
bih.biSize = sizeof(MyBITMAPINFOHEADER);
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 5000;
bih.biYPelsPerMeter = 5000;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
printf("filename=%s \n", filename);
FILE *file = fopen(filename, "wb");
if (!file)
{
printf("Could not write file\n");
return;
}
/*Write headers*/
fwrite(&bfType, sizeof(bfType), 1, file);
fwrite(&bfh, sizeof(bfh), 1, file);
fwrite(&bih, sizeof(bih), 1, file);
fwrite(rgbbuf, width*height * 3, 1, file);
fclose(file);
}
// rgb格式转rgr
int RGB2BGR(unsigned char *pImageDst, unsigned char *pImageSrc, int nWidth, int nHeight, int nBitCount)
{
unsigned char *pImageDataTmp = pImageSrc;
int nWidthNum = (nWidth * nBitCount + 31) / 32 * 4;
for (int i = 0; i < nHeight; i++)
{
for (int j = 0; j < nWidthNum; j += 3)
{
*pImageDst = *(pImageDataTmp + 2);
*(pImageDst + 1) = *(pImageDataTmp + 1);
*(pImageDst + 2) = *pImageDataTmp;
pImageDst += 3;
pImageDataTmp += 3;
}
}
return 0;
}
// 图像沿X轴旋转
void bmpXFlip(unsigned char* srcData, unsigned char* dstData, int nWidth, int nHeight, int nBitCount){
int lineSize = (nWidth * nBitCount + 31) / 32 * 4;
for (int i = 0; i < nHeight; i++) {
for (int j = 0; j < lineSize; j++) {
dstData[i * lineSize + j] = srcData[lineSize * (nHeight - 1 - i) + j];
}
}
}
// 涂抹图像,通过将按比例区域的像素点值清除达到效果
void smearImg(unsigned char* srcData, int nWidth, int nHeight, int nBitCount) {
int lineSize = (480 * 24 + 31) / 32 * 4;
for (int i = 0; i < 480; i++) {
for (int j = 0; j < lineSize; j++) {
if (j < lineSize * 2 / 3 && j > lineSize / 3 && i < 240 && i > 160) {
srcData[i * lineSize + j] = 10;
}
}
}
}
int main()
{
FILE *inFile = fopen("./01.img", "rb");
if (!inFile)
{
printf("Could not read file\n");
return 0;
}
unsigned int current_read_position = ftell(inFile);
int file_size;
fseek(inFile, 0, SEEK_END);
file_size = ftell(inFile);
fseek(inFile, current_read_position, SEEK_SET);
printf("uchar file size: %d\n", file_size);
int bufferSize = 480 * 480 * 2 * 2;
unsigned char* imgBuffer = (unsigned char*)malloc(bufferSize);
fread(imgBuffer, bufferSize, 1, inFile);
fclose(inFile);
//rgb转bgr
unsigned char* imgBgr = (unsigned char*)malloc(bufferSize);
RGB2BGR(imgBgr, imgBuffer, 480, 480, 24);
// 水平X轴翻转
memset(imgBuffer, 0, bufferSize);
bmpXFlip(imgBgr, imgBuffer, 480, 480, 24);
MySaveBmp("./out.bmp", imgBuffer, 480, 480);
free(imgBgr);
free(imgBuffer);
return 0;
}
PS:
1. 文中使用的01.img文件链接
https://download.csdn.net/download/Alger_magic/12518712
2. 从朋友出获取到一个信息:图像沿X轴翻转还有一个简便的做法,就是在构建位图时将MyBITMAPINFOHEADER结构中的biHeight赋值为负数,比如原数据高为480,那么这里赋值为-480.则生成的图像就是已经做过X轴翻转的,就不需要额外调用bmpXFlip()。
补充几点遇到的问题
1. unsigned short bfType = 0x4d42; 位图类型,必须写入位图文件的最前面,然后才是bmp文件头,位图信息头。
2. 由于原cv::Mat中选用的是RGB类型,所以在代码中添加了RGB转BGR的函数,如果原Mat直接加载的是BGR,则不需要转换。
3. 由于直接保存的unsigned char*数据写入位图以后发现,位图图片是沿X轴翻转过的倒立图片。目前不清楚原因,猜测可能跟cv::Mat存储图片信息的方式有关系。所以在代码中添加了沿X轴翻转的照片。
4. 由于原图包含人脸,所以使用了一个涂抹函数将人脸的部分进行了黑涂抹。