Friday, 15 January 2021

Troubleshooting virtualenvwrapper errors

It was my mistake that I did not track immediately the cause of the error that one day started appearing each time I'd open a new Terminal:

/usr/bin/python3: Error while finding module specification for 'virtualenvwrapper.hook_loader' (ModuleNotFoundError: No module named 'virtualenvwrapper')
virtualenvwrapper.sh: There was a problem running the initialization hooks.
If Python could not import the module virtualenvwrapper.hook_loader,
check that virtualenvwrapper has been installed for
VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
and that PATH is
set properly.

virtualenvwrapper is, as its name says, a wrapper around virtualenv. 

When a new Terminal is opened, it starts a shell which sources the first of .bash_profile, .bash_login, .profile that exists and is readable. [bash - Why must I source .bashrc every time I open terminal for aliases to work? - Ask Different] 

In my case .bash_profile and .bash_login don't exist but .profile does and it sources ~/.bashrc:

$ cat ~/.profile 
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.

# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
    fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/.local/bin" ] ; then
    PATH="$HOME/.local/bin:$PATH"
fi


So I looked at ~/.bashrc and I found that some time in past I changed it to include some additional environment variables:

$ cat ~/.bashrc
...
# B.Komazec added:
# virtualenv and virtualenvwrapper
export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh
...

Indeed, the error above was coming from virtualenvwrapper.sh, from function virtualenvwrapper_run_hook():

$ cat /usr/local/bin/virtualenvwrapper.sh
...
# Run the hooks
function virtualenvwrapper_run_hook {
    typeset hook_script
    typeset result

    hook_script="$(virtualenvwrapper_tempfile ${1}-hook)" || return 1

    # Use a subshell to run the python interpreter with hook_loader so
    # we can change the working directory. This avoids having the
    # Python 3 interpreter decide that its "prefix" is the virtualenv
    # if we happen to be inside the virtualenv when we start.
    ( \
        virtualenvwrapper_cd "$WORKON_HOME" &&
        "$VIRTUALENVWRAPPER_PYTHON" -m 'virtualenvwrapper.hook_loader' \
            ${HOOK_VERBOSE_OPTION:-} --script "$hook_script" "$@" \
    )
    result=$?

    if [ $result -eq 0 ]
    then
        if [ ! -f "$hook_script" ]
        then
            echo "ERROR: virtualenvwrapper_run_hook could not find temporary file $hook_script" 1>&2
            command \rm -f "$hook_script"
            return 2
        fi
        # cat "$hook_script"
        source "$hook_script"
    elif [ "${1}" = "initialize" ]
    then
        cat - 1>&2 <<EOF
virtualenvwrapper.sh: There was a problem running the initialization hooks.

If Python could not import the module virtualenvwrapper.hook_loader,
check that virtualenvwrapper has been installed for
VIRTUALENVWRAPPER_PYTHON=$VIRTUALENVWRAPPER_PYTHON and that PATH is
set properly.
EOF
    fi
    command \rm -f "$hook_script"
    return $result
}
...


I checked which packages are installed for pip3:

$ pip3 list

...but among them were neither virtualenv nor virtualenvwrapper. So I installed them:

$ sudo pip3 install virtualenv virtualenvwrapper 
Collecting virtualenv
  Downloading virtualenv-20.3.1-py2.py3-none-any.whl (5.7 MB)    
Collecting virtualenvwrapper
  Downloading virtualenvwrapper-4.8.4.tar.gz (334 kB)
Requirement already satisfied: six<2,>=1.9.0 in /usr/lib/python3/dist-packages (from virtualenv) (1.14.0)
Requirement already satisfied: appdirs<2,>=1.4.3 in /usr/lib/python3/dist-packages (from virtualenv) (1.4.3)
Requirement already satisfied: filelock<4,>=3.0.0 in /usr/lib/python3/dist-packages (from virtualenv) (3.0.12)
Collecting distlib<1,>=0.3.1
  Downloading distlib-0.3.1-py2.py3-none-any.whl (335 kB)
Collecting stevedore
  Downloading stevedore-3.3.0-py3-none-any.whl (49 kB)
Collecting virtualenv-clone
  Downloading virtualenv_clone-0.5.4-py2.py3-none-any.whl (6.6 kB)
Collecting pbr!=2.1.0,>=2.0.0
  Using cached pbr-5.5.1-py2.py3-none-any.whl (106 kB)
Building wheels for collected packages: virtualenvwrapper
  Building wheel for virtualenvwrapper (setup.py) ... done
  Created wheel for virtualenvwrapper: filename=virtualenvwrapper-4.8.4-py2.py3-none-any.whl size=24833 sha256=0e77420c4c5bd24518d388768cf102170bd8b0243fd3052fdbec24b01f3bb59a
  Stored in directory: /root/.cache/pip/wheels/47/15/3d/7a26eaf92e79f80a3df3ac5f8e0f0f5b7efdf24d313c594a44
Successfully built virtualenvwrapper
Installing collected packages: distlib, virtualenv, pbr, stevedore, virtualenv-clone, virtualenvwrapper
  Attempting uninstall: distlib
    Found existing installation: distlib 0.3.0
    Not uninstalling distlib at /usr/lib/python3/dist-packages, outside environment /usr
    Can't uninstall 'distlib'. No files were found to uninstall.
Successfully installed distlib-0.3.1 pbr-5.5.1 stevedore-3.3.0 virtualenv-20.3.1 virtualenv-clone-0.5.4 virtualenvwrapper-4.8.4


To verify that packages are installed indeed:

$ pip3 list
...
virtualenv             20.3.1              
virtualenv-clone       0.5.4               
virtualenvwrapper      4.8.4  
...

I opened a new Terminal and voila - the error is gone!

For a reference, my  Python paths are:

$ which python
/usr/bin/python

$ which python3
/usr/bin/python3


References


Sunday, 10 January 2021

Installing JetPack on Jetson TX2 from NVIDIA SDK Manager Docker container

For a very long time, if you wanted to flash your Jetson TX and install JetPack SDK you had to download and install NVIDIA JetPack and later, NVIDIA SDK Manager, on the Linux host computer first. Every package installation pollutes your host machine and also takes some disk space. To avoid this, NVIDA created a Docker image with SDK Manager so once JetPack is installed on Jetson, this Docker image can be deleted and your Linux host remains in the same state as before. This has been available since NVIDIA SDK Manager 1.4 (December 2020).

I had some of the older versions of JetPack installed on my Jetson TX2 and I wanted to install the most recent one (4.4.1 at the moment; 4.5 is announced for January 2021). One of the benefits I wanted to get is the upgrading to the next JetPack release via apt package management tool (this has been available since JetPack 4.4).

I want to share here my experience with the process of running NVIDIA SDK Manager Docker container and flashing the Jetson TX2 with it. I followed the official documentation about this process: Docker Images :: NVIDIA SDK Manager Documentation.

I logged in to NVIDIA Developer center and downloaded this Docker image from this URL: https://developer.nvidia.com/nvidia-sdk-manager-docker-image. The image came as an 942MB archive named sdkmanager-1.4.0.7363_docker.tar.gz.

$ docker load -i ./sdkmanager-1.4.0.7363_docker.tar.gz 
805802706667: Loading layer  65.61MB/65.61MB
3fd9df553184: Loading layer  15.87kB/15.87kB
7a694df0ad6c: Loading layer  3.072kB/3.072kB
2f694c79b042: Loading layer  148.2MB/148.2MB
26765aed7e25: Loading layer  502.3kB/502.3kB
b398b8335e67: Loading layer  6.015MB/6.015MB
08b68150484c: Loading layer  1.135MB/1.135MB
31fbfacf550e: Loading layer  1.135MB/1.135MB
84979f95b15f: Loading layer  502.3kB/502.3kB
cd9205d2e1f9: Loading layer  2.075MB/2.075MB
414d8a00c66e: Loading layer  66.07MB/66.07MB
cc3271f36011: Loading layer  84.83MB/84.83MB
5072b3ebcb77: Loading layer  2.108MB/2.108MB
38be13d541b9: Loading layer  1.781MB/1.781MB
ff63398d24ea: Loading layer  99.17MB/99.17MB
7cb4d04a8659: Loading layer [==================================================>]  462.3MB/462.3MB
cd2c9f6e22b0: Loading layer [==================================================>]  2.048kB/2.048kB
65cd593db96f: Loading layer [==================================================>]  3.584kB/3.584kB
8900dbcf5626: Loading layer [==================================================>]  3.584kB/3.584kB
9ac46abadb31: Loading layer [==================================================>]  3.072kB/3.072kB
a20c9aaeb9c3: Loading layer [==================================================>]  417.3kB/417.3kB
cae1bf65143a: Loading layer [==================================================>]  3.584kB/3.584kB
Loaded image: sdkmanager:1.4.0.7363

As this is the latest version of this Docker image, I tagged it with the latest tag:

$ docker tag sdkmanager:1.4.0.7363 sdkmanager:latest

I made sure that the image is listed among other Docker images on my machine:

$ docker images  
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
sdkmanager              1.4.0.7363          0e9d62e318ad        2 weeks ago         913MB
sdkmanager              latest              0e9d62e318ad        2 weeks ago         913MB
...

SDK Manager executable (sdkmanager) is the entrypoint of this Docker image and I wanted to test the Docker image by running it with some simple CLI commands listed here.

$ docker run -it --rm sdkmanager --help

NVIDIA SDK MANAGER

  NVIDIA SDK Manager is an all-in-one tool that bundles developer software and
  provides an end-to-end development environment setup solution for NVIDIA
  SDKs.

General Options

  -h, --help                             Displays this usage guide.
  --ver                                  Output the version of the installed SDK Manager client
  --settings                             Optional. Configure SDK Manager settings in the terminal.
  --query interactive|noninteractive     Prints all options available for the user. Must be executed with the --use or --offline settings
  --showallversions                      Prints all available product versions for the user.
  --logs                                 Optional. Set this option to export the log files when the process is complete.
  --exitonfinish                         Optional. Automatically exit from SDK Manager when the install/uninstall session is finished (skip user input). Intended for scripts/automation usage.
  --user email_address                   Optional. Set the user email to login. Valid only for NVOnline login.
  --password string                      Optional. Set the user login password. Valid only for NVOnline login.
  --logintype devzone|nvonline           Optional. Login with developer.nvidia.com or partners.nvidia.com account. Default is devzone.
  --staylogin true|false                 Optional. Keep the user account logged-in for next running session.
  --logout                               Logout user account from SDK Manager.
  --offline                              Optional. Skip login to NVIDIA servers. Install SDK from pre downloaded location, used with --downloadfolder option.
  --downloadfolder string                Optional. Set the download folder for the SDK components. Used for downloading the files and for locating the SDK components when using --offline.
  --archivedversions                     Optional. Display only archived versions.
  --cli install|uninstall|downloadonly   Mandatory. Set the requested action.
  --sudopassword string                  Optional. Set the sudo password to skip the authentication prompt.
  --datacollection enable|disable        Optional. Set to enable or disalbe usage data collection.

Specific arguments for install/uninstall:

  --product product_name                 Mandatory. Set the product name.
  --version string                       Mandatory. Set the product version. Use --query to get available version values.
  --targetos target_os                   Mandatory. Set the target hardware operating system.
  --host                                 Optional. Set if host side components need to be installed.
  --target target_hardware               Optional. Set the target hardware in use. Use hardware code name.
  --flash all|a|b|ab|skip                Optional. Set the flash operation mode, which of the Tegras should be flashed.
  --additionalsdk additional_sdk_title   Optional. Specify any additional SDK to install. Multiple entries are allowed.
  --select section_or_group_title        Optional. Specify section or group to installation list. Multiple entries are allowed.
  --deselect section_or_group_title      Optional. Specify section or group to exclude from installation list. Multiple entries are allowed.
  --license accept                       Optional. Set this option to accept the terms and conditions of SDK license agreements.
  --targetimagefolder string             Optional. Set the host location of the target hardware image for flashing.
  --responsefile string                  Optional. Set the response file path. Response file samples can be found in the product folder /opt/nvidia/sdkmanager.

Example

  $ sdkmanager [--user user@user.com] [--query]
  $ sdkmanager [--cli install|uninstall|downloadonly] [cli options] ...
  $ sdkmanager [--settings]
  $ sdkmanager [--help]                                        

$ docker run -it --rm sdkmanager --ver
1.4.0.7363

I connected Jetson TX2 to my Ubuntu host via USB cable and put Jetson into forced recovery mode (as described here: Jetson_X2_Developer_Kit_User_Guide.pdf).

I checked that Jetson is listed among other USB devices:

$ lsusb
...
Bus 002 Device 004: ID 0955:7c18 NVIDIA Corp. APX
...

I then run a query command on SDK manager to get a list of available install options:

$ docker run -it --rm sdkmanager --query
To initiate login process open https://static-login.nvidia.com/service/default/pin?user_code=36223035 in a browser (can be done on a different machine) and login with your NVIDIA Developer account. SDK Manager will start once done.
Login user code: 36223035. (valid for: 10 minutes).
? SDK Manager is waiting for you to complete login. 
  1) Generate a new login user code
  2) Cancel login
  Answer: 
Waiting for user information from NVIDIA authentication server...
Retrieving user information...
Loading and processing available products...
Login succeeded.
Loading user information...
User information loaded successfully.
Loading server data...
Server data loaded successfully.
Available options are:

 Jetson 4.4
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P2888-0001 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P2888-0004 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P2888-0006 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P2888-0060 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3668-0000 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3668-0001 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3310-1000 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3489-0080 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3489-0888 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3489-0000 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P2180-1000 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3448-0000 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3448-0002 --flash all --additionalsdk DeepStream
sdkmanager --cli install --logintype devzone --product Jetson --version 4.4 --targetos Linux --host --target P3448-0020 --flash all --additionalsdk DeepStream


Query completed.

I was not sure which target I should choose so I used table from NVIDIA Jetson Linux Developer Guide : Introduction | NVIDIA Docs to check the P-number of my Jetson TX2 and it was P3310-1000.

It was now the time to do the main part of the job. As a guide, I looked at the docker run command example used for flashing Jetson Nano (listed here: Docker Images :: NVIDIA SDK Manager Documentation) and modified it for Jetson TX2:

$ docker run -it --rm --privileged -v /dev/bus/usb:/dev/bus/usb/ --name JetPack_TX2_Devkit sdkmanager --cli install --logintype devzone --product Jetson --target P3310-1000 --targetos Linux --version 4.4.1 --flash all --license accept --staylogin true --datacollection enable --exitonfinish
To initiate login process open https://static-login.nvidia.com/service/default/pin?user_code=64552553 in a browser (can be done on a different machine) and login with your NVIDIA Developer account. SDK Manager will start once done.
Login user code: 61234563. (valid for: 10 minutes).
? SDK Manager is waiting for you to complete login. 
  1) Generate a new login user code
  2) Cancel login
  Answer: 
Waiting for user information from NVIDIA authentication server...
Retrieving user information...
Loading and processing available products...
Login succeeded.
Loading user information...
User information loaded successfully.
Loading server data...
Server data loaded successfully.
Session initialized...

Installation of this software is under the terms and conditions of the license agreements located in /opt/nvidia/sdkmanager/Eula/
  ===== INSTALLATION COMPLETED SUCCESSFULLY. ===== 
      - Drivers for Jetson: Installed
      - File System and OS: Installed
      - Device Mode Host Setup in Flash: Installed
      - Flash Jetson TX2: Installed
      - Device Mode Host Setup in Target SDK: Installed
      - DateTime Target Setup: Installed
      - CUDA Toolkit for L4T: Installed
      - cuDNN on Target: Installed
      - TensorRT on Target: Installed
      - OpenCV on Target: Installed
      - VisionWorks on Target: Installed
      - VPI on Target: Installed
      - NVIDIA Container Runtime with Docker integration (Beta): Installed
      - Multimedia API: Installed

  ===== Installation completed successfully - Total 14 components =====
  ===== 14 succeeded, 0 failed, 0 up-to-date, 0 skipped =====



Here are some screenshots of the SDK manager running from within Docker container which show the process of downloading the packages, flashing the OS and installing JetPack on Jetson board:



















Upon flashing the Jetson, L4T (Linux 4 Tegra) Ubuntu flavor setup appears:




...and after some typical Ubuntu setup steps, we can see something like this:


...and the final look of the desktop:


Thursday, 17 December 2020

Webcam capture with ffmpeg and OpenCV from Jupyter Notebook

I want to share here my experience with using OpenCV and ffmpeg to capture a webcam output.


Setup:
  • Jupyter notebook running in jupyter-lab
  • Ubuntu 20.04
  • USB web camera
Goal:
  • Capture and display frames from the webcam

OpenCV: Video I/O with OpenCV Overview says that OpenCV: cv::VideoCapture Class calls video I/O backends (APIs) depending on which one is available.

To find out what backends (VideoCaptureAPIs) are available we can use the following code:

import cv2

# cv2a.videoio_registry.getBackends() returns list of all available backends.
availableBackends = [cv2.videoio_registry.getBackendName(b) for b in cv2.videoio_registry.getBackends()]
print(availableBackends)

# Returns list of available backends which works via cv::VideoCapture(int index)
availableCameraBackends = [cv2.videoio_registry.getBackendName(b) for b in cv2.videoio_registry.getCameraBackends()]
print(availableBackends)

The output in my case was: 

['FFMPEG', 'GSTREAMER', 'CV_IMAGES', 'CV_MJPEG']
['FFMPEG', 'GSTREAMER', 'CV_IMAGES', 'CV_MJPEG']

Let's see what is each of these backends:

• FFMPEG is a multimedia framework which can record, convert and stream audio and video.

It contains libavcodec, libavutil, libavformat, libavfilter, libavdevice, libswscale and libswresample which can be used by applications. As well as ffmpeg, ffplay and ffprobe which can be used by end users for transcoding and playing.

• GSTREAMER is a pipeline-based multimedia framework with similar capabilities as ffmpeg.

• CV_IMAGES -  OpenCV Image Sequence (e.g. img_%02d.jpg). Matches cv2.CAP_IMAGES API ID.

• CV_MJPEG - Built-in OpenCV MotionJPEG codec (used for reading video files). Matches cv2.CAP_OPENCV_MJPEG video capture API.

I was surprised to see GSTREAMER listed above as VideoCaptureAPIs documentation says

Backends are available only if they have been built with your OpenCV binaries. 

...and OpenCV package installed in my environment was built only with FFMPEG support:

>>> import cv2
>>> cv2.getBuildInformation()
...
Video I/O:\n    DC1394:                      NO\n    FFMPEG:                      YES\n      avcodec:                   YES (58.35.100)\n      avformat:                  YES (58.20.100)\n      avutil:                    YES (56.22.100)\n      swscale:                   YES (5.3.100)\n      avresample:                YES (4.0.0)\n\n  
...

...which can also be verifed by looking the cmake config in the repository (opencv-feedstock/build.sh at master · conda-forge/opencv-feedstock):

-DWITH_FFMPEG=1     \
-DWITH_GSTREAMER=0  \

Although my conda environment contained all relevant packages:

(my-env) $ conda list | grep 'opencv\|ffmpeg\|gstreamer'
ffmpeg                    4.1.3                h167e202_0    conda-forge
gstreamer                 1.14.5               h36ae1b5_2    conda-forge
opencv                    4.1.0            py36h79d2e43_1    conda-forge

...it is important to know that having ffmpeg and gstreamer packages installed means only that we have their binaries installed (executables and .so libraries) but not Python bindings (modules) or their OpenCV plugins. We are able to launch these applications from terminal but can't import them in Python code.

I tried to force using FFMPEG:

import cv2

deviceId = "/dev/video0"

# videoCaptureApi = cv2.CAP_ANY       # autodetect default API
videoCaptureApi = cv2.CAP_FFMPEG
# videoCaptureApi = cv2.CAP_GSTREAMER 
cap = cv2.VideoCapture("/dev/video2", videoCaptureApi)

cap = cv2.VideoCapture(deviceId)
cap.open(deviceId)
if not cap.isOpened():
    raise RuntimeError("ERROR! Unable to open camera")

try:
    while True:
        ret, frame = cap.read()
        cv2.imshow('frame', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
finally:        
    cap.release()
    cv2.destroyAllWindows()

...but cell execution would fail with:

RuntimeError: ERROR! Unable to open camera

I checked ($ v4l2-ctl --list-devices) - my webcam was indeed with index 2. As this was failing at the very beginning I decided to open python interpreter console and debug there only the isolated code snippet which opens the camera:

(my-env) $ export OPENCV_LOG_LEVEL=DEBUG; export OPENCV_VIDEOIO_DEBUG=1

(my-env) $ python 
Python 3.6.6 | packaged by conda-forge | (default, Oct 12 2018, 14:43:46) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cap = cv2.VideoCapture("/dev/video2", cv2.CAP_FFMPEG)
[ WARN:0] VIDEOIO(FFMPEG): trying capture filename='/dev/video2' ...
[ WARN:0] VIDEOIO(FFMPEG): can't create capture

I also tried to force using Gstreamer to no avail (which was expected):

>>> cap = cv2.VideoCapture("/dev/video2", cv2.CAP_GSTREAMER)
[ WARN:0] VIDEOIO(GSTREAMER): trying capture filename='/dev/video2' ...
[ INFO:0] VideoIO pluigin (GSTREAMER): glob is 'libopencv_videoio_gstreamer*.so', 1 location(s)
[ INFO:0]     - /home/bojan/anaconda3/envs/my-env/lib/python3.6/site-packages/../..: 0
[ INFO:0] Found 0 plugin(s) for GSTREAMER
[ WARN:0] VIDEOIO(GSTREAMER): backend is not available (plugin is missing, or can't be loaded due dependencies or it is not compatible)

Indeed ~/anaconda3/envs/my-env/lib did not contain ffmpeg plugin (libopencv_videoio_ffmpeg*.so files) or Gstreamer plugin (libopencv_videoio_gstreamer*.so files).

These plugins are installed only if OpenCV is build with following CMake options:

- DWITH_FFMPEG=1     \
-DVIDEOIO_PLUGIN_LIST=ffmpeg

...or (for Gstreamer):

-DWITH_GSTREAMER=1 \
-DVIDEOIO_PLUGIN_LIST=gstreamer \

...and apart from WITH_FFMPEG no other were used in the cmake config that was used to build OpenCV package installed in my environment.

As I didn't want to compile OpenCV myself but to achieve my goal with what I have I decided to see if I can run ffmpg process to stream camera output into a pipe and then read the binary information from it and convert it into frames:

import os
import tempfile
import subprocess
import cv2
import numpy as np

# To get this path execute:
#    $ which ffmpeg
FFMPEG_BIN = '/home/bojan/anaconda3/envs/my-env/bin/ffmpeg'


# To find allowed formats for the specific camera:
#    $ ffmpeg -f v4l2 -list_formats all -i /dev/video3
#    ...
#    [video4linux2,v4l2 @ 0x5608ac90af40] Raw: yuyv422: YUYV 4:2:2: 640x480 1280x720 960x544 800x448 640x360 424x240 352x288 320x240 800x600 176x144 160x120 1280x800
#    ...

def run_ffmpeg(fifo_path):
    ffmpg_cmd = [
        FFMPEG_BIN,
        '-i', '/dev/video2',
        '-video_size', '640x480',
        '-pix_fmt', 'bgr24',        # opencv requires bgr24 pixel format
        '-vcodec', 'rawvideo',
        '-an','-sn',                # disable audio processing
        '-f', 'image2pipe',
        '-',                        # output to go to stdout
    ]
    return subprocess.Popen(ffmpg_cmd, stdout = subprocess.PIPE, bufsize=10**8)

def run_cv_window(process):
    while True:
        # read frame-by-frame
        raw_image = process.stdout.read(640*480*3)
        if raw_image == b'':
            raise RuntimeError("Empty pipe")
        
        # transform the bytes read into a numpy array
        frame =  np.frombuffer(raw_image, dtype='uint8')
        frame = frame.reshape((480,640,3)) # height, width, channels
        if frame is not None:
            cv2.imshow('Video', frame)
        
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        process.stdout.flush()
    
    cv2.destroyAllWindows()
    process.terminate()
    print(process.poll())

def run():
    ffmpeg_process = run_ffmpeg()
    run_cv_window(ffmpeg_process)

run()

Et voila! I got the camera capture from Python notebook thanks to ffmpeg and OpenCV.



Monday, 16 November 2020

Introduction to Linux Networking

 

What is the difference between curl and wget?
curl vs Wget
How to download files in Linux from command line with dynamic url

to download a file when it is pointed by a dynamic url, all you need to to is to use single quotes for the url; -O = specify the output filename

$ wget 'http://some.site.com/download?id=234&status=download' -O output_filename


What to do if ping does not help? 

Options are:

  • wget
  • curl
  • traceroute
    • installation: $ sudo apt install traceroute
  • lft
    • "layer 4 traceroute"
    • $ lft -S 192.168.0.20


$ ping example.com
$ traceroute example.com
$ curl -v example.com
$ wget example.com


How to find what DNS servers are used on the local machine?

$ cat /etc/resolv.conf 
# This file is managed by man:systemd-resolved(8). Do not edit.
#
# This is a dynamic resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists all
# configured search domains.
#
# Run "systemd-resolve --status" to see details about the uplink DNS servers
# currently in use.
#
# Third party programs must not access this file directly, but only through the
# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,
# replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 127.0.0.53
options edns0
search whatever.example.com

To find out which DNS servers are used by each network adapter:

$ systemd-resolve --status
Global
          DNSSEC NTA: 10.in-addr.arpa
                      16.172.in-addr.arpa
                      168.192.in-addr.arpa
                      17.172.in-addr.arpa
                      18.172.in-addr.arpa
                      19.172.in-addr.arpa
                      20.172.in-addr.arpa
                      21.172.in-addr.arpa
                      22.172.in-addr.arpa
                      23.172.in-addr.arpa
                      24.172.in-addr.arpa
                      25.172.in-addr.arpa
                      26.172.in-addr.arpa
                      27.172.in-addr.arpa
                      28.172.in-addr.arpa
                      29.172.in-addr.arpa
                      30.172.in-addr.arpa
                      31.172.in-addr.arpa
                      corp
                      d.f.ip6.arpa
                      home
                      internal
                      intranet
                      lan
                      local
                      private
                      test

Link 137 (enxa44cc8e41d0f)
      Current Scopes: none
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no

Link 136 (vethed1f504)
      Current Scopes: none
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no

Link 38 (br-53b4f1b3fbda)
      Current Scopes: none
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no

Link 5 (br-3c8c9487a095)
      Current Scopes: none
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no

Link 4 (docker0)
      Current Scopes: none
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no

Link 3 (wlp2s0)
      Current Scopes: DNS
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
         DNS Servers: x.y.z.q
                      x.y.z.v
          DNS Domain: ~.
                      whatever.example.com

Link 2 (enp0s31f6)
      Current Scopes: none
       LLMNR setting: yes
MulticastDNS setting: no
      DNSSEC setting: no
    DNSSEC supported: no
lines 44-81/81 (END)

Another way:

$ nmcli dev show | grep 'IP4.DNS'
IP4.DNS[1]:                             x.y.z.q
IP4.DNS[2]:                             x.y.z.v

DNS on Ubuntu 18.04

How to block some domain?

Add entry e.g.

0.0.0.0 domain.to.be.blocked

to /etc/hosts and then flush DNS cache:

How To Flush Linux / UNIX DNS Cache - nixCraft

How to direct network traffic use Proxy server?

Use environment variables

export http_proxy=10.21.32.70:8080
export https_proxy=10.21.32.70:8080

Domain names can be used instead of IP addresses.

How To Use Proxy Server To Access Internet at Shell Prompt With http_proxy Variable - nixCraft

How to test proxy?

curl -v -x 11.22.33.44:8080 -L 'https://www.example.com/examples/1'

       -x, --proxy [protocol://]host[:port]
              Use the specified proxy.

              The  proxy string can be specified with a protocol:// prefix. No
              protocol specified or http:// will be treated as HTTP proxy. Use
              socks4://, socks4a://, socks5:// or socks5h:// to request a spe‐
              cific SOCKS version to be used.  (The protocol support was added
              in curl 7.21.7)

              HTTPS  proxy  support  via https:// protocol prefix was added in
              7.52.0 for OpenSSL, GnuTLS and NSS.

To let cURL follow redirects (3xx statuses) add -L:

       -L, --location
              (HTTP)  If  the server reports that the requested page has moved
              to a different location (indicated with a Location: header and a
              3XX  response code), this option will make curl redo the request
              on the new place.

To see HTTP status code, use verbose flag:
     -v, --verbose

How to get public IP address of the computer?


$ dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | awk -F'"' '{ print $2}'

3rd party GeoIP service (its web API) can be accessed via curl:

$ curl -m 30 -X GET "https://api.ipify.org?format=json"
{"ip":"197.44.76.233"}

The output of this command (an IP address) can be assigned to a variable

AGENT_IP=$(curl -s -m 30 -X GET "https://api.ipify.org?format=json")
echo "This build agent '%teamcity.agent.name%' has public IP: $AGENT_IP"