我正在学习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文件。
起初,我认为效果很好,但是那是因为我正在查看地势高度非常低的部分的结果。当我尝试渲染一些与山脉相关的文件时,下图显示了问题。
到目前为止,以下是我的代码。 我很确定问题出在开始时,是在读取数据时,但我不知道了。
#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;
}
}
答案 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);
}
请注意,这未经测试,您可能需要更改一些详细信息。