Skip to content

integration of SOACOM reader in xsar #285

Description

@agrouaze

Inputs from Guisepe Grieco (CNR):

You can download it from the SAOCOM web page managed by ASI. Nando, from
the Sapienza university (in copy) should be able to add you to the
licence and give you the credentials. Is that correct Nando?
Concerning the Aquarius GMFs, you can get them from the following link
in the shared ROSS folder:
https://drive.google.com/drive/folders/1_2SdM9NqI5XEJ6RVpAk7vpd8JPGjzK8n?usp=drive_link
[1]
In this folder, you can also find the matlab script to read the files.
Don't hesitate to ask for my support. I guess you will need it.
Cheers,
Giuseppe

SOACOM.py

# -*- coding: utf-8 -*-
"""
Created on Wed Jun 24 15:24:09 2015

@author: imac
"""

import os
import glob
import rasterio as rio
from rasterio.windows import Window
import warnings
import numpy as np

def evalLcCCF(im, dx_az, dx_ra):
    ''' Returns and estimation of the azimuth wavelength cut-off by means of \n
    a least-square fit of the CCF with a Gaussian function \n
    Call signature: \n
    lc,fxfit,ipsda,kmin,kmax,lmin,lmax=evalLcCCF(im, dl, hp) \n
    INPUT: \n
    im           integer SAR image (electric field) \n
    dx_az        pixel spacing in azimuth \n
    dx_ra        pixel spacing in range \n
    OUTPUT: \n
    lc           wavelength cut-off least square method\n
    fxfit        gaussian fit of the ACF function \n
    ipsda        ACF \n
    kmin         minimum wavenumber \n
    kmax         maximum wavenumber \n
    lmin         minimum value of the spatial domain \n
    lmax         masimum value of the spatial domain \n
    chi2         chi square \n
    pvalue       pvalue associated to chi2 \n
    
    Giuseppe Grieco \n
    23 Dec 2025
    '''
    from scipy import optimize
    from numpy.fft import ifft, ifft2, fft2, fftshift, ifftshift, fftfreq
#==============================================================================
# Proietto l'immagine nello spazio del Doppler. Definisco
# - numero di look
# - size in azimuth e range
# - costruisco una hanning window per fare un'apodizzazione dello spettro
#==============================================================================
    hp_az = int(im.shape[0]/2)
    hp_ra = int(im.shape[1]/2)
    az_size = 2*hp_az
    ran_size = 2*hp_ra
    az_win = np.hanning(az_size+2)[1:-1]
    ran_win = np.hanning(ran_size+2)[1:-1]
    intensity = (abs(im)**2).mean()
    window = np.sqrt(1/intensity)
    #window = np.sqrt(np.outer(az_win, ran_win)/intensity)
#==============================================================================
# Costruisco l'asse nel dominio della CCF. Definisco
# - pixel spacing in azimuth
#==============================================================================
    az_look_size_per = .3
    ran_look_size_per = .78
    az_look_sep_per = .01
    ran_look_sep_per = .02
    n_looks_az = int(np.floor((1+az_look_sep_per)/(az_look_size_per+az_look_sep_per)))
    n_looks_ran = int(np.floor((1+ran_look_sep_per)/(ran_look_size_per+ran_look_sep_per)))
    nsteps = n_looks_az-1
    az_look_sep_pt = int(np.round(az_look_sep_per*az_size))
    az_look_size_pt = int(az_look_size_per*az_size)
    ran_look_size_pt = int(np.round(ran_size*ran_look_size_per))
    ran_look_sep_pt = int(np.round(ran_size*ran_look_sep_per))
    d_dop = 1/(az_size*dx_az)
    x_ccf = fftshift(fftfreq(az_size, d_dop))
    image_fft = fftshift(fft2(window*im))
    i_az = int((az_size-az_look_size_pt*n_looks_az-az_look_sep_pt*(n_looks_az-1))/2)
    i_ran = int(np.ceil((ran_size-ran_look_size_pt)/2))
    az_look_iind = np.arange(i_az, az_size, (az_look_size_pt+az_look_sep_pt))[:n_looks_az]
    az_look_find = az_look_iind+az_look_size_pt
    ran_look_iind = np.arange(i_ran, ran_size, (ran_look_size_pt+ran_look_sep_pt))[:n_looks_ran]
    ran_look_find = ran_look_iind+ran_look_size_pt
#==============================================================================
# Inizializzo
# - look nel dominio Doppler
# - Power spectral density
# - Cross Correlation Spectra (CCS)
# - CCS mediati sul numero di step disponibili
# - Cross Correlation Function (CCF)
#==============================================================================
    look = np.zeros((az_size, ran_size, n_looks_az, n_looks_ran))
    int_look_fft = np.zeros((az_size, ran_size, n_looks_az, n_looks_ran), dtype='complex64')
    image_look_fft = np.zeros((az_size, ran_size), dtype='complex64')
    int_look = np.zeros((az_size, ran_size, n_looks_az, n_looks_ran), dtype='complex64')
    ccs = np.zeros((az_size, ran_size, n_looks_az, n_looks_az), dtype='complex64')
    accs = np.zeros((az_size, ran_size, n_looks_az-1), dtype='complex64')
    ccf = np.zeros((az_size, nsteps))

#==============================================================================
# Calcolo la densitá spettrale per ogni look
#==============================================================================
    for i_az_look in np.arange(n_looks_az):
        for i_ran_look in np.arange(n_looks_ran):
            image_look_fft = np.zeros((az_size, ran_size), dtype='complex64')
            image_look_fft[az_look_iind[i_az_look]:az_look_find[i_az_look], ran_look_iind[i_ran_look]: ran_look_find[i_ran_look]] = image_fft[az_look_iind[i_az_look]:az_look_find[i_az_look], ran_look_iind[i_ran_look]: ran_look_find[i_ran_look]]
            look[:, :, i_az_look, i_ran_look] = ifft2(ifftshift(image_look_fft))
            dum = fft2(abs(ifft2(image_look_fft))**2)
            int_look_fft[:, :, i_az_look, i_ran_look] = dum
            int_look[:, :, i_az_look, i_ran_look] = look[:, :, i_az_look, i_ran_look]**2

#==============================================================================
# Calcolo i cross correlation spectra per tutte le combinazioni di look
#==============================================================================
    for i_az_look in np.arange(n_looks_az):
        for j_az_look in np.arange(i_az_look, n_looks_az):
            ccs[:, :, i_az_look, j_az_look] = np.squeeze(int_look_fft[:, :, i_az_look]*np.conj(int_look_fft[:, :, j_az_look]))
#==============================================================================
# Remove DC value
#==============================================================================
    ccs[np.tile([0, 1, -1], 3), np.repeat([0, 1, -1], 3), :, :] = 0
    int_look_fft[np.tile([0, 1, -1], 3), np.repeat([0, 1, -1], 3), :, :] = 0

    for step in np.arange(1, n_looks_az):
        for i_az_look in np.arange(0, n_looks_az-step):
            accs[:, :, step-1] = accs[:, :, step-1] + 1/(n_looks_az-step)*ccs[:, :, i_az_look, i_az_look+step]

    for step in np.arange(nsteps):
        dum = fftshift(ifft(ifftshift(accs[:, :, step].mean(axis=1))))
        ccf[:, step] = np.real(np.sqrt(dum*np.conj(dum)))
        ccf[:, step] = ccf[:, step]/ccf[:, step].max()

    p0 = [0., 200/np.sqrt(2)/np.pi, 4., np.mean(np.abs(ccf[:, 0]))]
    outFitLS = optimize.leastsq(errfunc, p0, args=(x_ccf, ccf[:, 0]), full_output=1)
    lc = np.sqrt(2)*np.pi*outFitLS[0][1]
    fxfit = gaussian(outFitLS[0], x_ccf)
    #chi2, pvalue = chisquare(np.abs(dum), np.abs(fxfit), ddof=n-4)
    chi2 = np.nan
    pvalue = np.nan
    import pdb; pdb.set_trace()
    return lc, fxfit, dum, x_ccf[0], x_ccf[-1], chi2, pvalue, ccf[:, 0], outFitLS[0]


def errfunc(p, x, y):
    '''Returns the difference between data and the gaussian function \n
    Call signature: \n
    d=errfunc(p,x,y) \n
    INPUT: \n
    p               list of the parameters of the Gaussian distribution:\n
        p[0]=m      mean \n  
        p[1]=std    standard deviation \n
        p[2]=amp    amplitude factor \n
        p[3]=offset offset with respect to the x axis \n
    x               independent coordinate (space, time, ecc...) \n
    y               observations \n
    OUTPUT: \n
    d               difference between observations and Gaussian fit: \n
                    y-{p[3]+p[2]*Gauss(p[0],p[1])} \n
    
    Giuseppe Grieco \n
    26 Jun 2015
    '''
    
    return y-gaussian(p, x)


def gaussian(B, x):
    ''' Returns the gaussian function for the given x and the parameters of the distribution\n
    Call signature: \n
    y=gaussian(B,x) \n
    INPUT: \n
    B           list of the parameters of the distribution \n
        m       mean of the Gaussian function \n
        st      standard deviation \n
        amp     amplification factor \n
        offset  offest with respect to the x axis. \n
    x           independent coordinate (space, time, ecc..) \n
    
    OUTPUT: \n
    y       array of the dependent variable computed as follows \n
    y=offset+amp/(st*np.sqrt(2*np.pi))*np.exp(-((x-m)**2/(2*st**2))) \n
    
    Giuseppe Grieco \n
    26 Jun 2015
    '''
    return B[3]+B[2]/(B[1]*np.sqrt(2*np.pi))*np.exp(-((x-B[0])**2/(2*B[1]**2)))


def read_SLC_SM(dir_name, pol='HH', chunk=[0, 1, 0, 1]):
    '''
    This function reads SAOCOM StripMap SAR images from tiff files.
    Note that these images are already radiometrically calibrated.

    INPUT:
    dir_name     name of the directory where the entire SAOCOM file is located
    pol          polarization ('HH','HV') optional: default='HH'
    chunk        list containing respectively the first and last indices in the
                 range direction and in the azimuth direction [iR1,iR2,iA1,iA2].
                 Default: [0, 1, 0, 1]

    OUTPUT:
    image        subset image from the tiff file
    ncols        number of columns of the entire image
    nrows        number of rows of the entire image
    '''

#------------------------------------------------------------------------------
#   Directory where tiff files are stored in SAOCOM files
#------------------------------------------------------------------------------
    meas_dir_name = os.path.join(os.environ['SAO_DATA'], dir_name, 'Data')
    tiff_name = glob.glob(os.path.join(meas_dir_name, '*'+pol.lower()))[0]

    ras = rio.open(tiff_name)

    width = ras.width
    height = ras.height

    if (chunk[1] > width):
        warnings.warn("The final range index is larger than the image width. Exceeding columns will be ignored")

    # import pdb; pdb.set_trace()
    if (chunk[3] > height):
        warnings.warn("The final azimuth index is larger than the image heigth. Exceeding rows will be ignored")

    w_size = int(chunk[1]-chunk[0])
    h_size = int(chunk[3]-chunk[2])

    values = ras.read(1, window=Window(chunk[0], chunk[2], w_size, h_size))
    return values

readSLC_SOACOM.py

#!/home/giuseppe/.venv/bin/python3
# -*- coding: utf-8 -*-
"""

author: grieco
"""

from SAOCOM import read_SLC_SM, evalLcCCF, errfunc, gaussian
import numpy as np
import os
import matplotlib.pyplot as plt
from scipy import optimize
from numpy.fft import ifft, ifft2, fft2, fftshift, ifftshift, fftfreq

plt.ion()

SAOCOM_DIR = '/home/giuseppe/SAOCOM'
dir_name = os.path.join(SAOCOM_DIR, 'EOL1ASARSAO1A13639337')
pol = 'HH'

dx_az = 5.
dx_ra = 6.

Dx_az = 1e4
Dx_ra = 1e4

dn_az = 2**int(np.round(np.log2(Dx_az/2/dx_az)))
dn_ra = 2**int(np.round(np.log2(Dx_ra/2/dx_ra)))

ic_ra = 3500
ic_az = 9500

chunk = [ic_ra-dn_ra, ic_ra+dn_ra, ic_az-dn_az, ic_az+dn_az]
im = read_SLC_SM(dir_name, pol, chunk)

im_abs = abs(im)
sigma_0 = 10*np.log10(im_abs**2)

# lc, fxfit, dum, x_ccf[0], x_ccf[-1], chi2, pvalue, ccf[:, 0], outFitLS[0] = evalLcCCF(im, dx_az, dx_ra)

#==============================================================================
# Proietto l'immagine nello spazio del Doppler. Definisco
# - numero di look
# - size in azimuth e range
# - costruisco una hanning window per fare un'apodizzazione dello spettro
#==============================================================================
hp_az = int(im.shape[0]/2)
hp_ra = int(im.shape[1]/2)
az_size = 2*hp_az
ran_size = 2*hp_ra
az_win = np.hanning(az_size+2)[1:-1]
ran_win = np.hanning(ran_size+2)[1:-1]
intensity = (im_abs**2).mean()
window = np.sqrt(1/intensity)
# window = np.sqrt(np.outer(az_win, ran_win)/intensity)
#==============================================================================
# Costruisco l'asse nel dominio della CCF. Definisco
# - pixel spacing in azimuth
#==============================================================================
az_look_size_per = .3
ran_look_size_per = .78
az_look_sep_per = .01
ran_look_sep_per = .02
n_looks_az = int(np.floor((1+az_look_sep_per)/(az_look_size_per+az_look_sep_per)))
n_looks_ran = int(np.floor((1+ran_look_sep_per)/(ran_look_size_per+ran_look_sep_per)))
nsteps = n_looks_az-1
az_look_sep_pt = int(np.round(az_look_sep_per*az_size))
az_look_size_pt = int(az_look_size_per*az_size)
ran_look_size_pt = int(np.round(ran_size*ran_look_size_per))
ran_look_sep_pt = int(np.round(ran_size*ran_look_sep_per))
d_dop = 1/(az_size*dx_az)
x_ccf = fftshift(fftfreq(az_size, d_dop))
image_fft = fftshift(fft2(window*im))
i_az = int((az_size-az_look_size_pt*n_looks_az-az_look_sep_pt*(n_looks_az-1))/2)
i_ran = int(np.ceil((ran_size-ran_look_size_pt)/2))
az_look_iind = np.arange(i_az, az_size, (az_look_size_pt+az_look_sep_pt))[:n_looks_az]
az_look_find = az_look_iind+az_look_size_pt
ran_look_iind = np.arange(i_ran, ran_size, (ran_look_size_pt+ran_look_sep_pt))[:n_looks_ran]
ran_look_find = ran_look_iind+ran_look_size_pt
#==============================================================================
# Inizializzo
# - look nel dominio Doppler
# - Power spectral density
# - Cross Correlation Spectra (CCS)
# - CCS mediati sul numero di step disponibili
# - Cross Correlation Function (CCF)
#==============================================================================
look = np.zeros((az_size, ran_size, n_looks_az, n_looks_ran), dtype='complex64')
int_look_fft = np.zeros((az_size, ran_size, n_looks_az, n_looks_ran), dtype='complex64')
image_look_fft = np.zeros((az_size, ran_size), dtype='complex64')
int_look = np.zeros((az_size, ran_size, n_looks_az, n_looks_ran), dtype='complex64')
ccs = np.zeros((az_size, ran_size, n_looks_az, n_looks_az), dtype='complex64')
accs = np.zeros((az_size, ran_size, n_looks_az-1), dtype='complex64')
ccf = np.zeros((az_size, nsteps))

#==============================================================================
# Calcolo la densitá spettrale per ogni look
#==============================================================================
for i_az_look in np.arange(n_looks_az):
    for i_ran_look in np.arange(n_looks_ran):
        image_look_fft = np.zeros((az_size, ran_size), dtype='complex64')
        image_look_fft[az_look_iind[i_az_look]:az_look_find[i_az_look], ran_look_iind[i_ran_look]: ran_look_find[i_ran_look]] = image_fft[az_look_iind[i_az_look]:az_look_find[i_az_look], ran_look_iind[i_ran_look]: ran_look_find[i_ran_look]]
        look[:, :, i_az_look, i_ran_look] = ifft2(ifftshift(image_look_fft))
        dum = fft2(abs(ifft2(image_look_fft))**2)
        int_look_fft[:, :, i_az_look, i_ran_look] = dum
        int_look[:, :, i_az_look, i_ran_look] = np.abs(look[:, :, i_az_look, i_ran_look])**2

#==============================================================================
# Calcolo i cross correlation spectra per tutte le combinazioni di look
#==============================================================================
for i_az_look in np.arange(n_looks_az):
    for j_az_look in np.arange(i_az_look, n_looks_az):
        ccs[:, :, i_az_look, j_az_look] = np.squeeze(int_look_fft[:, :, i_az_look]*np.conj(int_look_fft[:, :, j_az_look]))
#==============================================================================
# Remove DC value
#==============================================================================
ccs[np.tile([0, 1, -1], 3), np.repeat([0, 1, -1], 3), :, :] = 0
int_look_fft[np.tile([0, 1, -1], 3), np.repeat([0, 1, -1], 3), :, :] = 0

for step in np.arange(1, n_looks_az):
    for i_az_look in np.arange(0, n_looks_az-step):
        accs[:, :, step-1] = accs[:, :, step-1] + 1/(n_looks_az-step)*ccs[:, :, i_az_look, i_az_look+step]

for step in np.arange(nsteps):
    dum = fftshift(ifft(ifftshift(accs[:, :, step].mean(axis=1))))
    ccf[:, step] = np.real(np.sqrt(dum*np.conj(dum)))
    ccf[:, step] = ccf[:, step]/ccf[:, step].max()

p0 = [0., 200/np.sqrt(2)/np.pi, 4., np.mean(np.abs(ccf[:, 0]))]
outFitLS = optimize.leastsq(errfunc, p0, args=(x_ccf, ccf[:, 0]), full_output=1)
lc = np.sqrt(2)*np.pi*outFitLS[0][1]
fxfit = gaussian(outFitLS[0], x_ccf)
#chi2, pvalue = chisquare(np.abs(dum), np.abs(fxfit), ddof=n-4)
chi2 = np.nan
pvalue = np.nan

import pdb; pdb.set_trace()
#==============================================================================
# Plot sigma0
#==============================================================================
vmin = np.percentile(sigma_0, .5)
vmax = np.percentile(sigma_0, 99.5)
plt.figure()
plt.imshow(sigma_0, cmap='gray', vmin=-40., vmax=10.)

#==============================================================================
# Plot abs(image_fft)
#==============================================================================
vmax = np.percentile(np.reshape(abs(image_fft), (image_fft.size,)), 99.5)
lev_dist = vmax/10.
plt.figure()
plt.contour(abs(image_fft), cmap='gray', levels=np.arange(0, vmax+1, lev_dist))

#==============================================================================
# Plot abs averaged cross-spectra for step 0
#==============================================================================
vmax = np.max(abs(accs[:, :, 0]))
lev_dist = vmax/10.
plt.figure()
plt.contour(fftshift(abs(accs[:, :, 0])), cmap='gray', levels=np.arange(0, vmax+1, lev_dist))

#==============================================================================
# Plot abs averaged cross-spectra for step 1
#==============================================================================
vmax = np.max(abs(accs[:, :, 1]))
lev_dist = vmax/10.
plt.figure()
plt.contour(fftshift(abs(accs[:, :, 0])), cmap='gray', levels=np.arange(0, vmax+1, lev_dist))

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions