docs: @Override
  	public void processImage(int sourceID, long frameID, final BufferedImage buffered, ImageBase input) {
  		System.out.flush();
  
  		synchronized (bufferedImageLock) {
  			original = ConvertBufferedImage.checkCopy(buffered, original);
  			work = ConvertBufferedImage.checkDeclare(buffered, work);
  		}
  
  		if( saveRequested ) {
  			saveInputImage();
  			saveRequested = false;
  		}
  
  		final double timeInSeconds;
  
  		// TODO Copy all data that's visualized outside so that GUI doesn't lock
  		synchronized (this) {
  			long before = System.nanoTime();
  			detector.process((T)input);
  			long after = System.nanoTime();
  			timeInSeconds = (after-before)*1e-9;
  		}
  
  		// create a local copy so that gui and processing thread's dont conflict
  		synchronized (detected) {
  			this.detected.reset();
  			for (QrCode d : detector.getDetections()) {
  				this.detected.grow().set(d);
  			}
  			this.failures.reset();
  			for (QrCode d : detector.getFailures()) {
  				if( d.failureCause.ordinal() >= QrCode.Failure.READING_BITS.ordinal())
  					this.failures.grow().set(d);
  			}
  //			System.out.println("Failed "+failures.size());
  //			for( QrCode qr : failures.toList() ) {
  //				System.out.println("  cause "+qr.failureCause);
  //			}
  		}
  		controlPanel.polygonPanel.thresholdPanel.updateHistogram((T)input);
  
  
  		SwingUtilities.invokeLater(() -> {
  			controls.setProcessingTimeS(timeInSeconds);
  			viewUpdated();
  			synchronized (detected) {
  				controlPanel.messagePanel.updateList(detected.toList(),failures.toList());
  			}
  		});
  	}