
#include <hip/hip_runtime.h>
#include <hipcub/hipcub.hpp>
#include <hipcub/block/block_radix_sort.hpp>
#include <hipcub/warp/warp_reduce.hpp>
#include <hipcub/block/block_load.hpp>
#include <hipcub/block/block_discontinuity.hpp>
#include <hipcub/block/block_store.hpp>
#include <hipcub/block/block_reduce.hpp>
#include <hip/hip_math_constants.h>
#include "preprocess_kernel.cuh"

namespace AIKernel{

	Norm Norm::mean_std(const float mean[3], const float std[3], float alpha, ChannelType channel_type){

		Norm out;
		out.type  = NormType::MeanStd;
		out.alpha = alpha;
		out.channel_type = channel_type;
		memcpy(out.mean, mean, sizeof(out.mean));
		memcpy(out.std,  std,  sizeof(out.std));
		return out;
	}

	Norm Norm::alpha_beta(float alpha, float beta, ChannelType channel_type){

		Norm out;
		out.type = NormType::AlphaBeta;
		out.alpha = alpha;
		out.beta = beta;
		out.channel_type = channel_type;
		return out;
	}

	Norm Norm::None(){
		return Norm();
	}	

	#define INTER_RESIZE_COEF_BITS 11
	#define INTER_RESIZE_COEF_SCALE (1 << INTER_RESIZE_COEF_BITS)
	#define CAST_BITS (INTER_RESIZE_COEF_BITS << 1)
	template<typename _T>
	static __inline__ __device__ _T limit(_T value, _T low, _T high){
		return value < low ? low : (value > high ? high : value);
	}

	static __inline__ __device__ int resize_cast(int value){
		return (value + (1 << (CAST_BITS - 1))) >> CAST_BITS;
	}


    __global__ void computeAvgBright(
        const uint8_t* src, int* sum, int edge)
    {   
        int position = blockDim.x * blockIdx.x + threadIdx.x;
        if (position >= edge) return;

        // puDst = src + 当前图像的位置
        const uint8_t* puDst = src + position * 3;
        
        // BGR 转灰度  + 0.5 是为了四舍五入
        // Y = 0.299 R + 0.587 G + 0.114B
        // uint8_t temp = uint8_t(puDst[0]*0.114f + puDst[1]*0.587f +  puDst[2]*0.299f + 0.5f);
        atomicAdd(sum, int(puDst[0]*0.114f + puDst[1]*0.587f +  puDst[2]*0.299f + 0.5f));
    }


    __global__ void perspective_kernel(uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height, 
		uint8_t const_value_st, float* warp_affine_matrix_3_3, Norm norm, int edge){

		int position = blockDim.x * blockIdx.x + threadIdx.x;
		if (position >= edge) return;

		float m_x1 = warp_affine_matrix_3_3[0];
		float m_y1 = warp_affine_matrix_3_3[1];
		float m_z1 = warp_affine_matrix_3_3[2];

		float m_x2 = warp_affine_matrix_3_3[3];
		float m_y2 = warp_affine_matrix_3_3[4];
		float m_z2 = warp_affine_matrix_3_3[5];

        float m_x3 = warp_affine_matrix_3_3[6];
		float m_y3 = warp_affine_matrix_3_3[7];
		float m_z3 = warp_affine_matrix_3_3[8];

		int dx      = position % dst_width;
		int dy      = position / dst_width;

        // 原图位置
		float src_x = (m_x1 * dx + m_y1 * dy + m_z1)/(m_x3 * dx + m_y3 * dy + m_z3);
		float src_y = (m_x2 * dx + m_y2 * dy + m_z2)/(m_x3 * dx + m_y3 * dy + m_z3);
		float c0, c1, c2;

		if(src_x <= -1 || src_x >= src_width || src_y <= -1 || src_y >= src_height){
			// out of range
			c0 = const_value_st;
			c1 = const_value_st;
			c2 = const_value_st;
		}else{
			int y_low = floorf(src_y);
			int x_low = floorf(src_x);
			int y_high = y_low + 1;
			int x_high = x_low + 1;

			uint8_t const_value[] = {const_value_st, const_value_st, const_value_st};
			float ly    = src_y - y_low;
			float lx    = src_x - x_low;
			float hy    = 1 - ly;
			float hx    = 1 - lx;
			float w1    = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;
			uint8_t* v1 = const_value;
			uint8_t* v2 = const_value;
			uint8_t* v3 = const_value;
			uint8_t* v4 = const_value;
			if(y_low >= 0){
				if (x_low >= 0)
					v1 = src + y_low * src_line_size + x_low * 3;

				if (x_high < src_width)
					v2 = src + y_low * src_line_size + x_high * 3;
			}
			
			if(y_high < src_height){
				if (x_low >= 0)
					v3 = src + y_high * src_line_size + x_low * 3;

				if (x_high < src_width)
					v4 = src + y_high * src_line_size + x_high * 3;
			}
			
			// same to opencv
			c0 = floorf(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0] + 0.5f);
			c1 = floorf(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1] + 0.5f);
			c2 = floorf(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2] + 0.5f);
		}

		if(norm.channel_type == ChannelType::Invert){
			float t = c2;
			c2 = c0;  c0 = t;
		}

		if(norm.type == NormType::MeanStd){
			c0 = (c0 * norm.alpha - norm.mean[0]) / norm.std[0];
			c1 = (c1 * norm.alpha - norm.mean[1]) / norm.std[1];
			c2 = (c2 * norm.alpha - norm.mean[2]) / norm.std[2];
		}else if(norm.type == NormType::AlphaBeta){
			c0 = c0 * norm.alpha + norm.beta;
			c1 = c1 * norm.alpha + norm.beta;
			c2 = c2 * norm.alpha + norm.beta;
		}

		int area = dst_width * dst_height;
		float* pdst_c0 = dst + dy * dst_width + dx;
		float* pdst_c1 = pdst_c0 + area;
		float* pdst_c2 = pdst_c1 + area;
		*pdst_c0 = c0;
		*pdst_c1 = c1;
		*pdst_c2 = c2;

        // 验证效果时, 注释掉上面的模型预处理的代码(倒数第二个if到这一行的所有代码); 开启下面的代码以保存图像透视变换之后的数据, 在进行下一次验证
		// float* buf_pdst_c0 = dst + position * 3;
		// float* buf_pdst_c1 = buf_pdst_c0 + 1;
		// float* buf_pdst_c2 = buf_pdst_c1 + 1;
		// *buf_pdst_c0 = c0;
		// *buf_pdst_c1 = c1;
		// *buf_pdst_c2 = c2;
	}

	// same to opencv
	// reference: https://github.com/opencv/opencv/blob/24fcb7f8131f707717a9f1871b17d95e7cf519ee/modules/imgproc/src/resize.cpp
	// reference: https://github.com/openppl-public/ppl.cv/blob/04ef4ca48262601b99f1bb918dcd005311f331da/src/ppl/cv/cuda/resize.cu
	/*
	  可以考虑用同样实现的resize函数进行训练，python代码在：tools/test_resize.py
	*/
	__global__ void resize_bilinear_and_normalize_kernel(
		uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height, 
		float sx, float sy, Norm norm, int edge
	){
		int position = blockDim.x * blockIdx.x + threadIdx.x;
		if (position >= edge) return;

        // 得到 每一个 目标像素的 坐标，仅仅是坐标
		int dx      = position % dst_width;
		int dy      = position / dst_width;
        // 得到 对应的 原图像的 坐标
		float src_x = (dx + 0.5f) * sx - 0.5f;
		float src_y = (dy + 0.5f) * sy - 0.5f;
		float c0, c1, c2;

		int y_low = floorf(src_y);
		int x_low = floorf(src_x);
		int y_high = limit(y_low + 1, 0, src_height - 1);
		int x_high = limit(x_low + 1, 0, src_width - 1);
		y_low = limit(y_low, 0, src_height - 1);
		x_low = limit(x_low, 0, src_width - 1);

		int ly    = rint((src_y - y_low) * INTER_RESIZE_COEF_SCALE);
		int lx    = rint((src_x - x_low) * INTER_RESIZE_COEF_SCALE);
		int hy    = INTER_RESIZE_COEF_SCALE - ly;
		int hx    = INTER_RESIZE_COEF_SCALE - lx;

		int w1    = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;
		// float* pdst = dst + dy * dst_width + dx * 3;
		uint8_t* v1 = src + y_low * src_line_size + x_low * 3;
		uint8_t* v2 = src + y_low * src_line_size + x_high * 3;
		uint8_t* v3 = src + y_high * src_line_size + x_low * 3;
		uint8_t* v4 = src + y_high * src_line_size + x_high * 3;

		c0 = resize_cast(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0]);
		c1 = resize_cast(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1]);
		c2 = resize_cast(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2]);

		if(norm.channel_type == ChannelType::Invert){
			float t = c2;
			c2 = c0;  c0 = t;
		}

		if(norm.type == NormType::MeanStd){
			c0 = (c0 * norm.alpha - norm.mean[0]) / norm.std[0];
			c1 = (c1 * norm.alpha - norm.mean[1]) / norm.std[1];
			c2 = (c2 * norm.alpha - norm.mean[2]) / norm.std[2];
		}else if(norm.type == NormType::AlphaBeta){
			c0 = c0 * norm.alpha + norm.beta;
			c1 = c1 * norm.alpha + norm.beta;
			c2 = c2 * norm.alpha + norm.beta;
		}

		int area = dst_width * dst_height;
		float* pdst_c0 = dst + dy * dst_width + dx;
		float* pdst_c1 = pdst_c0 + area;
		float* pdst_c2 = pdst_c1 + area;
		*pdst_c0 = c0;
		*pdst_c1 = c1;
		*pdst_c2 = c2;

        // float* buf_pdst_c0 = dst + position * 3;
		// float* buf_pdst_c1 = buf_pdst_c0 + 1;
		// float* buf_pdst_c2 = buf_pdst_c1 + 1;
		// *buf_pdst_c0 = c0;
		// *buf_pdst_c1 = c1;
		// *buf_pdst_c2 = c2;
	}

    __global__ void resize_bilinear_and_normalize_kernel_two_steps(
		uint8_t* src, float* resize_buffer, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height, 
		float sx, float sy, Norm norm, int edge
	){
		int position = blockDim.x * blockIdx.x + threadIdx.x;
		if (position >= edge) return;

        // 当前是第几行，第几列数据
		int dx      = position % dst_width;
		int dy      = position / dst_width;

		float src_x = (dx + 0.5f) * sx - 0.5f;
		float src_y = (dy + 0.5f) * sy - 0.5f;
		float c0, c1, c2;

		int y_low = floorf(src_y);
		int x_low = floorf(src_x);
		int y_high = limit(y_low + 1, 0, src_height - 1);
		int x_high = limit(x_low + 1, 0, src_width - 1);
		y_low = limit(y_low, 0, src_height - 1);
		x_low = limit(x_low, 0, src_width - 1);

		int ly    = rint((src_y - y_low) * INTER_RESIZE_COEF_SCALE);
		int lx    = rint((src_x - x_low) * INTER_RESIZE_COEF_SCALE);
		int hy    = INTER_RESIZE_COEF_SCALE - ly;
		int hx    = INTER_RESIZE_COEF_SCALE - lx;
        // w 为 插值权重
		int w1    = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;
        // float* temp_pdst = resize_buffer + dy * dst_width + dx * 3;
		// float* pdst = dst + dy * dst_width + dx * 3;

		uint8_t* v1 = src + y_low * src_line_size + x_low * 3;
		uint8_t* v2 = src + y_low * src_line_size + x_high * 3;
		uint8_t* v3 = src + y_high * src_line_size + x_low * 3;
		uint8_t* v4 = src + y_high * src_line_size + x_high * 3;

		c0 = resize_cast(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0]);
		c1 = resize_cast(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1]);
		c2 = resize_cast(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2]);
        

        int area = dst_width * dst_height;
		// float* buf_pdst_c0 = resize_buffer + dy * dst_width + dx;
		float* buf_pdst_c0 = resize_buffer + position * 3;
		float* buf_pdst_c1 = buf_pdst_c0 + 1;
		float* buf_pdst_c2 = buf_pdst_c1 + 1;
		*buf_pdst_c0 = c0;
		*buf_pdst_c1 = c1;
		*buf_pdst_c2 = c2;

		if(norm.channel_type == ChannelType::Invert){
			float t = c2;
			c2 = c0;  c0 = t;
		}

        

		if(norm.type == NormType::MeanStd){
			c0 = (c0 * norm.alpha - norm.mean[0]) / norm.std[0];
			c1 = (c1 * norm.alpha - norm.mean[1]) / norm.std[1];
			c2 = (c2 * norm.alpha - norm.mean[2]) / norm.std[2];
		}else if(norm.type == NormType::AlphaBeta){
			c0 = c0 * norm.alpha + norm.beta;
			c1 = c1 * norm.alpha + norm.beta;
			c2 = c2 * norm.alpha + norm.beta;
		}

		float* pdst_c0 = dst + dy * dst_width + dx;
		float* pdst_c1 = pdst_c0 + area;
		float* pdst_c2 = pdst_c1 + area;
		*pdst_c0 = c0;
		*pdst_c1 = c1;
		*pdst_c2 = c2;
	}


	__global__ void warp_affine_bilinear_and_normalize_plane_kernel(uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height, 
		uint8_t const_value_st, float* warp_affine_matrix_2_3, Norm norm, int edge){

		int position = blockDim.x * blockIdx.x + threadIdx.x;
		if (position >= edge) return;

		float m_x1 = warp_affine_matrix_2_3[0];
		float m_y1 = warp_affine_matrix_2_3[1];
		float m_z1 = warp_affine_matrix_2_3[2];
		float m_x2 = warp_affine_matrix_2_3[3];
		float m_y2 = warp_affine_matrix_2_3[4];
		float m_z2 = warp_affine_matrix_2_3[5];

		int dx      = position % dst_width;
		int dy      = position / dst_width;
		float src_x = m_x1 * dx + m_y1 * dy + m_z1;
		float src_y = m_x2 * dx + m_y2 * dy + m_z2;
		float c0, c1, c2;

		if(src_x <= -1 || src_x >= src_width || src_y <= -1 || src_y >= src_height){
			// out of range
			c0 = const_value_st;
			c1 = const_value_st;
			c2 = const_value_st;
		}else{
			int y_low = floorf(src_y);
			int x_low = floorf(src_x);
			int y_high = y_low + 1;
			int x_high = x_low + 1;

			uint8_t const_value[] = {const_value_st, const_value_st, const_value_st};
			float ly    = src_y - y_low;
			float lx    = src_x - x_low;
			float hy    = 1 - ly;
			float hx    = 1 - lx;
			float w1    = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;
			uint8_t* v1 = const_value;
			uint8_t* v2 = const_value;
			uint8_t* v3 = const_value;
			uint8_t* v4 = const_value;
			if(y_low >= 0){
				if (x_low >= 0)
					v1 = src + y_low * src_line_size + x_low * 3;

				if (x_high < src_width)
					v2 = src + y_low * src_line_size + x_high * 3;
			}
			
			if(y_high < src_height){
				if (x_low >= 0)
					v3 = src + y_high * src_line_size + x_low * 3;

				if (x_high < src_width)
					v4 = src + y_high * src_line_size + x_high * 3;
			}
			
			// same to opencv
			c0 = floorf(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0] + 0.5f);
			c1 = floorf(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1] + 0.5f);
			c2 = floorf(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2] + 0.5f);
		}

		if(norm.channel_type == ChannelType::Invert){
			float t = c2;
			c2 = c0;  c0 = t;
		}

		if(norm.type == NormType::MeanStd){
			c0 = (c0 * norm.alpha - norm.mean[0]) / norm.std[0];
			c1 = (c1 * norm.alpha - norm.mean[1]) / norm.std[1];
			c2 = (c2 * norm.alpha - norm.mean[2]) / norm.std[2];
		}else if(norm.type == NormType::AlphaBeta){
			c0 = c0 * norm.alpha + norm.beta;
			c1 = c1 * norm.alpha + norm.beta;
			c2 = c2 * norm.alpha + norm.beta;
		}

		int area = dst_width * dst_height;
		float* pdst_c0 = dst + dy * dst_width + dx;
		float* pdst_c1 = pdst_c0 + area;
		float* pdst_c2 = pdst_c1 + area;
		*pdst_c0 = c0;
		*pdst_c1 = c1;
		*pdst_c2 = c2;

        // float* buf_pdst_c0 = dst + position * 3;
		// float* buf_pdst_c1 = buf_pdst_c0 + 1;
		// float* buf_pdst_c2 = buf_pdst_c1 + 1;
		// *buf_pdst_c0 = c0;
		// *buf_pdst_c1 = c1;
		// *buf_pdst_c2 = c2;
	}

    __global__ void warp_affine_kernel(uint8_t* src, int src_line_size, int src_width, int src_height, uint8_t* dst, int dst_width, int dst_height, 
		uint8_t const_value_st, float* warp_affine_matrix_2_3, int edge){

		int position = blockDim.x * blockIdx.x + threadIdx.x;
		if (position >= edge) return;

		float m_x1 = warp_affine_matrix_2_3[0];
		float m_y1 = warp_affine_matrix_2_3[1];
		float m_z1 = warp_affine_matrix_2_3[2];
		float m_x2 = warp_affine_matrix_2_3[3];
		float m_y2 = warp_affine_matrix_2_3[4];
		float m_z2 = warp_affine_matrix_2_3[5];

		int dx      = position % dst_width;
		int dy      = position / dst_width;
		float src_x = m_x1 * dx + m_y1 * dy + m_z1;
		float src_y = m_x2 * dx + m_y2 * dy + m_z2;
		float c0, c1, c2;

		if(src_x <= -1 || src_x >= src_width || src_y <= -1 || src_y >= src_height){
			// out of range
			c0 = const_value_st;
			c1 = const_value_st;
			c2 = const_value_st;
		}else{
			int y_low = floorf(src_y);
			int x_low = floorf(src_x);
			int y_high = y_low + 1;
			int x_high = x_low + 1;

			uint8_t const_value[] = {const_value_st, const_value_st, const_value_st};
			float ly    = src_y - y_low;
			float lx    = src_x - x_low;
			float hy    = 1 - ly;
			float hx    = 1 - lx;
			float w1    = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;
			uint8_t* v1 = const_value;
			uint8_t* v2 = const_value;
			uint8_t* v3 = const_value;
			uint8_t* v4 = const_value;
			if(y_low >= 0){
				if (x_low >= 0)
					v1 = src + y_low * src_line_size + x_low * 3;

				if (x_high < src_width)
					v2 = src + y_low * src_line_size + x_high * 3;
			}
			
			if(y_high < src_height){
				if (x_low >= 0)
					v3 = src + y_high * src_line_size + x_low * 3;

				if (x_high < src_width)
					v4 = src + y_high * src_line_size + x_high * 3;
			}
			
			// same to opencv
			c0 = floorf(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0] + 0.5f);
			c1 = floorf(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1] + 0.5f);
			c2 = floorf(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2] + 0.5f);
		}


        uint8_t* buf_pdst_c0 = dst + position * 3;
		uint8_t* buf_pdst_c1 = buf_pdst_c0 + 1;
		uint8_t* buf_pdst_c2 = buf_pdst_c1 + 1;
		*buf_pdst_c0 = (uint8_t)c0;
		*buf_pdst_c1 = (uint8_t)c1;
		*buf_pdst_c2 = (uint8_t)c2;
	}

	__global__ void warp_affine_bilinear_and_normalize_focus_kernel(uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height, 
		uint8_t const_value_st, float* warp_affine_matrix_1_3, Norm norm, int edge){

		int position = blockDim.x * blockIdx.x + threadIdx.x;
		if (position >= edge) return;

		float m_k   = *warp_affine_matrix_1_3++;
		float m_b0  = *warp_affine_matrix_1_3++;
		float m_b1  = *warp_affine_matrix_1_3++;

		int dx      = position % dst_width;
		int dy      = position / dst_width;
		float src_x = m_k * dx + m_b0;
		float src_y = m_k * dy + m_b1;
		float c0, c1, c2;

		if(src_x <= -1 || src_x >= src_width || src_y <= -1 || src_y >= src_height){
			// out of range
			c0 = const_value_st;
			c1 = const_value_st;
			c2 = const_value_st;
		}else{
			int y_low = floorf(src_y);
			int x_low = floorf(src_x);
			int y_high = y_low + 1;
			int x_high = x_low + 1;

			uint8_t const_value[] = {const_value_st, const_value_st, const_value_st};
			float ly    = src_y - y_low;
			float lx    = src_x - x_low;
			float hy    = 1 - ly;
			float hx    = 1 - lx;
			float w1    = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx;
			uint8_t* v1 = const_value;
			uint8_t* v2 = const_value;
			uint8_t* v3 = const_value;
			uint8_t* v4 = const_value;
			if(y_low >= 0){
				if (x_low >= 0)
					v1 = src + y_low * src_line_size + x_low * 3;

				if (x_high < src_width)
					v2 = src + y_low * src_line_size + x_high * 3;
			}
			
			if(y_high < src_height){
				if (x_low >= 0)
					v3 = src + y_high * src_line_size + x_low * 3;

				if (x_high < src_width)
					v4 = src + y_high * src_line_size + x_high * 3;
			}

			// same to opencv
			c0 = floorf(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0] + 0.5f);
			c1 = floorf(w1 * v1[1] + w2 * v2[1] + w3 * v3[1] + w4 * v4[1] + 0.5f);
			c2 = floorf(w1 * v1[2] + w2 * v2[2] + w3 * v3[2] + w4 * v4[2] + 0.5f);
		}

		if(norm.channel_type == ChannelType::Invert){
			float t = c2;
			c2 = c0;  c0 = t;
		}

		if(norm.type == NormType::MeanStd){
			c0 = (c0 * norm.alpha - norm.mean[0]) / norm.std[0];
			c1 = (c1 * norm.alpha - norm.mean[1]) / norm.std[1];
			c2 = (c2 * norm.alpha - norm.mean[2]) / norm.std[2];
		}else if(norm.type == NormType::AlphaBeta){
			c0 = c0 * norm.alpha + norm.beta;
			c1 = c1 * norm.alpha + norm.beta;
			c2 = c2 * norm.alpha + norm.beta;
		}

		int after_focus_width  = dst_width / 2;
		int after_focus_height = dst_height / 2;
		int fdx = dx / 2;
		int fdy = dy / 2;
		int fc  = ((dx % 2) << 1) | (dy % 2);

		/**
		 *   x[..., ::2, ::2], x[..., 1::2, ::2], x[..., ::2, 1::2], x[..., 1::2, 1::2]
		 *    4                     fc
		 *    3                     [0, 1, 2]
		 *    after_focus_height    fdy
		 *    after_focus_width     fdx
		 *    左乘右加
		 **/

		float* pdst_c0 = dst + ((fc * 3 + 0) * after_focus_height + fdy) * after_focus_width + fdx;
		float* pdst_c1 = dst + ((fc * 3 + 1) * after_focus_height + fdy) * after_focus_width + fdx;
		float* pdst_c2 = dst + ((fc * 3 + 2) * after_focus_height + fdy) * after_focus_width + fdx;

		*pdst_c0 = c0;
		*pdst_c1 = c1;
		*pdst_c2 = c2;
	}

	__global__ void normalize_feature_kernel(float* feature_array, int num_feature, int feature_length, int edge){

		/*
		&   1 gz         bi.z   0
		*   1 gy         bi.y   0
        *   N NF         bi.x   ~
		*   1 1          ti.z   0
		*   F FL / 32    ti.y   ~
		*   Q 32         ti.x   ~
		*/

		int position = (blockIdx.x * blockDim.y + threadIdx.y) * blockDim.x + threadIdx.x;
        if (position >= edge) return;

		extern __shared__ float l2_norm[];

		int irow    = position / feature_length;
		int icol    = position % feature_length;

		if(icol == 0)
			l2_norm[irow] = 0;
		
		__syncthreads();

		float value = feature_array[position];
		atomicAdd(l2_norm + irow, value * value);

		__syncthreads();
		if(icol == 0)
			l2_norm[irow] = sqrt(l2_norm[irow]);

		__syncthreads();
		feature_array[position] = value / l2_norm[irow];
	}

    static __device__ uint8_t cast(float value){
        return value < 0 ? 0 : (value > 255 ? 255 : value);
    }

    static __global__ void convert_nv12_to_bgr_kernel(const uint8_t* y, const uint8_t* uv, int width, int height, int linesize, uint8_t* dst_bgr, int edge){

        int position = blockDim.x * blockIdx.x + threadIdx.x;
        if (position >= edge) return;

        int ox = position % width;
        int oy = position / width;
        const uint8_t& yvalue = y[oy * linesize + ox];
        int offset_uv = (oy >> 1) * linesize + (ox & 0xFFFFFFFE);
        const uint8_t& u = uv[offset_uv + 0];
        const uint8_t& v = uv[offset_uv + 1];
		dst_bgr[position * 3 + 0] = 1.164f * (yvalue - 16.0f) + 2.018f * (u - 128.0f);
		dst_bgr[position * 3 + 1] = 1.164f * (yvalue - 16.0f) - 0.813f * (v - 128.0f) - 0.391f * (u - 128.0f);
		dst_bgr[position * 3 + 2] = 1.164f * (yvalue - 16.0f) + 1.596f * (v - 128.0f);
    }

    __global__ void roi_kernel(uint8_t* src, int src_line_step, int src_width, 
                int src_height, uint8_t* dst, int x_start, int y_start, int roi_width, int roi_height, int edge)
    {   
        // blockDim.x = 512
        int position = blockDim.x * blockIdx.x + threadIdx.x;
        if (position >= edge) return;
        // printf(" position: %d \n", position);
        // 单纯计算像素位置, 不包含通道 OK
        int start_position = src_width * y_start + x_start;
        int end_position = src_width * (y_start + roi_height) + x_start + roi_width;
        // 调试, gan掉不好看的边界
        if (position >= start_position - 1 && position <= end_position)
        {   
            int sx = position % src_width;
            int sy = position / src_width;
            // 调试, gan掉不好看的边界
            if (sx >= x_start && sx <= x_start + roi_width)
            {   
                int dx = sx - x_start;
                int dy = sy - y_start;
                int dst_position = (dy * roi_width + dx ) * 3;

                uint8_t* psrc_c0 = src + position * 3;
                uint8_t* psrc_c1 = psrc_c0 + 1;
                uint8_t* psrc_c2 = psrc_c0 + 2;

                uint8_t* pdst_c0 = dst + dst_position;
                uint8_t* pdst_c1 = pdst_c0 + 1;
                uint8_t* pdst_c2 = pdst_c0 + 2;

                *pdst_c0     = *psrc_c0;
                *pdst_c1     = *psrc_c1;
                *pdst_c2     = *psrc_c2;
            }
        }
        else
        {
            return;
        }
    }


	/////////////////////////////////////////////////////////////////////////
	void convert_nv12_to_bgr_invoke(
		const uint8_t* y, const uint8_t* uv, int width, int height, int linesize, uint8_t* dst, hipStream_t stream){
			
		int total = width * height;
		dim3 grid = sg::grid_dims(total);
		dim3 block = sg::block_dims(total);

		checkHipKernel(convert_nv12_to_bgr_kernel<<<grid, block, 0, stream>>>(
			y, uv, width, height, linesize,
			dst, total
		));
	}

	void warp_affine_bilinear_and_normalize_plane(
		uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height,
		float* matrix_2_3, uint8_t const_value, const Norm& norm,
		hipStream_t stream) {
		
		int jobs   = dst_width * dst_height;
		auto grid  = sg::grid_dims(jobs);
		auto block = sg::block_dims(jobs);
		
		checkHipKernel(warp_affine_bilinear_and_normalize_plane_kernel <<<grid, block, 0, stream >>> (
			src, src_line_size,
			src_width, src_height, dst,
			dst_width, dst_height, const_value, matrix_2_3, norm, jobs
		));
	}

    void warp_affine(
		uint8_t* src, int src_line_size, int src_width, int src_height, uint8_t* dst, int dst_width, int dst_height,
		float* matrix_2_3, uint8_t const_value, hipStream_t& stream) {
		
		int jobs   = dst_width * dst_height;
		auto grid  = sg::grid_dims(jobs);
		auto block = sg::block_dims(jobs);
		
		checkHipKernel(warp_affine_kernel <<<grid, block, 0, stream >>> (
			src, src_line_size,
			src_width, src_height, dst,
			dst_width, dst_height, const_value, matrix_2_3, jobs
		));
	}

	
	void warp_affine_bilinear_and_normalize_focus(
        uint8_t* src, int src_line_size, int src_width, int src_height, 
        float* dst  , int dst_width, int dst_height,
        float* matrix_1_3, uint8_t const_value, const Norm& norm,
        hipStream_t stream){

		int jobs   = dst_width * dst_height;
		auto grid  = sg::grid_dims(jobs);
		auto block = sg::block_dims(jobs);
		
		checkHipKernel(warp_affine_bilinear_and_normalize_focus_kernel <<<grid, block, 0, stream >>> (
			src, src_line_size,
			src_width, src_height, dst,
			dst_width, dst_height, const_value, matrix_1_3, norm, jobs
		));
	}

	void resize_bilinear_and_normalize(
		uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height,
		const Norm& norm) {
		
		int jobs   = dst_width * dst_height;
		auto grid  = sg::grid_dims(jobs);
		auto block = sg::block_dims(jobs);
		
		resize_bilinear_and_normalize_kernel <<<grid, block, 0, nullptr >>> (
			src, src_line_size,
			src_width, src_height, dst,
			dst_width, dst_height, src_width/(float)dst_width, src_height/(float)dst_height, norm, jobs
		);

    

	}

    void resize_bilinear_and_normalize_two_steps(
		uint8_t* src, float* resize_buffer, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height,
		const Norm& norm,
		hipStream_t stream)
    {   
        int jobs   = dst_width * dst_height;
		auto grid  = sg::grid_dims(jobs);
		auto block = sg::block_dims(jobs);
        checkHipKernel(resize_bilinear_and_normalize_kernel_two_steps <<<grid, block, 0, stream >>> (
			src, resize_buffer, src_line_size,
			src_width, src_height, dst,
			dst_width, dst_height, src_width/(float)dst_width, src_height/(float)dst_height, norm, jobs
		));

    }
	
	void norm_feature(
        float* feature_array, int num_feature, int feature_length,
        hipStream_t stream
    ){
		Assert(feature_length % 32 == 0);

		int jobs   = num_feature * feature_length;
		auto grid  = dim3(num_feature);
		auto block = dim3(feature_length / 32, 32);
		checkHipKernel(normalize_feature_kernel <<<grid, block, num_feature * sizeof(float), stream >>> (
			feature_array, num_feature, feature_length, jobs
		));	
	}

    /************************************************************************
    函　数　名：GetImgAvgBright
    功　　　能：进行GPU上的图像亮度计算
    参　　　数：src         [IN] 图像地址(暂时为cpu地址); 
               width:      [IN] 图像宽度; 
               height:     [IN] 图像高度;
               linesize:   [IN] 图像一行的字节数; 
               &avgBright:  [OUT] 平均亮度 ; 
    返　回  值：无
    ************************************************************************/
    void GetImgAvgBright(
        const uint8_t* src, int width, int height, int linesize, float& avgBright
    )
    {   
        int total = width * height;

        dim3 grid = sg::grid_dims(total);
        dim3 block = sg::block_dims(total);

        int *sum_dev=nullptr, *sum_host=nullptr;

        // gpu, cpu 空间以及数据准备
        size_t space_size = sizeof(int); 

        checkHipRuntime(hipMalloc(&sum_dev, space_size));
        checkHipRuntime(hipMemset(sum_dev, 0, space_size));

        sum_host = (int*)malloc(space_size);
        memset(sum_host, 0, space_size);

        // 核函数执行
        computeAvgBright<<<grid, block, 0, nullptr>>> (
            src, sum_dev, total
        );

        hipMemcpy(sum_host, sum_dev, sizeof(int), hipMemcpyDeviceToHost);

        // 计算亮度
        avgBright = float(*sum_host) / float(total * 255);

        // 销毁显存 和 内存
        checkHipRuntime(hipFree(sum_dev));
        free(sum_host);
        sum_host = nullptr;
        sum_dev = nullptr;
    }


    void Perspective_gpu(
        uint8_t* src, int src_line_size, int src_width, int src_height, float* dst, int dst_width, int dst_height,
		float* matrix_3_3, uint8_t const_value, const Norm& norm
        )
    {   
        int jobs   = dst_width * dst_height;
		auto grid  = sg::grid_dims(jobs);
		auto block = sg::block_dims(jobs);

		
		checkHipKernel(perspective_kernel <<<grid, block, 0, nullptr >>> (
			src, src_line_size,
			src_width, src_height, dst,
			dst_width, dst_height, const_value, matrix_3_3, norm, jobs
		));

    }

    void obtain_roi(
    uint8_t* src, int src_line_step, int src_width, int src_height, uint8_t* dst, int x, int y, int dst_width, int dst_height,
    hipStream_t stream)
    {
        int jobs   = src_width * src_height;
        auto grid  = sg::grid_dims(jobs);
        auto block = sg::block_dims(jobs);
        
        checkHipKernel(roi_kernel <<<grid, block, 0, stream >>> (
            src, src_line_step,
            src_width, src_height, dst,
            x, y, dst_width, 
            dst_height, jobs
        ));
    }
};
