/** * Copyright 1993-2013 NVIDIA Corporation. All rights reserved. * * Please refer to the NVIDIA end user license agreement (EULA) associated * with this source code for terms and conditions that govern your use of * this software. Any use, reproduction, disclosure, or distribution of * this software and related documentation outside the terms of the EULA * is strictly prohibited. * */ // These are helper functions for the SDK samples (image,bitmap) #ifndef COMMON_HELPER_IMAGE_H_ #define COMMON_HELPER_IMAGE_H_ #include #include #include #include #include #include #include #include #ifndef MIN #define MIN(a, b) ((a < b) ? a : b) #endif #ifndef MAX #define MAX(a, b) ((a > b) ? a : b) #endif #ifndef EXIT_WAIVED #define EXIT_WAIVED 2 #endif #include "exception.h" #include "helper_string.h" // namespace unnamed (internal) namespace helper_image_internal { //! size of PGM file header const unsigned int PGMHeaderSize = 0x40; // types //! Data converter from unsigned char / unsigned byte to type T template struct ConverterFromUByte; //! Data converter from unsigned char / unsigned byte template <> struct ConverterFromUByte { //! Conversion operator //! @return converted value //! @param val value to convert float operator()(const unsigned char &val) { return static_cast(val); } }; //! Data converter from unsigned char / unsigned byte to float template <> struct ConverterFromUByte { //! Conversion operator //! @return converted value //! @param val value to convert float operator()(const unsigned char &val) { return static_cast(val) / 255.0f; } }; //! Data converter from unsigned char / unsigned byte to type T template struct ConverterToUByte; //! Data converter from unsigned char / unsigned byte to unsigned int template <> struct ConverterToUByte { //! Conversion operator (essentially a passthru //! @return converted value //! @param val value to convert unsigned char operator()(const unsigned char &val) { return val; } }; //! Data converter from unsigned char / unsigned byte to unsigned int template <> struct ConverterToUByte { //! Conversion operator //! @return converted value //! @param val value to convert unsigned char operator()(const float &val) { return static_cast(val * 255.0f); } }; } // namespace helper_image_internal #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) #ifndef FOPEN #define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode) #endif #ifndef FOPEN_FAIL #define FOPEN_FAIL(result) (result != 0) #endif #ifndef SSCANF #define SSCANF sscanf_s #endif #else #ifndef FOPEN #define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode)) #endif #ifndef FOPEN_FAIL #define FOPEN_FAIL(result) (result == NULL) #endif #ifndef SSCANF #define SSCANF sscanf #endif #endif inline bool __loadPPM(const char *file, unsigned char **data, unsigned int *w, unsigned int *h, unsigned int *channels) { FILE *fp = NULL; if (FOPEN_FAIL(FOPEN(fp, file, "rb"))) { std::cerr << "__LoadPPM() : Failed to open file: " << file << std::endl; return false; } // check header char header[helper_image_internal::PGMHeaderSize]; if (fgets(header, helper_image_internal::PGMHeaderSize, fp) == NULL) { std::cerr << "__LoadPPM() : reading PGM header returned NULL" << std::endl; return false; } if (strncmp(header, "P5", 2) == 0) { *channels = 1; } else if (strncmp(header, "P6", 2) == 0) { *channels = 3; } else { std::cerr << "__LoadPPM() : File is not a PPM or PGM image" << std::endl; *channels = 0; return false; } // parse header, read maxval, width and height unsigned int width = 0; unsigned int height = 0; unsigned int maxval = 0; unsigned int i = 0; while (i < 3) { if (fgets(header, helper_image_internal::PGMHeaderSize, fp) == NULL) { std::cerr << "__LoadPPM() : reading PGM header returned NULL" << std::endl; return false; } if (header[0] == '#') { continue; } if (i == 0) { i += SSCANF(header, "%u %u %u", &width, &height, &maxval); } else if (i == 1) { i += SSCANF(header, "%u %u", &height, &maxval); } else if (i == 2) { i += SSCANF(header, "%u", &maxval); } } // check if given handle for the data is initialized if (NULL != *data) { if (*w != width || *h != height) { std::cerr << "__LoadPPM() : Invalid image dimensions." << std::endl; } } else { *data = (unsigned char *)malloc(sizeof(unsigned char) * width * height * *channels); *w = width; *h = height; } // read and close file if (fread(*data, sizeof(unsigned char), width * height * *channels, fp) == 0) { std::cerr << "__LoadPPM() read data returned error." << std::endl; } fclose(fp); return true; } template inline bool sdkLoadPGM(const char *file, T **data, unsigned int *w, unsigned int *h) { unsigned char *idata = NULL; unsigned int channels; if (true != __loadPPM(file, &idata, w, h, &channels)) { return false; } unsigned int size = *w * *h * channels; // initialize mem if necessary // the correct size is checked / set in loadPGMc() if (NULL == *data) { *data = reinterpret_cast(malloc(sizeof(T) * size)); } // copy and cast data std::transform(idata, idata + size, *data, helper_image_internal::ConverterFromUByte()); free(idata); return true; } template inline bool sdkLoadPPM4(const char *file, T **data, unsigned int *w, unsigned int *h) { unsigned char *idata = 0; unsigned int channels; if (__loadPPM(file, &idata, w, h, &channels)) { // pad 4th component int size = *w * *h; // keep the original pointer unsigned char *idata_orig = idata; *data = reinterpret_cast(malloc(sizeof(T) * size * 4)); unsigned char *ptr = *data; for (int i = 0; i < size; i++) { *ptr++ = *idata++; *ptr++ = *idata++; *ptr++ = *idata++; *ptr++ = 0; } free(idata_orig); return true; } else { free(idata); return false; } } inline bool __savePPM(const char *file, unsigned char *data, unsigned int w, unsigned int h, unsigned int channels) { assert(NULL != data); assert(w > 0); assert(h > 0); std::fstream fh(file, std::fstream::out | std::fstream::binary); if (fh.bad()) { std::cerr << "__savePPM() : Opening file failed." << std::endl; return false; } if (channels == 1) { fh << "P5\n"; } else if (channels == 3) { fh << "P6\n"; } else { std::cerr << "__savePPM() : Invalid number of channels." << std::endl; return false; } fh << w << "\n" << h << "\n" << 0xff << std::endl; for (unsigned int i = 0; (i < (w * h * channels)) && fh.good(); ++i) { fh << data[i]; } fh.flush(); if (fh.bad()) { std::cerr << "__savePPM() : Writing data failed." << std::endl; return false; } fh.close(); return true; } template inline bool sdkSavePGM(const char *file, T *data, unsigned int w, unsigned int h) { unsigned int size = w * h; unsigned char *idata = (unsigned char *)malloc(sizeof(unsigned char) * size); std::transform(data, data + size, idata, helper_image_internal::ConverterToUByte()); // write file bool result = __savePPM(file, idata, w, h, 1); // cleanup free(idata); return result; } inline bool sdkSavePPM4ub(const char *file, unsigned char *data, unsigned int w, unsigned int h) { // strip 4th component int size = w * h; unsigned char *ndata = (unsigned char *)malloc(sizeof(unsigned char) * size * 3); unsigned char *ptr = ndata; for (int i = 0; i < size; i++) { *ptr++ = *data++; *ptr++ = *data++; *ptr++ = *data++; data++; } bool result = __savePPM(file, ndata, w, h, 3); free(ndata); return result; } ////////////////////////////////////////////////////////////////////////////// //! Read file \filename and return the data //! @return bool if reading the file succeeded, otherwise false //! @param filename name of the source file //! @param data uninitialized pointer, returned initialized and pointing to //! the data read //! @param len number of data elements in data, -1 on error ////////////////////////////////////////////////////////////////////////////// template inline bool sdkReadFile(const char *filename, T **data, unsigned int *len, bool verbose) { // check input arguments assert(NULL != filename); assert(NULL != len); // intermediate storage for the data read std::vector data_read; // open file for reading FILE *fh = NULL; // check if filestream is valid if (FOPEN_FAIL(FOPEN(fh, filename, "r"))) { printf("Unable to open input file: %s\n", filename); return false; } // read all data elements T token; while (!feof(fh)) { fscanf(fh, "%f", &token); data_read.push_back(token); } // the last element is read twice data_read.pop_back(); fclose(fh); // check if the given handle is already initialized if (NULL != *data) { if (*len != data_read.size()) { std::cerr << "sdkReadFile() : Initialized memory given but " << "size mismatch with signal read " << "(data read / data init = " << (unsigned int)data_read.size() << " / " << *len << ")" << std::endl; return false; } } else { // allocate storage for the data read *data = reinterpret_cast(malloc(sizeof(T) * data_read.size())); // store signal size *len = static_cast(data_read.size()); } // copy data memcpy(*data, &data_read.front(), sizeof(T) * data_read.size()); return true; } ////////////////////////////////////////////////////////////////////////////// //! Read file \filename and return the data //! @return bool if reading the file succeeded, otherwise false //! @param filename name of the source file //! @param data uninitialized pointer, returned initialized and pointing to //! the data read //! @param len number of data elements in data, -1 on error ////////////////////////////////////////////////////////////////////////////// template inline bool sdkReadFileBlocks(const char *filename, T **data, unsigned int *len, unsigned int block_num, unsigned int block_size, bool verbose) { // check input arguments assert(NULL != filename); assert(NULL != len); // open file for reading FILE *fh = fopen(filename, "rb"); if (fh == NULL && verbose) { std::cerr << "sdkReadFile() : Opening file failed." << std::endl; return false; } // check if the given handle is already initialized // allocate storage for the data read data[block_num] = reinterpret_cast(malloc(block_size)); // read all data elements fseek(fh, block_num * block_size, SEEK_SET); *len = fread(data[block_num], sizeof(T), block_size / sizeof(T), fh); fclose(fh); return true; } ////////////////////////////////////////////////////////////////////////////// //! Write a data file \filename //! @return true if writing the file succeeded, otherwise false //! @param filename name of the source file //! @param data data to write //! @param len number of data elements in data, -1 on error //! @param epsilon epsilon for comparison ////////////////////////////////////////////////////////////////////////////// template inline bool sdkWriteFile(const char *filename, const T *data, unsigned int len, const S epsilon, bool verbose, bool append = false) { assert(NULL != filename); assert(NULL != data); // open file for writing // if (append) { std::fstream fh(filename, std::fstream::out | std::fstream::ate); if (verbose) { std::cerr << "sdkWriteFile() : Open file " << filename << " for write/append." << std::endl; } /* } else { std::fstream fh(filename, std::fstream::out); if (verbose) { std::cerr << "sdkWriteFile() : Open file " << filename << " for write." << std::endl; } } */ // check if filestream is valid if (!fh.good()) { if (verbose) { std::cerr << "sdkWriteFile() : Opening file failed." << std::endl; } return false; } // first write epsilon fh << "# " << epsilon << "\n"; // write data for (unsigned int i = 0; (i < len) && (fh.good()); ++i) { fh << data[i] << ' '; } // Check if writing succeeded if (!fh.good()) { if (verbose) { std::cerr << "sdkWriteFile() : Writing file failed." << std::endl; } return false; } // file ends with nl fh << std::endl; return true; } ////////////////////////////////////////////////////////////////////////////// //! Compare two arrays of arbitrary type //! @return true if \a reference and \a data are identical, otherwise false //! @param reference timer_interface to the reference data / gold image //! @param data handle to the computed data //! @param len number of elements in reference and data //! @param epsilon epsilon to use for the comparison ////////////////////////////////////////////////////////////////////////////// template inline bool compareData(const T *reference, const T *data, const unsigned int len, const S epsilon, const float threshold) { assert(epsilon >= 0); bool result = true; unsigned int error_count = 0; for (unsigned int i = 0; i < len; ++i) { float diff = static_cast(reference[i]) - static_cast(data[i]); bool comp = (diff <= epsilon) && (diff >= -epsilon); result &= comp; error_count += !comp; #if 0 if (!comp) { std::cerr << "ERROR, i = " << i << ",\t " << reference[i] << " / " << data[i] << " (reference / data)\n"; } #endif } if (threshold == 0.0f) { return (result) ? true : false; } else { if (error_count) { printf("%4.2f(%%) of bytes mismatched (count=%d)\n", static_cast(error_count) * 100 / static_cast(len), error_count); } return (len * threshold > error_count) ? true : false; } } #ifndef __MIN_EPSILON_ERROR #define __MIN_EPSILON_ERROR 1e-3f #endif ////////////////////////////////////////////////////////////////////////////// //! Compare two arrays of arbitrary type //! @return true if \a reference and \a data are identical, otherwise false //! @param reference handle to the reference data / gold image //! @param data handle to the computed data //! @param len number of elements in reference and data //! @param epsilon epsilon to use for the comparison //! @param epsilon threshold % of (# of bytes) for pass/fail ////////////////////////////////////////////////////////////////////////////// template inline bool compareDataAsFloatThreshold(const T *reference, const T *data, const unsigned int len, const S epsilon, const float threshold) { assert(epsilon >= 0); // If we set epsilon to be 0, let's set a minimum threshold float max_error = MAX((float)epsilon, __MIN_EPSILON_ERROR); int error_count = 0; bool result = true; for (unsigned int i = 0; i < len; ++i) { float diff = fabs(static_cast(reference[i]) - static_cast(data[i])); bool comp = (diff < max_error); result &= comp; if (!comp) { error_count++; } } if (threshold == 0.0f) { if (error_count) { printf("total # of errors = %d\n", error_count); } return (error_count == 0) ? true : false; } else { if (error_count) { printf("%4.2f(%%) of bytes mismatched (count=%d)\n", static_cast(error_count) * 100 / static_cast(len), error_count); } return ((len * threshold > error_count) ? true : false); } } inline void sdkDumpBin(void *data, unsigned int bytes, const char *filename) { printf("sdkDumpBin: <%s>\n", filename); FILE *fp; FOPEN(fp, filename, "wb"); fwrite(data, bytes, 1, fp); fflush(fp); fclose(fp); } inline bool sdkCompareBin2BinUint(const char *src_file, const char *ref_file, unsigned int nelements, const float epsilon, const float threshold, char *exec_path) { unsigned int *src_buffer, *ref_buffer; FILE *src_fp = NULL, *ref_fp = NULL; uint64_t error_count = 0; size_t fsize = 0; if (FOPEN_FAIL(FOPEN(src_fp, src_file, "rb"))) { printf("compareBin2Bin unable to open src_file: %s\n", src_file); error_count++; } char *ref_file_path = sdkFindFilePath(ref_file, exec_path); if (ref_file_path == NULL) { printf("compareBin2Bin unable to find <%s> in <%s>\n", ref_file, exec_path); printf(">>> Check info.xml and [project//data] folder <%s> <<<\n", ref_file); printf("Aborting comparison!\n"); printf(" FAILED\n"); error_count++; if (src_fp) { fclose(src_fp); } if (ref_fp) { fclose(ref_fp); } } else { if (FOPEN_FAIL(FOPEN(ref_fp, ref_file_path, "rb"))) { printf( "compareBin2Bin " " unable to open ref_file: %s\n", ref_file_path); error_count++; } if (src_fp && ref_fp) { src_buffer = (unsigned int *)malloc(nelements * sizeof(unsigned int)); ref_buffer = (unsigned int *)malloc(nelements * sizeof(unsigned int)); fsize = fread(src_buffer, nelements, sizeof(unsigned int), src_fp); fsize = fread(ref_buffer, nelements, sizeof(unsigned int), ref_fp); printf( "> compareBin2Bin nelements=%d," " epsilon=%4.2f, threshold=%4.2f\n", nelements, epsilon, threshold); printf(" src_file <%s>, size=%d bytes\n", src_file, static_cast(fsize)); printf(" ref_file <%s>, size=%d bytes\n", ref_file_path, static_cast(fsize)); if (!compareData(ref_buffer, src_buffer, nelements, epsilon, threshold)) { error_count++; } fclose(src_fp); fclose(ref_fp); free(src_buffer); free(ref_buffer); } else { if (src_fp) { fclose(src_fp); } if (ref_fp) { fclose(ref_fp); } } } if (error_count == 0) { printf(" OK\n"); } else { printf(" FAILURE: %d errors...\n", (unsigned int)error_count); } return (error_count == 0); // returns true if all pixels pass } inline bool sdkCompareBin2BinFloat(const char *src_file, const char *ref_file, unsigned int nelements, const float epsilon, const float threshold, char *exec_path) { float *src_buffer = NULL, *ref_buffer = NULL; FILE *src_fp = NULL, *ref_fp = NULL; size_t fsize = 0; uint64_t error_count = 0; if (FOPEN_FAIL(FOPEN(src_fp, src_file, "rb"))) { printf("compareBin2Bin unable to open src_file: %s\n", src_file); error_count = 1; } char *ref_file_path = sdkFindFilePath(ref_file, exec_path); if (ref_file_path == NULL) { printf("compareBin2Bin unable to find <%s> in <%s>\n", ref_file, exec_path); printf(">>> Check info.xml and [project//data] folder <%s> <<<\n", exec_path); printf("Aborting comparison!\n"); printf(" FAILED\n"); error_count++; if (src_fp) { fclose(src_fp); } if (ref_fp) { fclose(ref_fp); } } else { if (FOPEN_FAIL(FOPEN(ref_fp, ref_file_path, "rb"))) { printf("compareBin2Bin unable to open ref_file: %s\n", ref_file_path); error_count = 1; } if (src_fp && ref_fp) { src_buffer = reinterpret_cast(malloc(nelements * sizeof(float))); ref_buffer = reinterpret_cast(malloc(nelements * sizeof(float))); printf( "> compareBin2Bin nelements=%d, epsilon=%4.2f," " threshold=%4.2f\n", nelements, epsilon, threshold); fsize = fread(src_buffer, sizeof(float), nelements, src_fp); printf(" src_file <%s>, size=%d bytes\n", src_file, static_cast(fsize * sizeof(float))); fsize = fread(ref_buffer, sizeof(float), nelements, ref_fp); printf(" ref_file <%s>, size=%d bytes\n", ref_file_path, static_cast(fsize * sizeof(float))); if (!compareDataAsFloatThreshold( ref_buffer, src_buffer, nelements, epsilon, threshold)) { error_count++; } fclose(src_fp); fclose(ref_fp); free(src_buffer); free(ref_buffer); } else { if (src_fp) { fclose(src_fp); } if (ref_fp) { fclose(ref_fp); } } } if (error_count == 0) { printf(" OK\n"); } else { printf(" FAILURE: %d errors...\n", (unsigned int)error_count); } return (error_count == 0); // returns true if all pixels pass } inline bool sdkCompareL2fe(const float *reference, const float *data, const unsigned int len, const float epsilon) { assert(epsilon >= 0); float error = 0; float ref = 0; for (unsigned int i = 0; i < len; ++i) { float diff = reference[i] - data[i]; error += diff * diff; ref += reference[i] * reference[i]; } float normRef = sqrtf(ref); if (fabs(ref) < 1e-7) { #ifdef _DEBUG std::cerr << "ERROR, reference l2-norm is 0\n"; #endif return false; } float normError = sqrtf(error); error = normError / normRef; bool result = error < epsilon; #ifdef _DEBUG if (!result) { std::cerr << "ERROR, l2-norm error " << error << " is greater than epsilon " << epsilon << "\n"; } #endif return result; } inline bool sdkLoadPPMub(const char *file, unsigned char **data, unsigned int *w, unsigned int *h) { unsigned int channels; return __loadPPM(file, data, w, h, &channels); } inline bool sdkLoadPPM4ub(const char *file, unsigned char **data, unsigned int *w, unsigned int *h) { unsigned char *idata = 0; unsigned int channels; if (__loadPPM(file, &idata, w, h, &channels)) { // pad 4th component int size = *w * *h; // keep the original pointer unsigned char *idata_orig = idata; *data = (unsigned char *)malloc(sizeof(unsigned char) * size * 4); unsigned char *ptr = *data; for (int i = 0; i < size; i++) { *ptr++ = *idata++; *ptr++ = *idata++; *ptr++ = *idata++; *ptr++ = 0; } free(idata_orig); return true; } else { free(idata); return false; } } inline bool sdkComparePPM(const char *src_file, const char *ref_file, const float epsilon, const float threshold, bool verboseErrors) { unsigned char *src_data, *ref_data; uint64_t error_count = 0; unsigned int ref_width, ref_height; unsigned int src_width, src_height; if (src_file == NULL || ref_file == NULL) { if (verboseErrors) { std::cerr << "PPMvsPPM: src_file or ref_file is NULL." " Aborting comparison\n"; } return false; } if (verboseErrors) { std::cerr << "> Compare (a)rendered: <" << src_file << ">\n"; std::cerr << "> (b)reference: <" << ref_file << ">\n"; } if (sdkLoadPPM4ub(ref_file, &ref_data, &ref_width, &ref_height) != true) { if (verboseErrors) { std::cerr << "PPMvsPPM: unable to load ref image file: " << ref_file << "\n"; } return false; } if (sdkLoadPPM4ub(src_file, &src_data, &src_width, &src_height) != true) { std::cerr << "PPMvsPPM: unable to load src image file: " << src_file << "\n"; return false; } if (src_height != ref_height || src_width != ref_width) { if (verboseErrors) { std::cerr << "PPMvsPPM: source and ref size mismatch (" << src_width << "," << src_height << ")vs(" << ref_width << "," << ref_height << ")\n"; } } if (verboseErrors) { std::cerr << "PPMvsPPM: comparing images size (" << src_width << "," << src_height << ") epsilon(" << epsilon << "), threshold(" << threshold * 100 << "%)\n"; } if (compareData(ref_data, src_data, src_width * src_height * 4, epsilon, threshold) == false) { error_count = 1; } if (error_count == 0) { if (verboseErrors) { std::cerr << " OK\n\n"; } } else { if (verboseErrors) { std::cerr << " FAILURE! " << error_count << " errors...\n\n"; } } // returns true if all pixels pass return (error_count == 0) ? true : false; } inline bool sdkComparePGM(const char *src_file, const char *ref_file, const float epsilon, const float threshold, bool verboseErrors) { unsigned char *src_data = 0, *ref_data = 0; uint64_t error_count = 0; unsigned int ref_width, ref_height; unsigned int src_width, src_height; if (src_file == NULL || ref_file == NULL) { if (verboseErrors) { std::cerr << "PGMvsPGM: src_file or ref_file is NULL." " Aborting comparison\n"; } return false; } if (verboseErrors) { std::cerr << "> Compare (a)rendered: <" << src_file << ">\n"; std::cerr << "> (b)reference: <" << ref_file << ">\n"; } if (sdkLoadPPMub(ref_file, &ref_data, &ref_width, &ref_height) != true) { if (verboseErrors) { std::cerr << "PGMvsPGM: unable to load ref image file: " << ref_file << "\n"; } return false; } if (sdkLoadPPMub(src_file, &src_data, &src_width, &src_height) != true) { std::cerr << "PGMvsPGM: unable to load src image file: " << src_file << "\n"; return false; } if (src_height != ref_height || src_width != ref_width) { if (verboseErrors) { std::cerr << "PGMvsPGM: source and ref size mismatch (" << src_width << "," << src_height << ")vs(" << ref_width << "," << ref_height << ")\n"; } } if (verboseErrors) std::cerr << "PGMvsPGM: comparing images size (" << src_width << "," << src_height << ") epsilon(" << epsilon << "), threshold(" << threshold * 100 << "%)\n"; if (compareData(ref_data, src_data, src_width * src_height, epsilon, threshold) == false) { error_count = 1; } if (error_count == 0) { if (verboseErrors) { std::cerr << " OK\n\n"; } } else { if (verboseErrors) { std::cerr << " FAILURE! " << error_count << " errors...\n\n"; } } // returns true if all pixels pass return (error_count == 0) ? true : false; } #endif // COMMON_HELPER_IMAGE_H_