特征分解的推导与意义与opencv代码

特征值与特征向量

定义: A x ⃗ = λ x ⃗ A\vec{x}=\lambda\vec{x} Ax =λx 则称 λ \lambda λ为矩阵A的特征值, x ⃗ \vec{x} x 称为 λ \lambda λ对应的特征向量。
假设A是一个可对角化的矩阵,并且具有n个线性独立的特征向量 P = [ P 1 ⃗   P 2 ⃗   P 3 ⃗ … P n ⃗ ] P=[\vec{P_1} \ \vec{P_2}\ \vec{P_3} … \vec{P_n}] P=[P1  P2  P3 Pn ],对应的特征值为 Λ = [ λ 1 ⋱ λ n ] \Lambda=\left[ \begin{matrix} \lambda_1 & & \\ & \ddots & \\ && \lambda_n \end{matrix} \right] Λ=λ1λn, 可以将 y ⃗ = A x ⃗ \vec{y}=A\vec{x} y =Ax 进行这样的理解

由于P由n个线性独立的向量组成,所以 x ⃗ \vec{x} x 肯定可以表示再P的基底下,即 x ⃗ = I x ⃗ = a 1 P 1 ⃗ + a 2 P 2 ⃗ + … + a n P n ⃗ = P [ a 1 a 2 ⋮ a n ] \vec{x}=I\vec{x}=a1\vec{P_1}+a2\vec{P_2}+…+an\vec{P_n}=P \left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right] x =Ix =a1P1 +a2P2 ++anPn =Pa1a2an

y ⃗ = A x ⃗ = A P [ a 1 a 2 ⋮ a n ]                = [ A P 1 ⃗   A P 2 ⃗   A P 3 ⃗ … A P n ⃗ ] [ a 1 a 2 ⋮ a n ]                = a 1 λ 1 P 1 ⃗ + a 2 λ 2 P 2 ⃗ + … + a n λ n P n ⃗ \vec{y}=A\vec{x}=AP \left[\begin{matrix} a_1\\a_2\\ \vdots \\ a_n \end{matrix} \right] \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ = [A\vec{P_1} \ A\vec{P_2}\ A\vec{P_3} … A\vec{P_n}]\left[\begin{matrix} a_1\\a_2\\ \vdots \\ a_n \end{matrix} \right] \\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ =a_1\lambda_1\vec{P_1}+a_2\lambda_2\vec{P_2}+…+a_n\lambda_n\vec{P_n} y =Ax =APa1a2an              =[AP1  AP2  AP3 APn ]a1a2an              =a1λ1P1 +a2λ2P2 ++anλnPn
可以看到矩阵A的作用就是在以P为基的情况下,将在各个P轴分量上的值拉伸 λ \lambda λ倍。

例子
假设 A = [ 3 0 0 1 ] , x ⃗ = [ 1   1 ] T A=\left[\begin{matrix}3&0\\0&1 \end{matrix} \right],\vec{x}=[1 \ 1]^T A=[3001],x =[1 1]T
P 1 = [ 1   0 ] T , λ 1 = 3 P_1=[1\ 0]^T,\lambda_1=3 P1=[1 0]T,λ1=3 P 2 = [ 0   1 ] T , λ 2 = 1 P_2=[0\ 1]^T,\lambda_2=1 P2=[0 1]T,λ2=1,
此时的P=E,则正好在E的基底下,就是在P的基底下,则 y ⃗ = A x ⃗ \vec{y}=A\vec{x} y =Ax 得到的结果就是将在 [ 1   0 ] T [1\ 0]^T [1 0]T分量上的值即x拉伸 λ 1 = 3 \lambda_1=3 λ1=3倍;在 [ 0   1 ] T [0\ 1]^T [0 1]T分量上的值即y拉伸 λ 2 = 1 \lambda_2=1 λ2=1倍,即 y ⃗ = [ 3   1 ] T \vec{y}=[3 \ 1]^T y =[3 1]T,可以直接运算进行验证。
在图片上表示则是

特征分解

由之前的推导可以看到
矩阵A的作用与在各个特征向量方向上进行特征值( λ \lambda λ)倍拉伸的作用是一样的。
既然如此,我们在进行运算 y ⃗ = A x ⃗ \vec{y}=A\vec{x} y =Ax 可以将矩阵A等价于三部分的功能

  1. x ⃗ \vec{x} x 从I的基底下,表示成在P的基底下。
  2. 将已经转换成P基底的 x p ⃗ \vec{x_p} xp 的各个分量进行 λ \lambda λ倍的拉伸。
  3. 将拉伸后的 x p ⃗ \vec{x_p} xp 转换回I的基底下 x ⃗ \vec{x} x

特征分解做的正是以上的三件事
先来看下怎么换基底

x ⃗ = [ x 1 x 2 ⋮ x n ] = I x ⃗ = a 1 P 1 ⃗ + a 2 P 2 ⃗ + … + a n P n ⃗ = P [ a 1 a 2 ⋮ a n ] \vec{x}=\left[\begin{matrix} x_1\\x_2\\ \vdots \\x_n \end{matrix} \right]=I\vec{x}=a1\vec{P_1}+a2\vec{P_2}+…+an\vec{P_n}=P \left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right] x =x1x2xn=Ix =a1P1 +a2P2 ++anPn =Pa1a2an
我们的目标就是将 x ⃗ = [ x 1 x 2 ⋮ x n ] \vec{x}=\left[\begin{matrix} x_1\\x_2\\ \vdots \\x_n \end{matrix} \right] x =x1x2xn转换成 x p ⃗ = [ a 1 a 2 ⋮ a n ] \vec{x_p}=\left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right] xp =a1a2an
我们先逆过来讨论,怎么将 x p ⃗ \vec{x_p} xp 转化成 x ⃗ \vec{x} x ,再上面的推导中,可以看到 x ⃗ = P [ a 1 a 2 ⋮ a n ] = P x p ⃗ \vec{x}=P \left[\begin{matrix} a_1\\a_2\\ \vdots \\a_n \end{matrix} \right]=P\vec{x_p} x =Pa1a2an=Pxp
只要将P乘上 x p ⃗ \vec{x_p} xp 即可以得到 x ⃗ \vec{x} x ,即 x ⃗ = P x p ⃗ \vec{x}=P\vec{x_p} x =Pxp
那我们要得到 x p ⃗ \vec{x_p} xp 只需要两边同时乘上 P − 1 P^{-1} P1,即 x p ⃗ = P − 1 x ⃗ \vec{x_p}=P^{-1}\vec{x} xp =P1x

那么第一步的操作便是等价于 P − 1 P^{-1} P1,第三步的操作便是等价于 P P P
接下来看第二步的拉伸操作

[ a 1 λ 1 a 2 λ 2 ⋮ a n λ n ] = [ λ 1 ⋱ λ n ] [ a 1 a 2 ⋮ a n ] = Λ [ a 1 a 2 ⋮ a n ] \left[\begin{matrix} a_1\lambda_1\\ a_2\lambda_2\\ \vdots \\ a_n\lambda_n \end{matrix} \right] =\left[ \begin{matrix} \lambda_1 & & \\ & \ddots & \\ && \lambda_n \end{matrix} \right]\left[ \begin{matrix} a_1 \\a_2 \\ \vdots \\a_n \end{matrix} \right]=\Lambda \left[\begin{matrix} a_1 \\a_2 \\ \vdots \\a_n \end{matrix} \right] a1λ1a2λ2anλn=λ1λna1a2an=Λa1a2an

即第二步的拉伸操作等价于 Λ \Lambda Λ
将以上的三步组合起来,即可以得到特征分解的公式,即 A = P Λ P − 1 A=P\Lambda P^{-1} A=PΛP1
注意:
1.不是所有的矩阵都可以对角化
2.实对称阵都可以对角化,并且P是正交矩阵,此时公式可以简化为 A = P Λ P T A=P\Lambda P^{T} A=PΛPT
3.可以对角化的情况下,P不一定是正交矩阵

接下来看一下对图片使用特征分解的效果

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char const* argv[])
{
    if (argc != 2) {
        cout << "error" << endl
             << "./matrixFeature pic" << endl;
        return -1;
    }
    Mat pic = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
    pic.convertTo(pic, CV_64FC1, 1 / 255.0);
    resize(pic, pic, cv::Size(pic.rows, pic.rows));
    Mat eValuteM;
    Mat eVectorsInvM;
    Mat eVectorsM;
    eigen(pic, eValuteM, eVectorsM);
    eVectorsM = eVectorsM.t();
    eVectorsInvM = eVectorsM.t(); //如果不是正交矩阵的话,要用inv
    Mat diag(eValuteM.rows, eValuteM.rows, CV_64FC1);
    diag = 0;
    for (int i = 0; i < diag.rows; i++) {
        diag.at<double>(i, i) = eValuteM.at<double>(i, 0);
    }
    Mat res;
    res = eVectorsM * diag * eVectorsInvM;
    imshow("the spirit of strom", pic);
    imshow("evd", res);
    waitKey(0);
}

原图
这是原图
由于图片不是实对称矩阵,使用eigen函数,只能接受对称矩阵,强行使用的结果是得到一张对关于对角线对称的图片
关于对角线对称
opencv提供了另一个函数eigenNonSymmetric可以对非对称矩阵求特征向量,但是有特征向量不一定满足对角化的要求,如果强行进行evd分解再重构,效果是这样的
强行evd
图片明显效果不好,但是有些部分还是可以看出来和原图有些地方是一样的。

关于特征分解的推广(SVD分解可以看我的另一篇文章)
奇异值分解的理解与oepncv代码
参考连接:
如何理解特征值和特征向量

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值