def CalculateSpectrumBlock(self, region):
        '''Return the power spectrum of a region based on a Welch-Bartlett method.
        The block used in each FFT is half the length of the total window.
        The step size is half the size of the FFT window.
        Average over A-lines.

        This function assumes the size of the region is divisible by 4.

        It uses a zoomed in FFT to compute the power spectrum.  The zoomed in FFT is given by the
        chirpz transform.
        '''
        from scipy.signal import hann,convolve
        import numpy
        from chirpz import chirpz
        points = region.shape[0]
        points -= points%4
        points /= 2
        #######SAMPLE REGION#############
        maxDataWindow = region[0:2*points, :]

        #compute 3 fourier transforms and average them
        #Cutting off the zero-value end points of the hann window
        #so it matches Matlab's definition of the function
        windowFunc = hann(points+2)[1:-1].reshape(points,1)
        fftSample = numpy.zeros(points)
        for f in range(3):
            dataWindow = maxDataWindow[(points/2)*f:(points/2)*f + points, :]*windowFunc

            for l in range(dataWindow.shape[1]):
                fftSample += abs(chirpz(dataWindow[:,l], self.cztA, self.cztW, points))**2

        return fftSample
Ejemplo n.º 2
0
print('actual frequency content', freqs)
m = len(x)//2

Fs = 48e3
F1 = 900
F2 = 3000

# input, start, step, length
# def chirpz(x, A, W, M):
A = np.e**(1j*2*np.pi*F1/Fs)
W = np.e**(-1j*2.0*np.pi*(F2-F1)/(Fs*m))

cztAmps = np.abs(
		chirpz(x,
		A,
		W,
		m)
	)

cztFreqs = np.linspace(F1, F2, m)

cztPeaks = [cztFreqs[i-1] for i in range(m) if cztAmps[i-2] < cztAmps[i-1] > cztAmps[i] if cztAmps[i] > max(cztAmps)*0.5]
print('freq peaks from czt / zoom FFT', cztPeaks)

plt.vlines(cztPeaks, 0, max(cztAmps), 'r', label='peaks from czt')

plt.plot(cztFreqs, cztAmps, 'or', label='czt')

fftFreqs = np.linspace(0, Fs/2, len(x)*2)

fftAmps = np.abs(np.fft.rfft(x, len(x)*4))[1::]
    tmpRf = rfClass(TISSUE_A_DIR + '/' + fName, 'rfd')
    tmpRf.SetRoiFixedSize(WINDOWX, WINDOWY)    
    tmpRf.ReadFrame()
        
    #PSD fit
    windowFuncPsd = hann(psdPoints+2)[1:-1].reshape(psdPoints,1)
    powerSpectrum = np.zeros( psdPoints )
        
    region = tmpRf.data[tmpRf.roiY[0]:tmpRf.roiY[1], tmpRf.roiX[0]:tmpRf.roiX[1]]
    maxDataWindow = region
    for r in range(3):
        dataWindow = maxDataWindow[psdPoints*r:psdPoints*r + psdPoints, :]*windowFuncPsd
        fourierData = np.zeros( dataWindow.shape )
        for l in range(maxDataWindow.shape[1]):
            fourierData[:,l] = abs(chirpz(dataWindow[:,l], cztA, cztW, psdPoints))
        powerSpectrum += fourierData.mean(axis = 1)

    powerSpectrum/= maxDataWindow.shape[1]*3 

    #now fit the spectrum to a gaussian
    errfunc = lambda param,x,y: y - np.exp(- (x - param[0])**2/(2*param[1]**2) )
    param0 = (5., 3.0)
    args = (spectrumFreq,powerSpectrum/powerSpectrum.max() )
    param, message = optimize.leastsq(errfunc, param0, args)
    mu = param[0]
    sigma = param[1]

    runningTotalMu += mu
    runningTotalSigma += sigma
    counter += 1
    def CalculateGeneralizedSpectrum(self, region):
        '''Use 3 50% overlapping windows axially to compute PSD.
        Calculate Power spectrum first.  Normalize it to have a max value of 1.
        Fit this to a Gaussian.  f(x) = exp[ -(x - mu)**2 / 2 (sigma**2)]
        A Gaussian falls to -6 dB (1/4 of max value) at a little more than one sigma.
        
        Use full window length to compute axial signal.'''

        from scipy.signal import hann,convolve
        from scipy import optimize, interpolate
        import numpy
        from chirpz import chirpz

        maxDataWindow = region[0:self.gsPoints, :]
        
        windowFuncPsd = hann(self.psdPoints+2)[1:-1].reshape(self.psdPoints,1)
        powerSpectrum = numpy.zeros( self.psdPoints )

        #first compute the power spectrum, normalize it to maximum value
        for r in range(3):
            dataWindow = maxDataWindow[self.psdPoints/2*r:self.psdPoints/2*r + self.psdPoints, :]*windowFuncPsd
            fourierData = numpy.zeros( dataWindow.shape )
            for l in range(maxDataWindow.shape[1]):
                fourierData[:,l] = abs(chirpz(dataWindow[:,l], self.cztA, self.cztW, self.psdPoints))
            powerSpectrum += fourierData.mean(axis = 1)

        powerSpectrum /= powerSpectrum.max()

        #now fit the spectrum to a gaussian
        errfunc = lambda param,x,y: y - numpy.exp(- (x - param[0])**2/(2*param[1]**2) )
        param0 = (5., 1.0)
        args = (self.psdFreq,powerSpectrum)
        param, message = optimize.leastsq(errfunc, param0, args)
        mu = param[0]
        sigma = param[1]
        if sigma < .75:
            sigma = .75

        #set low and high frequency cutoffs based on output mu, sigma
        #3 Sigmas is 1% intensity on PSD is -20 dB
        lowCut = mu - 3*sigma
        if lowCut < 0:
            lowCut = 0
        highCut = mu + 3*sigma
        if highCut > self.fs/10**6:
            highCut = self.fs/10**6

        freqStep = (highCut - lowCut)/self.psdPoints
        spectrumFreq = numpy.arange(0,self.psdPoints)*freqStep  + lowCut
        fracUnitCircle = (highCut - lowCut)/(self.fs/10**6)
        cztW = numpy.exp(1j* (-2*numpy.pi*fracUnitCircle)/self.psdPoints )
        cztA = numpy.exp(1j* (2*numpy.pi*lowCut/(self.fs/10**6) ) )

        self.gsFreq = spectrumFreq

        ###########################
        ###Time averaged GS########
        ###########################
        GS = numpy.zeros( (self.psdPoints, self.psdPoints,3 ) , numpy.complex128)
        
        for r in range(3):
            dataWindow = maxDataWindow[self.psdPoints/2*r:self.psdPoints/2*r + self.psdPoints, :]
            maxPointDelay = dataWindow.argmax(axis = 0 )/self.fs
            dataWindow*=windowFuncPsd
            tempPoints = dataWindow.shape[0]
            tempLines = dataWindow.shape[1]
            fourierData = numpy.zeros( dataWindow.shape, numpy.complex128 )
            
            for l in range(tempLines):
                fourierData[:,l] = chirpz(dataWindow[:,l], cztA, cztW, self.psdPoints)

            #get point delay in seconds
            delta = numpy.outer(spectrumFreq*10**6,maxPointDelay)
            phase = numpy.exp(1j*2*numpy.pi*delta)
            
            for l in range(tempLines):
                outerProd = numpy.outer(fourierData[:,l]*phase[:,l], fourierData[:,l].conjugate()*phase[:,l].conjugate() )
                GS[:,:,r] += outerProd/abs(outerProd)

        numSegs = tempLines*3
        
        GS = GS.sum(axis = 2)/numSegs
        ##############################################
        ########COMPUTE COLLAPSED AVERAGE#############
        ##############################################
        #Along diagonal lines the scatterer spacing/frequency difference is the same
        #exclude all the entries that have a larger scatter spacing/lower frequency difference
        #than 1.5 mm:  .51 MHz 
        #Exclude all sizes less than .5 mm, which is 1.54 MHz
        numFreq = len(spectrumFreq)
        self.CA = numpy.zeros(numFreq)
        counts = numpy.zeros(numFreq)
       
        for f1 in range(numFreq):
            for f2 in range(numFreq):
                d = abs(f1-f2)
                self.CA[d] += abs(GS[f1,f2])
                counts[d] += 1

        
        self.CA /= counts
        self.CAaxis = numpy.arange(numFreq)*freqStep
        self.GS = GS

        #######################################
        ########COMPUTE THE AVERAGE OF THE#####
        ########COLLAPSED AVERAGE##############
        ###########AND THE SLOPE###############
        ###CA avg. bins:
        ###0.0-1.0 sigma
        ###1.0-2.0 sigma
        ###2.0-3.0 sigma
        ###3.0-4.0 sigma
        ###4.0-5.0 sigma
        ###5.0-6.0 sigma
        ###0.0-6.0 sigma
        #######################################

        ###Work out leakage location from PSD#######
        self.psdLimit = (1540./(2*self.gsWindowYmm*10**-3/2 ))/10**6
        secondDeriv = self.CA[0:-2] - 2*self.CA[1:-1] + self.CA[2:]
        psdInd = int(self.psdLimit/freqStep) 
        psdInd = secondDeriv[0:psdInd+10].argmax() + 1

        print "The PSD leakage stops at: " + str(secondDeriv[0:psdInd+10].argmax()  +1 )
        
        CAbinned = numpy.zeros(7)
        CAcounts = numpy.zeros(7)
        CAslope = numpy.zeros(7)
        
        for f, val in enumerate(self.CA):
            if f*freqStep > self.psdLimit and f*freqStep < 1.0*sigma:
                CAbinned[0] += val
                CAcounts[0] += 1
            elif f*freqStep > 1.0*sigma and f*freqStep < 2.0*sigma:
                CAbinned[1] += val
                CAcounts[1] += 1
            elif f*freqStep > 2.0*sigma and f*freqStep < 3.0*sigma:
                CAbinned[2] += val
                CAcounts[2] += 1
            elif f*freqStep > 3.0*sigma and f*freqStep < 4.0*sigma:
                CAbinned[3] += val
                CAcounts[3] += 1
            elif f*freqStep > 4.0*sigma and f*freqStep < 5.0*sigma:
                CAbinned[4] += val
                CAcounts[4] += 1
            elif f*freqStep > 5.0*sigma and f*freqStep < 6.0*sigma:
                CAbinned[5] += val
                CAcounts[5] += 1
            
            if f*freqStep > self.psdLimit:
                CAbinned[6] += val
                CAcounts[6] += 1


        for ind,count in enumerate(CAcounts):
            if val > 0:
                CAbinned[ind] /= count 

        
        ##compute slope of Collapsed Average
        for ind in range(7):
            if ind == 0 or ind == 6:
                lowCut = int(self.psdLimit/freqStep)
            else:
                lowCut = int(sigma*ind/freqStep)
            
            highCut = int(sigma*(ind+1)/freqStep)

            CAslope[ind] = self.ComputeSlope( self.CA[lowCut:highCut], self.CAaxis[lowCut:highCut])
        
        return CAbinned, CAslope, mu, sigma
    tmpRf.ReadFrame()

    #PSD fit
    windowFuncPsd = hann(psdPoints + 2)[1:-1].reshape(psdPoints, 1)
    powerSpectrum = np.zeros(psdPoints)

    region = tmpRf.data[tmpRf.roiY[0]:tmpRf.roiY[1],
                        tmpRf.roiX[0]:tmpRf.roiX[1]]
    maxDataWindow = region
    for r in range(3):
        dataWindow = maxDataWindow[psdPoints * r:psdPoints * r +
                                   psdPoints, :] * windowFuncPsd
        fourierData = np.zeros(dataWindow.shape)
        for l in range(maxDataWindow.shape[1]):
            fourierData[:, l] = abs(
                chirpz(dataWindow[:, l], cztA, cztW, psdPoints))
        powerSpectrum += fourierData.mean(axis=1)

    powerSpectrum /= maxDataWindow.shape[1] * 3

    #now fit the spectrum to a gaussian
    errfunc = lambda param, x, y: y - np.exp(-(x - param[0])**2 /
                                             (2 * param[1]**2))
    param0 = (5., 3.0)
    args = (spectrumFreq, powerSpectrum / powerSpectrum.max())
    param, message = optimize.leastsq(errfunc, param0, args)
    mu = param[0]
    sigma = param[1]

    ##Set up CZT parameters for GS calculation###
    lowCut = mu - GS_BW
    def CalculateGeneralizedSpectrum(self, region):
        '''Use 3 50% overlapping windows axially to compute PSD.
        Calculate Power spectrum first.  Normalize it to have a max value of 1.
        Fit this to a Gaussian.  f(x) = exp[ -(x - mu)**2 / 2 (sigma**2)]
        A Gaussian falls to -6 dB (1/4 of max value) at a little more than one sigma.
        
        Use full window length to compute axial signal.'''

        from scipy.signal import hann, convolve
        from scipy import optimize, interpolate
        import numpy
        from chirpz import chirpz

        maxDataWindow = region[0:self.gsPoints, :]

        windowFuncPsd = hann(self.psdPoints + 2)[1:-1].reshape(
            self.psdPoints, 1)
        powerSpectrum = numpy.zeros(self.psdPoints)

        #first compute the power spectrum, normalize it to maximum value
        for r in range(3):
            dataWindow = maxDataWindow[self.psdPoints / 2 *
                                       r:self.psdPoints / 2 * r +
                                       self.psdPoints, :] * windowFuncPsd
            fourierData = numpy.zeros(dataWindow.shape)
            for l in range(maxDataWindow.shape[1]):
                fourierData[:, l] = abs(
                    chirpz(dataWindow[:, l], self.cztA, self.cztW,
                           self.psdPoints))
            powerSpectrum += fourierData.mean(axis=1)

        powerSpectrum /= powerSpectrum.max()

        #now fit the spectrum to a gaussian
        errfunc = lambda param, x, y: y - numpy.exp(-(x - param[0])**2 /
                                                    (2 * param[1]**2))
        param0 = (5., 1.0)
        args = (self.psdFreq, powerSpectrum)
        param, message = optimize.leastsq(errfunc, param0, args)
        mu = param[0]
        sigma = param[1]
        if sigma < .75:
            sigma = .75

        #set low and high frequency cutoffs based on output mu, sigma
        #3 Sigmas is 1% intensity on PSD is -20 dB
        lowCut = mu - 3 * sigma
        if lowCut < 0:
            lowCut = 0
        highCut = mu + 3 * sigma
        if highCut > self.fs / 10**6:
            highCut = self.fs / 10**6

        freqStep = (highCut - lowCut) / self.psdPoints
        spectrumFreq = numpy.arange(0, self.psdPoints) * freqStep + lowCut
        fracUnitCircle = (highCut - lowCut) / (self.fs / 10**6)
        cztW = numpy.exp(1j * (-2 * numpy.pi * fracUnitCircle) /
                         self.psdPoints)
        cztA = numpy.exp(1j * (2 * numpy.pi * lowCut / (self.fs / 10**6)))

        self.gsFreq = spectrumFreq

        ###########################
        ###Time averaged GS########
        ###########################
        GS = numpy.zeros((self.psdPoints, self.psdPoints, 3), numpy.complex128)

        for r in range(3):
            dataWindow = maxDataWindow[self.psdPoints / 2 *
                                       r:self.psdPoints / 2 * r +
                                       self.psdPoints, :]
            maxPointDelay = dataWindow.argmax(axis=0) / self.fs
            dataWindow *= windowFuncPsd
            tempPoints = dataWindow.shape[0]
            tempLines = dataWindow.shape[1]
            fourierData = numpy.zeros(dataWindow.shape, numpy.complex128)

            for l in range(tempLines):
                fourierData[:, l] = chirpz(dataWindow[:, l], cztA, cztW,
                                           self.psdPoints)

            #get point delay in seconds
            delta = numpy.outer(spectrumFreq * 10**6, maxPointDelay)
            phase = numpy.exp(1j * 2 * numpy.pi * delta)

            for l in range(tempLines):
                outerProd = numpy.outer(
                    fourierData[:, l] * phase[:, l],
                    fourierData[:, l].conjugate() * phase[:, l].conjugate())
                GS[:, :, r] += outerProd / abs(outerProd)

        numSegs = tempLines * 3

        GS = GS.sum(axis=2) / numSegs
        ##############################################
        ########COMPUTE COLLAPSED AVERAGE#############
        ##############################################
        #Along diagonal lines the scatterer spacing/frequency difference is the same
        #exclude all the entries that have a larger scatter spacing/lower frequency difference
        #than 1.5 mm:  .51 MHz
        #Exclude all sizes less than .5 mm, which is 1.54 MHz
        numFreq = len(spectrumFreq)
        self.CA = numpy.zeros(numFreq)
        counts = numpy.zeros(numFreq)

        for f1 in range(numFreq):
            for f2 in range(numFreq):
                d = abs(f1 - f2)
                self.CA[d] += abs(GS[f1, f2])
                counts[d] += 1

        self.CA /= counts
        self.CAaxis = numpy.arange(numFreq) * freqStep
        self.GS = GS

        #######################################
        ########COMPUTE THE AVERAGE OF THE#####
        ########COLLAPSED AVERAGE##############
        ###########AND THE SLOPE###############
        ###CA avg. bins:
        ###0.0-1.0 sigma
        ###1.0-2.0 sigma
        ###2.0-3.0 sigma
        ###3.0-4.0 sigma
        ###4.0-5.0 sigma
        ###5.0-6.0 sigma
        ###0.0-6.0 sigma
        #######################################

        ###Work out leakage location from PSD#######
        self.psdLimit = (1540. / (2 * self.gsWindowYmm * 10**-3 / 2)) / 10**6
        secondDeriv = self.CA[0:-2] - 2 * self.CA[1:-1] + self.CA[2:]
        psdInd = int(self.psdLimit / freqStep)
        psdInd = secondDeriv[0:psdInd + 10].argmax() + 1

        print "The PSD leakage stops at: " + str(secondDeriv[0:psdInd +
                                                             10].argmax() + 1)

        CAbinned = numpy.zeros(7)
        CAcounts = numpy.zeros(7)
        CAslope = numpy.zeros(7)

        for f, val in enumerate(self.CA):
            if f * freqStep > self.psdLimit and f * freqStep < 1.0 * sigma:
                CAbinned[0] += val
                CAcounts[0] += 1
            elif f * freqStep > 1.0 * sigma and f * freqStep < 2.0 * sigma:
                CAbinned[1] += val
                CAcounts[1] += 1
            elif f * freqStep > 2.0 * sigma and f * freqStep < 3.0 * sigma:
                CAbinned[2] += val
                CAcounts[2] += 1
            elif f * freqStep > 3.0 * sigma and f * freqStep < 4.0 * sigma:
                CAbinned[3] += val
                CAcounts[3] += 1
            elif f * freqStep > 4.0 * sigma and f * freqStep < 5.0 * sigma:
                CAbinned[4] += val
                CAcounts[4] += 1
            elif f * freqStep > 5.0 * sigma and f * freqStep < 6.0 * sigma:
                CAbinned[5] += val
                CAcounts[5] += 1

            if f * freqStep > self.psdLimit:
                CAbinned[6] += val
                CAcounts[6] += 1

        for ind, count in enumerate(CAcounts):
            if val > 0:
                CAbinned[ind] /= count

        ##compute slope of Collapsed Average
        for ind in range(7):
            if ind == 0 or ind == 6:
                lowCut = int(self.psdLimit / freqStep)
            else:
                lowCut = int(sigma * ind / freqStep)

            highCut = int(sigma * (ind + 1) / freqStep)

            CAslope[ind] = self.ComputeSlope(self.CA[lowCut:highCut],
                                             self.CAaxis[lowCut:highCut])

        return CAbinned, CAslope, mu, sigma