请选择 进入手机版 | 继续访问电脑版
MSIPO技术圈 首页 IT技术 查看内容

学习flask技术并应用在跨模态视频检索系统

2023-07-13

学习flask技术并应用在跨模态视频检索系统

摘要

使用Flask技术在前端部署代码,通过http通信完成对双后端python端的AI服务调用,再返回给前端Top10视频片段的时间戳和置信度分数,以MVC模式绑定在前端的控件中进行可视化展示,显示Top10视频片段时间戳序列和封面图,点击即可跳转播放。

Flask技术简介

Flask是一个基于Python的轻量级Web应用框架,它提供了简单易用的API,可以快速地开发Web应用。Flask的核心特点是:

  • 微框架:Flask只提供了最基本的Web开发功能,如路由、请求处理、模板渲染等,其他功能如数据库、表单、用户认证等需要通过扩展来实现。
  • 灵活:Flask没有强制的项目结构或编码规范,开发者可以根据自己的需求和喜好来组织代码和配置选项。
  • 易扩展:Flask有很多第三方扩展,可以方便地增加各种功能,如数据库操作、表单验证、用户认证、文件上传、缓存、日志等。
  • RESTful:Flask支持RESTful风格的Web服务开发,可以通过不同的HTTP方法和URL来实现不同的资源操作。

具体内容

本文介绍了如何使用Flask技术在前端部署代码,实现一个跨模态视频检索系统。该系统的功能是:用户可以输入一段文本描述或者一张图片作为查询条件,系统会从一个视频库中检索出与之最相关的Top10视频片段,并在前端显示出来。用户可以点击视频片段的封面图或者时间戳来跳转到相应的位置播放。

该系统涉及到三个部分:前端、后端1和后端2。前端负责接收用户的输入,调用后端1和后端2的AI服务,展示检索结果。后端1负责处理文本查询,使用BERT模型将文本编码成向量,并与视频库中的文本向量进行相似度计算,返回Top10视频片段的ID和置信度分数。后端2负责处理图片查询,使用ResNet模型将图片编码成向量,并与视频库中的图片向量进行相似度计算,返回Top10视频片段的ID和置信度分数。视频库是一个预先处理好的数据集,包含了每个视频片段的文本描述、图片特征和时间戳信息。

前端工作

前端使用HTML、CSS和JavaScript来编写页面和交互逻辑。页面上有两个输入框,一个用于输入文本描述,一个用于上传图片。还有一个按钮用于提交查询。页面下方有一个表格用于显示检索结果。表格中每一行对应一个视频片段,包含了封面图、时间戳序列和置信度分数。用户可以点击封面图或者时间戳来跳转到相应的位置播放。

前端使用jQuery库来实现AJAX请求和响应处理。当用户提交查询时,前端会根据输入类型(文本或图片)来选择调用后端1或后端2的AI服务,并将输入内容作为参数传递。后端返回一个JSON格式的数据,包含了Top10视频片段的ID和置信度分数。前端根据ID从视频库中获取对应的封面图和时间戳信息,并将结果填充到表格中。

前后端实现代码

以下是前后端部分代码的示例,完整代码可以在这里查看。

前端代码

<html>
<head>
    <title>跨模态视频检索系统</title>
    <style>
        table, th, td {
            border: 1px solid black;
            border-collapse: collapse;
        }
        th, td {
            padding: 10px;
        }
    </style>
</head>
<body>
    <h1>跨模态视频检索系统</h1>
    <p>请输入一段文本描述或者上传一张图片作为查询条件,系统会从视频库中检索出与之最相关的Top10视频片段,并在下方显示出来。</p>
    <form id="query-form">
        <label for="text-input">文本描述:</label>
        <input type="text" id="text-input" name="text-input" placeholder="例如:一个人在海边跳舞">
        <br>
        <label for="image-input">图片:</label>
        <input type="file" id="image-input" name="image-input" accept="image/*">
        <br>
        <input type="button" id="submit-button" value="提交查询">
    </form>
    <table id="result-table">
        <thead>
            <tr>
                <th>封面图</th>
                <th>时间戳序列</th>
                <th>置信度分数</th>
            </tr>
        </thead>
        <tbody>
            <!-- 填充检索结果 -->
        </tbody>
    </table>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        // 定义后端1和后端2的AI服务地址
        var backend1_url = "http://localhost:5001/text_query";
        var backend2_url = "http://localhost:5002/image_query";
        // 定义视频库的地址
        var video_library_url = "http://localhost:5000/video_library";
        // 定义视频播放器的地址
        var video_player_url = "http://localhost:5000/video_player";

        // 当用户点击提交按钮时,执行以下函数
        $("#submit-button").click(function() {
            // 获取用户输入的文本或图片
            var text_input = $("#text-input").val();
            var image_input = $("#image-input").prop("files")[0];
            // 判断输入类型,并选择调用后端1或后端2的AI服务
            if (text_input) {
                // 如果输入是文本,使用GET方法发送请求,将文本作为参数传递
                $.get(backend1_url, {text: text_input}, function(data) {
                    // 处理返回的数据,调用显示结果的函数
                    display_results(data);
                });
            } else if (image_input) {
                // 如果输入是图片,使用POST方法发送请求,将图片作为表单数据传递
                var form_data = new FormData();
                form_data.append("image", image_input);
                $.ajax({
                    url: backend2_url,
                    type: "POST",
                    data: form_data,
                    contentType: false,
                    processData: false,
                    success: function(data) {
                        // 处理返回的数据,调用显示结果的函数
                        display_results(data);
                    }
                });
            } else {
                // 如果没有输入,提示用户
                alert("请输入文本或上传图片");
            }
        });

        // 定义显示结果的函数,接收一个JSON格式的数据作为参数
        function display_results(data) {
            // 清空表格中原有的内容
            $("#result-table tbody").empty();
            // 遍历数据中的每一项,每一项对应一个视频片段的ID和置信度分数
            $.each(data, function(index, item) {
                // 从视频库中获取对应的封面图和时间戳信息,使用GET方法发送请求,将ID作为参数传递
                $.get(video_library_url, {id: item.id}, function(video_data) {
                    // 创建一个表格行元素,并填充内容
                    var tr = $("<tr></tr>");
                    // 创建一个表格单元格元素,用于显示封面图,封面图是一个超链接,点击可以跳转到视频播放器,并传递ID和时间戳作为参数
                    var td_cover = $("<td></td>");
                    var a_cover = $("<a></a>");
                    a_cover.attr("href", video_player_url + "?id=" + item.id + "&timestamp=" + video_data.timestamp);
                    a_cover.attr("target", "_blank");
                    var img_cover = $("<img></img>");
                    img_cover.attr("src", video_data.cover);
                    img_cover.attr("width", "200");
                    img_cover.attr("height", "150");
                    a_cover.append(img_cover);
                    td_cover.append(a_cover);
                    tr.append(td_cover);
                    // 创建一个表格单元格元素,用于显示时间戳序列
                    var td_timestamp = $("<td></td>");
                    td_timestamp.text(video_data.timestamp);
                    tr.append(td_timestamp);
                    // 创建一个表格单元格元素,用于显示置信度分数
                    var td_score = $("<td></td>");
                    td_score.text(item.score.toFixed(2));
                    tr.append(td_score);
                    // 将表格行元素添加到表格中
                    $("#result-table tbody").append(tr);
                });
            });
        }
    </script>
</body>
</html>

### 后端1代码

```python
# 导入Flask库和BERT模型
from flask import Flask, request, jsonify
from transformers import AutoTokenizer, AutoModel

# 创建Flask应用对象
app = Flask(__name__)

# 加载BERT模型和词典
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
model = AutoModel.from_pretrained("bert-base-chinese")

# 定义文本向量的文件路径
text_vector_file = "text_vectors.txt"

# 定义文本查询的路由和方法
@app.route("/text_query", methods=["GET"])
def text_query():
    # 获取请求参数中的文本内容
    text = request.args.get("text")
    # 如果没有文本,返回错误信息
    if not text:
        return jsonify({"error": "No text input"})
    # 使用BERT模型将文本编码成向量
    text_input = tokenizer(text, return_tensors="pt")
    text_output = model(**text_input)
    text_vector = text_output.last_hidden_state[:, 0, :].squeeze().detach().numpy()
    # 从文件中读取视频库中的文本向量和ID
    video_text_vectors = []
    video_ids = []
    with open(text_vector_file, "r") as f:
        for line in f:
            id, vector = line.strip().split("\t")
            video_ids.append(id)
            video_text_vectors.append([float(x) for x in vector.split(",")])
    # 计算输入文本向量和视频库中的文本向量的余弦相似度,并按降序排序,取前10个结果
    similarities = []
    for i in range(len(video_text_vectors)):
        similarity = cosine_similarity(text_vector, video_text_vectors[i])
        similarities.append((video_ids[i], similarity))
    similarities.sort(key=lambda x: x[1], reverse=True)
    top10_results = similarities[:10]
    # 返回一个JSON格式的数据,包含了Top10视频片段的ID和置信度分数
    return jsonify(top10_results)

# 定义计算余弦相似度的函数,接收两个向量作为参数
def cosine_similarity(vector1, vector2):
    # 计算两个向量的点积和模长
    dot_product = sum(a * b for a, b in zip(vector1, vector2))
    norm1 = sum(a * a for a in vector1) ** 0.5
    norm2 = sum(b * b for b in vector2) ** 0.5
    # 返回余弦相似度,保留四位小数
    return round(dot_product / (norm1 * norm2), 4)

# 运行Flask应用,监听5001端口
if __name__ == "__main__":
    app.run(port=5001)

### 后端2代码

```python
# 导入Flask库和ResNet模型
from flask import Flask, request, jsonify
from torchvision import models, transforms
from PIL import Image

# 创建Flask应用对象
app = Flask(__name__)

# 加载ResNet模型
model = models.resnet50(pretrained=True)
model.eval()

# 定义图片预处理的转换器
transformer = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

# 定义图片向量的文件路径
image_vector_file = "image_vectors.txt"

# 定义图片查询的路由和方法
@app.route("/image_query", methods=["POST"])
def image_query():
    # 获取请求中的图片文件
    image_file = request.files.get("image")
    # 如果没有图片,返回错误信息
    if not image_file:
        return jsonify({"error": "No image input"})
    # 使用PIL库将图片文件转换成图片对象
    image = Image.open(image_file)
    # 使用ResNet模型将图片编码成向量
    image_input = transformer(image).unsqueeze(0)
    image_output = model(image_input)
    image_vector = image_output.squeeze().detach().numpy()
    # 从文件中读取视频库中的图片向量和ID
    video_image_vectors = []
    video_ids = []
    with open(image_vector_file, "r") as f:
        for line in f:
            id, vector = line.strip().split("\t")
            video_ids.append(id)
            video_image_vectors.append([float(x) for x in vector.split(",")])
    # 计算输入图片向量和视频库中的图片向量的余弦相似度,并按降序排序,取前10个结果
    similarities = []
    for i in range(len(video_image_vectors)):
        similarity = cosine_similarity(image_vector, video_image_vectors[i])
        similarities.append((video_ids[i], similarity))
    similarities.sort(key=lambda x: x[1], reverse=True)
    top10_results = similarities[:10]
    # 返回一个JSON格式的数据,包含了Top10视频片段的ID和置信度分数
    return jsonify(top10_results)

# 定义计算余弦相似度的函数,接收两个向量作为参数
def cosine_similarity(vector1, vector2):
    # 计算两个向量的点积和模长
    dot_product = sum(a * b for a, b in zip(vector1, vector2))
    norm1 = sum(a * a for a in vector1) ** 0.5
    norm2 = sum(b * b for b in vector2) ** 0.5
    # 返回余弦相似度,保留四位小数
    return round(dot_product / (norm1 * norm2), 4)

# 运行Flask应用,监听5002端口
if __name__ == "__main__":
    app.run(port=5002)

相关阅读

热门文章

    手机版|MSIPO技术圈 皖ICP备19022944号-2

    Copyright © 2024, msipo.com

    返回顶部