; $Id: look.pro,v 1.15 1997/06/13 01:09:16 loo Exp $
;
;+
; NAME:
;       LOOK
;
; PURPOSE:
;       This procedure displays an image and relevant statistics on it.
;
; CATEGORY:
;       Image display
;
; CALLING SEQUENCE:
;       LOOK, Image [, Output, Rev_ind]
;
; INPUTS:
;       Image:      The image to be displayed. If input is not 2-D, then
;                   it is not displayed, but its histogram is plotted.
;
; KEYWORD PARAMETERS:
;       HISTOGRAM:  If set, the histogram of image intensities is plotted.
;
;                   If the image is an integer type (BYTE, INT, or LONG) then
;                   this keyword may be set to the bin size.
;                   Default is 1 for BYTE, 128 for INT, and 256 for LONG.
;
;                   If the image is a floating point type (FLOAT or DOUBLE),
;                   then this keyword may be set to the number of
;                   desired bins in the histogram. Default is 256 bins.
;
;       NOPLOT:     If set, histogram is computed if specified, but not
;                   plotted.
;                   Ignored if no output variable for histogram is specified.
;
;       NODISPLAY:  If set, display statistics without displaying image.
;
;       NOSTATS:    If set, display image without displaying statistics.
;
;       NOSCALE:    If set, image is displayed with scaling from zero to the
;                   maximum value. Also, the histogram is plotted from zero
;                   to maximum value.
;                   Default is to scale from minimum to maximum value of image.
;
;       FIT_SCREEN: If set to 1, large images are shrunk to fit the screen
;                   size, if necessary.
;
;                   If set to 2, images are forced to fit the screen size by
;                   shrinking or expanding by the required amount.
;                   The aspect ratio is not changed - i.e., the same zoom
;                   factor is used for both x and y dimensions.
;
;                   If set to 3, images are forced to conform to the screen
;                   shape by shrinking, expanding, or modifying the aspect
;                   ratio.
;
;                   The zoom factor used is saved in the ZOOM keyword
;                   parameter.
;
;                   Ignored if NODISPLAY is set.
;
;       ZOOM:       The zoom factor to apply when displaying the image.
;                   It may have one or two elements. If there is only one
;                   element, the same zoom factor is applied to the x and y
;                   dimensions. Otherwise, the two elements specify the
;                   x and y zoom factors.
;
;                   Alternatively, a variable to receive the zoom factor used
;                   when fitting the image to the screen.
;
;                   NOTE: In both cases, the ZOOM keyword parameter is
;                   overwritten by the actual zoom factor(s) used, which can
;                   be different from the supplied parameter due to roundoff.
;                   This can result in conversion of a one element zoom
;                   factor into a two element zoom factor.
;
;                   NOTE: Zoom factor < 1 shrinks image.
;                         Zoom factor > 1 expands image.
;
;       SCROLL:     If set, and if the displayed image size is larger than
;                   75% of the screen size, a scrollable window is used to
;                   display the image.
;                   Alternatively, this keyword can be set explicitly to the
;                   image size as a fraction of the screen size (floating
;                   point value between 0.0-1.0) above which scrollbars
;                   will be used.
;
;                   NOTE: This keyword is ignored if a desired window index
;                         is specified using the WINDOW keyword below.
;                         It is also ignored if the FIT keyword is set.
;
;       WINDOW:     The window number in which to display the image.
;                   The default is window 0.
;                   If an undefined named variable is supplied to this keyword,
;                   it will receive the window index of  the window created
;                   by the LOOK procedure.
;
;       HWINDOW:    The window number in which to plot the histogram.
;                   The default is window 1. If Hwindow is the same as Window,
;                   the histogram is displayed in the next higher window
;                   number.
;
; OPTIONAL OUTPUTS:
;       Output:     A variable to receive a FLOAT version of the image scaled
;                   into the range of 0 to 255. Any applied zoom factor will
;                   be reflected in the output.
;
;                   Alternatively, a variable to receive the histogram, if
;                   the HISTOGRAM keyword is set.
;                   output[*,0] contains the intensities at the bin midpoints.
;                   output[*,1] contains the frequency at each intensity.
;
;       Rev_ind:    If the HISTOGRAM keyword is set, a variable to receive
;                   the reverse indices array from the histogram operation.
;                   See the documentation for HISTOGRAM for details.
;
; ROUTINES USED BY THIS MODULE:
;       CONGRID, STDEV     (IDL User Library)
;
;       XWINDOW SUITE      (Bill Loo's IDL Routines)
;
; COMMON BLOCKS:
;       LOOK_COMMON
;
; ORIGINALLY WRITTEN BY:
;       Werner Meyer-Ilse
;       Center for X-Ray Optics
;       Lawrence Berkeley National Lab
;
; REWRITTEN BY:
;       Billy W. Loo, Jr.
;       Bioengineering Graduate Group, UCSF / UCB
;       School of Medicine, UCD
;       Lawrence Berkeley National Lab
;       September, 1996
;-
;
; MODIFICATION HISTORY:
;
; $Log: look.pro,v $
; Revision 1.15  1997/06/13 01:09:16  loo
; * Modified SCROLL keyword to allow choosing of the fraction.
;
; Revision 1.14  1997/06/12  22:50:11  loo
; * Modified to work with new XWINDOW routine suite.
;
; Revision 1.13  1997/06/10  02:25:07  loo
; * Added INTERP keyword to CONGRID call.
;
; Revision 1.12  1997/06/04 17:08:18  loo
; * Modified for compatibility with PLOT_COLORS.
;
; Revision 1.11  1997/01/23  02:09:37  loo
; * Added common block LOOK_COMMON so that screen size determination is done
;   only once instead of every time FIT_SCREEN is used.
;
; Revision 1.10  1997/01/22  23:18:49  loo
; * Changed to allow generation of output image without displaying it.
;
; Revision 1.9  1997/01/21  04:09:17  loo
; * Force return of two element zoom parameter when FIT_SCREEN is used.
;
; Revision 1.8  1997/01/21  03:47:38  loo
; * Added FIT_SCREEN and ZOOM keywords.
;
; Revision 1.7  1996/10/12  16:44:34  loo
; * Changed behavior to plot histogram only, if input does not have
;   2 dimensions.
;
; Revision 1.6  1996/09/30  14:11:24  loo
; * Added ability to return reverse indices array.
;
; Revision 1.5  1996/09/25  19:36:55  loo
; * Added ability to change display window number.
;
; Revision 1.4  1996/09/24  16:02:52  loo
; * Added several histogram features.
;
; Revision 1.3  1996/09/15  00:20:00  loo
; * Fixed bug in displaying stats on BYTE type data.
;
; Revision 1.2  1996/09/14  14:51:59  loo
; * Added ability to return histogram values.
;
; Revision 1.1  1996/09/13  17:53:00  loo
; * Initial revision.

PRO LOOK, image, output, rev_ind, $
          HISTOGRAM=histogram,    $
          NOPLOT=noplot,          $
          NODISPLAY=nodisplay,    $
          NOSTATS=nostats,        $
          NOSCALE=noscale,        $
          FIT_SCREEN=fit_screen,  $
          ZOOM=zoom,              $
          SCROLL=scroll,          $
          WINDOW=window,          $
          HWINDOW=hwindow

  ;Declare common block for module.
  COMMON LOOK_COMMON, scr_sz_

  ;Check for !TOP_COLOR system variable.
  DEFSYSV, '!TOP_COLOR', EXISTS=top_color_exists

  im_sz = SIZE(image)

  ;Check if 2-D image was supplied.
  IF (im_sz[0] NE 2) THEN BEGIN
    IF (N_ELEMENTS(image) LE 1) THEN $
      MESSAGE, 'Input must have more than one element.' + STRING(7B) ;Beep!

    nodisplay = 1 ;Do not display image.

    IF NOT(KEYWORD_SET(HISTOGRAM)) THEN histogram = 1 ;Display histogram.
  ENDIF

  ;Must do something.
  IF KEYWORD_SET(NOSTATS) AND NOT(KEYWORD_SET(HISTOGRAM)) $
     AND (N_PARAMS() LT 2) THEN nodisplay = 0

  ;Save current display window.
  orig_window = !D.WINDOW

  ;Find max and min values.
  max = MAX(image, MIN=min)

  ;Set scaling, if specified.
  IF KEYWORD_SET(NOSCALE) THEN min = 0

  ;Upgrade min and max to at least INT type.
  max = max * 1
  min = min * 1

  ;Fit image to screen, if requested.
  IF KEYWORD_SET(FIT_SCREEN) AND NOT(KEYWORD_SET(NODISPLAY)) THEN BEGIN
    ;Check in which window the image should be displayed.
    IF (N_ELEMENTS(window) EQ 0) THEN window = 0

    ;Determine screen size, if necessary.
    IF (N_ELEMENTS(scr_sz_) EQ 0) THEN BEGIN
      DEVICE, GET_SCREEN_SIZE=scr_sz_

      ;Create test window to determine actual max image size.
      XWINDOW, window, XSIZE=scr_sz_[0], YSIZE=scr_sz_[1]
      scr_sz_ = [!D.X_SIZE, !D.Y_SIZE]
      XWDELETE, window
    ENDIF

    ;Determine required zoom factor.
    zoom = [FLOAT(scr_sz_[0]) / im_sz[1], FLOAT(scr_sz_[1]) / im_sz[2]]

    ;Don't change aspect ratio unless specified.
    IF (fit_screen LT 3) THEN zoom = [(zoom[0]<zoom[1]), (zoom[0]<zoom[1])]

    ;Don't expand image unless specified.
    IF (fit_screen EQ 1) THEN zoom = zoom < 1.0
  ENDIF

  ;Default zoom factor.
  IF (N_ELEMENTS(zoom) EQ 0) THEN zoom = 1

  IF (N_ELEMENTS(zoom) EQ 1) THEN BEGIN
    xzoom = zoom
    yzoom = zoom
  ENDIF $

  ELSE BEGIN
    xzoom = zoom[0]
    yzoom = zoom[1]
  ENDELSE

  ;Create zoomed image, if specified.
  IF (xzoom GT 0 AND yzoom GT 0 AND (xzoom NE 1 OR yzoom NE 1)) THEN BEGIN
    ;Compute display size.
    d_xs = ROUND(im_sz[1] * xzoom)
    d_ys = ROUND(im_sz[2] * yzoom)

    ;Compute actual zoom used.
    xzoom = FLOAT(d_xs) / im_sz[1]
    yzoom = FLOAT(d_ys) / im_sz[2]

    IF (N_ELEMENTS(zoom) EQ 1) THEN BEGIN
      IF (xzoom NE yzoom) THEN zoom = [xzoom, yzoom] ELSE zoom = xzoom
    ENDIF $

    ELSE zoom = [xzoom, yzoom]

    ;Check if zoom is integer.
    int_zoom = NOT([FLOAT(d_xs > im_sz[1]) / (d_xs < im_sz[1]), $
                    FLOAT(d_ys > im_sz[2]) / (d_ys < im_sz[2])] MOD 1.0)

    ;If yes, use REBIN.
    IF (int_zoom[0] AND int_zoom[1]) THEN BEGIN
      d_img = REBIN(image, d_xs, d_ys)
    ENDIF $

    ;Otherwise, use CONGRID.
    ELSE BEGIN
      d_img = CONGRID(image, d_xs, d_ys, /INTERP)
    ENDELSE
  ENDIF

  ;Display image, if specified.
  IF NOT(KEYWORD_SET(NODISPLAY)) THEN BEGIN
    ;Check in which window the image should be displayed.
    ;If index is specified, disable scrolling.
    IF (N_ELEMENTS(window) EQ 0) THEN window = 0 ELSE scroll = 0

    ;Check if scrollbars are needed.
    IF KEYWORD_SET(SCROLL) THEN BEGIN
      fraction = scroll > 0.01 < 0.95
      IF (scroll EQ 1) THEN fraction = 0.75
    ENDIF

    ;Display zoomed image, if specified.
    IF (N_ELEMENTS(d_img) GT 0) THEN BEGIN
      ;Create window at correct size.
      IF KEYWORD_SET(SCROLL) THEN BEGIN
        ;Get the screen size, and see if image is bigger than the specified
        ;fraction.
        DEVICE, GET_SCREEN_SIZE=screen_sz

        IF (d_xs GT ROUND(fraction*screen_sz[0])) OR $
           (d_ys GT ROUND(fraction*screen_sz[1])) THEN BEGIN
          x_scroll_size = d_xs < ROUND(fraction*screen_sz[0])
          y_scroll_size = d_ys < ROUND(fraction*screen_sz[1])

          XWINDOW, XSIZE=d_xs, YSIZE=d_ys,      $
                   X_SCROLL_SIZE=x_scroll_size, $
                   Y_SCROLL_SIZE=y_scroll_size

;          xwin    = XWGET()
          window = xwin.window
          WIDGET_CONTROL, xwin.tlb_id, $
            TLB_SET_TITLE='XWIN ' + STRTRIM(window, 2) + ': Zoomed Image'
        ENDIF $

        ;If not, turn off scrolling.
        ELSE scroll = 0
      ENDIF

      IF NOT(KEYWORD_SET(SCROLL)) THEN BEGIN
        XWINDOW, window, XSIZE=d_xs, YSIZE=d_ys, $
                         TITLE='IDL ' + STRTRIM(window, 2) + ': Zoomed Image'
      ENDIF

      ;Display image.
      IF KEYWORD_SET(top_color_exists) THEN BEGIN
        need_reset = 0
        tmp = EXECUTE('need_reset = !TOP_COLOR GT (!D.TABLE_SIZE-1)')
        IF (need_reset) THEN PLOT_COLORS, /RESET
        tmp = EXECUTE('TV, BYTSCL(d_img, MIN=min, TOP=!TOP_COLOR)')
      ENDIF $
      ELSE TV, BYTSCL(d_img, MIN=min, TOP=!D.TABLE_SIZE-1)
    ENDIF $

    ;If no zoom, display original image.
    ELSE BEGIN
      ;Create window at correct size.
      IF KEYWORD_SET(SCROLL) THEN BEGIN
        ;Get the screen size, and see if image is bigger than the specified
        ;fraction.
        DEVICE, GET_SCREEN_SIZE=screen_sz

        IF (im_sz[1] GT ROUND(fraction*screen_sz[0])) OR $
           (im_sz[2] GT ROUND(fraction*screen_sz[1])) THEN BEGIN
          x_scroll_size = im_sz[1] < ROUND(fraction*screen_sz[0])
          y_scroll_size = im_sz[2] < ROUND(fraction*screen_sz[1])

          XWINDOW, XSIZE=im_sz[1], YSIZE=im_sz[2], $
                   X_SCROLL_SIZE=x_scroll_size,    $
                   Y_SCROLL_SIZE=y_scroll_size

;          xwin    = XWGET()
          window = xwin.window
        ENDIF $

        ;If not, turn off scrolling.
        ELSE scroll = 0
      ENDIF

      IF NOT(KEYWORD_SET(SCROLL)) THEN BEGIN
         XWINDOW, window, XSIZE=im_sz[1], YSIZE=im_sz[2]
      ENDIF

      ;Display image.
      IF KEYWORD_SET(top_color_exists) THEN BEGIN
        need_reset = 0
        tmp = EXECUTE('need_reset = !TOP_COLOR GT (!D.TABLE_SIZE-1)')
        IF (need_reset) THEN PLOT_COLORS, /RESET
        tmp = EXECUTE('TV, BYTSCL(image, MIN=min, TOP=!TOP_COLOR)')
      ENDIF $
      ELSE TV, BYTSCL(image, MIN=min, TOP=!D.TABLE_SIZE-1)
    ENDELSE
  ENDIF

  ;Print statistics, if specified.
  IF NOT(KEYWORD_SET(NOSTATS)) THEN BEGIN
    ;Find relevant statistics.
    std = STDEV(image, mean)
    med = MEDIAN(image)

    sizes = STRTRIM(im_sz[1:im_sz[0]], 2)
    IF (im_sz[0] GT 1) THEN $
      sizes[0:(im_sz[0]-2)] = sizes[0:(im_sz[0]-2)] + ' x'

    PRINT, 'Image size:    ', sizes

    IF (N_ELEMENTS(d_img) GT 0) THEN                                          $
      PRINT, 'Displayed size: ' + STRTRIM(d_xs, 2) + ' x ' + STRTRIM(d_ys, 2) $
           + ', Zoom factor: ' + STRTRIM(xzoom, 2) + ' x ' + STRTRIM(yzoom, 2)

    PRINT, 'Image statistics:'
    PRINT, 'Min = ' + STRTRIM(min, 2) + ', Mean   = ' + STRTRIM(mean, 2) + $
           ', Standard deviation = ' + STRTRIM(std, 2)
    PRINT, 'Max = ' + STRTRIM(max, 2) + ', Median = ' + STRTRIM(med, 2)
  ENDIF

  ;Compute histogram, if specified.
  IF KEYWORD_SET(HISTOGRAM) THEN BEGIN
    ;Figure out type of image.
    ;See if it is an integer type (BYTE, INT, or LONG).
    im_tp = im_sz[im_sz[0] + 1]
    IF (im_tp GE 1) AND (im_tp LE 3) THEN int_tp = 1 ELSE int_tp = 0

    ;Case of integer types.
    IF KEYWORD_SET(int_tp) THEN BEGIN
      ;Set bin size.
      IF (histogram EQ 1) THEN BEGIN
        CASE im_tp OF
          ;BYTE type.
          1: bin_sz = 1

          ;INT type.
          2: bin_sz = 128

          ;LONG type.
          3: bin_sz = 256
        ENDCASE
      ENDIF $

      ELSE bin_sz = LONG(histogram) > 1 ;Smallest bin size is 1.

      ;Compute histogram. Compute reverse indices if specified.
      IF (N_PARAMS() EQ 3) THEN BEGIN
        h = HISTOGRAM(image, MIN=min, BINSIZE=bin_sz, REVERSE_INDICES=rev_ind)
      ENDIF $

      ELSE h = HISTOGRAM(image, MIN=min, BINSIZE=bin_sz)

      ;Input intensity vector. Vector of bin midpoints.
      num_bins = N_ELEMENTS(h)
      x = (min + (bin_sz - 1) / 2.0) + FINDGEN(num_bins) * bin_sz
    ENDIF $

    ;Case of floating point types.
    ELSE BEGIN
      ;Number of bins in histogram.
      IF (histogram EQ 1) THEN num_bins = 256 $

      ELSE num_bins = histogram > 2 ;At least 2 bins.

      ;Compute bin size.
      bin_sz = (max - min) / FLOAT(num_bins)

      ;Input intensity vector. Vector of bin midpoints.
      x = (min + bin_sz/2.0) + FINDGEN(num_bins) * bin_sz

      ;Adjustment to prevent endpoint artifacts.
      ;Values at exactly the bin boundary are counted in the bin to the left.
      factor = 1 + (0.01 / num_bins)

      ;Compute histogram. Compute reverse indices if specified.
      IF (N_PARAMS() EQ 3) THEN BEGIN
         h = HISTOGRAM(image, MIN=min, BINSIZE=bin_sz*factor, $
                       REVERSE_INDICES=rev_ind)
      ENDIF $

      ELSE h = HISTOGRAM(image, MIN=min, BINSIZE=bin_sz*factor)
    ENDELSE

    ;Plot histogram, if specified.
    IF NOT(KEYWORD_SET(NOPLOT)) OR (N_PARAMS() LT 2) THEN BEGIN
      ;Check in which window the histogram should be plotted.
      IF (N_ELEMENTS(hwindow) EQ 0) THEN hwindow = 1

      ;Prevent histogram from being plotted in the same window as the image.
      IF NOT(N_ELEMENTS(NODISPLAY)) THEN $
        IF (hwindow EQ window) THEN hwindow = window + 1

      ;Create window for plotting.
      XWINDOW, hwindow, XSIZE=512, YSIZE=320

      ;Set plotting color.
      pcol = !D.TABLE_SIZE -1
      IF KEYWORD_SET(top_color_exists) THEN tmp = EXECUTE('pcol = !TOP_COLOR')

      PLOT, x, h, XTITLE='Image intensity', YTITLE='Frequency', $
                  PSYM=10, COLOR=pcol
    ENDIF
  ENDIF

  ;Generate histogram output, if specified.
  IF (N_PARAMS() GE 2) AND KEYWORD_SET(HISTOGRAM) THEN output = [[x], [h]]

  ;Generate image output, if specified.
  IF (N_PARAMS() GE 2) AND NOT(KEYWORD_SET(HISTOGRAM)) THEN BEGIN
    IF (N_ELEMENTS(d_img) GT 0) THEN BEGIN
      output = FLOAT(BYTSCL(d_img, MIN=min))
    ENDIF $

    ELSE output = FLOAT(BYTSCL(image, MIN=min))
  ENDIF

  ;Reset plotting window to original window.
  IF (orig_window NE -1) THEN XWSET, orig_window         $
  ELSE IF NOT(KEYWORD_SET(NODISPLAY)) THEN XWSET, window $
  ELSE IF NOT(KEYWORD_SET(NOPLOT))    THEN XWSET, hwindow

  RETURN
END ;look
