docs: public void process(T input , float threshLow, float threshHigh , GrayU8 output ) {
  
  		if( threshLow < 0 || threshHigh < 0 )
  			throw new IllegalArgumentException("Threshold must be >= zero!");
  
  		if( hysteresisMark != null ) {
  			if( output == null )
  				throw new IllegalArgumentException("An output image must be specified when configured to mark edge points");
  		}
  
  		// setup internal data structures
  		blurred.reshape(input.width,input.height);
  		derivX.reshape(input.width,input.height);
  		derivY.reshape(input.width,input.height);
  		intensity.reshape(input.width,input.height);
  		suppressed.reshape(input.width,input.height);
  		angle.reshape(input.width,input.height);
  		direction.reshape(input.width,input.height);
  		work.reshape(input.width,input.height);
  
  		// run canny edge detector
  		blur.process(input,blurred);
  		gradient.process(blurred, derivX, derivY);
  		GGradientToEdgeFeatures.intensityAbs(derivX, derivY, intensity);
  		GGradientToEdgeFeatures.direction(derivX, derivY, angle);
  		GradientToEdgeFeatures.discretizeDirection4(angle, direction);
  		GradientToEdgeFeatures.nonMaxSuppression4(intensity, direction, suppressed);
  
  		performThresholding(threshLow, threshHigh, output);
  	}