; docformat = 'rst'
;
; NAME:
;   cgClipScl
;
; PURPOSE:
;   This is a utility routine to perform linear scaling (similar to BYTSCL)
;   on image arrays. If differs from BYTSCL only in that a user-specified
;   percentage of pixels can be clipped from the image histogram, prior to
;   scaling. By default, two percent of the pixels are clipped. Clipping
;   occurs at both ends of the image histogram.
;
;******************************************************************************************;
;                                                                                          ;
;  Copyright (c) 2015, by Fanning Software Consulting, Inc. All rights reserved.           ;
;                                                                                          ;
;  Redistribution and use in source and binary forms, with or without                      ;
;  modification, are permitted provided that the following conditions are met:             ;
;                                                                                          ;
;      * Redistributions of source code must retain the above copyright                    ;
;        notice, this list of conditions and the following disclaimer.                     ;
;      * Redistributions in binary form must reproduce the above copyright                 ;
;        notice, this list of conditions and the following disclaimer in the               ;
;        documentation and/or other materials provided with the distribution.              ;
;      * Neither the name of Fanning Software Consulting, Inc. nor the names of its        ;
;        contributors may be used to endorse or promote products derived from this         ;
;        software without specific prior written permission.                               ;
;                                                                                          ;
;  THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY        ;
;  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES    ;
;  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT     ;
;  SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,             ;
;  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED    ;
;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         ;
;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND             ;
;  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT              ;
;  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           ;
;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                            ;
;******************************************************************************************;
;
;+
; This is a utility routine to perform linear scaling (similar to BYTSCL)
; on image arrays. If differs from BYTSCL only in that a user-specified
; percentage of pixels can be clipped from the image histogram, prior to
; scaling. By default, two percent of the pixels are clipped. Clipping
; occurs at both ends of the image histogram.
;
; :Categories:
;    Image Processing
;
; :Returns:
;     A byte scaled image is returned.
;
; :Params:
;    image: in, required
;       The image to be scaled. Written for 2D images, but arrays of any size are treated alike.
;       
;    clip: in, optional, type=float, default=2.0
;        The percent of image clipping. Must be value between 0 and 49. Clipping
;        occurs from both ends of image histogram, so a clip of 2.0 linearly scales 
;        approximately 96% of the image histogram. Clipping percents are approximations 
;        only, and depend entirely on the distribution of pixels in the image. For
;        interactive scaling, see cgStretch.

;
; :Keywords:
;     negative, in, optional, type=boolean, default=0
;          If set, the "negative" of the result is returned.
;
;     omax: in, optional, type=byte, default=255
;          The output image is scaled between OMIN and OMAX.
;
;     omin: in, optional, type=byte, default=0
;          The output image is scaled between OMIN and OMAX.
;          
;     threshold: out, optional
;         A two-element array containing the image thresholds that were used for the clipping.
;
; :Examples:
;    To display a clipped image::
;       cgLoadCT, 0                                            ; Gray-scale colors.
;       image = cgDemoData(22)                                 ; Load image.
;       cgImage, cgClipScl(image, 4)
;
; :Author:
;       FANNING SOFTWARE CONSULTING::
;           David W. Fanning
;           1645 Sheely Drive
;           Fort Collins, CO 80526 USA
;           Phone: 970-221-0438
;           E-mail: david@idlcoyote.com
;           Coyote's Guide to IDL Programming: http://www.idlcoyote.com
;
; :History:
;     Change History::
;       Written by:  David W. Fanning, 6 September 2007.
;       Not sure what this program was doing, but not what I thought. I've reworked
;          the algorithm to scale the data appropriately. 25 Oct 2011. DWF.
;       Renamed cgClipScl from ClipScl. 27 March 2015. DWF.
;
; :Copyright:
;     Copyright (c) 2007-2015, Fanning Software Consulting, Inc.
;-
FUNCTION cgClipScl, image, clip, $
   NEGATIVE=negative, $
   OMAX=maxOut, $
   OMIN=minOut, $
   THRESHOLD=threshold

   ; Return to caller on error.
   ;On_Error, 2
   Catch, theError
   IF theError NE 0 THEN BEGIN
      Catch, /Cancel
      void = cgErrorMsg()
      RETURN, vector
   ENDIF

   ; Check arguments.
   IF N_Elements(image) EQ 0 THEN Message, 'Must pass IMAGE argument.'
   IF N_Elements(clip) EQ 0 THEN clip = 2 ELSE clip = 0 > clip < 48

   ; Check for underflow of values near 0. Yuck!
   curExcept = !Except
   !Except = 0
   i = Where(image GT -1e-35 AND image LT 1e-35, count)
   IF count GT 0 THEN image[i] = 0.0
   void = Check_Math()
   !Except = curExcept

   output = image

   ; Check keywords.
   IF N_Elements(maxOut) EQ 0 THEN maxOut = 255B ELSE maxout = 0 > Byte(maxOut) < 255
   IF N_Elements(minOut) EQ 0 THEN minOut = 0B ELSE minOut = 0 > Byte(minOut) < 255
   IF minOut GE maxout THEN Message, 'OMIN must be less than OMAX.'

   ; Calculate binsize.
   maxr = Max(image, MIN=minr, /NAN)
   range = maxr - minr
   IF Size(image, /TName) EQ 'BYTE' THEN binsize = 1.0 ELSE binsize = range / 1000.
   IF Size(image, /TName) NE Size(binsize, /TName) THEN image = Convert_To_Type(image, Size(binsize, /TName))
   h = Histogram(image, BINSIZE=binsize, OMIN=omin, OMAX=omax, /NAN)
   n = N_Elements(image)
   cumTotal = Total(h, /CUMULATIVE)
   minIndex = Value_Locate(cumTotal, n * (clip/100.))
   IF minIndex EQ -1 THEN minIndex = 0
   WHILE cumTotal[minIndex] EQ cumTotal[minIndex + 1] DO BEGIN
        minIndex = minIndex + 1
   ENDWHILE
   minThresh = minIndex * binsize + omin

   ; Not all files can be clipped appropriately. If maxIndex
   ; is -1 or N_Elements(cumTotal), or maxIndex=minIndex then 
   ; just byte scale the image and get out of here.
   maxIndex  = Value_Locate(cumTotal, n * ((100-clip)/100.))
   IF (maxIndex EQ -1) || (maxIndex EQ N_Elements(cumTotal)) || (maxIndex EQ minIndex) THEN BEGIN
       threshold = [minr, maxr]
       Message, 'Image histogram could not be clipped successfully. Image is byte scaled.', /Informational
       IF Keyword_Set(negative) THEN RETURN, 255B - BytScl(image, /NAN) ELSE RETURN, BytScl(image, /NAN)
   ENDIF
   
   ; If you are still here, try to clip the histogram.
   WHILE cumTotal[maxIndex] EQ cumTotal[maxIndex - 1] DO BEGIN
       maxIndex = maxIndex - 1
   ENDWHILE
   maxThresh = maxIndex * binsize + omin

   ; Save the thresholds.
   threshold = [minThresh, maxThresh]
   
   ; Scale the data.
   output = cgScaleVector(Temporary(output), MIN=threshold[0], MAX=threshold [1], minOut, maxOut)

   IF Keyword_Set(negative) THEN RETURN, 0B > (maxout - output + minOut) < 255B $
      ELSE RETURN, output

END
