Skip to content
Snippets Groups Projects
Commit 944ba34e authored by Alexander Kutkin's avatar Alexander Kutkin
Browse files

implemented Voronoi clustering

parent c7a66e8c
Branches
Tags
No related merge requests found
......@@ -16,18 +16,24 @@ from astropy.coordinates import SkyCoord, angles
from astropy.stats import median_absolute_deviation as mad
import astropy.units as u
import numpy as np
import logging
import matplotlib
matplotlib._log.disabled = True
logging.getLogger("matplotlib").setLevel(logging.WARNING)
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.patches import Circle, Rectangle, Ellipse
# import copy
# from sklearn.cluster import KMeans
from scipy.spatial import Voronoi, cKDTree
import logging
logging.basicConfig(level=logging.DEBUG)
def ra2deg(ra):
s = np.array(ra.split(':'), dtype=float)
if s[0] < 0.0:
if ra.startswith('-'):
sign = -1.0
else:
sign = 1.0
......@@ -445,17 +451,220 @@ def auto_clustering(fig, ax, df, wcs, resid_data, pix_arcmin_scale, nbright,
return final_clusters
# TODO
def voronoy_clustering():
def voronoi_plot_2d_world(vor, ax=None, **kw):
"""
Plot the given Voronoi diagram in 2-D on "world" ax transform
Parameters
----------
vor : scipy.spatial.Voronoi instance
Diagram to plot
ax : matplotlib.axes.Axes instance, optional
Axes to plot on
show_points: bool, optional
Add the Voronoi points to the plot.
show_vertices : bool, optional
Add the Voronoi vertices to the plot.
line_colors : string, optional
Specifies the line color for polygon boundaries
line_width : float, optional
Specifies the line width for polygon boundaries
line_alpha: float, optional
Specifies the line alpha for polygon boundaries
point_size: float, optional
Specifies the size of points
Returns
-------
fig : matplotlib.figure.Figure instance
Figure for the plot
See Also
--------
Voronoi
Notes
-----
Requires Matplotlib.
Examples
--------
Set of point:
>>> import matplotlib.pyplot as plt
>>> points = np.random.rand(10,2) #random
Voronoi diagram of the points:
>>> from scipy.spatial import Voronoi, voronoi_plot_2d
>>> vor = Voronoi(points)
using `voronoi_plot_2d` for visualisation:
>>> fig = voronoi_plot_2d(vor)
using `voronoi_plot_2d` for visualisation with enhancements:
>>> fig = voronoi_plot_2d(vor, show_vertices=False, line_colors='orange',
... line_width=2, line_alpha=0.6, point_size=2)
>>> plt.show()
"""
from matplotlib.collections import LineCollection
if ax:
xlim = ax.get_xlim()
ylim = ax.get_ylim()
if vor.points.shape[1] != 2:
raise ValueError("Voronoi diagram is not 2-D")
if kw.get('show_points', True):
point_size = kw.get('point_size', None)
ax.plot(vor.points[:,0], vor.points[:,1], '+', markersize=point_size, transform=ax.get_transform("world"))
if kw.get('show_vertices', True):
ax.plot(vor.vertices[:,0], vor.vertices[:,1], '.', transform=ax.get_transform("world"))
line_colors = kw.get('line_colors', 'k')
line_width = kw.get('line_width', 1.0)
line_alpha = kw.get('line_alpha', 1.0)
center = vor.points.mean(axis=0)
ptp_bound = vor.points.ptp(axis=0)
finite_segments = []
infinite_segments = []
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
simplex = np.asarray(simplex)
if np.all(simplex >= 0):
finite_segments.append(vor.vertices[simplex])
else:
i = simplex[simplex >= 0][0] # finite end Voronoi vertex
t = vor.points[pointidx[1]] - vor.points[pointidx[0]] # tangent
t /= np.linalg.norm(t)
n = np.array([-t[1], t[0]]) # normal
midpoint = vor.points[pointidx].mean(axis=0)
direction = np.sign(np.dot(midpoint - center, n)) * n
if (vor.furthest_site):
direction = -direction
far_point = vor.vertices[i] + direction * ptp_bound.max()
infinite_segments.append([vor.vertices[i], far_point])
ax.add_collection(LineCollection(finite_segments,
colors=line_colors,
lw=line_width,
alpha=line_alpha,
linestyle='solid',
transform=ax.get_transform("world")))
ax.add_collection(LineCollection(infinite_segments,
colors=line_colors,
lw=line_width,
alpha=line_alpha,
linestyle='dashed',
transform=ax.get_transform("world")))
if ax:
ax.set_xlim(xlim)
ax.set_ylim(ylim)
return ax.figure
def write_df_voronoi(df, vor, output=None):
output = output or 'model_clustered.txt'
with open(output, 'w') as out:
out.write("Format = Name,Patch,Type,Ra,Dec,I,Q,U,V,SpectralIndex,LogarithmicSI,ReferenceFrequency='1399603271.48438',MajorAxis,MinorAxis,Orientation\n")
nn_tree = cKDTree(vor.points)
dists, regs = nn_tree.query(np.array([df.ra, df.dec]).T)
df['nn'] = regs
df['dist'] = dists
for i, p in enumerate(vor.points):
clust_df = df.query('nn == @i').copy()
clust_df.loc[clust_df.index,'Patch'] = 'cluster{}'.format(i+1)
with open(output, 'a') as out:
out.write(', cluster{}, POINT, , , , , , , , , , , ,\n'.format(i+1))
clust_df.to_csv(out, index=False, header=False, columns=df.columns[:-4])
return output
def voronoi_clustering(fig, ax, df, wcs, resid_data, nbright, nclusters,
boxsize=250, same_source_radius=5, central_region=True):
"""
Use Voronoy clustering instead of fixed radius around sources
Use Voronoi clustering instead of fixed radius around sources
"""
pass
# logging.info('Checking {} brightest model components'.format(nbright))
bright_df = df.sort_values('I')[::-1][:nbright][['ra', 'dec', 'I']]
# csnrs = []
# cfluxes = []
# cmeasures = [] # the main value to clusterize by
# cellipse_params = []
# fmin, fmax = min(a.I), max(a.I)
rms = mad(resid_data)
# resid_mean = np.mean(resid_data)
logging.info('Getting measures for the potential clusters...')
clusters = []
clusters_centers = [] #
for ra, dec, flux in bright_df.values:
c = SkyCoord(ra, dec, unit='deg')
px, py = np.round(wcs.all_world2pix(c.ra, c.dec, 0)).astype(int)
# skip the edge sources
if (abs(px-resid_data.shape[1]) < boxsize) or (abs(py-resid_data.shape[0]) < boxsize):
# logging.debug('Skipping the edge source')
continue
# Check if the component already in a cluster
if clusters and any([c.separation(_).arcmin<same_source_radius for _ in clusters]):
continue
small_resid = resid_data[py-boxsize:py+boxsize, px-boxsize:px+boxsize]
ellipse_mean, ecc, amaj, numpix = ellipses_coh(small_resid, amin=20, amax=boxsize-1, dr=1.0)
if abs(ellipse_mean/rms) > 1.4:
# rect = plt.Rectangle((px-boxsize, py-boxsize), 2*boxsize, 2*boxsize,
# lw=1, color='k', fc='none', alpha=0.3)
# ax.add_artist(rect)
clusters_centers.append([ra, dec])
clusters.append(c)
print(ra, dec)
if (isinstance(nclusters, int)) and (len(clusters_centers) >= nclusters):
logging.debug('Max cluster number reached. Breaking...')
break
if isinstance(nclusters, int) and len(clusters_centers) < nclusters:
logging.warning('Decreasing number of clusters')
nclusters = len(clusters_centers)
def main(img, resid, model, auto=True, add_manual=False, nclusters=5, boxsize=250,
# X = np.vstack([df.ra, df.dec]).T
# kmeans = KMeans(n_clusters=nclusters, init=clusters_centers, n_init=1, random_state=0, max_iter=1)
# kmeans.fit(X)
# y_kmeans = kmeans.predict(X) # cluster index for each observation
# ax.scatter(clusters_centers[:, 0], clusters_centers[:, 1], c='black', s=100, alpha=0.5, transform=ax.get_transform("world"))
vor = Voronoi(np.array(clusters_centers))
voronoi_plot_2d_world(vor, ax=ax, show_vertices=False)
return vor
def main(img, resid, model, clustering_method='Voronoi', add_manual=False, nclusters=10, boxsize=250,
nbright=80, cluster_radius=5, cluster_overlap=1.6):
"""
clustering
methods:
Voronoi
auto
manual
"""
imgbase = os.path.splitext(img)[0]
output = imgbase + '-clustered.txt'
......@@ -476,22 +685,28 @@ def main(img, resid, model, auto=True, add_manual=False, nclusters=5, boxsize=25
pix_arcmin_scale = f[0].header['CDELT2']*60
# racen = f[0].header['CRVAL1']
# deccen = f[0].header['CRVAL2']
cluster_radius = angles.Angle(cluster_radius, unit='arcmin')
fig = plt.figure(figsize=[12,12])
ax = fig.add_subplot(1,1,1, projection=wcs.celestial)
vmin, vmax = np.percentile(image_data, 5), np.percentile(image_data, 95)
ax.imshow(resid_data, vmin=vmin, vmax=vmax, origin='lower')#cmap='gray', vmin=2e-5, vmax=0.1)#, norm=LogNorm())
if auto:
if clustering_method.lower() == 'voronoi':
vor = voronoi_clustering(fig, ax, df, wcs, resid_data, nbright, nclusters=nclusters)
write_df_voronoi(df, vor, output=output)
elif clustering_method.lower() == 'auto':
cluster_radius = angles.Angle(cluster_radius, unit='arcmin')
clusters = auto_clustering(fig, ax, df, wcs, resid_data, pix_arcmin_scale, nbright, cluster_radius,
cluster_overlap, boxsize=boxsize, nclusters=nclusters)
if add_manual:
clusters_man = manual_clustering(fig, ax, wcs, pix_arcmin_scale, startnum=len(clusters)+1)
clusters = clusters + clusters_man
else:
write_df(df, clusters, output=output)
elif clustering_method.lower() == 'manual':
clusters = manual_clustering(fig, ax, wcs, pix_arcmin_scale)
if clusters:
write_df(df, clusters, output=output)
fig.tight_layout()
fig.savefig(imgbase+'-clustering.png')
return output
......@@ -500,15 +715,7 @@ def main(img, resid, model, auto=True, add_manual=False, nclusters=5, boxsize=25
### if __name__ == "__main__":
if __name__ == "__main__":
# base = '/home/kutkin/apertif/clustering/191209026/01/dical2-'
# base = '/home/kutkin/apertif/clustering/191209026/15/dical2-'
# base = '/home/kutkin/apertif/clustering/191010042/00/dical2-'
# base = '/home/kutkin/apertif/clustering/191006041_21/dical2-'
# base = '/home/kutkin/apertif/clustering/191010042_25/dical-'
# base = '/home/kutkin/apertif/clustering/191209026/10/dical2-'
base = '/home/kutkin/apertif/clustering/191010041/23/dical2-'
# base = '/home/kutkin/apertif/clustering/190915041/25/dical2-'
base = '/kutkin/apipeline/to_validate/210109001_06-dical-'
img = base + 'image.fits'
resid = base + 'residual.fits'
model = base + 'sources.txt'
......@@ -516,5 +723,5 @@ if __name__ == "__main__":
# resid = sys.argv[2]
# model = sys.argv[3]
main(img, resid, model, auto=True, nclusters='auto')
main(img, resid, model, clustering_method='Voronoi', nclusters=6)
......@@ -199,7 +199,7 @@ def dical(msin, srcdb, msout=None, h5out=None, solint=1, startchan=0, split_ncha
def ddecal(msin, srcdb, msout=None, h5out=None, solint=1, nfreq=15,
startchan=0, nchan=192, mode='diagonal', uvlambdamin=500, subtract=True):
startchan=0, nchan=0, mode='diagonal', uvlambdamin=500, subtract=True):
""" Perform direction dependent calibration with DPPP """
h5out = h5out or os.path.split(msin)[0] + '/ddcal.h5'
msbase = os.path.basename(msin).split('.')[0]
......
......@@ -76,10 +76,11 @@ clean4:
cluster:
nbright: 80 # number of brightest clean components (CC) to check for artefacts
boxsize: 250 # the boxsize around CC in pixels where to check for artefacts
nclusters: 10 # number of clusters ('auto' -- to set automatically)
nclusters: 6 # number of clusters ('auto' -- to set automatically)
clustering_method: 'Voronoi'
# the following is only for 'auto' and 'manual' mathods:
cluster_radius: 5 # arcmin
cluster_overlap: 1.6 # if lower than 2 clusters can intersect
auto: True
cluster_overlap: 2 # if lower than 2 clusters can intersect
add_manual: False
######################## DD CALIBRATION #######################
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment