基于SVD实现PCA的图像识别
基于SVD实现PCA的图像识别
初雪之音 发表于3年前
基于SVD实现PCA的图像识别
  • 发表于 3年前
  • 阅读 1388
  • 收藏 14
  • 点赞 0
  • 评论 0

【腾讯云】买域名送云解析+SSL证书+建站!>>>   

摘要: 本文实现基于SVD奇异矩阵分解的PCA主成分分析,使用该算法来完成对人脸图像的识别,主要讲解SVD实现PCA的原理,如何利用SVD实现图像特征的降维,以及SVD在文本聚类方面的使用,例如弱化同义词、多义词的影响,解决传统文本向量空间无法解决的问题。

        本文实现基于SVD奇异矩阵分解的PCA主成分分析,使用该算法来完成对人脸图像的识别,主要讲解SVD实现PCA的原理,如何利用SVD实现图像特征的降维,以及SVD在文本聚类方面的使用,例如弱化同义词、多义词的影响,解决传统文本向量空间无法解决的问题。

需求分析

        本次实验需求分析很简单,实现人脸图像识别,简单地模仿谷歌、百度的识图搜索功能。

特征表示

        特征用来区别一个事物属于哪种类别,例如“是否有翅膀”可以拿来区别麻雀和哈士奇。如何判别两幅图是否是同一个人,我们可以选取图像的像素矩阵构成特征向量,例如一副180 * 200的jpg图像,根据其像素矩阵可以构成一个特征向量vector[180 * 200],有了特征向量我们就可以使用欧氏距离、余弦相似度等计算两幅图的相似度,从而得出是否属于同一个人。

特征选取

        直接使用像素矩阵构成特征向量,当图像很大时计算难免耗时Time Limited,我们将像素称为原始特征。如何将原始特征向量降维,同时又保存它的主要特性,我们可以联想到PCA主成分分析。在PCA主成分分析中,有一种奇异矩阵分解SVD算法,原矩阵可以分解为 M = U x S x V',然后选取前 k 个重要的特征组成新的特征向量完成降维。例如上例,k=20时,可以组成新的特征向量vec'[180 * 20 + 200 * 20],计算量大大减少又不影响比较结果。接下来,我们就来看一下SVD是个是么东西。

        SVD入门基础三步曲:

(1)机器学习中的数学(5)-强大的矩阵奇异值分解(SVD)及其应用

(2)【译】从几何角度看SVD

(3)奇异值分解和图像压缩

        看完上面3篇也就大概知道SVD的原理是什么了,矩阵U由左奇异特征向量组成(描述y轴方向的变化趋势),矩阵S代表对于左(右)特征向量的重要性,矩阵V' 由右奇异特征向量组成(描述x轴方向的变化趋势)。

        由SVD原理可知,我们可以选取前r 个左奇异特征向量、前r 个右奇异特征向量,组成一个新的特征向量vec[ m * k + n * k],从而完成降维,减少计算量而又保佑原有人脸图像特性。

模型建立

        模型特征向量选取完成后,我们就可以拿来比较两幅人脸图像的相似性了。在计算图像相似性时,本文采用余弦相似度算法进行比较,算法代码实现如下:

Pca.java

package key;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import Jama.*;

public class Pca {
    public double Cosine(double[] v1, double[] v2) {
        double ans = 0;

        int n = v1.length;
        double a = 0;
        for (int i = 0; i < n; i++) {
            a += v1[i] * v2[i];
        }
        double b = 0;
        for (int i = 0; i < n; i++) {
            b += v1[i] * v1[i];
        }
        b = Math.sqrt(b);
        double c = 0;
        for (int i = 0; i < n; i++) {
            c += v2[i] * v2[i];
        }
        c = Math.sqrt(c);
        ans = a / b / c;

        return ans;
    }

    public double[] PcaVector(double[][] pixels, int k) {
        Matrix ps = Matrix.constructWithCopy(pixels);
        if (ps.getRowDimension() < ps.getColumnDimension()) {
            ps = ps.transpose();
        }

        SingularValueDecomposition svd = ps.svd();
        Matrix u = svd.getU();
        Matrix s = svd.getS();
        Matrix vt = svd.getV().transpose();

        double[] vec = new double[u.getRowDimension() * k
                + vt.getColumnDimension() * k];
        int cur = 0;
        for (int i = 0; i < k; i++) {
            for (int j = 0; j < u.getRowDimension(); j++) {
                vec[cur + i * u.getRowDimension() + j] = u.get(j, i);
            }
        }
        cur += u.getRowDimension() * k;
        for (int i = 0; i < k; i++) {
            for (int j = 0; j < vt.getColumnDimension(); j++) {
                vec[cur + i * vt.getColumnDimension() + j] = vt.get(i, j);
            }
        }

        return vec;
    }

    public double[][] Pixels(File file) throws IOException {
        BufferedImage bi = ImageIO.read(file);

        int h = bi.getHeight();
        int w = bi.getWidth();
        double arr[][] = new double[w][h];

        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                arr[i][j] = bi.getRGB(i, j);
            }
        }

        return arr;
    }
}

Keyven.java

package key;

import java.io.File;
import java.io.IOException;

public class Keyven {
    public static void main(String[] args) throws IOException {
        Pca pk = new Pca();
        int k = 20;

        File key = new File("data/key.jpg");
        double[] vec_key = pk.PcaVector(pk.Pixels(key), k);

        File folder = new File("data/faces");
        if (folder.isDirectory()) {
            for (File f : folder.listFiles()) {
                double[] vec_f = pk.PcaVector(pk.Pixels(f), k);
                System.out.println(f.getName() + ":\t"
                        + pk.Cosine(vec_key, vec_f));
            }
        }
    }
}

实验效果

        原图像集和候选搜索图像如下:

        我们来看一下实验效果:


        对比还是蛮不错的^_^!

测试数据集下载

实验扩展

        上面讲到左奇异特征向量代表y轴的变化趋势,右奇异向量代表x轴的变化趋势,想象用在自然语言处理文本聚类中,有一个文档-词项共现矩阵,使用SVD作主成分分析,有可能使得原共现矩阵里正交的向量变得不正交,从而弱化了同义词、多义词的影响,而不是简简单单地依靠一个词是否在一篇文档中出现过来造特征……

  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 41
博文 242
码字总数 136279
×
初雪之音
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: