Blob Blame Raw
#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" /* igs::image::rgba */

//------------------------------------------------------------
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 {
			/* 制限(in_min〜in_max) */
			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;
				}
			}
			/* 正規化(0〜1) */
			value = (value - in_min) / (in_max - in_min);

			/* Gamma変換 */
			if ((1.0 != gamma) && (0.0 != gamma)) {
				value = pow(value, 1.0 / gamma);
			}
		}
		/* Outputの範囲にスケール変換 */
		value = out_min + value * (out_max - out_min);
		/* 0...1の間で制限かける */
		if (value < 0.0) {
			value = 0.0;
		} else if (1.0 < value) {
			value = 1.0;
		}
	}
	/* 0〜1.0 --> 0〜mul_maxスケール変換し、整数値化 */
	return floor(value * mul_max);
}
//------------------------------------------------------------
void level_ctable_template_(
	const unsigned int channels,
	const bool *act_sw,			// user setting
	const int *in_min,			// image pixel value
	const int *in_max,			// image pixel value
	const double *in_min_shift, // user settting
	const double *in_max_shift, // user settting
	const double *out_min,		// user settting
	const double *out_max,		// user settting
	const double *gamma,		// user settting
	const unsigned int div_num,
	std::vector<std::vector<unsigned int>> &table_array
	/*
	std::vector< std::vector<T> > &table_array
 とするとvc2005mdで他プログラムとのリンク時、
	allocatorの2重定義でエラーとなる。
	std::vector< std::vector<unsigned int> >&table_array
 ならOK
 2009-01-27
*/
	)
{
	const double div_val = static_cast<double>(div_num);
	const double mul_val = div_val + 0.999999;
#if defined _WIN32 // vc compile_type
	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)
{
/* 1.まず最大値、最小値を求める */
#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;
		}
	}

	/* 2.最大値、最小値から変換テーブルを求める */
	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);

	/* 3.変換テーブルを使ってlevel変換する */
	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) { /* grayscale */
		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,			// channels array
	const double *in_min_shift, // channels array
	const double *in_max_shift, // channels array
	const double *out_min,		// channels array
	const double *out_max,		// channels array
	const double *gamma			// channels array
	)
{
	if ((igs::image::rgba::siz != channels) &&
		(igs::image::rgb::siz != channels) &&
		(1 != channels) /* grayscale */
		) {
		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");
	}
}