| #include <cmath> // pow(-) |
| #include <stdexcept> /* std::domain_error(-) */ |
| #include <limits> // std::numeric_limits |
| #include <vector> |
| #include "igs_level_auto.h" |
| #include "igs_ifx_common.h" |
| |
| |
| namespace { |
| double level_value_(double value, double mul_max, bool act_sw, double in_min, |
| double in_max, double out_min, double out_max, |
| double gamma) { |
| if (act_sw) { |
| if (in_max == in_min) { |
| value = in_max; |
| } else { |
| |
| if (in_min < in_max) { |
| if (value < in_min) { |
| value = in_min; |
| } else if (in_max < value) { |
| value = in_max; |
| } |
| } else { |
| if (value < in_max) { |
| value = in_max; |
| } else if (in_min < value) { |
| value = in_min; |
| } |
| } |
| |
| value = (value - in_min) / (in_max - in_min); |
| |
| |
| if ((1.0 != gamma) && (0.0 != gamma)) { |
| value = pow(value, 1.0 / gamma); |
| } |
| } |
| |
| value = out_min + value * (out_max - out_min); |
| |
| if (value < 0.0) { |
| value = 0.0; |
| } else if (1.0 < value) { |
| value = 1.0; |
| } |
| } |
| |
| return floor(value * mul_max); |
| } |
| |
| void level_ctable_template_(const unsigned int channels, |
| const bool *act_sw, |
| const int *in_min, |
| const int *in_max, |
| const double *in_min_shift, |
| const double *in_max_shift, |
| const double *out_min, |
| const double *out_max, |
| const double *gamma, |
| const unsigned int div_num, |
| std::vector<std::vector<unsigned int>> &table_array |
| |
| |
| |
| |
| |
| |
| |
| |
| ) { |
| const double div_val = static_cast<double>(div_num); |
| const double mul_val = div_val + 0.999999; |
| #if defined _WIN32 |
| double in_min_[4], in_max_[4]; |
| #else |
| double in_min_[channels], in_max_[channels]; |
| #endif |
| for (unsigned int cc = 0; cc < channels; ++cc) { |
| in_min_[cc] = in_min_shift[cc] + in_min[cc] / div_val; |
| in_max_[cc] = in_max_shift[cc] + in_max[cc] / div_val; |
| } |
| table_array.resize(channels); |
| for (unsigned int cc = 0; cc < channels; ++cc) { |
| table_array[cc].resize(div_num + 1); |
| for (unsigned int yy = 0; yy <= div_num; ++yy) { |
| table_array[cc][yy] = static_cast<unsigned int>( |
| level_value_(yy / div_val, mul_val, act_sw[cc], in_min_[cc], |
| in_max_[cc], out_min[cc], out_max[cc], gamma[cc])); |
| } |
| } |
| } |
| |
| template <class T> |
| void change_template_(T *image_array, const int height, const int width, |
| const int channels, |
| |
| const bool *act_sw, const double *in_min_shift, |
| const double *in_max_shift, const double *out_min, |
| const double *out_max, const double *gamma) { |
| |
| #if defined _WIN32 |
| int in_min[4], in_max[4]; |
| #else |
| int in_min[channels], in_max[channels]; |
| #endif |
| for (int zz = 0; zz < channels; ++zz) { |
| in_min[zz] = in_max[zz] = image_array[zz]; |
| } |
| T *image_crnt = image_array; |
| for (int yy = 0; yy < height; ++yy) { |
| for (int xx = 0; xx < width; ++xx) { |
| for (int zz = 0; zz < channels; ++zz) { |
| if (image_crnt[zz] < in_min[zz]) { |
| in_min[zz] = image_crnt[zz]; |
| } else if (in_max[zz] < image_crnt[zz]) { |
| in_max[zz] = image_crnt[zz]; |
| } |
| } |
| image_crnt += channels; |
| } |
| } |
| |
| |
| std::vector<std::vector<unsigned int>> table_array; |
| |
| level_ctable_template_(channels, act_sw, in_min, in_max, in_min_shift, |
| in_max_shift, out_min, out_max, gamma, |
| std::numeric_limits<T>::max(), table_array); |
| |
| |
| image_crnt = image_array; |
| const int pixsize = height * width; |
| |
| if (igs::image::rgba::siz == channels) { |
| using namespace igs::image::rgba; |
| for (int ii = 0; ii < pixsize; ++ii, image_crnt += channels) { |
| image_crnt[red] = static_cast<T>(table_array[0][image_crnt[red]]); |
| image_crnt[gre] = static_cast<T>(table_array[1][image_crnt[gre]]); |
| image_crnt[blu] = static_cast<T>(table_array[2][image_crnt[blu]]); |
| image_crnt[alp] = static_cast<T>(table_array[3][image_crnt[alp]]); |
| } |
| } else if (igs::image::rgb::siz == channels) { |
| using namespace igs::image::rgb; |
| for (int ii = 0; ii < pixsize; ++ii, image_crnt += channels) { |
| image_crnt[red] = static_cast<T>(table_array[0][image_crnt[red]]); |
| image_crnt[gre] = static_cast<T>(table_array[1][image_crnt[gre]]); |
| image_crnt[blu] = static_cast<T>(table_array[2][image_crnt[blu]]); |
| } |
| } else if (1 == channels) { |
| for (int ii = 0; ii < pixsize; ++ii, ++image_crnt) { |
| image_crnt[0] = static_cast<T>(table_array[0][image_crnt[0]]); |
| } |
| } |
| |
| table_array.clear(); |
| } |
| } |
| |
| void igs::level_auto::change(unsigned char *image_array, |
| |
| const int height, const int width, |
| const int channels, const int bits, |
| |
| const bool *act_sw, |
| const double *in_min_shift, |
| const double *in_max_shift, |
| const double *out_min, |
| const double *out_max, |
| const double *gamma |
| ) { |
| if ((igs::image::rgba::siz != channels) && |
| (igs::image::rgb::siz != channels) && (1 != channels) |
| ) { |
| throw std::domain_error("Bad channels,Not rgba/rgb/grayscale"); |
| } |
| |
| if (std::numeric_limits<unsigned char>::digits == bits) { |
| change_template_(image_array, height, width, channels, act_sw, in_min_shift, |
| in_max_shift, out_min, out_max, gamma); |
| } else if (std::numeric_limits<unsigned short>::digits == bits) { |
| change_template_(reinterpret_cast<unsigned short *>(image_array), height, |
| width, channels, act_sw, in_min_shift, in_max_shift, |
| out_min, out_max, gamma); |
| } else { |
| throw std::domain_error("Bad bits,Not uchar/ushort"); |
| } |
| } |