美文网首页
python matplotlib 频谱图等获取图片像素点数据

python matplotlib 频谱图等获取图片像素点数据

作者: 牛奶大泡芙 | 来源:发表于2022-09-22 12:18 被阅读0次

    python开发中,常使用绘图工具matplotlib绘制带有坐标轴或者colorbar的图像,比如下图是结合librosa进行频谱分析的梅尔频谱图。
    当matplotlib绘制的图形只有部分是我们需要的,或者当我们没有条件将其生成的图像保存到本地,需要将像素数据放入内存中时,就会想到是否有一种办法可以将matplotlib绘制的图片部分/全部像素点提取出来,以备使用。
    下面按照小编的思维方式介绍一下走弯路的过程,如果需要直接查看正确答案,移步至文章最后的代码,如有需要勘误之处,请移步评论区。
    首先介绍业务需求,基于如下的代码,将频谱图中的频谱窗口(中间的绘图部分,不包括外侧色度条、空白区域和坐标轴)的像素点提取出来,且不能保存图像到本地进行二次读取操作(没有保存到本地的条件,需要到其他环境中运行,只能在内存中操作):

    import matplotlib.pyplot as plt
    import librosa
    s = librosa.feature.melspectrogram(y=x, sr=fs)
    fig, ax = plt.subplots()
    s1 = librosa.power_to_db(s, ref=np.max)
    img = librosa.display.specshow(s1, x_axis=xxx, y_axis='mel', sr=fs, ax=ax)
    # 生成右侧的渐变色板条
    fig.colorbar(img, ax=ax, format='%+2.0f dB')
    

    保存到本地的图片如下:


    频谱图.jpg

    第一次尝试,想到了plt.subplots()或者librosa.display.specshow()返回的对象是否提供rgb三通道像素点的API?反复查看了几遍,发现有一个get_array(),获取代码如下:

    img = librosa.display.specshow(S_dB, x_axis='time', y_axis='mel', sr=fs, ax=ax)
    a = img.get_array()
    

    a其实就是s1,并不是什么像素点了。
    第二次尝试,想到是否可以定位到频谱区域的位置进行截图,丢弃坐标、色板条和空白部分。
    考虑使用PIL的截图API,具体代码如下:

    imageObject = Image.open(‘xxx’)
    cropped = imageObject.crop((x1,y1,x2,y2))
    cropped.save('xxx')
    

    (x1,y1)和(x2,y2)分别是截图区域的左上角和右下角,但是发现,需要将图像存储到本地,不符合小编的要求。
    需要注意的是,librosa 的所有绘图功能都依赖于 matplotlib,一般需要导入 matplotlib 的 pyplot API。
    最后查阅资料发现,可以通过matplotlib.figure的canvas获取像素数据,具体地通过tostring_rgb()方法,查看tostring_rgb()方法的源码,发现仅有一行:

    return np.asarray(self._renderer).take([0, 1, 2], axis=2).tobytes()
    

    可以见得,是将self._renderer中的部分维度、部分位置的数据提取出来了,并不是直接解析某对象得到的。
    由于目前还需要对像素数据进行截取,所以确定了[57:429,79:578:,]这个截取范围,至于确定范围的方法,需要根据图像调整,但是基本可以确定的是,在前面绘图参数和数据的尺寸不变的情况下,频谱图窗口的位置相对于整个图像是不变的,也就是说截取范围可以不变。
    最后为了准确,使用PIL Image对象,将像素点填充进去,并show()出来,以查看截取的部分是否能够正常成像。

    import matplotlib.pyplot as plt
    from PIL import Image
    import librosa
    def test():
        # ---------原始频谱图------------------
        # 1、加载音频文件的时域信号、采样率到内存中
        # 2、根据时域信号、采样率等参数生成梅尔频谱
        s = librosa.feature.melspectrogram(y=x, sr=fs)
        # 3、matplotlib构建figure和一组子图,返回图形(matplotlib.figure)和坐标轴(matplotlib.axes.Axes)
        fig, ax = plt.subplots()
        # 4、频谱转成分贝单位的值
        S_dB = librosa.power_to_db(s, ref=np.max)
        # 5、显示频谱图像
        img = librosa.display.specshow(S_dB, x_axis='time', y_axis='mel', sr=fs, ax=ax)
        # ---------利用canvas对象获取像素点------------------
        # 6、绘制figure的边界框
        fig.canvas.draw()
        # 7、提取rgb数据,返回字节流对象
        buf = fig.canvas.tostring_rgb()
        # 8、获取canvas的宽度、高度
        ncols, nrows = fig.canvas.get_width_height()
        # 9、PIL创建图片对象,确定要截取的区域像素点的数量,这里是499×372
        im = Image.new("RGB",(499,372))
        # 10、将字节流转成numpy数组,并形状重置
        d = np.fromstring(buf, dtype=np.uint8).reshape(nrows, ncols, 3)
        # 11、截取目标区域的rgb像素点
        dd = d[57:429,79:578:,] 
        # ---------测试:查看像素点对应的图像------------------
        # 12、赋值给图片对象的像素点,每一个像素点都由rgb三通道组成
        for i in range(0,372):
            for j in range(0,499):
                im.putpixel((j,i),(int(dd[i][j][0]),int(dd[i][j][1]),int(dd[i][j][2])))
        # 13、查看截取的图像
        im.show()
    

    相关文章

      网友评论

          本文标题:python matplotlib 频谱图等获取图片像素点数据

          本文链接:https://www.haomeiwen.com/subject/cblwortx.html