/* * Copyright 2018, Theodore Chabardes * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "src/paraws.hpp" #include "src/io_png_raw.hpp" #include "src/output_color.hpp" #include #include #include // Portable version of getopt. #define OPTPARSE_IMPLEMENTATION #define OPTPARSE_API static #include "src/optparse.h" #ifdef BENCH #include "src/bench.hpp" #include "src/debug.hpp" #include "src/label.hpp" #endif // BENCH bool check_compliance_image(const char *path, bool is_uint8, bool is_uint16, bool is_uint32, size_t x, size_t y, std::string &ext) { if (path == NULL) return false; std::string path_str = std::string(path); ext = path_str.substr(path_str.find_last_of(".") + 1); if (ext == "raw" && (x == 0 || y == 0)) return false; return (is_uint8 + is_uint16 + is_uint32) == 1 && (ext == "png" || ext == "raw"); } bool check_structuring_element(bool cross, bool hexa, bool square, bool cross3d, bool cube) { return (cross + hexa + square + cross3d + cube) == 1; } void usage(const char *execName) { std::cout << execName << " [options]" << std::endl << "Options:" << std::endl #ifdef BENCH << "-A | --paraws_bench Benchs paraws" << std::endl << "-B | --hqws_bench Benchs hqws" << std::endl #endif // BENCH << "-h | --help Print this help" << std::endl << "-i | --input_uint8 Path to uint8 input image" << std::endl << "-I | --input_uint16 Path to uint16 input image" << std::endl << "-m | --marker_uint8 Path to uint8 marker image" << std::endl << "-M | --marker_uint16 Path to uint16 marker image" << std::endl << "-N | --marker_uint32 Path to uint32 marker image" << std::endl << "-o | --output_uint8 Path to uint8 output image" << std::endl << "-O | --output_uint16 Path to uint16 output image" << std::endl << "-P | --output_uint32 Path to uint32 output image" << std::endl << "-C | --cross 4-connex structuring element" << std::endl << "-H | --hexa Hexagonal structuring element" << std::endl << "-S | --square 8-connex structuring element" << std::endl << "-R | --cross3d 6-connex structuring element (3d)" << std::endl << "-U | --cube 26-connex structuring element (3d)" << std::endl << "-x | --size_x Width dimension (only for raw images)" << std::endl << "-y | --size_y Height dimension (only for raw images)" << std::endl << "-z | --size_z Depth dimension (only for raw images)" << std::endl << "-w | --without_labels_limit Ignore errors due to label number " "overflow" << std::endl << "-g | --export_RGB Export the output image in RGB format." << std::endl << std::endl << "This program needs at least one input image, one output image and a " "structuring element. Only png and raw format are supported, and it " "should be identical for the input and the ouput. If raw format is " "used, dimensions must be specified. An additional markers image can " "be used for constrained watershed, whose type should match the " "output image.." << std::endl; } template int exec_paraws(const char *inp, const char *outp, const char *markp, std::string ext1, std::string ext2, std::string ext3, size_t &x, size_t &y, size_t &z, const structuring_element &se, bool ignore_label_limit, bool export_RGB) { Tin *im = NULL; Tout *out = NULL; int result = RES_OK; if (ext1 == "png") { io_png_read(im, inp, &x, &y); } else if (ext1 == "raw") { io_raw_read(im, inp, x, y, z); } if (im == NULL) return ERROR_IN; out = new Tout[x * y * z]; if (markp) { Tout *markers = NULL; if (ext3 == "png") { io_png_read(markers, markp, &x, &y); } else if (ext3 == "raw") { io_raw_read(markers, markp, x, y, z); } if (markers == NULL) return ERROR_IN; result = paraws(im, markers, out, x, y, z, se, ignore_label_limit); delete[] markers; } else { result = paraws(im, out, x, y, z, se, ignore_label_limit); } if(result == RES_OK) { if (!export_RGB) { if (ext2 == "png") { if (io_png_write(outp, out, x, y) != 0) result = ERROR_IN; } else if (ext2 == "raw") { if (io_raw_write(outp, out, x, y, z) != 0) result = ERROR_IN; } } else { io_png_write_color (outp, out, x, y); } } delete[] im; delete[] out; return result; } template ERROR benchmark(size_t x, size_t y, size_t z, const structuring_element &se, bool ignore_label_limit, bool paraws_bench, bool hqws_bench) { Tin *im = new Tin[x * y * z]; Tout *out = new Tout[x * y * z](); random_matrix(im, false, domain(x, y, z)); ERROR result = RES_OK; if (paraws_bench) { result = paraws(im, out, x, y, z, se, ignore_label_limit); if (result != RES_OK) { delete[] im; delete[] out; return result; } } if (hqws_bench) { result = hqws(im, out, x, y, z, se, ignore_label_limit); } delete[] im; delete[] out; return result; } int main(const int __attribute__((__unused__)) argc, char **argv) { // Data types bool in8 = false, in16 = false, mark8 = false, mark16 = false, mark32 = false, out8 = false, out16 = false, out32 = false; // Connectivity bool cross = false, hexa = false, square = false, cross3d = false, cube = false; #ifdef BENCH // Bench bool paraws_bench = false; bool hqws_bench = false; #endif // BENCH // paths char *inp = NULL, *markp = NULL, *outp = NULL; // Dimensions size_t x = 0, y = 0, z = 1; // Default value for z. // Labels overflow bool ignore_label_limit = false; // Export the output image in RGB format for visualisation purposes. bool export_RGB = false; struct optparse_long long_options[] = { #ifdef BENCH {"paraws_bench", 'A', OPTPARSE_NONE}, {"hqws_bench", 'B', OPTPARSE_NONE}, #endif // BENCH {"input_uint8", 'i', OPTPARSE_REQUIRED}, {"input_uint16", 'I', OPTPARSE_REQUIRED}, {"marker_uint8", 'm', OPTPARSE_REQUIRED}, {"marker_uint16", 'M', OPTPARSE_REQUIRED}, {"marker_uint32", 'N', OPTPARSE_REQUIRED}, {"output_uint8", 'o', OPTPARSE_REQUIRED}, {"output_uint16", 'O', OPTPARSE_REQUIRED}, {"output_uint32", 'P', OPTPARSE_REQUIRED}, {"cross", 'C', OPTPARSE_NONE}, {"hexa", 'H', OPTPARSE_NONE}, {"square", 'S', OPTPARSE_NONE}, {"cross3d", 'R', OPTPARSE_NONE}, {"cube", 'U', OPTPARSE_NONE}, {"size_x", 'x', OPTPARSE_REQUIRED}, {"size_y", 'y', OPTPARSE_REQUIRED}, {"size_z", 'z', OPTPARSE_REQUIRED}, {"without_labels_limit", 'w', OPTPARSE_NONE}, {"export_RGB", 'g', OPTPARSE_NONE}, {"help", 'h', OPTPARSE_NONE}, {0, 0, OPTPARSE_NONE}}; // optparse variables. int option; struct optparse options; optparse_init(&options, argv); while ((option = optparse_long(&options, long_options, NULL)) != -1) { switch (option) { #ifdef BENCH case 'A': paraws_bench = true; break; case 'B': hqws_bench = true; break; #endif // BENCH case 'i': inp = options.optarg; in8 = true; break; case 'I': inp = options.optarg; in16 = true; break; case 'm': markp = options.optarg; mark8 = true; break; case 'M': markp = options.optarg; mark16 = true; break; case 'N': markp = options.optarg; mark32 = true; break; case 'o': outp = options.optarg; out8 = true; break; case 'O': outp = options.optarg; out16 = true; break; case 'P': outp = options.optarg; out32 = true; break; case 'C': cross = true; break; case 'H': hexa = true; break; case 'S': square = true; break; case 'R': cross3d = true; break; case 'U': cube = true; break; case 'x': x = atoi(options.optarg); break; case 'y': y = atoi(options.optarg); break; case 'z': z = atoi(options.optarg); break; case 'w': ignore_label_limit = true; break; case 'g': export_RGB = true; break; case 'h': break; case '?': std::cerr << argv[0] << ": " << options.errmsg << std::endl; exit(EXIT_FAILURE); } } // If arguments remain, then failure. if ((optparse_arg(&options))) { std::cerr << argv[0] << ": " << "Unknown arguments." << std::endl; } // Checking if everything is correct, before reading... bool is_compliant = true; is_compliant &= check_structuring_element(cross, hexa, square, cross3d, cube); // Loading the appropriate structuring element. structuring_element se; if (cross) { se = get_structuring_element(SE_CROSS); } else if (hexa) { se = get_structuring_element(SE_HEX); } else if (square) { se = get_structuring_element(SE_SQUARE); } else if (cross3d) { se = get_structuring_element(SE_CROSS_3D); } else if (cube) { se = get_structuring_element(SE_CUBE); } // For Benchs #ifdef BENCH bool bench = paraws_bench || hqws_bench; if (bench) { // Something is wrong, print the help message and exit. if (!is_compliant) { usage(argv[0]); exit(EXIT_FAILURE); } if (x == 0 || y == 0) { usage(argv[0]); exit(EXIT_FAILURE); } if (benchmark(x, y, z, se, ignore_label_limit, paraws_bench, hqws_bench) == RES_OK) exit(EXIT_SUCCESS); exit(EXIT_FAILURE); } #endif // BENCH size_t x1 = x, y1 = y, z1 = z; std::string ext1; is_compliant &= check_compliance_image(inp, in8, in16, false, x1, y1, ext1); size_t x2 = x, y2 = y, z2 = z; std::string ext2; is_compliant &= check_compliance_image(outp, out8, out16, out32, x2, y2, ext2); is_compliant &= x1 == x2 && y1 == y2 && z1 == z2; is_compliant &= ext1 == ext2; // Color images are only available in png format. if (ext2 == "raw" && export_RGB) is_compliant = false; size_t x3 = x, y3 = y, z3 = z; std::string ext3; if (mark8 || mark16) { is_compliant &= check_compliance_image(markp, mark8, mark16, mark32, x3, y3, ext3); is_compliant &= ext2 == ext3; is_compliant &= (out8 == mark8) && (out16 == mark16) && (out32 == mark32); is_compliant &= x1 == x3 && y1 == y3 && z1 == z3; } // Something is wrong, print the help message and exit. if (!is_compliant) { usage(argv[0]); exit(EXIT_FAILURE); } int result = EXIT_SUCCESS; if (in8) { if (out8) { result = exec_paraws(inp, outp, markp, ext1, ext2, ext3, x, y, z, se, ignore_label_limit, export_RGB); } else if (out16) { result = exec_paraws( inp, outp, markp, ext1, ext2, ext3, x, y, z, se, ignore_label_limit, export_RGB); } else if (out32) { result = exec_paraws( inp, outp, markp, ext1, ext2, ext3, x, y, z, se, ignore_label_limit, export_RGB); } } else if (in16) { if (out8) { result = exec_paraws( inp, outp, markp, ext1, ext2, ext3, x, y, z, se, ignore_label_limit, export_RGB); } else if (out16) { result = exec_paraws( inp, outp, markp, ext1, ext2, ext3, x, y, z, se, ignore_label_limit, export_RGB); } else if (out32) { result = exec_paraws( inp, outp, markp, ext1, ext2, ext3, x, y, z, se, ignore_label_limit, export_RGB); } } if (result != 0) { if (result == ERROR_IN) { std::cerr << "Error: in/out error..." << std::endl; usage(argv[0]); } else if (result == ERROR_LABEL) { std::cerr << "Error: maximum label value reached..." << std::endl; } exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); }