天天看点

前端js摄像头拍照,后端java调用百度api进行人脸识别登录0. 需求1. 前端实现2. 后端实现3. 后端难点4. 细节问题5. end

文章目录

  • 0. 需求
  • 1. 前端实现
    • 1.1 如何调用摄像头
    • 1.2 如何用ajax将图片从前端传到后端
  • 2. 后端实现
    • 2.1 如何接收ajax上传的图片
    • 2.2 如何调用百度人脸对比api
  • 3. 后端难点
    • 3.1 图片怎么存mysql数据库
    • 3.2 图片怎么取出数据库
  • 4. 细节问题
    • 4.1 百度api访问频率限制
    • 4.2 为啥改了后端代码前端没有任何改变
    • 4.3 我有一个form表单负责用户输入,怎么让它始终在屏幕最底端?
  • 5. end

0. 需求

在前端调用摄像头进行用户拍照,将照片传至后端,后端调用百度人脸对比api进行人相对比,如果相似度达到90%以上则进行登录。

1. 前端实现

1.1 如何调用摄像头

js代码如下:

// 启动摄像头
function activateCamera() {
    document.getElementById("vedioWindow").hidden=false;
    document.getElementById("canvasWindow").hidden=false;

    //video捕获摄像头画面
    navigator.mediaDevices.getUserMedia({
        video: true,
    }).then(success).catch(error)

    function success(stream) {
        video.src = window.webkitURL.createObjectURL(stream);
        video.play();
    }

    function error(err) {
        alert('video error: ' + err)
    }
}

function shoot() {
    //把当前视频帧内容渲染到画布上
    context.drawImage(self.video, 0, 0, 200, 200);
}

function myUpload() {
    //生成图片格式base64包括:jpg、png格式
    var Data = canvas.toDataURL("image/jpeg", 1.0);

    var imageDataB64 = Data.substring(22);

    //这里我打算用ajax方式,所以需要使用Formdata形式
    var data = new FormData();
    data.append("imgData", imageDataB64);

    $.ajax({
        url:"/admin/faceLogin",
        type: "post",
        data: data,
        //async: true,
        contentType: false,
        processData:false,  // 告诉浏览器不要处理我的数据 直接发就行
        success: function (res) {
            setCookie('isLogin','1');
            setCookie('userId',res.data.id);
            setCookie('userName', res.data.username);
            window.location.href = "myQuestionnaires.html";
        }
    });
}
           

第一个函数对应人脸识别按钮,点击此按钮打开摄像头;

第二个函数对应拍照按钮,点击按钮将当前摄像头的内容捕获下来并显示到canvas上。

第三个函数对应上传按钮,点击上传就可以将canvas上显示的照片上传到服务器端,进行后续操作。

1.2 如何用ajax将图片从前端传到后端

代码如下:

//生成图片格式base64包括:jpg、png格式
    var Data = canvas.toDataURL("image/jpeg", 1.0);

	//去掉图片首端的一些说明信息(占22位)
    var imageDataB64 = Data.substring(22);

    //这里我打算用ajax方式,所以需要使用Formdata形式
    var data = new FormData();
    data.append("imgData", imageDataB64);

    $.ajax({
        url:"/admin/faceLogin",
        type: "post",
        data: data,
        //async: true,
        contentType: false,
        processData:false,  // 告诉浏览器不要处理我的数据 直接发就行
        success: function (res) { // 登陆成功的回调函数
            setCookie('isLogin','1');
            setCookie('userId',res.data.id);
            setCookie('userName', res.data.username);
            window.location.href = "myQuestionnaires.html";
        }
    });
           

前端的内容大概就是这些。

2. 后端实现

2.1 如何接收ajax上传的图片

前面我们用ajax传递了一个data过来,data中有一个字段"imgData"对应了用户的图片。因此可以在controller的参数中取到这个图片的内容。

@RequestMapping(value = "/faceLogin", method = RequestMethod.POST, headers = "Accept=application/json")
    @ResponseBody
    public HttpResponseEntity faceLogin(@RequestParam("imgData") String imgData, HttpServletRequest request) {
        HttpResponseEntity httpResponseEntity = new HttpResponseEntity();

		//这里实现你的其他操作
    }
           

RequestPara代表传递过来的内容中必须要有"imgData",否则会出错。

String imgData就直接可以取到图片信息!!!名字一样就可以了!

需要注意前端我们取到的图片就已经是String类型的了(编码是base64),所以这里也要用String接收。

2.2 如何调用百度人脸对比api

我简单说一下要使用百度api的准备工作:

1.先去百度智能云注册登录

2.创建人脸识别应用

3.获得application key和secret key

调用百度api的大致流程:

1.先用你的application key(简称ak)和你的secret key(简称sk)发送请求获取一个access_token

2.用这个token和你要对比的两张图片发送url请求进行人脸对比

3.获取百度api的返回值,再返回值中取得相似度。

代码我不贴了,百度智能云api文档中有。

我说一个要注意的地方,那就是传递过去的数据格式。

首先,你要传递的数据格式是json,其次,你要传递的图片编码是base64。

我们在前端获取的图片非常好,本身就是base64的编码,因此我们可以直接传给百度api使用。但是如果你要使用你本机的图片jpg或者png等就需要先把文件转为base64编码的字符串才可以传过去。

现在问题又来了,你分别有了两张图片的base64编码的字符串,怎么把他们转成格式符合要求的json进行发送呢?代码如下:

List<Map<String, Object>> map = new ArrayList<>();
Map<String, Object> map1 = constructImageJson(img);
Map<String, Object> map2 = constructImageJson(imgData);
map.add(map1);
map.add(map2);

private Map<String, Object> constructImageJson(String res) {
        Map<String, Object> map = new HashMap<>();
        map.put("image", res);
        map.put("image_type", "BASE64");
        map.put("face_type", "LIVE");
        map.put("quality_control", "LOW");
        map.put("liveness_control", "NORMAL");
        return map;
    }
           

经过上述处理,你可以或者一个包含两个map的List,然后再把这个List转成json格式就可以传过去了。

String param = GsonUtils.toJson(map); //转List为json

//向百度api发送请求,result就是结果
// url代表百度api的url
result = HttpUtil.post(url, access_token, "application/json", param);
           

如果对比成功,那么在返回的json中将会有一个score字段记录了两种图片的相似度,通常来说,如果相似度达到百分之90以上就可以认为是同一个人,可以成功登录。

//解析传回的结果
JSONObject sb = JSONObject.parseObject(result);

//从结果中取得score
double score = JSONObject.parseObject(sb.getString("result")).getDouble("score");

//如果相似度大于等于90
if (score>=90) {
	httpResponseEntity.setData(ue); //传递用户信息
	httpResponseEntity.setCode(Constans.SUCCESS_CODE); //传递用户名
	httpResponseEntity.setMessage("登陆成功!");

	break;
}
return httpResponseEntity;

           

然后在前端进行页面的跳转和相应cookie的设置,整个登录过程就到此结束啦!

3. 后端难点

3.1 图片怎么存mysql数据库

思路如下:

1.设置数据库相应字段的类型为BLOB(或者MediumBlob等)

2.再将图片变为字节数组byte[]

3.将字节数组存入数据库即可

File转byte[]代码:

File f = new File("D:\\Temp\\anwserQuestionnaire-master\\anwserQuestionnaire-master\\src\\main\\resources\\static\\images\\zzq.png");

byte[] data = null;

try {
	FileInputStream fis = new FileInputStream(f);
	ByteArrayOutputStream baos = new ByteArrayOutputStream();

	int len;
	byte[] buffer = new byte[1024];
	while ((len = fis.read(buffer)) != -1) {
	baos.write(buffer, 0, len);
}

data = baos.toByteArray();

fis.close();
baos.close();
} catch (Exception e) {
	e.printStackTrace();
}

ue.setImage(data);
userService.insertUserImage(ue);
           

mybatis怎么写?

<update id="insertUserImage" parameterType="com.aim.questionnaire.dao.entity.UserEntity">
    update user_info set image = #{image, jdbcType=BLOB} where id = #{id, jdbcType=VARCHAR}
  </update>
           

需要注意的就是jdbcType写BLOB

3.2 图片怎么取出数据库

先看mybatis:

<resultMap type="com.aim.questionnaire.dao.entity.UserEntity" id="imgResultMap" >
    <id column="id" jdbcType="VARCHAR" property="id" />
    <result property="username" column="username" jdbcType="VARCHAR" />
    <result property="image" column="image" jdbcType="BLOB" typeHandler="org.apache.ibatis.type.BlobTypeHandler"/>
  </resultMap>
  <select id="queryAllUserImage" resultMap="imgResultMap">
    select id, username, image
    from user_info
  </select>
           

1.resultMap标签中,id表示主键,只有一个,result表示普通的字段。

1.property表示type中实体的一个属性(强烈建议要有一个对应的实体,不管你需不需要,都最好建立一个实体与之对应),column表示数据库的一个字段名。

3.取图片时,一定要在后方额外加一个typeHandler=“org.apache.ibatis.type.BlobTypeHandler”,否则出错。

而对于queryAllUserImage函数的返回值,写成相应实体的List就行。如:List<UserEntity>。

4. 细节问题

4.1 百度api访问频率限制

限制是qps=2,所以每请求两次就Thread.sleep(1000);睡眠一秒就行。

土豪请充钱。

4.2 为啥改了后端代码前端没有任何改变

1.首先确定你代码是不是写对了,如果对了,看2.

2.解决方法(1)删除你浏览器最近24小时的所有数据(2)进入浏览器的无痕模式,重新尝试

4.3 我有一个form表单负责用户输入,怎么让它始终在屏幕最底端?

前端我不太懂。

代码:

<form action="" style="position: fixed; bottom: 0px; margin-bottom: 20px">

...

</form>
           

具体参数还可以调整。其他标签也是同样使用了,要让标签在其他位置,比如最上,最右也可以这样,调一下方向就好。

当然最左最右也可以用float: right;和float: left来实现,这样也可以让两个标签位于同一行。

5. end

end