综合设计——多源异构数据采集与融合应用综合实践

这个项目属于哪个课程 2024数据采集与融合技术实践
组名 在大大的数据里面挖呀挖呀挖
项目简介

项目名称:城市记忆

Logo:

项目需求:整合城市历史文化资源:需要以交互式的方式展示城市的历史发展、新旧照片、方言特色以及名人故事等内容。增强用户参与感:为用户提供交互性体验,如照片上色、语音生成、内容检索等。利用多媒体技术提升展示效果:通过地图、视频、音频、图像等多种形式,全方位展示城市记忆,构建沉浸式体验。

项目开展技术路线:python、html、JavaScript、flask、mysql

团队成员学号 102202114农晨曦,102202118杨美荔、102202144傅钰、102202147赖越、102202150魏雨萱、102202152张静雯
这个项目的目标 建立多模块融合的城市记忆平台:打造一个集历史资源展示、文化传承和科技互动为一体的平台。
提升用户体验与交互性:通过地图导航、名人故事展示、黑白照片上色和方言音频生成等功能,让用户更生动地了解城市文化。
促进文化保护与传承:利用现代技术对城市历史资源进行数字化存档,帮助大众更便捷地探索与分享城市记忆。
探索跨学科技术融合:结合深度学习、多模态技术与地理信息系统等技术,提升文化展示的深度与广度。
其他参考文献 近20 年城市记忆研究综述
城市记忆与城市形态——从心理学、社会学视角探讨城市历史文化的延续
星火认知大模型Web API文档

综合设计——多源异构数据采集与融合应用综合实践

项目介绍

项目gitee链接

https://gitee.com/wei-yuxuan6/myproject/tree/master/实践作业

项目背景

城市记忆承载着一座城市的历史、文化与情感,其背后的故事和影像是研究城市变迁与文化保护的重要资源。然而,许多城市的历史资源缺乏系统化的整理与展示,普通大众难以方便地接触这些内容。随着人工智能与多媒体技术的快速发展,利用技术手段将历史内容与现代形式结合,可以更好地传递城市文化,增强公众的文化认同感。

功能介绍

  1. 时空地图
    功能描述:
    用户通过交互式地图探索城市历史文化资源。
    点击地图标记点:显示对应城市的方言音频、新旧照片对比、历史发展时间轴信息。
    查看名人故事:点击标记点的“名人故事”按钮跳转到城市的名人集页面,展示与该城市相关的名人列表。
    名人故事展示:点击名人列表中的名人姓名,可以查看详细的名人故事,包括其生平事迹、贡献和与城市的关联性。
    技术实现:
    使用 高德地图 API 提供地图展示与交互功能。
    数据库存储包含城市地理信息及相关文化资源。
    名人资源使用Spark 4.0 Ultra搜索并整理名人集和名人故事。
  2. 照片上色
    功能描述:
    用户上传黑白照片,系统自动将其转换为色彩丰富的上色照片,直观展现历史影像的色彩还原效果。
    支持多种照片格式,快速处理。
    生成对比图,展示黑白原图与上色图的差异。
    技术实现:
    使用 百度图片上色接口 提供高精度的图片上色服务。
    后端实现文件上传和上色结果的高效存储与展示。
  3. 方言之声
    功能描述:
    用户输入一段文本并选择城市名称,系统自动生成对应城市方言的音频文件。
    支持多种方言生成,包括普通话、粤语、东北话等。
    生成音频后提供播放功能,用户可以在线收听或下载保存。
    技术实现:
    使用 讯飞星火语音合成 API 实现高质量的多方言语音合成。
  4. 时光影像
    功能描述:
    用户输入城市名称,系统搜索该城市的老视频并在页面中展示。
    视频内容涵盖城市建设、名
    技术实现:
    使用Whisper模型,从视频中提取音频并进行精准的转录,从而生成标准格式的字幕文件

个人分工

1.数据采集

利用Selenium和yt-dlp实现B站视频批量下载

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from bs4 import BeautifulSoup
import os
import subprocess
import time
import threading

# 设置基本URL和目标网页
BASE_URL = "https://space.bilibili.com/402595174/channel/collectiondetail?sid=1261067"

# 设置保存视频的目录
SAVE_DIR = "E:/video"
if not os.path.exists(SAVE_DIR):
    os.makedirs(SAVE_DIR)

# 设置cookies文件路径
COOKIES_FILE = "E:/cookies.txt"

# 每个线程下载视频的数量限制
MAX_VIDEOS = 200  # 假设下载最多200个视频

# 计数已下载的视频数量
downloaded_videos = 0
lock = threading.Lock()

# 增加重试次数
RETRY_LIMIT = 3

# 使用Selenium来获取页面
def get_html_using_selenium(driver):
    """获取当前页面的HTML"""
    time.sleep(5)  # 等待页面加载完成
    return driver.page_source


# 提取视频链接
def extract_video_links(html):
    """从页面HTML中提取视频链接"""
    soup = BeautifulSoup(html, "html.parser")
    video_links = []
    for li_tag in soup.find_all('li', class_="small-item"):
        data_aid = li_tag.get('data-aid')
        if data_aid:
            video_url = f"https://www.bilibili.com/video/{data_aid}/"
            video_links.append(video_url)
    return video_links


# 下载视频
def download_video(video_url):
    """下载视频并删除未合并的文件"""
    global downloaded_videos
    if downloaded_videos >= MAX_VIDEOS:
        print(f"已下载 {MAX_VIDEOS} 个视频,停止下载。")
        return

    # 获取视频标题
    video_title = video_url.split("/")[-1]
    video_file_path = os.path.join(SAVE_DIR, f"{video_title}.mp4")

    if os.path.exists(video_file_path):
        print(f"视频已存在,跳过下载: {video_title}")
        return

    with lock:  # 使用锁来防止多线程时的竞争
        downloaded_videos += 1
        print(f"正在下载第 {downloaded_videos} 个视频: {video_url}")

    # 设置命令以下载视频并保存文件
    command = f'yt-dlp --cookies "{COOKIES_FILE}" -f 30064+30280 "{video_url}" -o "{SAVE_DIR}/%(title)s.%(ext)s" -k'
    try:
        print(f"执行命令: {command}")
        subprocess.run(command, shell=True, check=True)
        print(f"视频下载成功: {video_url}")

        # 删除多余的.m4a文件,仅保留.mp4
        cleanup_files(video_title)

    except subprocess.CalledProcessError as e:
        print(f"下载失败: {e}")
        # 重试下载
        for retry in range(RETRY_LIMIT):
            print(f"正在重试下载: {video_url},重试 {retry + 1}/{RETRY_LIMIT}")
            try:
                subprocess.run(command, shell=True, check=True)
                print(f"视频下载成功: {video_url}")

                # 删除多余的.m4a文件,仅保留.mp4
                cleanup_files(video_title)
                break
            except subprocess.CalledProcessError as retry_error:
                print(f"重试失败: {retry_error}")
                if retry == RETRY_LIMIT - 1:
                    print(f"重试 {RETRY_LIMIT} 次后下载失败: {video_url}")

    # 加入文件合并后延迟,防止文件占用
    time.sleep(2)  # 休眠 2 秒,确保文件完全释放


一共下载了60多GB的视频,因为前面调试代码重复下载了很多视频

接下来清理文件夹中的冗余文件

import os
import glob
import re

# 定义文件夹路径
folder_path = r'E:\video'

# 遍历目录中的所有 .mp4 文件
mp4_files = glob.glob(os.path.join(folder_path, '**', '*.mp4'), recursive=True)

# 打印所有找到的 mp4 文件
print("所有 .mp4 文件:")
for file in mp4_files:
    print(repr(file))  # 使用 repr 来检查路径中是否有隐藏字符

for file in mp4_files:
    # 使用正则表达式匹配带有 [ ] 的文件(带附加标识的文件)
    if re.search(r'\[.*\]\.mp4$', file):  # 匹配带有方括号的文件
        # 去除文件名中的附加标识部分 [BV1DE41127GQ],保留基本文件名
        base_file = re.sub(r'\[.*\]\.mp4$', '.mp4', file)  # 去除 [内容].mp4 部分

        # 打印检查信息,查看文件名匹配情况
        print(f"检查文件: {repr(file)}")
        print(f"对应的基文件: {repr(base_file)}")

        # 去掉路径中的额外空格,强制清除文件名和路径的末尾空格
        file = file.strip()  # 去除文件路径两端的空格
        base_file = base_file.strip()  # 去除基文件路径两端的空格

        # 强制去除路径末尾空格
        file = re.sub(r'\s+$', '', file)  # 去除路径结尾的空格
        base_file = re.sub(r'\s+$', '', base_file)  # 去除路径结尾的空格

结果展示

2.使用 Whisper 和 FFmpeg 自动提取视频字幕

因为爬取到的老视频大部分都是早期的网络视频、电影或者纪录片都没有提供字幕,这使得听力障碍者或非母语观众无法完全理解视频的内容,因此我们选用Whisper模型,帮助我们从视频中提取音频并进行精准的转录,从而生成标准格式的字幕文件。

import os
import whisper
import subprocess  # 使用 subprocess 来调用 ffmpeg
import srt

# 加载 Whisper 模型(small)
model = whisper.load_model("small")
model = model.float()

# 视频文件目录
video_directory = r"E:\video1"  # 使用原始字符串处理路径
srt_directory = os.path.join(video_directory, "video_srt")  # 保存字幕
audio_directory = os.path.join(video_directory, "video_wav")  # 保存音频

# 确保目录存在
os.makedirs(srt_directory, exist_ok=True)
os.makedirs(audio_directory, exist_ok=True)

# 遍历目录中的所有视频文件
for filename in os.listdir(video_directory):
    if filename.endswith(".mp4"):  # 如果是 .mp4 文件
        video_path = os.path.join(video_directory, filename)
        audio_path = os.path.join(audio_directory, f"{os.path.splitext(filename)[0]}.wav")
        srt_path = os.path.join(srt_directory, f"{os.path.splitext(filename)[0]}.srt")

        try:
            # 提取音频文件
            print(f"开始提取音频: {video_path} -> {audio_path}")
            subprocess.run(['ffmpeg', '-i', video_path, audio_path, '-y'], check=True)
            print(f"音频文件已提取: {audio_path}")
        except Exception as e:
            print(f"音频提取失败: {e}")
            continue

        try:
            # 对音频进行转录
            print(f"开始转录音频: {audio_path}")
            result = model.transcribe(audio_path)
            print(f"转录成功: {audio_path}")

            # 创建 SRT 字幕列表
            subtitles = []
            for segment in result["segments"]:
                start = segment["start"]
                end = segment["end"]
                text = segment["text"]
                subtitle = srt.Subtitle(index=len(subtitles) + 1, start=srt.timedelta(seconds=start),
                                        end=srt.timedelta(seconds=end), content=text)
                subtitles.append(subtitle)

            # 保存字幕为 SRT 文件
            with open(srt_path, "w", encoding="utf-8") as srt_file:
                srt_file.write(srt.compose(subtitles))

生成的srt文件展示:

将字幕嵌入到视频中,结果展示:

3.心得体会

参与构建城市记忆的网站让我感到意义非凡。在爬取和处理老视频的过程中,我深刻体会到技术不仅是工具,更是一座桥梁,连接过去与未来。通过为这些记录着城市历史的视频提取字幕并规范化处理,我仿佛与历史对话,感受到了那些承载时光的影像背后的人文情怀。能够为城市记忆的保存和传承贡献一份力量,让更多人了解这些珍贵的历史片段,我倍感自豪。技术与情怀结合,让这份工作更加温暖且有意义。

posted @ 2024-12-15 23:29  旺旺cc冰  阅读(9)  评论(0编辑  收藏  举报