在C中读取二进制签名的简短问题

时间:2018-09-22 10:39:42

标签: c

我正在学习C作为业余爱好者。作为一个有趣的项目,我决定编写一个.hgt文件阅读器。 hgt文件是地球高程文件。 我发现有关此文件格式的信息很少:https://dds.cr.usgs.gov/srtm/version2_1/Documentation/Quickstart.pdf

您可以在此处找到整个星球的文件: http://viewfinderpanoramas.org/Coverage%20map%20viewfinderpanoramas_org3.htm

但似乎很简单:他们说的是一个有符号的两个字节整数的列表。

我发现两个字节的整数可以很好地用“ signed short”类型表示,对吗?在我的代码中,您将看到我使用了int_16t(我尝试过在签名短裤出现问题时相信它们的范围相同) 无论如何,我打开文件,将数据转储到数组中,然后将其写入bmp文件。

起初,我认为效果很好,但是那是因为我正在查看地势高度非常低的部分的结果。当我尝试渲染一些与山脉相关的文件时,下图显示了问题。

到目前为止,以下是我的代码。 我很确定问题出在开始时,是在读取数据时,但我不知道了。

我希望获得一些帮助。 resulting image file

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <math.h>

int main(int argc, char *argv[]) {
    if(argc < 2) {
        printf("I need a hgt file path as a paramater\n");
        return 0;
    } else {
        const int DIM = 1201;
        FILE *fp;
        int16_t *elevation_buffer;

        elevation_buffer = malloc(sizeof(int16_t) * DIM * DIM); // 2 bytes integers

        fp = fopen(argv[1], "rb");

        /* Seek to the beginning of the file */
        fseek(fp, 0, SEEK_SET);

        /* read elevation data from HGT file */
        fread(elevation_buffer, sizeof(int16_t), DIM*DIM, fp);

        fclose(fp);       

        printf("sizeof signed short int : %d\n", sizeof(signed short int));
        printf("sizeof int16_t : %d\n", sizeof(int16_t));

        /*  creating a bmp file to visualize elevation tile*/
        int w = DIM;
        int h = DIM;

        int x,y,r,g,b;

        FILE *f;

        unsigned char *img = NULL;
        int filesize = 54 + 3 * w * h; //w is your image width, h is image height, both int

        img = (unsigned char *)malloc(3 * w * h);
        memset(img, 0, 3 * w * h);

        for (int i = 0; i < w; i++)
        {
            for (int j = 0; j < h; j++)
            {
                x = i;
                y = (h - 1) - j;
                float elevation = (elevation_buffer[x + y * w] - INT16_MIN) / (float)(INT16_MAX - INT16_MIN);
                r = (int)(elevation * 255);
                float freq = 100.0f;

                if (r > 255) {
                    r = 255;
                } else if(r < 0) {
                    r = 0;
                }
                g = r;
                b = r;
                img[(x + y * w) * 3 + 2] = (unsigned char)(r);
                img[(x + y * w) * 3 + 1] = (unsigned char)(g);
                img[(x + y * w) * 3 + 0] = (unsigned char)(b);
            }
        }

        unsigned char bmpfileheader[14] = {'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0};
        unsigned char bmpinfoheader[40] = {40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0};
        unsigned char bmppad[3] = {0, 0, 0};

        bmpfileheader[2] = (unsigned char)(filesize);
        bmpfileheader[3] = (unsigned char)(filesize >> 8);
        bmpfileheader[4] = (unsigned char)(filesize >> 16);
        bmpfileheader[5] = (unsigned char)(filesize >> 24);

        bmpinfoheader[4] = (unsigned char)(w);
        bmpinfoheader[5] = (unsigned char)(w >> 8);
        bmpinfoheader[6] = (unsigned char)(w >> 16);
        bmpinfoheader[7] = (unsigned char)(w >> 24);
        bmpinfoheader[8] = (unsigned char)(h);
        bmpinfoheader[9] = (unsigned char)(h >> 8);
        bmpinfoheader[10] = (unsigned char)(h >> 16);
        bmpinfoheader[11] = (unsigned char)(h >> 24);

        f = fopen("img.bmp", "wb");
        fwrite(bmpfileheader, 1, 14, f);
        fwrite(bmpinfoheader, 1, 40, f);
        for (int i = 0; i < h; i++)
        {
            fwrite(img + (w * (h - i - 1) * 3), 3, w, f);
            fwrite(bmppad, 1, (4 - (w * 3) % 4) % 4, f);
        }

        free(img);
        free(elevation_buffer);
        fclose(f);

        return 0;
    }
}

1 个答案:

答案 0 :(得分:1)

您应该考虑字节顺序...您的输入文件格式被指定为包含 big endian 字节顺序的字节。

作为一个简单的解决方法,您可以仅检查字节顺序并根据需要反转读入的数据。答案https://stackoverflow.com/a/8571139/5265292解释了一种如何在系统上检测字节顺序的方法。

// ...

fread(elevation_buffer, sizeof(int16_t), DIM*DIM, fp);

fclose(fp);       

int byteOrderCheck = 1;
if (*(char *)&byteOrderCheck == 1)
{
    // need to revert byte order for little endian
    for (int i = 0; i < DIM*DIM; ++i)
    {
        elevation_buffer[i] = (int16_t)reverse_byte_order((uint16_t)elevation_buffer[i]);
    }
}

// ...

函数reverse_byte_order

uint16_t reverse_byte_order(uint16_t num)
{
    return ((num & 0xff) << 8) | (num >> 8);
}

请注意,这未经测试,您可能需要更改一些详细信息。