Spring Boot + Vue 接入腾讯云人脸识别API(SDK版本3.1.830)

news2025/2/26 11:15:49

一、需求分析

这次是基于一个Spring Boot +Vue的在线考试系统进行二次开发,添加人脸识别功能以防止学生替考。其他有对应场景的也可按需接入API,方法大同小异。

主要有以下两个步骤:

  • 人脸录入:将某个角色(如学生)的人脸绑定其唯一属性(如学号)录入人脸库
  • 人脸搜索(人脸识别):传递当前用户唯一属性(如学号)+ 摄像头图像给后台,在人脸库中进行匹配

二、腾讯云官网开通人脸服务

  1. 注册并进入官网:https://cloud.tencent.com/

  2. 主页搜索人脸识别,并进入产品控制台开通服务

  3. 创建人员库(注意人员库ID,后续会使用)

  4. 阅读查看官网API文档

三、后端开发

依赖(腾讯云核心SDK)
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java</artifactId>
            <version>3.1.830</version>
        </dependency>
配置
tencent:
  face:
    secret-id: xxx
    secret-key: xxx
    region: ap-guangzhou
    group-id: exam_stu_face
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.iai.v20200303.IaiClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TencentCloudConfig {

    @Value("${tencent.face.secret-id}")
    private String secretId;

    @Value("${tencent.face.secret-key}")
    private String secretKey;

    @Value("${tencent.face.region}")
    private String region;

    @Value("${tencent.face.group-id}")
    private String groupId;

    @Bean
    public Credential credential() {
        return new Credential(secretId, secretKey);
    }

    @Bean
    public IaiClient iaiClient() {
        return new IaiClient(credential(), region);
    }

    public String getGroupId() {
        return groupId;
    }
}
控制器
import com.mindskip.xzs.service.tencentcloud.FaceService;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/face")
public class FaceController {

    @Autowired
    private FaceService faceService;

    /**
     * 人脸注册接口
     *
     * @param studentId
     * @param file
     * @return
     */
    @PostMapping(value = "/register", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<Map<String, Object>> handleRegistration(
            @RequestParam("studentId") String studentId,
            @RequestParam("file") MultipartFile file) {

        Map<String, Object> responseBody = new HashMap<>();

        try {
            faceService.registerFace(studentId, file);
            log.info("人脸录入成功");
            responseBody.put("code", 200);
            responseBody.put("message", "人脸录入成功");
            return ResponseEntity.ok().body(responseBody);
        } catch (TencentCloudSDKException e) {
            log.error("Tencent Cloud SDK Exception: ", e);
            String errorMsg = parseTencentError(e);
            responseBody.put("code", 500);
            responseBody.put("message", errorMsg);
            return ResponseEntity.status(500).body(responseBody);
        } catch (IllegalArgumentException e) {
            log.error("参数错误:{}", e.getMessage());
            responseBody.put("code", 400);
            responseBody.put("message", e.getMessage());
            return ResponseEntity.badRequest().body(responseBody);
        } catch (Exception e) {
            log.error("系统异常:", e);
            responseBody.put("code", 500);
            responseBody.put("message", "系统异常");
            return ResponseEntity.status(500).body(responseBody);
        }
    }

    /**
     * 人脸验证接口
     *
     * @param studentId
     * @param file
     * @return
     */
    @PostMapping(value = "/verify", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<Map<String, Object>> handleVerification(
            @RequestParam("studentId") String studentId,
            @RequestParam("file") MultipartFile file) {

        Map<String, Object> responseBody = new HashMap<>();

        try {
            boolean isValid = faceService.verifyFace(studentId, file);
            log.info("人脸验证结果:{}", isValid);
            responseBody.put("code", 200);
            responseBody.put("success", isValid);
            responseBody.put("message", isValid ? "人脸验证成功" : "人脸验证失败");
            return ResponseEntity.ok().body(responseBody);
        } catch (TencentCloudSDKException e) {
            log.error("Tencent Cloud SDK Exception: ", e);
            String errorMsg = parseTencentError(e);
            responseBody.put("code", 500);
            responseBody.put("success", false);
            responseBody.put("message", errorMsg);
            return ResponseEntity.status(500).body(responseBody);
        } catch (IllegalArgumentException e) {
            log.error("参数错误:{}", e.getMessage());
            responseBody.put("code", 400);
            responseBody.put("success", false);
            responseBody.put("message", e.getMessage());
            return ResponseEntity.badRequest().body(responseBody);
        } catch (Exception e) {
            log.error("系统异常:", e);
            responseBody.put("code", 500);
            responseBody.put("success", false);
            responseBody.put("message", "系统异常");
            return ResponseEntity.status(500).body(responseBody);
        }
    }

    // 补充错误码解析
    private String parseTencentError(TencentCloudSDKException e) {
        // 具体错误码处理逻辑
        if (e.getMessage().contains("InvalidParameterValue.PersonIdAlreadyExist")) {
            return "该考生已存在人脸信息";
        }
        if (e.getMessage().contains("InvalidParameterValue.FaceNotExist")) {
            return "人脸信息不存在";
        }
        if (e.getMessage().contains("InvalidParameterValue.NoFaceInPhoto")) {
            return "照片中未检测到人脸";
        }
        return "腾讯云服务异常:" + e.getMessage();
    }
}
服务层
import com.mindskip.xzs.configuration.tencentcloud.TencentCloudConfig;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.iai.v20200303.IaiClient;
import com.tencentcloudapi.iai.v20200303.models.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Base64;

@Service
public class FaceService {

    @Autowired
    private IaiClient iaiClient;

    @Autowired
    private TencentCloudConfig config;

    /**
     * 录入人脸
     *
     * @param studentId
     * @param imageFile
     * @throws IOException
     * @throws TencentCloudSDKException
     */
    public void registerFace(String studentId, MultipartFile imageFile)
            throws IOException, TencentCloudSDKException {

        // 1. 人脸检测
        DetectFaceRequest detectRequest = new DetectFaceRequest();
        detectRequest.setImage(base64Encode(imageFile.getBytes()));
        DetectFaceResponse detectResponse = iaiClient.DetectFace(detectRequest);

        // 验证检测结果
        if (detectResponse.getFaceInfos() == null) {
            throw new IllegalArgumentException("照片中必须包含且仅包含一张人脸");
        }

        // 2. 创建人员并添加人脸
        CreatePersonRequest createRequest = new CreatePersonRequest();
        createRequest.setGroupId(config.getGroupId());
        createRequest.setPersonId(studentId);
        createRequest.setPersonName("考生_" + studentId);
        createRequest.setImage(base64Encode(imageFile.getBytes()));

        iaiClient.CreatePerson(createRequest);
    }

    /**
     * 人脸验证
     *
     * @param studentId
     * @param imageFile
     * @return
     * @throws IOException
     * @throws TencentCloudSDKException
     */
    public boolean verifyFace(String studentId, MultipartFile imageFile)
            throws IOException, TencentCloudSDKException {

        // 1. 人脸检测
        DetectFaceRequest detectRequest = new DetectFaceRequest();
        detectRequest.setImage(base64Encode(imageFile.getBytes()));
        DetectFaceResponse detectResponse = iaiClient.DetectFace(detectRequest);

        // 验证检测结果
        if (detectResponse.getFaceInfos() == null) {
            throw new IllegalArgumentException("照片中必须包含且仅包含一张人脸");
        }

        // 2. 人脸搜索
        SearchPersonsRequest searchRequest = new SearchPersonsRequest();
        searchRequest.setGroupIds(new String[]{config.getGroupId()});
        searchRequest.setImage(base64Encode(imageFile.getBytes()));
        searchRequest.setMaxPersonNum(1L); // 最多返回1个结果

        SearchPersonsResponse searchResponse = iaiClient.SearchPersons(searchRequest);

        // 3. 验证结果
        if (searchResponse.getResults() != null && searchResponse.getResults().length > 0) {
            Result result = searchResponse.getResults()[0];
            if (result.getCandidates() != null && result.getCandidates().length > 0) {
                Candidate candidate = result.getCandidates()[0];
                // 判断匹配的用户ID且置信度大于80(阈值可根据需求调整)
                return studentId.equals(candidate.getPersonId()) && candidate.getScore() > 80;
            }
        }
        return false;
    }

    private String base64Encode(byte[] bytes) {
        return Base64.getEncoder().encodeToString(bytes);
    }
}

四、前端开发

人脸录入
人脸录入弹窗组件
<template>
  <el-dialog
    title="人脸录入"
    :visible.sync="visible"
    width="800px"
    @close="handleClose">
    <div class="capture-container">
      <div class="capture-layout">
        <!-- 左侧输入区域 -->
        <div class="input-section">
          <!-- 摄像头预览 -->
          <div v-show="captureMode === 'camera'" class="camera-preview">
            <video ref="video" autoplay class="video"></video>
            <canvas ref="canvas" class="canvas" style="display: none;"></canvas>
            <el-button
              type="primary"
              @click="capture"
              class="capture-btn">
              拍照
            </el-button>
          </div>

          <!-- 图片上传 -->
          <el-upload
            v-show="captureMode === 'upload'"
            class="avatar-uploader"
            action="#"
            :show-file-list="false"
            :before-upload="beforeUpload"
            :http-request="handleUpload">
            <img v-if="imageUrl" :src="imageUrl" class="avatar">
            <div v-else class="uploader-default">
              <i class="el-icon-plus avatar-uploader-icon"></i>
              <div class="upload-tip">上传清晰正面照(支持JPG/PNG)</div>
            </div>
          </el-upload>
        </div>

        <!-- 右侧预览区域 -->
        <div class="preview-section">
          <div class="preview-title">照片预览</div>
          <div class="preview-content">
            <img v-if="imageUrl" :src="imageUrl" class="preview-image">
            <div v-else class="preview-placeholder">
              <i class="el-icon-picture-outline"></i>
              <p>预览区域</p>
            </div>
          </div>
        </div>
      </div>

      <!-- 模式切换 -->
      <div class="mode-switch">
        <el-radio-group v-model="captureMode">
          <el-radio-button label="camera">摄像头拍摄</el-radio-button>
          <el-radio-button label="upload">图片上传</el-radio-button>
        </el-radio-group>
      </div>
    </div>

    <div slot="footer">
      <el-button @click="visible = false">取消</el-button>
      <el-button
        type="primary"
        :disabled="!imageData"
        @click="submitFace">
        确认提交
      </el-button>
    </div>
  </el-dialog>
</template>

<script>
import { registerCamera, stopCamera } from '@/utils/camera'
import { compressImage } from '@/utils/image'
import { post } from '@/utils/request'

export default {
  data () {
    return {
      visible: false,
      captureMode: 'camera',
      imageUrl: '',
      imageData: null,
      studentId: null,
      mediaStream: null
    }
  },
  methods: {
    open (studentId) {
      this.studentId = studentId
      this.visible = true
      this.$nextTick(() => {
        if (this.captureMode === 'camera') {
          this.initCamera()
        }
      })
    },
    async initCamera () {
      try {
        this.mediaStream = await registerCamera(this.$refs.video)
      } catch (error) {
        this.$message.error('摄像头访问失败,请检查权限')
        this.captureMode = 'upload'
      }
    },
    capture () {
      const video = this.$refs.video
      const canvas = this.$refs.canvas
      canvas.width = video.videoWidth
      canvas.height = video.videoHeight
      canvas.getContext('2d').drawImage(video, 0, 0)

      canvas.toBlob(async blob => {
        this.imageData = await compressImage(blob)
        this.imageUrl = URL.createObjectURL(this.imageData)
      }, 'image/jpeg', 0.8)
    },
    async beforeUpload (file) {
      const isImage = ['image/jpeg', 'image/png'].includes(file.type)
      if (!isImage) {
        this.$message.error('只能上传JPG/PNG格式图片')
        return false
      }
      return true
    },
    async handleUpload ({ file }) {
      try {
        this.imageData = await compressImage(file)
        this.imageUrl = URL.createObjectURL(this.imageData)
      } catch (error) {
        this.$message.error('图片处理失败')
      }
    },
    async submitFace () {
      try {
        const formData = new FormData()
        formData.append('file', this.imageData)
        formData.append('studentId', this.studentId)
        console.log(this.studentId)
        console.log(formData)

        const res = await post('/api/face/register', formData)

        if (res.code === 200) {
          this.$message.success('人脸录入成功')
          this.visible = false
        } else {
          this.$message.error(res.message || '录入失败')
        }
      } catch (error) {
        this.$message.error('请求失败,请稍后重试')
      }
    },
    handleClose () {
      if (this.mediaStream) {
        stopCamera(this.mediaStream)
      }
      this.imageUrl = ''
      this.imageData = null
    }
  },
  watch: {
    captureMode (newVal) {
      if (newVal === 'camera') {
        this.initCamera()
      } else if (this.mediaStream) {
        stopCamera(this.mediaStream)
        this.mediaStream = null
      }
    }
  }
}
</script>

<style scoped>
.capture-layout {
  display: flex;
  gap: 20px;
  margin-bottom: 20px;
}

.input-section,
.preview-section {
  flex: 1;
  min-width: 0;
}

.preview-section {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  padding: 10px;
}

.preview-title {
  color: #606266;
  font-size: 14px;
  margin-bottom: 10px;
  text-align: center;
}

.preview-content {
  height: 340px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.preview-image {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.preview-placeholder {
  text-align: center;
  color: #999;
}

.preview-placeholder i {
  font-size: 40px;
  margin-bottom: 10px;
}

.camera-preview {
  position: relative;
  height: 360px;
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
}

.video, .canvas {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.capture-btn {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
}

.avatar-uploader {
  height: 360px;
}

.avatar {
  max-width: 100%;
  max-height: 400px;
}

.uploader-default {
  text-align: center;
}

.upload-tip {
  margin-top: 10px;
  color: #999;
}

.mode-switch {
  margin-top: 20px;
  text-align: center;
}
</style>

摄像头访问/停止js

export const registerCamera = async (videoElement) => {
  const constraints = {
    video: {
      width: { ideal: 1280 },
      height: { ideal: 720 },
      facingMode: 'user'
    }
  }

  const stream = await navigator.mediaDevices.getUserMedia(constraints)
  videoElement.srcObject = stream
  await new Promise(resolve => videoElement.onloadedmetadata = resolve)
  return stream
}

export const stopCamera = (stream) => {
  stream.getTracks().forEach(track => track.stop())
}

图像压缩js

export const compressImage = (file, quality = 0.8) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = (e) => {
      const img = new Image()
      img.onload = () => {
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')

        // 限制最大尺寸
        const maxWidth = 1024
        const scale = maxWidth / img.width
        canvas.width = maxWidth
        canvas.height = img.height * scale

        ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
        canvas.toBlob(
          blob => resolve(new File([blob], file.name, { type: 'image/jpeg' })),
          'image/jpeg',
          quality
        )
      }
      img.src = e.target.result
    }
    reader.readAsDataURL(file)
  })
}

在自己需要添加人脸录入的页面引入弹窗组件FaceCaptureDialog即可,如:

<template>
  <div class="app-container">
    <!-- ... -->
    <!-- 呼出弹窗按钮 -->
    <el-button size="mini" type="success" @click="openFaceDialog(row)" class="link-left">录入人脸</el-button>
    <!-- ... -->
    <face-capture-dialog ref="faceDialog" />
  </div>
</template>

<script>
import FaceCaptureDialog from '@/components/face/FaceCaptureDialog'

  // ...
  // 点击事件(呼出人脸录入弹窗)
  // row.id -> 学生id,传递到弹窗组件
  methods: {
    openFaceDialog(row) {
      this.$refs.faceDialog.open(row.id)
    },
  // ...
</script>
人脸搜索
人脸搜索弹窗
<template>
  <el-dialog :title="title" :visible.sync="visible" width="400px" :close-on-click-modal="false"
             :close-on-press-escape="false" :show-close="false">
    <div v-if="loading" class="loading-container">
      <i class="el-icon-loading"></i>
      <span>人脸识别中...</span>
    </div>
    <div v-else>
      <video ref="video" width="300" height="200" autoplay playsinline></video>
      <canvas ref="canvas" width="300" height="200" style="display: none;"></canvas>
      <el-button type="primary" @click="capture">点击拍照</el-button>
    </div>
  </el-dialog>
</template>

<script>
import { post } from '@/utils/request'

export default {
  props: {
    studentId: {
      type: String,
      required: true
    }
  },
  data () {
    return {
      visible: false,
      loading: false,
      stream: null
    }
  },
  methods: {
    open () {
      this.visible = true
      this.initCamera()
    },
    close () {
      this.visible = false
      this.stopCamera()
    },
    async initCamera () {
      const constraints = { video: true }
      try {
        this.stream = await navigator.mediaDevices.getUserMedia(constraints)
        this.$refs.video.srcObject = this.stream
      } catch (error) {
        this.$message.error('无法访问摄像头,请检查权限设置')
      }
    },
    stopCamera () {
      if (this.stream) {
        this.stream.getTracks().forEach(track => track.stop())
        this.stream = null
      }
    },
    async capture () {
      this.stopCamera()
      const canvas = this.$refs.canvas
      const video = this.$refs.video
      canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)
      const imgData = canvas.toDataURL('image/jpeg')
      const formData = new FormData()
      formData.append('studentId', this.studentId)
      formData.append('file', this.dataURLtoBlob(imgData))

      this.loading = true
      post(`/api/face/verify`, formData, {
        headers: { 'Content-Type': 'multipart/form-data' }
      }).then(response => {
        this.loading = false
        if (response.success) {
          this.$message.success('人脸验证成功!')
          this.$emit('verifySuccess')
        } else {
          this.$message.error(`人脸验证失败:${response.message}`)
          this.$emit('verifyError', response.message) // 触发 verifyError 事件
          this.initCamera() // 重新初始化摄像头
        }
        this.visible = false // 验证完成后关闭弹窗
      }).catch(error => {
        this.loading = false
        this.$message.error('人脸验证失败,请稍后重试')
        this.$emit('verifyError', error.message) // 触发 verifyError 事件
        this.initCamera() // 重新初始化摄像头
      })
    },
    dataURLtoBlob (dataurl) {
      const arr = dataurl.split(',')
      const mime = arr[0].match(/:(.*?);/)[1]
      const bstr = atob(arr[1])
      let n = bstr.length
      const u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new Blob([u8arr], { type: mime })
    }
  }
}
</script>

<style scoped>
/* 自定义样式 */
</style>

在需要的页面引入人脸搜索弹窗,目前的流程就是进入做题页面后弹窗识别考生,三次识别失败后强制退出(根据需要,可以考虑间隔多少时间再次人脸认证,注意后端权限校验):

<template>
  <div>
    <!-- ... -->
    <!-- 弹窗组件 -->
    <FaceVerifyDialog ref="faceVerifyDialog" :studentId="currentUserId" @verifySuccess="handleVerifySuccess"
                      @verifyError="handleVerifyError"/>
    <!-- ... -->
  </div>
</template>

<script>
  import FaceVerifyDialog from '@/components/face/FaceVerifyDialog.vue'

export default {
  components: { FaceVerifyDialog },
  data () {
    return {
      currentUserId: '', // 用于存储当前用户的 studentId
      // ...
      isFaceVerified: false // 是否完成人脸识别验证
    }
  },
  // ...
  mounted () {
    this.initFaceVerify() // 初始化人脸识别
  },
  // ...
  methods: {
    // ...
    initFaceVerify () {
      // 开题前验证
      this.$alert('开考前需要进行人脸识别验证', '人脸验证提示', {
        closeOnClickModal: false, // 禁用点击背景关闭
        closeOnPressEscape: false, // 禁用按下 ESC 关闭
        showClose: false, // 隐藏关闭按钮
        callback: () => {
          // 弹窗关闭后的回调
          this.$refs.faceVerifyDialog.open()
        }
      })
    },
    handleVerifySuccess () {
      this.isFaceVerified = true // 标记验证成功
      this.closeFaceVerifyDialog()
    },
    handleVerifyError (error) {
      // 验证失败,允许用户重试,超过 3 次失败强制退出
      this.verifiedCount++
      if (this.verifiedCount >= 3) {
        this.$message.warning('人脸识别失败次数超过限制,请联系管理员', '人脸验证失败')
        this.closeFaceVerifyDialog()
        this.logout() // 退出登录
      } else {
        this.$message.error(`人脸识别失败:${error},可以点击重新验证`)
      }
    },
    closeFaceVerifyDialog () {
      this.$refs.faceVerifyDialog.close()
    },
    logout () {
      // 登出
    }
  },
  // ...
</script>

测试

录入成功后可以再腾讯云 -> 人脸识别控制台 -> 人脸库 看到录入的人脸:

识别测试过程就不展示了 (`へ´*)ノ

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

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

相关文章

JAVA中包装类和泛型 通配符

目录 1. 包装类 1.1 基本数据类型和对应的包装类 1.2 装箱和封箱 1.3 自动自动装箱和封箱 2. 什么是泛型 3. 引出泛型 3.1 语法 4. 泛型类的使⽤ 4.1 语法 4.2 ⽰例 4.3 类型推导(Type Inference) 5 泛型的上界 5.1 语法 6. 通配符 6.1 通配符解决什么问题 6.2…

Qt TCP服务端和客户端程序

1、服务端程序 利用QtCreator新建QMainWindow或QWidget工程&#xff0c;绘制UI如下所示。 mainwindow.h代码如下&#xff1a; #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QTcpServer> #include <QTcpSocket> #include &l…

level2Day5

Makefile make是工程管理器 先写了1个f1.c里面写了一个函数 然后f2.c里面也写了一个函数 还有一个头节点 又写了一个makefile的函数 输入make编译&#xff0c;但是我没装make需要装一下。 sudo apt install make 然后make&#xff0c; Makefile变量的使用 通过赋值&#xff…

minio作为K8S后端存储

docker部署minio mkdir -p /minio/datadocker run -d \-p 9000:9000 \-p 9001:9001 \--name minio \-v /minio/data:/data \-e "MINIO_ROOT_USERjbk" \-e "MINIO_ROOT_PASSWORDjbjbjb123" \quay.io/minio/minio server /data --console-address ":90…

redis小记

redis小记 下载redis sudo apt-get install redis-server redis基本命令 ubuntu16下的redis没有protected-mode属性&#xff0c;就算sudo启动&#xff0c;也不能往/var/spool/cron/crontabs写计划任务&#xff0c;感觉很安全 #连接到redis redis-cli -h 127.0.0.1 -p 6379 …

计算机视觉(opencv-python)入门之图像的读取,显示,与保存

在计算机视觉领域&#xff0c;Python的cv2库是一个不可或缺的工具&#xff0c;它提供了丰富的图像处理功能。作为OpenCV的Python接口&#xff0c;cv2使得图像处理的实现变得简单而高效。 示例图片 目录 opencv获取方式 图像基本知识 颜色空间 RGB HSV CV2常用图像处理方…

ActiveMQ之VirtualTopic

一句话总结&#xff1a; VirtualTopic是为了解决持久化模式下多消费端同时接收同一条消息的问题。 现实中多出现这样一个场景&#xff1a; 生产端产生了一笔订单&#xff0c;作为消息MessageOrder发了出去。 这笔订单既要入订单系统归档&#xff0c;又要入结算系统收款&#x…

UE5 Computer Shader学习笔记

首先这里是绑定.usf文件的路径&#xff0c;并声明是用声明着色器 上面就是对应的usf文件路径&#xff0c;在第一张图进行链接 Shader Frequency 的作用 Shader Frequency 是 Unreal Engine 中用于描述着色器类型和其执行阶段的分类。常见的 Shader Frequency 包括&#xff1a…

2.1部署logstash:9600

实验环境&#xff1a;关闭防火墙&#xff0c;完成java环境 yum -y install wget wget https://d6.injdk.cn/oraclejdk/8/jdk-8u341-linux-x64.rpm yum localinstall jdk-8u341-linux-x64.rpm -y java -version 1.安装logstash tar xf logstash-6.4.1.tar.gz -C /usr/local…

SQL笔记#集合运算

目录 一、表的加减法 1、什么是集合运算 2、表的加法——UNION 3、集合运算的注意事项 4、包含重复行的集合运算——ALL运算 5、选取表中公共部分——INTERSECT 6、记录的减法——EXCEPT 二、联结(以列为单位对表进行联结) 1、什么是联结(JOIN) 2、内联结——INSER…

多模态人物视频驱动技术回顾与业务应用

一种新的商品表现形态&#xff0c;内容几乎存在于手淘用户动线全流程&#xff0c;例如信息流种草内容、搜索消费决策内容、详情页种草内容等。通过低成本、高时效的AIGC内容生成能力&#xff0c;能够从供给端缓解内容生产成本高的问题&#xff0c;通过源源不断的低成本供给倒推…

多功能免费网络测速及问题诊断工具

​软件介绍 在日常网络使用中&#xff0c;网络问题常常难以即时察觉&#xff0c;很多时候&#xff0c;只有当视频卡顿、网页加载半天没反应&#xff0c;乃至无法连接部分服务时&#xff0c;我们才惊觉网络出状况了。 这里有一款免费工具&#xff0c;专为家庭、办公以及跨国网…

【算法设计与分析】(一)介绍算法与复杂度分析

【算法设计与分析】&#xff08;一&#xff09;介绍算法与复杂度分析 前言一、什么是算法&#xff1f;二、算法的抽象机制三、描述算法四、复杂度分析4.1 时间复杂度4.2 空间复杂度 前言 从搜索引擎的高效检索&#xff0c;到推荐系统的个性化推荐&#xff0c;再到人工智能领域…

HTML5特殊字符

HTML中常用的特殊符号一般都以“&”开头&#xff0c;以“;”结束。

使用python接入腾讯云DeepSeek

本文主要从提供SSE方式接入DeepSeek&#xff0c;并通过fastapi websocket对外提供接入方法。 参考文档&#xff1a; 腾讯云大模型&#xff1a;https://cloud.tencent.com/document/product/1759/109380 fastAPI官网&#xff1a;https://fastapi.tiangolo.com/ WebSocketManager…

无法打开数据库 CAUsers\Public\EPLAN(Data\翻译\Company name\Translate.mdb。

eplan生成更新列表后报错&#xff0c;报错内容如下&#xff1a; 无法打开数据库 CAUsers\Public\EPLAN(Data\翻译\Company name\Translate.mdb。针对 64 位版本的EPLAN平台需要使用64 位版本的Microsoft Office。 原因&#xff1a;eplan的列表更新需要64位的微软办公软件版本支…

将CUBE或3DL LUT转换为PNG图像

概述 在大部分情况下&#xff0c;LUT 文件通常为 CUBE 或 3DL 格式。但是我们在 OpenGL Shader 中使用的LUT&#xff0c;通常是图像格式的 LUT 文件。下面&#xff0c;我将教大家如何将这些文件转换为 PNG 图像格式。 条形LUT在线转换&#xff08;不是8x8网络&#xff09;&am…

C语言(13)------------>do-while循环

1.do-while循环的语法 我们知道C语言有三大结构&#xff0c;顺序、选择、循环。我们可以使用while循环、for循环、do-while循环实现循环结构。之前的博客中提及到了前两者的技术实现。可以参考&#xff1a; C语言&#xff08;11&#xff09;-------------&#xff1e;while循…

FS800DTU联动OneNET平台数据可视化View

目录 1 前言 2 环境搭建 2.1 硬件准备 2.2 软件环境 2.3 硬件连接 3 注册OneNET云平台并建立物模型 3.1 参数获取 3.2 连接OneNET 3.3上报数据 4 数据可视化View 4.1 用户信息获取 4.2 启用数据可视化View 4.3 创建项目 4.4 编辑项目 4.5 新增数据源 4.6 数据过滤器配置 4.6 项…

Linux 第三次脚本作业

源码编译安装httpd 2.4&#xff0c;提供系统服务管理脚本并测试&#xff08;建议两种方法实现&#xff09; 一、第一种方法 1、把 httpd-2.4.63.tar.gz 这个安装包上传到你的试验机上 2、 安装编译工具 (俺之前已经装好了&#xff09; 3、解压httpd包 4、解压后的httpd包的文…