当前位置:   article > 正文

uniapp人脸识别解决方案_uniapp 小程序人脸识别

uniapp 小程序人脸识别

APP端:
因为APP端无法使用uni的camera组件,最开始考虑使用内嵌webview的方式,通过原生dom调用video渲染画面然后通过canvas截图。但是此方案兼容性在ios几乎为0,如果app只考虑安卓端的话可以采用此方案。后面又想用live-pusher组件来实现,但是发现快照api好像需要真实流地址才能截取图像。因为种种原因,也是安卓ios双端兼容性不佳。最终决定采用5+api实现。经实测5+api兼容性还算可以,但是毕竟是调用原生能力,肯定是没有原生开发那么丝滑的,难免会出现一些不可预测的兼容性问题。所以建议app和手机硬件交互强的话还是不要用uni开发了。不然真的是翻文档能翻死人。社区也找不到靠谱的解决方案。

5+api 文档
https://www.html5plus.org/doc/zh_cn/video.html#plus.video.createLivePusher

就是使用这个api调用原生的camera完成。并且可以直接在预览模式下完成快照,也不需要真实的推流地址。

  1. <template>
  2. <view class="content">
  3. <view class="c-footer">
  4. <view class="msg-box">
  5. <view class="msg">
  6. 1、请保证本人验证。
  7. </view>
  8. <view class="msg">
  9. 2、请使头像正面位于画框中。
  10. </view>
  11. <view class="msg">
  12. 3、请使头像尽量清晰。
  13. </view>
  14. <view class="msg">
  15. 4、请保证眼镜不反光,双眼可见。
  16. </view>
  17. <view class="msg">
  18. 5、请保证无墨镜,口罩,面膜等遮挡物。
  19. </view>
  20. <view class="msg">
  21. 6、请不要化浓妆,不要戴帽子。
  22. </view>
  23. </view>
  24. <view class="but" @click="snapshotPusher" v-if="!cilckSwitch">采集本人人脸</view>
  25. </view>
  26. </view>
  27. </template>
  28. <script>
  29. import permission from '../../util/permission.js'
  30. export default {
  31. data() {
  32. return {
  33. type: '', //是否是补签拉起的人脸识别
  34. imgData: '',
  35. pusher: null,
  36. scanWin: null,
  37. faceInitTimeout: null,
  38. snapshTimeout: null,
  39. uploadFileTask: null,
  40. cilckSwitch: false, //防止多次点击
  41. };
  42. },
  43. methods: {
  44. //人脸比对
  45. handleFaceContrast(param) {
  46. uni.hideLoading()
  47. this.$http({
  48. url: '/API_AUTH/AppIaiFace/faceContrast.api',
  49. data: {
  50. ...param,
  51. userid: uni.getStorageSync('userInfo').id
  52. }
  53. }).then(res => {
  54. console.log(res)
  55. if (res.data.compareResult == 1) {
  56. let pages = getCurrentPages(); //获取所有页面栈实例列表
  57. let nowPage = pages[pages.length - 1]; //当前页页面实例
  58. let prevPage = pages[pages.length - 2]; //上一页页面实例
  59. if (this.type == 'signOut') {
  60. prevPage.$vm.signOutXh = param.xh;
  61. prevPage.$vm.signOutPhotoPath = param.path
  62. } else {
  63. prevPage.$vm.xh = param.xh;
  64. prevPage.$vm.photoPath = param.path
  65. }
  66. uni.navigateBack()
  67. } else {
  68. uni.showToast({
  69. title: '人脸比对不通过,请重试',
  70. icon: 'none'
  71. })
  72. this.cilckSwitch = false
  73. }
  74. }).catch(err => {
  75. uni.showToast({
  76. title: '人脸比对不通过,请重试',
  77. icon: 'none'
  78. })
  79. this.cilckSwitch = false
  80. })
  81. },
  82. //初始化
  83. faceInit() {
  84. uni.showLoading({
  85. title: '请稍后...'
  86. })
  87. this.faceInitTimeout = setTimeout(async () => {
  88. //创建livepusher
  89. if (uni.getSystemInfoSync().platform === 'android') {
  90. const data1 = await permission.requestAndroidPermission(
  91. "android.permission.RECORD_AUDIO")
  92. const data2 = await permission.requestAndroidPermission("android.permission.CAMERA")
  93. console.log(data1,data2,1111)
  94. if (data1 == 1 && data2 == 1) {
  95. this.pusherInit();
  96. }
  97. } else {
  98. this.pusherInit();
  99. }
  100. //覆盖在视频之上的内容,根据实际情况编写
  101. // this.scanWin = plus.webview.create('/hybrid/html/faceTip.html', '', {
  102. // background: 'transparent'
  103. // });
  104. //新引入的webView显示
  105. // this.scanWin.show();
  106. //初始化上传本地文件的api
  107. this.initUploader()
  108. }, 500);
  109. },
  110. //初始化播放器
  111. pusherInit() {
  112. const currentWebview = this.$mp.page.$getAppWebview();
  113. this.pusher = plus.video.createLivePusher('livepusher', {
  114. url: '',
  115. top: '0px',
  116. left: '0px',
  117. width: '100%',
  118. height: '50%',
  119. position: 'absolute',
  120. aspect: '9:16',
  121. muted: false,
  122. 'z-index': 999999,
  123. 'border-radius': '50%',
  124. });
  125. currentWebview.append(this.pusher);
  126. //反转摄像头
  127. this.pusher.switchCamera();
  128. //开始预览
  129. this.pusher.preview();
  130. uni.hideLoading()
  131. },
  132. //初始化读取本地文件
  133. initUploader() {
  134. let that = this
  135. this.uploadFileTask = plus.uploader.createUpload(
  136. "完整的接口请求地址", {
  137. method: "POST",
  138. headers: {
  139. // 修改请求头Content-Type类型 此类型为文件上传
  140. "Content-Type": "multipart/form-data"
  141. }
  142. },
  143. // data:服务器返回的响应值 status: 网络请求状态码
  144. (data, status) => {
  145. // 请求上传文件成功
  146. if (status == 200) {
  147. console.log(data)
  148. // 获取data.responseText之后根据自己的业务逻辑做处理
  149. let result = JSON.parse(data.responseText);
  150. console.log(result.data.xh)
  151. that.handleFaceContrast({
  152. xh: result.data.xh,
  153. path: result.data.path
  154. })
  155. }
  156. // 请求上传文件失败
  157. else {
  158. uni.showToast({
  159. title: '上传图片失败',
  160. icon: 'none'
  161. })
  162. console.log("上传失败", status)
  163. that.cilckSwitch = false
  164. uni.hideLoading()
  165. }
  166. }
  167. );
  168. },
  169. //快照
  170. snapshotPusher() {
  171. if (this.cilckSwitch) {
  172. uni.showToast({
  173. title: '请勿频繁点击',
  174. icon: 'none'
  175. })
  176. return
  177. }
  178. this.cilckSwitch = true
  179. uni.showLoading({
  180. title: '正在比对,请勿退出'
  181. })
  182. let that = this
  183. this.snapshTimeout = setTimeout(() => {
  184. this.pusher.snapshot(
  185. e => {
  186. // this.pusher.close();
  187. // this.scanWin.hide();
  188. //拿到本地文件路径
  189. var src = e.tempImagePath;
  190. this.uploadImg(src)
  191. //获取图片base64
  192. // this.getMinImage(src);
  193. },
  194. function(e) {
  195. plus.nativeUI.alert('snapshot error: ' + JSON.stringify(e));
  196. that.cilckSwitch = false
  197. uni.hideLoading()
  198. }
  199. );
  200. }, 500);
  201. },
  202. //调用原生能力读取本地文件并上传
  203. uploadImg(imgPath) {
  204. this.uploadFileTask.addFile('file://' + imgPath, {
  205. key: "file" // 填入图片文件对应的字段名
  206. });
  207. //添加其他表单字段(参数) 两个参数可能都只支持传字符串
  208. // uploadFileTask.addData("参数名", 参数值);
  209. this.uploadFileTask.start();
  210. },
  211. //获取图片base64
  212. getMinImage(imgPath) {
  213. plus.zip.compressImage({
  214. src: imgPath,
  215. dst: imgPath,
  216. overwrite: true,
  217. quality: 40
  218. },
  219. zipRes => {
  220. setTimeout(() => {
  221. var reader = new plus.io.FileReader();
  222. reader.onloadend = res => {
  223. var speech = res.target.result; //base64图片
  224. console.log(speech.length);
  225. console.log(speech)
  226. this.imgData = speech;
  227. };
  228. reader.readAsDataURL(plus.io.convertLocalFileSystemURL(zipRes.target));
  229. }, 1000);
  230. },
  231. function(error) {
  232. console.log('Compress error!', error);
  233. }
  234. );
  235. },
  236. },
  237. onLoad(option) {
  238. //#ifdef APP-PLUS
  239. this.type = option.type
  240. this.faceInit();
  241. //#endif
  242. },
  243. onHide() {
  244. console.log('hide')
  245. this.faceInitTimeout && clearTimeout(this.faceInitTimeout);
  246. this.snapshTimeout && clearTimeout(this.snapshTimeout);
  247. // this.scanWin.hide();
  248. },
  249. onBackPress() {
  250. // let pages = getCurrentPages(); //获取所有页面栈实例列表
  251. // let nowPage = pages[pages.length - 1]; //当前页页面实例
  252. // let prevPage = pages[pages.length - 2]; //上一页页面实例
  253. // prevPage.$vm.xh = '11111';
  254. // prevPage.$vm.photoPath = '22222'
  255. this.faceInitTimeout && clearTimeout(this.faceInitTimeout);
  256. this.snapshTimeout && clearTimeout(this.snapshTimeout);
  257. // this.scanWin.hide();
  258. },
  259. onUnload() {
  260. this.faceInitTimeout && clearTimeout(this.faceInitTimeout);
  261. this.snapshTimeout && clearTimeout(this.snapshTimeout);
  262. // this.scanWin.hide();
  263. },
  264. };
  265. </script>
  266. <style lang="scss" scoped>
  267. .but {
  268. margin: 50rpx auto 0;
  269. border-radius: 10px;
  270. width: 80%;
  271. height: 100rpx;
  272. display: flex;
  273. align-items: center;
  274. justify-content: center;
  275. background-color: #008AFF;
  276. font-size: 16px;
  277. color: #FFFFFF;
  278. }
  279. .c-footer {
  280. width: 100%;
  281. position: fixed;
  282. top: 50%;
  283. left: 0;
  284. z-index: 10;
  285. padding: 30rpx 0;
  286. .msg-box {
  287. width: 80%;
  288. margin: 0 auto;
  289. text-align: left;
  290. .msg {
  291. margin-bottom: 15rpx;
  292. font-size: 13px;
  293. color: #666;
  294. }
  295. }
  296. }
  297. .img-data {
  298. width: 100%;
  299. height: auto;
  300. }
  301. .content {
  302. background-color: #fff;
  303. }
  304. </style>

 以上是完整的包含逻辑的代码。

  1. 关键代码
  2. //初始化播放器
  3. pusherInit() {
  4. const currentWebview = this.$mp.page.$getAppWebview();
  5. this.pusher = plus.video.createLivePusher('livepusher', {
  6. url: '',
  7. top: '0px',
  8. left: '0px',
  9. width: '100%',
  10. height: '50%',
  11. position: 'absolute',
  12. aspect: '9:16',
  13. muted: false,
  14. 'z-index': 999999,
  15. 'border-radius': '50%',
  16. });
  17. currentWebview.append(this.pusher);
  18. //反转摄像头
  19. this.pusher.switchCamera();
  20. //开始预览
  21. this.pusher.preview();
  22. uni.hideLoading()
  23. },
  24. //快照
  25. snapshotPusher() {
  26. if (this.cilckSwitch) {
  27. uni.showToast({
  28. title: '请勿频繁点击',
  29. icon: 'none'
  30. })
  31. return
  32. }
  33. this.cilckSwitch = true
  34. uni.showLoading({
  35. title: '正在比对,请勿退出'
  36. })
  37. let that = this
  38. this.snapshTimeout = setTimeout(() => {
  39. this.pusher.snapshot(
  40. e => {
  41. //拿到本地文件路径
  42. var src = e.tempImagePath;
  43. //这里因为接口参数需要加密,用base64的话加密出来的参数太大了,所以选择了直接读取本地文件上传文件流的方式。
  44. this.uploadImg(src)
  45. //获取图片base64
  46. // this.getMinImage(src);
  47. },
  48. function(e) {
  49. plus.nativeUI.alert('snapshot error: ' + JSON.stringify(e));
  50. that.cilckSwitch = false
  51. uni.hideLoading()
  52. }
  53. );
  54. }, 500);
  55. },
  56. //获取图片base64
  57. getMinImage(imgPath) {
  58. plus.zip.compressImage({
  59. src: imgPath,
  60. dst: imgPath,
  61. overwrite: true,
  62. quality: 40
  63. },
  64. zipRes => {
  65. setTimeout(() => {
  66. var reader = new plus.io.FileReader();
  67. reader.onloadend = res => {
  68. var speech = res.target.result; //base64图片
  69. console.log(speech.length);
  70. console.log(speech)
  71. this.imgData = speech;
  72. };
  73. reader.readAsDataURL(plus.io.convertLocalFileSystemURL(zipRes.target));
  74. }, 1000);
  75. },
  76. function(error) {
  77. console.log('Compress error!', error);
  78. }
  79. );
  80. },
  81. //初始化读取本地文件
  82. initUploader() {
  83. let that = this
  84. this.uploadFileTask = plus.uploader.createUpload(
  85. "完整的接口请求地址", {
  86. method: "POST",
  87. headers: {
  88. // 修改请求头Content-Type类型 此类型为文件上传
  89. "Content-Type": "multipart/form-data"
  90. }
  91. },
  92. // data:服务器返回的响应值 status: 网络请求状态码
  93. (data, status) => {
  94. // 请求上传文件成功
  95. if (status == 200) {
  96. console.log(data)
  97. // 获取data.responseText之后根据自己的业务逻辑做处理
  98. let result = JSON.parse(data.responseText);
  99. console.log(result.data.xh)
  100. that.handleFaceContrast({
  101. xh: result.data.xh,
  102. path: result.data.path
  103. })
  104. }
  105. // 请求上传文件失败
  106. else {
  107. uni.showToast({
  108. title: '上传图片失败',
  109. icon: 'none'
  110. })
  111. console.log("上传失败", status)
  112. that.cilckSwitch = false
  113. uni.hideLoading()
  114. }
  115. }
  116. );
  117. },
  118. //调用原生能力读取本地文件并上传
  119. uploadImg(imgPath) {
  120. this.uploadFileTask.addFile('file://' + imgPath, {
  121. key: "file" // 填入图片文件对应的字段名
  122. });
  123. //添加其他表单字段(参数) 两个参数可能都只支持传字符串
  124. // uploadFileTask.addData("参数名", 参数值);
  125. this.uploadFileTask.start();
  126. },

以上就是关键的代码。
接下来补充几个坑的地方。创建出来的livepusher层级很高,无法在同一页面被别的元素遮挡。所以想要在他上面写样式是行不通了。只能再创建一个webview。然后将这个webview覆盖在livepusher上,达到人脸识别页面的样式。

  1. //覆盖在视频之上的内容,根据实际情况编写
  2. this.scanWin = plus.webview.create('/hybrid/html/faceTip.html', '', {
  3. background: 'transparent'
  4. });
  5. //新引入的webView显示
  6. this.scanWin.show();
  7. //新引入的webView影藏
  8. this.scanWin.hide();

这种方案在ios基本没问题。至少目前没遇到过。但是安卓就一言难尽了。首先这个组件默认调起的是后置摄像头,这显然不符合我们的需求。但是官方提供的文档里也没有明确支持可以配置优先调起哪个摄像头。好早提供了一个switchCamera的api可以翻转摄像头。

但是在安卓系统上,尤其是鸿蒙系统,调用这个api就会导致程序闪退,而且发生频率还特别高。这个问题至今不知道该怎么解决。除了闪退问题,安卓还存在一个麻烦事儿,那就是首次进入app,翻转摄像头的api没有用,拉起的还是后置摄像头。但是后续再进入app就无此问题了。后面折腾来折腾去,发现好像是首次进入拉起授权弹窗的时候才会出现这种问题。
然后写了个定时器做测试,五秒之后再拉起摄像头再去翻转摄像头。然后再五秒内赶紧把授权给同意了。结果发现翻转竟然生效了。
然后决定再渲染推流元素之前先让用户通过权限授权,然后再拉起摄像头。 也就是上文完整代码中的

 

  1. //创建livepusher
  2. if (uni.getSystemInfoSync().platform === 'android') {
  3. const data1 = await permission.requestAndroidPermission(
  4. "android.permission.RECORD_AUDIO")
  5. const data2 = await permission.requestAndroidPermission("android.permission.CAMERA")
  6. console.log(data1,data2,1111)
  7. if (data1 == 1 && data2 == 1) {
  8. this.pusherInit();
  9. }
  10. } else {
  11. this.pusherInit();
  12. }

具体的意思就不过多赘述了,自行看permission的文档。或者看他的代码。很简单
permission下载地址
https://ext.dcloud.net.cn/plugin?id=594
以上就是调用原生能力拉起摄像头实现快照功能的所有内容了。

下面也记录一下web端如果实现这种功能,毕竟当时搞出来也不容易,但是最终还是败在了兼容性上

方案的话大致有两种,一种是借助tracking js 有兴趣的可以了解一下,一个web端人脸识别库。他可以识别画面中是否出现人脸。以及一下更高级的功能我就没有去探索了。有需要的可以自行研究

  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title>人脸识别</title>
  6. <script src="../html/js/tracking-min.js"></script>
  7. <script src="../html/js/face-min.js"></script>
  8. <style>
  9. video,
  10. canvas {
  11. position: absolute;
  12. }
  13. </style>
  14. </head>
  15. <body>
  16. <div class="demo-container">
  17. <video id="video" width="320" height="240" preload autoplay loop muted></video>
  18. <canvas id="canvas" width="320" height="240"></canvas>
  19. </div>
  20. <script>
  21. window.onload = function() {
  22. console.log(123123123)
  23. // 视频显示
  24. var video = document.getElementById('video');
  25. // 绘图
  26. var canvas = document.getElementById('canvas');
  27. var context = canvas.getContext('2d');
  28. var time = 10000;
  29. var tracker = new tracking.ObjectTracker('face');
  30. // 设置识别的放大比例
  31. tracker.setInitialScale(4);
  32. // 设置步长
  33. tracker.setStepSize(2);
  34. // 边缘密度
  35. tracker.setEdgesDensity(0.1);
  36. // 启动摄像头,并且识别视频内容
  37. var trackerTask = tracking.track('#video', tracker, {
  38. camera: true
  39. });
  40. var flag = true;
  41. tracker.on('track', function(event) {
  42. if (event.data.length === 0) {
  43. console.log('未检测到人脸')
  44. context.clearRect(0, 0, canvas.width, canvas.height);
  45. } else if (event.data.length > 1) {
  46. console.log('检测到多张人脸')
  47. } else {
  48. context.clearRect(0, 0, canvas.width, canvas.height);
  49. event.data.forEach(function(rect) {
  50. context.strokeStyle = '#ff0000';
  51. context.strokeRect(rect.x, rect.y, rect.width, rect.height);
  52. context.fillStyle = "#ff0000";
  53. //console.log(rect.x, rect.width, rect.y, rect.height);
  54. });
  55. if (flag) {
  56. console.log("拍照");
  57. context.drawImage(video, 0, 0, 320, 240);
  58. saveAsLocalImage();
  59. context.clearRect(0, 0, canvas.width, canvas.height);
  60. flag = false;
  61. setTimeout(function() {
  62. flag = true;
  63. }, time);
  64. } else {
  65. //console.log("冷却中");
  66. }
  67. }
  68. });
  69. };
  70. function saveAsLocalImage() {
  71. var myCanvas = document.getElementById("canvas");
  72. // here is the most important part because if you dont replace you will get a DOM 18 exception.
  73. // var image = myCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream;Content-Disposition: attachment;filename=foobar.png");
  74. var image = myCanvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
  75. // window.location.href = image; // it will save locally
  76. // create temporary link
  77. return
  78. var tmpLink = document.createElement('a');
  79. tmpLink.download = 'image.png'; // set the name of the download file
  80. tmpLink.href = image;
  81. // temporarily add link to body and initiate the download
  82. document.body.appendChild(tmpLink);
  83. tmpLink.click();
  84. document.body.removeChild(tmpLink);
  85. }
  86. </script>
  87. </body>
  88. </html>

 另外一种就是纯video+canvas截取一张视频中的画面。

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>人脸采集</title>
  6. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  7. <link rel="stylesheet" type="text/css" href="./css/index.css" />
  8. <script src="./js/jq.js" type="text/javascript" charset="utf-8"></script>
  9. <!-- uni 的 SDK -->
  10. <script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js">
  11. </script>
  12. <script src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.0.0/vconsole.min.js" type="text/javascript"
  13. charset="utf-8"></script>
  14. <script>
  15. // init vConsole,app中查看
  16. var vConsole = new VConsole();
  17. // console.log('Hello world');
  18. </script>
  19. <style>
  20. .mui-content {
  21. margin: 0 auto;
  22. text-align: center;
  23. border: 0px solid red;
  24. }
  25. /*摄像头翻转180*/
  26. video {
  27. transform: rotateY(180deg);
  28. -webkit-transform: rotateY(180deg);
  29. /* Safari 和 Chrome */
  30. -moz-transform: rotateY(180deg);
  31. }
  32. </style>
  33. </head>
  34. <body class='body'>
  35. <div class="mui-content">
  36. <div style="margin: 40px auto;">
  37. <!-- <input type="file" id='image' accept="image/*" capture='user'> -->
  38. <video id="video" style="margin: 0 auto; border-radius: 150px;"></video>
  39. <canvas id='canvas' width="300" height="300"
  40. style="border: 0px solid red;margin: auto; display: none;"></canvas>
  41. </div>
  42. <div class="msg-box">
  43. <div class="msg">
  44. 1、请保证本人验证。
  45. </div>
  46. <div class="msg">
  47. 2、请使头像正面位于画框中。
  48. </div>
  49. <div class="msg">
  50. 3、请使头像尽量清晰。
  51. </div>
  52. <div class="msg">
  53. 4、请保证眼镜不反光,双眼可见。
  54. </div>
  55. <div class="msg">
  56. 5、请保证无墨镜,口罩,面膜等遮挡物。
  57. </div>
  58. <div class="msg">
  59. 6、请不要化浓妆,不要戴帽子。
  60. </div>
  61. </div>
  62. <div style="width: 80%; position: absolute; bottom: 20px; left: 50%; transform: translate(-50%, -50%);">
  63. <div class="but" id="start">采集本人人脸</div>
  64. </div>
  65. </div>
  66. </body>
  67. <script type="text/javascript">
  68. var video, canvas, vendorUrl, interval, videoHeight, videoWidth, time = 0;
  69. // 获取webview页面数据
  70. // var data = JSON.parse(getUrlParam('data'))
  71. // var info = data.info;
  72. // var userInfo = data.userInfo;
  73. // const userId = data.userId; // 当前登录用户id
  74. $(function() {
  75. // 初始化
  76. initVideo()
  77. // uni.app事件
  78. document.addEventListener('UniAppJSBridgeReady', function() {
  79. uni.getEnv(function(res) {
  80. console.log('当前环境:' + JSON.stringify(res));
  81. });
  82. setInterval(() => {
  83. uni.postMessage({
  84. data: {
  85. action: 'postMessage'
  86. }
  87. });
  88. }, 1000)
  89. });
  90. })
  91. // 获取url携带的数据
  92. function getUrlParam(name) {
  93. var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
  94. var r = window.location.search.substr(1).match(reg);
  95. if (r != null) return unescape(r[2]);
  96. return null;
  97. }
  98. // 摄像头初始化
  99. function initVideo() {
  100. console.log('摄像头初始化')
  101. video = document.getElementById("video");
  102. videoHeight = 300
  103. videoWidth = 300
  104. setTimeout(() => {
  105. console.log(navigator)
  106. navigator.mediaDevices
  107. .getUserMedia({
  108. video: {
  109. width: {
  110. ideal: videoWidth,
  111. max: videoWidth
  112. },
  113. height: {
  114. ideal: videoHeight,
  115. max: videoHeight
  116. },
  117. facingMode: 'user', //前置摄像头
  118. // facingMode: { exact: "environment" }, //后置摄像头
  119. frameRate: {
  120. ideal: 30,
  121. min: 10
  122. }
  123. }
  124. })
  125. .then(videoSuccess)
  126. .catch(videoError);
  127. if (
  128. navigator.mediaDevices.getUserMedia ||
  129. navigator.getUserMedia ||
  130. navigator.webkitGetUserMedia ||
  131. navigator.mozGetUserMedia ||
  132. navigator.mediaCapabilities
  133. ) {
  134. console.log('调用用户媒体设备, 访问摄像头')
  135. //调用用户媒体设备, 访问摄像头
  136. getUserMedia({
  137. video: {
  138. width: {
  139. ideal: videoWidth,
  140. max: videoWidth
  141. },
  142. height: {
  143. ideal: videoHeight,
  144. max: videoHeight
  145. },
  146. facingMode: 'user', //前置摄像头
  147. // facingMode: { exact: "environment" }, //后置摄像头
  148. frameRate: {
  149. ideal: 30,
  150. min: 10
  151. }
  152. }
  153. },
  154. videoSuccess,
  155. videoError
  156. );
  157. } else {}
  158. }, 300);
  159. }
  160. // 获取用户设备
  161. function getUserMedia(constraints, success, error) {
  162. if (navigator.mediaDevices.getUserMedia) {
  163. //最新的标准API
  164. navigator.mediaDevices
  165. .getUserMedia(constraints)
  166. .then(success)
  167. .catch(error);
  168. } else if (navigator.webkitGetUserMedia) {
  169. //webkit核心浏览器
  170. navigator.webkitGetUserMedia(constraints, success, error);
  171. } else if (navigator.mozGetUserMedia) {
  172. //firfox浏览器
  173. navigator.mozGetUserMedia(constraints, success, error);
  174. } else if (navigator.getUserMedia) {
  175. //旧版API
  176. navigator.getUserMedia(constraints, success, error);
  177. }
  178. }
  179. // 开始有画面
  180. function videoSuccess(stream) {
  181. //this.mediaStreamTrack = stream;
  182. console.log("=====stream")
  183. video.srcObject = stream;
  184. video.play();
  185. //$("#max-bg").css('background-color', 'rgba(0,0,0,0)')
  186. // 这里处理我的的东西
  187. }
  188. function videoError(error) {
  189. alert('摄像头获取错误')
  190. console.log('摄像头获取错误')
  191. setTimeout(() => {
  192. initVideo()
  193. }, 6000)
  194. }
  195. // 单次拍照
  196. function getFaceImgBase64() {
  197. canvas = document.getElementById('canvas');
  198. //绘制canvas图形
  199. canvas.getContext('2d').drawImage(video, 0, 0, 300, 300);
  200. //把canvas图像转为img图片
  201. var bdata = canvas.toDataURL("image/jpeg");
  202. //img.src = canvas.toDataURL("image/png");
  203. return bdata.split(',')[1]; //照片压缩成base位数据
  204. }
  205. $('#start').on('click', function() {
  206. time = 0;
  207. console.log("开始人脸采集,请正对屏幕");
  208. faceGather();
  209. })
  210. // 人脸采集
  211. function faceGather() {
  212. const faceImgBase64 = getFaceImgBase64();
  213. console.log(faceImgBase64);
  214. }
  215. </script>
  216. </html>
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/318036
推荐阅读
相关标签
  

闽ICP备14008679号