之前学习完矩阵的理解和作用,又经历过一轮基本仿射变换推导,我想大家对矩阵在实际程序中的应用应该基本了解了,这里我们就实际应用一下。

之前学习的变换过程基本都是变换一个“规范”的图形,这次我们就反过来,把一个“不规范”的图形变换“规范”。

首先作为码农们,我们应该都会阅读大量书籍的,但是为了方便我自己下载过大量pdf文档,因为那样我不需要随身带一本厚重厚重的书,只用带个ipad就ok了,不过我下载的很多pdf的扫描页面歪七歪八的,比如像下面这样:

看完这种扫描页面的pdf简直就是治疗了我的颈椎病(ps:本图并不是针对矩阵论这本书,只是打个夸张的比方而已)。其实我们现实中书本都是四四方方的,我们当然也希望下载的pdf页面都规整了,下图是实体书籍:

如果要我们把上面扭曲的页面变成下面的四四方方的页面要怎么办?其实仔细一想,这就是一个仿射变换的过程,前面我们谈过仿射变换的特点,那就是“平直性”,也就是说图形变换后,图形边是直线还是直线,是平行线的还是平行线。我们的处理方法可以思考一下,无非就是让仿射矩阵T将扭曲的页面的每个网格顶点和四四方方的页面的网格顶点相对应。

既然了解到这是个仿射变换的过程,那么我们就建立仿射变换矩阵T变换一下就ok了。这样我们就来考虑怎么计算出这个变换的矩阵T呢?前面我们推导矩阵一般都是使用已知的“映射坐标点”带入矩阵*向量构建的线性方程组中,既然如此就先建立仿射变换矩阵T,然后建立好若干“映射坐标点”。

我们可以把页面的四个角的坐标点当作“映射坐标点”,然后标识出坐标信息用来计算,如下图:

上面两幅图我创建了对应正正方方图形比例的rectangle拓扑网格承载两幅图片(不清楚怎么创建拓扑mesh的小伙伴可以看前面的博客,这里我就不贴重复mesh代码),形象的展示了下两张纹理图的“映射坐标点”,因为我们的目的是为了将扭曲图顶点变换为正方图顶点,所以我们可以参考下幅图中两图的仿射坐标系原点重合的情况,不考虑矩阵平移维度。这样问题就变成了推导3x3的无“平移维度”的仿射变换矩阵T,所以只需要三个“映射坐标点”就可以了,so我们开始矩阵的推导,如下图:

我来解释下解法,首先我们建立矩阵T,然后建立三个映射坐标点,于是我们建立好了矩阵*向量的线性方程组。接下来我们组合出a1b1c1的三元一次方程组(同样也能建立a2b2c2和a3b3c3的三元一次方程组),通过消元法,先消去a1,得到b1c1的二元一次方程组,接着分别消元得到b1c1的值就行了。因为是三元一次方程组,所以算出来的代数式还是很复杂的。

接下来我们就消元b1和c1来求出a1,如下图:

这样的话我们就把矩阵的第一行[a1  b1  c1]求出来了,因为我们全部使用的代数式,所以只需要替换m n u v x y的代数就能得到[a2 b2 c2]和[a3 b3 c3]了,如下图:

其实最开始组建的一个三元一次方程组和上图的三元一次方程组区别就在于n v y分量不同而已,我们只需要替换掉n v y分量就行了,下图的[a3  b3  c3]一样的,如下图:

这时候我们的矩阵T就建立完毕了,当然实际使用中我们要扩充“平移维度{0 0 0 1}”得出一个无平移的4x4矩阵T'用来进行齐次坐标的变换,那么我们接下来继续写程序。

Shader "Unlit/CorrectUnlitShader"
{Properties{_MainTex("Texture", 2D) = "white" {}_PNGCut("PNGCut",Range(0,1)) = 0.5_M("M",vector) = (0,0,0,1)_U("U",vector) = (0,0,0,1)_X("X",vector) = (0,0,0,1)_N("N",vector) = (0,0,0,1)_V("V",vector) = (0,0,0,1)_Y("Y",vector) = (0,0,0,1)}SubShader{Tags { "RenderType"="Opaque" }LOD 100Cull OffPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;float _PNGCut;  //png图片alpha剔除阀值vector _M;  //映射起点1vector _U;  //映射起点2vector _X;  //映射起点3vector _N;  //映射终点1vector _V;  //映射终点2vector _Y;  //映射终点3;vector _Scale;  //不规范网格缩放矫正参数//获取矩阵的一行,因为代数式n v y分量的替换性float4 getMatrixRow(float n, float v, float y, vector m, vector u, vector x){float4 row = float4(0, 0, 0, 1);row.x = ((n*u.y - v*m.y)*(u.z*x.y - u.y*x.z) - (v*x.y - y*u.y)*(m.z*u.y - m.y*u.z)) / ((m.x*u.y - m.y*u.x)*(u.z*x.y - u.y*x.z) - (u.x*x.y - u.y*x.x)*(m.z*u.y - m.y*u.z));row.y = ((n*u.x - v*m.x)*(x.x*u.z - x.z*u.x) - (v*x.x - y*u.x)*(m.z*u.x - m.x*u.z)) / ((m.y*u.x - m.x*u.y)*(x.x*u.z - x.z*u.x) - (x.x*u.y - x.y*u.x)*(m.z*u.x - m.x*u.z));row.z = ((n*u.x - v*m.x)*(x.x*u.y - x.y*u.x) - (v*x.x - y*u.x)*(m.y*u.x - m.x*u.y)) / ((m.z*u.x - m.x*u.z)*(x.x*u.y - x.y*u.x) - (x.x*u.z - x.z*u.x)*(m.y*u.x - m.x*u.y));row.w = 0;return row;}v2f vert (appdata v){v2f o;//构建矫正的变换矩阵,扩充的平移维度float4x4 _Mat_correct = float4x4(getMatrixRow(_N.x, _V.x, _Y.x, _M, _U, _X),getMatrixRow(_N.y, _V.y, _Y.y, _M, _U, _X),getMatrixRow(_N.z, _V.z, _Y.z, _M, _U, _X),0, 0, 0, 1);float4 vx = mul(_Mat_correct, v.vertex);o.vertex = UnityObjectToClipPos(vx);o.uv = TRANSFORM_TEX(v.uv, _MainTex);return o;}fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);clip(col.a - _PNGCut);return col;}ENDCG}}
}

上面的cgshader代码,我们把映射点当作参数传入,然后再vert顶点函数中建立对应的顶点映射变换矩阵,然后使用c#代码进行控制参数。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CorrectCtrl : MonoBehaviour {public Renderer mRender;public Transform MPoint;public Transform UPoint;public Transform XPoint;public Transform NPoint;public Transform VPoint;public Transform YPoint;private Vector4 mM;private Vector4 mU;private Vector4 mX;private Vector4 mN;private Vector4 mV;private Vector4 mY;private Material mMat;void Start(){mMat = mRender.sharedMaterial;mM = getTransformVector(MPoint);mU = getTransformVector(UPoint);mX = getTransformVector(XPoint);mN = getTransformVector(NPoint);mV = getTransformVector(VPoint);mY = getTransformVector(YPoint);mMat.SetVector("_M", mM);mMat.SetVector("_U", mU);mMat.SetVector("_X", mX);mMat.SetVector("_N", mN);mMat.SetVector("_V", mV);mMat.SetVector("_Y", mY);#if UNITY_EDITOR  //验证方程组解法是否正确Matrix4x4 mat = getMatrixCorrect();Debug.LogFormat("mat = {0}", mat);Vector4 r1 = mat * mM;Vector4 r2 = mat * mU;Vector4 r3 = mat * mX;if (r1 == mN && r2 == mV && r3 == mY){Debug.Log("equation right");}
#endif}/// <summary>/// 转换成齐次坐标表示/// </summary>/// <param name="p"></param>/// <returns></returns>private Vector4 getTransformVector(Transform p){return new Vector4(p.position.x, p.position.y, p.position.z, 1);}/// <summary>/// 使用c#语言测试获取矩阵/// </summary>/// <returns></returns>private Matrix4x4 getMatrixCorrect(){Vector4 row0 = getMatrixRow(mN.x, mV.x, mY.x, mM, mU, mX);Vector4 row1 = getMatrixRow(mN.y, mV.y, mY.y, mM, mU, mX);Vector4 row2 = getMatrixRow(mN.z, mV.z, mY.z, mM, mU, mX);Matrix4x4 mat = new Matrix4x4();mat.m00 = row0.x;mat.m01 = row0.y;mat.m02 = row0.z;mat.m03 = row0.w;mat.m10 = row1.x;mat.m11 = row1.y;mat.m12 = row1.z;mat.m13 = row1.w;mat.m20 = row2.x;mat.m21 = row2.y;mat.m22 = row2.z;mat.m23 = row2.w;mat.m30 = 0;mat.m31 = 0;mat.m32 = 0;mat.m33 = 1;return mat;}private Vector4 getMatrixRow(float n, float v, float y, Vector4 m, Vector4 u, Vector4 x){Vector4 row = new Vector4(0, 0, 0, 1);row.x = ((n * u.y - v * m.y) * (u.z * x.y - u.y * x.z) - (v * x.y - y * u.y) * (m.z * u.y - m.y * u.z)) / ((m.x * u.y - m.y * u.x) * (u.z * x.y - u.y * x.z) - (u.x * x.y - u.y * x.x) * (m.z * u.y - m.y * u.z));row.y = ((n * u.x - v * m.x) * (x.x * u.z - x.z * u.x) - (v * x.x - y * u.x) * (m.z * u.x - m.x * u.z)) / ((m.y * u.x - m.x * u.y) * (x.x * u.z - x.z * u.x) - (x.x * u.y - x.y * u.x) * (m.z * u.x - m.x * u.z));row.z = ((n * u.x - v * m.x) * (x.x * u.y - x.y * u.x) - (v * x.x - y * u.x) * (m.y * u.x - m.x * u.y)) / ((m.z * u.x - m.x * u.z) * (x.x * u.y - x.y * u.x) - (x.x * u.z - x.z * u.x) * (m.y * u.x - m.x * u.y));row.w = 0;return row;}
}

上面的c#代码是控制参数传入,同时矩阵创建我也用c#代码实现,为了log一下验证推导的正确性,这时候启动程序,会看到扭曲的页面的网格顶点被矩阵变换成四方页面的网格顶点,这样就做到了图形矫正的作用,如下图:

 

上面展示了传入参数然后cg中计算矩阵最后变换达成效果。

demo下载地址:https://download.csdn.net/download/yinhun2012/10321497

线性代数:仿射变换图形矫正相关推荐

  1. matlab图形矫正,图像畸变矫正算法实现 matlab版

    真正的相机镜头不理想,并在图像中引入一些失真. 为了解释这些非理想性,有必要在透视投影的方程中添加失真模型. 一.原图如下: 二.实现的效果图 三.算法具体实现 function undistorte ...

  2. 线性代数不深入,机器学习两行泪!

    我经常听到有人说,机器学习很难,到底怎么学更高效? 其实,我想说,机器学习本身没有多大难度,因为经过多年的积累后,很多规则已经成型了.对于我们来说真正难的,是机器学习背后的算法所涉及的基础数学原理,包 ...

  3. 线性代数学习全攻略(内附机器学习路径图)

    高考刚结束,之前不少人让我推荐专业,对理工科的同学,我一般会说,如果你没有特别执着的专业方向,就报数学系好了. 其实小时候我也想不明白学好数学有什么用,直到后来学了工程数学之后,才意识到原来数学可以应 ...

  4. 07-人脸识别-人脸矫正

    人脸矫正有几个问题. 1.歪头: 2.侧脸: 3.半边脸:缺失另外半边脸,要寻找其他的解决方案. 大多数情况下,截取到的人脸是包含歪头和侧脸的现象的.这两个问题,可以同时通过仿射变换来矫正. 但是要注 ...

  5. 基于u-net,cv2以及cnn的中文车牌定位,矫正和端到端识别软件

    本文链接:https://blog.csdn.net/qq_32194791/article/details/106748685,转载请注明出处 完整项目已上传至github: https://git ...

  6. 自然场景文本检测识别技术集合(转)

    本文及其它机器学习.深度学习算法的全面系统讲解可以阅读<机器学习与应用>,清华大学出版社,雷明著,由SIGAI公众号作者倾力打造,自2019年1月出版以来已重印3次. 书的购买链接 书的勘 ...

  7. 天然场景文本检测识别技术综述

    这篇文章主要向大家介绍天然场景文本检测识别技术综述,主要内容包括基础应用.实用技巧.原理机制等方面,希望对大家有所帮助. 标签:html前端gitgithub算法网络框架机器学习ide函数 本文及其它 ...

  8. 自然场景文本检测识别技术综述【转】

    转载自https://blog.csdn.net/SIGAI_CSDN/article/details/80858565 番外青蛇: 姐, 图像文本检测和识别领域现在的研究热点是什么? 白蛇: 白纸黑 ...

  9. 自然场景文本检测识别技术综述

    其它机器学习.深度学习算法的全面系统讲解可以阅读<机器学习-原理.算法与应用>,清华大学出版社,雷明著,由SIGAI公众号作者倾力打造. 书的购买链接 书的勘误,优化,源代码资源 番外青蛇 ...

最新文章

  1. seaborn可视化displot绘制直方图(histogram)并通过axvline函数在直方图中添加中位数(median)竖线(自定义中位数竖线的线条形式)
  2. mapbox 修改初始位置_3dmax样条线的创建和修改
  3. javaweb学习总结(三十七)——获得MySQL数据库自动生成的主键
  4. boost::parallel::distributed_property_map用法的测试程序
  5. nginx.conf文件配置后访问边下载 以及yaf框架使用中NGINX.conf文件配置
  6. UVa439 Knight Move 骑士的移动(bfs)
  7. 自加一运算_C语言i++、++i混合运算老手未必全掌握,看了你就明白了
  8. AI赋能新闻播报 搜狐新闻客户端联合搜狗打造首个明星“数字人”主播
  9. 问题以及发现问题和解决问题
  10. 使用API网关构建微服务
  11. 使用Mybatis实例
  12. @Transactional注解失效场景之——同类中方法调用,事务失效
  13. ssfn授权_Steam盗号木马窃取授权文件,360安全大脑强力拦截
  14. 年底将至 怎么向国外客户开口催单 附话术模板
  15. 图解数据交换技术——电路交换、报文交换、分组交换
  16. html怎么把网址设为首页,怎样让网站设为首页和加入收藏
  17. 敏捷领导力2.0培训体会
  18. 安卓学习专栏——百度地图(6)移动到我的位置(图文+代码)
  19. 在Android上应用PhoneGap和Dojo Mobile
  20. IT 基础设施趋势合集 | 多云、超融合、SDS、容器之趋势解读与政策分析

热门文章

  1. 凌科芯安公司推出32位高端加密IC
  2. 申请Google Voice失败之后的替代品
  3. pt mysql summary_ptsummary ptmysqlsummary工具
  4. 中国SaaS应用大会,助力企业数字化转型新路径
  5. 各种磁性材料在磁性器件磁芯中的应用
  6. 下载spotify音乐_完成播放列表或专辑后如何停止Spotify停止自动播放音乐
  7. 90. 基于Notes/Domino的文档工作流系统(二)
  8. 关于各种运输层的可靠传输协议
  9. 数据库知识体系框架图01
  10. Python OpenCV中的Stitcher.stitch图像拼接方法介绍(详细)