PRO saturation_fudge, image_in, sat, image_out, i_bleed, $
                      nsigm=nsigm

; =========================================================================
;+
; PROJECT:
;       Solar-B / XRT
;
; NAME:
;       SATURATION_FUDGE
;
; CATEGORY:
;       Data calibration
;
; PURPOSE:
;       Smooths over saturated areas by linearly connecting neighboring
;       unsaturated pixels and smoothing the result. This allows better 
;       removal of Fourier signals introduced into the data by the
;       read-out electronics (it reduces sinc ringing at sharp saturation 
;       edges).  Can optionally give locations of corrected saturation 
;       "bloom/bleed" pixels - pixels at the saturated region boundary 
;       which are affected by saturation but not saturated themselves.
;
; CALLING SEQUENCE:
;       SATURATION_FUDGE, image_in, sat, image_out, [i_bleed,]
;                     [nsigm=nsigm]
;
; INPUTS:
;       IMAGE_IN  - [Mandatory] (2-dim float array, [Nx,Ny])
;                   The image containing read-out signals.
;            SAT  - [Mandatory] can be either 
;                   a 2-dim byte array, [Nx,Ny] containing 1 if pixel 
;                   the pixel is saturated, or 0 otherwise 
;                   or
;                   scalar value of saturation level 
;
; KEYWORDS:
;         NSIGM   - [Optional] Number of standard deviations
;                   beyond which a pixel at the edge of the 
;                   saturated region must be above the local median  
;                   to considered bad  (affected by saturation 
;                   "bloom/bleed").
;                   Default is 0.2
;                   (Note: <=0 or much >1 not recommended)
;
; OUTPUTS:
;       IMAGE_OUT - [Mandatory] (2-dim float array, [Nx,Ny])
;                   The image with saturated pixels smoothed over.
;                   Optionally, if /stop_bleed is set, saturation
;                   "bloom/bleed" pixels are corrected.
;       I_BLEED   - [Optional] (1-dim long array, [Nbleed])
;                   Index of pixels where saturation "bloom/bleed"
;                   has been corrected.
;
; EXAMPLES:
;
;       Basic usage #1 (with input map of saturated pixel locations):
;       IDL> saturation_fudge, image_in, sat_map, image_out 
;
;       Basic usage #2 (with input saturation value (=2500)):
;       IDL> saturation_fudge, image_in, 2500, image_out 
;
;       You want the locations of corrected saturation "bloom/bleed" pixels:
;     IDL> saturation_fudge, image_in, sat_map, image_out, i_bleed 
;
; COMMON BLOCKS:
;       None.
;
; NOTES:
;       1) CCD saturation often causes a bleed of excess charge in
;       the vertical (y) direction on the chip.  If the bleed is
;       over areas with low DN, gradients can be large, causing
;       problems for the Fourier space removal of read-out patterns 
;       (sinc ringing in the output).  This procedure linearly connects
;       (in x) unsaturated pixels just outside the saturation border,
;       and smooths the resulting "patch" in x and y.
;
;       2) Since border pixels are often affected by charge bleed as well
;       (though not saturated themselves), these "bloom/bleed" pixels 
;       are located and replaced with a local median value. Optionally,
;       the locations of these corrected pixels (i_bleed) can be kept 
;       (e.g., for recording purposes, or to change them back to their
;       original values later).
;       
; MODIFICATION HISTORY:
;
progver = 'v2007.May.18' ; --- (SS, MW) Written by SS. Cleaned up by MW.
progver = 'v2013.Feb.01' ; --- (SS) Fixed overcorrection for cases with 
;                        ;       multiple boundaries in x.  
progver = 'v2016.Nov.22' ; --- (JS) Fixed bloom/bleed calculation to prevent 
;                                wraparound and ensure no double counts
progver = 'v2017.May.30' ; --- (JS) Fixed problem for cases where only one
                         ;     bloom/bleed pixel is found, which caused error
                         ;     in call to STDEV
;
;-
; =========================================================================


;===== define "bloom/bleed" level as >nsigm*sigma above local median 
if not keyword_set(nsigm) then nsigm=0.2  

siz=size(image_in)
nx=siz(1)
ny=siz(2)

;===== ID and number distinct regions with DN>sat_level (isat)
im2=image_in
nsat=n_elements(sat)
if nsat eq 1 then begin    ;=====  saturation level entered 
   satu= im2 ge sat
   isat=label_region(satu)
   sat_level=sat
endif else begin          ;=====  saturation array entered
   isat=label_region(sat)
   sat_level=max(sat)
   satu=sat
end

if total(satu) eq 0 then begin ;=====  return if no saturation present
  image_out = image_in
  i_bleed = -1L
  return
endif

;===== find possible saturation bleed/bloom (in y) pixels at boundaries
;=====   and replace with local median (with saturated pixels suppressed)
unsat = satu eq 0
mask_edge = satu*0 + 1
mask_edge[*, [0,ny-1]] = 0
bsat = mask_edge*(shift(satu,0,1)+shift(satu,0,-1))
bbnd = (bsat*unsat < 1)
ibbnd = where(bbnd eq 1,nbbnd)
im2m = median(im2*unsat,3)
if nbbnd gt 1 then begin
    ; if only 1 bb pixel then can't get a stdev
    ib = where(im2[ibbnd] ge (im2m[ibbnd] + nsigm*stdev(im2m[ibbnd])),nib)
    if nib gt 0 then begin
       ib = ibbnd[ib]
       im2[ib] = im2m[ib] + (im2m[ib] eq 0)*im2[ib]
    endif
endif else if nbbnd eq 1 then begin
    imm = im2m[ibbnd[0]]
    if im2[ibbnd[0]] > imm then im2[ibbnd[0]] = im2m[ibbnd[0]]
endif

;=====  get indicies in the various regions (revi)
nr=max(isat)
hr=histogram(isat,rev=revi)

;===== loop over regions, determine x,y indicies for points in each
for j=1,nr do begin
   irj=revi(revi(j):revi(j+1)-1)
   irxj= irj mod nx
   iryj= (irj - irxj)/nx 

;=====  step in y, connect unsaturated edges of saturated regions with lines 
   for k=min(iryj),max(iryj) do begin
      iryk=where(iryj eq k)
      xkmin=min(irxj(iryk)) 
      xkmax=max(irxj(iryk)) 
      xkmax0=xkmax
      xkbit=findgen(xkmax-xkmin+1)+xkmin
;===== look for end of next saturated section of region at row y=k 
;=====    set section endpoint, or if none, set at end  
      repeat begin
         iend=min(where(im2(xkmin:xkmax0,k) lt sat_level))
         iend=iend(0)
         if iend eq -1 then xkmax=xkmax0 else xkmax=xkbit(iend)-1

;===== if section hasnt yet been done, do the linear fits connecting 
;=====   saturation-unaffected endpoints
         if iend ne 0 then begin
            slope=(im2(xkmax+1,k)-im2(xkmin-1,k))/(xkmax+1-(xkmin-1.))
            icept=im2(xkmin-1,k)-slope*(xkmin-1)
            im2(xkmin:xkmax,k)=slope*xkbit(0:xkmax-xkmin) + icept
         endif 

;===== look for start of next saturated section of region at row y=k 
         if iend ne -1 then begin
            ibeg=min(where(im2(xkmax+1:xkmax0,k) ge sat_level))
            ibeg=ibeg(0) 
            if ibeg ne -1 then begin
               xkmin=xkmax+1+ibeg(0)
               xkbit=findgen(xkmax0-xkmin+1)+xkmin
            endif else xkmin=xkmax0+1
         endif else xkmin=xkmax0+1
      end until iend eq -1 or xkmin gt xkmax0
   endfor
endfor

;===== find where changes have been made (idiff), and boundary
;=====    "bloom/bleed" pixels (i_bleed)
;idiff=where(im2 ne image_in, count_idiff)
;i_bleed=where(im2*unsat ne image_in*unsat, count_i_bleed)
idiff=where(bsat eq 1, count_idiff)
i_bleed = ibbnd 
count_i_bleed=n_elements(i_bleed)

;===== smooth the linear saturation replacement "cap" and make output
im2s=smooth(median(im2,3),[1,5])
image_out= image_in
if (count_idiff ge 1) then image_out(idiff) = im2s(idiff)
if (count_i_bleed ge 1) then image_out(i_bleed) = im2(i_bleed)

return


END ;======================================================================
