英文:
Try to build and get Open Image Denoise working with Bazel
问题
目前,我尝试让 Open Image Denoise 与 Bazel 配合使用。因此,我实现了 rules_oidn。
要尝试它,你可以执行以下命令:
git clone https://github.com/Vertexwahn/rules_oidn.git
cd rules_oidn
cd tests
在 Ubuntu 22.04 上运行示例:
bazel run --config=gcc11 //:example
在 Visual Studio 2022 上运行示例:
bazel run --config=vs2022 //:example
这个示例会对嘈杂的图像进行去噪。
...
int main() {
cout << "Simple denoising example" << endl;
Image3f color = load_image_openexr("data/cornel_box.naive_diffuse.box_filter.spp128.embree.exr");
...
Image3f out{color.width(), color.height()};
...
float* colorPtr = color.data();
...
float* outputPtr = out.data();
int width = out.width();
int height = out.height();
oidn::DeviceRef device = oidn::newDevice();
device.set("verbose", 1);
device.commit();
// 创建用于去噪美丽(彩色)图像的滤镜,可以使用可选的辅助图像
oidn::FilterRef filter = device.newFilter("RT"); // 通用射线追踪滤镜
filter.setImage("color", colorPtr, oidn::Format::Float3, width, height); // 美丽图像
...
filter.setImage("output", outputPtr, oidn::Format::Float3, width, height); // 去噪后的美丽图像
filter.set("hdr", true); // 美丽图像为HDR
filter.commit();
// 过滤图像
filter.execute();
// 检查错误
const char* errorMessage;
if (device.getError(errorMessage) != oidn::Error::None) {
std::cout << "Error: " << errorMessage << std::endl;
}
store_open_exr("denoised.exr", out);
return 0;
}
不幸的是,去噪后的图像包含黑色条纹。
我使用 https://github.com/DeclanRussell/IntelOIDenoiser 测试了相同的输入,并获得了预期的结果(没有黑色条纹)。因此,问题必须出现在我的 OIDN 的 Bazel 构建或其相关支持中。
如果我选择一个常量颜色图像,例如:
// 出于调试目的,颜色图像可以初始化为常量颜色
if (true) {
for (int x = 0; x < color.width(); ++x) {
for (int y = 0; y < color.height(); ++y) {
color.set_pixel(x,y,.5f, .5f, .5f);
}
}
}
我仍然得到黑色条纹。
目前,我还没有找到解决问题的良好策略。欢迎任何关于解决问题的提示或解决方案。
我还创建了一个 oidn 分支,其中包含一个“直接” bazelization 版本,几乎与 rules_oidn 相似:https://github.com/Vertexwahn/oidn/tree/add-bazel-support。在这个分支中,我对 oidnTest
进行了 Bazel 构建,其中包含一些测试用例,所有测试都成功。我还对 oidnDenoiser
进行了 Bazel 构建。你可以通过以下命令运行它:
the_cornell_box.pfm 在 oidn 存储库的 data 文件夹中
bazel run --config=gcc11 //:oidnDenoise -- --hdr /home/vertexwahn/Desktop/the_cornell_box.pfm -o /home/vertexwahn/Desktop/denoised.pfm
生成的文件 denoised.pfm
显示相同的黑色条纹。黑色条纹始终是 5 个像素宽,后面跟着 3 个看起来正常的彩色条纹。
不确定是否在将内存传递给 ISPC 或执行滤波操作等方面存在问题。由于我测试了不同的图像格式(OpenEXR、PFM),我认为错误不在于我存储图像的方式。
我开始调试并比较工作的 CMake/Windows 构建与不工作的 Bazel/Ubuntu 构建 - 但到目前为止,我没有找到明显的差异。
Image3f
类如下所示:
class Image3f {
public:
Image3f(const int width, const int height) : width_(width), height_(height) {
data_ = new float[width_ * height_ * 3];
for (int x = 0; x < width_; ++x) {
for (int y = 0; y < height_; ++y) {
auto r = 0.f;
auto g = 0.f;
auto b = 0.f;
data_[(width_ * y + x) * 3] = r;
data_[(width_ * y + x) * 3 + 1] = g;
data_[(width_ * y + x) * 3 + 2] = b;
}
}
}
void set_pixel(int x, int y, float red, float green, float blue) {
assert(x >= 0);
assert(y >= 0);
assert(x < width_);
assert(y < height_);
data_[(width_ * y + x) * 3] = red;
data_[(width_ * y + x) *
<details>
<summary>英文:</summary>
Currently, I try to get [Open Image Denoise](https://www.openimagedenoise.org/) to work with [Bazel](https://bazel.build/).
Therefore, I implemented [rules_oidn](https://github.com/Vertexwahn/rules_oidn).
To try it out, you can do a
```shell
git clone https://github.com/Vertexwahn/rules_oidn.git
cd rules_oidn
cd tests
Run example with Ubuntu 22.04:
bazel run --config=gcc11 //:example
Run example with Visual Studio 2022:
bazel run --config=vs2022 //:example
The example takes a noisy image and denoises it.
...
int main() {
cout << "Simple denoising example" << endl;
Image3f color = load_image_openexr("data/cornel_box.naive_diffuse.box_filter.spp128.embree.exr");
...
Image3f out{color.width(), color.height()};
...
float* colorPtr = color.data();
...
float* outputPtr = out.data();
int width = out.width();
int height = out.height();
oidn::DeviceRef device = oidn::newDevice();
device.set("verbose", 1);
device.commit();
// Create a filter for denoising a beauty (color) image using optional auxiliary images too
oidn::FilterRef filter = device.newFilter("RT"); // generic ray tracing filter
filter.setImage("color", colorPtr, oidn::Format::Float3, width, height); // beauty
...
filter.setImage("output", outputPtr, oidn::Format::Float3, width, height); // denoised beauty
filter.set("hdr", true); // beauty image is HDR
filter.commit();
// Filter the image
filter.execute();
// Check for errors
const char* errorMessage;
if (device.getError(errorMessage) != oidn::Error::None) {
std::cout << "Error: " << errorMessage << std::endl;
}
store_open_exr("denoised.exr", out);
return 0;
}
Unfortunately, the denoised image contains black stripes:
I tested the same input with https://github.com/DeclanRussell/IntelOIDenoiser and got the expected result (without black stripes). Therefore, the problem must be within my bazalization of OIDN or my supporting around it.
If I choose a constant color image, e.g.
// for debug reasons the color image can be initialized with a const color
if(true) {
for (int x = 0; x < color.width(); ++x) {
for (int y = 0; y < color.height(); ++y) {
color.set_pixel(x,y,.5f, .5f, .5f);
}
}
}
I also get black stripes.
Currently, I am missing a good strategy to find the issue.
Any hints or solutions to fix the issue are welcome.
I also created a branch of oidn that contains a "direct"
bazelization of oidn which is almost similar to rules_oidn: https://github.com/Vertexwahn/oidn/tree/add-bazel-support. In this branch I bazelized oidnTest
which contains a few test cases which all pass with success. I bazelized also oidnDenoiser
. You can run it via:
# the_cornell_box.pfm is in data folder in the oidn repo
bazel run --config=gcc11 //:oidnDenoise -- --hdr /home/vertexwahn/Desktop/the_cornell_box.pfm -o /home/vertexwahn/Desktop/denoised.pfm
The generated file denoised.pfm
shows the same black stripes. It seems that the black stripes are always 5 pixel wide followed by 3 correct looking color stripes.
Not sure if there are any problems with handing over memory to ISPC, or doing the Filter Operation, etc. Since I tested different image formats (OpenEXR, PFM), I assume that the error is not within my way of storing images.
I started to debug and compare the working CMake/Windows build with the non-working Bazel/Ubuntu build - but until now I did not find obvious differences:
The Image3f
class looks like this:
class Image3f {
public:
Image3f(const int width, const int height) : width_(width), height_(height) {
data_ = new float[width_ * height_ * 3];
for (int x = 0; x < width_; ++x) {
for (int y = 0; y < height_; ++y) {
auto r = 0.f;
auto g = 0.f;
auto b = 0.f;
data_[(width_ * y + x) * 3] = r;
data_[(width_ * y + x) * 3 + 1] = g;
data_[(width_ * y + x) * 3 + 2] = b;
}
}
}
void set_pixel(int x, int y, float red, float green, float blue) {
assert(x >= 0);
assert(y >= 0);
assert(x < width_);
assert(y < height_);
data_[(width_ * y + x) * 3] = red;
data_[(width_ * y + x) * 3 + 1] = green;
data_[(width_ * y + x) * 3 + 2] = blue;
}
float *data() const {
return data_;
}
float *data() {
return data_;
}
int width() const {
return width_;
}
int height() const {
return height_;
}
private:
int width_ = 0;
int height_ = 0;
float *data_;
};
More details on the Bazelization
Open Image Denoise v1.4.3 has some dependencies. It depends on oneTBB, oneDNN, makes use of ISPC and the Intel Implicit SPMD Program Compiler, and uses Python to generate C++ Code from trained weights. All of this needs to be supported via Bazel to get a Open Image Denoise Bazel build working.
oneTBB was already bazelized by me in Mid of the year 2021. I have a CI build job that pulls the latest oneTBB master and does a Bazel test build with it. At the time of this writing, this still works. The test build uses oneTBB in combination Embree. I have also a standalone demo that shows how to use oneTBB with Bazel alone.
To support ISPC with Bazel I created rules_ispc. To use ISPC with C++ you need to write ISPC programs that are then translated by the ISPC compiler. The ISPC-compiled program can be invoked via C++. All the scaffolding to download the correct ISPC compiler version, translation of ISPC programs, and linking them to C++ executables is part of rules_ispc.
An overview of the whole dependencies can be seen here:
Generated via:
bazel query --noimplicit_deps 'deps(//:example) - @com_openexr//...:* - @Imath//...:* - @net_zlib_zlib//...:*' --output graph > simplified_graph.in
dot -Tpng < simplified_graph.in > simple_graph.png
A failing test case for the Bazel Build (but working on CMake side)
If I add
// -----------------------------------------------------------------------------
TEST_CASE("denoise constant image", "[sanitization]")
{
DeviceRef device = newDevice();
REQUIRE(bool(device));
device.commit();
REQUIRE(device.getError() == Error::None);
const int W = 1024;
const int H = 1024;
FilterRef filter = device.newFilter("RT");
REQUIRE(bool(filter));
std::shared_ptr<ImageBuffer> input = makeConstImage(device, W, H, 3, 0.5f);
ImageBuffer output(device, W, H, 3);
setFilterImage(filter, "color", *input);
setFilterImage(filter, "output", output);
filter.set("hdr", true);
filter.commit();
REQUIRE(device.getError() == Error::None);
filter.execute();
REQUIRE(device.getError() == Error::None);
REQUIRE_THAT(((float*)output.bufferPtr)[0], Catch::Matchers::WithinAbs(0.4787f, 1e-4));
REQUIRE(((float*)output.bufferPtr)[9] != 0.f); // This is violated on Bazel build but not CMake build
for (int i = 0; i < output.size(); ++i) {
REQUIRE(((float*)output.bufferPtr)[i] != 0.f); // This is violated on Bazel build but not CMake build
}
}
to oidnTest
all tests succeed in a CMake-based build environment. At the same time, the same code fails with Bazel.
Note: I am also willing to accept answers that narrow the problem to a smaller code "footprint" as in the shown unit test (which is more like high-level acceptance test)
答案1
得分: 0
问题是ISCP编译器还需要定义OIDN_DNNL
。在查看tensor.isph
文件时,可以看到以下内容:
inline size_t getIndex(uniform TensorAccessor& tz, uniform int h, int w, uniform int c)
{
#if defined(OIDN_DNNL)
// ChwKc layout (blocked)
return ((size_t)tz.H * (c/K) + h) * ((size_t)tz.W*K) + (size_t)w*K + (c%K);
#else
// chw layout
return ((size_t)tz.H * c + h) * (size_t)tz.W + w;
#endif
}
因为我正在使用oneDNN,但没有为ISCP文件定义这些宏,所以出现了黑色条纹。现在这个问题已经解决,一切都正常运作。
英文:
The issue was that the ISCP compiler also need the define OIDN_DNNL
. When looking into the tensor.isph
file one can see this:
inline size_t getIndex(uniform TensorAccessor& tz, uniform int h, int w, uniform int c)
{
#if defined(OIDN_DNNL)
// ChwKc layout (blocked)
return ((size_t)tz.H * (c/K) + h) * ((size_t)tz.W*K) + (size_t)w*K + (c%K);
#else
// chw layout
return ((size_t)tz.H * c + h) * (size_t)tz.W + w;
#endif
}
Since I am using oneDNN and but did not define for the ISCP files the defines I got black strips. This issue is fixed now and everything works as expected.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论