; ALIGN_CUBE at end
; PRO ALIGN_CUBE, IN_CUBE, OUT_CUBE, DMAX=DMAX, SHIFTS=SHIFTS, $
;                 INSHIFTS=INSHIFTS

FUNCTION MAXLOC,ARRAY,MAX_ARRAY
;+
; NAME:
;	MAXLOC
;
; PURPOSE:
;	Find the position of maximum in a two dimensional array.
;
; CALLING SEQUENCE:
;	Result = MAXLOC(ARRAY,MAX_ARRAY)
;
; INPUTS:
;	ARRAY = a two dimensional array.
;
; OUTPUTS:
;	Result = a vector containing the X,Y coordinates of maximum.
;
; OPTIONAL OUTPUT:
;	MAX_ARRAY = value of the array at X,Y.
;
; SIDE EFFECTS:
;	None.
;
; COMMON BLOCKS:
;	None.
;
; RESTRICTIONS:
;	None.
;
; PROCEDURE:
;	Straightforward.
;
; MODIFICATION HISTORY:
;	Written by Roberto Molowny-Horas, 1991.
;	MAX_ARRAY added in March 1994, RMH
;
;-
ON_ERROR,2

	s = SIZE(array)			;Size of input array.
	IF s(0) NE 2 THEN MESSAGE,'Input array must be two dimensional'

	max_array = MAX(array,n)	;Finds maximum.
	RETURN,[n MOD s(1),n/s(1)]	;Output as a vector.
	END

;------------------------------------------------------------------------

PRO FIVEPOINT,CC,X,Y
;+
; NAME:
;	FIVEPOINT
;
; PURPOSE:
;	Measure the position of minimum or maximum in a 3x3 matrix.
;
; CALLING SEQUENCE:
;	FIVEPOINT,CC,X,Y
;
; INPUTS:
;	CC = Cross correlation function. It must have dimensions like
;	CC(3,3), CC(*,3,3) or CC(*,*,3,3)
;
; OUTPUTS:
;	X & Y = Position of the minimum, taking cc(*,*,1,1) as centre.
;
; SIDE EFFECTS:
;	None.
;
; COMMON BLOCKS:
;	None.
;
; RESTRICTIONS:
;	None.
;
; PROCEDURE:
;	Simple interpolation with a 2-rd polynomial in X and Y.
;
; MODIFICATION HISTORY:
;	Written by Roberto Luis Molowny Horas, Institute of Theoretical
;	Astrophysics, University of Oslo. August 1991.
;-
;
ON_ERROR,2

	IF N_PARAMS(0) LT 3 THEN MESSAGE,'Wrong number of parameters.'
	n = SIZE(cc)
	IF n(0) LT 2 OR n(0) GT 4 THEN MESSAGE,'Wrong input array'
	IF n(n(0)-1) NE 3 OR n(n(0)) NE 3 THEN MESSAGE,$
		'Array must be CC(*,*,3,3)'

	CASE 1 OF
		n(0) EQ 4: BEGIN
			y =  2.*cc(*,*,1,1)
			x = (cc(*,*,0,1)-cc(*,*,2,1))/(cc(*,*,2,1)+ $
						cc(*,*,0,1)-y)*.5
			y = (cc(*,*,1,0)-cc(*,*,1,2))/(cc(*,*,1,2)+ $
						cc(*,*,1,0)-y)*.5
		END
		n(0) EQ 3: BEGIN
			y = 2.*cc(*,1,1)
			x = (cc(*,0,1)-cc(*,2,1))/(cc(*,2,1)+cc(*,0,1)-y)*.5
			y = (cc(*,1,0)-cc(*,1,2))/(cc(*,1,2)+cc(*,1,0)-y)*.5
		END
		n(0) EQ 2: BEGIN
			y = 2.*cc(1,1)
			x = (cc(0,1)-cc(2,1))/(cc(2,1)+cc(0,1)-y)*.5
			y = (cc(1,0)-cc(1,2))/(cc(1,2)+cc(1,0)-y)*.5
		END
	ENDCASE

	END

;------------------------------------------------------------------------

FUNCTION COALIGN,A,B
;+
; NAME:
;	ALIGN
;
; PURPOSE:
;	Compute the shift image B has to be given to match image A.
;
; CALLING SEQUENCE:
;	Result = ALIGN(A,B)
;
; INPUTS:
;	A = reference image.
;
;	B = image to be aligned.
;
; OUTPUTS:
;	Result = Shift in X,Y to give image B to match A.
;
; SIDE EFFECTS:
;	None.
;
; COMMON BLOCKS:
;	None.
;
; RESTRICTIONS:
;	IF dimensions of images are not a power of 2, algorithm can be
;	slow.
;
; PROCEDURE:
;	It uses the properties of the Fourier transform to compute the
;	cross correlation between the two images.
;
; MODIFICATION HISTORY:
;	Written by Roberto Luis Molowny Horas, July 1992.
;
;-
;
ON_ERROR,2

	sa = SIZE(a)
	sb = SIZE(b)
	IF sa(0) NE 2 THEN MESSAGE,'Image must be 2-D'
	IF sa(1) NE sb(1) OR sa(2) NE sb(2) THEN $
		MESSAGE,'Images must have same dimensions'

	cc = SHIFT(FLOAT(FFT(FFT(a,-1)*$		;Cross correlation.
		CONJ(FFT(b,-1)),1)),sa(1)/2,sa(2)/2)

	xy = MAXLOC(cc)					;Finding the maximum.

	IF xy(0) EQ 0 OR xy(0) EQ sa(1)-1 OR xy(1) EQ 0 OR xy(1) EQ sa(2)-1 $
		THEN BEGIN
			PRINT,' >>>> Shift too large! '
			x = 0 & y = 0			;Outside image.
	ENDIF ELSE BEGIN
		cc = cc(xy(0)-1:xy(0)+1,xy(1)-1:xy(1)+1);Maximum in centre.
		FIVEPOINT,cc,x,y
		x = xy(0) - sa(1)/2 + x			;Centering.
		y = xy(1) - sa(2)/2 + y
	ENDELSE

	RETURN,[x,y]
	END

;---------------------------------------------------------------------

 function shift_align,a,b,inshift=inshift,outshift=outshift

; shifts b to match a: uses coalign to get shift, 
; shift_image to shift
; shift is alternative shift supplied

 asz=size(a)
 bsz=size(b)
 if ((asz[0] ne bsz[0]) or (asz[1] ne bsz[1]) or (asz[2] ne bsz[2])) $
                then begin
    print,'Dimensions must be the same.'
    return,-1
 endif

; inshift is the shift to be given to b to match a
 if not keyword_set(inshift) then sh=coalign(a,b) else sh=inshift
 print,'Shift in pixels is ',sh
 if keyword_set(outshift) then outshift=sh

 shift_image,b,shift_im,sh
 
 return,shift_im
 end

; -------------------------------------------------------------------------

; increase nex in shift_align for large shifts

PRO ALIGN_CUBE, IN_CUBE, OUT_CUBE, DMAX=DMAX, SHIFTS=SHIFTS, $
                INSHIFTS=INSHIFTS, FIRST=FIRST, REVERSE=REVERSE

 IF (n_params(0) LT 1) THEN BEGIN
    print,'Usage: ALIGN_CUBE, IN_CUBE, OUT_CUBE, [DMAX=DMAX, /FIRST],'
    print,'                   [SHIFTS=SHIFTS, INSHIFTS=INSHIFTS]'
    print,''
    print,'Aligns sequence of images in IN_CUBE and creates aligned array'
    print,'   in OUT_CUBE. DMAX is optinal maximum for correlation range.'
    print,'   The shift is cumulative so alignment is to first image.'
    print,'SHIFTS is optional array to return calculated shifts.'
    print,'INSHIFTS is optional array to supply shifts to be applied: if supplied,'
    print,'   new shifts are not calculated.'
    print,'If /FIRST, all images are co-aligned with the first image in cube'
    print,'If FIRST=IMAGE, all images are co-aligned with IMAGE'
    print,'If /REVERSE, starts with last image and works backwards.'
 RETURN
 END

 ; align using data range in first image

 IF not keyword_set(dmax) then dmax=0.9*max(IN_CUBE)

 IF KEYWORD_SET(FIRST) THEN $
    if (n_elements(first) eq 1) then test=IN_CUBE[*,*,0] $
    else test=first

 OUT_CUBE = IN_CUBE
 sz=size(in_cube)
 nim=sz[3]
 shifts=0.0*fltarr(2,nim)
 sh=fltarr(2)
 
 ; if applying supplied shifts, must do every image
 if keyword_set(inshifts) then $
    for i=0,nim-1 do $
       OUT_CUBE[*,*,i]=shift_align(OUT_CUBE[*,*,i],IN_CUBE[*,*,i], $
                                   inshift=inshifts[*,i]) $
 else begin
    for i=1,nim-1 do begin
     IF NOT KEYWORD_SET(REVERSE) THEN BEGIN  
       ; first derive shifts using clipped data 
       ; shift relative to first image made cumulative by using shifted image
       if not keyword_set(first) then test=OUT_CUBE[*,*,i-1]
       OUT_CUBE[*,*,i]=shift_align(TEST<DMAX,IN_CUBE[*,*,i]<DMAX, $
                                   outshift=sh)
       shifts[*,i] = sh 
       ; then apply shifts to unclipped data
       OUT_CUBE[*,*,i]=shift_align(OUT_CUBE[*,*,i-1],IN_CUBE[*,*,i], $
                                   inshift=sh)
     ENDIF ELSE BEGIN
       if not keyword_set(first) then test=OUT_CUBE[*,*,nim-i]
       OUT_CUBE[*,*,nim-1-i]=shift_align(TEST<DMAX, $
                             IN_CUBE[*,*,nim-i-1]<DMAX, outshift=sh)
       shifts[*,nim-i-1] = sh
       ; then apply shifts to unclipped data
       OUT_CUBE[*,*,nim-i-1]=shift_align(OUT_CUBE[*,*,nim-i], $
                             IN_CUBE[*,*,nim-i-1], inshift=sh)
     ENDELSE
    end
 endelse

 return
 end
