OpenImageIO:打开图像显示 window 分辨率而不是数据分辨率
OpenImageIO: Open image to the display window resolution instead of the data resolution
我正在使用 OpenImage Denoiser,它使用 OpenImageIO 加载 EXR 文件。
图像加载如下:
std::shared_ptr<ImageBuffer> loadImageOIIO(const std::string& filename, int channels)
{
auto in = OIIO::ImageInput::open(filename);
if (!in)
throw std::runtime_error("cannot open image file: " + filename);
const OIIO::ImageSpec& spec = in->spec();
if (channels == 0)
channels = spec.nchannels;
else if (spec.nchannels < channels)
throw std::runtime_error("not enough image channels");
auto image = std::make_shared<ImageBuffer>(spec.width, spec.height, channels);
if (!in->read_image(0, 0, 0, channels, OIIO::TypeDesc::FLOAT, image->getData()))
throw std::runtime_error("failed to read image data");
in->close();
#if OIIO_VERSION < 10903
OIIO::ImageInput::destroy(in);
#endif
return image;
}
但是,这会将图像裁剪到数据的边界框 window。因为我的图像有0值,所以这个图像比实际输入图像小。
如何获得具有显示器全分辨率的 ImageBuffer window?
上面几行分配的是一张宽x高x通道的图片,但问题是从OIIO的角度来看,spec.width和高度描述了数据window。所以你想从
开始
auto image = std::make_shared<ImageBuffer>(spec.full_width, spec.full_height, channels);
为“完整”(显示)分配缓冲区window。
然后你想调用 read_image 使用一些你保留为默认值的附加参数,这些参数可以让你给出明确的步骤。如果没有大步前进,它会假设您正在读入一个连续且大小刚好适合数据 window 的缓冲区。提供步幅会将像素偏移量以正确的间距写入正确的位置,并且您还希望将偏移量正确地写入缓冲区。我想你想要这个:
size_t pixelsize = channels * sizeof(float);
size_t scanlinesize = pixelsize * spec.full_width;
if (!in->read_image(/*subimage*/ 0, /*miplevel*/ 0, /*chbegin*/ 0, /*chend*/ channels,
OIIO::TypeDesc::FLOAT,
image->getData() + spec.x * pixelsize + spec.y * scanlinesize,
/*xstride*/ pixelsize, /*ystride*/ scanlinesize)
不要忘记先将缓冲区归零,因为现在 read_image 调用不会覆盖所有缓冲区值,只会覆盖具有数据像素的缓冲区值!
现在,即使这样也不是很普遍。我通过假设您的显示 window(OIIO 称之为“完整”)的原点为 (0,0) 并且与没有黑色像素的图像的数据 window 相同来简化-- 也就是说,data 和 full 的不同只是因为“裁剪”到非零区域。但实际上,有可能(在生产中经常出现)数据 windows 比显示 window 大 ,这意味着您正在计算“过扫描” " 区域(包括让数据 window 可能延伸到负像素索引)。如果可能是这种情况,并且您需要覆盖所有可能的基础,那么您可能需要分配一个图像,该图像是显示和数据 windows 之间重叠的并集,并调整上面的代码来处理这个问题正确。
我正在使用 OpenImage Denoiser,它使用 OpenImageIO 加载 EXR 文件。
图像加载如下:
std::shared_ptr<ImageBuffer> loadImageOIIO(const std::string& filename, int channels)
{
auto in = OIIO::ImageInput::open(filename);
if (!in)
throw std::runtime_error("cannot open image file: " + filename);
const OIIO::ImageSpec& spec = in->spec();
if (channels == 0)
channels = spec.nchannels;
else if (spec.nchannels < channels)
throw std::runtime_error("not enough image channels");
auto image = std::make_shared<ImageBuffer>(spec.width, spec.height, channels);
if (!in->read_image(0, 0, 0, channels, OIIO::TypeDesc::FLOAT, image->getData()))
throw std::runtime_error("failed to read image data");
in->close();
#if OIIO_VERSION < 10903
OIIO::ImageInput::destroy(in);
#endif
return image;
}
但是,这会将图像裁剪到数据的边界框 window。因为我的图像有0值,所以这个图像比实际输入图像小。
如何获得具有显示器全分辨率的 ImageBuffer window?
上面几行分配的是一张宽x高x通道的图片,但问题是从OIIO的角度来看,spec.width和高度描述了数据window。所以你想从
开始auto image = std::make_shared<ImageBuffer>(spec.full_width, spec.full_height, channels);
为“完整”(显示)分配缓冲区window。
然后你想调用 read_image 使用一些你保留为默认值的附加参数,这些参数可以让你给出明确的步骤。如果没有大步前进,它会假设您正在读入一个连续且大小刚好适合数据 window 的缓冲区。提供步幅会将像素偏移量以正确的间距写入正确的位置,并且您还希望将偏移量正确地写入缓冲区。我想你想要这个:
size_t pixelsize = channels * sizeof(float);
size_t scanlinesize = pixelsize * spec.full_width;
if (!in->read_image(/*subimage*/ 0, /*miplevel*/ 0, /*chbegin*/ 0, /*chend*/ channels,
OIIO::TypeDesc::FLOAT,
image->getData() + spec.x * pixelsize + spec.y * scanlinesize,
/*xstride*/ pixelsize, /*ystride*/ scanlinesize)
不要忘记先将缓冲区归零,因为现在 read_image 调用不会覆盖所有缓冲区值,只会覆盖具有数据像素的缓冲区值!
现在,即使这样也不是很普遍。我通过假设您的显示 window(OIIO 称之为“完整”)的原点为 (0,0) 并且与没有黑色像素的图像的数据 window 相同来简化-- 也就是说,data 和 full 的不同只是因为“裁剪”到非零区域。但实际上,有可能(在生产中经常出现)数据 windows 比显示 window 大 ,这意味着您正在计算“过扫描” " 区域(包括让数据 window 可能延伸到负像素索引)。如果可能是这种情况,并且您需要覆盖所有可能的基础,那么您可能需要分配一个图像,该图像是显示和数据 windows 之间重叠的并集,并调整上面的代码来处理这个问题正确。