Plotting a fits image with aplpy using the right wcs transformation - fits

I want to plot a fits file with aplpy but I get the following error:
INFO: The WCS transformation has more axes (3) than the image it is associated with (2) [astropy.wcs.wcs]
ERROR: IndexError: list index out of range [aplpy.wcs_util]
If I delete the header key words CTYPE3, CD3_3, WAT3_001 then I don't get the error anymore, but when plotting the fits file I see that the WCS transformation has not been done properly with RA and DEC coordinates all over the place.
This is the header:
HCG89_halpha_plot.fits[2929,2881][real]: HCG89-SII
No bad pixels, min=0., max=0. (old)
Line storage mode, physdim [2929,2881], length of user area 5144 s.u.
Created Tue 15:40:54 28-Jan-2014, Last modified Thu 22:20:03 30-Jan-2014
Pixel file "HCG89_halpha_plot.fits" [ok]
ORIGIN = 'NOAO-IRAF FITS Image Kernel July 2003' / FITS file originator
IRAF-TLM= '2014-01-30T19:20:03' / Time of last modification
OBJECT = 'HCG89-SII' / Name of the object observed
DATE-OBS= '2013-10-11T00:14:11.700' / Date of observation
DATE = '2014-01-28T12:40:54' / Date Format is YYYY-MM-DD
TIME = '00:14:11.07 to 00:19:11.40' / ~ Start & Stop of Exposure
N_PARAM = 80 / Number of Parameters
TELESCOP= 'SOAR 4.1m'
INSTRUME= 'Goodman Spectrograph'
NOTES = ' '
RA = '21:20:09.042' / right ascension [hh:mm:ss.sss]
DEC = '-3:55:23.160' / declination [dd:mm:ss.sss]
AIRMASS = 1.13 / airmass at approx. start of exposure
UT = '00:14:10.9' / time at approx. start of exposure [UTC]
FOCUS = -1021.0 / SOAR telescope focus
MOUNT_AZ= 17.1482 / SOAR mount azimuth
MOUNT_EL= 62.2622 / SOAR mount elevation
ROTATOR = 228.003 / Nasymth cage rotator angle [deg]
POSANGLE= 360.0 / position angle [deg. E of N]
SEEING = -1.0 / DIMM seeing [arcsec]
LST = '20:50:17.4' / Local Sidereal Time [hh:mm:ss.sss]
OBSRA = '21:20:09.946' / target right ascension [hh:mm:ss.sss]
OBSDEC = '-3:55:32.480' / target declination [hh:mm:ss.sss]
CAM_ANG = 0.014461 / camera angle [deg]
GRT_ANG = 0.661 / grating angle [deg]
CAM_TARG= 0.0 / camera target [deg]
GRT_TARG= 0.661 / grating target [deg]
CAM_FOC = 660 / camera focus
COLL_FOC= 1002 / collimator focus
FILTER = 'SII ' / primary filter wheel
FILTER2 = '<NO FILTER>' / secondary filter wheel
GRATING = '<NO GRATING>' / VPH grating [1/mm]
SLIT = '<NO MASK>' / slit [arcsec]
COL_TEMP= 10.421875 / coll ext temp (deg C)
CAM_TEMP= 14.234375 / cam ext temp (deg C)
EXPTIME = 300.0 / integration time
RDNOISE = 4.74 / CCD readnoise [e-]
GAIN = 1.4 / CCD gain [e-/ADU]
OBSTYPE = 'OBJECT ' / observation type
OBSERVER= ' '
PROPOSAL= ' '
EQUINOX = 2000.0 / equinox of coordinates
CRPIX1 = 1477.39599990845 / Reference pixel on axis 1
CRPIX2 = 1488.23082170899 / Reference pixel on axis 2
CRVAL1 = 320.041401353 / Value at ref. pixel on axis 1
CRVAL2 = -3.91652009277 / Value at ref. pixel on axis 2
CTYPE1 = 'RA---TAN' / Type of co-ordinate on axis 1
CTYPE2 = 'DEC--TAN' / Type of co-ordinate on axis 2
PC1_1 = 0.999884048946387 / Transformation matrix element
PC1_2 = 0.0152278909432213 / Transformation matrix element
PC2_1 = -0.0140395410920601 / Transformation matrix element
PC2_2 = 1.0 / Transformation matrix element
MJD-OBS = 56576.0098576389 / Modified Julian Date of observation
RADESYS = 'FK5 ' / Reference frame for RA/DEC values
DISPAXIS= 1
DETSIZE = '[1:4096,1:4096]'
TRIMSEC = '[1:3096,1:3096]'
CCDSIZE = '[1:4096,1:4096]'
CCDSUM = '1 1 '
OPENTIME= '00:14:11.611' / GPS-Synched Time for Shutter Open (UT)
INSTRUME= 'Goodman Spectrograph'
COMMENT Triggered Acquisition, Exp Time= 05:00
WCSDIM = 3
LTM1_1 = 1.0
LTM2_2 = 1.0
LTM3_3 = 1.0
WAXMAP01= '1 0 2 0 0 0 '
WAT0_001= 'system=physical'
WAT1_001= 'wtype=linear axtype=ra'
WAT2_001= 'wtype=linear axtype=dec'
WAT3_001= 'wtype=linear'
LTV1 = -159.604000091553
LTV2 = -134.766998291016
IMCMB001= '0073.HCG89-SII_scaled.fits'
IMCMB002= '0074.HCG89-SII_scaled.fits'
IMCMB003= '0079.HCG89-SII_scaled.fits'
IMCMB004= '0080.HCG89-SII_scaled.fits'
IMCMB005= '0083.HCG89-SII_scaled.fits'
IMCMB006= '0084.HCG89-SII_scaled.fits'
NCOMBINE= 6
CTYPE3 = 'LINEAR '
CD1_1 = 4.03250815042544E-5
CD2_2 = 4.04084336509694E-5
CD3_3 = 1.

I am not sure because I don't have access to your image, but do you have a multi-extension FITS file? If you do you might have the same error I had (Find physical coordinates of a pixel in a fits file with python).
Check to make sure you are using the HDU and header that are connected to the science data.

Related

How to define prob_threshold to avoid double counting during object detection?

I am developing an object detection application using SSD model and I have defined the bounding box and the prob_threshold, when I run the code I realise that the model double count person in frame. Please see below my code
## Setting Pro_threshold for person detection filtering
try:
prob_threshold = float(os.environ['PROB_THRESHOLD'])
except:
prob_threshold = 0.4
def draw_boxes(frame, result, width, height):
"""
:Draws bounding box when person is detected on video frame
:and the probability is more than the specified threshold
"""
present_count = 0
for obj in result[0][0]:
conf = obj[2]
if conf >= prob_threshold:
xmin = int(obj[3] * width)
ymin = int(obj[4] * height)
xmax = int(obj[5] * width)
ymax = int(obj[6] * height)
cv2.rectangle(frame, (xmin, ymin), (xmax, ymax), (0, 255, 0), 3)
present_count += 1
return frame, present_count
In order to ensure that the number of people in the video frame was not double counted I first initialise the variables and used if statement to calculate the duration spent by each person in the video frame.
## Initialise variables##
present_request_id = 0
present_count = 0
start_time = 0
last_count = 0
total_count = 0
## Calculating the duration a person spent on video#
if present_count < last_count and int(time.time() - start_time) >=1:
duration = int(time.time() - start_time)
if duration > 0:
# Publish messages to the MQTT server
client.publish("person/duration",
json.dumps({"duration": duration + lagtime}))
else:
lagtime += 1
log.warning(lagtime)
adding below argument and experimenting between the seconds, in my case I experimented between 1secs and 3sec
int(time.time() - start_time) >=1
see GitHub Repo for explanation.

How to obtain 2D Cutout of an image from a SkyCoord position?

I am following the example from Astropy docs for 2D Cutout.
The header of my FITS file:
SIMPLE = T / file does conform to FITS standard
BITPIX = -32 / number of bits per data pixel
NAXIS = 3 / number of data axes
NAXIS1 = 512 / length of data axis 1
NAXIS2 = 512 / length of data axis 2
NAXIS3 = 3 / length of data axis 3
EXTEND = T / FITS dataset may contain extensions
COMMENT FITS (Flexible Image Transport System) format is defined in 'Astronomy
COMMENT and Astrophysics', volume 376, page 359; bibcode: 2001A&A...376..359H
SURVEY = 'DECaLS '
VERSION = 'DR8-south'
IMAGETYP= 'image '
BANDS = 'grz '
BAND0 = 'g '
BAND1 = 'r '
BAND2 = 'z '
CTYPE1 = 'RA---TAN' / TANgent plane
CTYPE2 = 'DEC--TAN' / TANgent plane
CRVAL1 = 186.11382 / Reference RA
CRVAL2 = 0.15285422 / Reference Dec
CRPIX1 = 256.5 / Reference x
CRPIX2 = 256.5 / Reference y
CD1_1 = -7.27777777777778E-05 / CD matrix
CD1_2 = 0. / CD matrix
CD2_1 = 0. / CD matrix
CD2_2 = 7.27777777777778E-05 / CD matrix
IMAGEW = 512. / Image width
IMAGEH = 512. / Image height
So far what I have tried :
from astropy.coordinates import SkyCoord
from astropy.wcs import WCS
position = SkyCoord(hdu[0].header['CRVAL1']*u.deg,hdu[0].header['CRVAL2']*u.deg)
size = 200*u.pixel
wcs1 = WCS(hdu[0].header)
cutout = Cutout2D(hdu[0].data[0], position ,size, wcs = wcs1 )
I run into error in the last line.
Error :
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-142-7cc21a13e941> in <module>
----> 1 cutout = Cutout2D(hdu[0].data[0], position ,size, wcs = wcs1 )
/Applications/anaconda3/lib/python3.7/site-packages/astropy/nddata/utils.py in __init__(self, data, position, size, wcs, mode, fill_value, copy)
714 if wcs is not None:
715 self.wcs = deepcopy(wcs)
--> 716 self.wcs.wcs.crpix -= self._origin_original_true
717 self.wcs.array_shape = self.data.shape
718 if wcs.sip is not None:
ValueError: operands could not be broadcast together with shapes (3,) (2,) (3,)
My guess is it is because naxis =3 in my file and the documentation assumes naxis = 2. Though I am not sure if that is the actual issue here. Can anybody help fix this error?
Since your WCS is 3d, but you're getting a 2d cutout, you need to drop the 3rd dimension. Try cutout = Cutout2D(hdu[0].data[0], position ,size, wcs = wcs1.celestial ), where .celestial is a convenience tool to drop the third dimension in the WCS.

Rotating a 2d sub-array using numpy without aliasing effects

I would like to rotate only the positive value pixels in my 2d array some degree about the center point. The data represents aerosol concentrations from a plume dispersion model, and the chimney position is the origin of rotation.
I would like to rotate this dispersion pattern given the wind direction.
The concentrations are first calculated for a wind direction along the x-axis and then translated to their rotated position using a 2d linear rotation about the center point of my array (the chimney position) for all points whose concentration is > 0.
The input X,Y to the rotation formula are pixel indexes.
My problem is that the output is aliased since integers become floats. In order to obtain integers, I rounded up or down the output. However, this creates null cells which become increasingly numerous as the angle increases.
Can anyone help me find a solution to my problem? I would like to fix this problem if possible using numpy, or a minimum of packages...
The part of my script that deals with computing the concentrations and rotating the pixel by 50°N is the following. Thank you for your help.
def linear2D_rotation(xcoord,ycoord,azimuth_degrees):
radians = (90 - azimuth_degrees) * (np.pi / 180) # in radians
xcoord_rotated = (xcoord * np.cos(radians)) - (ycoord * np.sin(radians))
ycoord_rotated = (xcoord * np.sin(radians)) + (ycoord * np.cos(radians))
return xcoord_rotated,ycoord_rotated
u_orient = 50 # wind orientation in degres from North
kernel = np.zeros((NpixelY, NpixelX)) # initialize matrix
Yc = int((NpixelY - 1) / 2) # position of central pixel
Xc = int((NpixelX - 1) / 2) # position of central pixel
nk = 0
for Y in list(range(0,NpixelX)):
for X in list(range(0,NpixelY)):
# compute concentrations only in positive x-direction
if (X-Xc)>0:
# nnumber of pixels to origin point (chimney)
dx = ((X-Xc)+1)
dy = ((Y-Yc)+1)
# distance of point to origin (chimney)
DX = dx*pixel_size_X
DY = dy*pixel_size_Y
# compute diffusivity coefficients
Sy, Sz = calcul_diffusivity_coeff(DX, stability_class)
# concentration at ground level below the centerline of the plume
C = (Q / (2 * np.pi * u * Sy * Sz)) * \
np.exp(-(DY / (2 * Sy)) ** 2) * \
(np.exp(-((Z - H) / (2 * Sz)) ** 2) + np.exp(-((Z + H) / (2 * Sz)) ** 2)) # at point away from center line
C = C * 1e9 # convert MBq to Bq
# rotate only if concentration value at pixel is positive
if C > 1e-12:
X_rot, Y_rot = linear2D_rotation(xcoord=dx, ycoord=dy,azimuth_degrees=u_orient)
X2 = int(round(Xc+X_rot))
Y2 = int(round(Yc-Y_rot)) # Y increases downwards
# pixels that fall out of bounds -> ignore
if (X2 > (NpixelX - 1)) or (X2 < 0) or (Y2 > (NpixelY - 1)):
continue
else:
# replace new pixel position in kernel array
kernel[Y2, X2] = C
The original array to be rotated
The rotated array by 40°N showing the data loss
Your problem description is not 100% clear, but here are a few recommendations:
1.) Don't reinvent the wheel. There are standard solutions for things like rotating pixels. Use them! In this case
scipy.ndimage.affine_transform for performing the rotation
a homogeneous coordinate matrix for specifying the rotation
nearest neighbor interpolation (parameter order=0 in code below).
2.) Don't loop where not necessary. The speed you gain by not processing non-positive pixels is nothing against the speed you lose by looping. Compiled functions can ferry around a lot of redundant zeros before hand-written python code catches up with them.
3.) Don't expect a solution that maps pixels one-to-one because it is a fact that there will be points that are no ones nearest neighbor and points that are nearest neighbor to multiple other points. With that in mind, you may want to consider a higher order, smoother interpolation.
Comparing your solution to the standard tools solution we find that the latter
gives a comparable result much faster and without those hole artifacts.
Code (without plotting). Please note that I had to transpose and flipud to align the results :
import numpy as np
from scipy import ndimage as sim
from scipy import stats
def mock_data(n, Theta=50, put_neg=True):
y, x = np.ogrid[-20:20:1j*n, -9:3:1j*n, ]
raster = stats.norm.pdf(y)*stats.norm.pdf(x)
if put_neg:
y, x = np.ogrid[-5:5:1j*n, -3:9:1j*n, ]
raster -= stats.norm.pdf(y)*stats.norm.pdf(x)
raster -= (stats.norm.pdf(y)*stats.norm.pdf(x)).T
return {'C': raster * 1e-9, 'Theta': Theta}
def rotmat(Theta, offset=None):
theta = np.radians(Theta)
c, s = np.cos(theta), np.sin(theta)
if offset is None:
return np.array([[c, -s] [s, c]])
R = np.array([[c, -s, 0], [s, c,0], [0,0,1]])
to, fro = np.identity(3), np.identity(3)
offset = np.asanyarray(offset)
to[:2, 2] = offset
fro[:2, 2] = -offset
return to # R # fro
def f_pp(C, Theta):
m, n = C.shape
clipped = np.maximum(0, 1e9 * data['C'])
clipped[:, :n//2] = 0
M = rotmat(Theta, ((m-1)/2, (n-1)/2))
return sim.affine_transform(clipped, M, order = 0)
def linear2D_rotation(xcoord,ycoord,azimuth_degrees):
radians = (90 - azimuth_degrees) * (np.pi / 180) # in radians
xcoord_rotated = (xcoord * np.cos(radians)) - (ycoord * np.sin(radians))
ycoord_rotated = (xcoord * np.sin(radians)) + (ycoord * np.cos(radians))
return xcoord_rotated,ycoord_rotated
def f_OP(C, Theta):
kernel = np.zeros_like(C)
m, n = C.shape
for Y in range(m):
for X in range(n):
if X > n//2:
c = C[Y, X] * 1e9
if c > 1e-12:
dx = X - n//2 + 1
dy = Y - m//2 + 1
X_rot, Y_rot = linear2D_rotation(xcoord=dx, ycoord=dy,azimuth_degrees=Theta)
X2 = int(round(n//2+X_rot))
Y2 = int(round(m//2-Y_rot)) # Y increases downwards
# pixels that fall out of bounds -> ignore
if (X2 > (n - 1)) or (X2 < 0) or (Y2 > (m - 1)):
continue
else:
# replace new pixel position in kernel array
kernel[Y2, X2] = c
return kernel
n = 100
data = mock_data(n, 70)

Description of parameters of GDAL SetGeoTransform

Can anyone help me with parameters for SetGeoTransform? I'm creating raster layers with GDAL, but I can't find description of 3rd and 5th parameter for SetGeoTransform. It should be definition of x and y axis for cells. I try to find something about it here and here, but nothing.
I need to find description of these two parameters... It's a value in degrees, radians, meters? Or something else?
The geotransform is used to convert from map to pixel coordinates and back using an affine transformation. The 3rd and 5th parameter are used (together with the 2nd and 4th) to define the rotation if your image doesn't have 'north up'.
But most images are north up, and then both the 3rd and 5th parameter are zero.
The affine transform consists of six coefficients returned by
GDALDataset::GetGeoTransform() which map pixel/line coordinates into
georeferenced space using the following relationship:
Xgeo = GT(0) + Xpixel*GT(1) + Yline*GT(2)
Ygeo = GT(3) + Xpixel*GT(4) + Yline*GT(5)
See the section on affine geotransform at:
https://gdal.org/tutorials/geotransforms_tut.html
I did do like below code.
As a result I was able to do same with SetGeoTransform.
# new file
dst = gdal.GetDriverByName('GTiff').Create(OUT_PATH, xsize, ysize, band_num, dtype)
# old file
ds = gdal.Open(fpath)
wkt = ds.GetProjection()
gcps = ds.GetGCPs()
dst.SetGCPs(gcps, wkt)
...
dst.FlushCache()
dst = Nonet
Given information from the aforementioned gdal datamodel docs, the 3rd & 5th parameters of SatGeoTransform (x_skew and y_skew respectively) can be calculated from two control points (p1, p2) with known x and y in both "geo" and "pixel" coordinate spaces. p1 should be above-left of p2 in pixelspace.
x_skew = sqrt((p1.geox-p2.geox)**2 + (p1.geoy-p2.geoy)**2) / (p1.pixely - p2.pixely)`
y_skew = sqrt((p1.geox-p2.geox)**2 + (p1.geoy-p2.geoy)**2) / (p1.pixelx - p2.pixelx)`
In short this is the ratio of Euclidean distance between the points in geospace to the height (or width) of the image in pixelspace.
The units of the parameters are "geo"length/"pixel"length.
Here is a demonstration using the corners of the image stored as control points (gcps):
import gdal
from math import sqrt
ds = gdal.Open(fpath)
gcps = ds.GetGCPs()
assert gcps[0].Id == 'UpperLeft'
p1 = gcps[0]
assert gcps[2].Id == 'LowerRight'
p2 = gcps[2]
y_skew = (
sqrt((p1.GCPX-p2.GCPX)**2 + (p1.GCPY-p2.GCPY)**2) /
(p1.GCPPixel - p2.GCPPixel)
)
x_skew = (
sqrt((p1.GCPX-p2.GCPX)**2 + (p1.GCPY-p2.GCPY)**2) /
(p1.GCPLine - p2.GCPLine)
)
x_res = (p2.GCPX - p1.GCPX) / ds.RasterXSize
y_res = (p2.GCPY - p1.GCPY) / ds.RasterYSize
ds.SetGeoTransform([
p1.GCPX,
x_res,
x_skew,
p1.GCPY,
y_skew,
y_res,
])

Verlet integrator + friction

I have been following "A Verlet based approach for 2D game physics" on Gamedev.net and I have written something similar.
The problem I am having is that the boxes slide along the ground too much.
How can I add a simple rested state thing where the boxes will have more friction and only slide a tiny bit?
Just introduce a small, constant acceleration on moving objects that points in the direction opposite to the motion. And make sure it can't actually reverse the motion; if you detect that in an integration step, just set the velocity to zero.
If you want to be more realistic, the acceleration should derive from a force which is proportional to the normal force between the object and the surface it's sliding on.
You can find this in any basic physics text, as "kinetic friction" or "sliding friction".
At the verlet integration: r(t)=2.00*r(t-dt)-1.00*r(t-2dt)+2at²
change the multipliers to 1.99 and 0.99 for friction
Edit: this is more true:
r(t)=(2.00-friction_mult.)*r(t-dt)-(1.00-friction_mult.)*r(t-2dt)+at²
Here is a simple time stepping scheme (symplectic Euler method with manually resolved LCP) for a box with Coulomb friction and a spring (frictional oscillator)
mq'' + kq + mu*sgn(q') = F(t)
import numpy as np
import matplotlib.pyplot as plt
q0 = 0 # initial position
p0 = 0 # initial momentum
t_start = 0 # initial time
t_end = 10 # end time
N = 500 # time points
m = 1 # mass
k = 1 # spring stiffness
muN = 0.5 # friction force (slip and maximal stick)
omega = 1.5 # forcing radian frequency [RAD]
Fstat = 0.1 # static component of external force
Fdyn = 0.6 # amplitude of harmonic external force
F = lambda tt,qq,pp: Fstat + Fdyn*np.sin(omega*tt) - k*qq - muN*np.sign(pp) # total force, note sign(0)=0 used to disable friction
zero_to_disable_friction = 0
omega0 = np.sqrt(k/m)
print("eigenfrequency f = {} Hz; eigen period T = {} s".format(omega0/(2*np.pi), 2*np.pi/omega0))
print("forcing frequency f = {} Hz; forcing period T = {} s".format(omega/(2*np.pi), 2*np.pi/omega))
time = np.linspace(t_start, t_end, N) # time grid
h = time[1] - time[0] # time step
q = np.zeros(N+1) # position
p = np.zeros(N+1) # momentum
absFfriction = np.zeros(N+1)
q[0] = q0
p[0] = p0
for n, tn in enumerate(time):
p1slide = p[n] + h*F(tn, q[n], p[n]) # end-time momentum, assuming sliding
q1slide = q[n] + h*p1slide/m # end-time position, assuming sliding
if p[n]*p1slide > 0: # sliding goes on
q[n+1] = q1slide
p[n+1] = p1slide
absFfriction[n] = muN
else:
q1stick = q[n] # assume p1 = 0 at t=tn+h
Fstick = -p[n]/h - F(tn, q1stick, zero_to_disable_friction) # friction force needed to stop at t=tn+h
if np.abs(Fstick) <= muN:
p[n+1] = 0 # sticking
q[n+1] = q1stick
absFfriction[n] = np.abs(Fstick)
else: # sliding starts or passes zero crossing of velocity
q[n+1] = q1slide # possible refinements (adapt to slip-start or zero crossing)
p[n+1] = p1slide
absFfriction[n] = muN