python matplotlib 頻譜圖等獲取圖片像素點(diǎn)數(shù)據(jù)

python開發(fā)中,常使用繪圖工具matplotlib繪制帶有坐標(biāo)軸或者colorbar的圖像,比如下圖是結(jié)合librosa進(jìn)行頻譜分析的梅爾頻譜圖。
當(dāng)matplotlib繪制的圖形只有部分是我們需要的,或者當(dāng)我們沒有條件將其生成的圖像保存到本地,需要將像素?cái)?shù)據(jù)放入內(nèi)存中時(shí),就會(huì)想到是否有一種辦法可以將matplotlib繪制的圖片部分/全部像素點(diǎn)提取出來,以備使用。
下面按照小編的思維方式介紹一下走彎路的過程,如果需要直接查看正確答案,移步至文章最后的代碼,如有需要勘誤之處,請(qǐng)移步評(píng)論區(qū)。
首先介紹業(yè)務(wù)需求,基于如下的代碼,將頻譜圖中的頻譜窗口(中間的繪圖部分,不包括外側(cè)色度條、空白區(qū)域和坐標(biāo)軸)的像素點(diǎn)提取出來,且不能保存圖像到本地進(jìn)行二次讀取操作(沒有保存到本地的條件,需要到其他環(huán)境中運(yùn)行,只能在內(nèi)存中操作):

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)
# 生成右側(cè)的漸變色板條
fig.colorbar(img, ax=ax, format='%+2.0f dB')

保存到本地的圖片如下:


頻譜圖.jpg

第一次嘗試,想到了plt.subplots()或者librosa.display.specshow()返回的對(duì)象是否提供rgb三通道像素點(diǎn)的API?反復(fù)查看了幾遍,發(fā)現(xiàn)有一個(gè)get_array(),獲取代碼如下:

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

a其實(shí)就是s1,并不是什么像素點(diǎn)了。
第二次嘗試,想到是否可以定位到頻譜區(qū)域的位置進(jìn)行截圖,丟棄坐標(biāo)、色板條和空白部分。
考慮使用PIL的截圖API,具體代碼如下:

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

(x1,y1)和(x2,y2)分別是截圖區(qū)域的左上角和右下角,但是發(fā)現(xiàn),需要將圖像存儲(chǔ)到本地,不符合小編的要求。
需要注意的是,librosa 的所有繪圖功能都依賴于 matplotlib,一般需要導(dǎo)入 matplotlib 的 pyplot API。
最后查閱資料發(fā)現(xiàn),可以通過matplotlib.figure的canvas獲取像素?cái)?shù)據(jù),具體地通過tostring_rgb()方法,查看tostring_rgb()方法的源碼,發(fā)現(xiàn)僅有一行:

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

可以見得,是將self._renderer中的部分維度、部分位置的數(shù)據(jù)提取出來了,并不是直接解析某對(duì)象得到的。
由于目前還需要對(duì)像素?cái)?shù)據(jù)進(jìn)行截取,所以確定了[57:429,79:578:,]這個(gè)截取范圍,至于確定范圍的方法,需要根據(jù)圖像調(diào)整,但是基本可以確定的是,在前面繪圖參數(shù)和數(shù)據(jù)的尺寸不變的情況下,頻譜圖窗口的位置相對(duì)于整個(gè)圖像是不變的,也就是說截取范圍可以不變。
最后為了準(zhǔn)確,使用PIL Image對(duì)象,將像素點(diǎn)填充進(jìn)去,并show()出來,以查看截取的部分是否能夠正常成像。

import matplotlib.pyplot as plt
from PIL import Image
import librosa
def test():
    # ---------原始頻譜圖------------------
    # 1、加載音頻文件的時(shí)域信號(hào)、采樣率到內(nèi)存中
    # 2、根據(jù)時(shí)域信號(hào)、采樣率等參數(shù)生成梅爾頻譜
    s = librosa.feature.melspectrogram(y=x, sr=fs)
    # 3、matplotlib構(gòu)建figure和一組子圖,返回圖形(matplotlib.figure)和坐標(biāo)軸(matplotlib.axes.Axes)
    fig, ax = plt.subplots()
    # 4、頻譜轉(zhuǎn)成分貝單位的值
    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對(duì)象獲取像素點(diǎn)------------------
    # 6、繪制figure的邊界框
    fig.canvas.draw()
    # 7、提取rgb數(shù)據(jù),返回字節(jié)流對(duì)象
    buf = fig.canvas.tostring_rgb()
    # 8、獲取canvas的寬度、高度
    ncols, nrows = fig.canvas.get_width_height()
    # 9、PIL創(chuàng)建圖片對(duì)象,確定要截取的區(qū)域像素點(diǎn)的數(shù)量,這里是499×372
    im = Image.new("RGB",(499,372))
    # 10、將字節(jié)流轉(zhuǎn)成numpy數(shù)組,并形狀重置
    d = np.fromstring(buf, dtype=np.uint8).reshape(nrows, ncols, 3)
    # 11、截取目標(biāo)區(qū)域的rgb像素點(diǎn)
    dd = d[57:429,79:578:,] 
    # ---------測(cè)試:查看像素點(diǎn)對(duì)應(yīng)的圖像------------------
    # 12、賦值給圖片對(duì)象的像素點(diǎn),每一個(gè)像素點(diǎn)都由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()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容