Beispiel #1
0
class MJPEGStreamer(object):
	_httpPort = 8085

	def __init__(self, videoDevice, size, fps, format):
		self._device = '/dev/video%d' % videoDevice
		self._size = size
		self._fps = fps
		self._format = format
		self._videoRunning = False
		self._process = None


		self._infoArea = cv2.imread(os.path.join(app.static_folder, 'img', 'camera-info-overlay.jpg'), cv2.cv.CV_LOAD_IMAGE_COLOR)
		self._infoAreaShape = self._infoArea.shape

		#precalculated stuff
		watermark = cv2.imread(os.path.join(app.static_folder, 'img', 'astroprint_logo.png'))
		watermark = cv2.resize( watermark, ( 100, 100 * watermark.shape[0]/watermark.shape[1] ) )
		
		self._watermarkShape = watermark.shape
		
		watermarkMask = cv2.cvtColor(watermark, cv2.COLOR_BGR2GRAY) / 255.0
		watermarkMask = np.repeat( watermarkMask, 3).reshape( (self._watermarkShape[0],self._watermarkShape[1],3) )
		self._watermakMaskWeighted = watermarkMask * watermark
		self._watermarkInverted = 1.0 - watermarkMask

	def startVideo(self):
		if not self._process:
			command = [
				"/mjpeg_streamer/mjpg_streamer",
				"-i",
				"input_uvc.so -d %s -f %s -r %s --no_dynctrl%s" % (self._device, self._fps, self._size, ' -y' if self._format == 'x-raw' else ''),
				"-o",
				"output_http.so -p %d" % self._httpPort
			]

			self._process = Command(command, env={'LD_LIBRARY_PATH': '/mjpeg_streamer'}, stderr=open(os.devnull, 'w'))
			if self._process:
				self._process.run(async=True)

				time.sleep(0.2)

				return self._process.returncode is None

		return False

	def stopVideo(self):
		if self._process:
			if self._process.returncode is None:
				self._process.terminate()
				self._process.wait()
			
			self._process = None

	def getPhoto(self, text=None, doneCb=None):
		image = None
		stopAfterPhoto = False

		if not self._process:
			self.startVideo()
			stopAfterPhoto = True

		try:
			if self._format == 'x-raw':
				time.sleep(1.8) # we need to give the camera some time to stabilize the image. 1.8 secs has been tested to work in low end cameras

			response = urllib2.urlopen('http://127.0.0.1:%d?action=snapshot' % self._httpPort)
			image = response.read()

		except urllib2.URLError:
			pass

		if image and text:
			decodedImage = cv2.imdecode(np.fromstring(image, np.uint8), cv2.CV_LOAD_IMAGE_COLOR)
			self._apply_watermark(decodedImage, text)
			image = cv2.cv.EncodeImage('.jpeg', cv2.cv.fromarray(decodedImage), [cv2.cv.CV_IMWRITE_JPEG_QUALITY, 80]).tostring()

		if stopAfterPhoto:
			self.stopVideo()

		if doneCb:
			doneCb(image)

		else:
			return image

	def _apply_watermark(self, img, text):
			if text and img != None:
				imgPortion = img[-(self._watermarkShape[0]+5):-5, -(self._watermarkShape[1]+5):-5]
				img[-(self._watermarkShape[0]+5):-5, -(self._watermarkShape[1]+5):-5] = (self._watermarkInverted * imgPortion) + self._watermakMaskWeighted

				img[:self._infoAreaShape[0], :self._infoAreaShape[1]] = self._infoArea
				cv2.putText(img, text, (30,17), cv2.FONT_HERSHEY_PLAIN, 1.0, (81,82,241), thickness=1)

				return True

			return False
Beispiel #2
0
class MJPEGStreamer(object):
    _httpPort = 8085

    def __init__(self, videoDevice, size, fps, format):
        self._logger = logging.getLogger(__name__)
        self._device = '/dev/video%d' % videoDevice
        self._size = size
        self._fps = fps
        self._format = format
        self._videoRunning = False
        self._process = None
        self._streaming = False
        self._needsExposure = True

        self._infoArea = cv2.imread(
            os.path.join(app.static_folder, 'img', 'camera-info-overlay.jpg'),
            cv2.cv.CV_LOAD_IMAGE_COLOR)
        self._infoAreaShape = self._infoArea.shape

        #precalculated stuff
        watermark = cv2.imread(
            os.path.join(app.static_folder, 'img', 'astroprint_logo.png'))
        watermark = cv2.resize(
            watermark, (100, 100 * watermark.shape[0] / watermark.shape[1]))

        self._watermarkShape = watermark.shape

        watermarkMask = cv2.cvtColor(watermark, cv2.COLOR_BGR2GRAY) / 255.0
        watermarkMask = np.repeat(watermarkMask, 3).reshape(
            (self._watermarkShape[0], self._watermarkShape[1], 3))
        self._watermakMaskWeighted = watermarkMask * watermark
        self._watermarkInverted = 1.0 - watermarkMask

    def startStreamer(self):
        if not self._process:
            command = [
                "/mjpeg_streamer/mjpg_streamer", "-i",
                "input_uvc.so -d %s -f %s -r %s --no_dynctrl%s" %
                (self._device, self._fps, self._size,
                 ' -y' if self._format == 'x-raw' else ''), "-o",
                "output_http.so -p %d" % self._httpPort
            ]

            self._process = Command(command,
                                    env={'LD_LIBRARY_PATH': '/mjpeg_streamer'},
                                    stderr=open(os.devnull, 'w'))
            if self._process:
                self._process.run(async=True)

                time.sleep(0.2)

                running = self._process.returncode is None

                return running

        return False

    def startVideo(self):
        if self._streaming:
            return True

        if self.startStreamer():
            self._streaming = True
            return True

        return False

    def stop(self):
        if self._process:
            if self._process.returncode is None:
                self._process.terminate()
                tries = 4
                while self._process.returncode is None:
                    if tries > 0:
                        tries -= 1
                        time.sleep(0.5)
                        self._process.poll()
                    else:
                        break

                if self._process.returncode is None:
                    self._logger.warn(
                        'Unable to terminate nicely, killing the process.')
                    self._process.kill()
                    self._process.wait()

            self._process = None

        self._streaming = False
        self._needsExposure = True

    def stopVideo(self):
        self._streaming = False

    def isVideoStreaming(self):
        return self._streaming

    def getPhoto(self, doneCb, text=None):
        image = None

        if not self._process:
            if not self.startStreamer():
                self._logger.error('Unable to start MJPEG Streamer')
                doneCb(None)
                return

        try:
            if self._needsExposure and not self.isVideoStreaming():
                time.sleep(
                    1.8
                )  # we need to give the camera some time to stabilize the image. 1.8 secs has been tested to work in low end cameras
                self._needsExposure = False

            response = urllib2.urlopen('http://127.0.0.1:%d?action=snapshot' %
                                       self._httpPort)
            image = response.read()

        except urllib2.URLError as e:
            self._logger.error(e)

        if image and text:
            decodedImage = cv2.imdecode(np.fromstring(image, np.uint8),
                                        cv2.CV_LOAD_IMAGE_COLOR)
            self._apply_watermark(decodedImage, text)
            image = cv2.cv.EncodeImage(
                '.jpeg', cv2.cv.fromarray(decodedImage),
                [cv2.cv.CV_IMWRITE_JPEG_QUALITY, 80]).tostring()

        doneCb(image)

    def _apply_watermark(self, img, text):
        if text and img != None:
            imgPortion = img[-(self._watermarkShape[0] + 5):-5,
                             -(self._watermarkShape[1] + 5):-5]
            img[-(self._watermarkShape[0] + 5):-5,
                -(self._watermarkShape[1] +
                  5):-5] = (self._watermarkInverted *
                            imgPortion) + self._watermakMaskWeighted

            img[:self._infoAreaShape[0], :self.
                _infoAreaShape[1]] = self._infoArea
            cv2.putText(img,
                        text, (30, 17),
                        cv2.FONT_HERSHEY_PLAIN,
                        1.0, (81, 82, 241),
                        thickness=1)

            return True

        return False
Beispiel #3
0
class Shell(AbstractContextManager):
    """A real shell running on the host machine, to be used in a context."""
    def __init__(self, shell_binary, shell_argstr, return_code_echo_command,
                 command_separator, is_interactive):
        """Sets up the context for a system shell process.

        Parameters
        ----------
        shell_binary : str
            The shell binary, e.g. ``/bin/bash``, to start.
        shell_argstr : str
            Additional arguments to be passed to the shell at start.
        return_code_echo_command : str
            A command string in the shell's own language that prints to
            standard output the return code of the previously executed command.
        command_separator : str
            The character sequence to separate commands with.
        is_interactive : bool
            Whether the started shell is an interactive one.
            This does only change the behaviour of the context manager, to make
            the shell itself interactive, additional arguments in
            `shell_argstr` might need to be passed.
        """
        self._args = shell_argstr
        self._binary = shell_binary
        # Note: The execution of the command and the reading of the output
        # has to happen BEFORE this timeout is hit, but a large timeout would
        # also mean waiting a lot for small commands, so this has to be
        # balanced carefully.
        self._capture = Capture(timeout=0.5, buffer_size=-1)
        self._command = Command(shell_binary + ' ' + shell_argstr,
                                stdout=self._capture)
        self._echo = return_code_echo_command + command_separator
        self._interactive = is_interactive
        self._separator = command_separator
        self._started = False

    def __enter__(self):
        """Starts the shell in a context manager setting."""
        return self.start()

    def __exit__(self, exc_type, exc_value, trace):
        """Destroys the shell and leaves the context."""
        if self._interactive:
            self.kill()
        else:
            self.terminate()
        return False

    def start(self):
        """Starts the underlying shell process as configured in
        :py:func:`__init__`.
        """
        if self._started:
            raise OSError(EEXIST, "The shell is already running!")
        print("[Shell] Starting '{0} {1}'...".format(self._binary, self._args),
              file=sys.stderr)
        try:
            self._command.run(input=PIPE, async_=True)
        except ValueError:
            raise FileNotFoundError("The shell binary '{0}' cannot be found "
                                    "on the system.".format(self._binary))
        self._started = True
        return self

    @property
    def pid(self):
        """The PID of the started process."""
        if not self._started:
            raise OSError(ESRCH, "The shell is not running!")
        return self._command.process.pid

    def kill(self):
        """Kills (by sending ``SIGKILL`` (``9``)) to the shell process."""
        if not self._started:
            raise OSError(ESRCH, "The shell is not running!")
        self._command.kill()
        self._command.wait()
        self._capture.close(True)
        self._started = False

    def terminate(self):
        """Terminates (by sending ``SIGTERM`` (``15``)) to the shell process.

        Note
        ----
        Interactive shells (:py:attr:`is_interactive`) usually catch and ignore
        this signal, and as such, :py:func:`kill` should be used to shut them
        down properly.
        """
        if not self._started:
            raise OSError(ESRCH, "The shell is not running!")
        self._command.terminate()
        self._command.wait()
        self._capture.close(True)
        self._started = False

    def execute_command(self, cmd, timeout=None):
        """Execute `cmd` in the shell, wait `timeout` seconds, and read back
        the result.

        Parameters
        ----------
        cmd : str
            The command to execute.
            This string will be written into the shell's standard input
            verbatim.
        timeout : int
            The time (in seconds) to wait before the output of the command is
            read.

        Returns
        -------
        return_code : int
            The return code of the executed command.
        result : str
            The *standard output* of the executed command.

        Note
        ----
        The command executed in the shell is extended with
        :py:attr:`command_separator` and :py:attr:`return_code_echo_command`,
        and written to the shell.
        In case of a conventional ``/bin/bash`` shell, for example, executing
        `cmd` ``echo "Foo"`` will actually execute:

        .. code-block:: bash

           echo "Foo";
           echo $;

        Which will result in the output:

        .. code-block:: bash

           Foo
           0

        to be read as a result.

        Warning
        -------
        The underlying library and the execution of piped shells does not allow
        a good method of "sensing" when the output became available while
        keeping interactivity.
        A too small `timeout` on a loaded system might result in output being
        lost, while a too big one will result in every command waiting for
        a considerable time.
        """
        cmd = cmd + self._separator + self._echo
        print("[Shell '{0}'] Running command:\n{1}\n".format(
            self._binary,
            '\n'.join(list(map(lambda l: "    > " + l, cmd.split('\n'))))),
              file=sys.stderr)

        self._command.stdin.write(cmd.encode('utf-8'))
        self._command.stdin.flush()

        stdout = self._capture.read(timeout=timeout)
        parts = stdout.decode().rstrip().split('\n')
        result, returncode = '\n'.join(parts[:-1]).rstrip(), parts[-1].rstrip()

        print("[Shell '{0}'] Command result #{1}:\n{2}".format(
            self._binary, returncode, result),
              file=sys.stderr)
        return int(returncode), result