title: Streaming from IP camera using OpenCV
body: <p>I'm using OpenCV to process a camera image, however, at the moment I'm using a USB webcam that is connected to my computer. I was wondering if it is possible to make changes to my current code to stream a video from an IP address.</p>
  
  <p><code>startCamera</code> method opens up the camera.</p>
  
  <p>Code:</p>
  
  <pre><code>public class ObjRecognitionController {
  // FXML camera button
  @FXML
  private Button cameraButton;
  // the FXML area for showing the current frame
  @FXML
  private ImageView originalFrame;
  // the FXML area for showing the mask
  @FXML
  private ImageView maskImage;
  // the FXML area for showing the output of the morphological operations
  @FXML
  private ImageView morphImage;
  // FXML slider for setting HSV ranges
  @FXML
  private Slider hueStart;
  @FXML
  private Slider hueStop;
  @FXML
  private Slider saturationStart;
  @FXML
  private Slider saturationStop;
  @FXML
  private Slider valueStart;
  @FXML
  private Slider valueStop;
  // FXML label to show the current values set with the sliders
  @FXML
  private Label hsvCurrentValues;
  
  // a timer for acquiring the video stream
  private ScheduledExecutorService timer;
  // the OpenCV object that performs the video capture
  private VideoCapture capture = new VideoCapture();
  // a flag to change the button behavior
  private boolean cameraActive;
  
  // property for object binding
  private ObjectProperty&lt;String&gt; hsvValuesProp;
  
  /**
   * The action triggered by pushing the button on the GUI
   */
  @FXML
  private void startCamera()
  {
      // bind a text property with the string containing the current range of
      // HSV values for object detection
      hsvValuesProp = new SimpleObjectProperty&lt;&gt;();
      this.hsvCurrentValues.textProperty().bind(hsvValuesProp);
  
      // set a fixed width for all the image to show and preserve image ratio
      this.imageViewProperties(this.originalFrame, 400);
      this.imageViewProperties(this.maskImage, 200);
      this.imageViewProperties(this.morphImage, 200);
  
      if (!this.cameraActive) {
          // start the video capture
          this.capture.open(0);
  
          // is the video stream available?
          if (this.capture.isOpened()) {
              this.cameraActive = true;
  
              // grab a frame every 33 ms (30 frames/sec)
              Runnable frameGrabber = new Runnable() {
  
                  @Override
                  public void run() {
                      Image imageToShow = grabFrame();
                      originalFrame.setImage(imageToShow);
                  }
              };
  
              this.timer = Executors.newSingleThreadScheduledExecutor();
              this.timer.scheduleAtFixedRate(frameGrabber, 0, 33, TimeUnit.MILLISECONDS);
  
              // update the button content
              this.cameraButton.setText("Stop Camera");
          } else {
              // log the error
              System.err.println("Failed to open the camera connection...");
          }
      } else {
          // the camera is not active at this point
          this.cameraActive = false;
          // update again the button content
          this.cameraButton.setText("Start Camera");
  
          // stop the timer
          try {
              this.timer.shutdown();
              this.timer.awaitTermination(33, TimeUnit.MILLISECONDS);
          } catch (InterruptedException e) {
              // log the exception
              System.err.println("Exception in stopping the frame capture, trying to release the camera now... " + e);
          }
  
          // release the camera
          this.capture.release();
      }
  }
  
  /**
   * Get a frame from the opened video stream (if any)
   * 
   * @return the {@link Image} to show
   */
  private Image grabFrame()
  {
      // init everything
      Image imageToShow = null;
      Mat frame = new Mat();
  
      // check if the capture is open
      if (this.capture.isOpened())
      {
          try
          {
              // read the current frame
              this.capture.read(frame);
  
              // if the frame is not empty, process it
              if (!frame.empty())
              {
                  // init
                  Mat blurredImage = new Mat();
                  Mat hsvImage = new Mat();
                  Mat mask = new Mat();
                  Mat morphOutput = new Mat();
  
                  // remove some noise
                  Imgproc.blur(frame, blurredImage, new Size(7, 7));
  
                  // convert the frame to HSV
                  Imgproc.cvtColor(blurredImage, hsvImage, Imgproc.COLOR_BGR2HSV);
  
                  // get thresholding values from the UI
                  // remember: H ranges 0-180, S and V range 0-255
                  Scalar minValues = new Scalar(this.hueStart.getValue(), this.saturationStart.getValue(),
                          this.valueStart.getValue());
                  Scalar maxValues = new Scalar(this.hueStop.getValue(), this.saturationStop.getValue(),
                          this.valueStop.getValue());
  
                  // show the current selected HSV range
                  String valuesToPrint = "Hue range: " + minValues.val[0] + "-" + maxValues.val[0]
                          + "\tSaturation range: " + minValues.val[1] + "-" + maxValues.val[1] + "\tValue range: "
                          + minValues.val[2] + "-" + maxValues.val[2];
                  this.onFXThread(this.hsvValuesProp, valuesToPrint);
  
                  // threshold HSV image to select tennis balls
                  Core.inRange(hsvImage, minValues, maxValues, mask);
                  // show the partial output
                  this.onFXThread(this.maskImage.imageProperty(), this.mat2Image(mask));
  
                  // morphological operators
                  // dilate with large element, erode with small ones
                  Mat dilateElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(24, 24));
                  Mat erodeElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(12, 12));
  
                  Imgproc.erode(mask, morphOutput, erodeElement);
                  Imgproc.erode(mask, morphOutput, erodeElement);
  
                  Imgproc.dilate(mask, morphOutput, dilateElement);
                  Imgproc.dilate(mask, morphOutput, dilateElement);
  
                  // show the partial output
                  this.onFXThread(this.morphImage.imageProperty(), this.mat2Image(morphOutput));
  
                  // find the tennis ball(s) contours and show them
                  frame = this.findAndDrawBalls(morphOutput, frame);
  
                  // convert the Mat object (OpenCV) to Image (JavaFX)
                  imageToShow = mat2Image(frame);
              }
  
          }
          catch (Exception e)
          {
              // log the (full) error
              System.err.print("ERROR");
              e.printStackTrace();
          }
      }
  
      return imageToShow;
  }
  
  /**
   * Given a binary image containing one or more closed surfaces, use it as a
   * mask to find and highlight the objects contours
   * 
   * @param maskedImage
   *            the binary image to be used as a mask
   * @param frame
   *            the original {@link Mat} image to be used for drawing the
   *            objects contours
   * @return the {@link Mat} image with the objects contours framed
   */
  private Mat findAndDrawBalls(Mat maskedImage, Mat frame) {
      // init
      List&lt;MatOfPoint&gt; contours = new ArrayList&lt;&gt;();
      Mat hierarchy = new Mat();
      // find contours
      Imgproc.findContours(maskedImage, contours, hierarchy, Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);
  
      // if any contour exist...
      if (hierarchy.size().height &gt; 0 &amp;&amp; hierarchy.size().width &gt; 0) {
          // for each contour, display it in yellow
          for (int idx = 0; idx &gt;= 0; idx = (int) hierarchy.get(0, idx)[0]) {
              Imgproc.drawContours(frame, contours, idx, new Scalar(0, 255, 255),2);
  
              Rect rect = Imgproc.boundingRect(contours.get(idx));
  
              if(rect.x &lt; 25){
                  System.out.println("Turn left " + rect.x);
              }else if(rect.x &gt; 600){
                  System.out.println("Turn Right: " + rect.x);
              }
              System.out.println();
          }
      }
  
      return frame;
  }
  
  /**
   * Set typical {@link ImageView} properties: a fixed width and the
   * information to preserve the original image ration
   * 
   * @param image
   *            the {@link ImageView} to use
   * @param dimension
   *            the width of the image to set
   */
  private void imageViewProperties(ImageView image, int dimension) {
      // set a fixed width for the given ImageView
      image.setFitWidth(dimension);
      // preserve the image ratio
      image.setPreserveRatio(true);
  }
  
  /**
   * Convert a {@link Mat} object (OpenCV) in the corresponding {@link Image}
   * for JavaFX
   * 
   * @param frame
   *            the {@link Mat} representing the current frame
   * @return the {@link Image} to show
   */
  private Image mat2Image(Mat frame) {
      // create a temporary buffer
      MatOfByte buffer = new MatOfByte();
      // encode the frame in the buffer, according to the PNG format
      Imgcodecs.imencode(".png", frame, buffer);
      // build and return an Image created from the image encoded in the
      // buffer
      return new Image(new ByteArrayInputStream(buffer.toArray()));
  }
  
  /**
   * Generic method for putting element running on a non-JavaFX thread on the
   * JavaFX thread, to properly update the UI
   * 
   * @param property
   *            a {@link ObjectProperty}
   * @param value
   *            the value to set for the given {@link ObjectProperty}
   */
  private &lt;T&gt; void onFXThread(final ObjectProperty&lt;T&gt; property, final T value)
  {
      Platform.runLater(new Runnable() {
  
          @Override
          public void run()
          {
              property.set(value);
          }
      });
  }
  </code></pre>
  
  <p>}</p>
  
  <p><strong>Camera Documentation</strong></p>
  
  <p><strong>VLC Media Player</strong>
  VLC Media Player is a cross-platform video player, streaming server and converter solution in a single package. Get it here.</p>
  
  <p>For using IP Webcam with VLC media player, in its menu select Media ⇨ Open Network Stream and enter <a href="http://192.168.1.8:8080/video" rel="nofollow">http://192.168.1.8:8080/video</a> for streaming video or <a href="http://192.168.1.8:8080/audio.wav" rel="nofollow">http://192.168.1.8:8080/audio.wav</a> for streaming audio.</p>
  
  <p>You can also use VLC Media player for video recording:</p>
  
  <p>Select Media -> Convert/Save.
  Select Network tab
  Enter <a href="http://192.168.1.8:8080/video" rel="nofollow">http://192.168.1.8:8080/video</a> as URL
  Click convert/save
  Select destination file, format in which you want to save and you're good to go
  <strong>ZoneMinder</strong>
  Use this configuration:</p>
  
  <p>General:
  Source type: Remote
  Function: Monitor</p>
  
  <p>Source: 
  Protocol: HTTP 
  Method: Simple 
  Host name: 192.168.1.8 
  Port: 8080 
  Remote host path: /video (just "video" doesn't work).</p>
  
  <p>Note: If necessary, specify your username in password in URL, like this: username:password@192.168.1.8.</p>
  
  <p><strong>Blue Iris</strong>
  Open "Add camera" dialog as usual</p>
  
  <p>When using IP Webcam, you have to pick "MJPEG Stream" from the list instead of a specific camera.</p>
  
  <p>Video path: /video 
  Audio path: /audio.wav 
  Host name: 192.168.1.8 
  Port: 8080 
  It doesn't matter what you enter into "RTSP/video port" box.</p>
  
  <p><strong>webcamXP</strong>
  In webcamXP main interface, right click on video source and select Network cameras ⇨ Connect...</p>
  
  <p>Set Camera Brand to Android and Camera Model to IP Webcam. Select desired preset (MJPEG recommended). Click Next.</p>
  
  <p>Set Hostname to 192.168.1.8 and Port to 8080.</p>
  
  <p>Click Ok, and you're ready to use your new camera!</p>
  
  <p><strong>Advanced</strong>
  Here is the list of IP Webcam service URLs:</p>
  
  <p><a href="http://192.168.1.8:8080/video" rel="nofollow">http://192.168.1.8:8080/video</a> is the MJPEG URL.
  <a href="http://192.168.1.8:8080/shot.jpg" rel="nofollow">http://192.168.1.8:8080/shot.jpg</a> fetches the latest frame.
  <a href="http://192.168.1.8:8080/audio.wav" rel="nofollow">http://192.168.1.8:8080/audio.wav</a> is the audio stream in Wav format.
  <a href="http://192.168.1.8:8080/audio.aac" rel="nofollow">http://192.168.1.8:8080/audio.aac</a> is the audio stream in AAC format (if supported by hardware).
  <a href="http://192.168.1.8:8080/audio.opus" rel="nofollow">http://192.168.1.8:8080/audio.opus</a> is the audio stream in Opus format.
  <a href="http://192.168.1.8:8080/focus" rel="nofollow">http://192.168.1.8:8080/focus</a> focuses the camera.
  <a href="http://192.168.1.8:8080/nofocus" rel="nofollow">http://192.168.1.8:8080/nofocus</a> releases the focus.</p>