Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a simple CSV-based image format to halide_image_io #8169

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ struct Expr : public Internal::IRHandle {
Expr(bfloat16_t x)
: IRHandle(Internal::FloatImm::make(BFloat(16), (double)x)) {
}
#ifdef HALIDE_CPP_COMPILER_HAS_FLOAT16
explicit Expr(_Float16 x)
: IRHandle(Internal::FloatImm::make(Float(16), (double)x)) {
}
#endif
Expr(float x)
: IRHandle(Internal::FloatImm::make(Float(32), x)) {
}
Expand Down
3 changes: 3 additions & 0 deletions src/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ HALIDE_DECLARE_EXTERN_SIMPLE_TYPE(Halide::float16_t);
HALIDE_DECLARE_EXTERN_SIMPLE_TYPE(Halide::bfloat16_t);
HALIDE_DECLARE_EXTERN_SIMPLE_TYPE(halide_task_t);
HALIDE_DECLARE_EXTERN_SIMPLE_TYPE(halide_loop_task_t);
#ifdef HALIDE_CPP_COMPILER_HAS_FLOAT16
HALIDE_DECLARE_EXTERN_SIMPLE_TYPE(_Float16);
#endif
HALIDE_DECLARE_EXTERN_SIMPLE_TYPE(float);
HALIDE_DECLARE_EXTERN_SIMPLE_TYPE(double);
HALIDE_DECLARE_EXTERN_STRUCT_TYPE(halide_buffer_t);
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/HalideRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ extern "C" {
// Ideally there would be a better way to detect if the type
// is supported, even in a compiler independent fashion, but
// coming up with one has proven elusive.
#if defined(__clang__) && (__clang_major__ >= 16) && !defined(__EMSCRIPTEN__) && !defined(__i386__)
#if defined(__clang__) && (__clang_major__ >= 15) && !defined(__EMSCRIPTEN__) && !defined(__i386__)
#if defined(__is_identifier)
#if !__is_identifier(_Float16)
#define HALIDE_CPP_COMPILER_HAS_FLOAT16
Expand Down
22 changes: 22 additions & 0 deletions test/correctness/debug_to_file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@

using namespace Halide;

// clang had _Float16 added as a reserved name in clang 8, but
// doesn't actually support it on most platforms until clang 15.
// Ideally there would be a better way to detect if the type
// is supported, even in a compiler independent fashion, but
// coming up with one has proven elusive.
#if defined(__clang__) && (__clang_major__ >= 15) && !defined(__EMSCRIPTEN__) && !defined(__i386__)
#if defined(__is_identifier)
#if !__is_identifier(_Float16)
#error "clang says we have f16"
#endif
#endif
#endif

// Similarly, detecting _Float16 for gcc is problematic.
// For now, we say that if >= v12, and compiling on x86 or arm,
// we assume support. This may need revision.
#if defined(__GNUC__) && (__GNUC__ >= 12)
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
#error "gcc says we have f16"
#endif
#endif

int main(int argc, char **argv) {
if (get_jit_target_from_environment().arch == Target::WebAssembly) {
printf("[SKIP] WebAssembly JIT does not support debug_to_file() yet.\n");
Expand Down
62 changes: 43 additions & 19 deletions test/correctness/image_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,26 @@ void test_round_trip(Buffer<T> buf, std::string format) {
reloaded.translate(d, buf.dim(d).min() - reloaded.dim(d).min());
}

Tools::save_image(reloaded, Internal::get_test_tmp_dir() + "test_reloaded." + format);
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]);
}
uint32_t diff = evaluate<uint32_t>(maximum(abs(cast<int>(buf(args)) - cast<int>(reloaded(args)))));
double diff = evaluate<double>(maximum(abs(cast<double>(buf(args)) - cast<double>(reloaded(args)))));

uint32_t max_diff = 0;
double max_diff = 0.00001;
if (format == "jpg") {
max_diff = 32;
}
if (diff > max_diff) {
printf("test_round_trip: Difference of %d when saved and loaded as %s\n", diff, format.c_str());
abort();
printf("test_round_trip: Difference of %f when saved and loaded as %s\n", diff, format.c_str());
exit(1);
}
}

Expand All @@ -62,7 +65,7 @@ void test_convert_image_s2s(Buffer<T> buf) {
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);
abort();
exit(1);
}
}

Expand All @@ -85,7 +88,7 @@ void test_convert_image_d2s(Buffer<T> buf) {
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);
abort();
exit(1);
}
}

Expand All @@ -110,7 +113,7 @@ void test_convert_image_s2d(Buffer<T> buf) {
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);
abort();
exit(1);
}
}

Expand All @@ -135,7 +138,7 @@ void test_convert_image_d2d(Buffer<> buf_d) {
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);
abort();
exit(1);
}
}

Expand Down Expand Up @@ -166,8 +169,8 @@ void do_test() {
// Make some colored noise
Func f;
Var x, y, c, w;
const float one = std::numeric_limits<T>::max();
f(x, y, c) = cast<T>(clamp(make_noise(10)(x, y, c), 0.0f, 1.0f) * one);
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});

Expand All @@ -176,24 +179,34 @@ void do_test() {
color_buf.crop(0, inset, width - inset * 2);
color_buf.crop(1, inset, height - inset * 2);

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);
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 = {"ppm", "pgm", "tmp", "mat", "tiff"};
std::vector<std::string> formats = {"csv", "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) {
if (format == "jpg" && halide_type_of<T>() != halide_type_t(halide_type_uint, 8)) {
// CSV is the only format here that can handle f16 at present
if (ht == halide_type_t(halide_type_float, 16) && format != "csv") {
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") {
Expand Down Expand Up @@ -238,7 +251,7 @@ void test_mat_header() {
std::ifstream fs(filename.c_str(), std::ifstream::binary);
if (!fs) {
std::cout << "Cannot read " << filename << "\n";
abort();
exit(1);
}
fs.seekg(0, fs.end);
// .mat file begins with a 128 bytes header and a 8 bytes
Expand All @@ -251,13 +264,24 @@ void test_mat_header() {
fs.close();
if (file_size != stored_file_size) {
std::cout << "Wrong file size written for " << filename << ". Expected " << file_size << ", got" << stored_file_size << "\n";
abort();
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>();
#ifdef HALIDE_CPP_COMPILER_HAS_FLOAT16
do_test<_Float16>();
#endif
do_test<float>();
do_test<double>();
test_mat_header();
printf("Success!\n");
return 0;
Expand Down
Loading
Loading