OpenCV仿射变换实现图像扭曲与旋转

news2024/9/23 1:22:59

目录

1. 仿射变换

2. 仿射变换的求解

3. 代码实现

3.1 图像扭曲

3.2 图像旋转

参考内容


1. 仿射变换

仿射变换是一种可以表达为乘以一个矩阵(线性变换)再加上一个向量(平移)的变换。在几何中,就是将一个向量空间进行一次线性变化并接上一个平移。

因此我们可以用仿射变化表达如下内容:

  1. 旋转Rotations(线性变换,linear transformation);
  2. 平移Translations(矢量加,vector addition);
  3. 缩放操作Scale operations(线性变换)。

由此可见,在图像处理当中,仿射变换本质上反映了两个图像之间的关系。

我们通常使用2×3矩阵来表示仿射变换。

对于“乘以一个矩阵”的线性变换,我们引入矩阵A:

A=\left[\begin{matrix}a_{00}&a_{01}\\a_{10}&a_{11}\\\end{matrix}\right]

对于加法部分(平移),我们引入矩阵B:

B=\left[\begin{matrix}b_{00}\\b_{10}\\\end{matrix}\right]

设待转换的二维列向量为X,转换后的向量为T,则:

X=\left[\begin{matrix}x\\y\\\end{matrix}\right]

T=AX+B=A\cdot\left[\begin{matrix}x\\y\\\end{matrix}\right]+B

考虑齐次坐标和齐次矩阵更易于进行仿射几何变换,令:

M=[\begin{matrix}A&B\\\end{matrix}]=\begin{bmatrix} a_{00} & a_{01} & b_{00}\\ a_{10} & a_{11} & b_{10} \end{bmatrix}

二维向量X视作一个点,其齐次坐标表示为这样的列向量:

\left[\begin{matrix}x\\y\\1\\\end{matrix}\right]

这样,我们可以借助齐次坐标,把平移变量也通过一个矩阵表示,则有:

T=M\cdot\left[\begin{matrix}x\\y\\1\\\end{matrix}\right]

可见矩阵M就是我们所需要的仿射变换矩阵。

2. 仿射变换的求解

参考内容通过几何方式(三角形三个顶点的映射)描述仿射变换的求解过程,我们也可以用代数知识表达,对于不共线的三点,令:

T=\begin{bmatrix} {x}'\\ {y}' \end{bmatrix} =\begin{bmatrix} {x_0'} & {x_1'} & {x_2'} \\ {y_0'} & {y_1'} & {y_2'} \end{bmatrix}

X=\begin{bmatrix} {x}\\ {y} \end{bmatrix} =\begin{bmatrix} {x_0} & {x_1} & {x_2} \\ {y_0} & {y_1} & {y_2} \end{bmatrix}

方程

T=M\left[\begin{matrix}X\\1\\\end{matrix}\right]=M\left[\begin{matrix}x\\y\\1\\\end{matrix}\right]

其方程组形式为:

\left\{\begin{matrix} a_{00}x_0+a_{01}y_0+b_{00}={x_0'} \\ a_{10}x_0+a_{11}y_0+b_{10}={y_0'} \\ a_{00}x_1+a_{01}y_1+b_{00}={x_1'} \\ a_{10}x_1+a_{11}y_1+b_{10}={y_1'} \\ a_{00}x_2+a_{01}y_2+b_{00}={x_2'} \\ a_{10}x_2+a_{11}y_2+b_{10}={y_2'} \end{matrix}\right.

有唯一解,从而可以求出仿射矩阵M。

在OpenCV中,我们可以通过cv::getAffineTransform函数求解仿射矩阵或cv::getRotationMatrix2D,求解二维旋转矩阵。

Mat cv::getAffineTransform

(

InputArray

src,

InputArray

dst 

)

Python:

cv.getAffineTransform(

src, dst

) ->

retval

getRotationMatrix2D()

Mat cv::getRotationMatrix2D

(

Point2f

center,

double 

angle,

double 

scale 

)

inline

Python:

cv.getRotationMatrix2D(

center, angle, scale

) ->

retval

3. 代码实现

3.1 图像扭曲

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>

using namespace cv;

int main()
{
    // 读取图像
    Mat src = imread("park.jpg");
    if (src.empty())
    {
        std::cout << "Could not open or find the image!\n" << std::endl;
        return -1;
    }
    // 选定三角形三个顶点
    Point2f srcTri[3];
    srcTri[0] = Point2f(0.f, 0.f);
    srcTri[1] = Point2f(src.cols - 1.f, 0.f);
    srcTri[2] = Point2f(0.f, src.rows - 1.f);
    // 假定转换后的三个顶点坐标
    Point2f dstTri[3];
    dstTri[0] = Point2f(0.f, src.rows * 0.33f);
    dstTri[1] = Point2f(src.cols * 0.55f, src.rows * 0.25f);
    dstTri[2] = Point2f(src.cols * 0.35f, src.rows * 0.7f);
    // 求解仿射矩阵
    Mat warp_mat = getAffineTransform(srcTri, dstTri);
    // 求解扭曲后的图像
    Mat warp_dst;
    warpAffine(src, warp_dst, warp_mat, src.size());

    imshow("Warped Image", warp_dst);

    waitKey(0);

    return 0;
}
# 图像扭曲
import cv2
import numpy as np

# 读取图像
img = cv2.imread('park.jpg')

# 图像扭曲
(rows, cols) = img.shape[:2]
srcTri = np.array( [[0, 0], [img.shape[1] - 1, 0], [0, img.shape[0] - 1]] ).astype(np.float32)
dstTri = np.array( [[0, img.shape[0]*0.33], [img.shape[1]*0.55, img.shape[0]*0.25], [img.shape[1]*0.35, img.shape[0]*0.7]] ).astype(np.float32)
M = cv2.getAffineTransform(srcTri, dstTri)
warped_img = cv2.warpAffine(img, M, (cols, rows))

# 显示旋转后的图像 
cv2.imshow('Warped Image', warped_img)

# 等待用户输入并关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

3.2 图像旋转

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>

using namespace cv;

int main()
{
    // 读取图像
    Mat src = imread("park.jpg");
    if (src.empty())
    {
        std::cout << "Could not open or find the image!\n" << std::endl;
        return -1;
    }
    // 设置旋转中心、旋转角度和缩放比例
    Point center = Point(src.cols / 2, src.rows / 2);
    double angle = 35;
    double scale = 0.5;
    // 获取旋转矩阵
    Mat rot_mat = getRotationMatrix2D(center, angle, scale);
    // 旋转后的图像
    Mat rotated_dst;
    warpAffine(src, rotated_dst, rot_mat, src.size());    
    // 显示图像
    imshow("Rotated Image", rotated_dst);

    waitKey(0);

    return 0;
}
# 图像旋转
import cv2

# 读取图像
img = cv2.imread('park.jpg')

# 旋转图像
(rows, cols) = img.shape[:2]
M = cv2.getRotationMatrix2D((cols/2, rows/2), 35, 0.5)
rotated_img = cv2.warpAffine(img, M, (cols, rows))

# 显示旋转后的图像 
cv2.imshow('Rotated Image', rotated_img)

# 等待用户输入并关闭窗口
cv2.waitKey(0)
cv2.destroyAllWindows()

参考内容

OpenCV: Affine Transformations

注:OpenCV这个文档的示例代码中,C++代码和Python代码并不匹配,Python代码中的dstTri第一个点的y坐标,shape[1]应为shape[0](见本文示例代码)。

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

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

相关文章

天空NFT源码:数字藏品交易平台,铸造市场转售盲盒商城系统,附搭建教程和视频

&#x1f31f;【火热NFT数藏交易平台源码限时&#xff01;】&#x1f680; &#x1f47e; 想进军NFT市场却苦于无门&#xff1f; &#x1f3a8; 梦想拥有自己的数字藏品平台&#xff1f; &#x1f6e0;️ 寻找全方位、无加密、易搭建的NFT解决方案&#xff1f; &#x1f525;…

postman查询单条数据Get方法,无任何输出,idea后端也没有任何数据和提示的解决方法

问题描述&#xff1a; 正常使用postman测试&#xff0c;输入内容没有错误&#xff0c;但是却没有任何消息 后端也是&#xff0c;没有任何消息&#xff1a; 解决方法&#xff1a; 问题的原因主要是因为postman&#xff1a; 我们只需要新建一个页面&#xff0c;把刚才的查询语…

Spring Controller接口地址的骚玩法,很有用!

一&#xff0c;背景 项目里有一个接口需要对外提供&#xff0c;对方的解析方式有不同的方式&#xff0c;一个是使用流行的json格式&#xff0c;另外一个却是老系统&#xff0c;只能用xml格式&#xff0c;但是接口内部的实现逻辑是完全一样的&#xff0c;为了适配更多调用方的需…

AS400==创建主机,使用客户端连接上主机

因为AS400基于的CPU架构和自己用的PC不一致&#xff0c;所以要么自己买台AS400主机/上云服务买一台&#xff0c;或者去些网站免费申请一台。 申请地址 PUB400.COM - Your public IBM i server 注册成功后获取到账号密码 然后下载客户端 TN5250 Terminal Emulation for Win…

10. 计算机网络HTTP协议

1. 前言 无论是作为后端开发、前端开发、测试开发程序员或者是运维人员,在面试过程中,大概率都会被问到 HTTP 协议相关题目。 因为伴随着 2010 年之后移动互联网在全世界的高速发展,各种各样的浏览器(Chrome、FireFox、Safari 等)层出不穷,也诞生了诸多服务端开发的语言…

全志Tina_NPU开发部署说明

1 前言 1.1 读者对象 本文档&#xff08;本指南&#xff09;主要适用于以下人员&#xff1a; • 技术支持工程师 • 软件开发工程师 • AI 应用案客户 2 正文 2.1 NPU 开发简介 • 支持int8/uint8/int16 量化精度&#xff0c;运算性能可达1TOPS. • 相较于GPU 作为AI …

关于CSDN登录失效,频繁弹到登录界面

典型特征&#xff1a; 访问其它任何网站都没问题&#xff0c;就是访问任何一个和CSDN有关的网页都会弹到登录界面&#xff0c;且提示认证失败或者Cookies失效等。 刚才我照旧打开CSDN时&#xff0c;CSDN弹出重定向失败&#xff0c;提到了Cookies问题&#xff0c;在个人中心也弹…

ECCV 2024|是真看到了,还是以为自己看到了?多模态大模型对文本预训练知识的过度依赖该解决了

随着大型语言模型&#xff08;LLMs&#xff09;的进步&#xff0c;多模态大型语言模型&#xff08;MLLMs&#xff09;迅速发展。它们使用预训练的视觉编码器处理图像&#xff0c;并将图像与文本信息一同作为 Token 嵌入输入至 LLMs&#xff0c;从而扩展了模型处理图像输入的对话…

移动UI:活泼风格如何识别,有什么应用场景。

在移动UI设计中&#xff0c;活泼风格通常具有以下特征&#xff1a; 1. 鲜艳的色彩&#xff1a; 活泼风格的移动UI通常采用鲜艳、明快的色彩&#xff0c;如橙色、黄色、绿色等&#xff0c;以增加活力和生机。 2. 元素动画&#xff1a; 活泼风格的UI设计通常包含丰富的动画效…

无缝融入,即刻智能[2]:MaxKB内置强大工作流引擎,编排AI工作流程,满足多样化业务需求

无缝融入,即刻智能[2]:MaxKB内置强大工作流引擎,编排AI工作流程,满足多样化业务需求 1.简介 MaxKB(Max Knowledge Base)是一款基于 LLM 大语言模型的开源知识库问答系统, 官方网址:https://maxkb.cn/ GitHub:https://github.com/1Panel-dev/MaxKB 1.1 产品优势 开箱即…

嵌入式Linux系统中LCD屏驱动框架基本实现

大家好,今天主要给大家分享一下,如何使用linux系统中LCD屏驱动框架Framebuffer编写具体的代码。 第一:如何编写字符设备驱动程序 1、驱动框架基本操作: 驱动主设备号 * 构造file_operations结构体,填充open/read/write等成员函数 * 注册驱动:register_chrdev(major, name…

C#中WebView2调用与交互实现

简要说明&#xff1a; 此控件实际上是 [WebView2 COM API] &#xff08;https://aka.ms/webview2&#xff09; 的包装器。 可以通过访问 Microsoft.Web.WebView2.Wpf.WebView2.CoreWebView2 属性来直接访问基础 ICoreWebView2 接口及其所有功能。 一些最常见的 COM 功能也可以…

解决k8s flannel网络插件国内镜像docker拉取不到问题

一、准备下载资源 https://download.csdn.net/download/weixin_43205308/89608560 以下&#xff0c;每个k8s节点都要执行 二、载入镜像 解压上面的下载资源的文件夹后&#xff0c;会有图中的两个资源 载入资源 docker load --input flannel-flannel-v0.25.1-amd64.tar.gzd…

59在Linux中加docker中加mysql,tomcat,redis

一、引言 1.1 环境不一致 我本地运行没问题啊&#xff1a;由于环境不一致&#xff0c;导致相同的程序&#xff0c;运行结果却不一致。 1.2 隔离性 哪个哥们又写死循环了&#xff0c;怎么这么卡&#xff1a;在多用户的操作系统下&#xff0c;会因为其他用户的操作失误影响到你自…

基于STM32的智能家居控制系统教程

目录 引言环境准备智能家居控制系统基础代码实现&#xff1a;实现智能家居控制系统 照明控制模块温度与湿度监控模块安防监控模块用户界面与远程控制应用场景&#xff1a;智能家居优化常见问题与解决方案收尾与总结 引言 随着物联网技术的发展&#xff0c;智能家居控制系统变…

libvir服务机制与通信原理

libvir服务机制 前言 libvirt服务机制是一个复杂的结构&#xff0c;里面包含了event事件&#xff0c;rpc网络通信&#xff0c;线程池以及相关的job机制&#xff0c;哪一个单拿出来都是一个复杂的模型结构&#xff0c;更何况libvirt服务是这几个机制之间相互协作的复杂结构&…

【数据结构】——二叉树OJ题

文章目录 前言1. 单值二叉树2. 检查两颗树是否相同3. 判断一棵树是否为另一颗树的子树4. 对称二叉树5. 平衡二叉树6. 二叉树的前序遍历7. 二叉树的中序遍历8. 二叉树的后序遍历9. 二叉树的构建及遍历 前言 我们先想想二叉树我们学习了哪些内容再来做题哈 其实学习二叉树重要的…

JavaScript (十四)——JavaScript typeof和类型转换

目录 JavaScript typeof, null, 和 undefined typeof 操作符 null undefined undefined 和 null 的区别 JavaScript 类型转换 JavaScript 数据类型 JavaScript 类型转换 将数字转换为字符串 将布尔值转换为字符串 将日期转换为字符串 将字符串转换为数字 一元运算符…

新浪微博笔试秋招管培笔试测评肯耐珂萨题型题库解析

新浪微博的笔试是该公司用于筛选潜在候选人的重要环节&#xff0c;主要目的在于评估应聘者的逻辑推理、图表计算和文字理解能力。以下是新浪微博笔试题型的详细解析&#xff1a; 1. 逻辑推理 - **题型概述**&#xff1a;逻辑推理部分通常包含20题&#xff0c;考察应试者的逻…

算法板子:树形DP、树的DFS——树的重心

思想&#xff1a; 代码&#xff1a; #include <iostream> #include <cstring> using namespace std;const int N 1e5 10;// vis标记当前节点是否被访问过; vis[1]true代表编号为1的节点被访问过 bool vis[N]; // h数组为邻接表; h数组上的每个坑位都串了一个单链…