一四三、人脸识别自动点赞、关注

文章目录

      • 脚本功能
      • 获取video当前播放帧图片
      • 将图片传到后台
      • 调用百度人脸识别接口
      • 拿到识别结果处理逻辑
      • 效果展示
      • 问题记录

脚本功能

  1. 通过获取video当前播放帧图片,截图调用后台接口,再调用百度人脸识别
  2. 拿到人脸信息(年龄、颜值、性别等)
  3. 判断点赞、收藏或者下一条

获取video当前播放帧图片

参考js使用canvas实现视频截图

  1. 创建canvas
  2. 获取当前屏幕的ratio值(如果不获取,截取的图片会比较模糊)
  3. 通过使用 drawImage() 方法将视频画面画在 canvasCtx 上

drawImage() 参数介绍
drawImage(image, dx, dy)
drawImage(image, dx, dy, dw, dh)
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
image:必选,要截取的 image 或者 video 元素
sx:可选,被截取图片的裁剪开始位置的 x 坐标
sy:可选,被截取图片的裁剪开始位置的 y 坐标
sw:可选,被截取图片的裁剪宽度
sh:可选,被截取图片的裁剪高度
dx:必选,裁剪图片放在画布上位置的 x 坐标
dy:必选,裁剪图片放在画布上位置的 y 坐标
dw:可选,裁剪图片放在画布上的宽度(放大或缩小)
dh:可选,裁剪图片放在画布上的高度(放大或缩小)

  1. 使用 toDataURL() 将 canvas 转为图片(base64)
  2. 将base64转为blob格式
    var canvas = document.createElement('canvas');
    var canvasCtx = canvas.getContext('2d');
    var ratio = getPixelRatio(canvasCtx);
    video = document.querySelector("div[data-e2e='feed-active-video'] video");
    canvas.width = video.offsetWidth * ratio;
    canvas.height = video.offsetHeight * ratio;
    canvasCtx.fillStyle = '#222125';
    canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
    var imgWidth = Math.min(canvas.width, (video.videoWidth * canvas.height) / video.videoHeight);
    var imgHeight = Math.min(canvas.height, (video.videoHeight * canvas.width) / video.videoWidth);
    canvasCtx.drawImage(
        video,
        0,
        0,
        video.videoWidth,
        video.videoHeight,
        (canvas.width - imgWidth) / 2,
        (canvas.height - imgHeight) / 2,
        imgWidth,
        imgHeight
    );
    var MIME_TYPE = 'image/png'; // 保存文件类型
    var imgURL = canvas.toDataURL(MIME_TYPE);
    const blob = convertBase64ToBlob(imgURL);
    // 获取radio
	function getPixelRatio(context) {
	    var backingStore =
	        context.backingStorePixelRatio ||
	        context.webkitBackingStorePixelRatio ||
	        context.mozBackingStorePixelRatio ||
	        context.msBackingStorePixelRatio ||
	        context.oBackingStorePixelRatio ||
	        context.backingStorePixelRatio ||
	        1;
	    return (window.devicePixelRatio || 1) / backingStore - 0.5;
	}
// 将 base64 转换为二进制格式
	function convertBase64ToBlob(base64) {
	    const byteString = atob(base64.split(',')[1]);
	    const mimeType = base64.split(';')[0].split(':')[1];
	    const ab = new ArrayBuffer(byteString.length);
	    const ia = new Uint8Array(ab);
	    for (let i = 0; i < byteString.length; i++) {
	        ia[i] = byteString.charCodeAt(i);
	    }
	    return new Blob([ab], {type: mimeType});
	}

将图片传到后台

直接使用fetch、jQuery的$ajax会存在跨域,通过后端设置cors也不能解决,搜索后发现tampermonkey提供GM_xmlhttpRequest来跨域请求

  1. 封装GM_xmlhttpRequest
//参数1:url;参数2:请求类型get或post;参数3:post的body;
function runAsync(url, send_type, data_ry) {
    var p = new Promise((resolve, reject) => {
        GM_xmlhttpRequest({
            method: send_type,
            url: url,
            headers: {
                'Content-Type': 'application/octet-stream'
            },
            data: data_ry,
            onload: function (response) {
                resolve(JSON.parse(response.responseText));
            },
            onerror: function (err) {
                reject(err);
            }
        });
    });
    return p;
}
  1. 调用接口
    runAsync('http://xxxx:api', 'POST', blob)
        .then(result => {
            return result;
        })
        .then(function (result) {
            console.log('🚀 >result',result);
            //拿到res 处理逻辑
        });
  1. 后端接受blob,并转为base64格式
package service
import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
)

type RequestBody struct {
	Img string `json:"img"`
}
// FaceRecognition
// @summary 人脸识别
// @Description 人脸识别
// @param img formData string false "用户名"
// @Tags face
// @Success 200 {string} json{"code":200,"message":"data"}
// @Router /face/recognition [post]
func FaceRecognition(c *gin.Context) {
	// 解析请求体
	imageData, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"message": "Invalid image data",
		})
		return
	}
	// 解码 base64 格式图片数据(二进制转base64)
	base64Image := base64.StdEncoding.EncodeToString(imageData)
	// 本地测试可以使用GetFileContentAsBase64 方法
	// base64Image := GetFileContentAsBase64("/Users/meijuntao/Pictures/csdn封面/vscode.jpeg")
	reader := map[string]interface{}{
		"image":      base64Image,
		"image_type": "BASE64",
		"face_field": "beauty,age,expression,gender",
	}
	// 人脸识别...
}

/**
 * 获取文件base64编码
 * @param string  path 文件路径
 * @return string base64编码信息,不带文件头
 */
func GetFileContentAsBase64(path string) string {
	srcByte, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	return base64.StdEncoding.EncodeToString(srcByte)
}

调用百度人脸识别接口

百度人脸识别文档

  1. 到百度开放平台注册账号,实名认证
  2. 到引用列表创建一个应用,选择你要的服务
  3. 穿件好之后就可以拿到API_KEY、SECRET_KEY在这里插入图片描述
  4. 调用接口可以通过API_KEY、SECRET_KEY,调用GetAccessToken方法先获取到ACCESS_TOLEN,也可以获取到后直接保存下来,不用每次都调用
package service

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
)

type RequestBody struct {
	Img string `json:"img"`
}

const API_KEY = "you API_KEY"
const SECRET_KEY = "you SECRET_KEY"
const ACCESS_TOLEN = "you ACCESS_TOLEN"

// FaceRecognition
// @summary 人脸识别
// @Description 人脸识别
// @param img formData string false "用户名"
// @Tags face
// @Success 200 {string} json{"code":200,"message":"data"}
// @Router /face/recognition [post]
func FaceRecognition(c *gin.Context) {
	url := "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=" + ACCESS_TOLEN
	// 解析请求体
	imageData, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
			"message": "Invalid image data",
		})
		return
	}
	// 解码 base64 格式图片数据(二进制转base64)
	base64Image := base64.StdEncoding.EncodeToString(imageData)

	// imgs := GetFileContentAsBase64("/Users/meijuntao/Pictures/csdn封面/vscode.jpeg")
	reader := map[string]interface{}{
		"image":      base64Image,
		"image_type": "BASE64",
		"face_field": "beauty,age,expression,gender",
	}
	// 将 map 转换为 JSON 格式
	jsonStr, err := json.Marshal(reader)
	if err != nil {
		fmt.Println("Failed to marshal:", err)
		return
	}
	payload := strings.NewReader(string(jsonStr))
	client := &http.Client{}
	req, err := http.NewRequest("POST", url, payload)

	if err != nil {
		fmt.Println(err)
		return
	}
	req.Header.Add("Content-Type", "application/json")

	res, err := client.Do(req)
	if err != nil {
		fmt.Println(err)
		return
	}
	defer res.Body.Close()

	body, err := ioutil.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		return
	}

	var result map[string]interface{}
	err = json.Unmarshal(body, &result)
	if err != nil {
		return
	}
	c.JSON(200, gin.H{
		"msg":  "success",
		"data": result["result"],
	})
}

/**
 * 获取文件base64编码
 * @param string  path 文件路径
 * @return string base64编码信息,不带文件头
 */
func GetFileContentAsBase64(path string) string {
	srcByte, err := ioutil.ReadFile(path)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	return base64.StdEncoding.EncodeToString(srcByte)
}

/**
 * 使用 AK,SK 生成鉴权签名(Access Token)
 * @return string 鉴权签名信息(Access Token)
 */
func GetAccessToken() string {
	url := "https://aip.baidubce.com/oauth/2.0/token"
	postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", API_KEY, SECRET_KEY)
	resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(postData))
	if err != nil {
		fmt.Println(err)
		return ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	accessTokenObj := map[string]string{}
	json.Unmarshal([]byte(body), &accessTokenObj)
	return accessTokenObj["access_token"]
}

拿到识别结果处理逻辑

    runAsync('http://xxxx:api', 'POST', blob)
        .then(result => {
            return result;
        })
        .then(function (result) {
            console.log('🚀 >result',result);
            //拿到res 处理逻辑
                        if (result.data) {
                const resFace = result.data['face_list'][0];
                const expression = {
                    none: '无表情',
                    smile: '微笑',
                    laugh: '大笑'
                };
                const conNode = `<div class="m_item m_item_title">检测到 <span class="text_gradient face_num">${
                    result.data.face_num
                }</span> 张人脸</div>
                    <div class="m_item">
                        <span>性别:</span>
                        <span class="text_gradient m_female">${resFace.gender.type}</span>
                    </div>
                    <div class="m_item">
                        <span>年龄:</span>
                        <span class="text_gradient m_age">${resFace.age}</span>
                    </div>
                    <div class="m_item">
                        <span>颜值:</span>
                        <span class="text_gradient">${resFace.beauty}</span>
                    </div>
                    <div class="m_item">
                        <span>表情:</span>
                        <span class="text_gradient m_expression">${expression[resFace.expression.type]}</span>
                    </div>`;
                document.querySelector('.m_wrap').style.height = '230px';
                // var conDom = document.createElement('div');
                // conDom.innerHTML = conNode;
                // document.querySelector('.m_content').appendChild(conDom);
                document.querySelector('.m_item_wrap').innerHTML = conNode;
                document.querySelector('.m_discern').innerHTML = '检测完成';
                if (parseInt(resFace.beauty) >= 50) {
                    isBeauty = true;
                    // 关注
                    var follow = document.querySelector(
                        "div[data-e2e='feed-active-video'] div[data-e2e='feed-follow-icon'] div div"
                    );
                    resFace.gender.type === 'female' && follow?.click();
                    var like = document.querySelector(
                        "div[data-e2e='feed-active-video'] div[data-e2e-state='video-player-no-digged'] div"
                    );
                    like?.click();
                } else if (checkNum <= 3) {
                    checkNum++;
                    console.log('🚀 > 第', checkNum, '次检测');
                    timeoutInit(2000);
                } else if (checkNum > 3) {
                    // 多次检测没有就跳过
                    console.log('🚀 > 多次检测颜值未达标,跳过', checkNum);
                    document.querySelector('.xgplayer-playswitch-next').click();
                }
            } else {
                if (checkNum <= 3) {
                    checkNum++;
                    timeoutInit(2000);
                } else {
                    // 多次检测没有就跳过
                    console.log('🚀 > 多次检测没有就跳过', checkNum);
                    document.querySelector('.xgplayer-playswitch-next').click();
                }
            }
        });

效果展示

视频演示

github源码

问题记录

  1. tampermonkey新增脚本不生效
    • 在这里插入图片描述
  2. 请求接口跨域:使用GM_xmlhttpRequest
  3. 在本地测试video截图保存一直存在跨域,设置了video 标签中属性 crossorigin=“anonymous”,也没用,研究了好久,突然发现,我的脚本就是在目标域名下触发,压根不存在跨域,浪费了半天时间。。。
  4. canvas.drawImage获取到的图片不清晰,这是浏览器的像素比devicePixelRatio有关,可以先获取到ratio,调用drawImage是宽高乘ratio,当然ratio太高也会影响性能,导致图片太大,可根据项目适当调整
  5. 截图后生成的base64没法直接通过application/json或者application/x-www-form-urlencoded传,后端接受不到参数,猜测是太大超过限制了,改为将base64先转为blob格式,在通过application/octet-stream二进制流的形式,后端接收到后再将blob转为base64

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

元宇宙医生虚拟形象提高远程医疗服务质量

与现实中不同&#xff0c;3D虚拟形象是由个人在数字空间中自由选择并进行扮演的。这种3D虚拟形象在元宇宙中的重要性越来越突出。 在元宇宙虚拟空间中&#xff0c;用户借助元宇宙3D虚拟形象就能与其他用户互动、交流并获得真实的沉浸式体验&#xff0c;因此能广泛融入各种生活、…

「解析」牛客网-华为机考企业真题 41-60

又是一年春招时&#xff0c;有幸收到华为自动驾驶算法岗&#xff0c;之前刷题不多&#xff0c;在此汇总下牛客网的真题&#xff0c;主要采用Python编写&#xff0c;个人觉得语言只是实现工具而已&#xff0c;并不是很关键&#xff0c;Python简洁易懂&#xff0c;更加适合算法工…

基于EB工具的TC3xx_MCAL配置开发06_PWM模块配置

目录 1.概述2. EB配置2.1 PWM->General2.2 PWM->Channel2.2.1 PWMChannel配置2.2.2 PwmChannelClass配置2.2.3 GTM通道选取2.3 MCU关联配置2.4 Port关联配置1.概述 本篇开始我们基于EB Tresos工具对英飞凌TC3xx系列MCU的MCAL开发进行介绍,结合项目经验对各MCAL外设的开…

Docker:关于 Dockerfile 编写优化的一些笔记整理

写在前面 分享一些 Dickerfile 构建镜像优化方式的笔记理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#xff0c;全心全意&#xff0c;永不停息。所有其它的路都是不完整的&#xff0c;是人的逃避方式&#…

【React全家桶】Flux与Redux

&#x1f39e;️&#x1f39e;️&#x1f39e;️ 博主主页&#xff1a; 糖 &#xff0d;O&#xff0d; &#x1f449;&#x1f449;&#x1f449; react专栏&#xff1a;react全家桶 &#x1f339;&#x1f339;&#x1f339;希望各位博主多多支持&#xff01;&#xff01;&a…

javaScript扫雷

文章目录一、准备工作1.图片2.html2.css3.js二、初始化数据1. 配置文件2.工具文件3.逻辑文件1.main函数2.init函数1.随机生成雷2.css添加三、完整代码1.html2.js3.css一、准备工作 1.图片 需要找三张图片 旗子的图片 炸弹的图片 爆炸的图片 2.html html文件夹新建一个html文…

区块链基本原理

区块链的起源 创始者介绍 姓名&#xff1a;中本聪&#xff08;英语&#xff1a;SatoshiNakamoto&#xff09;&#xff0c;自称日裔美国人&#xff0c;日本媒体常译为中本哲史&#xff0c;此名是比特币协议及其相关软件Bitcoin-Qt的创造者&#xff0c;但真实身份未知。 中本聪于…

Chapter9.1:线性系统状态空间基础(上)

该系列博客主要讲述Matlab软件在自动控制方面的应用&#xff0c;如无自动控制理论基础&#xff0c;请先学习自动控制系列博文&#xff0c;该系列博客不再详细讲解自动控制理论知识。 自动控制理论基础相关链接&#xff1a;https://blog.csdn.net/qq_39032096/category_10287468…

解决Visual Studio设置C++标准 但是_cplusplus始终为199711

目录场景复现Visual Studio官方说明C标准对应表解决方案方法一 恢复__cplusplus宏方法二 使用_MSVC_LANG宏场景复现 我在VS2022偶然的一次测试C标准开发环境&#xff0c;发现无论我怎么修改C语言标准&#xff0c;输出的__cplusplus宏总是199711。 Visual Studio官方说明 链…

【C++】vector模拟实现及其应用

文章目录vector的介绍vector的使用及其实现vector的定义vector iterator 的使用vector空间增长问题vector的增删查改vector的介绍 vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素…

开源 Swallow 代码审计系统体验

最近在哔哩哔哩看 到Swallow 代码审计系统的宣传,发现功能比较适合我目前的工作需要,安装使用了一下,简单做了一个笔记,分享给有需要的朋友. 底层架构为蜻蜓编排系统,墨菲SCA,fortify,SemGrep,hema 项目地址:https://github.com/StarCrossPortal/swallow 安装与使用视频教程:ht…

hexo 搭建个人博客记录

看B站的程序羊的关于搭建hexo博客的方法自己搭了一个博客&#xff0c;链接是 手把手教你从0开始搭建自己的个人博客 |无坑版视频教程| hexo 下面就视频所讲做做笔记&#xff0c;以后可以回来查看&#xff0c;推荐小伙伴想搭建hexo博客的可以去看看这个视频。 1. 安装Node.js…

react项目路由组件懒加载和路由传值方式

项目实战 使用useRoutes配置路由&#xff0c;结合插槽配置用户登录检测。 用户登录成功进入login 直接系统主界面 路由模块抽离 整体代码外移 { path: "/admin", element: ( <Author name"admin"> <Index /> </Author> ), }, { path:…

「SAP ABAP」OPEN SQL(七)【GROUP BY | HAVING | ORDER BY】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后端的开发语言A…

基于matlab已知地球两点坐标求取距离和方位角函数distance

一、语法1.语法1[arclen,az] distance(lat1,lon1,lat2,lon2)&#xff1b;R6371.393; % 地球半径&#xff0c;单位&#xff1a;km地点1&#xff08;维度lat1&#xff0c;经度lon1&#xff09;&#xff0c;地点2&#xff08;维度lat2&#xff0c;经度lon2&#xff09;假设地点1和…

001 鸿蒙系统环境搭建及运行hello world

1 下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&#xff0c;首先需要完成开发工具DevEco Studio的下载与安装以及环境配置。 进入DevEco Studio下载官网&#xff0c;单击“立即下载”进入下载页面。 DevEco Studio提供了Windows版本…

PCIe基础

PCIe基础 PCI Express&#xff0c;简称PCI-E&#xff0c;官方简称PCIe&#xff0c;是计算机总线的一个重要分支&#xff0c;它沿用既有的PCI编程概念及信号标准&#xff0c;并且构建了更加高速的串行通信系统标准。目前这一标准由PCI-SIG组织制定和维护。 拓扑 配置空间 在 P…

【Python】plt.title()函数

plt.title() 是 matplotlib 库中用于设置图形标题的函数。 一、基本语法如下 plt.title(label, fontdictNone, locNone, padNone, **kwargs)其中&#xff1a; label 是要设置的标题文本&#xff0c;可以是字符串类型或者是数学表达式。fontdict 是一个可选的参数&#xff0c…

QT 基于AES加解密的使用,解析java端发来的密文

背景 java端往ukey中写授权信息&#xff0c;C端从ukey中读取授权信息。 java端写入的授权信息是加密的&#xff0c;并且要可逆。 因为java端采用的是AES加密的&#xff0c;所以我(C端)也只好采用对等形式搞定了。 使用的库 开发环境&#xff1a;Win10 Qt5.13 QT中AES加解密…

uniapp项目打包apk相关(androidStudio,Hbuildx,dCloud)

1、先注册和登陆dCloud平台&#xff0c;管理应用信息。 需要准备的参数(3个) APP_ID&#xff08;如&#xff1a;__UNI__123ABCD&#xff09; 包名&#xff08;如&#xff1a;com.hx.mhoa&#xff09; 应用签名&#xff08;应用sha1&#xff0c;应用md5&#xff0c;应用sha256&…