欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

毕设—高动态范围图像渲染算法之HDR文件的读取(二)

程序员文章站 2024-03-02 23:52:16
...

色调映射技术的根本目的是把无法直接在显示器上显示的hdr图像转化成可以直接显示的RGB图像。(hdr图像在一些外网上可以免费下载,不想找的可以直接下载:https://download.csdn.net/download/A_ACM/12449991)如图所示:
毕设—高动态范围图像渲染算法之HDR文件的读取(二)明白了要干什么,那我们就要开始干活了。

首先我们用Hex Editor Neo软件打开hdr图像,看看这种文件内都有啥内容。

毕设—高动态范围图像渲染算法之HDR文件的读取(二)在文件头部分我们看到:该文件采用的是Radiance RGB编码格式,每个像素点占32位,图像的长宽分别是768、512。数据部分就是每个像素点的数据了。

在读取hdr文件之前,我们要了解一下Radiance RGB编码格式(这里就不进行介绍了)。

因为是32位的,所以每个像素点占32位,每8位的信息如下。每个字节代表的意思就是字面上的意思。
毕设—高动态范围图像渲染算法之HDR文件的读取(二)
知道了RGBE的数据大小,知道了是Radiance RGB编码格式,我们就可以计算出HDR图像所代表场景的信息。计算公式如图。
毕设—高动态范围图像渲染算法之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三分量和长宽信息都提取出来了,下面进行色调映射。

相关标签: 毕设 色调映射