aboutsummaryrefslogtreecommitdiff
path: root/noao/twodspec/apextract/peaks.x
blob: fe525f88363b4d9b51b5bf3ac0023b5227b2505f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
# PEAKS -- The following procedures are general numerical functions
# dealing with finding peaks in a data array.
#
# FIND_PEAKS		Find additional peaks in the data array.
# FIND_LOCAL_MAXIMA	Find the local maxima in the data array.
# IS_LOCAL_MAX		Test a point to determine if it is a local maximum.
# FIND_CONTRAST		Find the peaks satisfying the contrast constraint.
# FIND_ISOLATED		Flag peaks which are within separation of a peak
#			with a higher peak value.
# FIND_NMAX		Select up to the nmax highest ranked peaks.
# COMPARE		Compare procedure for sort used in FIND_PEAKS.

# FIND_PEAKS -- Find the peaks in the data array.
#
# The peaks are found using the following algorithm:
#
# 1.  Find the local maxima which are not near the edge of existing peaks.
# 2.  Reject those below the absolute threshold.
# 3.  Reject those below the contrast threshold.
# 4.  Determine the ranks of the remaining peaks.
# 5.  Flag weaker peaks within separation of a stronger peak.
# 6.  Add strongest peaks to the peaks array.
#
# Indefinite data points are ignored.

procedure find_peaks (data, npts, contrast, edge, nmax, separation, threshold,
    peaks, npeaks)

real	data[npts]		# Input data array
int	npts			# Number of data points

real	contrast		# Maximum contrast between strongest and weakest
int	edge			# Minimum distance from the edge
int	nmax			# Maximum number of peaks to be returned
real	separation		# Minimum separation between peaks
real	threshold		# Minimum threshold level for peaks

real	peaks[nmax]		# Positons of input peaks / output peaks
int	npeaks			# Number of input peaks / number of output peaks

int	i, nx
pointer	sp, x, y, rank

int	compare()
extern	compare()

common	/sort/ y

begin
	if (npeaks >= nmax)
	    return

	call smark (sp)
	call salloc (x, npts, TY_INT)
	call salloc (y, npts, TY_REAL)

	# Find the positions of the local maxima.
	call find_local_maxima (data, npts, peaks, npeaks, edge, separation,
	    threshold, Memi[x], Memr[y], nx)

	# Eliminate points not satisfying the contrast constraint.
	call find_contrast (data, Memi[x], Memr[y], nx, contrast)

	# Rank the peaks by peak value.
	call salloc (rank, nx, TY_INT)
	for (i = 1; i <= nx; i = i + 1)
	    Memi[rank + i - 1] = i
	call qsort (Memi[rank], nx, compare)

	# Reject weaker peaks within a specified separation of a stronger peak.
	call find_isolated (Memi[x], Memi[rank], nx, separation)

	# Add the strongest peaks.
	call find_nmax (Memi[x], Memi[rank], nx, nmax, peaks, npeaks)

	call sfree (sp)
end


# FIND_LOCAL_MAXIMA -- Find the local maxima in the data array.

procedure find_local_maxima (data, npts, peaks, npeaks, edge, separation,
    threshold, x, y, nx)

real	data[npts]		# Input data array
int	npts			# Number of input points
real	peaks[ARB]		# Positions of peaks
int	npeaks			# Number of peaks
int	edge			# Edge buffer distance
real	separation		# Minimum separation from peaks
real	threshold		# Data threshold
int	x[npts]			# Output positions
real	y[npts]			# Output data values
int	nx			# Number of maxima

int	i, j

bool	is_local_max()

begin
	# Find the local maxima above the threshold and not near the edge.
	nx = 0
	for (i = edge + 1; i <= npts - edge; i = i + 1) {
	    if ((data[i] >= threshold) && (is_local_max (i, data, npts))) {
	        nx = nx + 1
	        x[nx] = i
	    }
	}

	# Flag maxima within separation of previous peaks.
	for (j = 1; j <= npeaks; j = j + 1) {
	    for (i = 1; i <= nx; i = i + 1) {
		if (IS_INDEFI (x[i]))
		    next
		if (x[i] < peaks[j] - separation)
		    next
		if (x[i] > peaks[j] + separation)
		    break
		x[i] = INDEFI
	    }
	}

	# Eliminate flagged maxima and set y values.
	j = 0
	for (i = 1; i <= nx; i = i + 1) {
	    if (!IS_INDEFI (x[i])) {
		j = j + 1
		x[j] = x[i]
		y[j] = data[x[j]]
	    }
	}

	nx = j
end


# IS_LOCAL_MAX -- Test a point to determine if it is a local maximum.
#
# Indefinite points are ignored.

bool procedure is_local_max (index, data, npts)

# Procedure parameters:
int	index			# Index to test for local maximum
real	data[npts]		# Data values
int	npts			# Number of points in the data vector

int	i, j, nright, nleft

begin
	# INDEFR points cannot be local maxima.
	if (IS_INDEFR (data[index]))
	    return (false)

	# Find the left and right indices where data values change and the
	# number of points with the same value.  Ignore INDEFR points.
	nleft = 0
	for (i = index - 1; i >= 1; i = i - 1) {
	    if (!IS_INDEFR (data[i])) {
		if (data[i] != data[index])
		    break
		nleft = nleft + 1
	    }
	}
	nright = 0
	for (j = index + 1; i <= npts; j = j + 1) {
	    if (!IS_INDEFR (data[j])) {
		if (data[j] != data[index])
		    break
		nright = nright + 1
	    }
	}

	# Test for failure to be a local maxima
	if ((i == 0) && (j == npts)) {
	    return (FALSE)		# Data is constant
	} else if (i == 0) {
	    if (data[j] > data[index])
	        return (FALSE)		# Data increases to right
	} else if (j == npts) {
	    if (data[i] > data[index])	# Data increase to left
		return (FALSE)
	} else if ((data[i] > data[index]) || (data[j] > data[index])) {
	    return (FALSE)		# Not a local maximum
	} else if (!((nleft - nright == 0) || (nleft - nright == 1))) {
	    return (FALSE)		# Not center of plateau
	}

	# Point is a local maxima
	return (TRUE)
end



# FIND_CONTRAST -- Find the peaks with positions satisfying contrast constraint.

procedure find_contrast (data, x, y, nx, contrast)

real	data[ARB]		# Input data values
int	x[ARB]			# Input/Output peak positions
real	y[ARB]			# Output peak data values
int	nx			# Number of peaks input
real	contrast		# Contrast constraint

int	i, j
real	minval, maxval, threshold

begin
	if ((nx == 0.) || (contrast <= 0.))
	    return

	call alimr (y, nx, minval, maxval)
	threshold = contrast * maxval

	j = 0
	do i = 1, nx {
	    if (y[i] < threshold) {
		j = j + 1
		x[j] = x[i]
		y[j] = y[i]
	    }
	}

	nx = j
end

# FIND_ISOLATED -- Flag peaks which are within separation of a peak
# with a higher peak value.
#
# The peak positions, x, and their ranks, rank, are input.
# The rank array contains the indices of the peak positions in order from
# the highest peak value to the lowest peak value.  Starting with
# highest rank (rank[1]) all peaks of lower rank within separation
# are marked by setting their positions to INDEFI.

procedure find_isolated (x, rank, nx, separation)

int	x[ARB]			# Positions of points
int	rank[ARB]		# Rank of peaks
int	nx			# Number of peaks
real	separation		# Minimum allowed separation

int	i, j

begin
	if ((nx == 0) || (separation <= 0.))
	    return

	# Eliminate close neighbors.  The eliminated
	# peaks are marked by setting their positions to INDEFI.

	for (i = 1; i < nx; i = i + 1) {
	    if (IS_INDEFI (x[rank[i]]))
		next
	    for (j = i + 1; j <= nx; j = j + 1) {
		if (IS_INDEFI (x[rank[j]]))
		    next
		if (abs (x[rank[i]] - x[rank[j]]) < separation)
		    x[rank[j]] = INDEFI
	    }
	}
end


# FIND_NMAX -- Select up to the nmax highest ranked peaks.

procedure find_nmax (x, rank, nx, nmax, peaks, npeaks)

int	x[ARB]			# Peak positions
int	rank[ARB]		# Ranks of peaks
int	nx			# Number of input / output peaks
int	nmax			# Max number of peaks to be selected
real	peaks[nmax]		# Output peak position array
int	npeaks			# Output number of peaks

int	i

begin
	for (i = 1; (i <= nx) && (npeaks < nmax); i = i + 1) {
	    if (IS_INDEFI (x[rank[i]]))
		next
	    npeaks = npeaks + 1
	    peaks[npeaks] = x[rank[i]]
	}
end


# COMPARE -- Compare procedure for sort used in FIND_PEAKS.
# Larger values are indexed first.  INDEFR values are indexed last.

int procedure compare (index1, index2)

# Procedure parameters:
int	index1				# Comparison index
int	index2				# Comparison index

pointer	y

common	/sort/ y

begin
	# INDEFR points are considered to be smallest possible values.
	if (IS_INDEFR (Memr[y - 1 + index1]))
	    return (1)
	else if (IS_INDEFR (Memr[y - 1 + index2]))
	    return (-1)
	else if (Memr[y - 1 + index1] < Memr[y - 1 + index2])
	    return (1)
	else if (Memr[y - 1 + index1] > Memr[y - 1 + index2])
	    return (-1)
	else
	    return (0)
end