【184】Win10下Java8调用Python的face_recognition库来实现人脸识别

news2024/10/8 15:35:15

前言

face_recognition 是一个开源的、人脸识别的Python库。本文讲解了在 Windows 10操作系统上,使用 Java8 来调用 Python 的 face_recognition 库来实现人脸识别。虽然 Java8 有 Jython,但是由于截至发文时 Jython 支持的版本太老(只有Python 2),所以此处不适合用 Jython。

Jython 官网对支持版本的介绍:

在这里插入图片描述

配置Python环境

去 Anaconda 官网:https://www.anaconda.com/ 下载 Anaconda 安装包并且安装。

安装Python 3.6 的环境,打开命令行程序,执行下面命令:

conda create --name py36 python=3.6

激活 python 3.6 环境,用来安装必要的库:

activate py36

安装 face_recognition :

pip3 install face_recognition

安装 pandas :

pip3 install pandas

去C盘,找到 C:\Users\用户名 文件夹,找到 C:\Users\用户名\.conda\envs\py36\python 文件。这个就是 Python 的程序,当在命令行里激活 python36 环境的时候,就是在运行此文件。记录下这个具体的文件路径,后面Java 调用 Python 的时候会用到。我的电脑是 C:\Users\51489\.conda\envs\py36\python ,我以这个文件路径做例子讲解。

代码实现

Python代码:

# author: zhangchao
import os,sys
import face_recognition
import json
import numpy as np
from PIL import Image



imagePath_1 = sys.argv[1]

image1 = Image.open(imagePath_1)
image1 = np.array(image1)
locs = face_recognition.face_locations(image1, number_of_times_to_upsample=1, model="cnn")

locsJsonStr = json.dumps(locs)
print(locsJsonStr)
print("---")

locsLen = len(locs)
if locsLen <= 0:
    exit()

encodingResult = face_recognition.face_encodings(image1, locs)
print(encodingResult)

本例子 Java 代码使用 Maven 管理, pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>zhangchao</groupId>
    <artifactId>javaShortBlog</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>
</project>

处理命令行流的 CmdInputStreamRunnable 类:

package zhangchao.pyfacerecognition;


import java.io.*;

/**
 * @author 张超
 * 操作系统执行命令时会有输出,这些输出会被Java当作输入进行读取。
 * 本类启动线程来读取操作系统执行命令时的输出,避免出现JVM线程卡死,但命令本身正常的情况。
 */
public class CmdInputStreamRunnable implements Runnable {
    private StringBuffer stringBuffer;
    private InputStream inputStream;

    public CmdInputStreamRunnable(StringBuffer stringBuffer, InputStream inputStream) {
        this.stringBuffer = stringBuffer;
        this.inputStream = inputStream;
    }

    @Override
    public void run() {
        BufferedReader bufrIn = null;
        try {
            bufrIn = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            // 读取输出
            String line = null;
            while ((line = bufrIn.readLine()) != null) {
                stringBuffer.append(line).append('\n');
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != bufrIn) {
                    bufrIn.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

调用命令行的 CmdUtils 类:

package zhangchao.pyfacerecognition;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeUnit;

/**
 * @author 张超
 */
public class CmdUtils {

    /**
     * 执行命令,返回系统输出的字符串
     *
     * @param cmd 要执行的执行命令。对于exe等可执行程序,建议在命令中写好绝对路径,或者在操作系统中设置好环境变量。
     * @return 系统执行命令后输出的字符串
     */
    public static String exec(String cmd) {
        // 存放OS执行命令时的输出(对Java来说是输入)
        StringBuffer result = new StringBuffer();

        Process process = null;

        try {
            // 执行命令, 返回一个子进程对象(命令在子进程中执行)
            process = Runtime.getRuntime().exec(cmd, null, null);

            CmdInputStreamRunnable processInput = new CmdInputStreamRunnable(result, process.getInputStream());
            CmdInputStreamRunnable processError = new CmdInputStreamRunnable(result, process.getErrorStream());

            new Thread(processInput).start();
            new Thread(processError).start();


            // 方法阻塞, 等待命令执行完成(成功会返回0)
            process.waitFor();

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 销毁子进程
            if (process != null) {
                process.destroy();
            }
        }

        // 返回执行结果
        return result.toString();
    }

    /**
     * 执行命令,返回系统输出的字符串
     *
     * @param cmd 要执行的执行命令。对于exe等可执行程序,建议在命令中写好绝对路径,或者在操作系统中设置好环境变量。
     * @param timeout 子进程的存活时间,子进程超时后
     * @param unit 过期时间的时间单位
     * @return 系统执行命令后输出的字符串
     */
    public static String exec(String cmd, long timeout, TimeUnit unit) {
        // 存放OS执行命令时的输出(对Java来说是输入)
        StringBuffer result = new StringBuffer();

        Process process = null;

        try {
            // 执行命令, 返回一个子进程对象(命令在子进程中执行)
            process = Runtime.getRuntime().exec(cmd, null, null);

            CmdInputStreamRunnable processInput = new CmdInputStreamRunnable(result, process.getInputStream());
            CmdInputStreamRunnable processError = new CmdInputStreamRunnable(result, process.getErrorStream());

            new Thread(processInput).start();
            new Thread(processError).start();


            // 方法阻塞, 等待命令执行完成(成功会返回0)
            process.waitFor(timeout, unit);

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 销毁子进程
            if (process != null) {
                process.destroy();
            }
        }

        // 返回执行结果
        return result.toString();
    }

}

辅助解析JSON的ZcFaceVectorDto类

package zhangchao.pyfacerecognition;

import java.math.BigDecimal;
import java.util.List;

/**
 * 辅助解析JSON的DTO
 * @author zhangchao
 */
public class ZcFaceVectorDto {
    private List<List<Integer>> locs;

    private List<List<BigDecimal>> encodings;

    
    //   setters/getters
    

    public List<List<Integer>> getLocs() {
        return locs;
    }

    public void setLocs(List<List<Integer>> locs) {
        this.locs = locs;
    }

    public List<List<BigDecimal>> getEncodings() {
        return encodings;
    }

    public void setEncodings(List<List<BigDecimal>> encodings) {
        this.encodings = encodings;
    }
}

调用 Python 计算人脸向量的 FaceVectorUtils 类:

package zhangchao.pyfacerecognition;

import com.google.gson.Gson;

import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 调用python计算人脸向量
 * @author zhangchao
 */
public class FaceVectorUtils {

    private static final String PYTHON_EXE_PATH = "C:\\Users\\51489\\.conda\\envs\\py36\\python";

    /**
     * 从图片中计算人脸向量
     * @param originFilePath 原图片的硬盘路径位置。
     * @return 人脸向量列表。可能会返回多个人脸的向量。
     */
    public static List<List<BigDecimal>> calFaceVector(String originFilePath) {
        if (null == originFilePath) {
            return null;
        }
        File originFile = new File(originFilePath);
        if (!originFile.exists()) {
            return null;
        }
        String tmpCompressPath = null;
        // 如果文件太大,就压缩文件
        if (originFile.length() > 380 * 1024) {
            tmpCompressPath = "D:\\ws\\zctest\\ShortBlog\\pyFaceRecognition\\tmpCompressFile.jpg";
        } else {
            tmpCompressPath = originFilePath;
        }
        // 调用python3.6 计算人脸特征向量
        String result = CmdUtils.exec(PYTHON_EXE_PATH +
                " D:\\ws\\zctest\\ShortBlog\\pyFaceRecognition\\calVector.py " +
                tmpCompressPath);

        String[] arr = result.split("---");
        // 判断图片中有没有适合对比的人脸。如果没有就不存入 ep_face_vector
        Gson gson = new Gson();
        String locsStr = "{locs: " + arr[0] + "}";
        ZcFaceVectorDto faceVectorDto = gson.fromJson(locsStr, ZcFaceVectorDto.class);
        if (null == faceVectorDto || null == faceVectorDto.getLocs() ||
                faceVectorDto.getLocs().isEmpty()) {
            return null;
        }

        // 从字符串中解析出人脸特征向量。
        String encodingStr = arr[1];
        encodingStr = encodingStr.replaceAll("array", "");
        encodingStr = encodingStr.replaceAll("[(]", "");
        encodingStr = encodingStr.replaceAll("[)]", "");
        encodingStr = "{encodings:" + encodingStr + "}";

        ZcFaceVectorDto encodingFaceVectorDto = gson.fromJson(encodingStr, ZcFaceVectorDto.class);
        if (null == encodingFaceVectorDto || encodingFaceVectorDto.getEncodings() == null ||
                encodingFaceVectorDto.getEncodings().isEmpty()) {
            return null;
        }
        return encodingFaceVectorDto.getEncodings();
    }

    /**
     * 计算两个向量之间的距离。
     *
     * @param vector1 第一个向量
     * @param vector2 第二个向量
     * @return 两个向量之间的距离。
     */
    public static double calVectorDistance(List<BigDecimal> vector1, List<BigDecimal> vector2) {
        List<BigDecimal> minus = new ArrayList<>();
        int size = vector1.size();
        for (int i = 0; i < size; i++) {
            minus.add(vector1.get(i).subtract(vector2.get(i)));
        }
        BigDecimal total = new BigDecimal("0");
        for (BigDecimal item : minus) {
            BigDecimal tmp = item.multiply(item);
            total = total.add(tmp);
        }
        double dbTotal = total.doubleValue();
        double distance = Math.sqrt(dbTotal);
        return distance;
    }
}

测试类 TestFace:

package zhangchao.pyfacerecognition;

import java.util.List;
import java.math.BigDecimal;

public class TestFace {
    public static void main(String args[]) {
        System.out.println("begin");
        List<List<BigDecimal>> vectorList_1 = FaceVectorUtils.calFaceVector(
                "D:\\ws\\zctest\\ShortBlog\\pyFaceRecognition\\origin01.jpg"
        );

        List<List<BigDecimal>> vectorList_2 = FaceVectorUtils.calFaceVector(
                "D:\\ws\\zctest\\ShortBlog\\pyFaceRecognition\\origin02.jpg"
        );

        for (List<BigDecimal> item01 : vectorList_1) {
            for (List<BigDecimal> item02 : vectorList_2) {
                double distance = FaceVectorUtils.calVectorDistance(item01, item02);
                // 这里用户可以根据需要设置边界值,不一定必须时 0.49。
                // distance 数值越小说明两张脸越像。
                if (distance < 0.49) {
                    System.out.println(true);
                } else {
                    System.out.println(false);
                }
            }
        }
        System.out.println("end");
    }
}

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

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

相关文章

oracle——列表分页查询(原理)

文章目录前言数据表的准备分页sql1、简单分页实现2、排序分页3、排序优化前言 在平时的开发中&#xff0c;Oracle的分页查询用的特别多&#xff0c;接下来不止是说使用&#xff0c;更讲分页sql写法的原理。 数据表的准备 创建一张数据表&#xff0c;并填充大量的数据。 cre…

大数据技术架构(组件)12——Hive:判断函数

1.4.6、判断函数1.4.6.1、ifselect if(11,a,b),if(12,a,b) ;1.4.6.2、isnullselect isnull(1),isnull(null);1.4.6.3、isnotnullselect isnotnull(1),isnotnull(null);1.4.6.4、nvlselect nvl(1,1),nvl(null,1);1.4.6.5、coalesceselect coalesce(1,null,2,3,null,4,null),coal…

感谢第三弹 | 开启地铁国产化浪潮 GBASE获多方城市“地下动脉”肯定

岁末年初&#xff0c;GBASE收到了来自深圳地铁、高新现代智能系统股份有限公司、深圳达实智能股份有限公司等客户及合作伙伴发来的荣誉证书及感谢信。作为亲密无间的战友&#xff0c;GBASE携手高新现代、达实智能在深圳地铁CLC、ACC、AFC多个条线项目中通力合作&#xff0c;助力…

背包问题学习

01背包 01背包&#xff08;0-1 Knapsack Problem&#xff09; 有NNN件物品和一个容量为VVV的背包。放入第iii件物品耗费的费用是CiC_iCi​,得到的价值为WiW_iWi​。求解将哪些物品装入背包可以使价值总和最大 设F[i,v]F\left[i,v\right]F[i,v]表示前iii件物品敲好放入一个容量…

软件防错设计

出现非预期错误的原因解决方案原理介绍断根原理原理&#xff1a;将可能造成人错误的原因/条件从根本上排除断绝掉。通过冗余设计&#xff0c;屏蔽掉其中9种错误的方式&#xff1b;案例&#xff1a;USB的SD卡防写开关。4种可能性断根设计为只有1种可能是正确的。软件设计&#x…

Linux学习之环境准备【Vm+Centos】

文章目录前言一 VMware Workstation17 Pro下载和安装1.1 Vm下载指南1.2 VM安装指南二 Centos7安装2.1 装机器2.2 装系统三 补充内容3.1 卸载Centos前言 工欲善其事&#xff0c;必先利其器&#xff0c;我们要学习Linux当然需要Linux的环境由于大部分人使用的是Windows系统无法进…

介绍OAuth2

目录 一、什么是OAuth2&#xff1f; 二、OAuth2中的角色 1、资源所有者 2、资源服务器 3、客户 4、授权服务器 三、认证流程 四、OAuth2授权方式 注&#xff1a;使用令牌方式的优点 1、授权码 2、隐藏方式 3、密码方式 4、凭证方式 一、什么是OAuth2&#xff1f…

深入浅出ConcurrentHashMap1.8

前言 HashMap是我们平时开发过程中用的比较多的集合&#xff0c;但它是非线程安全的&#xff0c;在涉及到多线程并发的情况&#xff0c;进行get操作有可能会引起死循环&#xff0c;导致CPU利用率接近100%。 final HashMap<String, String> map new HashMap<String,…

基于Springboot搭建java项目(二十四)——权限认证框架SpringSecurity

权限认证框架SpringSecurity 一、为什么要做权限认证 在WEB开发的过程当中&#xff0c;安全一直是很重要的一方面。安全和直接的业务是没有直接挂钩的&#xff0c;但是在业务实现的初期如果没有考虑的安全性的化&#xff0c;在后期迭代的时候就会出现很多的问题。例如存在着安…

4.1--贪心--活动安排问题

今天开始&#xff0c;快速过一遍贪心&#xff0c;贪心要比动态规划简单许多&#xff0c;但是&#xff0c;我们也要理解其中的证明过程 贪心算法采用自顶向下&#xff0c;以迭代的方法做出相继的贪心选择&#xff0c;每做一次贪心选择就将所求问题简化为一个规模更小的子问题&am…

Maven实战-3.setting.xml标签说明

前言 持续更新中… setting.xml文件 1.<localRepository> 自定义本地Maven仓库目录地址。 这样本地的仓库地址就是配置的目录了。 2.<proxies> 基于安全因素考虑&#xff0c;使用通过安全认证的代理访问因特网&#xff0c;这时候需要为Maven配置HTTP代理&…

TOOM大数据舆情监控系统方案,如何做好舆情大数据监测分析?

大数据舆情监控是指通过对大量数据的收集、分析和处理&#xff0c;了解舆论的发展趋势和活动&#xff0c;并对舆论风险进行预警和评估&#xff0c;以保障企业的舆论安全。TOOM大数据舆情监控系统方案&#xff0c;如何做好舆情大数据监测分析? 一、大数据舆情监控系统方案 1.…

车载以太网 - SomeIP - SomeIP Entry - 04

前面总纲内容有说,车载以太网中的SomeIP内容是非常多的,并且为了实现SOA的相关需求,提供了多种多样的报文类型,因此也就有了今天要说的SomeIP-SD中的重要组成部分之一的条目(Entry)部分,而SomeIP-SD在车载网络中占有相当大的比重,可以当做是一定要实现的,如果这块不实…

C语言知识点复习------排序法(冒泡、选择、插入)与链表

目录 排序 冒泡排序&#xff1a; 选择排序&#xff1a; 插入排序&#xff1a; 链表 单链表的建立 单向循环链表的建立 双向循环链表 排序 冒泡排序&#xff1a; 冒泡排序&#xff08;Bubble Sort&#xff09;也是一种简单直观的排序算法。它重复地走访过要排序的数列&a…

RK356X系列(RK3568)GL857L芯片 USB驱动开发

首先我们先区分一下USB几种控制器类型协议 OHCI UHCI EHCI xHCI。OHCI、UHCI都是USB1.1的接口标准&#xff0c;而EHCI是对应USB2.0的接口标准&#xff0c;最新的xHCI是USB3.0的接口标准。1. OHCI&#xff08;Open Host Controller Interface&#xff09;是支持USB1.1的标准&…

C++ | 数据结构与算法 | 单源最短路径 | Dijkstra Bellman Ford

文章目录前言Dijkstra算法讲解与实现Bellman Ford算法与实现前言 &#xff08;关于代码实现的图结构&#xff0c;可以看图结构的实现这篇文章&#xff09; Dijkstra的实现与Prim的实现相似&#xff0c;两者都是通过贪心思想实现&#xff0c;它们有什么不同呢&#xff1f;首先…

数字IC设计工程师一般都干什么

数字IC设计工程师一般都干什么 简单来说&#xff0c;数字IC设计工程师一般就是负责写verilog代码&#xff08;当然&#xff0c;不是仅仅写个代码&#xff09;。本文主要对数字IC设计的工作流程进行简单的介绍&#xff0c;也算是对我从业一年来的总结。 一般来说&#xff0c;数…

linux下的僵尸进程处理SIGCHLD信号

阅读目录 对于waitpid的p i d参数的解释与其值有关&#xff1a;wait与waitpid区别&#xff1a; 转自&#xff1a;linux下的僵尸进程处理SIGCHLD信号 - Jessica程序猿 - 博客园 什么是僵尸进程&#xff1f; 首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区&am…

Java——字母大小写全排列

题目链接 字母大小写全排列 题目描述 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。 题目示例 输入&#xff1a;s “a1b2” 输出&#xff1a…

spring-boot国际化i18n中英文实现

一、背景 接触到的项目&#xff0c;使用国际化越来越多&#xff0c;系统页面实现中英文环境&#xff0c;记录下&#xff0c;前端使用vue vue-i8n 后端java自己封装 前端翻译对象&#xff1a;页面涉及到的所有按钮&#xff08;包括新增、删除、导出、下载、导入、上一页、下…