-
Notifications
You must be signed in to change notification settings - Fork 18
add Dockerfiles for Nvidia Jetson devices #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # OSNICK=stretch|bionic|buster | ||
| ARG OSNICK=buster | ||
|
|
||
| #---------------------------------------------------------------------------------------------- | ||
| FROM redisfab/redisedgevision-${OSNICK}:0.2.0 | ||
|
|
||
| # This is due on the following error on ARMv8: | ||
| # /usr/lib/aarch64-linux-gnu/libgomp.so.1: cannot allocate memory in static TLS block | ||
| # Something is exausting TLS, causing libgomp to fail. Preloading it as a workaround helps. | ||
|
|
||
| ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libgomp.so.1 | ||
| ENV DEBIAN_FRONTEND=noninteractive | ||
|
|
||
| RUN apt-get -qq update | ||
|
|
||
| RUN set -ex ;\ | ||
|
||
| apt-get install -y wget python3-distutils patch ;\ | ||
| wget -q https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py ;\ | ||
| python3 /tmp/get-pip.py | ||
|
|
||
| WORKDIR /app | ||
| ADD . /app | ||
|
|
||
| # patch init script for aarch64 | ||
| RUN patch init.py jetson.patch | ||
|
|
||
| RUN pip3 install -r requirements.txt | ||
|
|
||
| ENTRYPOINT [ "python3" ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| diff --git a/app/gear.py b/app/gear.py | ||
| index b602dfe..ab92667 100644 | ||
| --- a/app/gear.py | ||
| +++ b/app/gear.py | ||
| @@ -27,6 +27,7 @@ def toOneList(l): | ||
| return res | ||
|
|
||
| def addToGraphRunner(x): | ||
| + x = x['value'] | ||
| try: | ||
| xlog('addToGraphRunner:', 'count=', x['count']) | ||
|
|
||
| @@ -54,7 +55,7 @@ def addToGraphRunner(x): | ||
| animal = index[str(res.index(res1[0]) - 1)][1] | ||
| xlog('addToGraphRunner:', 'animal=', animal) | ||
|
|
||
| - return (animal, x['img']) | ||
| + return animal, x['img'] | ||
| except: | ||
| xlog('addToGraphRunner: error:', sys.exc_info()[0]) | ||
|
|
||
| @@ -62,21 +63,24 @@ def addToStream(x): | ||
| # save animal name into a new stream | ||
| try: | ||
| redisgears.executeCommand('xadd', 'cats', 'MAXLEN', '~', str(MAX_IMAGES), '*', 'image', 'data:image/jpeg;base64,' + base64.b64encode(x[1]).decode('utf8')) | ||
| + xlog('addToStream: ', x[0]) | ||
| except: | ||
| xlog('addToStream: error:', sys.exc_info()[0]) | ||
|
|
||
| def shouldTakeFrame(x): | ||
| + v = x['value'] | ||
| try: | ||
| global framesToDrop | ||
| framesToDrop += 1 | ||
| - xlog('shouldTakeFrame', x['count'], (framesToDrop % 10 == 0)) | ||
| + xlog('shouldTakeFrame', v['count'], (framesToDrop % 10 == 0)) | ||
| return framesToDrop % 10 == 0 | ||
| except: | ||
| xlog('shouldTakeFrame: error:', sys.exc_info()[0]) | ||
|
|
||
| def passAll(x): | ||
| + v = x['value'] | ||
| try: | ||
| - redisgears.executeCommand('xadd', 'all', 'MAXLEN', '~', str(MAX_IMAGES), '*', 'image', 'data:image/jpeg;base64,' + base64.b64encode(x['img']).decode('utf8')) | ||
| + redisgears.executeCommand('xadd', 'all', 'MAXLEN', '~', str(MAX_IMAGES), '*', 'image', 'data:image/jpeg;base64,' + base64.b64encode(v['img']).decode('utf8')) | ||
| except: | ||
| xlog('passAll: error:', sys.exc_info()[0]) | ||
|
|
||
| @@ -87,4 +91,5 @@ gearsCtx('StreamReader').\ | ||
| map(addToGraphRunner).\ | ||
| filter(lambda x: 'cat' in x[0]).\ | ||
| foreach(addToStream).\ | ||
| - register('camera:0') | ||
| + register(prefix='camera:0') | ||
| + | ||
| diff --git a/app/init.py b/app/init.py | ||
| index 09f77bc..64a1303 100644 | ||
| --- a/app/init.py | ||
| +++ b/app/init.py | ||
| @@ -27,7 +27,7 @@ if __name__ == '__main__': | ||
| print('Loading model - ', end='') | ||
| with open('models/mobilenet_v2_1.4_224_frozen.pb', 'rb') as f: | ||
| model = f.read() | ||
| - res = conn.execute_command('AI.MODELSET', 'mobilenet:model', 'TF', 'CPU', 'INPUTS', 'input', 'OUTPUTS', 'MobilenetV2/Predictions/Reshape_1', model) | ||
| + res = conn.execute_command('AI.MODELSET', 'mobilenet:model', 'TF', 'CPU', 'INPUTS', 'input', 'OUTPUTS', 'MobilenetV2/Predictions/Reshape_1', 'BLOB', model) | ||
| print(res) | ||
|
|
||
| # Load the gear |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| FROM nvcr.io/nvidia/l4t-base:r32.5.0 | ||
|
|
||
| # This is due on the following error on ARMv8: | ||
| # /usr/lib/aarch64-linux-gnu/libgomp.so.1: cannot allocate memory in static TLS block | ||
| # Something is exausting TLS, causing libgomp to fail. Preloading it as a workaround helps. | ||
| # ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libgomp.so.1 | ||
|
|
||
| ENV DEBIAN_FRONTEND=noninteractive | ||
| ENV LD_LIBRARY_PATH=/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH | ||
| ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libgomp.so.1 | ||
|
|
||
| RUN apt-get -qq update && apt-get upgrade -y | ||
|
|
||
| RUN set -x; \ | ||
|
||
| apt-get install -y curl patch; \ | ||
| curl https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py ;\ | ||
|
||
| python3 /tmp/get-pip.py | ||
|
|
||
| RUN pip install redis==3.2.1 | ||
|
|
||
| WORKDIR /usr/src/app | ||
|
|
||
| ADD read_camera_jetson.py ./ | ||
| ADD *.jpg ./ | ||
| ADD build_opencv.patch ./ | ||
|
|
||
| # build opencv from the source for Jetson Nano aarch64 | ||
| RUN wget https://raw.githubusercontent.com/mdegans/nano_build_opencv/301e95dd6c361ed29dc523a46483f05bafd7f70b/build_opencv.sh | ||
| RUN patch build_opencv.sh build_opencv.patch | ||
| RUN chmod +x build_opencv.sh | ||
| RUN ./build_opencv.sh | ||
|
|
||
| CMD [ "python3", "./read_camera_jetson.py", "-u", "redis://redis:6379"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| diff --git a/build_opencv.sh b/build_opencv.sh | ||
| index 486bca9..e3c22c1 100755 | ||
| --- a/build_opencv.sh | ||
| +++ b/build_opencv.sh | ||
| @@ -2,6 +2,7 @@ | ||
| # 2019 Michael de Gans | ||
|
|
||
| set -e | ||
| +set -x | ||
|
|
||
| # change default constants here: | ||
| readonly PREFIX=/usr/local # install prefix, (can be ~/.local for a user install) | ||
| @@ -24,7 +25,7 @@ cleanup () { | ||
| if ! [[ "$1" -eq "--test-warning" ]] ; then | ||
| echo "(Doing so may make running tests on the build later impossible)" | ||
| fi | ||
| - read -p "Y/N " yn | ||
| + yn="Y" | ||
| case ${yn} in | ||
| [Yy]* ) rm -rf /tmp/build_opencv ; break;; | ||
| [Nn]* ) exit ;; | ||
| @@ -53,9 +54,9 @@ install_dependencies () { | ||
| # open-cv has a lot of dependencies, but most can be found in the default | ||
| # package repository or should already be installed (eg. CUDA). | ||
| echo "Installing build dependencies." | ||
| - sudo apt-get update | ||
| - sudo apt-get dist-upgrade -y --autoremove | ||
| - sudo apt-get install -y \ | ||
| + apt-get update | ||
| + apt-get dist-upgrade -y --autoremove | ||
| + apt-get install -y \ | ||
| build-essential \ | ||
| cmake \ | ||
| git \ | ||
| @@ -104,7 +105,7 @@ install_dependencies () { | ||
| configure () { | ||
| local CMAKEFLAGS=" | ||
| -D BUILD_EXAMPLES=OFF | ||
| - -D BUILD_opencv_python2=ON | ||
| + -D BUILD_opencv_python2=OFF | ||
| -D BUILD_opencv_python3=ON | ||
| -D CMAKE_BUILD_TYPE=RELEASE | ||
| -D CMAKE_INSTALL_PREFIX=${PREFIX} | ||
| @@ -114,13 +115,13 @@ configure () { | ||
| -D CUDNN_VERSION='8.0' | ||
| -D EIGEN_INCLUDE_PATH=/usr/include/eigen3 | ||
| -D ENABLE_NEON=ON | ||
| - -D OPENCV_DNN_CUDA=ON | ||
| + -D OPENCV_DNN_CUDA=OFF | ||
| -D OPENCV_ENABLE_NONFREE=ON | ||
| -D OPENCV_EXTRA_MODULES_PATH=/tmp/build_opencv/opencv_contrib/modules | ||
| -D OPENCV_GENERATE_PKGCONFIG=ON | ||
| -D WITH_CUBLAS=ON | ||
| - -D WITH_CUDA=ON | ||
| - -D WITH_CUDNN=ON | ||
| + -D WITH_CUDA=OFF | ||
| + -D WITH_CUDNN=OFF | ||
| -D WITH_GSTREAMER=ON | ||
| -D WITH_LIBV4L=ON | ||
| -D WITH_OPENGL=ON" | ||
| @@ -175,7 +176,7 @@ main () { | ||
| if [[ -w ${PREFIX} ]] ; then | ||
| make install 2>&1 | tee -a install.log | ||
| else | ||
| - sudo make install 2>&1 | tee -a install.log | ||
| + make install 2>&1 | tee -a install.log | ||
| fi | ||
|
|
||
| cleanup --test-warning |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| import argparse | ||
| import cv2 | ||
| import redis | ||
| import time | ||
| import sys | ||
| import os | ||
| import atexit | ||
|
|
||
| try: | ||
| import urllib.parse | ||
| except ImportError: | ||
| import urllib.parse as urlparse | ||
|
|
||
| IMAGE_WIDTH = 640 | ||
| IMAGE_HEIGHT = 480 | ||
|
|
||
| MAX_IMAGES = 1000 # 5 | ||
|
|
||
| def gstreamer_pipeline( | ||
| capture_width=IMAGE_WIDTH, | ||
| capture_height=IMAGE_HEIGHT, | ||
| display_width=IMAGE_WIDTH, | ||
| display_height=IMAGE_HEIGHT, | ||
| framerate=15, | ||
| flip_method=0, | ||
| ): | ||
| return ( | ||
| "nvarguscamerasrc ! " | ||
| "video/x-raw(memory:NVMM), " | ||
| "width=(int)%d, height=(int)%d, " | ||
| "format=(string)NV12, framerate=(fraction)%d/1 ! " | ||
| "nvvidconv flip-method=%d ! " | ||
| "video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! " | ||
| "videoconvert ! " | ||
| "video/x-raw, format=(string)BGR ! appsink" | ||
| % ( | ||
| capture_width, | ||
| capture_height, | ||
| framerate, | ||
| flip_method, | ||
| display_width, | ||
| display_height, | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| class Webcam: | ||
| def __init__(self, infile=0, fps=15.0): | ||
| if infile: | ||
| self.cam = cv2.VideoCapture(infile) | ||
| self.cam.set(cv2.CAP_PROP_FPS, fps) | ||
| self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, IMAGE_WIDTH) | ||
| self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, IMAGE_HEIGHT) | ||
| else: | ||
| self.cam = cv2.VideoCapture(gstreamer_pipeline(flip_method=0), cv2.CAP_GSTREAMER) | ||
| atexit.register(self.cam.release) | ||
|
|
||
|
|
||
| def __iter__(self): | ||
| self.count = -1 | ||
| return self | ||
|
|
||
| def __next__(self): # Python 2.7 | ||
| self.count += 1 | ||
|
|
||
| # Read image | ||
| ret_val, img0 = self.cam.read() | ||
| assert ret_val, 'Webcam Error' | ||
|
|
||
| # Preprocess | ||
| # img = cv2.flip(img0, 1) | ||
| img = img0 | ||
|
|
||
| return self.count, img | ||
|
|
||
| def __len__(self): | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == '__main__': | ||
| parser = argparse.ArgumentParser() | ||
| parser.add_argument('infile', help='Input file (leave empty to use webcam)', nargs='?', type=str, default=None) | ||
| parser.add_argument('-o', '--output', help='Output stream key name', type=str, default='camera:0') | ||
| parser.add_argument('-u', '--url', help='Redis URL', type=str, default='redis://localhost:6379') | ||
| parser.add_argument('--fmt', help='Frame storage format', type=str, default='.jpg') | ||
| parser.add_argument('--fps', help='Frames per second (webcam)', type=float, default=15.0) | ||
| parser.add_argument('--maxlen', help='Maximum length of output stream', type=int, default=1000) | ||
| parser.add_argument('--test', help='transmit image instead of reading webcam', action="store_true") | ||
| args = parser.parse_args() | ||
|
|
||
| # Set up Redis connection | ||
| url = urllib.parse.urlparse(args.url) | ||
| conn = redis.Redis(host=url.hostname, port=url.port) | ||
| if not conn.ping(): | ||
| raise Exception('Redis unavailable') | ||
| print('Connected to Redis') | ||
| sys.stdout.flush() | ||
|
|
||
| if args.test is False: | ||
| print('Operating in camera mode') | ||
| sys.stdout.flush() | ||
| if args.infile is None: | ||
| loader = Webcam(infile=0, fps=args.fps) | ||
| else: | ||
| loader = Webcam(infile=int(args.infile), fps=args.fps) | ||
|
|
||
| for (count, img) in loader: | ||
| _, data = cv2.imencode(args.fmt, img) | ||
| msg = { | ||
| 'count': count, | ||
| 'image': data.tobytes() | ||
| } | ||
| _id = conn.execute_command('xadd', args.output, 'MAXLEN', '~', str(MAX_IMAGES), '*', 'count', msg['count'], 'img', msg['image']) | ||
| print('count: {} id: {}'.format(count, _id)) | ||
| sys.stdout.flush() | ||
| else: | ||
| image_file = os.environ['ANIMAL'] + '.jpg' | ||
| print('Operating in test mode with image ' + image_file) | ||
| sys.stdout.flush() | ||
| img0 = cv2.imread(image_file) | ||
| img = cv2.resize(img0, (IMAGE_WIDTH, IMAGE_HEIGHT)) | ||
| _, data = cv2.imencode(args.fmt, img) | ||
| count = 1 | ||
| while True: | ||
| msg = { | ||
| 'count': count, | ||
| 'image': data.tobytes() | ||
| } | ||
| _id = conn.execute_command('xadd', args.output, 'MAXLEN', '~', str(MAX_IMAGES), '*', 'count', msg['count'], 'img', msg['image']) | ||
| # print('count: {} rc: {} id: {}'.format(count, rc, _id)) | ||
| # sys.stdout.flush() | ||
| count += 1 | ||
| time.sleep(0.1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.