Skip to content

Commit 8c381d9

Browse files
committed
Better recursive descent based Fibre Apertures. Correctly computes the fractional coverage down to a nominal recusion depth.
1 parent fd869c7 commit 8c381d9

8 files changed

Lines changed: 261 additions & 94 deletions

File tree

‎DESCRIPTION‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
Package: ProFound
22
Type: Package
33
Title: Photometry Tools
4-
Version: 1.26.3
5-
Date: 2025-10-09
4+
Version: 1.27.0
5+
Date: 2025-10-14
66
Author: Aaron Robotham
77
Maintainer: Aaron Robotham <aaron.robotham@uwa.edu.au>
88
Description: Core package containing all the tools for simple and advanced source extraction. This is used to create inputs for 'ProFit', or for source detection, extraction and photometry in its own right.
99
License: LGPL-3
1010
Depends: R (>= 3.1), Rfits (>= 1.8.0), magicaxis (>= 2.0.8), Rcpp (>= 1.0.2)
1111
Imports: data.table, celestial (>= 1.4.1), foreach, matrixStats, doParallel
12-
Suggests: ProFit, knitr, rmarkdown, EBImage, imager, LaplacesDemon, Rfast, Rfast2, fastmatch, snow, doSNOW, bigmemory, mvtnorm, Rwcs, Highlander (>= 0.1.7), ProPane
12+
Suggests: ProFit, knitr, rmarkdown, EBImage, imager, LaplacesDemon, Rfast, Rfast2, fastmatch, snow, doSNOW, bigmemory, mvtnorm, Rwcs, Highlander (>= 0.1.7), ProPane, plotrix
1313
VignetteBuilder: knitr
1414
LinkingTo: Rcpp
1515
NeedsCompilation: yes

‎R/RcppExports.R‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
invisible(.Call(`_ProFound_interpolateLinearGrid`, xseq, yseq, tempmat_sky, output))
1717
}
1818

19+
profoundAperCover <- function(x, y, cx, cy, radius, depth = 4L) {
20+
.Call(`_ProFound_profoundAperCover`, x, y, cx, cy, radius, depth)
21+
}
22+
1923
.dilate_cpp <- function(segim, kern, expand = 0L) {
2024
.Call(`_ProFound_dilate_cpp`, segim, kern, expand)
2125
}

‎R/profoundAperPhot.R‎

Lines changed: 75 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,84 @@
1-
.fluxcalcapp = function(x=NULL, y=NULL, rad2=NULL, flux=NULL, xcen=NA, ycen=NA, rad_app=NULL, centype='mean'){
2-
if(is.null(rad2)){
3-
if(is.na(xcen)){
4-
if(centype == 'wt' | centype == 'mean'){
5-
xcen = .meanwt(x, flux)
6-
}else if(centype == 'max'){
7-
xcen = x[which.max(flux)]
8-
}else{
9-
stop('centype must be max or mean!')
10-
}
11-
}
12-
13-
if(is.na(ycen)){
14-
if(centype == 'wt' | centype == 'mean'){
15-
ycen = .meanwt(y, flux)
16-
}else if(centype == 'max'){
17-
ycen = y[which.max(flux)]
18-
}else{
19-
stop('centype must be max or mean!')
20-
}
21-
}
22-
23-
if(xcen == 0 & ycen == 0){
24-
rad2 = x^2 + y^2
1+
.fluxcalcapp = function(x=NULL, y=NULL, flux=NULL, xcen=NA, ycen=NA, rad_app=NULL, centype='mean', depth=4){
2+
if(is.na(xcen)){
3+
if(centype == 'wt' | centype == 'mean'){
4+
xcen = .meanwt(x, flux)
5+
}else if(centype == 'max'){
6+
xcen = x[which.max(flux)]
257
}else{
26-
rad2 = (x - xcen)^2 + (y - ycen)^2
8+
stop('centype must be max or mean!')
279
}
28-
}else{
29-
xcen = 0
30-
ycen = 0
3110
}
3211

33-
if(is.na(rad_app)){
34-
#left here for a different mode I tested for applying rad2 selection outside of this function.
35-
#that wasn't any faster, so probably remove this if case in the future
36-
Nsel = length(rad2)
37-
rad_out = max(rad2)
38-
sel_out = which(rad2 == rad_out)
39-
40-
flux_app = sum(flux, na.rm=TRUE)
41-
flux_min = mean(flux[sel_out], na.rm=TRUE)
42-
}else{
43-
sel = which(rad2 <= rad_app^2)
44-
Nsel = length(sel)
45-
46-
if(length(Nsel) == 0){
47-
flux_app = NA_real_
48-
flux_min = 0
12+
if(is.na(ycen)){
13+
if(centype == 'wt' | centype == 'mean'){
14+
ycen = .meanwt(y, flux)
15+
}else if(centype == 'max'){
16+
ycen = y[which.max(flux)]
4917
}else{
50-
if(all(is.na(flux[sel]))){
51-
flux_app = NA_real_
52-
flux_min = 0
53-
}else{
54-
rad_out = max(rad2[sel])
55-
sel_out = which(rad2 == rad_out)
56-
57-
flux_app = sum(flux[sel], na.rm=TRUE)
58-
flux_min = mean(flux[sel_out], na.rm=TRUE)
59-
}
18+
stop('centype must be max or mean!')
6019
}
6120
}
6221

22+
# if(xcen == 0 & ycen == 0){
23+
# rad2 = x^2 + y^2
24+
# }else{
25+
# rad2 = (x - xcen)^2 + (y - ycen)^2
26+
# }
27+
28+
# if(is.na(rad_app)){
29+
# #left here for a different mode I tested for applying rad2 selection outside of this function.
30+
# #that wasn't any faster, so probably remove this if case in the future
31+
# Nsel = length(rad2)
32+
# rad_out = max(rad2)
33+
# sel_out = which(rad2 == rad_out)
34+
#
35+
# flux_app = sum(flux, na.rm=TRUE)
36+
# #flux_min = mean(flux[sel_out], na.rm=TRUE)
37+
# }else{
38+
# sel = which(rad2 <= rad_app^2)
39+
# Nsel = length(sel)
40+
#
41+
# if(length(Nsel) == 0){
42+
# flux_app = NA_real_
43+
# #flux_min = 0
44+
# }else{
45+
# if(all(is.na(flux[sel]))){
46+
# flux_app = NA_real_
47+
# #flux_min = 0
48+
# }else{
49+
# rad_out = max(rad2[sel])
50+
# sel_out = which(rad2 == rad_out)
51+
#
52+
# flux_app = sum(flux[sel], na.rm=TRUE)
53+
# #flux_min = mean(flux[sel_out], na.rm=TRUE)
54+
# }
55+
# }
56+
# }
57+
58+
aper_frac = profoundAperCover(x, y, xcen, ycen, rad_app, depth=depth)
59+
flux_app = sum(flux * aper_frac, na.rm=TRUE)
60+
N_app = sum((!is.na(aper_frac))*aper_frac, na.rm=TRUE)
61+
62+
suppressWarnings({
63+
if(depth > 0){
64+
flux_min = min(flux[aper_frac > 0 & aper_frac < 1 & flux > 0], na.rm=TRUE)
65+
}else{
66+
flux_min = min(flux[aper_frac > 0 & aper_frac <= 1 & flux > 0], na.rm=TRUE)
67+
}
68+
})
69+
6370
if(!isTRUE(is.finite(flux_min))){ #this catches NA, NaN, NULL, Inf events
6471
flux_min = 0 #don't want to penalise when masked or other weird events
6572
}else if(flux_min < 0){
6673
flux_min = 0 #don't want to penalise when in the sky noise
6774
}
6875

69-
return(list(flux_app=flux_app, flux_min=flux_min, N=Nsel))
76+
return(list(flux_app=flux_app, N_app=N_app, flux_min=flux_min))
7077
}
7178

7279
profoundAperPhot = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, keyvalues=NULL, tar=NULL,
73-
pixscale=1, magzero=0, correction=TRUE, centype='mean', fluxtype='Raw',
74-
verbose=FALSE){
80+
pixscale=1, magzero=0, correction=TRUE, centype='mean',
81+
fluxtype='Raw', depth=4, verbose=FALSE){
7582
if(!is.null(image)){
7683
if(inherits(image, 'Rfits_image')){
7784
keyvalues = image$keyvalues
@@ -128,7 +135,7 @@ profoundAperPhot = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, keyva
128135
stop('fluxtype must be Jansky / Microjansky / Raw!')
129136
}
130137

131-
segID = x = y = flux = j = rad2 = NULL
138+
segID = x = y = flux = j = NULL
132139

133140
Rapp = (app_diam / 2 / pixscale) #in pixels
134141
Aapp = (pi * Rapp^2) #in pixels
@@ -207,7 +214,7 @@ profoundAperPhot = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, keyva
207214
match_segID = match(tempDT$segID, tar$segID)
208215
tempDT[, x:= x - tar[match_segID, 'xcen']]
209216
tempDT[, y:= y - tar[match_segID, 'ycen']]
210-
tempDT[, rad2:= x^2 + y^2]
217+
#tempDT[, rad2:= x^2 + y^2]
211218

212219
#setkey(tempDT, segID, rad2)
213220

@@ -218,7 +225,7 @@ profoundAperPhot = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, keyva
218225
if(verbose){message(' Aperture: ', app_diam[j], ' [asec]')}
219226
# the top one can completely remove segments in some weird edge cases, and doesn't appear to be faster. Use the second!
220227
#temp_app = tempDT[rad2 <= Rapp[j]^2, .fluxcalcapp(x=x, y=y, rad2=rad2, flux=flux, xcen=0, ycen=0, rad_app=NA), by=segID]
221-
temp_app = tempDT[, .fluxcalcapp(rad2=rad2, flux=flux, xcen=0, ycen=0, rad_app=Rapp[j]), by=segID]
228+
temp_app = tempDT[, .fluxcalcapp(x=x, y=y, flux=flux, xcen=0, ycen=0, rad_app=Rapp[j], depth=depth), by=segID]
222229

223230
if(correction){
224231
temp_app$flux_app = temp_app$flux_app - (temp_app$N - Aapp[j])*temp_app$flux_min
@@ -228,9 +235,10 @@ profoundAperPhot = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, keyva
228235
return(data.frame(flux_app = temp_app$flux_app*fluxscale,
229236
mag_app = mag_app,
230237
SB_app = mag_app + 2.5*log10(Aapp[j]) + 5*log10(pixscale),
231-
N_app = temp_app$N,
232-
frac_app = temp_app$N/Aapp[j],
233-
flux_min = temp_app$flux_min*fluxscale)
238+
N_app = temp_app$N_app,
239+
frac_app = temp_app$N_app/Aapp[j],
240+
flux_min = temp_app$flux_min*fluxscale
241+
)
234242
)
235243
}
236244

@@ -242,7 +250,8 @@ profoundAperPhot = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, keyva
242250
}
243251

244252
profoundAperRan = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, Nran=100, keyvalues=NULL,
245-
pixscale=1, magzero=0, correction=TRUE, fluxtype='Raw', verbose=FALSE){
253+
pixscale=1, magzero=0, correction=TRUE, fluxtype='Raw', depth=4,
254+
verbose=FALSE){
246255
if(!is.null(image)){
247256
if(inherits(image, 'Rfits_image')){
248257
keyvalues = image$keyvalues
@@ -318,8 +327,8 @@ profoundAperRan = function(image=NULL, segim=NULL, app_diam=1, mask=NULL, Nran=1
318327
segim_ran[segim > 0L] = 0L
319328

320329
output = profoundAperPhot(image=image, segim=segim_ran, app_diam=app_diam, keyvalues=keyvalues,
321-
tar=tar, pixscale=pixscale, magzero=magzero, correction=correction,
322-
fluxtype=fluxtype, verbose=verbose)
330+
tar=tar, pixscale=pixscale, magzero=magzero, correction=correction,
331+
fluxtype=fluxtype, depth=depth, verbose=verbose)
323332

324333
i = NULL
325334

‎R/profoundSegim.R‎

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,26 +95,26 @@
9595
N100seg=length(flux)
9696
}
9797

98-
if(!is.na(Napp)){
99-
Nsel = N100seg:(N100seg - Napp + 1)
100-
Nsel = Nsel[Nsel>0]
101-
}else{
102-
Nsel = 0
103-
}
98+
# if(!is.na(Napp)){
99+
# Nsel = N100seg:(N100seg - Napp + 1)
100+
# Nsel = Nsel[Nsel>0]
101+
# }else{
102+
# Nsel = 0
103+
# }
104104

105105
if(N100seg > 0){
106106

107107
sumflux=sum(flux[good])
108108

109-
if(length(Nsel)>0){
110-
if(Nsel[1]>0){
111-
sumflux_app=sum(flux[good][Nsel])
112-
}else{
113-
sumflux_app=0
114-
}
115-
}else{
116-
sumflux_app=0
117-
}
109+
# if(length(Nsel)>0){
110+
# if(Nsel[1]>0){
111+
# sumflux_app=sum(flux[good][Nsel])
112+
# }else{
113+
# sumflux_app=0
114+
# }
115+
# }else{
116+
# sumflux_app=0
117+
# }
118118

119119
temp=cumsum(flux[good])/sumflux
120120

@@ -139,21 +139,21 @@
139139

140140
}else{
141141
sumflux=NA
142-
sumflux_app=NA
142+
#sumflux_app=NA
143143
N100seg=length(flux)
144144
N50seg=N100seg*0.5
145145
N90seg=N100seg*0.9
146146
cenfrac=NA
147147
}
148148

149149
mode(sumflux)='numeric'
150-
mode(sumflux_app)='numeric'
150+
#mode(sumflux_app)='numeric'
151151
mode(N50seg)='numeric'
152152
mode(N90seg)='numeric'
153153
mode(cenfrac)='numeric'
154154
mode(cenfrac)='numeric'
155155

156-
return(list(flux=sumflux, flux_app=sumflux_app, N50seg=N50seg, N90seg=N90seg, N100seg=N100seg, cenfrac=cenfrac))
156+
return(list(flux=sumflux, N50seg=N50seg, N90seg=N90seg, N100seg=N100seg, cenfrac=cenfrac))
157157
}
158158

159159
.fluxcalcmin=function(flux){
@@ -791,15 +791,17 @@ profoundSegimStats=function(image=NULL, segim=NULL, mask=NULL, sky=NULL, skyRMS=
791791

792792
x=NULL; y=NULL; flux=NULL; sky=NULL; skyRMS=NULL
793793

794-
fluxout = tempDT[, .fluxcalc(flux, Napp = NA), by = segID]
794+
fluxout = tempDT[, .fluxcalc(flux), by = segID]
795795
#old very rough fibre mag stuff is being ignored (hence the NA above and commented out line below)
796796
#fluxout$flux_app[which(fluxout$flux_app > fluxout$flux)] = fluxout$flux[which(fluxout$flux_app > fluxout$flux)]
797797

798798
if(!is.na(Rapp)){
799799
#newer more accurate fibre mag calculation
800800
temp_app = tempDT[, .fluxcalcapp(x=x, y=y, flux=flux, rad_app=Rapp), by=segID]
801801
#Here we correct by the lowest value pixel in the outer aperture
802+
#no need now
802803
fluxout$flux_app = temp_app$flux_app - (temp_app$N - Aapp)*temp_app$flux_min
804+
fluxout$flux_app = temp_app$flux_app
803805
}
804806

805807
mag = profoundFlux2Mag(flux = fluxout$flux, magzero = magzero)

‎man/profoundAperCover.Rd‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
\name{profoundAperCover}
2+
\alias{profoundAperCover}
3+
%- Also NEED an '\alias' for EACH other topic documented here.
4+
\title{
5+
Fraction of Pixel in Aperture
6+
}
7+
\description{
8+
Calculates the fraction of pixels inside an aperture using recursive descent.
9+
}
10+
\usage{
11+
profoundAperCover(x, y, cx, cy, radius, depth = 4L)
12+
}
13+
%- maybe also 'usage' for other objects documented here.
14+
\arguments{
15+
\item{x}{
16+
Numeric vector; the pixel x centre positions (where pixel centres are half integer in \code{ProFound}).
17+
}
18+
\item{y}{
19+
Numeric vector; the pixel y centre positions (where pixel centres are half integer in \code{ProFound}).
20+
}
21+
\item{cx}{
22+
Numeric scalar; the x centre of the aperture (in pixels).
23+
}
24+
\item{cy}{
25+
Numeric scalar; the y centre of the aperture (in pixels).
26+
}
27+
\item{radius}{
28+
Numeric scalar; the radius of the aperture (in pixels).
29+
}
30+
\item{depth}{
31+
Integer scalar; the recursion depth to use.
32+
}
33+
}
34+
\details{
35+
Re the recursion \option{depth}, higher numbers are more accurate, where you probably do not want to set it below 3. Above 5 will suffer notable slow-down. 4 is usually about the performance versus accuracy sweet spot.
36+
}
37+
\value{
38+
Numeric vector the same length as \option{x} and \option{y}, which is what fraction of the pixel lies under the desired aperture.
39+
}
40+
\author{
41+
Aaron Robotham
42+
}
43+
44+
\seealso{
45+
\code{\link{profoundAperPhot}}
46+
}
47+
\examples{
48+
library(magicaxis)
49+
library(plotrix)
50+
51+
temp = expand.grid(1:10 - 0.5, 1:10 - 0.5)
52+
53+
output = matrix(profoundAperCover(temp[,1], temp[,2], 5, 5, 3, depth=4), 10, 10)
54+
magimage(output, magmap=FALSE)
55+
draw.circle(5, 5, radius=3.2, border='red')
56+
57+
output = matrix(profoundAperCover(temp[,1], temp[,2], 3.2, 3.2, 1.8, depth=4), 10, 10)
58+
magimage(output, magmap=FALSE)
59+
draw.circle(3.2, 3.2, radius=1.8, border='red')
60+
61+
output = matrix(profoundAperCover(temp[,1], temp[,2], 7.5, 7.5, 5.8, depth=4), 10, 10)
62+
magimage(output, magmap=FALSE)
63+
draw.circle(7.5, 7.5, radius=5.8, border='red')
64+
}
65+
% Add one or more standard keywords, see file 'KEYWORDS' in the
66+
% R documentation directory (show via RShowDoc("KEYWORDS")):
67+
% \keyword{ ~kwd1 }
68+
% \keyword{ ~kwd2 }
69+
% Use only one keyword per line.
70+
% For non-standard keywords, use \concept instead of \keyword:
71+
% \concept{ ~cpt1 }
72+
% \concept{ ~cpt2 }
73+
% Use only one concept per line.

0 commit comments

Comments
 (0)