【CUDA】C++实现warpaffine仿射变换及其逆变换

news2024/9/20 21:33:34

目录

  • 仿射变换矩阵工具类
  • 进行前向仿射变换:i->d
  • 进行仿射变换逆向变换d->i

仿射变换矩阵工具类

假设有图片i,要将其仿射变换至图片d,使用下面的类计算仿射变换矩阵i2d及d2i:
在调用compute函数后,输入i及d的尺寸,自动得到仿射变换矩阵i2d及d2i。

struct AffineMatrix{
    float i2d[6];   
    float d2i[6];  
    void invertAffineTransform(float imat[6], float omat[6]){
        float i00 = imat[0];  float i01 = imat[1];  float i02 = imat[2];
        float i10 = imat[3];  float i11 = imat[4];  float i12 = imat[5];
        float D = i00 * i11 - i01 * i10;
        D = D != 0 ? 1.0 / D : 0;
        float A11 = i11 * D;
        float A22 = i00 * D;
        float A12 = -i01 * D;
        float A21 = -i10 * D;
        float b1 = -A11 * i02 - A12 * i12;
        float b2 = -A21 * i02 - A22 * i12;
        omat[0] = A11;  omat[1] = A12;  omat[2] = b1;
        omat[3] = A21;  omat[4] = A22;  omat[5] = b2;
    }

    void compute(const Size& from, const Size& to){
        float scale_x = to.width / (float)from.width;
        float scale_y = to.height / (float)from.height;
        float scale = min(scale_x, scale_y); 
        i2d[0] = scale;  i2d[1] = 0;  i2d[2] = 
            -scale * from.width  * 0.5  + to.width * 0.5 + scale * 0.5 - 0.5;

        i2d[3] = 0;  i2d[4] = scale;  i2d[5] = 
            -scale * from.height * 0.5 + to.height * 0.5 + scale * 0.5 - 0.5;

        invertAffineTransform(i2d, d2i);
    }
};

进行前向仿射变换:i->d

在将i变换到d时,是对d进行变换操作,使用cuda遍历d中的每个点,找出d映射到i的对应关系,此时应使用d2i进行变换,相关代码如下:
调用处代码:

#include <cuda_runtime.h>
#include <opencv2/opencv.hpp>
#include <stdio.h>

using namespace cv;

#define min(a, b)  ((a) < (b) ? (a) : (b))
#define checkRuntime(op)  __check_cuda_runtime((op), #op, __FILE__, __LINE__)

bool __check_cuda_runtime(cudaError_t code, const char* op, const char* file, int line){
    if(code != cudaSuccess){
        const char* err_name = cudaGetErrorName(code);    
        const char* err_message = cudaGetErrorString(code);  
        printf("runtime error %s:%d  %s failed. \n  code = %s, message = %s\n", file, line, op, err_name, err_message);   
        return false;
    }
    return true;
}

void warp_affine_bilinear( // 声明
    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
);

Mat warpaffine_to_center_align(const Mat& image, const Size& size){  
    Mat output(size, CV_8UC3);
    uint8_t* psrc_device = nullptr;
    uint8_t* pdst_device = nullptr;
    size_t src_size = image.cols * image.rows * 3;
    size_t dst_size = size.width * size.height * 3;
    checkRuntime(cudaMalloc(&psrc_device, src_size)); // 在GPU上开辟两块空间
    checkRuntime(cudaMalloc(&pdst_device, dst_size));
    checkRuntime(cudaMemcpy(psrc_device, image.data, src_size, cudaMemcpyHostToDevice)); // 搬运数据到GPU上
    
    warp_affine_bilinear(
        psrc_device, image.cols * 3, image.cols, image.rows,
        pdst_device, size.width * 3, size.width, size.height,
        114
    );

    // 检查核函数执行是否存在错误
    checkRuntime(cudaPeekAtLastError());
    checkRuntime(cudaMemcpy(output.data, pdst_device, dst_size, cudaMemcpyDeviceToHost)); 
    checkRuntime(cudaFree(psrc_device));
    checkRuntime(cudaFree(pdst_device));
    return output;
}

int main(){ 
    Mat image = imread("i.jpg");
    Mat output = warpaffine_to_center_align(image, Size(640, 640));
    imwrite("d.jpg", output);
    return 0;
}

核函数代码:

#include <cuda_runtime.h>

#define min(a, b)  ((a) < (b) ? (a) : (b))
#define num_threads   512
typedef unsigned char uint8_t;
struct Size{
    int width = 0, height = 0;

    Size() = default;
    Size(int w, int h)
    :width(w), height(h){}
};


struct AffineMatrix{
    float i2d[6];   
    float d2i[6];     
    void invertAffineTransform(float imat[6], float omat[6]){
        float i00 = imat[0];  float i01 = imat[1];  float i02 = imat[2];
        float i10 = imat[3];  float i11 = imat[4];  float i12 = imat[5];

        // 计算行列式
        float D = i00 * i11 - i01 * i10;
        D = D != 0 ? 1.0 / D : 0;

        // 计算剩余的伴随矩阵除以行列式
        float A11 = i11 * D;
        float A22 = i00 * D;
        float A12 = -i01 * D;
        float A21 = -i10 * D;
        float b1 = -A11 * i02 - A12 * i12;
        float b2 = -A21 * i02 - A22 * i12;
        omat[0] = A11;  omat[1] = A12;  omat[2] = b1;
        omat[3] = A21;  omat[4] = A22;  omat[5] = b2;
    }

    void compute(const Size& from, const Size& to){
        float scale_x = to.width / (float)from.width;
        float scale_y = to.height / (float)from.height;
        float scale = min(scale_x, scale_y); // 缩放比例辅助视频讲解 https://v.douyin.com/NhrH8Gm/
        i2d[0] = scale;  i2d[1] = 0;  i2d[2] = 
            -scale * from.width  * 0.5  + to.width * 0.5 + scale * 0.5 - 0.5;

        i2d[3] = 0;  i2d[4] = scale;  i2d[5] = 
            -scale * from.height * 0.5 + to.height * 0.5 + scale * 0.5 - 0.5;

        invertAffineTransform(i2d, d2i);
    }
};

__device__ void affine_project(float* matrix, int x, int y, float* proj_x, float* proj_y){
    *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
){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;
    //这里使用了d2i
    affine_project(matrix.d2i, dx, dy, &src_x, &src_y);
    if(src_x < -1 || src_x >= src_width || src_y < -1 || src_y >= src_height){
    }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;

            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);
        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;
}

void warp_affine_bilinear(
    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
){
    dim3 block_size(32, 32); 
    dim3 grid_size((dst_width + 31) / 32, (dst_height + 31) / 32);
    AffineMatrix affine;
    affine.compute(Size(src_width, src_height), Size(dst_width, dst_height));

    warp_affine_bilinear_kernel<<<grid_size, block_size, 0, nullptr>>>(
        src, src_line_size, src_width, src_height,
        dst, dst_line_size, dst_width, dst_height,
        fill_value, affine
    );
}

进行仿射变换逆向变换d->i

此时的输入图片为d,待求i,遍历待求i的每一像素,根据i至d的映射关系找出i(即前向计算出矩阵的i2d);调用处为:

int main(){ 
    Mat image = imread("d.jpg");
    Mat output = warpaffine_to_center_align(image, Size(550, 676));
    imwrite("i.jpg", output);
    return 0;
}

这个例子中i尺寸为Size(550, 676),d的尺寸为(640,640)
核函数只需改一处代码即可:

//这里使用了i2d
affine_project(matrix.i2d, dx, dy, &src_x, &src_y);

注意,这里的矩阵matrix仍是前向计算时得出的矩阵:

AffineMatrix affine;
affine.compute(Size(550, 676), Size(640, 640));

变换前后效果如下图:
请添加图片描述
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/138968.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[3] Jenkins 系列:如何获取出发Jenkins Job的用户信息?

Jenkins提供两种方式的Script&#xff0c;一种是基于声明式的&#xff0c;一种是基于脚本式的。 Jenkins申明式的格式 Jenkins脚本式的格式 Jenkins 官方推荐使用申明式的方式定义Jenkins的Pipeline。 有的时候我们需要在Pipeline给开发团队发消息或者邮件&#xff0c;告知当…

MAC(m1)-VMWare Fusion安装CentOS7.9

下载安装VMWare Fusion&#xff0c;安装完成后打开 https://blog.csdn.net/ZHOU_VIP/article/details/128513824 centos7.9安装镜像拖过来&#xff1a; 打开自定义设置&#xff1a; 换文件夹&#xff1a; 问题出来了&#xff0c;点击install闪一下&#xff0c;就没了&#xff…

C进阶:结构体的内存对齐

目录 本篇文章注意讲解结构体的内存对齐。 &#x1f54a;️&#x1f432;一.为什么存在内存对齐 &#x1f916;&#x1f47b;二.内存对齐规则 &#x1f42c;&#x1f431;三.实例 &#x1f407;例1. &#x1f984; 例2. &#x1f42f;例3. &#x1f63c;例4. 一.为什么…

二十四节气-小寒。文案、海报分享,小寒料峭 年味渐浓。

小寒&#xff0c;是二十四节气中的第23个节气&#xff0c;冬季的第5个节气&#xff0c;正处在“二九”“三九”期间&#xff0c;可以说是一年中最冷的时段。 中国古代将“小寒”分为三候&#xff1a;“一候雁北乡&#xff0c;二候鹊始巢&#xff0c;三候雉始鸲。” 大雁开始向…

Java堆空间(Heap Space)

Java 堆空间(Heap Space)概述在Java程序中&#xff0c;堆是JVM内存空间中最大的一块&#xff0c;同时我们知道&#xff0c;每个线程都拥有一个虚拟机栈&#xff0c;但是堆不同&#xff0c;Java堆是被所有线程共享的一块内存区域&#xff0c;在虚拟机启动时创建。在《Java虚拟机…

STM32——TIM输入捕获

文章目录一、TIM输入捕获输入捕获简介频率测量二、通用定时器的输入捕获通道通用定时器框图通道的输出部分三、主从触发模式主模式从模式四、输入捕获基本结构五、PWMI基本结构六、输入捕获模式测频率电路设计关键代码七、PWMI模式测频率占空比电路设计关键代码八、定时器库函数…

分而治之——图的连通性问题及板子

连通性的判断 两大算法&#xff1a;并查集 和 图的遍历&#xff08;DFS BFS&#xff09; 分而治之的题目与解答 在这道题的下面 3587. 连通图 - AcWing题库 连通性的判断 两种方法 1.这里连通性的判断是判断连通分支是否包含所有的点。 2.也可以不开cnt数组&#xff0c;直接…

【算法】贪心算法(第四章习题解答)

4 贪心算法 4.1 若在 0−10-10−1 背包问题中, 各物品依重量递增排列时, 其价值恰好依递减序排列. 对于这个特殊的 0−10-10−1 背包问题, 设计一个有效算法找出最优解, 并说明算法的正确性. 算法设计&#xff1a;由题目所给的信息可以知道这种特殊的背包问题可以通过贪心算法…

基于多保真方法来估计方差和全局敏感度指数分析(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 此代码实现了多保真方法来估计方差和全局敏感度指数。当模型具有不确定的输入时&#xff0c;模型输出也是不确定的。基于方差的…

C++与QML混合编程

一、前言 简单来说&#xff0c;混合编程就是通过Qml高效便捷的构建UI界面&#xff0c;而使用C 来实现业务逻辑和复杂算法。Qt集成了QML引擎和Qt元对象系统&#xff0c;使得QML很容易从C 中得到扩展&#xff0c;在一定的条件下&#xff0c;QML就可以访问QObject派生类的成员&am…

112.(leaflet之家)leaflet椭圆修改

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

OpenHarmony之轻量系统编译构建流程

首先我们先来熟悉几个概念&#xff1a; - 子系统 子系统是一个逻辑概念&#xff0c;它由一个或多个具体的组件组成。OpenHarmony整体遵从分层设计&#xff0c;从下向上依次为&#xff1a;内核层、系统服务层、框架层和应用层。系统功能按照“系统 > 子系统 > 组件”逐级…

别人家的公司年终奖52个月工资-互联网企业年终裁员脸在哪

52个月工资&#xff0c;开玩笑吧&#xff0c;还真不是。据台湾媒体报道&#xff0c;中国台湾长荣海运年终奖出炉&#xff0c;继去年40个月年终奖后&#xff0c;今年再创新高&#xff0c;传言最高发放52个月年终奖金。以基层员工月薪5万新台币计算&#xff0c;表现好的员工可领到…

6.vector、set和map

一、vector 1.简介 有些时候想开一个数组&#xff0c;但是却不知道应该开多大长度的数组合适&#xff0c;因为我们需要用到的数组可能会根据情况变动。这时候我们就需要用到动态数组。 所谓动态数组&#xff0c;也就是不定长数组&#xff0c;数组的长度是可以根据我们的需要…

第四章快速排序——分而治之

分而治之&#xff08;divide and conquer,D&C&#xff09; D&C算法是递归的&#xff0c;并且有2个步骤&#xff1a; 找出基线条件&#xff0c;并且条件尽可能简单不断将问题分解&#xff0c;直到符合基线条件 给定一个数组&#xff0c;求和&#xff1a; 利用循环很容…

秒懂双向链表

目录 一、双向链表概述 二、模拟实现双向链表 1、头插法插入元素 2、尾插法插入元素 3、在指定位置插入元素 4、查询双向链表中是否包含key 5、删除第一次出现关键字为key的结点 6、删除所有值为key的结点 7、求双向链表的长度 8、遍历双向链表 9、清空双向链表 三…

【 uniapp - 黑马优购 | 商品列表 】如何实现数据获取、结构渲染、自定义组件的封装

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生&#xff0c;讨厌编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;小新爱学习. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc…

在 Mac 上将 PDF 转换为 Word 的 5 种简单方法

当谈到将PDF格式转换为Word格式时&#xff0c;用户可能会从互联网上搜索并尝试在线将PDF转换为Word。如果是这样&#xff0c;您可能会得到不好的结果并冒着文件本身的风险。在线 PDF 到 Word 转换器工具可能会产生低质量的输出&#xff0c;对文件大小有限制&#xff0c;更糟糕的…

maven多模块依赖版本不一致问题

项目结构&#xff1a; ├─springcloud-alibaba ├─.idea├─shop-common├─shop-order├─shop-product└─shop-user项目环境&#xff1a; 父工程&#xff1a; <properties><spring-cloud.version>Greenwich.RELEASE</spring-cloud.version><sprin…

java虚拟机之垃圾回收机制

一.需要回收的内存区域 程序计数器、虚拟机栈、本地方法栈 3 个区域随线程生灭(因为是线程私有)&#xff0c;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。而 Java 堆和方法区则不一样&#xff0c;一个接口中的多个实现类需要的内存可能不一样&#xff0c…