Skip to content

Commit 39fe29a

Browse files
committed
add Dockerfiles for Nvidia Jetson devices
1 parent 3ed59e3 commit 39fe29a

File tree

7 files changed

+479
-0
lines changed

7 files changed

+479
-0
lines changed

app/Dockerfile.jetson

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# OSNICK=stretch|bionic|buster
2+
ARG OSNICK=buster
3+
4+
#----------------------------------------------------------------------------------------------
5+
FROM redisfab/redisedgevision-${OSNICK}:0.2.0
6+
7+
# This is due on the following error on ARMv8:
8+
# /usr/lib/aarch64-linux-gnu/libgomp.so.1: cannot allocate memory in static TLS block
9+
# Something is exausting TLS, causing libgomp to fail. Preloading it as a workaround helps.
10+
11+
ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libgomp.so.1
12+
ENV DEBIAN_FRONTEND=noninteractive
13+
14+
RUN apt-get -qq update
15+
16+
RUN set -ex ;\
17+
apt-get install -y wget python3-distutils patch ;\
18+
wget -q https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py ;\
19+
python3 /tmp/get-pip.py
20+
21+
WORKDIR /app
22+
ADD . /app
23+
24+
# patch init script for aarch64
25+
RUN patch init.py jetson.patch
26+
27+
RUN pip3 install -r requirements.txt
28+
29+
ENTRYPOINT [ "python3" ]

app/jetson.patch

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
diff --git a/app/gear.py b/app/gear.py
2+
index b602dfe..ab92667 100644
3+
--- a/app/gear.py
4+
+++ b/app/gear.py
5+
@@ -27,6 +27,7 @@ def toOneList(l):
6+
return res
7+
8+
def addToGraphRunner(x):
9+
+ x = x['value']
10+
try:
11+
xlog('addToGraphRunner:', 'count=', x['count'])
12+
13+
@@ -54,7 +55,7 @@ def addToGraphRunner(x):
14+
animal = index[str(res.index(res1[0]) - 1)][1]
15+
xlog('addToGraphRunner:', 'animal=', animal)
16+
17+
- return (animal, x['img'])
18+
+ return animal, x['img']
19+
except:
20+
xlog('addToGraphRunner: error:', sys.exc_info()[0])
21+
22+
@@ -62,21 +63,24 @@ def addToStream(x):
23+
# save animal name into a new stream
24+
try:
25+
redisgears.executeCommand('xadd', 'cats', 'MAXLEN', '~', str(MAX_IMAGES), '*', 'image', 'data:image/jpeg;base64,' + base64.b64encode(x[1]).decode('utf8'))
26+
+ xlog('addToStream: ', x[0])
27+
except:
28+
xlog('addToStream: error:', sys.exc_info()[0])
29+
30+
def shouldTakeFrame(x):
31+
+ v = x['value']
32+
try:
33+
global framesToDrop
34+
framesToDrop += 1
35+
- xlog('shouldTakeFrame', x['count'], (framesToDrop % 10 == 0))
36+
+ xlog('shouldTakeFrame', v['count'], (framesToDrop % 10 == 0))
37+
return framesToDrop % 10 == 0
38+
except:
39+
xlog('shouldTakeFrame: error:', sys.exc_info()[0])
40+
41+
def passAll(x):
42+
+ v = x['value']
43+
try:
44+
- redisgears.executeCommand('xadd', 'all', 'MAXLEN', '~', str(MAX_IMAGES), '*', 'image', 'data:image/jpeg;base64,' + base64.b64encode(x['img']).decode('utf8'))
45+
+ redisgears.executeCommand('xadd', 'all', 'MAXLEN', '~', str(MAX_IMAGES), '*', 'image', 'data:image/jpeg;base64,' + base64.b64encode(v['img']).decode('utf8'))
46+
except:
47+
xlog('passAll: error:', sys.exc_info()[0])
48+
49+
@@ -87,4 +91,5 @@ gearsCtx('StreamReader').\
50+
map(addToGraphRunner).\
51+
filter(lambda x: 'cat' in x[0]).\
52+
foreach(addToStream).\
53+
- register('camera:0')
54+
+ register(prefix='camera:0')
55+
+
56+
diff --git a/app/init.py b/app/init.py
57+
index 09f77bc..64a1303 100644
58+
--- a/app/init.py
59+
+++ b/app/init.py
60+
@@ -27,7 +27,7 @@ if __name__ == '__main__':
61+
print('Loading model - ', end='')
62+
with open('models/mobilenet_v2_1.4_224_frozen.pb', 'rb') as f:
63+
model = f.read()
64+
- res = conn.execute_command('AI.MODELSET', 'mobilenet:model', 'TF', 'CPU', 'INPUTS', 'input', 'OUTPUTS', 'MobilenetV2/Predictions/Reshape_1', model)
65+
+ res = conn.execute_command('AI.MODELSET', 'mobilenet:model', 'TF', 'CPU', 'INPUTS', 'input', 'OUTPUTS', 'MobilenetV2/Predictions/Reshape_1', 'BLOB', model)
66+
print(res)
67+
68+
# Load the gear

camera/Dockerfile.jetson

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
FROM nvcr.io/nvidia/l4t-base:r32.5.0
2+
3+
# This is due on the following error on ARMv8:
4+
# /usr/lib/aarch64-linux-gnu/libgomp.so.1: cannot allocate memory in static TLS block
5+
# Something is exausting TLS, causing libgomp to fail. Preloading it as a workaround helps.
6+
# ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libgomp.so.1
7+
8+
ENV DEBIAN_FRONTEND=noninteractive
9+
ENV LD_LIBRARY_PATH=/usr/lib/aarch64-linux-gnu:$LD_LIBRARY_PATH
10+
ENV LD_PRELOAD /usr/lib/aarch64-linux-gnu/libgomp.so.1
11+
12+
RUN apt-get -qq update && apt-get upgrade -y
13+
14+
RUN set -x; \
15+
apt-get install -y curl patch; \
16+
curl https://bootstrap.pypa.io/get-pip.py -o /tmp/get-pip.py ;\
17+
python3 /tmp/get-pip.py
18+
19+
RUN pip install redis==3.2.1
20+
21+
WORKDIR /usr/src/app
22+
23+
ADD read_camera_jetson.py ./
24+
ADD *.jpg ./
25+
ADD build_opencv.patch ./
26+
27+
# build opencv from the source for Jetson Nano aarch64
28+
RUN wget https://raw.githubusercontent.com/mdegans/nano_build_opencv/301e95dd6c361ed29dc523a46483f05bafd7f70b/build_opencv.sh
29+
RUN patch build_opencv.sh build_opencv.patch
30+
RUN chmod +x build_opencv.sh
31+
RUN ./build_opencv.sh
32+
33+
CMD [ "python3", "./read_camera_jetson.py", "-u", "redis://redis:6379"]

camera/build_opencv.patch

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
diff --git a/build_opencv.sh b/build_opencv.sh
2+
index 486bca9..e3c22c1 100755
3+
--- a/build_opencv.sh
4+
+++ b/build_opencv.sh
5+
@@ -2,6 +2,7 @@
6+
# 2019 Michael de Gans
7+
8+
set -e
9+
+set -x
10+
11+
# change default constants here:
12+
readonly PREFIX=/usr/local # install prefix, (can be ~/.local for a user install)
13+
@@ -24,7 +25,7 @@ cleanup () {
14+
if ! [[ "$1" -eq "--test-warning" ]] ; then
15+
echo "(Doing so may make running tests on the build later impossible)"
16+
fi
17+
- read -p "Y/N " yn
18+
+ yn="Y"
19+
case ${yn} in
20+
[Yy]* ) rm -rf /tmp/build_opencv ; break;;
21+
[Nn]* ) exit ;;
22+
@@ -53,9 +54,9 @@ install_dependencies () {
23+
# open-cv has a lot of dependencies, but most can be found in the default
24+
# package repository or should already be installed (eg. CUDA).
25+
echo "Installing build dependencies."
26+
- sudo apt-get update
27+
- sudo apt-get dist-upgrade -y --autoremove
28+
- sudo apt-get install -y \
29+
+ apt-get update
30+
+ apt-get dist-upgrade -y --autoremove
31+
+ apt-get install -y \
32+
build-essential \
33+
cmake \
34+
git \
35+
@@ -104,7 +105,7 @@ install_dependencies () {
36+
configure () {
37+
local CMAKEFLAGS="
38+
-D BUILD_EXAMPLES=OFF
39+
- -D BUILD_opencv_python2=ON
40+
+ -D BUILD_opencv_python2=OFF
41+
-D BUILD_opencv_python3=ON
42+
-D CMAKE_BUILD_TYPE=RELEASE
43+
-D CMAKE_INSTALL_PREFIX=${PREFIX}
44+
@@ -114,13 +115,13 @@ configure () {
45+
-D CUDNN_VERSION='8.0'
46+
-D EIGEN_INCLUDE_PATH=/usr/include/eigen3
47+
-D ENABLE_NEON=ON
48+
- -D OPENCV_DNN_CUDA=ON
49+
+ -D OPENCV_DNN_CUDA=OFF
50+
-D OPENCV_ENABLE_NONFREE=ON
51+
-D OPENCV_EXTRA_MODULES_PATH=/tmp/build_opencv/opencv_contrib/modules
52+
-D OPENCV_GENERATE_PKGCONFIG=ON
53+
-D WITH_CUBLAS=ON
54+
- -D WITH_CUDA=ON
55+
- -D WITH_CUDNN=ON
56+
+ -D WITH_CUDA=OFF
57+
+ -D WITH_CUDNN=OFF
58+
-D WITH_GSTREAMER=ON
59+
-D WITH_LIBV4L=ON
60+
-D WITH_OPENGL=ON"
61+
@@ -175,7 +176,7 @@ main () {
62+
if [[ -w ${PREFIX} ]] ; then
63+
make install 2>&1 | tee -a install.log
64+
else
65+
- sudo make install 2>&1 | tee -a install.log
66+
+ make install 2>&1 | tee -a install.log
67+
fi
68+
69+
cleanup --test-warning

camera/read_camera_jetson.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import argparse
2+
import cv2
3+
import redis
4+
import time
5+
import sys
6+
import os
7+
import atexit
8+
9+
try:
10+
import urllib.parse
11+
except ImportError:
12+
import urllib.parse as urlparse
13+
14+
IMAGE_WIDTH = 640
15+
IMAGE_HEIGHT = 480
16+
17+
MAX_IMAGES = 1000 # 5
18+
19+
def gstreamer_pipeline(
20+
capture_width=IMAGE_WIDTH,
21+
capture_height=IMAGE_HEIGHT,
22+
display_width=IMAGE_WIDTH,
23+
display_height=IMAGE_HEIGHT,
24+
framerate=15,
25+
flip_method=0,
26+
):
27+
return (
28+
"nvarguscamerasrc ! "
29+
"video/x-raw(memory:NVMM), "
30+
"width=(int)%d, height=(int)%d, "
31+
"format=(string)NV12, framerate=(fraction)%d/1 ! "
32+
"nvvidconv flip-method=%d ! "
33+
"video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! "
34+
"videoconvert ! "
35+
"video/x-raw, format=(string)BGR ! appsink"
36+
% (
37+
capture_width,
38+
capture_height,
39+
framerate,
40+
flip_method,
41+
display_width,
42+
display_height,
43+
)
44+
)
45+
46+
47+
class Webcam:
48+
def __init__(self, infile=0, fps=15.0):
49+
if infile:
50+
self.cam = cv2.VideoCapture(infile)
51+
self.cam.set(cv2.CAP_PROP_FPS, fps)
52+
self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, IMAGE_WIDTH)
53+
self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, IMAGE_HEIGHT)
54+
else:
55+
self.cam = cv2.VideoCapture(gstreamer_pipeline(flip_method=0), cv2.CAP_GSTREAMER)
56+
atexit.register(self.cam.release)
57+
58+
59+
def __iter__(self):
60+
self.count = -1
61+
return self
62+
63+
def __next__(self): # Python 2.7
64+
self.count += 1
65+
66+
# Read image
67+
ret_val, img0 = self.cam.read()
68+
assert ret_val, 'Webcam Error'
69+
70+
# Preprocess
71+
# img = cv2.flip(img0, 1)
72+
img = img0
73+
74+
return self.count, img
75+
76+
def __len__(self):
77+
return 0
78+
79+
80+
if __name__ == '__main__':
81+
parser = argparse.ArgumentParser()
82+
parser.add_argument('infile', help='Input file (leave empty to use webcam)', nargs='?', type=str, default=None)
83+
parser.add_argument('-o', '--output', help='Output stream key name', type=str, default='camera:0')
84+
parser.add_argument('-u', '--url', help='Redis URL', type=str, default='redis://localhost:6379')
85+
parser.add_argument('--fmt', help='Frame storage format', type=str, default='.jpg')
86+
parser.add_argument('--fps', help='Frames per second (webcam)', type=float, default=15.0)
87+
parser.add_argument('--maxlen', help='Maximum length of output stream', type=int, default=1000)
88+
parser.add_argument('--test', help='transmit image instead of reading webcam', action="store_true")
89+
args = parser.parse_args()
90+
91+
# Set up Redis connection
92+
url = urllib.parse.urlparse(args.url)
93+
conn = redis.Redis(host=url.hostname, port=url.port)
94+
if not conn.ping():
95+
raise Exception('Redis unavailable')
96+
print('Connected to Redis')
97+
sys.stdout.flush()
98+
99+
if args.test is False:
100+
print('Operating in camera mode')
101+
sys.stdout.flush()
102+
if args.infile is None:
103+
loader = Webcam(infile=0, fps=args.fps)
104+
else:
105+
loader = Webcam(infile=int(args.infile), fps=args.fps)
106+
107+
for (count, img) in loader:
108+
_, data = cv2.imencode(args.fmt, img)
109+
msg = {
110+
'count': count,
111+
'image': data.tobytes()
112+
}
113+
_id = conn.execute_command('xadd', args.output, 'MAXLEN', '~', str(MAX_IMAGES), '*', 'count', msg['count'], 'img', msg['image'])
114+
print('count: {} id: {}'.format(count, _id))
115+
sys.stdout.flush()
116+
else:
117+
image_file = os.environ['ANIMAL'] + '.jpg'
118+
print('Operating in test mode with image ' + image_file)
119+
sys.stdout.flush()
120+
img0 = cv2.imread(image_file)
121+
img = cv2.resize(img0, (IMAGE_WIDTH, IMAGE_HEIGHT))
122+
_, data = cv2.imencode(args.fmt, img)
123+
count = 1
124+
while True:
125+
msg = {
126+
'count': count,
127+
'image': data.tobytes()
128+
}
129+
_id = conn.execute_command('xadd', args.output, 'MAXLEN', '~', str(MAX_IMAGES), '*', 'count', msg['count'], 'img', msg['image'])
130+
# print('count: {} rc: {} id: {}'.format(count, rc, _id))
131+
# sys.stdout.flush()
132+
count += 1
133+
time.sleep(0.1)

0 commit comments

Comments
 (0)