__device__ void affine_project(float* matrix, int x, int y, float* proj_x, float* proj_y){
// matrix
// m0, m1, m2
// m3, m4, m5
*proj_x = matrix[0] * x + matrix[1] * y + matrix[2];
*proj_y = matrix[3] * x + matrix[4] * y + matrix[5];
}
__global__ void warp_affine_bilinear_kernel(
uint8_t* src, int src_line_size, int src_width, int src_height,
uint8_t* dst, int dst_line_size, int dst_width, int dst_height,
uint8_t fill_value, AffineMatrix matrix
){
/*
建议先阅读代码,若有疑问,可点击抖音短视频进行辅助讲解(建议1.5倍速观看)
- https://v.douyin.com/Nhr4vTF/
*/
int dx = blockDim.x * blockIdx.x + threadIdx.x;
int dy = blockDim.y * blockIdx.y + threadIdx.y;//每一个线程负责处理一个像素的三个通道。
if (dx >= dst_width || dy >= dst_height) return;
float c0 = fill_value, c1 = fill_value, c2 = fill_value;
float src_x = 0; float src_y = 0;
affine_project(matrix.d2i, dx, dy, &src_x, &src_y);//已知dxdy的位置,src_x , src_y 去哪里取值. 因为这是一个平移变换的核函数,需要对于像素值进行拷贝
/*
建议先阅读代码,若有疑问,可点击抖音短视频进行辅助讲解(建议1.5倍速观看)
- 双线性理论讲解:https://v.douyin.com/NhrH2tb/
- 代码代码:https://v.douyin.com/NhrBqpc/
*/
if(src_x < -1 || src_x >= src_width || src_y < -1 || src_y >= src_height){
// out of range
// src_x < -1时,其高位high_x < 0,超出范围
// src_x >= -1时,其高位high_x >= 0,存在取值
}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_values[] = {fill_value, fill_value, fill_value};//三通道给三个值,给那些不在区域之内的,不正常的值。
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_values;
uint8_t* v2 = const_values;
uint8_t* v3 = const_values;
uint8_t* v4 = const_values;
if(y_low >= 0){
if (x_low >= 0)
v1 = src + y_low * src_line_size + x_low * 3; //拿到正常值的首地址 (比如4*3的矩阵的(1,1)那就是第一行的srcline(4*1 *3(channel) +1*3)拿到了这个1的地址,之后就可以取rgb三个通道的值)
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;
}
c0 = floorf(w1 * v1[0] + w2 * v2[0] + w3 * v3[0] + w4 * v4[0] + 0.5f); //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* pdst = dst + dy * dst_line_size + dx * 3;
pdst[0] = c0; pdst[1] = c1; pdst[2] = c2;
}
warpaffine最核心的kernel就是这一段了。
- 分配线程,确保一个线程负责处理一个像素的三个通道。
- 对于尺度映射的浮点坐标,分别取其左右临点,分别为v1,v2,v3,v4。
- 运用临点坐标求面积,求出权重。
- 运用权重及各临点的RGB 值,求该映射浮点的RGB 值,最终即为dst的RGB 值