raw格式数据转BMP格式(OpenCV)

xiaoxiao2021-02-28  79

环境:Win7+VS2013+OpenCV2.4.13

材料:14bit的raw红外数据,低8位+高8位

raw格式: 1、摄像头或者探测器得到的原始数据,一般的是14位,于是需要两个字节保存。 2、只有一个通道像素数据。

BMP格式: 1、win最常用图片格式 2、有数据头,信息头,数据等等信息

把raw变成bmp,使用opencv,很多教程都说可以使用cvCvtColor函数,但是我一直没有成功过,可能是我的raw数据和标准的有冲突或者不一致。

于是,我纯手工完成。

首先,我有一个思路就是,听说所有bmp的文件头和信息头都是一样的,并且占据1078个字节,找了几幅图像验证了一下,好像是一样的,于是乎,读取已经存在的bmp的文件头和信息头,然后把raw数据直接加到文件头和信息头的后面。 下面是我的代码:

#define BMP_WIDTH 384 #define BMP_HEIGHT 288 #define WRITE_COUNT_PILXEL 2 //写的时候每个像素点4个字节 #define BMP_COUNT_PILXEL 1 //图像每个像素3个字节 #define BUFFER_SIZE BMP_WIDTH*BMP_HEIGHT*WRITE_COUNT_PILXEL //缓冲区大小 #define BMP_BUFFER_SIZE BMP_WIDTH*BMP_HEIGHT*BMP_COUNT_PILXEL //用于存储图像的缓冲区大小 //使用纯c代码 //可以出图,但是图形自己基本没有了 //按照第二种是可以出图的,很清晰的。 int raw2bmp() { /***********读取本地bmp文件头*************/ unsigned char buf1[111670] = { 0 };// 111670=384*288+1078 FILE *fp1 = fopen("D:\\images\\CV\\图片\\chuliyihou.bmp", "rb"); if (!fp1) { printf("fopen failed \n"); return 0; } fread(buf1, 1, 1078, fp1); fclose(fp1); /***********读取本地raw文件头*************/ unsigned char read_buffer[BUFFER_SIZE] = { 0 };//初始化read_buffer缓冲区一维数组 FILE *fp = fopen("D:\\images\\CV\\图片\\yuantu.raw", "rb");//打开lena.raw if (fp == NULL) { printf("can't open the file yuantu.raw\n"); return -1; } int ret = fread(read_buffer, BUFFER_SIZE, 1, fp); if (ret != 1) { printf("Fail to read raw data\r\n"); return -2; } int i, j; long max = INT_MIN; long min = INT_MAX; //求出整幅图里面像素最大值和最小值 for (i = 0; i<BUFFER_SIZE; i++){ if ((i + 1) % 2){ unsigned char a2 = read_buffer[i]; unsigned char a1 = read_buffer[i + 1]; long t1 = a1 << 8; long t2 = t1 + a2; if (t2 > max){ max = t2; } if (t2 < min){ min = t2; } } } printf("max=%d", max); printf("min=%d", min); j = 1078; for (i = 0; i<BUFFER_SIZE; i++){ if ((i + 1) % 2){ unsigned char a2 = read_buffer[i]; unsigned char a1 = read_buffer[i + 1]; //按14到8位的归一化 long t1 = a1 << 8; long t2 = t1 + a2; //unsigned char res = t2 / 64; //第一种方式 unsigned char res = (255 * (t2 - min)) / (max - min);//第二种 buf1[j] = res; j = j + 1; } } FILE *fp_save = fopen("D:\\images\\CV\\图片\\yuantu.bmp", "wb"); fwrite(buf1, 1, 111670, fp_save); fclose(fp_save); cin.get(); return 0; }

注意:为什么要求最大值和最小值?这是因为我的是红外探测的raw格式数据,像素分布很集中,对比度很低,基本是一片白或者一片黑,最大值和最小值可以完成像素值分散。 问题:不管我怎么写,怎么改,我的bmp和原图的有一个旋转角度,我解决不了,但是总算是出现图片了。出于不满意,另外想了一条思路。

第二条思路: 在看OpenCV的Mat类是时候,他有一个构造函数:

//! constructor for matrix headers pointing to user-allocated data Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP);

参数介绍: rows:行,也就是高; cols:列,也就是宽; type:可以写CV_8UC1,表示一个字节长度的unsigned char类型; data:属于void*类型,任何指针类型都可以传进来,为了适配; step:默认就好; 通过这个构造函数,我大概猜想一下,如果我可以得到一个8位的unsigned char类型的数组,宽和高也知道的情况下,raw是不是就可以直接变成Mat呢?

现在问题就在如何把14bit的raw变成8bit的,网上有直接舍弃低6位,这也是最简单最直接的方法,但是这样就把我对比度本来不大的图片变得越发纯了。放弃这个方法。也就是说,我需要把14bit的按比例缩小到8bit。

还有一种,14bit和8bit之间隔了6bit,把14bit的数据统一除以64(2的6次方),我发现这种方式依然对我的红外图像的对比度有很大影响,也是看不清楚的。

最后在实验室师妹的启发下,给了下面的公式:

Max:原来14bit的像素中最大值 Min:原来14bit的像素中最小值 val:原来14bit的像素的实际值,也就是变换前的值 ret:变化后的8bit像素值

公式如下: 这样处理以后,对比度明显增强。

我的代码如下:

//使用opencv完成raw到bmp的转变 //完美实现 int createBmpFromRaw() { char *rawFileName = "D:\\images\\CV\\图片\\yuantu.raw"; FILE *fp = NULL; int ret = 0; int width = 384; int height = 288; //为14位的raw数据分配内存空间 //为什么要乘以2?这是因为两个字节存储一个像素 unsigned char pRawData[384 * 288 * 2] = { 0 }; if (NULL == pRawData) { printf("Fail to calloc pRawData!\n"); return -1; } //打开raw文件 fp = fopen(rawFileName, "rb"); if (NULL == fp) { printf("Fail to read %s!\n", rawFileName); return -1; } //读取数据到pRawData中去 //第二个参数表示读取字节数 ret = fread(pRawData, 384 * 288 * 2, 1, fp); if (1 != ret) { printf("Fail to fread data!\n"); return -1; } //这个用来存储变化后的8bit的数组结果 unsigned char buf1[384 * 288] = { 0 };//把14位的变成8位,那么数组长度减半 int i, j; long max = INT_MIN; long min = INT_MAX; //求出整幅图里面像素最大值和最小值 for (i = 0; i<384 * 288 * 2; i++){ if ((i + 1) % 2){ unsigned char a2 = pRawData[i]; unsigned char a1 = pRawData[i + 1]; long t1 = a1 << 8; long t2 = t1 + a2; if (t2 > max){ max = t2; } if (t2 < min){ min = t2; } } } //经过测试,可以得到最大像素值7660和最小像素值6167 //这个值和我用Matlab读取的是一致的 printf("max=%d", max); printf("min=%d", min); j = 0; for (i = 0; i<384 * 288 * 2; i++){ if ((i + 1) % 2){ unsigned char a2 = pRawData[i]; unsigned char a1 = pRawData[i + 1]; //按14到8位的归一化 long t1 = a1 << 8; long t2 = t1 + a2; //第一种直接舍弃的,忽略了 //unsigned char res = t2 / 64; //第二种方式 unsigned char res = (255 * (t2 - min)) / (max - min);//第三种 buf1[j] = res; j = j + 1; } } //下面就是完成raw数据到OpenCV数据结构的转换 Mat iMat(height, width, CV_8UC1, buf1); imwrite("D:\\images\\CV\\图片\\yuantu1.bmp", iMat); cin.get(); return 0; }

至此,整个过程结束了。图片如下。

转载请注明原文地址: https://www.6miu.com/read-81167.html

最新回复(0)