def filterByBasefrq(src, basefrq, width): peaksPos = findpeaks(src, spacing=50, limit=max(src) * 0.05) peaks = src[peaksPos] # 峰值大小 tar = np.copy(src) num = min(int(len(src) / basefrq), 30) for i in np.arange(num): frq = i * basefrq tar[frq - width:frq + width] = min(src[frq - width], src[frq + width]) return tar
def subpeakAmpLimiting(dataClip, space, limit): peaks = findpeaks(dataClip, spacing=space, limit=max(dataClip) * limit) if len(peaks) == 0: return dataClip dots = np.copy(dataClip[peaks]) dots[np.argmax(dots)] = 0 maxval = max(dots) # 如果等于0 说明只有一个峰 if maxval > 0: # dataClip = 3 np.where(dataClip < maxval, dataClip, maxval) return dataClip
def subpeak_amplimiting(dataclip, space, limit): """ :param dataclip:输入 :param space:寻峰宽度 :param limit:寻峰阈值 :return:次高峰值限制幅度结果 """ peaks = findpeaks(dataclip, spacing=space, limit=max(dataclip) * limit) if len(peaks) == 0: return dataclip dots = np.copy(dataclip[peaks]) dots[np.argmax(dots)] = 0 maxval = max(dots) # 如果等于0 说明只有一个峰 if maxval > 0: dataclip = np.where(dataclip < maxval, dataclip, maxval) return dataclip
def getPitch(dataClip,Fs,nfft): pitch=0 dataClip[0:int(30*nfft/Fs)]=0 #print(frame*nfft/Fs) #当前时刻 fftData=fft(dataClip)[0:int(len(dataClip)/2)] if showTestView: plt.subplot(121) plt.plot(np.arange(len(dataClip)), dataClip) cepstrum=MaxMinNormalization(np.abs(fftData)*2/nfft,0,1) cutoff=int(Fs/2/1200) #cepstrum[0:cutoff]=0 cepstrum=MaxMinNormalization(cepstrum,0,1) if showTestView: plt.subplot(122) plt.plot(np.arange(len(cepstrum)), cepstrum) length=len(cepstrum) peakind = signal.find_peaks_cwt(cepstrum, np.arange(1,20)) peakind2=findpeaks(cepstrum, spacing=15, limit=0.05) if showTestView==1: print(peakind) print(peakind2) # 把 corlor 设置为空,通过edgecolors来控制颜色 if showTestView==1: plt.scatter(peakind2, cepstrum[peakind2], color='', marker='o', edgecolors='r', s=100) peaks=[] pos=cutoff maxpos=np.argmax(cepstrum[pos:-1])+pos#最高峰位置 #检测半峰 simiPos=int(maxpos/2) rad=int(simiPos/2) simiMax=np.argmax(cepstrum[simiPos-rad:simiPos+rad])+simiPos-rad#半峰位置 bias=abs(simiPos-simiMax)/rad#偏离度10% if bias<0.2: #半峰与最高峰之间的最小值 simiMin=np.min(cepstrum[simiMax:maxpos]) #与半峰最小值差值 dist=cepstrum[simiMax]-simiMin #差值比例 dist=dist/(cepstrum[maxpos]-simiMin) if(dist>0.66): #1 2 #return Fs/2/simiMax// #四分检测 fourPos1=int(simiMax/2)#四分之一峰 p0=fourPos1 Xi=np.array([2,4]) Yi=np.array([simiMax,maxpos]) para=leastsq(error,p0,args=(Xi,Yi)) #把error函数中除了p以外的参数打包到args中 fourPos1=int(para[0]) fourPos2=int(para[0]*3) rad=int(fourPos1/2) fourMax1=np.argmax(cepstrum[fourPos1-rad:fourPos1+rad])+fourPos1-rad#四分峰位置1 bias=abs(fourPos1-fourMax1)/rad#偏离度10% if bias<0.2: #半峰与四分峰之间的最小值 fouMin1=np.min(cepstrum[fourMax1:simiMax]) #与半峰最小值差值 dist=cepstrum[fourMax1]-fouMin1 #差值比例 dist=dist/(cepstrum[simiMax]-fouMin1) if(dist>0.66): #寻找四分3峰 fourMax2=np.argmax(cepstrum[fourPos2-rad:fourPos2+rad])+fourPos2-rad#四分峰位置1 bias=abs(fourPos2-fourMax2)/rad#偏离度10% if bias<0.2: #添加四分峰 peaks.append([len(peaks)+1,fourMax1]) peaks.append([len(peaks)+1,simiMax]) peaks.append([len(peaks)+1,fourMax2]) if len(peaks)<1: peaks.append([len(peaks)+1,simiMax]) #不存在半峰才检测三分峰 if len(peaks)<1: thd1=0#第一个三分峰的位置 thd2=0#第二个三分峰的位置 #三分峰值检测 thrPos=int(maxpos*2/3) rad=int((maxpos-simiPos)/2) thrMax=np.argmax(cepstrum[thrPos-rad:thrPos+rad])+thrPos-rad#三分峰位置 bias=abs(thrPos-thrMax)/rad#偏离度10% if bias<0.2: #2/3峰与最大峰之间的最小数 thrMin=np.min(cepstrum[thrMax:maxpos]) #与最小值差值 dist=cepstrum[thrMax]-thrMin #差值比例 dist=dist/(cepstrum[maxpos]-thrMin) if(dist>0.66): #return Fs/2/simiMax #寻找1 如果寻找不到1 则取消2 thd1=thrMax #三分峰2检测 (做回归) p0=maxpos/3 Xi=np.array([2,3]) Yi=np.array([thd1,maxpos]) para=leastsq(error,p0,args=(Xi,Yi)) #把error函数中除了p以外的参数打包到args中 thrPos2=int(para[0]*1) rad=int((thd1-thrPos2)/2) #print(rad) #print(thrPos2) thrMax2=np.argmax(cepstrum[thrPos2-rad:thrPos2+rad])+thrPos2-rad#三分峰位置 bias=abs(thrPos2-thrMax2)/rad#偏离度10% #print(bias) if bias<0.2: #与2/3峰之间的最小值 thrMin2=np.min(cepstrum[thrMax2:thrMax]) #与最小值差值 dist=cepstrum[thrMax]-thrMin2 #差值比例 dist=dist/(cepstrum[thrMax2]-thrMin2) if(dist>0.66): thd2=thrMax2 else: thd1=0 thd2=0 else: #1: #return Fs/2/maxpos thd1=0 thd2=0 if thd1>0: peaks.append([len(peaks)+1,thd2]) peaks.append([len(peaks)+1,thd1]) #print(thd1) #print(thd2) peaks.append([len(peaks)+1,maxpos]) #每做一次线性回归 找下一个峰,直至找出右侧所有峰位置 getNextPeaks(peaks,cepstrum,0.1,0.25,0)#递归求右侧波峰 lenPeaks=len(peaks) if showTestView: if lenPeaks>0: print(peaks) rs=[] if lenPeaks>1: #线性拟合求基频,根据找到的所有二次fft峰求二次fft的基频,此基频求倒数然后乘以采样率就是原始信号的基频,对原始信号的高频部分分辨率不足 #,但原始信号高频部分在第一次fft中有好的频率分辨率,因此后期可以弥补 #harmonicIDs=np.arange(lenPeaks)+1#设置波峰ID p0=peaks[lenPeaks-1][1]/lenPeaks#初始化参数 Xi=np.array(peaks)[:,0] Yi=np.array(peaks)[:,1] para=leastsq(error,p0,args=(Xi,Yi)) #把error函数中除了p以外的参数打包到args中 peaksfindWidth=int(nfft/2/para[0]*0.6) peakindofFFT=findpeaks(dataClip, spacing=peaksfindWidth, limit=max(dataClip[int(30*nfft/Fs):-1])/10) if showTestView==1: plt.subplot(121) plt.scatter(peakindofFFT, dataClip[peakindofFFT], color='', marker='o', edgecolors='r', s=100) plt.show() rs= [Fs/2/para[0],Fs/2/peaks[0][1]] else: if lenPeaks==1: if showTestView==1: plt.show() rs= [0,0] else: plt.show() rs= [0,0] return rs
def getPitchDeScan(dataClip, Fs, nfft, showTestView): pitch = 0 dataClip[0:int(30 * nfft / Fs)] = 0 dataClip = subpeakAmpLimiting(dataClip, int(30.0 / Fs * nfft), 0.1) # 次峰限幅 if showTestView: plt.subplot(231) plt.plot(np.arange(len(dataClip)), dataClip, label='amp-frq') lowCutoff = int(40 * 441000.0 / Fs) # 最低截止频率对应的坐标 highCutoff = int(1400 * 441000.0 / Fs) # 最高截止频率对应的坐标 peakSearchPixes = int(3 * 441000 / Fs) # 寻峰间距 peakSearchAmp = 0.1 # 寻峰高度 # 线性内插重新采样 processingX = np.arange(0, min(int( nfft / Fs * 4000), len(dataClip))) # 最大采集到4000Hz,不包括最大值,此处为尚未重采样的原始频谱 processingY = dataClip[processingX] # 重采样的fft lenProcessingX = len(processingX) # 待处理频谱长度 finterp = interp1d(processingX, processingY, kind='linear') # 线性内插配置 x_pred = np.linspace( 0, processingX[lenProcessingX - 1] * 1.0, int(processingX[lenProcessingX - 1] * 441000 / nfft) + 1) maxProcessingX = x_pred[len(x_pred) - 1] resampY = finterp(x_pred) lenResampY = len(resampY) # print(len(resampY)) if showTestView == 1: plt.subplot(232) plt.plot(np.arange(len(resampY)), resampY, label='rs_Amp-frq') maxResampY = max(resampY[lowCutoff:-1]) / 2 # 待测频率内的最大值 # 测试梳状变换 # pixes=10 num = highCutoff # 栅栏变换后的长度 combTrans = np.zeros(num) # 存放栅栏变换的结果 indexComb = np.arange(lowCutoff, highCutoff, 0.1) # 栅栏变换索引 for k in np.arange(1, highCutoff, 1): combTrans[k] = np.sum( [resampY[i] for i in np.arange(0, lenResampY - 1, k)]) if showTestView == 1: plt.subplot(233) plt.plot(np.arange(len(combTrans)), combTrans, label='combTrans') deSamp = [combTrans[m] for m in np.arange(0, len(combTrans), 10)] # 10倍数降采样 deSamp[0] = 1000 if showTestView == 1: plt.subplot(234) plt.plot(np.arange(len(deSamp)), deSamp, label='DsCombTrans') baseLine = getBaseLineFromScan(deSamp, num) # 通过扫描线算法求基准曲线 if showTestView == 1: plt.subplot(235) plt.plot(np.arange(len(baseLine)), baseLine, label='baseline') trueTrans = combTrans - baseLine trueTrans[0:lowCutoff] = 0 if showTestView == 1: plt.subplot(236) plt.plot(np.arange(len(trueTrans)), trueTrans, label='trueTrans') if (sum(dataClip) < 1): return [0 / 10.0, resampY, trueTrans] pitch = max(trueTrans) / sum(dataClip) # 频率采样变换后寻峰 combTransPeaks = findpeaks(trueTrans, spacing=peakSearchPixes, limit=max(trueTrans) * peakSearchAmp) peaks = trueTrans[combTransPeaks] # 峰值大小 if (len(peaks) == 0): return [0 / 10.0, resampY, trueTrans] maxindex = np.argmax(peaks) maxfrq = combTransPeaks[maxindex] # 最高峰值位置 # 寻找1hz以内的最大的峰 combThr = 6 * 441000 / Fs # 寻峰宽度 decThr = 0.65 # 递减阈值 preBaseFrq = maxfrq for n in range(2, 10, 1): newfrq = getNearPeaks(trueTrans, combTransPeaks, peaks, n * maxfrq, combThr, preBaseFrq, decThr, showTestView) if (newfrq > 0): preBaseFrq = newfrq else: continue pitch = preBaseFrq if showTestView == 1: plt.scatter(combTransPeaks, trueTrans[combTransPeaks], color='', marker='o', edgecolors='r', s=100) plt.show() return [pitch / 10.0, resampY, trueTrans]
def getpitch(self, dataclip, fs, nfft, showtestview): """ 音高估计 :param dataclip:输入fft :param fs:采样率 :param nfft:窗口大小 :param showtestview:是否显示figure :return:频率,插值采样后的输入,梳状统计向量 """ dataclip[0:int(30 * nfft / fs)] = 0 dataclip = BaseFrqDetector.subpeak_amplimiting(dataclip, int(30.0 / fs * nfft), 0.1) # 次峰限幅 lowcutoff = int(32.5 * 441000.0 / fs) # 最低截止频率对应的坐标 highcutoff = int(1400 * 441000.0 / fs) # 最高截止频率对应的坐标 peaksearchpixes = int(3 * 441000 / fs) # 寻峰间距 peaksearchamp = 0.1 # 寻峰高度 # 线性内插重新采样 processingx = np.arange( 0, min(int(nfft / fs * 4000), len(dataclip))) # 最大采集到4000Hz,不包括最大值,此处为尚未重采样的原始频谱 processingy = dataclip[processingx] # 重采样的fft lenprocessingx = len(processingx) # 待处理频谱长度 finterp = interp1d(processingx, processingy, kind='linear') # 线性内插配置 x_pred = np.linspace( 0, processingx[lenprocessingx - 1] * 1.0, int(processingx[lenprocessingx - 1] * 441000 / nfft) + 1) resampy = finterp(x_pred) lenresampy = len(resampy) # pixes=10 num = highcutoff # 栅栏变换后的长度 combtrans = np.zeros(num) # 存放栅栏变换的结果 # 梳状变换, 2019-03-10 16:54:45 for k in np.arange(lowcutoff, highcutoff, 1): combtrans[k] = sum(resampy[::k]) combtrans[0:lowcutoff] = max(combtrans) # 如果有去扫描参数,则去扫描 if self.isdescan is True: # 用于降低采样 desamp = [combtrans[m] for m in np.arange(0, len(combtrans), 10)] # 10倍数降采样 desamp[0] = np.max(desamp) baseline = BaseFrqDetector.getbaselinefromscan(desamp, num) # 通过扫描线算法求基准曲线 truetrans = combtrans - baseline else: truetrans = combtrans truetrans[0:lowcutoff] = 0 if sum(dataclip) < 1: return [0 / 10.0, resampy, truetrans] # 频率采样变换后寻峰 combtranspeaks = findpeaks(truetrans, spacing=peaksearchpixes, limit=max(truetrans) * peaksearchamp) peaks = truetrans[combtranspeaks] # 峰值大小 if len(peaks) == 0: return [0 / 10.0, resampy, truetrans] maxindex = np.argmax(peaks) maxfrq = combtranspeaks[maxindex] # 最高峰值位置 # 寻找1hz以内的最大的峰 combthr = 6 * 441000 / fs # 寻峰宽度 decthr = 0.65 # 递减阈值 prebasefrq = maxfrq for n in range(2, 10, 1): newfrq = BaseFrqDetector.getnearpeaks(truetrans, combtranspeaks, peaks, n * maxfrq, combthr, prebasefrq, decthr, showtestview) if newfrq > 0: prebasefrq = newfrq else: continue pitch = prebasefrq return [pitch / 10.0, resampy, truetrans]