惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
S
Security Affairs
PCI Perspectives
PCI Perspectives
Google Online Security Blog
Google Online Security Blog
W
WeLiveSecurity
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
P
Privacy & Cybersecurity Law Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Security @ Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
Cyberwarzone
Cyberwarzone
L
Lohrmann on Cybersecurity
TaoSecurity Blog
TaoSecurity Blog
V
Visual Studio Blog
博客园 - 聂微东
Scott Helme
Scott Helme
博客园 - 【当耐特】
K
Kaspersky official blog
Security Latest
Security Latest
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
MyScale Blog
MyScale Blog
Schneier on Security
Schneier on Security
WordPress大学
WordPress大学
博客园 - 叶小钗
C
Check Point Blog
V2EX - 技术
V2EX - 技术
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - Franky
T
Tor Project blog
Apple Machine Learning Research
Apple Machine Learning Research
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
雷峰网
雷峰网
博客园_首页
美团技术团队
Y
Y Combinator Blog
C
CERT Recently Published Vulnerability Notes
AWS News Blog
AWS News Blog
月光博客
月光博客
N
Netflix TechBlog - Medium
Last Week in AI
Last Week in AI
Recent Announcements
Recent Announcements
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
P
Proofpoint News Feed
MongoDB | Blog
MongoDB | Blog
C
Cybersecurity and Infrastructure Security Agency CISA

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角 .NET Core 原生 DI 扩展之基于名称的注入实现 - 元视角
作为技术宅的我,是这样追鬼滅の刃的 - 元视角
飞鸿踏雪 · 2020-12-16 · via 元视角

有人说,“男人至死都是少年”,而这句听起来有一点中二的话,其实是出自一部同样有一点中二的动漫——银魂。我个人的理解是,知世故而不世故。也许,年轻时那些天马行空的想法,就像堂吉诃德大战风车一样荒诞,可依然愿意去怀着这样的梦想去生活。正如罗曼罗兰所言,“世上只有一种英雄主义,就是在认清生活真相之后依然热爱生活”。所以,继《浪客剑心》之后,我再次被一部叫做《鬼灭之刃》的动漫吸引,毕竟男人的快乐往往就是这么朴实无华且枯燥。一个快三十岁的人,如果还能被一部热血少年番吸引,大概可以说明,他身体里的中二少年连同中二少年魂还活着。最早的印象来自朋友圈里的一位二次元“少年”,他和自己儿子站一起,有种浑然天成的协调感,整个人是非常年轻的感觉。所以,大概,男人至死都是少年。

漫画的抓取

鬼滅の刃的漫画早已更完,令我不舍昼夜去追的,实际上是动画版的鬼滅の刃。虽然 B 站上提供中配版本,可一周更新两集的节奏,还是让我追得有一点焦灼(PS:我没有大会员呢),甚至熬着夜提前“刷”了无限列车(PS:见文章末尾小程序码)。其实,鬼滅の刃前期并没有特别吸引人的地方,直到那田蜘蛛山那一话开始渐入佳境,鬼杀队和鬼两个阵营所构成的人物群像开始一点一点的展开。它的表达方式有点接近刺客信条,即反派都是在死亡一刹那间有了自我表达的机会,而玩家/观众都可以了解反派的过去。由于鬼是由人转变而来,所以,在热血和厮杀之外,同样有了一点关乎人性的思考。作为一名“自封”的技术宅,我必须要在追番的时候做点什么,从哪里开始好呢?既然漫画版早已更新完毕,我们要不先抓取漫画下来提前过过瘾?

OK,这里博主找了一个动漫网站,它上面有完整的鬼滅の刃漫画。我意识到从网上抓取漫画的行为是不对的,可这家网站提供的漫画明显是通过扫描获得的,因为正常的漫画都是通过购买杂志的方式获得的。所以,如果经济条件允许的情况下,还是希望大家可以支持正版,这里博主主要还是为了研究技术(逃,无意对这些资源做二次加工或者以任何方式盈利,所以,请大家不要向博主索取任何资源,我对自己的定位永远是一名软件工程师,谁让我无法成为尤小右这样的“美妆”博主呢?这一点希望大家可以理解哈!

鬼滅の刃作品页面 鬼滅の刃作品页面

鬼滅の刃章节页面 鬼滅の刃章节页面

简单分析下动漫网站结构,可以发现,它主要有两种界面,即作品页面和章节页面。作品页面里面会显示所有的章节,而每个章节里会显示所有的图片。所以,我们的思路是,首先,通过作品页面获取所有章节的链接。其次,针对每一个章节,获取总页数后逐页下载图片即可。注意到这个网站有部分内容是通过 JavaScript 动态生成的,所以,requests针对这种情况会有点力不从心。幸好,我们还有Selenium这个神器可以使用,我们一起来看这部分内容如何实现:

import requests
from bs4 import BeautifulSoup
import fake_useragent
import json
import urllib
from selenium import webdriver
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import os, time
import threading
import threadpool 

class DemonSlayer:

    def __init__(self, baseUrl):
        self.baseUrl = baseUrl
        self.session = requests.session()
        self.headers = {
            'User-Agent': fake_useragent.UserAgent(verify_ssl=False).random
        }
        # 使用无头浏览器
        fireFoxOptions = webdriver.FirefoxOptions()
        fireFoxOptions.set_headless()
        self.driver = webdriver.Firefox(firefox_options=fireFoxOptions)

    # 获取所有章节信息
    def getAllChapters(self, opusUrl):
        chapters = {}
        response = self.session.get(opusUrl, headers=self.headers)
        response.encoding = 'utf-8'
        response.raise_for_status()
        soup = BeautifulSoup(response.content)
        soup = soup.find(name='div', attrs={'class','cy_plist'})
        if (soup != None):
            for li in soup.ul.children:
                chapters[li.a.text] = self.baseUrl + li.a['href']
        return chapters
    
    # 获取指定章节信息
    def getChapter(self, url):
        page = 1
        maxPage = self.getPageOfChapter(url)
        images = []
        while page <= maxPage:
            reqUrl = url + '?p={page}'.format(page=page)
            self.driver.get(reqUrl)
            wait = WebDriverWait(self.driver, 10)
            # 等待漫画图片加载完成
            wait.until(EC.presence_of_element_located((By.ID, "qTcms_pic")))
            html = self.driver.page_source
            imgSrc = BeautifulSoup(html).find('img')['src']
            # 从网页中提取的图片地址需要做一次解码
            realSrc = urllib.parse.unquote(imgSrc.split('?')[1].split('&')[0].split('=')[1])
            images.append(realSrc)
            page += 1
        return images
    
    # 获取指定章节页数
    def getPageOfChapter(self, url):
        self.driver.get(url)
        wait = WebDriverWait(self.driver, 10)
        # 等待页数加载完成
        wait.until(EC.presence_of_element_located((By.ID, "k_pageSelect")))
        html = self.driver.page_source
        opts = list(BeautifulSoup(html).find('select').children)
        return int(opts[-1]['value'])
    
    # 下载漫画图片
    def getImage(self, chapterPath, index, url):
        imagePath = os.path.join(chapterPath, str(index) + '.jpg')
        with open(imagePath, 'wb') as fp:
            response = self.session.get(url)
            response.raise_for_status()
            fp.write(response.content)
    
    # 公共入口
    def run(self, url):
        chapters = self.getAllChapters(url)
        for chapter, url in chapters.items():
            images = spider.getChapter(url)
            # 为每一个章节建立文件夹
            chapterPath = './download/{chapter}/'.format(chapter=chapter)
            if (not os.path.exists(chapterPath)):
                os.mkdir(chapterPath)
            # 使用多线程下载图片
            args = []
            for index, url in enumerate(images):
                args.append((None,{'chapterPath':chapterPath, 'url':url, 'index':index}))
            pool = threadpool.ThreadPool(max(10, len(images))) 
            requests = threadpool.makeRequests(self.getImage, args) 
            [pool.putRequest(req) for req in requests] 
            pool.wait()

if __name__ == '__main__':
    spider = DemonSlayer('http://www.7edm.com/')
    spider.run('http://www.7edm.com/rexue/guimiezhiren/')

在这里,需要注意的 Firefox 驱动,即GeckoDriver需要提前安装好。同时,确保 Firefox 主程序和驱动程序所在的目录均已配置到环境变量Path中。在有的资料中提到,GeckoDriver需要和 Firefox 主程序在同一个目录底下,不过经过博主的验证,需要放在 Python 主程序的根目录底下。这个驱动程序可以从这里下载:https://github.com/mozilla/geckodriver/releases/。当然,如果你更喜欢使用 Chrome,只需要安装 Chrome 的驱动即可,完全遵从个人意愿即可。在运行这个脚本后,我们就可以获得完整的鬼滅の刃漫画:

鬼灭之刃204话 鬼灭之刃204话

在 Kindle 上“追”番

好了,到现在为止,我们实现了本文的第一个小目标,即漫画的抓取。那么,在抓取到漫画以后,我们可以做什么呢?可能会有读者朋友忍不住吐槽,“废话,漫画除了看还能干什么”。话虽如此,可这样一张张图片显然不能让我们方便地看漫画啊!早就听说在 Kindle 上可以看漫画,可一直没有真正尝试过,可能是因为我还不够中二吧!所以,接下来,我们来考虑将这些图片制作成电子书。Kindle 本身支持像 mobi、docx/doc、pdf 等等这样的格式,这里我们选择一种最简单的方式,利用PyMuPDF这个库来生成 PDF 文件,一起来看具体的代码实现:

def createPdf(self, chapterPath):
    doc = fitz.open()
    pdfPath = './ebook/' + os.path.basename(chapterPath) + '.pdf'
    for image in sorted(os.listdir(chapterPath)):
        filePath = os.path.join(chapterPath, image)

        # 为每一张图片创建一个单独的PDF
        imgdoc = fitz.open(filePath)
        pdfbytes = imgdoc.convertToPDF()
        imgpdf = fitz.open("pdf", pdfbytes)
            
        # 插入当前页
        doc.insertPDF(imgpdf)  # 将当前页插
        
    # 保存PDF
    doc.save(pdfPath)

经常使用 Kindle 的朋友,一定对它的邮箱传书功能非常熟悉,这意味着我们此基础上,将生成的 PDF 文件直接推送到 Kindle 上,而在 Python 中发送邮件则是非常简单的,这一点我们不再赘述,那到底能不能实现我们这个想法呢?显然可以,我们一起来看看效果:

由PyMuPDF生成的“漫画书” 由PyMuPDF生成的“漫画书”

我们知道 Kindle 对 PDF 的支持是有点差的,主要体现在它不支持重排,在我们这个例子中,我们可以发现图片是横屏显示的,或许是因为我的 Kindle 屏幕太小,或许是因为我没有一个 Pad,或许是因为图片尺寸无法适配,总之,它给人的体验是有点不舒服。博主注意到,Kindle 官方提供了一个漫画书制作软件Kindle Comic Creator,它可以生成 mobi 格式的电子书。作为对比,我们来看一下它在 Kindle 上的显示效果:

由Kindle Comic Creator生成的“漫画书” 由Kindle Comic Creator生成的“漫画书”

果然还是 Kindle 自家的格式更好一点,有同样需求的小伙伴们,不妨试一试这个软件!现在,我们终于可以在 Kindle 上“刷”漫画了,快乐肥宅水,安排!

让漫画“动”起来

一旦看过动画以后,再去看漫画,总觉得没那味儿,果然,“没声音再好的戏都出不来”,除了想念片头 OP 以外,更想念声优们绘声绘色的配音。有人说,博主你可以戴上耳机,放着片头 OP,刷着漫画啊!哎,你可真是小机灵鬼!可作为一名技术宅,我们当然要追求手工耿的无用之美,不就是想看有声音有画面的视频吗?安排!所以,下面我们来通过这些图片生成视频,这里主要用到opencv-pythonMoviePy两个库,前者可以通过OpenCV合成视频,而后者则可以对视频进行剪辑,例如加入片头、片尾、背景音乐等等。

def createVideo(self, chapterPath):
    # OpenCV创建视频
    videoFps = 40 
    videoSize = (655, 948) 
    videoPath = './video/' + os.path.basename(chapterPath) + '.avi'
    videoWriter = cv2.VideoWriter(videoPath, 
        cv2.VideoWriter_fourcc('I', '4', '2', '0'), 
        videoFps, 
        videoSize
    )

    for image in sorted(os.listdir(chapterPath)): 
        # 读取每张图片,并重复写40帧    
        image_path = chapterPath + '/' + image
        bg = cv2.imread('bg.jpg')
        max_h, max_w, _ = bg.shape
        img = cv2.imdecode(np.fromfile(image_path ,dtype=np.uint8),-1)
        img_h, img_w, _ = img.shape
        scale = max_h / img_h
        new_w = int(img_w * scale)
        new_h = int(img_h * scale)
        img = cv2.resize(img ,(new_w ,new_h))
        offsetX = int((max_w - new_w) / 2)
        offsetY = int((max_h - new_h) / 2)
        bg[offsetY:offsetY + img.shape[0], offsetX:offsetX + img.shape[1]] = img

        for i in range(0, videoFps):
            videoWriter.write(bg)
    videoWriter.release()
        
    # 为当前视频加入片头/尾
    videoClip = VideoFileClip(videoPath)
    titleClip = VideoFileClip('./title.mp4')
    finalClip = concatenate_videoclips([
        titleClip.subclip(0, 12), 
        videoClip,
        titleClip.subclip(70, 72),
        titleClip.subclip(85, 89),
    ])
    
    # 为当前视频增加BGM
    audioClip = AudioFileClip('BGM.mp3').subclip(0, finalClip.duration)
    finalClip.audio = audioClip
    finalClip.write_videofile('output.avi', codec='libvpx')

在使用 OpenCV 合成视频这一步,和大多数第一次接触 OpenCV 的人一样,博主在这里遇到了很多的问题,例如,最典型的问题有:OpenCV 合成的视频无法打开、OpenCV 无法使用中文路径、OpenCV 合成的视频长度较短等等。所以,在这里我简单做一下说明,首先,检查下视频画面的宽/高是否与图片的宽/高一致。其次,选择合适的编码器,在使用 OpenCV 保存视频的时候,需要综合考虑 FOURCC 和视频格式两个因素,前提是当前系统已经安装了对应的编码器,在 Windows 下推荐大家使用 DIVX。最后,关于中文路径,大部分的解决方案都是引入 NumPy,代码片段如下:

# 读取
cv_img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), -1)
# 写入
cv2.imencode('.jpg', cv_img)[1].tofile(file_path)

经过博客的验证,这个方案的确可以解决,不过同样有缺点,imdecode()方法读取的是 RGB,而 OpenCV 需要的则是 BGR,需要做一次转换,而转换则意味着会损失图片信息。所以,如果在意生成的视频质量的话,最好还是放在英文的路径下面。

好了,现在来看看效果,当片头 OP 响起的时候,有没有觉得,那种熟悉感觉的又回来了(PS:此处可与飞驰人生梦幻联动),虽然没有配音,还是有一点视频的样子的。我经常在 B 站看到那种,黑色背景 + 白色文字的视频,虽然没有特别复杂的转场动画,可搭配上小爱同学或者 Siri 同学的声音后,居然感觉还不错。知乎上同样提供了文章转视频的功能,所以,我在想这是不是可以作为一个思路,作为一个努力寻找流量方向的技术博客,我好累啊(逃……

这个思路,其实还可以用来制作表情包,作为一部拥有大量“名场面”的动漫作品,它为博主带来了不少的“燃点”和“笑点”,下面例举一二供大家欣赏:

世界名画 世界名画

安塞腰鼓 安塞腰鼓

对于静态表情包,通常只需要用到PIL库就可以完成,下面是富冈义勇迫害时间(逃:

image = Image.open('鬼灭之刃-09.jpg')
width, height = image.size
textFont = ImageFont.truetype('Deng.ttf', 18)
imageDraw = ImageDraw.Draw(image)
textPos = (width * 0.34, height * 0.8)
text = '我感觉我没有被讨厌'
imageDraw.text(textPos, text, fill='#fff', font=textFont, align='left')
image.save('富岗义勇表情包.jpg','jpeg')

如此,我们就可以得到下面的名场面:

我感觉我没有被讨厌 我感觉我没有被讨厌

冰柱表示不想和你说话 冰柱表示不想和你说话

而对于动态表情包,我们可以考虑使用imageioMoviePy,它们都可以从图片或者视频来生成 GIF,一起来看下面的例子:

def createGif(self, framesPath, gifPath, duration=0.5):
    # 读取图片
    frames = []
    for image_name in sorted(os.listdir(framesPath)):
        image_path = os.path.join(framesPath, image_name)
        frames.append(imageio.imread(image_path))

    # 保存GIF
    imageio.mimsave(gifPath, frames, 'GIF', duration=duration)

# 读取一组图片并生成GIF
handler = ImageHandler()
handler.createGif('./frames', '登峰造极.gif')

这里唯一需要注意的就是,imageio依赖于Pillow:

集中一点,登峰造极 集中一点,登峰造极

如果使用MoviePy来生成 GIF,则可以通过VideoFileClip或者ImageSequenceClip来分别从视频和图片创建 GIF。同样,这里有一个简单的例子:

# 从一组图片并生成GIF和视频
fps = 0.25
sequence = ImageSequenceClip('./frames', fps)
sequence.write_gif('登峰造极.gif')
sequence.write_videofile('登峰造极.avi', codec='libvpx')

# 从视频中截取指定片段生成GIF
videoClip = VideoFileClip('input.mp4')
videoClip = videoClip.subclip(33, 45)
videoClip.write_gif('霹雳一闪.gif')

在这里,我选取的是,我个人非常喜欢的角色——我妻善逸的名场面“霹雳一闪”:

霹雳一闪 霹雳一闪

至此,我们“绞尽脑汁”、“千方百计”、“搜肠刮肚”地想到了各种“刷”鬼滅の刃的方法,总算为这份中二信仰充了值,大家还有什么好的点子吗?欢迎大家在评论区留言、点赞、收藏、一键三连,我仿佛嗅到了 Bilibili 的味道(逃……

本文小结

坦白来讲,这篇博客的确有蹭热度的嫌疑,不过以鬼灭之刃在日本妇孺皆知的火热程度,足以令人们在这颇感失落的疫情背景下为之一振,甚至于连日本首相菅义伟在国会质询时,都引用了“全集中呼吸”这一经典台词。“纵有疾风起,人生不言弃”,热血少年漫告诉我们,在面对一个又一个的挫折的时候,不要沉溺于昨天的回忆,去勇敢地接受现实,直面惨淡的人生,这又何尝不是我们面对生活的态度呢?不管是旷日持久的疫情,还是似曾相识的失业,人生的道路上还有数不尽的“魑魅魍魉”呢!希望你活得像炭治郎一样乐观善良,像我妻善逸一样勇敢坚毅,像伊之助一样“猪”突猛进。如果这篇博客里所介绍的“追”动漫的方式,能让大家感到快乐和有趣的话,欢迎大家在评论区留言、点赞、收藏、一键三连。以上,这就是我,作为一个技术宅,在“追”动漫方面所做出的努力。少年,想加入鬼杀队吗?