毕设—高动态范围图像渲染算法之HDR文件的读取(二)
程序员文章站
2024-03-02 23:52:16
...
色调映射技术的根本目的是把无法直接在显示器上显示的hdr图像转化成可以直接显示的RGB图像。(hdr图像在一些外网上可以免费下载,不想找的可以直接下载:https://download.csdn.net/download/A_ACM/12449991)如图所示:
明白了要干什么,那我们就要开始干活了。
首先我们用Hex Editor Neo软件打开hdr图像,看看这种文件内都有啥内容。
在文件头部分我们看到:该文件采用的是Radiance RGB编码格式,每个像素点占32位,图像的长宽分别是768、512。数据部分就是每个像素点的数据了。
在读取hdr文件之前,我们要了解一下Radiance RGB编码格式(这里就不进行介绍了)。
因为是32位的,所以每个像素点占32位,每8位的信息如下。每个字节代表的意思就是字面上的意思。
知道了RGBE的数据大小,知道了是Radiance RGB编码格式,我们就可以计算出HDR图像所代表场景的信息。计算公式如图。
好了,现在知道了场景信息还原函数、每个像素点的位置信息、每个像素点的数据。现在我们开始编写代码。(下面的代码来源于一个外网,并不是自己编写的)。
hdrloader.h文件如下:
class HDRLoaderResult {
public:
int width, height;
float *cols;
};
class HDRLoader {
public:
static int load(const char *fileName, HDRLoaderResult &res);
};
hdrloader.cpp文件如下:
#include "hdrloader.h"
#include <math.h>
#include <memory.h>
#include <stdio.h>
typedef unsigned char RGBE[4];
#define R 0
#define G 1
#define B 2
#define E 3
#define MINELEN 8
#define MAXELEN 0x7fff
//定义函数
static void workOnRGBE(RGBE *scan, int len, float *cols);
static bool decrunch(RGBE *scanline, int len, FILE *file);
static bool oldDecrunch(RGBE *scanline, int len, FILE *file);
int HDRLoader::load(const char *fileName, HDRLoaderResult &res)
{
int i;
char str[200];
FILE *file;
//读文件
file = fopen(fileName, "rb");
if (!file)
{
return 0;
}
//判断文件编码类型
fread(str, 10, 1, file);
if (memcmp(str, "#?RADIANCE", 10)) {
fclose(file);
return -1;
}
fseek(file, 1, SEEK_CUR);
char cmd[200];
i = 0;
char c = 0, oldc;
//读取无用信息,并舍弃
while(true) {
oldc = c;
c = fgetc(file);
if (c == 0xa && oldc == 0xa)
break;
cmd[i++] = c;
}
char reso[200];
i = 0;
//读取图像长宽
while(true) {
c = fgetc(file);
reso[i++] = c;
printf("%c\n",c);
if (c == 0xa)
break;
}
int w, h;
if (!sscanf(reso, "-Y %ld +X %ld", &h, &w)) {
fclose(file);
return -2;
}
//把图像的长宽赋值给结构体
res.width = w;
res.height = h;
//定义图像数据数组
float *cols = new float[w * h * 3];
res.cols = cols;
RGBE *scanline = new RGBE[w];
if (!scanline) {
fclose(file);
return -3;
}
//每次对一行进行处理,一共处理h行
for (int y = h - 1; y >= 0; y--) {
if (decrunch(scanline, w, file) == false)
break;
workOnRGBE(scanline, w, cols);
cols += w * 3;
}
delete [] scanline;
//关闭文件
fclose(file);
return 1;
}
/*
HDRI文件三分量数据还原成场景三分量
*/
float convertComponent(int expo, int val)
{
float v = val / 256.0f;
float d = (float) pow(2.0, expo);
return v * d;
}
/*
计算得出HDRI文件的RGBE四分量
*/
void workOnRGBE(RGBE *scan, int len, float *cols)
{
while (len-- > 0) {
int expo = scan[0][E] - 128;
cols[0] = convertComponent(expo, scan[0][R]);
cols[1] = convertComponent(expo, scan[0][G]);
cols[2] = convertComponent(expo, scan[0][B]);
cols += 3;
scan++;
}
}
/*
对每一行数据进行读取
*/
bool decrunch(RGBE *scanline, int len, FILE *file)
{
int i, j;
//判断是否大于两字节
if (len < MINELEN || len > MAXELEN)
return oldDecrunch(scanline, len, file);
i = fgetc(file);
if (i != 2) {
fseek(file, -1, SEEK_CUR);
return oldDecrunch(scanline, len, file);
}
//如果就剩俩字节,默认为GB
scanline[0][G] = fgetc(file);
scanline[0][B] = fgetc(file);
i = fgetc(file);
if (scanline[0][G] != 2 || scanline[0][B] & 128) {
scanline[0][R] = 2;
scanline[0][E] = i;
return oldDecrunch(scanline + 1, len - 1, file);
}
//分四次读取
for (i = 0; i < 4; i++) {
for (j = 0; j < len; ) {
unsigned char code = fgetc(file);
if (code > 128) {
code &= 127;
unsigned char val = fgetc(file);
while (code--)
scanline[j++][i] = val;
}
else {
while(code--)
scanline[j++][i] = fgetc(file);
}
}
}
return feof(file) ? false : true;
}
bool oldDecrunch(RGBE *scanline, int len, FILE *file)
{
int i;
int rshift = 0;
//读取RGBE四分量数据
while (len > 0) {
scanline[0][R] = fgetc(file);
scanline[0][G] = fgetc(file);
scanline[0][B] = fgetc(file);
scanline[0][E] = fgetc(file);
if (feof(file))
return false;
if (scanline[0][R] == 1 &&
scanline[0][G] == 1 &&
scanline[0][B] == 1) {
for (i = scanline[0][E] << rshift; i > 0; i--) {
memcpy(&scanline[0][0], &scanline[-1][0], 4);
scanline++;
len--;
}
rshift += 8;
}
else {
scanline++;
len--;
rshift = 0;
}
}
return true;
}
使用方式:
int rows,cols;
//fileName是你的hdr文件的路径
HDRLoaderResult result;
int ret = HDRLoader::load(fileName, result);
//图像长宽
rows=result.height;
cols=result.width;
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
//场景的RGB信息
R=result.cols[i*cols*3+j*3+0];
G=result.cols[i*cols*3+j*3+1];
B=result.cols[i*cols*3+j*3+2];
}
}
好了,现在场景的RGB三分量和长宽信息都提取出来了,下面进行色调映射。
上一篇: 八. 内存池规划
下一篇: Linux操作系统中的内存映射