polsartools.polsar.fp.grvi — PolSARtools 0.9 documentation

Skip to main content
`Ctrl`+`K`

# Source code for polsartools.polsar.fp.grvi

    import os
    import numpy as np
    from polsartools.utils.proc_utils import process_chunks_parallel
    from polsartools.utils.utils import conv2d,time_it
    from polsartools.utils.convert_matrices import C3_T3_mat
    from .fp_infiles import fp_c3t3files

    @time_it
    def grvi(in_dir,  win=1, fmt="tif", cog=False,
              ovr = [2, 4, 8, 16], comp=False,
              max_workers=None,block_size=(512, 512),
              progress_callback=None,  # for QGIS plugin
              ):

        """Calculate Generalized volume based Radar Vegetation Index (GRVI) from full-pol SAR data.

        This function computes the Generalized volume based Radar Vegetation Index (GRVI) using
        full-polarimetric SAR data from either coherency (T3) or covariance (C3) matrices.
        GRVI is an advanced vegetation index that accounts for various scattering mechanisms
        and is particularly effective for vegetation structure characterization.

        Examples
        --------
        >>> # Basic usage with default parameters
        >>> grvi("/path/to/fullpol_data")

        >>> # Advanced usage with custom parameters
        >>> grvi(
        ...     in_dir="/path/to/fullpol_data",
        ...     win=5,
        ...     fmt="tif",
        ...     cog=True,
        ...     block_size=(1024, 1024)
        ... )

        Parameters
        ----------
        in_dir : str
            Path to the input folder containing full-pol T3 or C3 matrix files.
        win : int, default=1
            Size of the spatial averaging window. Larger windows reduce speckle noise
            but decrease spatial resolution.
        fmt : {'tif', 'bin'}, default='tif'
            Output file format:
            - 'tif': GeoTIFF format with georeferencing information
            - 'bin': Raw binary format
        cog : bool, default=False
            If True, creates a Cloud Optimized GeoTIFF (COG) with internal tiling
            and overviews for efficient web access.
        ovr : list[int], default=[2, 4, 8, 16]
            Overview levels for COG creation. Each number represents the
            decimation factor for that overview level.
        comp : bool, default=False
            If True, uses LZW compression for GeoTIFF outputs.
        max_workers : int | None, default=None
            Maximum number of parallel processing workers. If None, uses
            CPU count - 1 workers.
        block_size : tuple[int, int], default=(512, 512)
            Size of processing blocks (rows, cols) for parallel computation.
            Larger blocks use more memory but may be more efficient.

        Returns
        -------
        None
            Writes one output file to disk:
            - 'grvi.tif' or 'grvi.bin': GRVI image

        """
        write_flag=True
        input_filepaths = fp_c3t3files(in_dir)

        output_filepaths = []
        if fmt == "bin":
            output_filepaths.append(os.path.join(in_dir, "grvi.bin"))
        else:
            output_filepaths.append(os.path.join(in_dir, "grvi.tif"))

        process_chunks_parallel(input_filepaths, list(output_filepaths),
                                window_size=win, write_flag=write_flag,
                            processing_func=process_chunk_grvi,block_size=block_size,
                            max_workers=max_workers,  num_outputs=1,
                            cog=cog, ovr=ovr, comp=comp,
                            progress_callback=progress_callback
                            )

    def process_chunk_grvi(chunks, window_size, input_filepaths,*args):

        if 'T11' in input_filepaths[0] and 'T22' in input_filepaths[5] and 'T33' in input_filepaths[8]:
            t11_T1 = np.array(chunks[0])
            t12_T1 = np.array(chunks[1])+1j*np.array(chunks[2])
            t13_T1 = np.array(chunks[3])+1j*np.array(chunks[4])
            t21_T1 = np.conj(t12_T1)
            t22_T1 = np.array(chunks[5])
            t23_T1 = np.array(chunks[6])+1j*np.array(chunks[7])
            t31_T1 = np.conj(t13_T1)
            t32_T1 = np.conj(t23_T1)
            t33_T1 = np.array(chunks[8])

            # T_T1 = np.array([[t11_T1, t12_T1, t13_T1],
            #              [t21_T1, t22_T1, t23_T1],
            #              [t31_T1, t32_T1, t33_T1]])

        if 'C11' in input_filepaths[0] and 'C22' in input_filepaths[5] and 'C33' in input_filepaths[8]:
            C11 = np.array(chunks[0])
            C12 = np.array(chunks[1])+1j*np.array(chunks[2])
            C13 = np.array(chunks[3])+1j*np.array(chunks[4])
            C21 = np.conj(C12)
            C22 = np.array(chunks[5])
            C23 = np.array(chunks[6])+1j*np.array(chunks[7])
            C31 = np.conj(C13)
            C32 = np.conj(C23)
            C33 = np.array(chunks[8])
            C3 = np.array([[C11, C12, C13],
                             [C21, C22, C23],
                             [C31, C32, C33]])

            T_T1 = C3_T3_mat(C3)

            t11_T1 = T_T1[0,0,:,:]
            t12_T1 = T_T1[0,1,:,:]
            t13_T1 = T_T1[0,2,:,:]
            t21_T1 = np.conj(t12_T1)
            t22_T1 = T_T1[1,1,:,:]
            t23_T1 = T_T1[1,2,:,:]
            t31_T1 = np.conj(t13_T1)
            t32_T1 = np.conj(t23_T1)
            t33_T1 = T_T1[2,2,:,:]

        # if window_size>1:
        #     kernel = np.ones((window_size,window_size),np.float32)/(window_size*window_size)

        #     t11f = conv2d(T_T1[0,0,:,:],kernel)
        #     t12f = conv2d(T_T1[0,1,:,:],kernel)
        #     t13f = conv2d(T_T1[0,2,:,:],kernel)

        #     t21f = conv2d(T_T1[1,0,:,:],kernel)
        #     t22f = conv2d(T_T1[1,1,:,:],kernel)
        #     t23f = conv2d(T_T1[1,2,:,:],kernel)

        #     t31f = conv2d(T_T1[2,0,:,:],kernel)
        #     t32f = conv2d(T_T1[2,1,:,:],kernel)
        #     t33f = conv2d(T_T1[2,2,:,:],kernel)

        #     T_T1 = np.array([[t11f, t12f, t13f], [t21f, t22f, t23f], [t31f, t32f, t33f]])

        nrows, ncols  = np.shape(t11_T1)[1],np.shape(t11_T1)[0]

        span = np.zeros((ncols,nrows))
        rho_13_hhvv = np.zeros((ncols,nrows))
        # temp_rvi = np.zeros((ncols,nrows))
        fp22 = np.zeros((ncols,nrows))
        GD_t1_t = np.zeros((ncols,nrows))
        GD_t1_d = np.zeros((ncols,nrows))
        GD_t1_rv = np.zeros((ncols,nrows))
        GD_t1_nd = np.zeros((ncols,nrows))
        GD_t1_c = np.zeros((ncols,nrows))
        GD_t1_lh = np.zeros((ncols,nrows))
        GD_t1_rh = np.zeros((ncols,nrows))
        beta = np.zeros((ncols,nrows))
        beta_1 = np.zeros((ncols,nrows))
        f = np.zeros((ncols,nrows))
        a = np.zeros((ncols,nrows))
        b = np.zeros((ncols,nrows))
        temp_gamma = np.zeros((ncols,nrows))
        t_d = np.zeros((46,1))
        t_nd = np.zeros((46,1))
        t_t = np.zeros((46,1))
        t_c = np.zeros((46,1))
        theta_map = np.zeros((ncols,nrows))

        D = (1/np.sqrt(2))*np.array([[1,0,1], [1,0,-1],[0,np.sqrt(2),0]])
        # %% for window processing
        wsi=wsj=window_size

        inci=int(np.fix(wsi/2)) # Up & down movement margin from the central row
        incj=int(np.fix(wsj/2)) # Left & right movement from the central column
        # % Starting row and column fixed by the size of the patch extracted from the image of 21/10/1999

        starti=int(np.fix(wsi/2)) # Starting row for window processing
        startj=int(np.fix(wsj/2)) # Starting column for window processing

        stopi= int(nrows-inci)-1 # Stop row for window processing
        stopj= int(ncols-incj)-1 # Stop column for window processing
                # %% Elementary targets

        M_d = np.array([[1,0,0,0], [ 0,1,0,0], [ 0,0,-1,0], [ 0,0,0,1]])
        M_nd = np.array([[0.625,0.375,0,0], [ 0.375,0.625,0,0], [ 0,0,-0.5,0], [ 0,0,0,0.5]])
        M_t = np.array([[1,0,0,0], [ 0,1,0,0], [ 0,0,1,0], [ 0, 0,0,-1]])
        M_c = np.array([[0.625,0.375,0,0], [ 0.375,0.625,0,0], [0,0,0.5,0], [ 0,0,0,-0.5]])
        M_lh = np.array([[1,0,0,-1], [ 0,0,0,0], [ 0,0,0,0], [ -1,0,0,1]])
        M_rh = np.array([[1,0,0,1], [ 0,0,0,0], [ 0,0,0,0], [ 1,0,0,1]])

        for ii in np.arange(startj,stopj+1):

            for jj in np.arange(starti,stopi+1):

                t11s = np.nanmean(t11_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample
                t12s = np.nanmean(t12_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample
                t13s = np.nanmean(t13_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample

                t21s = np.nanmean(t21_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample
                t22s = np.nanmean(t22_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample
                t23s = np.nanmean(t23_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample

                t31s = np.nanmean(t31_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample
                t32s = np.nanmean(t32_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample
                t33s = np.nanmean(t33_T1[ii-inci:ii+inci+1,jj-incj:jj+incj+1])#i sample

                T_T1 = np.array([[t11s, t12s, t13s], [t21s, t22s, t23s], [t31s, t32s, t33s]])

                #Coherency matrix
                C_T1 = np.matmul(np.matmul((D.T),T_T1),D)

                span[ii,jj] = np.real(t11s + t22s + t33s)
                temp_span = span[ii,jj]
                # self.progress.emit(str('span Done'))
                Temp_T1 = T_T1

                t11 = Temp_T1[0,0]
                t12 = Temp_T1[0,1]
                t13 = Temp_T1[0,2]
                t21 = np.conj(t12)
                t22 = Temp_T1[1,1]
                t23 = Temp_T1[1,2]
                t31 = np.conj(t13)
                t32 = np.conj(t23)
                t33 = Temp_T1[2,2]

                # %% Ratio of VV/HH (Used in Yamagichi Volume)

                hh = 0.5*(t11 + t22 + 2*np.real(t12)) #HH
                hv = t33 #HV
                vv = 0.5*(t11 + t22 - 2*np.real(t12)) #VV
                vol_con = 10*np.log10(vv/hh)

                # %% Kennaugh Matrix

                m11 = t11+t22+t33
                m12 = t12+t21
                m13 = t13+t31
                m14 = -1j*(t23 - t32)
                m21 = t12+t21
                m22 = t11+t22-t33
                m23 = t23+t32
                m24 = -1j*(t13-t31)
                m31 = t13+t31
                m32 = t23+t32
                m33 = t11-t22+t33
                m34 = 1j*(t12-t21)
                m41 = -1j*(t23-t32)
                m42 = -1j*(t13-t31)
                m43 = 1j*(t12-t21)
                m44 = -t11+t22+t33

                M_T = 0.5*np.array([[m11, m12, m13, m14], [m21, m22, m23, m24], [m31, m32, m33, m34], [m41, m42, m43, m44]])

                M_T_theta = M_T

                # %% GVSM

                t011 = M_T_theta[0,0] + M_T_theta[1,1] + M_T_theta[2,2] - M_T_theta[3,3]
                t012 = M_T_theta[0,1] - 1j*M_T_theta[2,3]
                t013 = M_T_theta[0,2] + 1j*M_T_theta[1,3]
                t021 = np.conj(t012)
                t022 = M_T_theta[0,0] + M_T_theta[1,1] - M_T_theta[2,2] + M_T_theta[3,3]
                t023 = M_T_theta[1,2] +1j*M_T_theta[0,3]
                t031 = np.conj(t013)
                t032 = np.conj(t023)
                t033 = M_T_theta[0,0] - M_T_theta[1,1] + M_T_theta[2,2] + M_T_theta[3,3]

                # %% T to C

                T0 = np.array([[t011/2, t012, t013], [t021, t022/2, t023], [t031, t032, t033/2]])
                C0 = np.matmul(np.matmul((D.T),T0),D)

                # %% Gamma/Rho

                gamma = np.real(C0[0,0]/C0[2,2])
                rho = 1/3
                temp_gamma[ii,jj]= np.real(gamma) #% variable to save

                # %% Covariance matrix

                c11 = gamma
                c12 = 0
                c13 = rho*np.sqrt(gamma)
                c21 = 0
                c22 = 0.5*(1 + gamma) - rho*np.sqrt(gamma)
                c23 = 0
                c31 = np.conj(rho)*np.sqrt(gamma)
                c32 = 0
                c33 = 1

                R = (3/2)*(1 + gamma) - rho*np.sqrt(gamma)
                C1 = (1/R)*np.array([[c11, c12, c13], [c21, c22, c23], [c31, c32, c33]])
                # self.progress.emit(str('gamma and R Done'))
                # %% Coherency matrix

                T1 = np.matmul(np.matmul(D,C1),(D.T))

                t11 = T1[0,0]
                t12 = T1[0,1]
                t13 = T1[0,2]
                t21 = T1[1,0]
                t22 = T1[1,1]
                t23 = T1[1,2]
                t31 = T1[2,0]
                t32 = T1[2,1]
                t33 = T1[2,2]

                m11 = t11+t22+t33
                m12 = t12+t21
                m13 = t13+t31
                m14 = -1j*(t23 - t32)
                m21 = t12+t21
                m22 = t11+t22-t33
                m23 = t23+t32
                m24 = -1j*(t13-t31)
                m31 = t13+t31
                m32 = t23+t32
                m33 = t11-t22+t33
                m34 = 1j*(t12-t21)
                m41 = -1j*(t23-t32)
                m42 = -1j*(t13-t31)
                m43 = 1j*(t12-t21)
                m44 = -t11+t22+t33

                # %% Generalized Random Volume (Antropov et al.)

                M_rv = np.real(np.array([[m11, m12, m13, m14], [m21, m22, m23, m24], [m31, m32, m33, m34], [m41, m42, m43, m44]]))

                f[ii,jj] = 1

                # %% GD Volume

                num1 = np.matmul(((M_T_theta).T),M_rv) #% volume
                num = np.trace(num1)
                den1 = np.sqrt(abs(np.trace(np.matmul(((M_T_theta).T),M_T_theta))))
                den2 = np.sqrt(abs(np.trace(np.matmul(((M_rv).T),M_rv))))
                den = den1*den2
                temp_aa = np.real(2*np.arccos(num/den)*180/np.pi)
                GD_t1_rv[ii,jj] = np.real(temp_aa/180)
                # self.progress.emit(str('GD volume Done'))
                # %% GD ALL

                num1 = np.matmul(((M_T_theta).T),M_c) #% cylinder
                num = np.trace(num1)
                den1 = np.sqrt(abs(np.trace(np.matmul(((M_T_theta).T),M_T_theta))))
                den2 = np.sqrt(abs(np.trace(np.matmul(((M_c).T),M_c))))
                den = den1*den2
                temp_aa = np.real(2*np.arccos(num/den)*180/np.pi)
                GD_t1_c[ii,jj] = np.real(temp_aa/180)
                # self.progress.emit(str('GD cylider Done'))

                num1 = np.matmul(((M_T_theta).T),M_t) #% trihedral
                num = np.trace(num1)
                den1 = np.sqrt(abs(np.trace(np.matmul(((M_T_theta).T),M_T_theta))))
                den2 = np.sqrt(abs(np.trace(np.matmul(((M_t).T),M_t))))
                den = den1*den2
                temp_aa = 2*np.arccos(num/den)*180/np.pi
                GD_t1_t[ii,jj] = np.real(temp_aa/180)
                # self.progress.emit(str('GD trihedral Done'))

                num1 = np.matmul(((M_T_theta).T),M_d) #% dihedral
                num = np.trace(num1)
                den1 = np.sqrt(abs(np.trace(np.matmul(((M_T_theta).T),M_T_theta))))
                den2 = np.sqrt(abs(np.trace(np.matmul(((M_d).T),M_d))))
                den = den1*den2
                temp_aa = 2*np.arccos(num/den)*180/np.pi
                GD_t1_d[ii,jj] = np.real(temp_aa/180)
                # self.progress.emit(str('GD dihedral Done'))

                num1 = np.matmul(((M_T_theta).T),M_nd) #% n-dihedral
                num = np.trace(num1)
                den1 = np.sqrt(abs(np.trace(np.matmul(((M_T_theta).T),M_T_theta))))
                den2 = np.sqrt(abs(np.trace(np.matmul(((M_nd).T),M_nd))))
                den = den1*den2
                temp_aa = 2*np.arccos(num/den)*180/np.pi
                GD_t1_nd[ii,jj] = np.real(temp_aa/180)
                # self.progress.emit(str('GD n-dihedral Done'))

                # %% VI

                t_t = GD_t1_t[ii,jj]
                t_d = GD_t1_d[ii,jj]
                t_c = GD_t1_c[ii,jj]
                t_nd = GD_t1_nd[ii,jj]

                a[ii,jj] = np.nanmax([t_t, t_d, t_c, t_nd])
                b[ii,jj] = np.nanmin([t_t, t_d, t_c, t_nd])
                beta[ii,jj] = (b[ii,jj]/a[ii,jj])**2

        # %% GRVI
        f[f==0]=np.nan
        vi = np.power(beta, GD_t1_rv)*(1 - (1/f)*GD_t1_rv)

        x1 = np.power(beta, GD_t1_rv)
        x2 = (1 - GD_t1_rv)

        f =  np.nan_to_num(f)
        idx1 = np.argwhere(GD_t1_rv>f)
        vi[idx1] = 0
        vi[~idx1] = vi[~idx1]
        vi = np.real(vi)

        return vi.astype(np.float32)