<jupyter_start><jupyter_text>The astroImage Object The astroIm package rebolves around the astroImage class to manipulate fits images, and is tuned to particularly work in with Far-IR/Sub-mm images. Many of the methods provided are wrappers to astropy functions. Here we show some of the more common/basic methods of the astroImage class. To finish the tutorial we show the use-case where you want to match the resolution and projection of one image to another. For more specialised examples see some of our other notebooks or the API.This tutorial obtains data from the Dustpedia database, however, you can switch it to whatever fits file you want.  Obtaining Data for this Tutorial First we download example data from the dustpedia database of a SPIRE image of M100.<jupyter_code># import wget module to download data
import urllib.request
import os
from os.path import join as pj

# set folder to store data
folder = "/home/gandalf/spxmws/Hard-Drive/dustpedia"

# download 250 and 500um data file
if os.path.isfile(pj(folder,"NGC4321_SPIRE_250.fits")) is False:
    urllib.request.urlretrieve("http://www.astro.cf.ac.uk/pub/Matthew.Smith/web/astroIm/NGC4321_SPIRE_250.fits",pj(folder,"NGC4321_SPIRE_250.fits"))
if os.path.isfile(pj(folder,"NGC4321_SPIRE_500.fits")) is False:
    urllib.request.urlretrieve("http://www.astro.cf.ac.uk/pub/Matthew.Smith/web/astroIm/NGC4321_SPIRE_500.fits",pj(folder,"NGC4321_SPIRE_500.fits"))<jupyter_output><empty_output><jupyter_text>Importing the module and loading an imageIn the next box we will open a fits file. The code by defaults to loading the first extension, but other extensions can be loaded with the 'ext=X' parameter. The code searches the header for the instrument, band and units of the image. If this information is not present, it can be added manually (see the commented line below). Instead of a fits file the astroImage class can be initiated with an astropy hdu object (see second commented line).<jupyter_code># import module
from astroIm import astroImage

# load your fits file - adjust path to your fits file
fitsImage = astroImage(pj(folder,"NGC4321_SPIRE_250.fits"), dustpediaHeaderCorrect=True)

# load line with extra inputs defined
#fitsImage = astroImage("file.fits", ext=0, instrument='SPIRE', band=250, unit='Jy/pix', "FWHM":10.0)

# load from fits hdu
#fitsImage = astroImage(hduobject, load=False)<jupyter_output><empty_output><jupyter_text>As mentioned the code attempts to ascertain the instrument and band (if not given when loading the image).<jupyter_code>print(" Telescope: ", fitsImage.telescope)
print("Instrument: ", fitsImage.instrument) 
print("      Band: ", fitsImage.band)<jupyter_output>Telescope:  Herschel
Instrument:  SPIRE
      Band:  250<jupyter_text>Instead of providing a 2D image, a cube can be given. For more information how to do this see our 'Loading From a FITS Cube' Notebook Aside on Coordinates and QuantitiesThe astroImage object tries to make most use of both [astropy](https://www.astropy.org/) SkyCoordinates and [astropy](https://www.astropy.org/) units. This has several advantages, for example when specifing a coordinate the user can specify what coordinate system is used irregardless of the coordinate system of the image, or any units can be used for sizes.For the remainder of this notebook, we'll specify the centre of M100.<jupyter_code># first let's import astropy units so numbers can be given as quantities, and the SkyCoord class for locations
# import astropy units
import astropy.units as u
# import astropy Sky-Coordinates
from astropy.coordinates import SkyCoord

# set the centre coordinate of M100
centreM100 = SkyCoord(ra=185.72875*u.degree, dec=15.8225*u.degree, frame='icrs')<jupyter_output><empty_output><jupyter_text>Quick PlottingQuite often its useful to see a quick image of your data. The astroImage class has a method to obtain a quick image calling the [APLpy](https://aplpy.github.io/) package. The default output is to use a linear stretch:<jupyter_code># default plot
fitsImage.plot()<jupyter_output>INFO: Auto-setting vmin to -4.702e-03 [aplpy.core]
INFO: Auto-setting vmax to  3.212e-02 [aplpy.core]<jupyter_text>Quite often it is better for large dynamic ranges to use a log scale. The plot method has a algorithm to estimate the best parameters for a logscale:<jupyter_code># default quick look plot with log scale
fitsImage.plot(stretch='log')<jupyter_output><empty_output><jupyter_text>As a final example we show some of the additional parameters that can be specified to customise the plot. In this example we set the following: - *cmap*: change the colour map through the cmap parameter (any colourmaps in matplotlib can be sepicifed).  - *hide_colourbar*: set this to True to hide the colourbar from the plot.  - *recentre*: The recentre parameter can be used to zoom in a certain region of the image. *recentre* takes a dictionary which specifies the centre by passing a astropy SkyCood through the 'coord' keyword (this is recommended method, for other options see API), and the size by specifing radius through 'rad' keyword (alternatively width and height can be specified for non-square plots).<jupyter_code># call quick look plot with log scale, different colourmap,  and centre specified
fitsImage.plot(stretch='log', cmap='inferno', hide_colourbar=True, recentre={"coord":centreM100, "rad":6.0*u.arcmin})<jupyter_output><empty_output><jupyter_text>Other useful parameters the user can also specify in the method call are:  - *save*: use *save* to set the path where to save the plot.  - *vmin*, *vmax*, *vmid*: the user can specify any of the minimum, maximum or middle (log only) of the colour bar.   - *facecolor*: use this parameter to set the background colour outside the images.  - *nancolour*: use this parameter to set the NaN colour. Accessing the image array or headerSimilar to astropy the image array can be found using the image and header attributes. Here, we show this by finding the mean of the image, its unit, and a header keyword.<jupyter_code>print("Image Mean: ", fitsImage.image.mean(), fitsImage.unit)
print("Image Reference Pix: (", fitsImage.header['CRPIX1'], ",", fitsImage.header['CRPIX2'], ")")<jupyter_output>Image Mean:  8.698689370460493e-05 Jy/pix
Image Reference Pix: ( -498.0 , -4114.0 )<jupyter_text>Obtaining the Pixel SizeTo get the pixel size of the image, just use the 'pixSize' attribute. Sometimes the image header does not contain all the information (or the code could not identify it) when the object was initialised; if you've modified the header the getPixelScale() method will find the pixel size and add the 'pixSize' attribute.<jupyter_code># print the pixel size as attribute
print(fitsImage.pixSize)

# if the header when loading did not have the correct WCS, can run method later to get pixel sizes
#fitsImage.getPixelScale()<jupyter_output>6.0 arcsec<jupyter_text>Converting UnitsPossibly one of the most useful features of the astroImage class is that it provides an easy way to switch the image between different units. The user can provide the conversion to use or the beam area to assume if needed (see commented version).<jupyter_code>print("Initial Units: ", fitsImage.unit)
print("Peak Pixel: ", fitsImage.image.max(), fitsImage.unit, "\n")


# convert units to that desired
fitsImage.convertUnits("MJy/sr")

# if want to provide the conversion or beam area use the following inputs
#fitsImage.convertUnits("MJy/sr", conversion=1.0, beamArea=1.0)

print()
print("New Units: ", fitsImage.unit)
print("Peak Pixel: ", fitsImage.image.max(), fitsImage.unit, "\n")<jupyter_output>Initial Units:  Jy/pix
Peak Pixel:  0.41581757264896013 Jy/pix 

Image converted to:  MJy/sr

New Units:  MJy/sr
Peak Pixel:  491.4174844578513 MJy/sr<jupyter_text>Simple Image ArithmaticThis method is a bit redundant, as you can get the image change it how you want and save it back. Alteratively, the imageManipulation method takes operation (either '+','-','*','/','$**$), followed by the value to use. Here we show standard method where we double the image and use the method to restore the image back.<jupyter_code># traditional (and probably more useful method)
fitsImage.image = fitsImage.image * 2.0

# using the method
fitsImage.imageManipulation("/",2.0)<jupyter_output><empty_output><jupyter_text>Finding the Image Noise, Background Subtraction, and Aperture PhotometryThe astro image package provides methods to find the noise in the background of an image, to do a background subtaction around an individual object, and to perform aperture photometry of one or multiple objects.  **Finding the Background Level of an Image and its Noise, using a Sigma-Clipping Method**The *background_sigmaClip* method is provided to find the background level and noise of an image. The method tries to identify emission from sources using the astropy [photutils](https://photutils.readthedocs.io/en/stable/) *make_source_mask* task to make a source mask (which is returned by the function). With the mask applied we use the [astropy](https://www.astropy.org/) *sigma_clipped_stats* function to find the sigma-clipped median and standard deviation of the background. The parameters of these two methods can be adjusted (see commented command below).Both the background level and the noise are stored as the *bkgMedian* and *bkgStd* attributes of the astroImage object. The source mask found is also returned, that way the same source mask can be provided to use on another image via the *maskMatch* parameter.<jupyter_code># first we will switch back to Jy/pix units
fitsImage.convertUnits('mJy/pix')

# run the background analysis to find background median, standard-deviation, and source mask
mask = fitsImage.background_sigmaClip()

# as this runs on the whole image, the median and standard deviation are availabe via the
print("Background level measured as: ", fitsImage.bkgMedian)
print("Bacground noise measured as: ", fitsImage.bkgStd)<jupyter_output>Image converted to:  mJy/pix
Background level measured as:  -0.28698946672428194
Bacground noise measured as:  0.5179894471486374<jupyter_text>If you want the background level to be automatically subtracted you can set the *apply* parameter to *True*, in this case the *bkgMedian* attribute will equal 0.<jupyter_code># if you want the background level to be subtracted set apply to True (note then bkgLevel will then be 0):
#mask = fitsImage.background_sigmaClip(apply=True)<jupyter_output><empty_output><jupyter_text>Many of the settings for the source-masking and the sigma-clipping can be adjusted, see the API documentation for more details.You can apply a background subtraction with the *constantBackSub* method, just provide it with the value to subtract. Although this method is redundant with the arithmetic method above.<jupyter_code># apply a background subtraction
# fitsImage.constantBackSub(fitsImage.bkgMedian)<jupyter_output><empty_output><jupyter_text>**Local Object Background Subtraction**You may wish to choose a particular region of an image to perform a background subtraction and estimate the image noise, for example around a particular source, or if you want to specify particular regions. Two methods are provided do perform a local background subtraction the *circularAnnulusBackSub* and *ellipticalAnnulusBackSub* methods use a circular or ellipical annulus, respectively (both rely on astropy [photutils](https://photutils.readthedocs.io/en/stable/) at their core).Let's look at the key inputs to these methods:  .circularAnnulusBackSub(centre    => Centre annulus, recommended to provide an astropy SkyCoordinate                                       but can be an (X,Y) pixel coordinate instead.                        inner     => the inner radius of the annulus as an astropy quantity, if just a                                      float is provided that is assumed to be in pixels.                          outer     => Same as for inner but setting the outer radius.                        backNoise => Return the standard deviation of the background region. (True/False)                        apply     => Apply the background subtraction to the image. (True/False)                       )The *ellipticalAnnulusBackSub* but just has more options due to the shape:.ellipticalAnnulusBackSub(centre      => Centre annulus, recommended to provide an astropy SkyCoordinate                                           but can be an (X,Y) pixel coordinate instead.                          inner       => Either the semi-major axis of the inner ellipse (only if axisRatio                                         is set) either an astropy quantity or number of pixels,                                         or a list/tuple/array of the semi-major &amp; semi-minor axis.                             outer       => Same as for inner ellipse but size the outer ellipse.                          PA          => The Position Angle of the ellipses (typically in East of North                                         but see API for full discussion).                          axisRatio   => The axis ratio of the ellipses, only required if only the semi-major                                         values are provided to inner/outer.                          outerCircle => Set the outer boundary of the annulus to be circular (True/False).                          backNoise   => Return the standard deviation of the background region. (True/False)                          apply       => Apply the background subtraction to the image. (True/False)                       )Additional parameters passed to the photutils function are given in the API. Let's try this method using an annulus around M100..<jupyter_code># perform cicular annulus subtraction and noise analysis
circleBack, circlNoise = fitsImage.circularAnnulusBackSub(centre=centreM100, inner=6.0*u.arcmin, outer=7.0*u.arcmin, backNoise=True)

# perform elliptical annulus subtraction and analysis
ellipseBack, ellipNoise = fitsImage.ellipticalAnnulusBackSub(centre=centreM100, inner=6.0*u.arcmin, outer=7.0*u.arcmin, axisRatio=0.85, PA=30.0*u.deg, outerCircle=True, backNoise=True)

# remember if you want to apply the background subtraction add apply=True to either command<jupyter_output><empty_output><jupyter_text>Now let's compare the different values we've measured across all three methods<jupyter_code># Let's now compare the two background and noise estimates
print("\t    Mask/Clip \t Circular    Elliptical ")
print("\t     Method  \t  Annulus      Annulus")
print(f"Background:  {fitsImage.bkgMedian:.2f} \t  {circleBack:.2f}         {ellipseBack:.2f}      (mJy)")
print(f"    Noise:    {fitsImage.bkgStd:.2f} \t   {circlNoise:.2f}          {ellipNoise:.2f}      (mJy)")<jupyter_output>Mask/Clip 	 Circular    Elliptical 
	     Method  	  Annulus      Annulus
Background:  -0.29 	  -0.03         -0.05      (mJy)
    Noise:    0.52 	   0.76          0.73      (mJy)<jupyter_text>Aperture PhotometryThe astroImage object provides an easy method for performing aperture photometry. Note this procedure is fairly generic and will not do advanced corrections like correcting for extended PSF structure etc... These methods are essentially wrappers to the [photutils](https://photutils.readthedocs.io/en/stable/) aperture photometry functions. There are three types of aperture in-built this is circular, elliptical, and rectangular apertures, which can be run with the *circularAperture*, *elipticalAperture*, and the *rectangularAperture* methods. A key thing to know is based if the units of the image is recognised to be a surface-brightness unit (e.g., MJy/sr, or Jy arcsec-2). This is easily seen from the output astropy table (see below) where the column changes from *aperture_sum* to *aperture_mean*. **Simple Photometry**Let's try a simple case of using a circular aperture on M100, for this we just need to pass it a centre as the first argument, and radius (again an astropy quantity is recommended, but can also be in pixels). We also provide the name of the object via the *names* parameter:<jupyter_code># perform circular photometry
circlePhotometry = fitsImage.circularAperture(centreM100, radius=4.2*u.arcmin, names='M100')

# output astropy table
circlePhotometry<jupyter_output><empty_output><jupyter_text>Now we can do a local background subtraction in the same call by using the *localBackSubtract* parameter giving a dictionary with the *inner* and *outer* radius defined. We can also provide an error map via *error* parameter to calculate the 'aperture error' assuming basic Gaussian statistics.<jupyter_code># perform circular photometry with a local background subtraction
circlePhotometry = fitsImage.circularAperture(centreM100, radius=4.2*u.arcmin, localBackSubtract={'inner':5.0*u.arcmin, 'outer':6.0*u.arcmin}, error=fitsImage.image/20.0, names='M100')
circlePhotometry<jupyter_output><empty_output><jupyter_text>Lets now try measuring M100 using an elliptical aperture, a rectangular aperture, and compare the results. The elliptical aperture can be defined with *major* (semi-major axis), either *minor* or *axisRatio*, and *PA* (if using *localBackSubtract* can specify *outerCircle* if want the outer part of the background subtraction to be circular). For rectangular photometry, need to specify the *length*, *width* or "ratio", and *PA* of the rectangle (note any background annulus uses the same lenght ratios).<jupyter_code># perform elliptical aperture photometry
ellipsePhotometry = fitsImage.ellipticalAperture(centreM100, major=4.2*u.arcmin, axisRatio=0.85, PA=30.0*u.deg, localBackSubtract={'inner':5.0*u.arcmin, 'outer':6.0*u.arcmin, "outerCircle":True})

# perform rectangular photometry
rectanglePhotometry = fitsImage.rectangularAperture(centreM100, length=8.4*u.arcmin, width=8.40*u.arcmin, PA=0.0*u.deg, localBackSubtract={'inner':9.0*u.arcmin, 'outer':10.0*u.arcmin})

circlePhotometry['aperture_sum'][0]

print(f"   Circle Flux: {circlePhotometry['aperture_sum'][0]/1000.0:.1f} Jy  ({circlePhotometry['number_pixels'][0]:.0f} Pixels)")
print(f"  Ellipse Flux: {ellipsePhotometry['aperture_sum'][0]/1000.0:.1f} Jy  ({ellipsePhotometry['number_pixels'][0]:.0f} Pixels)")
print(f"Rectangle Flux: {rectanglePhotometry['aperture_sum'][0]/1000.0:.1f} Jy  ({rectanglePhotometry['number_pixels'][0]:.0f} Pixels)")<jupyter_output>Circle Flux: 63.5 Jy  (5729 Pixels)
  Ellipse Flux: 63.2 Jy  (4870 Pixels)
Rectangle Flux: 62.9 Jy  (7294 Pixels)<jupyter_text>Let's just show a quick example where we do everything in pixel coordinates.<jupyter_code># circular photometry with pixels
pixPhotometry = fitsImage.circularAperture((301.6,302.0), radius=42.0, localBackSubtract={'inner':50.0, 'outer':60.0})
print(f"Pixel Flux: {pixPhotometry['aperture_sum'][0]/1000.0:.1f} Jy  ({pixPhotometry['number_pixels'][0]:.0f} Pixels)")<jupyter_output>Pixel Flux: 63.4 Jy  (5542 Pixels)<jupyter_text>**Multi-Object Photometry**Quite often you will want to perform aperture photometry on more than one object, and there are variety of ways you can do this. Let's start by simultaneously measuring M100 and NGC4312 another extended local galaxy.The first method is instead of providing the centre as the first argument instead a dictionary is provided where each item is an object to perform photometry on, and each item is a dictionary given details of the settings to use. This is easiest to show in an example:<jupyter_code># First let's set the centre of NGC4312 with an astropy SkyCoord
centreNGC4312 = SkyCoord(185.63, 15.538, unit="deg", frame="icrs")

# create a dictionary for all objects, each object is a dictionary of the properties to use the object's aperture
galInfo = {'M100':{'centre':centreM100, 'major':4.2*u.arcmin, 'minor':3.57*u.arcmin, "PA":30.0*u.deg, "localBackSubtract":{'inner':5.0*u.arcmin, 'outer':6.0*u.arcmin, "outerCircle":True}},\
           'NGC4312':{'centre':centreNGC4312, 'major':1.12*u.arcmin, 'axisRatio':0.733, "PA":-8.0*u.deg, "localBackSubtract":{'inner':2.0*u.arcmin, 'outer':3.0*u.arcmin, "outerCircle":True}}}

# perform the elliptical aperture photometry
ellipsePhotometry = fitsImage.ellipticalAperture(galInfo)
ellipsePhotometry<jupyter_output><empty_output><jupyter_text>Alternatively, instead of providing a dictionary of all the objects (e.g., galInfo above), each of the inputs as in the single object photometry examples above can be instead be parsed a list of the parameters to vary.<jupyter_code># perform elliptical aperture photometry with each parameter a list of the values for each object.
ellipsePhotometry = fitsImage.ellipticalAperture([centreM100,centreNGC4312], major=[4.2*u.arcmin,1.12*u.arcmin], minor=[3.57*u.arcmin, 0.82*u.arcmin], PA=[30*u.deg, -8.0*u.deg], localBackSubtract=[{'inner':5.0*u.arcmin, 'outer':6.0*u.arcmin, "outerCircle":True}, {'inner':2.0*u.arcmin, 'outer':3.0*u.arcmin, "outerCircle":True}])
ellipsePhotometry<jupyter_output><empty_output><jupyter_text>In some circumstances you may want the apertures or some properties to all be the same size (like when measuring many background point sources). In this case any of the parameters can be kept fixed by giving a parameter a single value.<jupyter_code>centres = [SkyCoord(185.7962, 15.7438, unit="deg", frame="icrs"),\
           SkyCoord(185.6075, 15.8213, unit="deg", frame="icrs"),\
           SkyCoord(185.5368, 15.8798, unit="deg", frame="icrs")]

circlePhotometry = fitsImage.circularAperture(centres, radius=25.0*u.arcsec, localBackSubtract={'inner':30.0*u.arcsec, 'outer':35.0*u.arcsec}, backMedian=True)
circlePhotometry<jupyter_output><empty_output><jupyter_text>Curve's of Growth and Radial ProfilesInstead of using a single aperture on an object, sometimes you may want to see how the flux of the source changes with the size of your aperture, or see what the radial profile is of your object. The aperture photometry commands in the previous section can be easily adjusted to do this by setting the *multiRadius* parameter to True.*(Note for rectangular case setting multi-radius to True, that the length parameter is now the semi-length)*<jupyter_code># import numpy
import numpy as np

radialProfile = fitsImage.ellipticalAperture(centreM100, multiRadius=True, major=np.arange(0,306.0,6.0)*u.arcsec, axisRatio=0.85, PA=30.0*u.deg, localBackSubtract={'inner':5.0*u.arcmin, 'outer':6.0*u.arcmin, "outerCircle":True}, names='M100')
radialProfile<jupyter_output>here
here<jupyter_text>You may also want to do radial profiles for more than one object. Here is an example of applying this method to two extended objects in the image, each with its own local background subtraction.<jupyter_code>galInfo = {"M100":{"centre":centreM100, "axisRatio":0.85, "PA":30.0*u.deg, "localBackSubtract":{'inner':5.0*u.arcmin, 'outer':6.0*u.arcmin}, "outerCircle":True},\
           "NGC4312":{'centre':centreNGC4312, 'axisRatio':0.733, "PA":-8.0*u.deg, "localBackSubtract":{'inner':2.0*u.arcmin, 'outer':3.0*u.arcmin, "outerCircle":True}}}

radialProfile = fitsImage.ellipticalAperture(galInfo, multiRadius=True, major=np.arange(0,306.0,6.0)*u.arcsec)
radialProfile
radialProfile = fitsImage.ellipticalAperture([centreM100,centreNGC4312], multiRadius=True, major=np.arange(0,306.0,6.0)*u.arcsec, axisRatio=[0.85,0.733], PA=[30.0*u.deg, -8.0*u.deg], localBackSubtract=[{'inner':5.0*u.arcmin, 'outer':6.0*u.arcmin},{'inner':2.0*u.arcmin, 'outer':3.0*u.arcmin, "outerCircle":True}], names=['M100','NGC4312'])
radialProfile<jupyter_output>here
here
here<jupyter_text>Example Matching Resolution and Projection of One Image to AnotherTo finish this tutorial we're going to look at a typical use case where you want to convole and project one image to match another.ConvolutionFirst we need to convolve the image to match the spatial resolution, but we need to define how to go from one to another. The convolve method provides a handy way to perform the convolution creating a new astroImage. This can be done in two ways: - Provide a kernel with a 2D numpy array. This is best to provide an exact match (note the pixel size must match). Eventually this package will access a database of kernels, or generate its own. - Assume everything is Gaussian. By default you can provide the FWHM of the Gaussian (in angular units) to convolve the image (or the standard deviation if set FWHM=False)The default is to preserve flux but by setting the 'peakNorm' to True you can conserve peak flux (note though the code cannot update beam areas/FWHM yet. Finally, the 'boundary' and 'fill_value' parameters can be given to the astropy convolution routine. In our example we'll assume the simple example that the 250&181;m and 500&181;m images have Gaussian PSFs of 18 and 36 arcseconds. Examples of the convolve method shown below:<jupyter_code># import numpy and astropy.units
import numpy as np
import astropy.units as u

# Calculate the FWHM of the Gaussian we want to convolve the image by
convFWHM = np.sqrt((36.0*u.arcsecond)**2.0 - (18.0*u.arcsecond)**2.0)

# convolve the image
smo250 = fitsImage.convolve(convFWHM)

# alteratively if wanted to provide standard deviation
#smo250 = fitsImage.convolve(convSTD, FWHM=False)

# to provide a kernel imaage just load/create the array
#smo250 = fitsImage.convolve(kernelImage)

# to conserve the peak rather than total flux
#smo250 = fitsImage.convolve(convFWHM, peakNorm=True)<jupyter_output><empty_output><jupyter_text>ReprojectThe next stage is to reproject the data so the image projection is the same (i.e., the pixels all line up). The astroImage object has the reproject method which works as a wrapper for the reporject package. The user must supply a header to reproject the image to, but can switch to the interpolation method by serring 'exact=False', or can specify to conserve flux (rather than surface-brightness) by setting 'conserveFlux=True'. Note that if the units of the image are 'Jy/pix' or 'mJy/pix' then the method will automatically conserve flux.<jupyter_code># open the 500um image with astroImage
image500 = astroImage(pj(folder,"NGC4321_SPIRE_500.fits"), dustpediaHeaderCorrect=True)

# reproject the smoothed 250 image
reproSmo250 = smo250.reproject(image500.header)

# example with other keywords added
#reproSmo250 = smo250.reproject(image500.header, exact=False, conserveFlux=True)<jupyter_output><empty_output><jupyter_text>Let's see how our smoothed and reprojected 250&181;m map compares to the 500&181;m map using our quick plot method:<jupyter_code>print("Original 250um Image")
fitsImage.plot(stretch='log', cmap='inferno', hide_colourbar=True, recentre={"RA":185.72875*u.degree, "DEC":15.8225*u.degree, "rad":6.0*u.arcmin})
print("Smooth and Reprojected 250um Image")
reproSmo250.plot(stretch='log', cmap='inferno', hide_colourbar=True, recentre={"RA":185.72875*u.degree, "DEC":15.8225*u.degree, "rad":6.0*u.arcmin})
print("500um Image")
image500.plot(stretch='log', cmap='inferno', hide_colourbar=True, recentre={"RA":185.72875*u.degree, "DEC":15.8225*u.degree, "rad":6.0*u.arcmin})<jupyter_output>500um Image<jupyter_text>CutoutsCutout description here.<jupyter_code># create a cutout on M100
cutout250 = reproSmo250.cutout(SkyCoord(ra=185.72875*u.degree, dec=15.8225*u.degree, frame='icrs'), 12.0*u.arcmin)

# plot the cutout image
reproSmo250.plot(stretch='log', cmap='inferno', hide_colourbar=True)<jupyter_output><empty_output><jupyter_text>Saving the astro imageThe astroImage object can be saved to a fits file, using the saveToFits method. This method takes the path to the location where you want to save the file, and you can allow the method to overwrite an exisiting file (just the same as astropy).<jupyter_code>reproSmo250.saveToFits(pj(folder,"reproSmo250.fits"), overwrite=True)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09007253437263181, 'fraction_numerical': 0.030348958897188843, 'mean_word_length': 4.898680289484887, 'pattern_counts': {'":': 33, '<': 109, '<?xml version=': 0, '>': 122, 'https://': 7, 'lorem ipsum': 0, 'www.': 5, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 19, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '30', 'dir': 'jupyter-structured-clean-dedup', 'id': '404239', 'license': 'MIT', 'n_tokens_mistral': 8884, 'n_tokens_neox': 8091, 'n_words': 3098, 'path': 'Notebooks/AstroImage Tutorial.ipynb', 'repo_name': 'mwls/astroIm', 'size': '27711'}