将 exr/pfm 保存为小端
Save exr/pfm as little endian
我将一个 bmp 文件加载到 CImg 对象中,并将其保存到 pfm 文件中。成功的。这个 .pfm 文件我正在将它用于另一个库,但是这个库 不接受 big-endian,只是 小端.
CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.normalize(0.0, 1.0);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
那么,如何将 bmp 文件保存为 pfm 文件 为 little endian,not big endian ..这可能吗?
稍后编辑:
我已经检查了 .pfm 头文件中的前 5 个元素。这是 没有 invert_endianness:
的结果
CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.normalize(0.0, 1.0);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
PF
512
768
1.0
=øøù=€€=‘>
这是 与 invert_endianness:
的结果
CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.invert_endianness();
image.normalize(0.0, 1.0);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
PF
512
768
1.0
?yôx?!ù=‚ì:„ç‹?
结果是一样的。
这绝对不是一个正确的答案,但暂时可以作为一种解决方法。
我没有找到如何使用 CImg
s 函数正确反转字节序,所以我修改了生成的文件。这是一个黑客。结果在 GIMP 中打开很好,看起来非常接近原始图像,但我不能说它是否适用于您正在使用的库。也许值得一试。
代码中注释:
#include "CImg/CImg.h"
#include <algorithm>
#include <filesystem> // >= C++17 must be selected as Language Standard
#include <ios>
#include <iostream>
#include <iterator>
#include <fstream>
#include <string>
using namespace cimg_library;
namespace fs = std::filesystem;
// a class to remove temporary files
class remove_after_use {
public:
remove_after_use(const std::string& filename) : name(filename) {}
remove_after_use(const remove_after_use&) = delete;
remove_after_use& operator=(const remove_after_use&) = delete;
const char* c_str() const { return name.c_str(); }
operator std::string const& () const { return name; }
~remove_after_use() {
try {
fs::remove(name);
}
catch (const std::exception & ex) {
std::cerr << "remove_after_use: " << ex.what() << "\n";
}
}
private:
std::string name;
};
// The function to hack the file saved by CImg
template<typename T>
bool save_pfm_endianness_inverted(const T& img, const std::string& filename) {
remove_after_use tempfile("tmp.pfm");
// get CImg's endianness inverted image and save it to a temporary file
img.get_invert_endianness().save_pfm(tempfile.c_str());
// open the final file
std::ofstream os(filename, std::ios::binary);
// read "tmp.pfm" and modify
// The Scale Factor / Endianness line
if (std::ifstream is; os && (is = std::ifstream(tempfile, std::ios::binary))) {
std::string lines[3];
// Read the 3 PFM header lines as they happen to be formatted by
// CImg. Will maybe not work with another library.
size_t co = 0;
for (; co < std::size(lines) && std::getline(is, lines[co]); ++co);
if (co == std::size(lines)) { // success
// write the first two lines back unharmed:
os << lines[0] << '\n' << lines[1] << '\n';
if (lines[2].empty()) {
std::cerr << "something is wrong with the pfm header\n";
return false;
}
// add a '-' if it's missing, remove it if it's there:
if (lines[2][0] == '-') { // remove the - to invert
os << lines[2].substr(1);
}
else { // add a - to invert
os << '-' << lines[2] << '\n';
}
// copy all the rest as-is:
std::copy(std::istreambuf_iterator<char>(is),
std::istreambuf_iterator<char>{},
std::ostreambuf_iterator<char>(os));
}
else {
std::cerr << "failed reading pfm header\n";
return false;
}
}
else {
std::cerr << "opening files failed\n";
return false;
}
return true;
}
int main()
{
CImg<float> img("memorial.bmp");
img.normalize(0.f, 1.f);
std::cout << "saved ok: " << std::boolalpha
<< save_pfm_endianness_inverted(img, "memorial.pfm") << "\n";
}
想用经典的 C++ 风格解决同样的问题(作为语言的缘故),我写道:
BOOL CMyDoc::SavePfmEndiannessInverted(CImg<float>& img, const CString sFileName)
{
CString sDrive, sDir;
_splitpath(sFileName, sDrive.GetBuffer(), sDir.GetBuffer(), NULL, NULL);
CString sTemp;
sTemp.Format(_T("%s%sTemp.tmp"), sDrive, sDir);
sDrive.ReleaseBuffer();
sDir.ReleaseBuffer();
CRemoveAfterUse TempFile(sTemp);
img.get_invert_endianness().save_pfm(TempFile.c_str());
CFile fileTemp;
if (! fileTemp.Open(sTemp, CFile::typeBinary))
return FALSE;
char c;
UINT nRead = 0;
int nCount = 0;
ULONGLONG nPosition = 0;
CString sScale;
CByteArray arrHeader, arrData;
do
{
nRead = fileTemp.Read((char*)&c, sizeof(char));
switch (nCount)
{
case 0:
case 1:
arrHeader.Add(static_cast<BYTE>(c));
break;
case 2: // retrieve the '1.0' string
sScale += c;
break;
}
if ('\n' == c) // is new line
{
nCount++;
}
if (nCount >= 3) // read the header, so go out
{
nPosition = fileTemp.GetPosition();
break;
}
}while (nRead > 0);
if (nPosition > 1)
{
arrData.SetSize(fileTemp.GetLength() - nPosition);
fileTemp.Read(arrData.GetData(), (UINT)arrData.GetSize());
}
fileTemp.Close();
CFile file;
if (! file.Open(sFileName, CFile::typeBinary | CFile::modeCreate | CFile::modeReadWrite))
return FALSE;
CByteArray arrTemp;
ConvertCStringToByteArray(sScale, arrTemp);
arrHeader.Append(arrTemp);
arrHeader.Append(arrData);
file.Write(arrHeader.GetData(), (UINT)arrHeader.GetSize());
file.Close();
return TRUE;
}
但是好像不行,因为图像结果比较暗
我做错了什么?代码在我看来非常清晰,但仍然没有按预期工作...
当然,这种方法效率较低,我知道,但正如我之前所说,只是为了语言。
我认为我的代码没有问题:)
这是试用版:
CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.normalize(0.0f, 1.0f);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
image.get_invert_endianness().save(_T("D:\Temp\memorial_inverted.pfm"));
而 memorial.pfm 看起来像这样:
和 memorial_inverted.pfm 看起来像这样:
我将一个 bmp 文件加载到 CImg 对象中,并将其保存到 pfm 文件中。成功的。这个 .pfm 文件我正在将它用于另一个库,但是这个库 不接受 big-endian,只是 小端.
CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.normalize(0.0, 1.0);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
那么,如何将 bmp 文件保存为 pfm 文件 为 little endian,not big endian ..这可能吗?
稍后编辑:
我已经检查了 .pfm 头文件中的前 5 个元素。这是 没有 invert_endianness:
的结果CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.normalize(0.0, 1.0);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
PF
512
768
1.0
=øøù=€€=‘>
这是 与 invert_endianness:
的结果CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.invert_endianness();
image.normalize(0.0, 1.0);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
PF
512
768
1.0
?yôx?!ù=‚ì:„ç‹?
结果是一样的。
这绝对不是一个正确的答案,但暂时可以作为一种解决方法。
我没有找到如何使用 CImg
s 函数正确反转字节序,所以我修改了生成的文件。这是一个黑客。结果在 GIMP 中打开很好,看起来非常接近原始图像,但我不能说它是否适用于您正在使用的库。也许值得一试。
代码中注释:
#include "CImg/CImg.h"
#include <algorithm>
#include <filesystem> // >= C++17 must be selected as Language Standard
#include <ios>
#include <iostream>
#include <iterator>
#include <fstream>
#include <string>
using namespace cimg_library;
namespace fs = std::filesystem;
// a class to remove temporary files
class remove_after_use {
public:
remove_after_use(const std::string& filename) : name(filename) {}
remove_after_use(const remove_after_use&) = delete;
remove_after_use& operator=(const remove_after_use&) = delete;
const char* c_str() const { return name.c_str(); }
operator std::string const& () const { return name; }
~remove_after_use() {
try {
fs::remove(name);
}
catch (const std::exception & ex) {
std::cerr << "remove_after_use: " << ex.what() << "\n";
}
}
private:
std::string name;
};
// The function to hack the file saved by CImg
template<typename T>
bool save_pfm_endianness_inverted(const T& img, const std::string& filename) {
remove_after_use tempfile("tmp.pfm");
// get CImg's endianness inverted image and save it to a temporary file
img.get_invert_endianness().save_pfm(tempfile.c_str());
// open the final file
std::ofstream os(filename, std::ios::binary);
// read "tmp.pfm" and modify
// The Scale Factor / Endianness line
if (std::ifstream is; os && (is = std::ifstream(tempfile, std::ios::binary))) {
std::string lines[3];
// Read the 3 PFM header lines as they happen to be formatted by
// CImg. Will maybe not work with another library.
size_t co = 0;
for (; co < std::size(lines) && std::getline(is, lines[co]); ++co);
if (co == std::size(lines)) { // success
// write the first two lines back unharmed:
os << lines[0] << '\n' << lines[1] << '\n';
if (lines[2].empty()) {
std::cerr << "something is wrong with the pfm header\n";
return false;
}
// add a '-' if it's missing, remove it if it's there:
if (lines[2][0] == '-') { // remove the - to invert
os << lines[2].substr(1);
}
else { // add a - to invert
os << '-' << lines[2] << '\n';
}
// copy all the rest as-is:
std::copy(std::istreambuf_iterator<char>(is),
std::istreambuf_iterator<char>{},
std::ostreambuf_iterator<char>(os));
}
else {
std::cerr << "failed reading pfm header\n";
return false;
}
}
else {
std::cerr << "opening files failed\n";
return false;
}
return true;
}
int main()
{
CImg<float> img("memorial.bmp");
img.normalize(0.f, 1.f);
std::cout << "saved ok: " << std::boolalpha
<< save_pfm_endianness_inverted(img, "memorial.pfm") << "\n";
}
想用经典的 C++ 风格解决同样的问题(作为语言的缘故),我写道:
BOOL CMyDoc::SavePfmEndiannessInverted(CImg<float>& img, const CString sFileName)
{
CString sDrive, sDir;
_splitpath(sFileName, sDrive.GetBuffer(), sDir.GetBuffer(), NULL, NULL);
CString sTemp;
sTemp.Format(_T("%s%sTemp.tmp"), sDrive, sDir);
sDrive.ReleaseBuffer();
sDir.ReleaseBuffer();
CRemoveAfterUse TempFile(sTemp);
img.get_invert_endianness().save_pfm(TempFile.c_str());
CFile fileTemp;
if (! fileTemp.Open(sTemp, CFile::typeBinary))
return FALSE;
char c;
UINT nRead = 0;
int nCount = 0;
ULONGLONG nPosition = 0;
CString sScale;
CByteArray arrHeader, arrData;
do
{
nRead = fileTemp.Read((char*)&c, sizeof(char));
switch (nCount)
{
case 0:
case 1:
arrHeader.Add(static_cast<BYTE>(c));
break;
case 2: // retrieve the '1.0' string
sScale += c;
break;
}
if ('\n' == c) // is new line
{
nCount++;
}
if (nCount >= 3) // read the header, so go out
{
nPosition = fileTemp.GetPosition();
break;
}
}while (nRead > 0);
if (nPosition > 1)
{
arrData.SetSize(fileTemp.GetLength() - nPosition);
fileTemp.Read(arrData.GetData(), (UINT)arrData.GetSize());
}
fileTemp.Close();
CFile file;
if (! file.Open(sFileName, CFile::typeBinary | CFile::modeCreate | CFile::modeReadWrite))
return FALSE;
CByteArray arrTemp;
ConvertCStringToByteArray(sScale, arrTemp);
arrHeader.Append(arrTemp);
arrHeader.Append(arrData);
file.Write(arrHeader.GetData(), (UINT)arrHeader.GetSize());
file.Close();
return TRUE;
}
但是好像不行,因为图像结果比较暗
我做错了什么?代码在我看来非常清晰,但仍然没有按预期工作...
当然,这种方法效率较低,我知道,但正如我之前所说,只是为了语言。
我认为我的代码没有问题:)
这是试用版:
CImg<float> image;
image.load_bmp(_T("D:\Temp\memorial.bmp"));
image.normalize(0.0f, 1.0f);
image.save_pfm(_T("D:\Temp\memorial.pfm"));
image.get_invert_endianness().save(_T("D:\Temp\memorial_inverted.pfm"));
而 memorial.pfm 看起来像这样:
和 memorial_inverted.pfm 看起来像这样: