FaceRecognition.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <template>
  2. <div>
  3. <video id="video" ref="video" :width="width" :height="height" autoplay>
  4. </video>
  5. <div v-if="showRecognizeButton" style="position: absolute; width: 400px; text-align: center; margin-top: -50px; color: #232323;">
  6. <span class="verify-button" @click="snap">开始识别</span>
  7. </div>
  8. </div>
  9. </template>
  10. <script>
  11. import { mapState } from "vuex";
  12. import {
  13. UPYUN_URL,
  14. FACEPP_API,
  15. FACEPP_KEY,
  16. FACEPP_SECRET
  17. } from "../../constants/constants.js";
  18. export default {
  19. name: "FaceRecognition",
  20. data() {
  21. return { hide: true };
  22. },
  23. props: {
  24. width: String,
  25. height: String,
  26. showRecognizeButton: Boolean,
  27. closeCamera: Boolean // optional
  28. },
  29. async mounted() {
  30. this.openCamera();
  31. },
  32. watch: {
  33. closeCamera: function(newValue) {
  34. if (newValue) {
  35. console.log("关闭摄像头");
  36. this.$refs.video.srcObject.getTracks().forEach(function(track) {
  37. track.stop();
  38. });
  39. } else {
  40. this.openCamera();
  41. }
  42. }
  43. },
  44. beforeDestroy() {
  45. this.$refs.video.srcObject.getTracks().forEach(function(track) {
  46. track.stop();
  47. });
  48. },
  49. methods: {
  50. async openCamera() {
  51. const video = this.$refs.video;
  52. if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
  53. try {
  54. console.log("启动摄像头");
  55. const stream = await navigator.mediaDevices.getUserMedia({
  56. video: { facingMode: "user" }
  57. });
  58. video.srcObject = stream;
  59. video.play();
  60. } catch (error) {
  61. console.log(error);
  62. this.$Message.error("无法启用摄像头");
  63. }
  64. } else {
  65. this.$Message.error("没有找到可用的摄像头");
  66. }
  67. },
  68. // TODO: 定时抓拍
  69. snap() {
  70. const video = this.$refs.video;
  71. video.pause();
  72. var canvas = document.createElement("canvas");
  73. canvas.width = 220;
  74. canvas.height = 165;
  75. var context = canvas.getContext("2d");
  76. context.drawImage(video, 0, 0, 220, 165);
  77. canvas.toBlob(this.uploadToServer);
  78. video.play();
  79. },
  80. async uploadToServer(captureBlob) {
  81. //保存抓拍照片到又拍云
  82. var fileName = new Date().getTime() + ".jpg";
  83. var fileUrl =
  84. "/capture_photo/" + this.user.identityNumber + "/" + fileName;
  85. try {
  86. await this.$upyunhttp.put(fileUrl, captureBlob, {
  87. headers: {
  88. "Content-Type": "image/jpeg"
  89. }
  90. });
  91. } catch (e) {
  92. console.log(e);
  93. this.$Message.error(e.message);
  94. return;
  95. }
  96. const captureFilePath =
  97. UPYUN_URL +
  98. "/capture_photo/" +
  99. this.user.identityNumber +
  100. "/" +
  101. fileName;
  102. await this.faceCompare(captureFilePath);
  103. },
  104. async faceCompare(captureFilePath) {
  105. const res = await this.$http.get("/api/ecs_core/student/getStudentInfo", {
  106. params: {
  107. identityNumber: this.user.identityNumber,
  108. orgId: this.user.rootOrgId
  109. }
  110. });
  111. const faceToken = res.data.faceToken;
  112. console.log(faceToken);
  113. console.log(captureFilePath);
  114. // TODO: 研究是否有更合适的位置,只使用了一次. 应该由server进行检测
  115. try {
  116. const faceCompareRes = await fetch(
  117. FACEPP_API +
  118. "/compare?" +
  119. `api_key=${FACEPP_KEY}&api_secret=${FACEPP_SECRET}&face_token1=${faceToken}&image_url2=${captureFilePath}`,
  120. {
  121. method: "post",
  122. headers: {
  123. "Content-Type": "application/x-www-form-urlencoded"
  124. }
  125. }
  126. );
  127. const verifyResult = await faceCompareRes.json();
  128. console.log("人脸检测结果: ", verifyResult);
  129. // 告知服务器人脸检测结果
  130. const params = new URLSearchParams();
  131. params.append("pass", true);
  132. params.append("action", "COMPARE");
  133. await this.$http.post("/api/face_capture", params);
  134. // TODO: 识别成功、失败的通知或跳转
  135. let pass = false;
  136. if (verifyResult.faces2.length > 0) {
  137. var confidence = verifyResult.confidence;
  138. var thresholds = verifyResult.thresholds;
  139. if (confidence && thresholds && confidence > thresholds["1e-4"]) {
  140. pass = true;
  141. }
  142. }
  143. this.$emit("on-recognize-result", {
  144. error: null,
  145. pass,
  146. faceCount: verifyResult.faces2.length
  147. });
  148. } catch (e) {
  149. console.log(e);
  150. this.$Message.error(e.message);
  151. return;
  152. }
  153. }
  154. },
  155. computed: {
  156. ...mapState(["user"])
  157. }
  158. };
  159. </script>
  160. <style scoped>
  161. .verify-button {
  162. font-size: 16px;
  163. background-color: #ffcc00;
  164. display: inline-block;
  165. padding: 6px 16px;
  166. border-radius: 6px;
  167. }
  168. .verify-button:hover {
  169. color: #444444;
  170. cursor: pointer;
  171. }
  172. </style>