1

我正在使用 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;
  }

但是,这会将图像裁剪到数据窗口的边界框。因为我的图像有 0 个值,所以这个图像比实际输入的图像小。

如何获得具有显示窗口全分辨率的 ImageBuffer?

4

1 回答 1

1

上面的行是分配一个宽 x 高 x 通道的图像,但问题是从 OIIO 的角度来看,spec.width 和 height 描述了数据窗口。所以你想从

auto image = std::make_shared<ImageBuffer>(spec.full_width, spec.full_height, channels);

这为“完整”(显示)窗口分配了一个缓冲区。

然后,您想使用您保留为默认值的一些附加参数调用 read_image,这些参数可让您显式跨步。如果没有跨步,它假定您正在读取一个连续的缓冲区,并且大小正好适合数据窗口。提供步幅会将像素偏移写入具有正确间距的正确位置,并且您还希望正确偏移到缓冲区中。我想你想要这个:

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 调用不会覆盖所有缓冲区值,只会覆盖具有数据像素的值!

现在,即使这也不是很普遍。我通过假设您的显示窗口(OIIO 称为“完整”)的原点为 (0,0) 并且与没有黑色像素的图像的数据窗口相同 - 即数据和完整仅因为“裁剪”到非零区域而有所不同。但实际上,有可能(并且在生产中经常出现)具有大于显示窗口的数据窗口,这意味着您正在计算“过扫描”区域(包括让数据窗口可能扩展到负像素索引)。如果可能是这种情况,并且您需要覆盖所有可能的基础,那么您可能需要分配一个图像,该图像是显示窗口和数据窗口之间重叠的联合,并调整上面的代码以正确处理此问题。

于 2021-01-09T22:08:25.123 回答