• 售前

  • 售后

热门帖子
入门百科

NumPy实现多维数组中的线性代数

[复制链接]
计划你大爷计j 显示全部楼层 发表于 2021-8-14 06:39:53 |阅读模式 打印 上一主题 下一主题
目录


  • 简介
  • 图形加载和分析
  • 图形的灰度
  • 灰度图像的压缩
  • 原始图像的压缩
  • 总结

简介


本文将会以图表的情势为大家解说怎么在NumPy中进行多维数据的线性代数运算。
多维数据的线性代数通常被用在图像处置处罚的图形变更中,本文将会利用一个图像的例子进行分析。


图形加载和分析


认识颜色的朋侪应该都知道,一个颜色可以用R,G,B来表现,如果更高级一点,那么另有一个A表现透明度。通常我们用一个四个属性的数组来表现。

对于一个二维的图像来说,其分辨率可以看做是一个X*Y的矩阵,矩阵中的每个点的颜色都可以用(R,G,B)来表现。

有了上面的知识,我们就可以对图像的颜色进行分解了。

首先需要加载一个图像,我们利用imageio.imread方法来加载一个当舆图像,如下所示:
  1. import imageio
  2. img=imageio.imread('img.png')
  3. print(type(img))
复制代码
上面的代码从当地读取图片到img对象中,利用type可以查看img的范例,从运行效果,我们可以看到img的范例是一个数组。
  1. class 'imageio.core.util.Array'
复制代码
通过img.shape可以得到img是一个(80, 170, 4)的三维数组,也就是说这个图像的分辨率是80*170,每个像素是一个(R,B,G,A)的数组。

最后将图像画出来如下所示:
  1. import matplotlib.pyplot as plt
  2. plt.imshow(img)
复制代码


图形的灰度


对于三维数组来说,我们可以分别得到三种颜色的数组如下所示:
  1. red_array = img_array[:, :, 0]
  2. green_array = img_array[:, :, 1]
  3. blue_array = img_array[:, :, 2]
复制代码
有了三个颜色之后我们可以利用下面的公式对其进行灰度变更:
  1. Y=0.2126R + 0.7152G + 0.0722B
复制代码
上图中Y表现的是灰度。
怎么利用矩阵的乘法呢?利用 @ 就可以了:
  1. img_gray = img_array @ [0.2126, 0.7152, 0.0722]
复制代码
如今img是一个80 * 170的矩阵。
如今利用cmap="gray"作图:
  1. plt.imshow(img_gray, cmap="gray")
复制代码
可以得到下面的灰度图像:


灰度图像的压缩


灰度图像是对图像的颜色进行变更,如果要对图像进行压缩该怎么处置处罚呢?

矩阵运算中有一个概念叫做奇特值和特性值。

设A为n阶矩阵,若存在常数λ及n维非零向量x,使得Ax=λx,则称λ是矩阵A的特性值,x是A属于特性值λ的特性向量。

一个矩阵的一组特性向量是一组正交向量。

即特性向量被施以线性变更 A 只会使向量伸长或紧缩而其方向不被改变。

特性分解(Eigendecomposition),又称谱分解(Spectral decomposition)是将矩阵分解为由其特性值和特性向量表现的矩阵之积的方法。

如果A是m * n阶矩阵,q=min(m,n),A*A的q个非负特性值的算术平方根叫作A的奇特值。

特性值分解可以方便的提取矩阵的特性,但是前提是这个矩阵是一个方阵。如果黑白方阵的环境下,就需要用到奇特值分解了。先看下奇特值分解的定义:

A=UΣVT
其中A是目标要分解的m * n的矩阵,U是一个 m * m的方阵,Σ 是一个m * n 的矩阵,其非对角线上的元素都是0。VTV^TVT是V的转置,也是一个n * n的矩阵。

奇特值跟特性值雷同,在矩阵Σ中也是从大到小分列,而且奇特值的镌汰特殊的快,在很多环境下,前10%甚至1%的奇特值的和就占了全部的奇特值之和的99%以上了。也就是说,我们也可以用前r大的奇特值来近似形貌矩阵。r是一个远小于m、n的数,这样就可以进行压缩矩阵。

通过奇特值分解,我们可以通过更加少量的数据来近似更换原矩阵。

要想利用奇特值分解svd可以直接调用linalg.svd 如下所示:
  1. U, s, Vt = linalg.svd(img_gray)
复制代码
其中U是一个m * m矩阵,Vt是一个n * n矩阵。

在上述的图像中,U是一个(80, 80)的矩阵,而Vt是一个(170, 170) 的矩阵。而s是一个80的数组,s包罗了img中的奇特值。

如果将s用图像来表现,我们可以看到大部分的奇特值都会合在前的部分:

这也就意味着,我们可以取s中前面的部分值来进行图像的重构。
利用s对图像进行重构,需要将s还原成80 * 170 的矩阵:
  1. # 重建
  2. import numpy as np
  3. Sigma = np.zeros((80, 170))
  4. for i in range(80):
  5.     Sigma[i, i] = s[i]
复制代码
利用 U @ Sigma @ Vt 即可重修原来的矩阵,可以通过盘算linalg.norm来比较一下原矩阵和重修的矩阵之间的差别。
  1. linalg.norm(img_gray - U @ Sigma @ Vt)
复制代码
大概利用np.allclose来比较两个矩阵的差别:
  1. np.allclose(img_gray, U @ Sigma @ Vt)
复制代码
大概只取s数组的前10个元素,进行重新绘图,比较一下和原图的区别:
  1. k = 10
  2. approx = U @ Sigma[:, :k] @ Vt[:k, :]
  3. plt.imshow(approx, cmap="gray")
复制代码
可以看到,差别并不是很大:


原始图像的压缩


上一节我们讲到了如何进行灰度图像的压缩,那么如何对原始图像进行压缩呢?

同样可以利用linalg.svd对矩阵进行分解。

但是在利用前需要进行一些处置处罚,因为原始图像的img_array 是一个(80, 170, 3)的矩阵--这里我们将透明度去掉了,只保存了R,B,G三个属性。

在进行转换之前,我们需要把不需要变更的轴放到最前面,也就是说将index=2,换到index=0的位置,然后进行svd操纵:
  1. img_array_transposed = np.transpose(img_array, (2, 0, 1))
  2. print(img_array_transposed.shape)
  3. U, s, Vt = linalg.svd(img_array_transposed)
  4. print(U.shape, s.shape, Vt.shape)
复制代码
同样的,如今s是一个(3, 80)的矩阵,照旧少了一维,如果重修图像,需要将其进行添补和处置处罚,最后将重修的图像输出:
  1. Sigma = np.zeros((3, 80, 170))
  2. for j in range(3):
  3.     np.fill_diagonal(Sigma[j, :, :], s[j, :])
  4. reconstructed = U @ Sigma @ Vt
  5. print(reconstructed.shape)
  6. plt.imshow(np.transpose(reconstructed, (1, 2, 0)))
复制代码

固然,也可以选择前面的K个特性值对图像进行压缩:
  1. approx_img = U @ Sigma[..., :k] @ Vt[..., :k, :]
  2. print(approx_img.shape)
  3. plt.imshow(np.transpose(approx_img, (1, 2, 0)))
复制代码
重新构建的图像如下:

对比可以发现,虽然丧失了部分精度,但是图像照旧可以分辨的。


总结


图像的厘革会涉及到很多线性运算,大家可以以此文为例,过细研究。
到此这篇关于NumPy实现多维数组中的线性代数的文章就先容到这了,更多相干NumPy 多维数组线性代数内容请搜索脚本之家从前的文章或继续欣赏下面的相干文章盼望大家以后多多支持脚本之家!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作