基于随机森林+RNN+Tensorflow-Magenta的根据图片情感智能生成音乐系统——深度学习算法应用(含python、ipynb工程源码)+所有数据集(一)


在这里插入图片描述

前言

本项目基于Google的Magenta平台,它采用随机森林分类器来识别图片的情感色彩,接着项目使用递归神经网络(RNN)来生成与图片情感相匹配的音乐,最后通过图形用户界面(GUI)实现可视化结果展示。

首先,项目处理图片,使用随机森林分类器来确定图片中的情感色彩。这可以包括情感分类,如欢快、宁静等。该分类器会分析图片的视觉特征,以确定其中蕴含的情感。

随后,根据图片中的情感,项目使用递归神经网络(RNN)生成与情感相匹配的音乐。这个过程涉及到选定特定音符、节奏和和声,以创造出与图片情感相一致的音乐作品。

最后,项目通过图形用户界面(GUI)将图片、情感色彩、生成的音乐等结果以可视化方式呈现给用户。

总之,这个项目结合了计算机视觉、音乐生成和图形用户界面设计,旨在将图片的情感色彩与音乐创作相融合,为用户提供一种独特的艺术体验。这对于艺术和技术的交叉应用可能非常引人注目。

总体设计

本部分包括系统整体结构图和系统流程图。

系统整体结构图

系统整体结构如图所示。

在这里插入图片描述

系统流程图

系统流程如图所示。

在这里插入图片描述

运行环境

本部分包括 Python 环境和Magenta环境。

Python环境

需要Python 3.6及以上配置,在Windows环境下推荐下载Anaconda完成Python所需环境的配置,下载地址为https://www.anaconda.com/,也可下载虚拟机在Linux环境下运行代码。

鼠标右击“我的电脑”,单击“属性”,选择高级系统设置。单击“环境变量”,找到系统变量中的Path,单击“编辑”然后新建,将Python解释器所在路径粘贴并确定。

Magenta环境

安装指南为:https://github.com/tensorflow/magenta/blob/master/README.md#installation

Magenta支持Python 2.7以上版本与Python 3.5以上版本。对于Linux用户,Magenta官方给出了安装脚本,使用方法如下:

curl https://raw.githubusercontent.com/tensorflow/magenta/master/magenta/tools/magenta-install.sh>/tmp/magenta-install.sh
bash /tmp/magenta-install.sh

上述脚本安装完成后,在source activate magenta中打开Magenta运行环境。对于windows用户 (Linux用户也适用),也可使用Anaconda等包管理工具安装Magenta。

对于只使用CPU的用户:

conda create -n magenta python=3.6 #这里 3.5 也可以
activate magenta #(Linux 用户为 source activate magenta)
pip install magenta

Linux用户在安装Magenta之前需要先安装一些运行库,用以安装rimidi:

sudo apt-get install build-essential libasound2-dev libjack-dev

如果用到GPU,直接将上文中pip install magenta替换成pip install magenta-gpu。magenta 和magenta-gpu的区别仅在后者多了tensorfow-gpu库。

安装的TensorFlow版本为1.15.2。

模块实现

本项目包括3个模块:数据预处理、模型构建、模型训练及保存,下面分别给出各模块的功能介绍及相关代码。

1. 数据预处理

MIDI下载地址为http://midi.midicn.com/,图片在花瓣网收集获取地址为https://huaban.com/boards/60930738/。音乐模型包含欢快和安静两类MIDI文件各100个,图片包含欢快和安静两类各250张,格式为.jpg。另外,数据集也可以从本博客对应工程源码中下载。

(1)图片部分

提取图片中占比前十的色彩信息,将其转换成hsv格式,存储到.csv文件中,便于后续使用。

#-*- coding: utf-8 -*-
import sys
import csv
import traceback
import os
import pandas as pd
if sys.version_info < (3, 0):
    from urllib2 import urlopen
else:
    from urllib.request import urlopen
import io
from colorthief import ColorThief
def get_color(path):#获取图片的色彩信息
    def rgb2hsv(tp):#将转换成hsv,h是色相,s是饱和度,v是明度
        r,g,b=tp[0],tp[1],tp[2]
        r, g, b = r/255.0, g/255.0, b/255.0
        mx = max(r, g, b)
        mn = min(r, g, b)
        m = mx-mn
        if mx == mn:
            h = 0
        elif mx == r:
            if g >= b:
                h = ((g-b)/m)*60
            else:
                h = ((g-b)/m)*60 + 360
        elif mx == g:
            h = ((b-r)/m)*60 + 120
        elif mx == b:
            h = ((r-g)/m)*60 + 240
        if mx == 0:
            s = 0
        else:
            s = m/mx
        v = mx
        h,s,v = round(h,3),round(s,3),round(v,3) #保留小数点后三位
        h,s,v = str(h),str(s),str(v) #转成字符串类型能够写入csv
        return h,s,v
    fd = urlopen(path)
    f = io.BytesIO(fd.read())
    color_thief = ColorThief(f) #调用colortheif()函数
    mc=color_thief.get_color(quality=1)
#获取画面最主要颜色,不一定出现在画面中,是整体均值,一个元组(r,g,b)
    cp=color_thief.get_palette(quality=1) #获取调色盘
    hsv = rgb2hsv(mc)
    color_lists=[hsv] #用列表存储最主要颜色信息,[h,s,v]
    clist=[color_lists[0][0],color_lists[0][1],color_lists[0][2]]
#获取最主要颜色的h,s,v值
    for c in cp:#遍历调色盘中的颜色
        hp=rgb2hsv(c)
        color_lists.append(hp) #追加信息到列表中
        for i in hp: #获取每个色彩的h,s,v值
            clist.append(i)
    return clist
def to_csv(clist): #将色彩信息列表存储到.csv文件
    try:
        fpath = 'E:/college/synaes/image_csv/2.csv'
#存储色彩信息的.csv文件地址
        with open(fpath,'a',newline='')as f:
            writer = csv.writer(f)            #writer.writerow(["h0","s0","v0","h1","s1","v1","h2","s2","v2","h3","s3","v3","h4","s4","v4","h5","s5","v5","h6","s6","v6","h7","s7","v7","h8","s8","v8","h9","s9","v9","Label"])
            writer.writerow(clist)
    except:
        print(traceback())
#get_color_to_file("file:///E:/college/synaes/image_test/test2.jpg")
path="E:/college/synaes/image/happy" #图片存储路径
file_list=os.listdir(path)
for file in file_list: #遍历路径中的每张图片
    clist=get_color("file:///"+path+"/"+file)
    to_csv(clist)
# -*- coding: utf-8 -*-
#从图像中抓取调色板
__version__ = '0.2.1'
import math
from PIL import Image #导入pillow中的image模块
class cached_property(object):
    #创建的装饰器将单一参数的方法转换为缓存实例的属性
        def __init__(self, func):
        self.func = func
    def __get__(self, instance, type):
        res = instance.__dict__[self.func.__name__] = self.func(instance)
        return res
class ColorThief(object):
    #抓取调色的类
    def __init__(self, file):
        self.image = Image.open(file) #打开图像
    def get_color(self, quality=10):
    #获得主要的颜色,quality参数:1是最高的quality,数字越大,颜色返回越快
    #返回tuple:(r, g, b)元组类型
        palette = self.get_palette(5, quality)
        return palette[0]
    def get_palette(self, color_count=10, quality=10):
#获得调色盘,用中值切割算法聚类相似颜色
#参数color_count为调色盘的大小
#quality参数同上
#返回一个以(r,g,b)元组为元素的列表
        image = self.image.convert('RGBA')
        width, height = image.size
        pixels = image.getdata() #像素
        pixel_count = width * height
        valid_pixels = []
        for i in range(0, pixel_count, quality):
            r, g, b, a = pixels[i]
            #如果像素大部分是不透明的,而且不是白色的
            if a >= 125:
                if not (r > 250 and g > 250 and b > 250):
                    valid_pixels.append((r, g, b))
        #将数组传给quantize()函数,该函数对值进行聚类,使用中值切割算法
        cmap = MMCQ.quantize(valid_pixels, color_count)
        return cmap.palette
class MMCQ(object):
    #MMCQ基本是Python端口(改进的中值切割量化)
	#算法来自Leptonica库(http://www.leptonica.com/)
    SIGBITS = 5
    RSHIFT = 8 - SIGBITS
    MAX_ITERATION = 1000 #最大迭代次数
    FRACT_BY_POPULATIONS = 0.75
    @staticmethod
    def get_color_index(r, g, b):
        return (r << (2 * MMCQ.SIGBITS)) + (g << MMCQ.SIGBITS) + b
    @staticmethod
    def get_histo(pixels):
        histo = dict()
        for pixel in pixels:
            rval = pixel[0] >> MMCQ.RSHIFT
            gval = pixel[1] >> MMCQ.RSHIFT
            bval = pixel[2] >> MMCQ.RSHIFT
            index = MMCQ.get_color_index(rval, gval, bval)
            histo[index] = histo.setdefault(index, 0) + 1
        return histo
    @staticmethod
    def vbox_from_pixels(pixels, histo):
        rmin = 1000000
        rmax = 0
        gmin = 1000000
        gmax = 0
        bmin = 1000000
        bmax = 0
        for pixel in pixels:
            rval = pixel[0] >> MMCQ.RSHIFT
            gval = pixel[1] >> MMCQ.RSHIFT
            bval = pixel[2] >> MMCQ.RSHIFT
            rmin = min(rval, rmin)
            rmax = max(rval, rmax)
            gmin = min(gval, gmin)
            gmax = max(gval, gmax)
            bmin = min(bval, bmin)
            bmax = max(bval, bmax)
        return VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo)
    @staticmethod
    def median_cut_apply(histo, vbox): #中值切割
        if not vbox.count:
            return (None, None)
        rw = vbox.r2 - vbox.r1 + 1
        gw = vbox.g2 - vbox.g1 + 1
        bw = vbox.b2 - vbox.b1 + 1
        maxw = max([rw, gw, bw])
        #如果只有一个像素,不进行切割
        if vbox.count == 1:
            return (vbox.copy, None)
        #沿着选定的轴查找数组
        total = 0
        sum_ = 0
        partialsum = {}
        lookaheadsum = {}
        do_cut_color = None
        if maxw == rw:
            do_cut_color = 'r'
            for i in range(vbox.r1, vbox.r2+1):
                sum_ = 0
                for j in range(vbox.g1, vbox.g2+1):
                    for k in range(vbox.b1, vbox.b2+1):
                        index = MMCQ.get_color_index(i, j, k)
                        sum_ += histo.get(index, 0)
                total += sum_
                partialsum[i] = total
        elif maxw == gw:
            do_cut_color = 'g'
            for i in range(vbox.g1, vbox.g2+1):
                sum_ = 0
                for j in range(vbox.r1, vbox.r2+1):
                    for k in range(vbox.b1, vbox.b2+1):
                        index = MMCQ.get_color_index(j, i, k)
                        sum_ += histo.get(index, 0)
                total += sum_
                partialsum[i] = total
        else:  #maxw == bw
            do_cut_color = 'b'
            for i in range(vbox.b1, vbox.b2+1):
                sum_ = 0
                for j in range(vbox.r1, vbox.r2+1):
                    for k in range(vbox.g1, vbox.g2+1):
                        index = MMCQ.get_color_index(j, k, i)
                        sum_ += histo.get(index, 0)
                total += sum_
                partialsum[i] = total
        for i, d in partialsum.items():
            lookaheadsum[i] = total - d
        #确定切割平面
        dim1 = do_cut_color + '1'
        dim2 = do_cut_color + '2'
        dim1_val = getattr(vbox, dim1)
        dim2_val = getattr(vbox, dim2)
        for i in range(dim1_val, dim2_val+1):
            if partialsum[i] > (total / 2):
                vbox1 = vbox.copy
                vbox2 = vbox.copy
                left = i - dim1_val
                right = dim2_val - i
                if left <= right:
                    d2 = min([dim2_val - 1, int(i + right / 2)])
                else:
                    d2 = max([dim1_val, int(i - 1 - left / 2)])
                while not partialsum.get(d2, False):
                    d2 += 1
                count2 = lookaheadsum.get(d2)
                while not count2 and partialsum.get(d2-1, False):
                    d2 -= 1
                    count2 = lookaheadsum.get(d2)
                #设置维度
                setattr(vbox1, dim2, d2)
                setattr(vbox2, dim1, getattr(vbox1, dim2) + 1)
                return (vbox1, vbox2)
        return (None, None)
    @staticmethod
    def quantize(pixels, max_color): #将颜色进行量化
        #参数pixels是一个以(r,g,b)形式的像素列表
        #参数max_color是颜色的最大数量
        if not pixels:
            raise Exception('Empty pixels when quantize.')
        if max_color < 2 or max_color > 256:
            raise Exception('Wrong number of max colors when quantize.')
        histo = MMCQ.get_histo(pixels)
        #检查是否低于maxcolors
        if len(histo) <= max_color:
         #从histo生成新的颜色并返回
            pass
         #从颜色重新获取起始vbox
        vbox = MMCQ.vbox_from_pixels(pixels, histo)
        pq = PQueue(lambda x: x.count)
        pq.push(vbox)
        #实现迭代的内部函数
        def iter_(lh, target):
            n_color = 1
            n_iter = 0
            while n_iter < MMCQ.MAX_ITERATION:
                vbox = lh.pop()
                if not vbox.count:  #返回
                    lh.push(vbox)
                    n_iter += 1
                    continue
                #实现切割
                vbox1, vbox2 = MMCQ.median_cut_apply(histo, vbox)
                if not vbox1:
                    raise Exception("vbox1 not defined; shouldn't happen!")
                lh.push(vbox1)
                if vbox2:  #vbox2可以是null
                    lh.push(vbox2)
                    n_color += 1
                if n_color >= target:
                    return
                if n_iter > MMCQ.MAX_ITERATION:
                    return
                n_iter += 1
        #第一组颜色,按数量排序
        iter_(pq, MMCQ.FRACT_BY_POPULATIONS * max_color)
        #按像素占用率乘以色彩空间大小的乘积重新排序
        pq2 = PQueue(lambda x: x.count * x.volume)
        while pq.size():
            pq2.push(pq.pop())
     #下一组使用(npix * vol)排序生成中值切割
        iter_(pq2, max_color - pq2.size())
     #计算实际颜色
        cmap = CMap()
        while pq2.size():
            cmap.push(pq2.pop())
        return cmap
class VBox(object):
    #3D颜色空间
    def __init__(self, r1, r2, g1, g2, b1, b2, histo):
        self.r1 = r1
        self.r2 = r2
        self.g1 = g1
        self.g2 = g2
        self.b1 = b1
        self.b2 = b2
        self.histo = histo
    @cached_property
    def volume(self):
        sub_r = self.r2 - self.r1
        sub_g = self.g2 - self.g1
        sub_b = self.b2 - self.b1
        return (sub_r + 1) * (sub_g + 1) * (sub_b + 1)
    @property
    def copy(self):
        return VBox(self.r1, self.r2, self.g1, self.g2,
                    self.b1, self.b2, self.histo)
    @cached_property
    def avg(self):
        ntot = 0
        mult = 1 << (8 - MMCQ.SIGBITS)
        r_sum = 0
        g_sum = 0
        b_sum = 0
        for i in range(self.r1, self.r2 + 1):
            for j in range(self.g1, self.g2 + 1):
                for k in range(self.b1, self.b2 + 1):
                    histoindex = MMCQ.get_color_index(i, j, k)
                    hval = self.histo.get(histoindex, 0)
                    ntot += hval
                    r_sum += hval * (i + 0.5) * mult
                    g_sum += hval * (j + 0.5) * mult
                    b_sum += hval * (k + 0.5) * mult
        if ntot:
            r_avg = int(r_sum / ntot)
            g_avg = int(g_sum / ntot)
            b_avg = int(b_sum / ntot)
        else:
            r_avg = int(mult * (self.r1 + self.r2 + 1) / 2)
            g_avg = int(mult * (self.g1 + self.g2 + 1) / 2)
            b_avg = int(mult * (self.b1 + self.b2 + 1) / 2)
        return r_avg, g_avg, b_avg
    def contains(self, pixel):
        rval = pixel[0] >> MMCQ.RSHIFT
        gval = pixel[1] >> MMCQ.RSHIFT
        bval = pixel[2] >> MMCQ.RSHIFT
        return all([
            rval >= self.r1,
            rval <= self.r2,
            gval >= self.g1,
            gval <= self.g2,
            bval >= self.b1,
            bval <= self.b2,
        ])
    @cached_property
    def count(self):
        npix = 0
        for i in range(self.r1, self.r2 + 1):
            for j in range(self.g1, self.g2 + 1):
                for k in range(self.b1, self.b2 + 1):
                    index = MMCQ.get_color_index(i, j, k)
                    npix += self.histo.get(index, 0)
        return npix
class CMap(object):
    #颜色图
    def __init__(self):
        self.vboxes = PQueue(lambda x: x['vbox'].count * x['vbox'].volume)
    @property
    def palette(self):
        return self.vboxes.map(lambda x: x['color'])
    def push(self, vbox):
        self.vboxes.push({
            'vbox': vbox,
            'color': vbox.avg,
        })
    def size(self):
        return self.vboxes.size()
    def nearest(self, color):
        d1 = None
        p_color = None
        for i in range(self.vboxes.size()):
            vbox = self.vboxes.peek(i)
            d2 = math.sqrt(
                math.pow(color[0] - vbox['color'][0], 2) +
                math.pow(color[1] - vbox['color'][1], 2) +
                math.pow(color[2] - vbox['color'][2], 2)
            )
            if d1 is None or d2 < d1:
                d1 = d2
                p_color = vbox['color']
        return p_color
    def map(self, color):
        for i in range(self.vboxes.size()):
            vbox = self.vboxes.peek(i)
            if vbox['vbox'].contains(color):
                return vbox['color']
        return self.nearest(color)
class PQueue(object):
    #简单优先级队列
    def __init__(self, sort_key):
        self.sort_key = sort_key
        self.contents = []
        self._sorted = False
    def sort(self):
        self.contents.sort(key=self.sort_key)
        self._sorted = True
    def push(self, o):
        self.contents.append(o)
        self._sorted = False
    def peek(self, index=None):
        if not self._sorted:
            self.sort()
        if index is None:
            index = len(self.contents) - 1
        return self.contents[index]
    def pop(self):
        if not self._sorted:
            self.sort()
        return self.contents.pop()
    def size(self):
        return len(self.contents)
    def map(self, f):
        return list(map(f, self.contents))

相关其它博客

基于随机森林+RNN+Tensorflow-Magenta的根据图片情感智能生成音乐系统——深度学习算法应用(含python、ipynb工程源码)+所有数据集(二)

基于随机森林+RNN+Tensorflow-Magenta的根据图片情感智能生成音乐系统——深度学习算法应用(含python、ipynb工程源码)+所有数据集(三)

基于随机森林+RNN+Tensorflow-Magenta的根据图片情感智能生成音乐系统——深度学习算法应用(含python、ipynb工程源码)+所有数据集(四)

工程源代码下载

详见本人博客资源下载页


其它资料下载

如果大家想继续了解人工智能相关学习路线和知识体系,欢迎大家翻阅我的另外一篇博客《重磅 | 完备的人工智能AI 学习——基础知识学习路线,所有资料免关注免套路直接网盘下载
这篇博客参考了Github知名开源平台,AI技术平台以及相关领域专家:Datawhale,ApacheCN,AI有道和黄海广博士等约有近100G相关资料,希望能帮助到所有小伙伴们。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小胡说人工智能

谢谢老板打赏,祝您天天好心情!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值