如何利用大模型打造一个自己的视觉AI对话机器人

        一直在看cpm2.6的宣传视频,里面有一段就是拿着手机边走边提问跟模型互动的场景。其实我理解就是抽帧。所以就尝试着在电脑端弄了个demo来尝试下,我这里没用到抽帧,而是定式任务1秒一传,结果效果还可以~先上代码。

        前端:vue2

        后端:python

       前端首先定义video控件跟一个隐藏的画布,另外就是俩按钮。提一嘴,画布非常重要,因为video里面有个方法,mediaRecorder.ondataavailable,这个东西获取传递流的时候不好使!,所以用画布来替代往后台传输时候的主控件了

<Button v-if="os" @click="open" type="success">打开摄像头</Button>
<Button v-else @click="stop" type="warning">关闭摄像头</Button>
<video ref="video" :width="vWidth" :height="vHeight" autoPlay></video>
<canvas ref="canvas" style="display: none;"></canvas>

接下来是定义了几个变量,interval,intervalflag,因为前面用了画布,所以这块就不能用video提供的方法自动轮询了,所以用了定时任务来调后台。intervalflag 是用来暂停往后台传输的一个标记,因为跟模型互动的时候 如果一直传,可能会让模型解析的时候跑偏~~

data() {
    return {
        interval:null,
        intervalflag:false
    };
  },

然后是具体的前端实现:这里面 formData.append("video", blob, 'image.jpg');这句话 还有 canvas.toBlob((blob) => {xxx}, 'image/jpeg', 0.9); 这两块比较重要,注意下,要不后台解析的时候总是识别到emptyImg~~很奇怪~

//调用摄像头
open() {
        this.$nextTick(() => {
          const _this = this;
          this.os = false;
          if (navigator.mediaDevices === undefined) {
            navigator.mediaDevices= {}
          }
          if (navigator.mediaDevices.getUserMedia === undefined) {
            navigator.mediaDevices.getUserMedia = function (constraints) {
              let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.getUserMedia;
              if (!getUserMedia) {
                return Promise.reject(new Error('getUserMedia is not implemented in this browser'))
              }
              return new Promise(function (resolve, reject) {
                getUserMedia.call(navigator, constraints, resolve, reject)
              })
            }
          }
          navigator.mediaDevices.getUserMedia({video:true}).then(function (stream) {
            if ('srcObject' in _this.$refs.video) {
              _this.$refs.video.srcObject = stream
            } else {
              _this.$refs.video.src = window.URL.createObjectURL(stream)
            }

            const canvas = _this.$refs.canvas;
            const context = canvas.getContext('2d');

            _this.$refs.video.onloadedmetadata = function (e) {
            _this.$refs.video.play();

            _this.interval = setInterval(() => {
              if(_this.intervalflag == true){
                return
              }else{
                context.drawImage(_this.$refs.video, 0, 0, canvas.width, canvas.height);
                canvas.toBlob((blob) => {
                  if (blob) {
                    const formData = new FormData();
                    formData.append("video", blob, 'image.jpg');

                    fetch('url', {
                      method: "POST",
                      body: formData,
                    })
                    .then(response => response.text())
                    .then(data => {
                      console.log(data); // 打印后端响应
                    })
                    .catch(error => {
                      console.error('Error:', error);
                    });
                  }
                }, 'image/jpeg', 0.9); // 使用 JPEG 格式
              }
            }, 1000); // 每秒抽取一帧
            }
          }).catch(err => {
            console.log(err)
            this.$Message({
              message: '没有开启摄像头权限或浏览器版本不兼容',
              type: 'warning'
            });
          });
        });
      },

然后是关闭摄像头

//关闭摄像头
stop() {
        this.$refs.video.srcObject.getTracks()[0].stop();
        this.os = true;
        if (this.interval != null) {
          clearInterval(this.interval);
        }
      },

还有一个发送互动消息的方法(页面我之前的文章里有写~可以翻一翻),我这里测试机的相应时间比较长就放了超长的timeout~~~

handleSend() {
        
				//如果消息不为空
				if(this.chatMsg != ""){
          this.readloading = true;
          let that = this;

          if(this.os == false){
            if (this.interval != null) {
              this.intervalflag = true;
            }
          }
          // 使用axios发送POST请求到后端接口
          axios.post('url', 
            qs.stringify({
              q:this.chatMsg
            }),
            {
              timeout: 999999999
            }
          )
          .then(response => {
            if (that.interval != null) {
              that.intervalflag = false;
            }
            that.chatMsg = '';
            that.text = response.data.message;
            this.readloading = false;
          })
          .catch(error => {
            console.error(error);
            this.readloading = false;
          });
				}else {
					this.senddis = false;
					this.$Message.info('请输入提问内容!');
          this.readloading = false;
				}
			},

接下来是后台python的代码

这里我设置了个20长的队列,因为Minicpm这个模型里的多图模式有上限,另外就是太多了也影响速度~这里面的路径大家自己改下吧~

from flask import Flask, stream_with_context, request, Response ,jsonify
from time import sleep
import datetime
import os
import uuid
import schedule
import threading
from chat import img2base64
import base64
import io
from PIL import Image
import torch
from transformers import AutoModel, AutoTokenizer
import json
from decord import VideoReader, cpu

import cv2
import numpy as np
from screeninfo import get_monitors
from collections import deque

model = AutoModel.from_pretrained('/home/_26/', trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained('/home/_26/', trust_remote_code=True)
model.eval()

queue = deque(maxlen=20)

@app.route('/videostream', methods=['POST'])
def videostream():
    if 'video' not in request.files:
        return 'No file part', 400
    video_file = request.files['video']
    if video_file.filename == '':
        return 'No selected file', 400
    # 读取视频流并进行处理
    nparr = np.frombuffer(video_file.read(), np.uint8)
    frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
    # 进行抽帧处理
    # 例如保存或进一步处理
    now = datetime.datetime.now()
    timestamp = now.timestamp()
    filename = ('{}'+'111.jpg').format(timestamp)
    cv2.imwrite('/home/1', filename), frame)
    image = Image.open(io.BytesIO(base64.b64decode(img2base64('/home/1/' + filename)))).convert('RGB')
    queue.append(image)
    if os.path.exists(('/home/1/' + filename)):
        os.remove(('/home/1' + filename))
        print(f"文件 {('/home/1/' + filename)} 删除成功!")
    else:
        print(f"文件 {('/home/1/' + filename)} 不存在。")
    
    return jsonify({'message': {"key":queuec , "value" : "解析完成,可继续提问"}, 'success': True}), 200



@app.route('/askvideostream', methods=['POST'])
def ask_videostream():
    question = request.form.get("q")
    msgs = [{'role': 'user', 'content': list(queue) + [question]}]
    res = model.chat(
        image=None,
        msgs=msgs,
        tokenizer=tokenizer
    )
    return jsonify({'message': res, 'success': True}), 200

这里面可以根据自己的需求做很多扩展~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值