1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
|
// Copyright 2017 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "test_tools.h"
#include <fstream>
#include "rkcommon/utility/SaveImage.h"
extern OSPRayEnvironment *ospEnv;
OSPImageTools::OSPImageTools(
vec2i imgSize, std::string testName, OSPFrameBufferFormat frameBufferFormat)
: size(imgSize), imgName(testName)
{
switch (frameBufferFormat) {
case OSP_FB_RGBA8:
fileFormat = ".ppm";
break;
case OSP_FB_SRGBA:
fileFormat = ".png";
break;
case OSP_FB_RGBA32F:
fileFormat = ".pfm";
break;
default:
fileFormat = ".err";
break;
}
}
OsprayStatus OSPImageTools::writePNG(
std::string fileName, const uint32_t *pixel)
{
stbi_flip_vertically_on_write(true);
int retCode = stbi_write_png(
fileName.c_str(), size.x, size.y, ImgType::RGBA, (const void *)pixel, 0);
if (!retCode) {
std::cerr << "Failed to save image: " << fileName << std::endl;
return OsprayStatus::Fail;
}
return OsprayStatus::Ok;
}
OsprayStatus OSPImageTools::writeHDR(std::string fileName, const float *pixel)
{
stbi_flip_vertically_on_write(true);
int retCode =
stbi_write_hdr(fileName.c_str(), size.x, size.y, ImgType::RGBA, pixel);
if (!retCode) {
std::cerr << "Failed to save image: " << fileName << std::endl;
return OsprayStatus::Fail;
}
return OsprayStatus::Ok;
}
// helper function to write the rendered image
OsprayStatus OSPImageTools::writeImg(std::string fileName, const void *pixel)
{
OsprayStatus writeErr = OsprayStatus::Error;
fileName += GetFileFormat();
if (GetFileFormat() == ".ppm") {
rkcommon::utility::writePPM(
fileName, size.x, size.y, (const uint32_t *)pixel);
writeErr = OsprayStatus::Ok;
} else if (GetFileFormat() == ".png") {
writeErr = writePNG(fileName, (const uint32_t *)pixel);
} else if (GetFileFormat() == ".hdr") {
writeErr = writeHDR(fileName, (const float *)pixel);
} else if (GetFileFormat() == ".pfm") {
try {
rkcommon::utility::writePFM(fileName, size.x, size.y, (const vec4f *)pixel);
writeErr = OsprayStatus::Ok;
} catch (...) {
writeErr = OsprayStatus::Fail;
}
} else {
std::cerr << "Unsupported file format" << std::endl;
writeErr = OsprayStatus::Error;
}
return writeErr;
}
OsprayStatus OSPImageTools::verifyBaselineImage(const int sizeX,
const int sizeY,
const void *baselineImage,
const std::string &baselineName)
{
// Check if baseline image is suitable
if (!baselineImage) {
std::cerr << "Failed to load image: " << baselineName << std::endl;
return OsprayStatus::Fail;
} else if (sizeX != size.x || sizeY != size.y) {
std::cerr << "Wrong image loaded for: " << baselineName << std::endl;
return OsprayStatus::Fail;
}
return OsprayStatus::Ok;
}
vec4f OSPImageTools::getAveragedPixel(const vec4f *image,
vec2i pixelIndex,
const rkcommon::index_sequence_2D &imageIndices)
{
vec4f p(0.f);
unsigned int count = 0;
rkcommon::index_sequence_2D indices(vec2i(5));
for (vec2i id : indices) {
vec2i pid = pixelIndex + (id - vec2i(2));
if ((reduce_min(pid) < 0) || (reduce_min(size - pid) < 1))
continue;
p += image[imageIndices.flatten(pid)];
count++;
}
return p / float(count);
}
// compare the baseline image with the values form the framebuffer
template <typename T>
OsprayStatus OSPImageTools::compareImgWithBaselineTmpl(const T *testImage,
const T *baselineImage,
const std::string &baselineName,
const float pixelConversionFactor)
{
bool notPerfect = false;
double totalError = 0.;
rkcommon::index_sequence_2D imageIndices(size);
std::vector<T> diffAbsImage(imageIndices.total_indices());
{
// Prepare temporary diff image with floats
std::vector<vec4f> diffImage(imageIndices.total_indices());
for (vec2i i : imageIndices) {
const unsigned int pixelIndex = imageIndices.flatten(i);
const vec4f baselineValue = baselineImage[pixelIndex];
const vec4f renderedValue = testImage[pixelIndex];
diffImage[pixelIndex] = baselineValue - renderedValue;
}
for (vec2i i : imageIndices) {
const unsigned int pixelIndex = imageIndices.flatten(i);
const T diffValue = abs(diffImage[pixelIndex]);
const vec4f diffAvgValue =
abs(getAveragedPixel(diffImage.data(), i, imageIndices));
// Only count errors if above specified threshold, this removes blurred
// noise
const float pixelError = reduce_add(diffAvgValue) * pixelConversionFactor;
if (pixelError > pixelThreshold)
totalError += pixelError;
// Not a perfect match if any difference detected
notPerfect = notPerfect || reduce_add(diffValue);
// Set values for output diff image
diffAbsImage[pixelIndex] = diffValue;
diffAbsImage[pixelIndex].w = std::numeric_limits<unsigned char>::max();
}
}
if (notPerfect)
std::cerr << "[ WARNING ] " << baselineName << " is not pixel perfect"
<< std::endl;
double meanError = totalError / double(4 * size.x * size.y);
if (totalError) {
std::cerr << "[ STATISTIC] Total/mean error: " << totalError << "/"
<< std::fixed << std::setprecision(3) << meanError << std::endl;
}
bool failed = meanError > errorRate;
if (failed) {
writeImg(
ospEnv->GetFailedDir() + "/" + imgName + "_baseline", baselineImage);
writeImg(ospEnv->GetFailedDir() + "/" + imgName + "_rendered", testImage);
writeImg(
ospEnv->GetFailedDir() + "/" + imgName + "_diff", diffAbsImage.data());
}
if (failed)
return OsprayStatus::Fail;
else
return OsprayStatus::Ok;
}
OsprayStatus OSPImageTools::saveTestImage(const void *pixel)
{
return writeImg(ospEnv->GetBaselineDir() + "/" + imgName, pixel);
}
vec4f *loadPF4(std::string fileName, int &sizeX, int &sizeY)
{
std::ifstream ifs;
ifs.open(fileName, std::ifstream::in | std::ifstream::binary);
if (!ifs.good())
return nullptr;
std::string header;
std::getline(ifs, header);
if (!ifs.good() || header != "PF4")
return nullptr;
ifs >> sizeX >> sizeY;
std::getline(ifs, header); // eat newline after size information
if (!ifs.good())
return nullptr;
std::getline(ifs, header);
if (!ifs.good() || header != "-1.0")
return nullptr;
size_t sz = sizeX * sizeY;
vec4f *img = new vec4f[sz];
ifs.read((char*)img, sz * sizeof(float) * 4);
if (!ifs.good())
return nullptr;
ifs.close();
return img;
}
// compare the baseline image with the values form the framebuffer
OsprayStatus OSPImageTools::compareImgWithBaseline(const void *testImage)
{
std::string baselineName =
ospEnv->GetBaselineDir() + "/" + imgName + GetFileFormat();
stbi_set_flip_vertically_on_load(true);
int dataX, dataY, dataN;
OsprayStatus compErr = OsprayStatus::Error;
if (GetFileFormat() == ".png") {
vec4uc *baselineImage = (vec4uc *)stbi_load(
baselineName.c_str(), &dataX, &dataY, &dataN, ImgType::RGBA);
compErr = verifyBaselineImage(dataX, dataY, baselineImage, baselineName);
if (compErr == OsprayStatus::Ok)
compErr = compareImgWithBaselineTmpl<vec4uc>(
(vec4uc *)testImage, baselineImage, baselineName);
if (baselineImage)
stbi_image_free(baselineImage);
} else if (GetFileFormat() == ".hdr") {
vec4f *baselineImage = (vec4f *)stbi_loadf(
baselineName.c_str(), &dataX, &dataY, &dataN, ImgType::RGBA);
compErr = verifyBaselineImage(dataX, dataY, baselineImage, baselineName);
if (compErr == OsprayStatus::Ok)
compErr = compareImgWithBaselineTmpl<vec4f>(
(vec4f *)testImage, baselineImage, baselineName, 255.0f);
if (baselineImage)
stbi_image_free(baselineImage);
} else if (GetFileFormat() == ".pfm") {
vec4f *baselineImage = loadPF4(baselineName, dataX, dataY);
compErr = verifyBaselineImage(dataX, dataY, baselineImage, baselineName);
if (compErr == OsprayStatus::Ok)
compErr = compareImgWithBaselineTmpl<vec4f>(
(vec4f *)testImage, baselineImage, baselineName, 255.0f);
if (baselineImage)
delete[] baselineImage;
} else {
std::cerr << "Unsupported file format" << std::endl;
compErr = OsprayStatus::Error;
}
return compErr;
}
|