序
最近在熟悉大恒的 PallasSDK
,在其提供的 sample
- GxSingleCamMono
中,将采集到的图像保存为 ppm格式
。虽然这种格式 OPenCV
也支持。但是我就是想给它增加点东西,保存为 BMP
格式。
在 C应用 -BMP图片存储格式及生成 中,介绍了 BMP的存储格式
及如何使用C语言生成图像。
现在我们把它搬到 SDK
中,获取一帧数据(一般相机的SDK都会提供),保存为bmp格式
。
其实很简单~ 其实并不难~
BMP存储
值得注意的是:对于 8bit灰度图,需要使用 调色版,否则 BMP图像
显示是不正常的
void SaveBMPFile(unsigned char *image,uint32_t w,uint32_t h)
{
int l = (w*3+3)/4*4;
//调色板
unsigned char *palette = new unsigned char[256*4];
for (int i = 0; i < 256; ++i)
{
palette[i*4] = i;
palette[i*4 + 1] = i;
palette[i*4 + 2] = i;
palette[i*4 + 3] = 0;
}
int bmi[]= {l*h+54+1024,0,54+1024,40,w,-h,1|((8)<<16),0,l*h,0,0,0,0};
//创建/打开文件
FILE *fp = fopen("bmpimage.bmp","wb");
//写入BMP标志
fprintf(fp,"BM");
//写入位图头文件信息+信息数据
fwrite(&bmi,52,1,fp);
//写入调色板
fwrite(palette,1024,1,fp);
//写入位图数据
fwrite(image,1,g_nPayloadSize,fp);
fclose(fp);
}
更简单的采集Demo
个人认为,GxSingleCamMono
还不够简单。
分享我修改后的 demo
:
#include "GxIAPI.h"
#include "DxImageProc.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
//设备句柄
GX_DEV_HANDLE g_hDevice = NULL;
//image buffer
unsigned char* g_pMonoImageBuf = NULL;
int64_t g_nPayloadSize = 0;
#define PIXFMT_CVT_FAIL -1 ///< PixelFormatConvert fail
#define PIXFMT_CVT_SUCCESS 0 ///< PixelFormatConvert success
#define FILE_NAME_LEN 50 ///< Save image file name length
#define ACQ_BUFFER_NUM 5
#define GX_VERIFY_EXIT(status) \
if (status != GX_STATUS_SUCCESS) \
{ \
GetErrorString(status); \
GXCloseDevice(g_hDevice); \
g_hDevice = NULL; \
GXCloseLib(); \
printf("<App Exit!>\n"); \
return status; \
}
//Show error message
#define GX_VERIFY(mStatus) \
if (mStatus != GX_STATUS_SUCCESS) \
{ \
GetErrorString(mStatus); \
return mStatus; \
}
//Get description of error
void GetErrorString(GX_STATUS);
//Convert frame date to suitable pixel format
int PixelFormatConvert(PGX_FRAME_BUFFER);
//Save one frame to PPM image file
void SavePPMFile(uint32_t, uint32_t);
void SaveBMPFile(unsigned char *image,uint32_t w,uint32_t h);
int main(int argc, char const *argv[])
{
GX_STATUS mStatus = GX_STATUS_SUCCESS;
//初始化设备库,申请资源
mStatus = GXInitLib();
if (mStatus != GX_STATUS_SUCCESS)
{
GetErrorString(mStatus);
return 0;
}
//枚举可用设备
uint32_t nDeviceNum = 0;
mStatus = GXUpdateDeviceList(&nDeviceNum,1000);
if(mStatus != GX_STATUS_SUCCESS && nDeviceNum <= 0)
{
GetErrorString(mStatus);
GXCloseLib();
return 0;
}
//通过序号打开设备
mStatus = GXOpenDeviceByIndex(1, &g_hDevice);
if (mStatus != GX_STATUS_SUCCESS)
{
GetErrorString(mStatus);
GXCloseLib();
return 0;
}
//功能控制
//
//检查当前相机是否支持Bayer格式
bool bColorFilter = false;
mStatus = GXIsImplemented(g_hDevice, GX_ENUM_PIXEL_COLOR_FILTER, &bColorFilter);
GX_VERIFY_EXIT(mStatus);
mStatus = GXGetInt(g_hDevice, GX_INT_PAYLOAD_SIZE, &g_nPayloadSize);
GX_VERIFY(mStatus);
//This app only support mono cameras
if (bColorFilter)
{
printf("<This app only support mono cameras! App Exit!>\n");
GXCloseDevice(g_hDevice);
g_hDevice = NULL;
GXCloseLib();
return 0;
}
//设置枚举类型的当前值
//Set acquisition mode
mStatus = GXSetEnum(g_hDevice, GX_ENUM_ACQUISITION_MODE, GX_ACQ_MODE_CONTINUOUS);
GX_VERIFY_EXIT(mStatus);
//Set trigger mode
mStatus = GXSetEnum(g_hDevice, GX_ENUM_TRIGGER_MODE, GX_TRIGGER_MODE_OFF);
GX_VERIFY_EXIT(mStatus);
//设置采集队列的缓冲区数量
uint64_t nBufferNum = ACQ_BUFFER_NUM;
mStatus = GXSetAcqusitionBufferNumber(g_hDevice, nBufferNum);
GX_VERIFY_EXIT(mStatus);
g_pMonoImageBuf = new unsigned char[g_nPayloadSize];
//开采
mStatus = GXStreamOn(g_hDevice);
if(mStatus != GX_STATUS_SUCCESS)
{
//采集失败处理
if (g_pMonoImageBuf != NULL)
{
delete[] g_pMonoImageBuf;
g_pMonoImageBuf = NULL;
}
GX_VERIFY_EXIT(mStatus);
}
PGX_FRAME_BUFFER pFrameBuffer = NULL;
// 获取一帧数据到pFrameBuffer,完成后需要调用GxQBuf接口,否则采集将无法继续
mStatus = GXDQBuf(g_hDevice, &pFrameBuffer, 1000);
if(mStatus != GX_STATUS_SUCCESS)
{
GetErrorString(mStatus);
}
if(pFrameBuffer->nStatus != GX_FRAME_STATUS_SUCCESS)
{
printf("<Abnormal Acquisition: Exception code: %d>\n", pFrameBuffer->nStatus);
}
else
{
//帧格式转化为像素格式
int nRet = PixelFormatConvert(pFrameBuffer);
if (nRet == PIXFMT_CVT_SUCCESS)
{
//保存为PPM图片
SavePPMFile(pFrameBuffer->nWidth, pFrameBuffer->nHeight);
SaveBMPFile((unsigned char *)g_pMonoImageBuf,pFrameBuffer->nWidth, pFrameBuffer->nHeight);
}
else
{
printf("PixelFormat Convert failed!\n");
}
}
mStatus = GXQBuf(g_hDevice, pFrameBuffer);
if(mStatus != GX_STATUS_SUCCESS)
{
GetErrorString(mStatus);
}
//关闭设备
mStatus = GXCloseDevice(g_hDevice);
//释放资源
mStatus = GXCloseLib();
return 0;
}
void GetErrorString(GX_STATUS emErrorStatus)
{
char *error_info = NULL;
size_t size = 0;
GX_STATUS emStatus = GX_STATUS_SUCCESS;
// Get length of error description
emStatus = GXGetLastError(&emErrorStatus, NULL, &size);
if(emStatus != GX_STATUS_SUCCESS)
{
printf("<Error when calling GXGetLastError>\n");
return;
}
// Alloc error resources
error_info = new char[size];
if (error_info == NULL)
{
printf("<Failed to allocate memory>\n");
return ;
}
// Get error description
emStatus = GXGetLastError(&emErrorStatus, error_info, &size);
if (emStatus != GX_STATUS_SUCCESS)
{
printf("<Error when calling GXGetLastError>\n");
}
else
{
printf("%s\n", (char*)error_info);
}
// Realease error resources
if (error_info != NULL)
{
delete []error_info;
error_info = NULL;
}
}
int PixelFormatConvert(PGX_FRAME_BUFFER pFrameBuffer)
{
GX_STATUS emStatus = GX_STATUS_SUCCESS;
VxInt32 emDXStatus = DX_OK;
// Convert RAW8 or RAW16 image to RGB24 image
switch (pFrameBuffer->nPixelFormat)
{
case GX_PIXEL_FORMAT_MONO8:
{
printf("MONO8\n");
memcpy(g_pMonoImageBuf, pFrameBuffer->pImgBuf, g_nPayloadSize);
break;
}
case GX_PIXEL_FORMAT_MONO10:
case GX_PIXEL_FORMAT_MONO12:
{
printf("MONO10/12\n");
//Convert to the Raw8 image
emDXStatus = DxRaw16toRaw8((unsigned char*)pFrameBuffer->pImgBuf, g_pMonoImageBuf, pFrameBuffer->nWidth, pFrameBuffer->nHeight, DX_BIT_2_9);
if (emDXStatus != DX_OK)
{
printf("DxRaw16toRaw8 Failed, Error Code: %d\n", emDXStatus);
return PIXFMT_CVT_FAIL;
}
break;
}
default:
{
printf("Error : PixelFormat of this camera is not supported\n");
return PIXFMT_CVT_FAIL;
}
}
return PIXFMT_CVT_SUCCESS;
}
void SavePPMFile(uint32_t ui32Width, uint32_t ui32Height)
{
char szName[FILE_NAME_LEN] = {0};
static int nRawFileIndex = 0;
FILE* phImageFile = NULL;
sprintf(szName, "Frame_%d.ppm", nRawFileIndex++);
phImageFile = fopen(szName,"wb");
if (phImageFile == NULL)
{
printf("Save %s failed!\n", szName);
return;
}
if(g_pMonoImageBuf != NULL)
{
fprintf(phImageFile, "P5\n" "%u %u 255\n", ui32Width, ui32Height);
fwrite(g_pMonoImageBuf, 1, g_nPayloadSize, phImageFile);
fclose(phImageFile);
printf("Save %s successed\n", szName);
}
else
{
printf("Save %s failed!\n", szName);
}
return;
}
void SaveBMPFile(unsigned char *image,uint32_t w,uint32_t h)
{
int l = (w*3+3)/4*4;
//调色板
unsigned char *palette = new unsigned char[256*4];
for (int i = 0; i < 256; ++i)
{
palette[i*4] = i;
palette[i*4 + 1] = i;
palette[i*4 + 2] = i;
palette[i*4 + 3] = 0;
}
int bmi[]= {l*h+54+1024,0,54+1024,40,w,-h,1|((8)<<16),0,l*h,0,0,0,0};
//创建/打开文件
FILE *fp = fopen("bmpimage.bmp","wb");
//写入BMP标志
fprintf(fp,"BM");
//写入位图头文件信息+信息数据
fwrite(&bmi,52,1,fp);
//写入调色板
fwrite(palette,1024,1,fp);
//写入位图数据
fwrite(image,1,g_nPayloadSize,fp);
fclose(fp);
}