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 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
|
#include "Halide.h"
#include "halide_image_io.h"
#include "halide_test_dirs.h"
#include <fstream>
using namespace Halide;
template<typename T>
void test_round_trip(Buffer<T> buf, std::string format) {
// Save it
std::ostringstream o;
o << Internal::get_test_tmp_dir() << "test_" << halide_type_of<T>() << "x" << buf.channels() << "." << format;
std::string filename = o.str();
Tools::save_image(buf, filename);
// TIFF is write-only for now.
if (format == "tiff") return;
// Reload it
Buffer<T> reloaded = Tools::load_image(filename);
// Ensure that reloaded has the same origin as buf
for (int d = 0; d < buf.dimensions(); ++d) {
reloaded.translate(d, buf.dim(d).min() - reloaded.dim(d).min());
}
o = std::ostringstream();
o << Internal::get_test_tmp_dir() << "test_" << halide_type_of<T>() << "x" << buf.channels() << ".reloaded." << format;
filename = o.str();
Tools::save_image(reloaded, filename);
// Check they're not too different.
RDom r(reloaded);
std::vector<Expr> args;
for (int i = 0; i < r.dimensions(); ++i) {
args.push_back(r[i]);
}
double diff = evaluate<double>(maximum(abs(cast<double>(buf(args)) - cast<double>(reloaded(args)))));
double max_diff = 0.00001;
if (format == "jpg") {
max_diff = 32;
}
if (diff > max_diff) {
printf("test_round_trip: Difference of %f when saved and loaded as %s\n", diff, format.c_str());
exit(1);
}
}
// static -> static conversion test
template<typename T>
void test_convert_image_s2s(Buffer<T> buf) {
std::cout << "Testing static -> static image conversion for " << halide_type_of<T>() << "\n";
// convert to float
Buffer<float> buf_float = Tools::ImageTypeConversion::convert_image<float>(buf);
// convert back to T
Buffer<T> buf2 = Tools::ImageTypeConversion::convert_image<T>(buf_float);
// Check that they match (this conversion should be exact).
RDom r(buf2);
std::vector<Expr> args = {r.x, r.y, r.z};
uint32_t diff = evaluate<uint32_t>(maximum(abs(cast<int>(buf(args)) - cast<int>(buf2(args)))));
if (diff > 0) {
printf("test_convert_image_s2s: Difference of %d when converted\n", diff);
exit(1);
}
}
// dynamic -> static conversion test
template<typename T>
void test_convert_image_d2s(Buffer<T> buf) {
std::cout << "Testing dynamic -> static image conversion for " << halide_type_of<T>() << "\n";
// convert to float
Buffer<> buf_d(buf);
Buffer<float> buf_float = Tools::ImageTypeConversion::convert_image<float>(buf_d);
// convert back to T
Buffer<> buf_float_d(buf_float);
Buffer<T> buf2 = Tools::ImageTypeConversion::convert_image<T>(buf_float_d);
// Check that they match (this conversion should be exact).
RDom r(buf2);
std::vector<Expr> args = {r.x, r.y, r.z};
uint32_t diff = evaluate<uint32_t>(maximum(abs(cast<int>(buf(args)) - cast<int>(buf2(args)))));
if (diff > 0) {
printf("test_convert_image_d2s: Difference of %d when converted\n", diff);
exit(1);
}
}
// static -> dynamic conversion test
template<typename T>
void test_convert_image_s2d(Buffer<T> buf) {
std::cout << "Testing static -> dynamic image conversion for " << halide_type_of<T>() << "\n";
// convert to float
Buffer<> buf_float_d = Tools::ImageTypeConversion::convert_image(buf, halide_type_t(halide_type_float, 32));
// This will do a runtime check
Buffer<float> buf_float(buf_float_d);
// convert back to T
Buffer<> buf2_d = Tools::ImageTypeConversion::convert_image(buf_float, halide_type_of<T>());
// This will do a runtime check
Buffer<T> buf2(buf2_d);
// Check that they match (this conversion should be exact).
RDom r(buf2);
std::vector<Expr> args = {r.x, r.y, r.z};
uint32_t diff = evaluate<uint32_t>(maximum(abs(cast<int>(buf(args)) - cast<int>(buf2(args)))));
if (diff > 0) {
printf("test_convert_image_s2d: Difference of %d when converted\n", diff);
exit(1);
}
}
// dynamic -> dynamic conversion test
template<typename T>
void test_convert_image_d2d(Buffer<> buf_d) {
std::cout << "Testing dynamic -> dynamic image conversion for " << halide_type_of<T>() << "\n";
// convert to float
Buffer<> buf_float_d = Tools::ImageTypeConversion::convert_image(buf_d, halide_type_t(halide_type_float, 32));
// convert back to T
Buffer<> buf2_d = Tools::ImageTypeConversion::convert_image(buf_float_d, halide_type_of<T>());
// These will do a runtime check
Buffer<T> buf(buf_d);
Buffer<T> buf2(buf2_d);
// Check that they match (this conversion should be exact).
RDom r(buf2);
std::vector<Expr> args = {r.x, r.y, r.z};
uint32_t diff = evaluate<uint32_t>(maximum(abs(cast<int>(buf(args)) - cast<int>(buf2(args)))));
if (diff > 0) {
printf("test_convert_image_d2d: Difference of %d when converted\n", diff);
exit(1);
}
}
Func make_noise(int depth) {
Func f;
Var x, y, c;
if (depth == 0) {
f(x, y, c) = random_float();
} else {
Func g = make_noise(depth - 1);
Func g_up;
f(x, y, c) = (g(x / 2, y / 2, c) +
g((x + 1) / 2, y / 2, c) +
g(x / 2, (y + 1) / 2, c) +
g((x + 1) / 2, (y + 1) / 2, c) +
0.25f * random_float()) /
4.25f;
}
f.compute_root();
return f;
}
template<typename T>
void do_test() {
const int width = 160;
const int height = 120;
// Make some colored noise
Func f;
Var x, y, c, w;
const Expr one = std::is_floating_point<T>::value ? Expr(1.0) : Expr(std::numeric_limits<T>::max());
f(x, y, c) = cast<T>(clamp(make_noise(10)(x, y, c), Expr(0.0), Expr(1.0)) * one);
Buffer<T> color_buf = f.realize({width, height, 3});
// Inset it a bit to ensure that saving buffers with nonzero mins works
const int inset = 4;
color_buf.crop(0, inset, width - inset * 2);
color_buf.crop(1, inset, height - inset * 2);
const auto ht = halide_type_of<T>();
if (ht == halide_type_t(halide_type_uint, 8) || ht == halide_type_t(halide_type_uint, 16)) {
test_convert_image_s2s<T>(color_buf);
test_convert_image_s2d<T>(color_buf);
test_convert_image_d2s<T>(color_buf);
test_convert_image_d2d<T>(color_buf);
}
Buffer<T> luma_buf(width, height, 1);
luma_buf.copy_from(color_buf);
luma_buf.slice(2);
std::vector<std::string> formats = {"npy", "ppm", "pgm", "tmp", "mat", "tiff"};
#ifndef HALIDE_NO_JPEG
formats.push_back("jpg");
#endif
#ifndef HALIDE_NO_PNG
formats.push_back("png");
#endif
for (std::string format : formats) {
// .npy is the only format here that supports float16
if (halide_type_of<T>() == halide_type_t(halide_type_float, 16) && format != "npy") {
continue;
}
if ((format == "jpg" || format == "pgm" || format == "ppm") && ht != halide_type_t(halide_type_uint, 8)) {
continue;
}
if (format == "png" && ht != halide_type_t(halide_type_uint, 8) && ht != halide_type_t(halide_type_uint, 16)) {
continue;
}
if (format == "tmp") {
// .tmp only supports exactly-4-dimensions, so handle it separately.
// (Add a dimension to make it 4-dimensional)
Buffer<T> cb4 = color_buf.embedded(color_buf.dimensions());
std::cout << "Testing format: " << format << " for " << halide_type_of<T>() << "x4\n";
test_round_trip(cb4, format);
// Here we test matching strides
Func f2;
f2(x, y, c, w) = f(x, y, c);
Buffer<T> funky_buf = f2.realize({10, 10, 1, 3});
funky_buf.fill(42);
std::cout << "Testing format: " << format << " for " << halide_type_of<T>() << "x4\n";
test_round_trip(funky_buf, format);
continue;
}
if (format != "pgm") {
std::cout << "Testing format: " << format << " for " << halide_type_of<T>() << "x3\n";
// pgm really only supports gray images.
test_round_trip(color_buf, format);
}
if (format != "ppm") {
std::cout << "Testing format: " << format << " for " << halide_type_of<T>() << "x1\n";
// ppm really only supports RGB images.
test_round_trip(luma_buf, format);
}
}
}
void test_mat_header() {
// Test if the .mat file header writes the correct file size
std::ostringstream o;
Buffer<uint8_t> buf(15, 15);
buf.fill(42);
o << Internal::get_test_tmp_dir() << "test_mat_header.mat";
std::string filename = o.str();
Tools::save_image(buf, filename);
std::ifstream fs(filename.c_str(), std::ifstream::binary);
if (!fs) {
std::cout << "Cannot read " << filename << "\n";
exit(1);
}
fs.seekg(0, fs.end);
// .mat file begins with a 128 bytes header and a 8 bytes
// matrix tag, the second byte of the matrix describe
// the size of the rest of the file
uint32_t file_size = uint32_t((int)fs.tellg() - 128 - 8);
fs.seekg(128 + 4, fs.beg);
uint32_t stored_file_size = 0;
fs.read((char *)&stored_file_size, 4);
fs.close();
if (file_size != stored_file_size) {
std::cout << "Wrong file size written for " << filename << ". Expected " << file_size << ", got" << stored_file_size << "\n";
exit(1);
}
}
int main(int argc, char **argv) {
do_test<int8_t>();
do_test<int16_t>();
do_test<int32_t>();
do_test<int64_t>();
do_test<uint8_t>();
do_test<uint16_t>();
do_test<uint32_t>();
do_test<uint64_t>();
do_test<float>();
#ifdef HALIDE_CPP_COMPILER_HAS_FLOAT16
do_test<_Float16>();
#endif
do_test<double>();
test_mat_header();
printf("Success!\n");
return 0;
}
|