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

时间:2024-03-28 20:18:19

环境: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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

注意:为什么要求最大值和最小值?这是因为我的是红外探测的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);
  • 1
  • 2

参数介绍:
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像素值
  • 1
  • 2
  • 3
  • 4

公式如下:
raw格式数据转BMP格式(OpenCV)
这样处理以后,对比度明显增强。

我的代码如下:

//使用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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

至此,整个过程结束了。图片如下。
raw格式数据转BMP格式(OpenCV)

环境:Win7+VS2013+OpenCV2.4.13