Python编曲实践(八):我,乔鲁诺·乔巴那,能用两百行代码写出JOJO黄金之风里我自己的出场曲!

本文主要是介绍Python编曲实践(八):我,乔鲁诺·乔巴那,能用两百行代码写出JOJO黄金之风里我自己的出场曲!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

前些天笔者写的文章 Python编曲实践(七):整整一百行Python代码写出黑人抬棺梗曲《Astronomia》的旋律 受到了大家的许多支持和好评,本篇文章挑战更复杂、更有挑战性,同时也很有梗的一首音乐,那就是《JOJO的奇妙冒险第五部:黄金之风》的主要OST之一:Giorno’s Theme。这首音乐时常在主人公乔鲁诺·乔巴那召唤替身使者“黄金体验”(如下图)的时候响起,强烈的律动感使人过耳不忘。
在这里插入图片描述

B站有很多这首音乐的翻唱作品,其中下面这个比较有特色(为了更容易实现,本篇文章暂时忽略了中间的复杂部分,而保留了最有标志性的内容):

那不勒斯街头用萨克斯吹JoJo黄金之风的BGM-GIORNO'S THEME

与黑人抬棺曲《Astronimia》的相比,这篇音乐的结构更复杂,主要在以下几点:

  • 使用了两个音轨来模拟钢琴演奏时的左手低音区部分右手高音区部分
  • 出现了和弦,即在同一时间点多个音符同时奏响的织体结构
  • 使用了升降号,包括升号♯与降号♭,通过这两个标记来使得音符在原本的音高上升高或降低一个半音

后两种变化使得之前使用的MidiFileExtended类变得無駄無駄無駄了,为了适应这些变化,笔者对其进行了更新和升级。大家可以在文章头部👆👆下载,也可以通过百度网盘链接下载(提取码zyyc),并务必在运行代码前完成以下几项简单工作:

  • 使用pip指令安装好 mido 库和 PyGame 库,MidiExtended类中的相关功能依赖于这两个库;
  • 将midi_extended.zip中的内容解压,并将整个文件夹拷贝到工程根目录下;
  • 确保歌曲的保存路径是存在的

做好准备工作之后,我们就开始欧拉欧拉欧拉吧!如果这一过程中遇到任何问题请及时评论或私信反映给我!

二百行代码

同上一篇一样,如果你是个急性子,已经迫不及待地想刮起黄金之风,那么就可以在确保上述三项准备工作已经就绪的前提下直接在你的电脑上运行这二百行代码(或者可以在Github中参考这一文件):

from midi_extended.MidiFileExtended import MidiFileExtendedclass GoldenWind(object):def __init__(self):self.bpm = 127self.time_signature = '4/4'self.key = 'Am'self.file_path = '../data/midi/write/golden_wind.mid'self.mid = MidiFileExtended(self.file_path, type=1, mode='w')def write(self):self.mid.add_new_track('Piano1', self.time_signature, self.bpm, self.key, {'0': 0})self.mid.add_new_track('Piano2', self.time_signature, self.bpm, self.key, {'0': 0})for i in [1, 2, 1, 3]:self.intro_outro(i, False)for i in [1, 2, 1, 4]:self.intro_outro(i, True)for i in [1, 2, 1, 3,1, 2, 1, 3,1, 2, 1, 3,1, 2, 1, 3]:self.piano2_pattern(i)for i in [1, 2, 3, 4]:self.piano1_parapraph(i)for i in [1, 2, 1, 3]:self.intro_outro(i, False)for i in [1, 2, 1, 4]:self.intro_outro(i, True)def intro_outro(self, pattern, both):tracks = [self.mid.get_extended_track('Piano1'), self.mid.get_extended_track('Piano2')]for i in range(2):track = tracks[i]if not both and i == 1:track.wait(1)continueif pattern == 4:track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.add_note(6, 1/16, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.wait(7/16)else:track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/16, base_num=-1-i)track.add_note(6, 1/16, base_num=-1-i)track.wait(1/16)track.add_note(7, 1/16, base_num=-1-i)track.wait(1/16)if pattern == 1:track.add_note(2, 1/16, base_num=-i)track.wait(1/16)track.add_note(7, 1/16, base_num=-1-i)track.wait(1/16)track.add_note(4, 1/16, base_num=-1-i, alt=1)elif pattern == 2:track.add_note(4, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(3, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(2, 1/16, base_num=-i)elif pattern == 3:track.add_note(4, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(3, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(7, 1/16, base_num=-1-i)track.add_note(6, 1/8, base_num=-1-i)def piano2_pattern(self, pattern):track = self.mid.get_extended_track('Piano2')if pattern == 1:track.add_note([7, 4, 7], 1/4+1/8, alt=[0, 1, 0], base_num=[-1, -1, -2])track.add_note([4, 2, 5], 1/8+1/4, alt=[0, 0, 1], base_num=[-1, -1, -2])track.wait(1/4)elif pattern == 2:track.add_note([7, 7], 1/2, base_num=[-1, -2])track.add_note([4, 4], 1/2, base_num=[-1, -2], alt=[1, 1])elif pattern == 3:track.add_note([1, 1], 1/2, base_num=[0, -1], alt=[1, 1])track.add_note([4, 4], 1/2, base_num=[-1, -2], alt=[1, 1])elif pattern == 4:track.add_note([4, 7], 1/4, base_num=[-1, -2], alt=[1, 0])track.wait(3/4)def piano1_parapraph(self, paragraph):track = self.mid.get_extended_track('Piano1')if paragraph == 1:track.add_note(4, 1/4+1/8, base_num=1, alt=1)track.add_note(4, 1/8+1/4, base_num=1)track.wait(1/8)track.add_note(2, 1/16, base_num=1)track.add_note(3, 1/16, base_num=1)track.add_note(4, 1/8+1/16, base_num=1)track.add_note(3, 1/16+1/8, base_num=1)track.add_note(2, 1/8, base_num=1)track.add_note(1, 1/8+1/16, base_num=1, alt=1)track.add_note(2, 1/16+1/8, base_num=1)track.add_note(3, 1/8, base_num=1)track.add_note(4, 1/4+1/8, base_num=1, alt=1)track.add_note(7, 1/8+1/4, base_num=1)track.add_note(7, 1/8)track.add_note(1, 1/8, base_num=1, alt=1)track.add_note(2, 1/8+1/16, base_num=1)track.add_note(3, 1/16+1/8, base_num=1)track.add_note(2, 1/8, base_num=1)track.add_note(1, 1/8+1/16, base_num=1, alt=1)track.add_note(6, 1/16+1/8, base_num=1)track.add_note(5, 1/8, base_num=1)if paragraph == 2:track.add_note([4, 2, 7], 1/4+1/8, alt=[1, 0, 0], base_num=[1, 1, 0])track.add_note([4, 2, 7], 1/8+1/4, base_num=[1, 1, 0])track.wait(1/8)track.add_note(2, 1/16, base_num=1)track.add_note(3, 1/16, base_num=1)track.add_note([4, 1, 5], 1/8+1/16, alt=[0, 1, 0], base_num=[1, 1, 0])track.add_note(3, 1/16+1/8, base_num=1)track.add_note(2, 1/8, base_num=1)track.add_note([1, 6], 1/8+1/16, alt=[1, 1], base_num=[1, 0])track.add_note(2, 1/16+1/8, base_num=1)track.add_note(3, 1/8, base_num=1)track.add_note([4, 7], 1/4+1/8, alt=[1, 0], base_num=[1, 0])track.add_note([7, 4], 1/8+1/4, base_num=[1, 1])track.add_note(7, 1/8, base_num=1)track.add_note(1, 1/8, base_num=2, alt=1)track.add_note([2, 7], 1/8+1/16, base_num=[1, 0])track.add_note(3, 1/16+1/8, base_num=1)track.add_note(5, 1/8)track.add_note(4, 1/8+1/16, alt=1)track.add_note(2, 1/16+1/8, base_num=1)track.add_note(3, 1/8, base_num=1)if paragraph == 3:track.add_note([4, 2, 7], 1/4+1/8, alt=[1, 0, 0], base_num=[1, 1, 0])track.add_note([4, 2, 7], 1/8+1/4, base_num=[1, 1, 0])track.wait(1/8)track.add_note(2, 1/16, base_num=1)track.add_note(3, 1/16, base_num=1)track.add_note([4, 7, 5], 1/8+1/16, base_num=[1, 0, 0])track.add_note(3, 1/16+1/8, base_num=1)track.add_note(2, 1/8, base_num=1)track.add_note([1, 5], 1/8+1/16, alt=[1, 0], base_num=[1, 0])track.add_note(2, 1/16+1/8, base_num=1)track.add_note(3, 1/8, base_num=1)track.add_note([4, 2, 7], 1/4+1/8, alt=[1, 0, 0], base_num=[1, 1, 0])track.add_note([7, 4, 1], 1/8+1/4, base_num=[1, 1, 1], alt=[0, 0, 1])track.add_note(7, 1/8, base_num=1)track.add_note(1, 1/8, base_num=2, alt=1)track.add_note(2, 1/8+1/16, base_num=1)track.add_note(3, 1/16+1/8, base_num=1)track.add_note(2, 1/8, base_num=1)track.add_note(1, 1/8+1/16, base_num=1, alt=1)track.add_note(6, 1/16+1/8, base_num=1)track.add_note(5, 1/8, base_num=1)if paragraph == 4:track.add_note([4, 2, 7], 1/4+1/8, alt=[1, 0, 0], base_num=[1, 1, 0])track.add_note([4, 2, 7], 1/8+1/4, base_num=[1, 1, 0])track.wait(1 / 8)track.add_note(2, 1/16, base_num=1)track.add_note(3, 1/16, base_num=1)track.add_note([4, 1, 5], 1/8+1/16, alt=[0, 1, 0], base_num=[1, 1, 0])track.add_note(3, 1/16+1/8, base_num=1)track.add_note(2, 1/8, base_num=1)track.add_note([1, 6], 1/8+1/16, alt=[1, 1], base_num=[1, 0])track.add_note(2, 1/16+1/8, base_num=1)track.add_note(3, 1/8, base_num=1)track.add_note([4, 2], 1/4+1/8, base_num=1, alt=[1, 0])track.add_note([7, 4], 1/8+1/4, base_num=1)track.add_note(7, 1/8)track.add_note(1, 1/8, base_num=1, alt=1)track.add_note(2, 1/8+1/16, base_num=1)track.add_note(5, 1/16+1/8, base_num=1)track.add_note(4, 1/8, base_num=1, alt=1)track.add_note(4, 1/8+1/16, base_num=1)track.add_note(2, 1/16+1/8, base_num=2)track.add_note(6, 1/8, base_num=1, alt=1)if __name__ == '__main__':golden_wind = GoldenWind()golden_wind.write()golden_wind.mid.save_midi()golden_wind.mid.play_it()

大家可能已经发现,这两百行代码的复杂程度较上一篇《Astronomia》而言有较大的提升,不过不用担心,下面的内容我将带大家梳理好音乐结构与代码安排的对应关系,并让大家熟悉一下MidiExtended这个类的基础使用方法。

实现过程

寻找曲谱资源

看过JOJO的朋友们都知道,其中的音乐不论是插曲还是片头片尾曲,质量都一点不含糊,而这一首《Giorno’s Theme》则因为其复杂的结构和大量的即兴演绎空间而受到大量音乐UP主的喜爱,因而变得超级火爆。经过大量搜索,最终笔者找到了既能体现原版音乐旋律精髓,又不会因太过冗长而难以实现的一个最佳版本,大家可以去试听一下,其中包含的两页曲谱如下图(倒数第三小节出现了明显的问题,笔者在本文中进行了修正,同时对结尾部分使用了前奏部分的结构):
在这里插入图片描述
可以发现这一曲谱包含两个声部,上面的五线谱有一个高音谱号,下面的五线谱有一个低音谱号,分别对应于钢琴的高音区与低音区:
在这里插入图片描述
通过这一对照表,我们就可以对五线谱中表示的旋律进行辨识,并开始正式编程:

初始化

在加入音符前需要对GoldenWind类进行初始化,分别对音乐的速度(BPM)、节拍、调性、MIDI文件保存地址和使用的MidiFileExtended对象进行初始化(根据五线谱的调号来看,这一段音乐的调性可能是C大调或者是A小调,笔者通过音符的出现范围来看初步判断它是A小调,如果有错误请及时指正):

    def __init__(self):self.bpm = 127self.time_signature = '4/4'self.key = 'Am'self.file_path = './golden_wind.mid' self.mid = MidiFileExtended(self.file_path, type=1, mode='w')

下面是用来调用负责不同部分的函数来实现整篇音乐的write函数,其中声明了两个Track来负责不同的钢琴部分。由于整篇音乐第一部分与最后一部分相同(笔者修改过,与原乐谱不同),故可以通过调用intro_outro函数来统一实现;而中间部分的高音区部分使用piano1_paragraph函数来实现,低音区部分使用piano2_pattern函数实现:

    def write(self):self.mid.add_new_track('Piano1', self.time_signature, self.bpm, self.key, {'0': 0})self.mid.add_new_track('Piano2', self.time_signature, self.bpm, self.key, {'0': 0})for i in [1, 2, 1, 3]:self.intro_outro(i, False)for i in [1, 2, 1, 4]:self.intro_outro(i, True)for i in [1, 2, 1, 3,1, 2, 1, 3,1, 2, 1, 3,1, 2, 1, 3]:self.piano2_pattern(i)for i in [1, 2, 3, 4]:self.piano1_parapraph(i)for i in [1, 2, 1, 3]:self.intro_outro(i, False)for i in [1, 2, 1, 4]:self.intro_outro(i, True)

前奏/尾奏:intro_outro函数

前奏/尾奏包括八小节的长度,对应乐谱前八小节的内容,通过intro_outro函数来实现。
改进后的add_note函数中第一个参数表示一个八度内的音高,第二个参数表示以全音符为单位长度下音符的时值,base_num表示以中央C的音区为基准的降低/升高八度数,alt用于表示升/降音高

    def intro_outro(self, pattern, both):tracks = [self.mid.get_extended_track('Piano1'), self.mid.get_extended_track('Piano2')]for i in range(2):track = tracks[i]if not both and i == 1:track.wait(1)continueif pattern == 4:track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.add_note(6, 1/16, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.wait(7/16)else:track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/8, base_num=-1-i)track.add_note(7, 1/16, base_num=-1-i)track.add_note(6, 1/16, base_num=-1-i)track.wait(1/16)track.add_note(7, 1/16, base_num=-1-i)track.wait(1/16)if pattern == 1:track.add_note(2, 1/16, base_num=-i)track.wait(1/16)track.add_note(7, 1/16, base_num=-1-i)track.wait(1/16)track.add_note(4, 1/16, base_num=-1-i, alt=1)elif pattern == 2:track.add_note(4, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(3, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(2, 1/16, base_num=-i)elif pattern == 3:track.add_note(4, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(3, 1/16, base_num=-i)track.wait(1 / 16)track.add_note(7, 1/16, base_num=-1-i)track.add_note(6, 1/8, base_num=-1-i)

这一函数的关键设置如下:

  • 整个八小节是四种不同小节样式的组合。故使用参数中的pattern用于区分不同的小节样式,并根据pattern参数的值来进行不同小节样式的音符输入,以避免冗余代码;
  • 高音区与低音区的音高差了整整一个八度。这样就可以采用数组的形式来存储两个音轨,并通过循环变量i来辅助对低音区音轨降低一个八度;
  • 为了使得音乐结构复杂度的渐进性体现的更好,我对这八小节的内容进行了改编,使得前四小节仅有高音部分而删去了低音部分,函数内通过both参数来判断是否使用两个声部

中间部分的低音区样式:piano2_pattern函数

通过观察可以发现,在中间十六小节的音乐部分中,低音区仅仅有四种不同的样式,故可以通过以下函数来实现,其中pattern参数用于区分不同的样式:

    def piano2_pattern(self, pattern):track = self.mid.get_extended_track('Piano2')if pattern == 1:track.add_note([7, 4, 7], 1/4+1/8, alt=[0, 1, 0], base_num=[-1, -1, -2])track.add_note([4, 2, 5], 1/8+1/4, alt=[0, 0, 1], base_num=[-1, -1, -2])track.wait(1/4)elif pattern == 2:track.add_note([7, 7], 1/2, base_num=[-1, -2])track.add_note([4, 4], 1/2, base_num=[-1, -2], alt=[1, 1])elif pattern == 3:track.add_note([1, 1], 1/2, base_num=[0, -1], alt=[1, 1])track.add_note([4, 4], 1/2, base_num=[-1, -2], alt=[1, 1])elif pattern == 4:track.add_note([4, 7], 1/4, base_num=[-1, -2], alt=[1, 0])track.wait(3/4)

这一段代码包含了四个和弦,这一功能可以通过将add_note函数的第一个参数设置为数组来实现,若每个组成音的长度、所在八度区域、升降号不同的话,也可以将后面的参数设置成数组形式,并保证数组中参数的顺序相互对应。

中间部分的高音区段落:piano1_paragraph函数

中间十六小节的高音部分在整篇音乐中是最为复杂的,也是最难以实现的一部分,博主是一个一个音符来。听过之后可以发现,这一段音乐是四小节为单位进行划分的,即每四小节音乐的大体行进结构是相同的,故我们使用paragraph这个参数来区分不同的段落(由于最后四小节的内容明显出现了不协调,我对其进行了微调,故与原曲谱有一定的出入)。
函数整体由于体量太大,在此不单独给出,如果想参考的话请移步二百行代码板块,或者参考Github上的文件。

主函数

同上一篇文章一样,主函数用于调用write函数来编写音乐,并对音乐进行保存和播放。若运行成功则会立即播放音乐,并在之前选择的保存目录中找到该MIDI文件。

if __name__ == '__main__':golden_wind = GoldenWind()golden_wind.write()golden_wind.mid.save_midi()golden_wind.mid.play_it()

生成的 golden_wind.mid文件 在 MidiEditor 中打开界面如下:
在这里插入图片描述

结语

如果您对本文使用的 MidiFileExtended 类感兴趣,或者想进一步了解该类的话,请在下方评论或私信联系我,也欢迎大家对的文章提出质疑和改进方法。如您在实现本文章中提到的任何内容时遇到任何困难,请及时在下方评论或私信联系我!
最后,欢迎大家查看本专题下其他博文内容,十分感谢您的耐心阅读!

Python编曲实践(一):通过Mido和PyGame来编写和播放单轨MIDI文件
Python编曲实践(二):和弦的实现和进行
Python编曲实践(三):如何模拟“弯音轮”实现滑音和颤音效果
Python编曲实践(四):向MIDI文件中添加鼓组音轨
Python编曲实践(五):通过编写爬虫来爬取海量MIDI文件,预备构建数据集(附有百度云下载链接)
Python编曲实践(六):将MIDI文件转化成矩阵,继承PyTorch的Dataset类来构建数据集(附数据集网盘下载链接)
Python编曲实践(七):整整一百行Python代码写出黑人抬棺梗曲《Astronomia》的旋律

这篇关于Python编曲实践(八):我,乔鲁诺·乔巴那,能用两百行代码写出JOJO黄金之风里我自己的出场曲!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/927550

相关文章

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该