;;; -*- Mode: Lisp; Syntax: Common-lisp; Package: MU; Base: 10.; -*-

;;; Copyright (c) 1988, Massachusetts Institute of Technology
;;; Authors: Mike Drumheller, Walter E. Gillett

(in-package :mu)

;;; This file defines parameters for the stereo code, as well as functions for manipulating the parameters.

(defvar *AC* nil)				;accums - the matching scores for each disparity
(defvar *PM* nil)				;potential matches
(defvar *SP* nil)				;support planes
(defvar *DISPLAY?* t)				;whether to display results on color monitor
(defvar *GRABBED-PAIR* nil)			;identifies pair of grabbed images
(defvar *CALIBRATION-DATA-DIRECTORY* "b:>gillett>data>")	;where calibration data is stored

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;  "System" parameters:

(defvar *INTERPOLATE-AND-DRAW?* t)		;interpolate stereo data and draw results
(defvar CHANGE-INTERPOLATION-PARAMS? nil)      
(defvar CHANGE-DRAWING-PARAMS? nil)
(defvar CHANGE-STEREOMATCHING-PARAMS? t)

#+lispm
(defun CHANGE-SYSTEM-PARAMETERS ()
  (tv:choose-variable-values
    '("STEREO SYSTEM PARAMETERS"
;      (user-supplied-pair "Input other than frame grabber" :sexp)
      (change-stereomatching-params? "Change stereomatching parameters?" :boolean)
      (change-interpolation-params? "Change interpolation parameters?" :boolean)
      (change-drawing-params? "Change drawing parameters?" :boolean)
      (*display?* "Display?" :boolean)
      (*interpolate-and-draw?* "Interpolate disparity and draw contours?" :boolean))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;  Stereomatching parameters:

(defvar *AVERAGE?* t)				;whether to sample frames with averaging
;;; Experimental feature: bilinear subsampling of 576x454 frame.  Extracts 454x454 central
;;; section.  Note that calibration must be performed with the test pattern sampled in the same
;;; way.  Used to provide nice pictures (no boundary lossage) for other vision modules.
(defvar *BILINEAR-SUBSAMPLING?* nil)

(defvar *WARP?* t)
;;; Setting this to true causes matches outside of the right image boundary to be suppressed.
;;; It depends on a side effect of the warping procedure: the warped image is nonzero where it
;;; is defined.  Since it is risky to depend on this side effect, the default value for this
;;; parameter is nil.
(defvar *SUPPRESS-MATCHES-OUTSIDE-OF-RIGHT-IMAGE-BOUNDARY?* nil)
(defvar *KILL-NON-UNIQUE-MATCHES?* t)
(defvar *APPLY-CLEANUP-HEURISTICS?* nil)
(defvar *MAX-ALLOWED-DIFFERENCE-FROM-MEAN* 5)
(defvar *MAX-ALLOWED-VARIANCE* 5)
(defvar *CLEANUP-STATISTICS-WIDTH* 5)
(defvar *MARGIN* 0.2)

;;; Weak compatibility: allow edges with differing polarities to match.  Demonstrates
;;; that the matcher needs edge direction in order to do a good job.
(defvar *WEAK-COMPAT?* nil)
(defvar *SUPPORT-REGION-HEIGHT* 23)
(defvar *SUPPORT-REGION-WIDTH* 23)
(defvar *ITERS* 1)
(defvar *IVD* -1)
(defvar *FVD* 1)
(defvar *ID* -2)
(defvar *FD* 19)
(defvar *ENTIRE-FORBIDDEN-ZONE?* t)
(defvar *BLANK-OUT-SUPPORT?* t)
(defvar *CLEAR-ACCUMS-WHERE-NO-MATCH?* nil)
(defvar *ANGLES* '(0 90))
(defvar *ZC-THRESHOLD* 1)
(defvar *BOTTOM-EDGES-CLIP* 28)
(defvar *TOP-EDGES-CLIP* 28)
(defvar *LEFT-EDGES-CLIP* 16)
(defvar *RIGHT-EDGES-CLIP* 16)

(defvar *SIGMA* 1.5 "sigma of Gaussian smoothing for edge detection")


(defvar *IN-SIGMA* 1.5 "sigma of Gaussian smoothing for excitatory DOG")
(defvar *EX-SIGMA* 6.0 "sigma of Gaussian smoothing for inhibitory DOG")

;;; Minimum score required for a match to be declared.  If nil, don't threshold the score.
;;; By default, a score of 0 loses.
(defvar *MIN-SCORE-FOR-MATCH* 1)
;;; If true, use Canny edge detector, otherwise DOGs (Difference Of Gaussians approximation
;;; to the Laplacian of a Gaussian).
(defvar *CANNY-EDGES?* t)
;;; When using the Canny edge detector, this is the gradient noise threshold.
;;; (See the Canny edge code for an explanation.)
(defvar *CANNY-NOISE-PERCENTAGE* 0)

;;; If true, return sparse matching scores - set the score to zero where there is no match.
(defvar *USER-MATCHING-SCORES-SPARSE?* nil)
;;; If true, edge-based matching will yield results everywhere (except where matches have been
;;; suppressed, not just at edge locations.
(defvar *DENSE-DISPARITY?* t)

;;; globals to hold image pvars
(defvar *LEFT-IMAGE* nil)
(defvar *RIGHT-IMAGE* nil)

;;; Gradient matching: a definite win.  If *match-gradients* is t, allow edges to match only
;;; if the dot product of their gradient directions is more than *match-gradients-tolerance*.
;;; The idea is to compare the gradient angles.  Looking at the dot product is much less
;;; expensive than computing atan!! to get the angles.
;;;
;;; This feature is only available when using Canny edges.
(defvar *MATCH-GRADIENTS?* t)
(defvar *MATCH-GRADIENTS-TOLERANCE* 0.5 "equivalent to 30 degrees between vectors")
(defvar *LEFT-GRADIENTS* nil "globals to hold gradients")
(defvar *RIGHT-GRADIENTS* nil "globals to hold gradients")

(defvar *GRADIENT-BITS* 8 "how many gradient bits to keep around (not including sign bit)")

;;;; Experimental feature: if *find-close-winners* is true, then set the pvar *close-winners*
;;;; to true for the appropriate locations.
;(defvar *FIND-CLOSE-WINNERS?* nil)
;(defvar *CLOSE-WINNERS* nil)

;;;; Angles from which to look for close winners.  0 = left line of sight, 90 = right
;;;; line of sight are the only possibilities.
;(defvar *CLOSE-WINNER-ANGLES* '(0))

(defvar *NORMALIZE?* nil "normalize matching scores")

(defvar *square?* nil "Square (T) or Absolute value (NIL) of image diffences?")

(defvar *grey-feature-bits* 8 
  "How many bits to retain in grey-level feature description")

(defvar *BITS-PER-ACCUM* nil 
  "Use this variable to vary the number of bits allocated to hold matching scores.
   The code automatically drops bits from the scores to make sure that they fit in the 
   allotted space."
  )
 
;;; experimental feature: 
(defvar *MATCH-GREY?* nil "match grey levels directly, rather than edges")

(defvar *filter-type* :dog "alternatives: :dog :gaussian")

;;; If *discard-bad-scores?* is true, then all scores corresponding to an average pixel
;;; difference of *max-average-pixel-diff* or worse are discarded.  The main purpose of
;;; this is to save score bits.  If the score is bad, we don't necessarily care exactly
;;; how bad.
(defvar *MAX-AVERAGE-PIXEL-DIFF* (1- (floor cmv::*pixel-values* 4)))
;;; if true, don't allow matches where the maximum average pixel difference is too large
(defvar *DISCARD-BAD-SCORES?* t)

;;; There are two functions for summing square neighborhoods, cmv::*sum-nbhd and unsigned-square-sum!!.
;;; The first function is faster but uses a lot of stack space.  If *fast-square-sum?* is T, the code
;;; uses the faster function, and otherwise uses the slower function.
(defvar *FAST-SQUARE-SUM?* nil)

;;; When this is T, *USER-MATCHING-SCORES-SPARSE?* is t and *DENSE-DISPARITY?* is nil
(defvar *keep-Ed-happy?* nil)

;;; When this is T, neighborhoods are summed only in the X direction.  This is
;;; currently only implemented under (eq *match-grey?* t)
(defvar *use-1d-neighborhoods?* nil)

;;; When non-NIL, should be a number which is the sigma of the gaussian.
(defvar *vertical-smearing* nil)

;;; Only do NMS along the specified dimension:
(defvar *NMS-dimension* :full)

(defvar *returned-disparity-offset* 128.
  "This is the number you subtract from the (unsigned) returned disparity array
to arrive at the computed disparity value.  NIL means to just use the minimum value
in the disparity map (actually, the negative of that value).")

(defvar *min-disparity-for-display* nil
  "This parameter (and *max-disparity-for-display*) control the scaling used when
displaying the disparity map on the color screen.  This number represent what disparity
will be \"blue.\"")

(defvar *max-disparity-for-display* nil
  "This parameter (and *min-disparity-for-display*) control the scaling used when
displaying the disparity map on the color screen.  This number represent what disparity
will be \"red.\"")

#+lispm
(defun CHANGE-STEREOMATCHING-PARAMETERS ()
  (tv:choose-variable-values
    '("Stereo Algorithm Parameters:"
      ""
      "Disparity control:"
      (*id* "Initial disparity" :choose (-26 -24 -22 -20 -18 -16 -14 -12 -10 -8 -6 -4 -2 0))
      (*fd* "Final disparity" :choose (0 +2 +4 +6 +8 +10 +12 +14 +16 +18 +20 +22 +24 +26))
      (*ivd* "Initial vertical disparity" :choose (0 -1 -2 -3))
      (*fvd* "Final vertical disparity" :choose (0 1 2 3))
      (*iters* "Iterations" :choose (1 2 3 4 5 6))

      ""
      "Region widths:"
      (*support-region-width* "Square support width"
			      :choose (3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33))
;;; We are using square regions, not rectangular regions - this parameter
;;; is irrelevant.
;      (*support-region-height* "Rectangular support height"
;			       :choose (3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33))
      (*sigma* "Gaussian sigma for smoothing image"
	       :choose (1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0))
      (*ex-sigma* "sigma of Gaussian smoothing for excitatory DOG"
	       :choose (1.0 1.25 1.5 1.75 2.0 2.25 2.5 2.75 3.0))
      (*in-sigma* "sigma of Gaussian smoothing for inhibitory DOG"
	       :choose (1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0))

      ""
      "Edge detection parameters:"
      (*zc-threshold* "ZC threshold"
		      :choose (1 100000 200000 300000 400000 500000 1000000))
      (*canny-edges?* "Use Canny edge detector?" :boolean)
      (*canny-noise-percentage* "Canny noise threshold (percentage)" :number)
      (*match-gradients?* "Use gradient direction in matching? (Canny edges only)" :boolean)
      (*match-gradients-tolerance* "Tolerance for gradient matching? (between 0 and 1)" :number)
      (*gradient-bits* "Gradient precision in bits? (space is expensive)" :number)

      ""
      "Filtered intensity matching parameters:"
      (*match-grey?* "Match on intensity values?" :boolean)
      (*filter-type* "Filter: Gaussian or Difference of Gaussian" :choose (:gaussian :dog))
      (*normalize?* "Normalize?" :boolean)
      (*square?* "Square(Yes) or Absolute value (No) of image diffences?" :boolean)
      (*grey-feature-bits* "Bits kept in filtered images (both :dog and :gaussian)" :number)     
      (*bits-per-accum* "Bits-per-accum (should be 16 for intensity matching)" :number-or-nil)

;;; These are not generally useful
;;      (*use-1d-neighborhoods?* "Use one-dimensional neighborhoods" :boolean)
;;      (*vertical-smearing* "Vertical smearing sigma" :number-or-nil)

      ""
      "Scoring parameters:"
;;      (*entire-forbidden-zone?* "Use entire forbidden zone?" :boolean)
      (*nms-dimension* "Look for maxima along: " :choose (:left-and-right :left-only
							  :right-only :full))
      (*clear-accums-where-no-match?* "Clear accums at no-matches?" :boolean)
      (*user-matching-scores-sparse?* "Return matching scores only at edges?" :boolean)
      (*dense-disparity?* "Return disparity scores everywhere?" :boolean)
      (*keep-Ed-happy?* "Ensure that Ed Gamble is happy" :boolean)
      (*margin* "Close winners margin (between 0 and 1)" :number)

      ""
      "Preprocessing parameters:"
      (*average?* "Downsample with averaging?" :boolean)
      (*warp?* "Compensate for perspective distortion?" :boolean)
      

      ""
      "Postprocessing parameters:"
      (*apply-cleanup-heuristics?* "Apply cleanup heuristics?" :boolean)
      (*max-allowed-variance* "Maximum Variance" :choose (0 1 2 3 4 5 6 8 10 12 14 16 18 20))
      (*max-allowed-difference-from-mean* "Maximum Difference from Mean"
					  :choose (0 1 2 3 4 5 6 8 10 12 14 16 18 20))
      (*cleanup-statistics-width* "Width for cleanup statistics"
				  :choose (3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33))
      ""
      "Other parameters:"
      (*returned-disparity-offset* "Returned disparity offset" :number-or-nil)
      (*min-disparity-for-display* "Minimum disparity in color display" :number-or-nil)
      (*max-disparity-for-display* "Maximum disparity in color display" :number-or-nil)
      ""
      "Clipping:"
      (*top-edges-clip* "Amount by which to clip top border" :number)
      (*bottom-edges-clip* "Amount by which to clip bottom border" :number)
      (*left-edges-clip* "Amount by which to clip left border" :number)
      (*right-edges-clip* "Amount by which to clip right border" :number)
      )
    :max-lines 40)
  (when *keep-Ed-happy?*
    (setq *dense-disparity?* nil
	  *user-matching-scores-sparse?* t))
  (ecase *nms-dimension*
    (:full (setq *entire-forbidden-zone?* t))
    (:left-and-right (setq *entire-forbidden-zone?* nil *angles* '(0 90)))
    (:right-only (setq *entire-forbidden-zone?* nil *angles* '(90)))
    (:left-only (setq *entire-forbidden-zone?* nil *angles* '(0))))
  
  (if (not (and (= *ivd* -1) (= *fvd* 1)))
      (format t "~%~%WARNING.  You are operating with OTHER THAN *IVD* = =1, *FVD* = 1.  Do you REALLY want to do this?~%~%")))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Interpolation parameters
;;;
;;; Note: thin-plate interpolation appears broken.  At least, I have never used it
;;; successfully.  Rubber sheet works just fine.  (WEG 11/17/87)

(defvar INTERPOLATION-METHOD 'rubber-sheet)	; or 'thin-plate
(defvar SHIFT-BEFORE-DIFF 12)
(defvar SHIFT-AFTER-DIFF -8)
(defvar CONSTRAINED-DIFF-ITERS 300)
(defvar FREE-DIFF-ITERS 20)

(defvar SHIFT-BEFORE-TP 12)
(defvar SHIFT-AFTER-TP -8)
(defvar PRE-THIN-PLATE-DIFF-ITERS 500)
(defvar CONSTRAINED-TP-ITERS 1000)
(defvar FREE-DIFF-ITERS-AFTER-TP 20)

#+lispm
(defun CHANGE-INTERPOLATION-PARAMETERS ()
  (tv:choose-variable-values
    '("INTERPOLATION PARAMETERS"
      (interpolation-method "Interpolation method"
			    :choose (rubber-sheet thin-plate))
      (constrained-diff-iters "Constrained diffusion iterations"
			      :choose (0 10 50 100 200 500 1000 1500 2000 3000 5000))
      (free-diff-iters "Free diffusion iterations"
		       :choose (0 1 2 3 4 5 10 15 20 25 30 40 60 100))
      (shift-before-diff "Shift before diffusing"
			 :choose (12 10 8 6 4 2 0))
      (pre-thin-plate-diff-iters "Pre-thin-plate diffusions iters"
				 :choose (0 10 50 100 200 500 1000 1500 2000 3000 5000))
      (constrained-tp-iters "Constrained thin-plate iterations"
			    :choose (0 10 50 100 200 500 1000 1500 2000 3000 5000))
      (free-diff-iters-after-tp "Free diff-iters after thin-plate"
				:choose (0 1 2 3 4 5 10 15 20 25 30 40 60 100))
      (shift-before-tp "Shift before thin-plate"
		       :choose (12 10 8 6 4 2 0)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Drawing parameters:

(defvar CONTOUR-LINES 20)
(defvar CONTOUR-MAP-TOP-BORDER 28)
(defvar CONTOUR-MAP-BOTTOM-BORDER 28)
(defvar ZOOM? t)
(defvar USE-COLOR t)				;use color to indicate depth

;;; superimpose depth contours on the downsampled left view
(defvar *SHOW-LEFT-VIEW-WITH-STEREO-DATA* t)
;;; If true, show the original 576x454 left view with depth contours superimposed
;;; (expanded from 256x256 data).  Works only when the images were captured by the
;;; frame grabber, since the original full left view is otherwise seldom available.
;;; The downsampling must have been 512x512 -> 256x256; we expand by a factor of 2.
(defvar *SHOW-FULL-LEFT-VIEW-WITH-STEREO-DATA* nil)
;;; If true, superimpose sparse depth data on original image.  Otherwise, superimpose
;;; isodisparity contours on original image.
(defvar *SUPERIMPOSE-DEPTH-DATA* nil)
;;; If true, display the stereo data before and after applying cleanup heuristics.
(defvar *SHOW-CLEANUP-HEURISTICS?* nil)

(defvar *OVERLAY-RED* 255)			;for drawing contours as overlays
(defvar *OVERLAY-GREEN* 0)
(defvar *OVERLAY-BLUE* 0)

;;; Horizontal gap between views.  Note: this parameter should always be a multiple
;;; of 8, so that views can be displayed using fast i/o.
(defvar *STEREO-GAP* 16)			
(defvar *STEREO-X0-OFFSET*)			;x offset of left view
(defvar *STEREO-X1-OFFSET*)			;x offset of right view
(defvar *STEREO-Y-OFFSET*)			;y offset of both views

;;; set horizontal gap between displayed views
(defun SET-STEREO-GAP ()
  (setq *stereo-x0-offset* (floor (- *frame-width* 512 *stereo-gap*) 2)
	*stereo-x1-offset* (+ *stereo-x0-offset* 256 *stereo-gap*)
	*stereo-y-offset* (floor (- *frame-height* 256) 2)))
(set-stereo-gap)				;initialize

#+lispm
(defun CHANGE-DRAWING-PARAMETERS ()
  (let ((old-stereo-gap *stereo-gap*))		;save old value
    (tv:choose-variable-values
      '("MAP DRAWING PARAMETERS"
	(contour-lines "Contour lines"
		       :choose (2 5 10 15 20 25 30 35 40 45 50 55 60 65))
	(contour-map-top-border "Contour map top border"
				:choose (20 22 24 26 28 30 32 34 36 38))
	(contour-map-bottom-border "Contour map bottom border"
				   :choose (20 22 24 26 28 30 32 34 36 38))
	(zoom? "Show contour map big?" :boolean)
	(use-color "Use color to indicate depth?" :boolean)
	(*show-left-view-with-stereo-data* "Superimpose depth contours on downsampled left view?" 
					   :boolean)
	(*show-full-left-view-with-stereo-data* "Superimpose depth contours on full left view?"
						:boolean)
	(*stereo-gap* "Horizontal gap between displayed pairs of views" :number)))
    (if ( *stereo-gap* old-stereo-gap)		;if stereo gap has changed
	(set-stereo-gap))			;then fix associated display offsets
    ))

#+lispm
(defun SET-STEREO-PARAMETERS ()
  (change-system-parameters)
  (if change-stereomatching-params?
      (change-stereomatching-parameters))
  (if change-interpolation-params?
      (change-interpolation-parameters))
  (if change-drawing-params?
      (change-drawing-parameters)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; miscellaneous

(defvar *LEFT-EDGES* (make-array '(256 256) :element-type '(mod 2)))
(defvar *RIGHT-EDGES* (make-array '(256 256) :element-type '(mod 2)))
(defvar *PAIR-OF-EDGE-ARRAYS* (list *left-edges* *right-edges*))

;;; These are actually defined in warping.lisp

(proclaim '(special *x-warp-array* *y-warp-array*))
