void distort_2d_data_transformer::transform(
		const float * data,
		float * data_transformed,
		const layer_configuration_specific& original_config,
		unsigned int sample_id)
	{
		if (original_config.dimension_sizes.size() < 2)
			throw neural_network_exception((boost::format("distort_2d_data_transformer is processing at least 2d data, data is passed with number of dimensions %1%") % original_config.dimension_sizes.size()).str());

		float rotation_angle = rotate_angle_distribution.min();
		float scale = scale_distribution.min();
		float shift_x = shift_x_distribution.min();
		float shift_y = shift_y_distribution.min();
		bool flip_around_x_axis = (flip_around_x_distribution.min() == 1);
		bool flip_around_y_axis = (flip_around_y_distribution.min() == 1);
		float stretch = stretch_distribution.min();
		float stretch_angle = stretch_angle_distribution.min();
		float perspective_reverse_distance = perspective_reverse_distance_distribution.min();
		float perspective_distance = std::numeric_limits<float>::max();
		float perspective_angle = perspective_angle_distribution.min();

		{
			std::lock_guard<std::mutex> lock(gen_stream_mutex);

			if (apply_rotate_angle_distribution)
				rotation_angle = rotate_angle_distribution(generator);
			if (apply_scale_distribution)
				scale = scale_distribution(generator);
			if (apply_shift_x_distribution)
				shift_x = shift_x_distribution(generator);
			if (apply_shift_y_distribution)
				shift_y = shift_y_distribution(generator);
			if (flip_around_x_distribution.max() > flip_around_x_distribution.min())
				flip_around_x_axis = (flip_around_x_distribution(generator) == 1);
			if (flip_around_y_distribution.max() > flip_around_y_distribution.min())
				flip_around_y_axis = (flip_around_y_distribution(generator) == 1);
			if (apply_stretch_distribution)
				stretch = stretch_distribution(generator);
			stretch_angle = stretch_angle_distribution(generator);
			if (apply_perspective_reverse_distance_distribution)
			{
				perspective_reverse_distance = perspective_reverse_distance_distribution(generator);
				if (perspective_reverse_distance > 0.0F)
					perspective_distance = 1.0F / perspective_reverse_distance;
			}
			perspective_angle = perspective_angle_distribution(generator);
		}

		unsigned int neuron_count_per_image = original_config.dimension_sizes[0] * original_config.dimension_sizes[1];
		unsigned int image_count = original_config.get_neuron_count() / neuron_count_per_image;
		for(unsigned int image_id = 0; image_id < image_count; ++image_id)
		{
			cv::Mat1f dest_image(static_cast<int>(original_config.dimension_sizes[1]), static_cast<int>(original_config.dimension_sizes[0]), data_transformed + (image_id * neuron_count_per_image));
			cv::Mat1f image(static_cast<int>(original_config.dimension_sizes[1]), static_cast<int>(original_config.dimension_sizes[0]), const_cast<float *>(data) + (image_id * neuron_count_per_image));

			if ((rotation_angle != 0.0F) || (scale != 1.0F) || (shift_x != 0.0F) || (shift_y != 0.0F) || (stretch != 1.0F) || (perspective_distance != std::numeric_limits<float>::max()))
			{
				data_transformer_util::stretch_rotate_scale_shift_perspective(
					dest_image,
					image,
					cv::Point2f(static_cast<float>(image.cols) * 0.5F, static_cast<float>(image.rows) * 0.5F),
					rotation_angle,
					scale,
					shift_x,
					shift_y,
					stretch,
					stretch_angle,
					perspective_distance,
					perspective_angle,
					border_value);

				data_transformer_util::flip(
					dest_image,
					flip_around_x_axis,
					flip_around_y_axis);
			}
			else
			{
				data_transformer_util::flip(
					dest_image,
					image,
					flip_around_x_axis,
					flip_around_y_axis);
			}
		}
	}
	void distort_2d_data_transformer::transform(
		const void * data,
		void * data_transformed,
		neuron_data_type::input_type type,
		const layer_configuration_specific& original_config,
		unsigned int sample_id)
	{
		if (type != neuron_data_type::type_byte)
			throw neural_network_exception("distort_2d_data_transformer is implemented for data stored as bytes only");

		if (original_config.dimension_sizes.size() < 2)
			throw neural_network_exception((boost::format("distort_2d_data_transformer is processing at least 2d data, data is passed with number of dimensions %1%") % original_config.dimension_sizes.size()).str());

		float rotation_angle = rotate_angle_distribution.min();
		if (rotate_angle_distribution.max() > rotate_angle_distribution.min())
			rotation_angle = rotate_angle_distribution(generator);
		float scale = scale_distribution.min();
		if (scale_distribution.max() > scale_distribution.min())
			scale = scale_distribution(generator);
		float shift_x = shift_x_distribution.min();
		if (shift_x_distribution.max() > shift_x_distribution.min())
			shift_x = shift_x_distribution(generator);
		float shift_y = shift_y_distribution.min();
		if (shift_y_distribution.max() > shift_y_distribution.min())
			shift_y = shift_y_distribution(generator);
		bool flip_around_x_axis = (flip_around_x_distribution.min() == 1);
		if (flip_around_x_distribution.max() > flip_around_x_distribution.min())
			flip_around_x_axis = (flip_around_x_distribution(generator) == 1);
		bool flip_around_y_axis = (flip_around_y_distribution.min() == 1);
		if (flip_around_y_distribution.max() > flip_around_y_distribution.min())
			flip_around_y_axis = (flip_around_y_distribution(generator) == 1);
		float stretch = stretch_distribution.min();
		if (stretch_distribution.max() > stretch_distribution.min())
			stretch = stretch_distribution(generator);
		float stretch_angle = stretch_angle_distribution.min();
		if (stretch_angle_distribution.max() > stretch_angle_distribution.min())
			stretch_angle = stretch_angle_distribution(generator);
		float perspective_reverse_distance = perspective_reverse_distance_distribution.min();
		if (perspective_reverse_distance_distribution.max() > perspective_reverse_distance_distribution.min())
			perspective_reverse_distance = perspective_reverse_distance_distribution(generator);
		float perspective_distance = std::numeric_limits<float>::max();
		if (perspective_reverse_distance > 0.0F)
			perspective_distance = 1.0F / perspective_reverse_distance;
		float perspective_angle = perspective_angle_distribution.min();
		if (perspective_angle_distribution.max() > perspective_angle_distribution.min())
			perspective_angle = perspective_angle_distribution(generator);

		unsigned int neuron_count_per_image = original_config.dimension_sizes[0] * original_config.dimension_sizes[1];
		unsigned int image_count = original_config.get_neuron_count() / neuron_count_per_image;
		for(unsigned int image_id = 0; image_id < image_count; ++image_id)
		{
			cv::Mat1b image(static_cast<int>(original_config.dimension_sizes[1]), static_cast<int>(original_config.dimension_sizes[0]), static_cast<unsigned char *>(data_transformed) + (image_id * neuron_count_per_image));

			if ((rotation_angle != 0.0F) || (scale != 1.0F) || (shift_x != 0.0F) || (shift_y != 0.0F) || (stretch != 1.0F) || (perspective_distance != std::numeric_limits<float>::max()))
			{
				data_transformer_util::stretch_rotate_scale_shift_perspective(
					image,
					cv::Point2f(static_cast<float>(image.cols) * 0.5F, static_cast<float>(image.rows) * 0.5F),
					rotation_angle,
					scale,
					shift_x,
					shift_y,
					stretch,
					stretch_angle,
					perspective_distance,
					perspective_angle,
					border_value);
			}

			data_transformer_util::flip(
				image,
				flip_around_x_axis,
				flip_around_y_axis);
		}
	}