Try   HackMD

1. Cài đặt Deepstream 5.1 trên Ubuntu

1.1. Yêu cầu:

  • Máy có GPU của Nvdia.
  • Nên sử dụng Ubuntu 18.04
  • Cài đặt docker và docker-nvdia theo hướng dẫn link [1] và link[2] nếu sử dụng deepstream trên docker hoặc dùng triton chạy trên deepstream.

1.2. Các bước cài đặt:

Hướng dẫn cài đặt có trong link [3]

  • Cài đặt dependence như trong link [3:1].
  • Vào softwar & update trong ubuntu, trong phần Additional Driver để cài đặt driver nvidia 460.
  • Tải CUDA toolkit 11.1 như hướng dẫn trong link [3:2]. Cài đặt CUDA (chú ý cài đặt path) có thể tham khảo link [4].
  • Tải Cudnn 8.1.1 ở link [5]. Việc cài đặt có thể thao khảo link [4:1]
  • Cài đặt TensorRT 7.2.X theo hướng dẫn link [6], cài đặt bằng Debian Installation. Note: Nhớ đọc kĩ docs, đây là bước dễ lỗi nhất.
  • Cài đặt librdkafka như trong link [3:3].
  • Cài đặt Deepstream SDK dùng Method 1 trong link [3:4].

Note: Sau khi cài đặt xong thì các samples, configs, models sẽ nằm trong link /opt/nvidia/deepstream/deepstream-5.1/samples của máy. Sau này khi chạy các app test cần chỉnh lại đường dẫn cho phù hợp.

2. Hướng dẫn đọc tài liệu của Deepstream.

Link

2.1. Deepstream Getting Started

2.1.1. Welcome to the Deepstream Documentation

2.1.1.1. Nvidia Deepstream Overview

  • DS được phát triển dựa trên Gs-stream nên có rất nhiều thành phần được kế thừa từ nó. Nên đọc trước cơ bản về gs-stream để nắm cơ bản trước. Việc đọc phần nào của gs-stream sẽ được nhắc đến sau.
  • Hiểu được DS là gì: có thể hiểu đơn giản là tool giúp cho việc streaming và phân tích dữ liệu dựa trên các ứng dụng AI.
  • Hoạt động được trên những thiết bị nào: dGPU (discrete GPU), container, jetson,
  • Input là gì: USB/CSI camera, video, RTSP stream,
  • Ưu, nhược điểm của DS là gì: ưu điểm là nhanh, tận dụng tối đa phần cứng, được thiết kế thành một pipeline có sẵn. Nhược điểm là khó sử dụng, chỉ dùng được cho một vài task, khó ứng dụng cho các mô hình được tạo ra gần đây, độ chính xác không quá cao.
2.1.1.1.1. DeepStream Graph Architecture

Hiểu được pipeline của DS từ lúc input cho đến output. Hiểu được các Gst Elements của DS quan trọng sau:

  • Gst-nvvideoconvert: convert ảnh (video) từ định dạng nhận được từ source (vd camera) sang định dạng cần thiết cho việc sử lý hình ảnh.
  • Gst-nvstreammux: batch nhiều frames (gộp nhiều src) lại với nhau để tối ưu tốc độ xử lý.
  • Gst-nvinfer: sử dụng các model có sẵn trong DS cho các task detection, classification, segmentation. Nếu muốn dùng model bên ngoài thì sử dụng Gst-nvinferserver (sau khi đã convert model sang định dạng cần thiết).
  • Gst-nvtracker: được sử dụng sau task detection như là secondary task.
  • Gst-nvdsosd: osd mean on-creen display, dùng để điều chỉnh cho visualization.

Các Elements khác sẽ được đề cập trong các ví dụ.

2.1.1.1.2. DeepStream reference app

Xem hình hiểu được một ứng dụng cơ bản trong deepstream sẽ gồm các phần gì. Chủ yếu gồm 3 phần: Preprocess, AI-model (primiry, secondary), post-process.

2.1.1.1.3. Deepstream in Python

Xem hình để hiểu tại sao các app có thể chạy trên python (trong khi được viết bằng c++). Sử dụng python để tạo pipeline thông qua code trên gstream, và dùng để truy cập metadata để để lưu trữ thông tin, thay đổi config, xuất dữ liệu ra màn hình thông qua các hàm probe. Không thể dùng python để thay đổi metadata.

2.1.2. Quickstart Guide.

Xem setup cho dGPU và DS Triton Inference. (Đã note ở phần trước).

2.2. Deepstream sample

2.2.1. Python Sample Apps Source Details

  • Cài đặt Gst Python như hướng dẫn trong link.
  • Chú ý link [7] và link [8], trong phần Python Bindings.
  • Đọc sơ qua để hiểu sơ qua về cách truy cập metadata từ Python. Có thể khó để hiểu hết nhưng nên hiểu cơ bản để dễ đọc các phần phía sau.
  • Phần Sample Application Source Details mô tả sơ qua về các pipeline trong các test.
  • Trong quá trình xem các python-test-apps cần xem kết hợp link [7:1], link [8:1]Sample Application Source Details để hiểu hơn về code trong đó.

2.2.2. DeepStream Reference Application - deepstream-app

2.2.2.1. Application Architecture

Ôn lại pipeline của deepstream. Hiểu cơ bản về việc sử dụng cơ bản các Gs Elements tại mỗi phần của pipeline (Đã nói đến trong phần trước)

2.2.2.2. Configuration Groups

Note: Phần này khá là quan trọng trong việc hiểu việc thiết lập config của DS. Configs sẽ được chia ra nhiều group tương ứng với từng thành phần trong pipeline của DS.

2.2.2.2.1 Appication Group

Thiết lập chung cho toàn pipeline
Note: không dùng mấy.

2.2.2.2.2. Tiled-display Group

Thiết lập cấu hình để show output ra. Có tên tiled-display vì show ra nhiều output khác nhau (ứng với nhiều source khác nhau), và các output này được show ra theo thứ tự từ trái qua phải, trên xuống dưới (như hình dưới).

Các key quan trọng:

  • enable: có hiển thị hay không.
  • rows: số dòng
  • columns: số cột
  • with: rộng (pixel)
  • height: cao (pixel)
  • gpu_id: sử dụng gpu nào để hiển thị
2.2.2.2.3. Source group

Thiết lập cấu hình cho từng source (ví dụ input là nhều camera, nhiều video khác nhau).
Ít sử dụng để tạo config cho source theo cách này nhưng nên đọc qua để hiểu.

Các key quan trọng:

  • enable: có dùng source này hay không.
  • type: loại source là gì (camera, uri, multiurri, rtsp, csi camera)
  • uri: string
  • num-sources: số lượng source
  • gpu-id:
  • camera-id: thiết lập id cho cam
  • camera-width:
  • camera-height:
  • camera-fps-n: Numerator
  • camera-fps-d: Denominator
    fps = numerator / denominator
  • drop-frame-interval: Interval to drop frames
2.2.2.2.4. Streammux Group

Chỉnh config cho Gst-nvstreammux (elements để batch các sources lại với nhau). Config của group này sẽ sử dụng nên cần đọc kĩ.

Keys quan trọng:

  • gpu-id:
  • live-source: các source có live hay không.
  • batch-size: nên chỉnh size bằng với số lượng sources để tối ưu tốc độ xử lý.
  • width, height: thường không cần chỉnh.
  • config-path-file: đường dẫn tới fiel config (trong file đó có thể viết các thông số ở trên thay vì setup từng cái.)
2.2.2.2.5. Primary GIE and Secondary GIE Group

GIE: GPU Inference Engine
Chỉnh config cho GIE (dùng gst-nvinfer hoặc gst-nvinferserver) cho phần AI model: classification, detection, segmentation. Bao gồn cả Primary và Secondary (model theo sau primary).

Các key quan trọng:

  • enable: dùng hay không.
  • gie-unique-id: id cho model (từng model sẽ có id riếng để truy cập. Ví dụ Secondary cần sử dụng trên Primary với id tương ứng).
  • gpu-id: id GPU được sử dụng
  • model-engine-file: path file engine (sử dụng relative path vì absolute path đang bị lỗi). Thường không có sẵn và sẽ được tạo ra khi chạy model dựa vào file .etltlabels.txt.
  • batch-size: nên setup size bằng với số lượng sources để tối ưu tốc độ xử lý.
  • interval: number of skip frames.
  • operate-on-gie-id: id của primary mà Sencondary cần sử dụng lên. Ví dụng như mô hinh tracker (sencondary) sử dụng trên mô hình detection (primary) trước đó với id=1 chẳng hạn.
  • operate-on-class-ids: id classes của primary mà sencondary cần sử dụng lên. Cũng như ví dụng trên, ta có thể setup để mô hình tracker chỉ track lên people (class-id=0 chẳng hạn).
  • lablefile-path: path files .txt chứa các labels của mô hình
  • config-file: tất cả các thông số trên đều có thể được đặt trong file config và được defined dưới group [property] (sẽ rõ hơn khi app test).

Note: Nếu sử dụng element gst-nvinfer thì sẽ có thể các config được liệt kê trong link [9]. Có 2 nhóm config group trong này là [property] (ghi cùng với phần trên) và [class-attrs-...].

Nhóm config [property] tùy chỉnh config chung của gstream. Các key quan trọng:

  • num-detected-classes: số lượng classes của model.
  • net-scale-factor: chưa biết lấy đâu ra.
  • model-file: nếu có file .caffe (không dùng).
  • proto-file: pathname của file prototxt (chưa biết khi nào nên dùng).
  • int8-calib-file: (chưa biết khi nào nên dùng).
  • batch-size: g2.2.2.2.6. iống phần cho gie
  • model-engine-file: path file .engine. Thường sẽ không có sẵn và sẽ được tao ra khi chạy DS.
  • uff-file: khi có file .uff
  • labelfile-path: pathname của file labels.txt (bắt buộc)
  • gie-unique-id: như trên phần gie
  • operate-on-gie-id: như trên
  • operate-on-class-ids: như trên
  • interval: như trên
  • network-mode: fp32, int8, fp16 (trade-off between acc and speed).
  • output-blob-names: tên array của layer output của model. (không biết lấy ở đâu)
  • process-mode: 1:primary gie, 2:secondary gie.
  • classifier-threshold: ngưỡng cho output
  • network-type: detector, classifier, segmentation, instance segmentation. (Không hiểu sao trong file config lại không có.)
  • tlt-model-key: key for the TLT encoded model
  • tlt-encoded-model: Pathname of the Transfer Learning Toolkit (TLT) encoded model. (Các TLT này có sẵn, được cung cấp trong folder samples).

Nhóm config [class-attrs-...] tùy chỉnh config của từng class id. [class-attrs-all áp dụng cho tất cả các class id. Nhóm này áp dụng cho Detector. Các key quan trọng:

  • threshold: detection threshold
  • pre-cluster-threshold: chưa hiểu lắm
  • post-cluster-threshol: chưa hiểu lắm
  • eps: chưa hiểu lắm
  • nms-iou-threshold:
  • topk: chỉ giữ k giá trị cao nhất.
2.2.2.2.6. Tracker Group

Sử dụng để chỉnh config cho tracker (secondary task). Có 3 loại tracker có sẵn trong deepstream (sẽ nói đến ở phần sau). Các key quan trọng bao gồm:

  • enable:
  • tracker-width, tracker-height:
  • gpu-id:
  • ll-config-file: path for the low-level tracker configuration file
  • ll-lib-file: pathname for the low-level tracker implementation library.
  • display-tracking-id: enables trackings id display
2.2.2.2.7. Message Converter Group

Không quan trọng và ít dùng. Có thể xem qua.

2.2.2.2.8. Message Consumer Group

Không quan trọng và ít dùng. Có thể xem qua.

2.2.2.2.9. OSD Group

Tùy chỉnh các thông số để hiển thị ra màn hình (OSD: on-screen Display). Các key quan trọng:

  • enable:
  • gpu-id:
  • border-width, border-height:
  • text-size:
  • text-color:
  • font:
  • process-mode: sử dụng CPU hay GPU để hiển thị
2.2.2.2.10. Sink Group

Tùy chỉnh các thông số của sink conponents (rendering, encoding, saving). Các key quan trọng:

  • type: type of sink
  • sync:
  • qos: không hiểu lắm
  • source-id: source nào muốn được sink
  • gpu-id:
  • codec: H.264 hay H.265
  • output-file: vd /output.mp4
  • rtsp-port: port nếu muốn xuất ra dưới dạng rtsp
  • display-id:
2.2.2.2.11. Test Group

Không quan trọng

2.2.2.2.12. NvDs-analytics Group

Tủy chỉnh các thông số cho nvdsanalytics trong pipeline:

  • enable:
  • config-file: path. (có thể xem ví dụ file config trong các samples)

Important Note:

  • Tất cả các config phía trên có thể khai báo khi tạo gs-element hoặc đặt tất cả vào trong cùng một file config và nhập file config vào element đó.
  • Thông thường sẽ dùng file config cho các elements sau: nvinfer, nvinferserver, gie (primary and secondary), nvanalytics. Các elements còn lại nhập trực tiếp (vì không có nhiều thông số).
  • Xem thêm link [10] để biết tip chỉnh config sao cho DS chạy tối ưu.

2.3. Gstream Basic Tutorial

Phần này nên được xem trước khi bắt đầu phần tiếp theo vì DS được xây dựng trên Gstreamer. Cần nắm được các kiến thức cơ bản về cách tạo 1 pipeline bằng gstreamer, các thành phần trong gstreamer, cách nối các thành phần lại với nhau, cách setup properties cho các thành phần. Nên xem code python trước, nếu sau này cần phát triển sâu hơn trên C++ thì xem sau. Link tutorials

2.3.1. Tutorial 1: Hello world!

Đầu tiên nên xem qua đoạn code và đọc hiểu phần giải thích, chú ý các phần sau:

  • gi.require_version: khai báo cần thiết cho gstreamer.
  • Gst.init(sys.argv[1:]): khởi tạo gstreamer. Cần hiểu tạo sao cần khởi tạo.
  • pipeline.set_state: dùng để khai báo trạng thái, sử dụng cho việc bắt đầu và kết thúc pipeline.

2.3.2. Tutorial 2: Gstreamer concepts

  • Hiểu được các elements kết nối với nhau như thế nào.
  • Gst.ElementFactory: cách tạo các thành phần trong Gstreamer. Trong lệnh cần phải khai báo tên chính xác của element đó (string đầu) và tên mà chúng ta sẽ sử dụng sau naỳ để gọi lại element (string sau).
  • Gst.Pipeline: cách tạo một pipeline.
  • pipeline.add: cách thêm các elements (đã tạo phía trên) và pipeline mới được tạo.
  • Trong phần này các elements tự động link với nhau. Nhưng trong các test app, chúng ta phải link chúng lại thông qua lệnh element1.link(element2).

Important Note:

  • Các tutorial phía sau cũng có thể nên xem cho biết.
  • Khi cần tìm hiểu thêm về các phần khác trong Gstreamer thì có thể tham khảo ở link [11]. Trong quá trình đọc code test-app thì có thể cần phải tìm hiểu các phần sau của gstreamer: Elements, sink & src của elements, bin, pad,
  • Sau phần hiểu cơ bản về gstreamer này thì chúng ta quay lại documents của deepstream. Link. Các phần phía sau ít quan trọng nên chúng ta chuyển qua Plugins Development Guide.

2.4. DeepStream Plugin Guide

2.4.1. Metadata in Deepstream SDK

Metadata là data về thông tin đã được xử lý qua pipeline của DS (cụ thể ở đây là hình ảnh và video). Metadata được lưu bằng ngôn ngữ C++ ở tăng tối ưu về tốc độ; nếu dùng python thì phải sử dụng các hàm probe để lấy ra những data đó. Khi dùng python, chúng ta chỉ có thể lấy ra để lưu trữ, xử lý, và rất khó để thay đổi thông tin trong metadata. Metadata có tính kế thừa, ví dụ Batched metadata sẽ chứa list các Frame-Metadata. Tra cứu các thuộc tính của Metadata trong Python trong link [8:2].
Các Metadata cần tìm hiểu kĩ:

2.4.1.1. NvDsBatchMeta

Link
Các thuộc tính cần chú ý:

  • cast: overloaded function.
  • batch_user_meta_list: trả về danh sách các items NvDsUserMeta trong batch đó. UserMeta chủ yếu được dùng trong nvanalytics.
  • frame_meta_list: trả về danh sách các items NvDsFrameMeta trong batch đó. Trong batch thường chứa nhiều sources. FrameMeta sẽ tương ứng với từng khung hình tại một thời điểm ứng với các source khác nhau.

2.4.1.2. NvDsFrameMeta

Là metadata được chứa trong NvDsBatchMeta, chứa các thông tin của frame ứng với mỗi source.
Các thuộc tính cần chú ý:

  • batch-id:
  • cast:
  • frame_num: frame thứ mấy
  • source_id: thuộc source nào (camera hoặc video nào).
  • frame_user_meta_list: trả về list các object của NvDsUserMeta ứng với frame đó và source đó. Có thể dùng metadata để trích xuất thông tin trong nvanalytics (có bao nhiêu người trong khu vực đánh dấu sẵn, có bao nhiêu người đi theo hướng cho trước, có bao nhiêu người đi vào và đi ra, )
  • object_meta_list: trả về list các object của NvDsObjectMeta ứng với frame đó và source đó. Metadata này chưa thông tin về các object đã được detect (hoặc classified) trong frame đó. Có thể dùng để trích xuất các thông tin về tọa độ bbox, object-id, Các thuộc tính metadata này sẽ nhắc đến sau.
  • display_meta_list: trả về list các object của NvDsDisplayMeta ứng với frame đó và source đó. Có thể sử dụng metadata này để thay đổi chữ hiển thị trên frame đó cũng các thông số khác cho việc hiển thị như màu chữ, font, back ground, size, Các thuộc tính của metadata này sẽ được nhắc đến sau.

2.4.1.3. NvDsObjectMeta

Là metadata được chứa trong NvDsObjectMeta, chứa các thông tin về đối tượng trong từng frame ứng với từng source.
Các thuộc tính cần chú ý:

  • cast:
  • class_id: id của class mà object được detected/classified. Ví dụng thuộc class 0:people.
  • classifier_meta_list: chứa danh sách các NvDsClassifierMeta. Có thể dùng để trích suất thông tin về label của đối tượng. Sẽ được nhắc đến sau.
  • confidence: độ tin tưởng việc phân loại đối tượng.
  • obj_user_meta_list: trả về list các object của NvDsUserMeta ứng với object đó của frame đó và source đó. Có thể dùng metadata để trích xuất thông tin trong nvanalytics (đối tượng này có nằm trong khu vực được đánh dấu sẵn, đối tượng này có đi theo hướng được định trước hay không, đối tượng này có đang đi ra khỏi khu vực hay không, ). Chú ý sự khác nhau UserMeta chứa trong ObjectMeta và FrameMeta.
  • object_id: id for tracking object.
  • rect_params: chủ yếu chứa thông tin về bbox. Có xem thêm pyds.NvOSD_RectParams để tùy chỉnh các thông số.
  • text_params: dùng để thay đối text về đối tượng đó. Xem thêm pyds.NvOSD_TextParams để tùy chỉnh các thông số.
  • tracker-confidence:

2.4.1.4. NvDsClassifierMeta

Lưu trữ thông tin về classifer metadata của đối tượng được nhắc đến ở NvDsObjectMeta.
Các thuộc tính cần chú ý:

  • cast:
  • label_info_list:

2.4.1.5. NvDsDisplayMeta

Lưu trữ thông tin về display metadata của frame đó và source đó.
Các thuộc tính cần chú ý:

  • cast:
  • text_params: dùng để thay đối text của frame đó. Xem thêm pyds.NvOSD_TextParams để tùy chỉnh các thông số.

Note: Còn nhiều loại metadata khác nhưng ít sử dụng, có thể tham khảo các loại khác trong link [8:3]

2.4.1.6 Methods

Các methods được sử dụng để thêm, xóa, thay đổi, trích xuất từ metadata. Chỉ có một vài methods được thường xuyên xử dụng trong app-test. Các methods sẽ được nói kĩ hơn khi đi vào các ví dụ

2.4.2. Gst-nvinfer

Cần hiểu được input nhận vào là gì, output là gì, các thông số quan trọng cần điều chỉnh là gì. Các config đã được đề cập phía trên.

2.4.3. Gst-nvinferserver

Nội dung cần đọc hiểu như trên.

2.4.4. Gst-nvtracker

Nội dung cần đọc hiểu như trên.

2.4.5. Gst-streammux

Nội dung cần đọc hiểu như trên.

2.4.6. Gst-nvmultistreamtiler

Nội dung cần đọc hiểu như trên.

2.4.7. Gst-nvdsanalytics

Nội dung cần đọc hiểu như trên.

Note: Các Elements ít khi sử dụng nhưng cũng nên đọc qua cho biết. Ở đây chỉ liệt kê các Elements mà DS tạo ra dựa trên gstreamer, còn có những elements của gstreamer được sử dụng và được viết documents trên trang chính của gstreamer.

3. Hướng dẫn đọc hiểu apps deepstream python.

3.1. Deepstream_test_1.py

Yêu cầu:

  • Cài đặt deepstream theo hướng dẫn bên trên. Tất cả các file video, config, model dùng trong các deepstream-test đều có trong path /opt/nvidia/deepstream/deepstream/samples.
  • git clone: https://github.com/NVIDIA-AI-IOT/deepstream_python_apps
  • Cài đặt theo hướng dẫn trong link.
  • Xem giới thiệu về deepstream-test-1 trong link
  • code ví dụ chạy trên commandline.
python3 deepstream_test_1.py /home/phu/Documents/deepstream_python_apps/videos/sample_qHD.h264 
  • Ý tưởng cơ bản: chúng ta sẽ tạo ra 1 pipeline, tạo ra các elements và thêm chúng vào pipeline. Sau đó kết nối các elements lại với nhau. Sau đó thiết lập các attributes cho các elements. Dùng hàm probe để lấy dữ buffer metadata ra để xử lý. Cuối cùng là thiết lập các trạng thái của pipeline và chạy chương trình.

Hướng dẫn sẽ đi chi tiết từng dòng lệnh, từng class để hiểu được một cách rõ nhất.

  • Dòng 25-33
    • Import các thành phần của gstreamer (GObject, Gst). Import các hà is_aarch_64 để kiểm tra cấu trúc máy tính, bus_call để tạo bus message cho pipeline gstreamer.
    • Import pyds để có thể sử dụng được những lệnh truy cập metadata khi dùng python.
import sys sys.path.append('../') import gi gi.require_version('Gst', '1.0') from gi.repository import GObject, Gst from common.is_aarch_64 import is_aarch64 from common.bus_call import bus_call import pyds

Chúng ta sẽ xem hàm main trước.

  • Dòng 129:
    • hàm main để chạy chương trình nhận args làm argument. args này là một list các arg nhận được khi chạy command line. Trong cođe ví dụ trên thì args[0] là tên app (deepstream_test_1.py), args[1] là path tới video (.h264).
def main(args):
  • Dòng 131-133:
    • Kiểm tra xem command line có nhận vô đúng 2 input hay không, nếu không thì dừng chương trình.
if len(args) != 2: sys.stderr.write("usage: %s <media file or uri>\n" % args[0]) sys.exit(1)
  • Dòng 136-137:
    • Khởi tạo Gstreamer. Điều này là bắt buộc khi làm việc với gstreamer, nếu không khởi tạo sẽ bị lỗi.
GObject.threads_init() Gst.init(None)
  • Dòng 141-145:
    • Khởi tạo pipeline ban đầu để ghép các elements và kết nối chúng lại với nhau.
print("Creating Pipeline \n ") pipeline = Gst.Pipeline() if not pipeline: sys.stderr.write(" Unable to create Pipeline \n")
  • Dòng 148-151
    • Khởi tạo thành phần đầu tiên filesrc và gán là source. Dùng để đọc file video từ input file.
print("Creating Source \n ") source = Gst.ElementFactory.make("filesrc", "file-source") if not source: sys.stderr.write(" Unable to create Source \n")
  • Dòng 155-158
    • Khởi tạo thành phần h264parse và gán là h264parser. Vì input của chúng ta là file video .h264 nên cần phần này để hiểu được input đó.
print("Creating H264Parser \n") h264parser = Gst.ElementFactory.make("h264parse", "h264-parser") if not h264parser: sys.stderr.write(" Unable to create h264 parser \n")
  • Dòng 161-164
    • Khởi tạo thành phần nvv4l2decoder và gán là decoder. Dùng để decode để có thể dùng trên GPU, tăng khả năng xử lý.
print("Creating Decoder \n") decoder = Gst.ElementFactory.make("nvv4l2decoder", "nvv4l2-decoder") if not decoder: sys.stderr.write(" Unable to create Nvv4l2 Decoder \n")
  • Dòng 167-169
    • Khởi tạo thành phần nvstreammux và gán là streammux. Dùng để batch các sources lại với nhau. Trong test-1 thì chỉ có 1 source duy nhất (1 video .h264)
streammux = Gst.ElementFactory.make("nvstreammux", "Stream-muxer") if not streammux: sys.stderr.write(" Unable to create NvStreamMux \n")
  • Dòng 173-175
    • Khởi tạo thành phần nvinfer và gán là pgie. Tạo element cho primary gie (detector).
pgie = Gst.ElementFactory.make("nvinfer", "primary-inference") if not pgie: sys.stderr.write(" Unable to create pgie \n")
  • Dòng 178-180
    • Khởi tạo thành phần nvvideoconvert và gán là nvvidconv. Dùng để chuyển format từ NV12 (output của nvinfer) sang RGBA (input của nvvidconv).
nvvidconv = Gst.ElementFactory.make("nvvideoconvert", "convertor") if not nvvidconv: sys.stderr.write(" Unable to create nvvidconv \n")
  • Dòng 183-186
    • Khởi tạo thành phần nvdsosd và gán là nvosd. Dùng để tùy chỉnh những thứ sẽ hiện ra ngoài màn hình như là các dòng text, Phần sau sẽ dùng element này thông qua hàm osd_sink_pad_buffer_probe để thực hiện các tùy chỉnh cũng với chữ cần được ghi lên trên bức ảnh.
nvosd = Gst.ElementFactory.make("nvdsosd", "onscreendisplay") if not nvosd: sys.stderr.write(" Unable to create nvosd \n")
  • Dòng 188-195
    • Khởi tạo thành phần nvegltransform và gán là transform. Nếu PC (hoặc laptop hoặc server là x86_64) thì cần khởi tạo thành phần này.
    • Khởi tạo thành phần nveglglessink và gán là sink. Element này dùng để render output.
# Finally render the osd output if is_aarch64(): transform = Gst.ElementFactory.make("nvegltransform", "nvegl-transform") print("Creating EGLSink \n") sink = Gst.ElementFactory.make("nveglglessink", "nvvideo-renderer") if not sink: sys.stderr.write(" Unable to create egl sink \n")
  • Dòng 197-198
    • setup trực tiếp config cho source, cụ thể ở đây là source nhận vô đường dẫn tới video cần được xử lý.
print("Playing file %s " %args[1]) source.set_property('location', args[1])
  • Dòng 199-202
    • setup trực tiếp config cho streammux. Các config còn lại đã được nói rõ ở phần trên.
streammux.set_property('width', 1920) streammux.set_property('height', 1080) streammux.set_property('batch-size', 1) streammux.set_property('batched-push-timeout', 4000000)
  • Dòng 203
    • setup config cho pgie sử dụng file config. Các config đã được nói rõ ở phần trên.
pgie.set_property('config-file-path', "dstest1_pgie_config.txt")

Tiếp đến chúng ta sẽ thêm các elements vào trong pipeline.

  • Dòng 205-215
    • Thêm lần lượt các element vào trong pipeline thông qua lệnh pipeline.add(element)
print("Adding elements to Pipeline \n") pipeline.add(source) pipeline.add(h264parser) pipeline.add(decoder) pipeline.add(streammux) pipeline.add(pgie) pipeline.add(nvvidconv) pipeline.add(nvosd) pipeline.add(sink) if is_aarch64(): pipeline.add(transform)
  • Dòng 220-238
    • Liên kết các element lại với nhau để tạo thành một pipeline. Phần từ trước sẽ liên kết với nhau qua lệnh ele1.link(ele2).
    • Phần liên kết giữa streammux và decoder hơi đăc biệt hơn một chút. Chúng ta sẽ lấy srcpad của streamux (thông qua lệnh get_request_pad) nối với sinkpad của decoder (thông qua lệnh get_static_pad). Sourcepad và sinkpad là gì thì có thể tham khảo ở link [11:1]. Còn tại sao không liên kết như các element khác thì chưa rõ.
print("Linking elements in the Pipeline \n") source.link(h264parser) h264parser.link(decoder) sinkpad = streammux.get_request_pad("sink_0") if not sinkpad: sys.stderr.write(" Unable to get the sink pad of streammux \n") srcpad = decoder.get_static_pad("src") if not srcpad: sys.stderr.write(" Unable to get source pad of decoder \n") srcpad.link(sinkpad) streammux.link(pgie) pgie.link(nvvidconv) nvvidconv.link(nvosd) if is_aarch64(): nvosd.link(transform) transform.link(sink) else: nvosd.link(sink)
  • Dòng 241-244
    • Create an event loop and feed gstreamer bus mesages to it. Tạo vòng lập để chạy gstreammer.
# create an event loop and feed gstreamer bus mesages to it loop = GObject.MainLoop() bus = pipeline.get_bus() bus.add_signal_watch() bus.connect ("message", bus_call, loop)
  • Dòng 249-253
    • Khi dùng python, muốn truy cập được metadata thì ta cần dùng một hàm probe, cụ thể ở đây là hàm osd_sink_pad_buffer_probe. Chúng ta sẽ tìm hiểu hàm này trong phần kế tiếp.
    • Đầu tiên cần lấy ra sinkpad của nvosd.
    • Sau đó dùng lệnh add_probe để trả về probe đang pending. Để hiểu rõ hơn về lệnh này cho thể tham khảo link [12].
osdsinkpad = nvosd.get_static_pad("sink") if not osdsinkpad: sys.stderr.write(" Unable to get sink pad of nvosd \n") osdsinkpad.add_probe(Gst.PadProbeType.BUFFER, osd_sink_pad_buffer_probe, 0)
  • Dòng 256-263
    • Start play back and listen to events.
    • State.PLAYING để chạy pipeline.
    • State.NULL để kết thúc và clean up.
# start play back and listen to events print("Starting pipeline \n") pipeline.set_state(Gst.State.PLAYING) try: loop.run() except: pass # cleanup pipeline.set_state(Gst.State.NULL)

Chúng ta tìm hiểu về hàm osd_sink_pad_buffer_probe:

  • Dòng 41-50
    • Dùng để pending probe. Các arguments có thể thao khảo link [13]
    • Khởi tạo frame_number và num_rects bắt đầu từ 0.
    • Khởi tạo dictionary đếm các object.
def osd_sink_pad_buffer_probe(pad,info,u_data): frame_number=0 #Intiallizing object counter with 0. obj_counter = { PGIE_CLASS_ID_VEHICLE:0, PGIE_CLASS_ID_PERSON:0, PGIE_CLASS_ID_BICYCLE:0, PGIE_CLASS_ID_ROADSIGN:0 } num_rects=0
  • Dòng 52-60
    • Dùng lệnh pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer)) để nhận được Batch-Metadata. Batch metadata chứa những thông tin gì thì đã được đề cập phía trên.
gst_buffer = info.get_buffer() if not gst_buffer: print("Unable to get GstBuffer ") return # Retrieve batch metadata from the gst_buffer # Note that pyds.gst_buffer_get_nvds_batch_meta() expects the # C address of gst_buffer as input, which is obtained with hash(gst_buffer) batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer)) l_frame = batch_meta.frame_meta_list
  • Dòng 61
    • Lấy frame metadata list từ batch metadata. Frame metadata chứa những thông tin gì thì đã được đề cập phía trên.
l_frame = batch_meta.frame_meta_list
  • Dòng 62-76
    • Chạy vòng lập qua các frame trong batch
    • Lấy ra các thông tin của frame đó như frame_num (frame thứ mấy), num_obj_meta (số lượng object được detect trong frame đó), l_obj (truy xuất ra object metadata). Object meta data chứa những thông tin gì đã được đề cập ở trên.
while l_frame is not None: try: # Note that l_frame.data needs a cast to pyds.NvDsFrameMeta # The casting is done by pyds.glist_get_nvds_frame_meta() # The casting also keeps ownership of the underlying memory # in the C code, so the Python garbage collector will leave # it alone. #frame_meta = pyds.glist_get_nvds_frame_meta(l_frame.data) frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data) except StopIteration: break frame_number=frame_meta.frame_num num_rects = frame_meta.num_obj_meta l_obj=frame_meta.obj_meta_list
  • Dòng 77-89
    • Chạy vòng lập qua các object trong frame
    • Lấy ra các thông tin của frame đó như class_id, rect_params (nhận về NvOSD_RectParam, chứa thông tin gì thì đã được nói tới ở trên).
while l_obj is not None: try: # Casting l_obj.data to pyds.NvDsObjectMeta #obj_meta=pyds.glist_get_nvds_object_meta(l_obj.data) obj_meta=pyds.NvDsObjectMeta.cast(l_obj.data) except StopIteration: break obj_counter[obj_meta.class_id] += 1 obj_meta.rect_params.border_color.set(0.0, 0.0, 1.0, 0.0) try: l_obj=l_obj.next except StopIteration: break
  • Dòng 94-95
    • Sử dụng lệnh nvds_acquire_display_meta_from_pool để nhận được NvDsDisplayMeta từ NvDsBatchMeta.
    • Set up số chuối hiển thị hiển thị trong display meta. Ở đây ta cần hiển thị 1 dòng nên num_labels = 1.
display_meta=pyds.nvds_acquire_display_meta_from_pool(batch_meta) display_meta.num_labels = 1
  • Dòng 96-120
    • Lẩy ra NvOSD_TextParams từ display meta để tùy chỉnh text.
    • Tùy chỉnh nội dung text thông qua .display_text.
    • Xem thêm NvOSD_TextParams để hiểu các setup còn lại.
py_nvosd_text_params = display_meta.text_params[0] # Setting display text to be shown on screen # Note that the pyds module allocates a buffer for the string, and the # memory will not be claimed by the garbage collector. # Reading the display_text field here will return the C address of the # allocated string. Use pyds.get_string() to get the string content. py_nvosd_text_params.display_text = "Frame Number={} Number of Objects={} Vehicle_count={} Person_count={}".format(frame_number, num_rects, obj_counter[PGIE_CLASS_ID_VEHICLE], obj_counter[PGIE_CLASS_ID_PERSON]) # Now set the offsets where the string should appear py_nvosd_text_params.x_offset = 10 py_nvosd_text_params.y_offset = 12 # Font , font-color and font-size py_nvosd_text_params.font_params.font_name = "Serif" py_nvosd_text_params.font_params.font_size = 10 # set(red, green, blue, alpha); set to White py_nvosd_text_params.font_params.font_color.set(1.0, 1.0, 1.0, 1.0) # Text background color py_nvosd_text_params.set_bg_clr = 1 # set(red, green, blue, alpha); set to Black py_nvosd_text_params.text_bg_clr.set(0.0, 0.0, 0.0, 1.0) # Using pyds.get_string() to get display_text as string print(pyds.get_string(py_nvosd_text_params.display_text)) pyds.nvds_add_display_meta_to_frame(frame_meta, display_meta)
  • Dòng 121-124
    • Truy cập vào frame tiếp theo
    • Trả về trạng thái của PadProbe.
try: l_frame=l_frame.next except StopIteration: break return Gst.PadProbeReturn.OK

3.2. Deepstream_test_3.py

Các bước cài đặt và đọc hiểu phần lớn là giống deepstream_test_1.py. Chỉ cần chú ý thêm các chi tiết sau:

  • Các hàm cb_newpad, decodebin_child_added, create_source_bin dùng để nối nhiều sources vào pipeline (khác với test_1 chỉ có 1 source).
  • Các queue được dùng xen kẽ trong pipeline để tránh hiện tượng bị nghẽn khi chạy deepstream.

3.3. Deepstream_nvdsanalytics.py

Các bước cài đặt và đọc hiểu phần lớn là giống deepstream_test_1.py. Chỉ cần chú ý thêm các chi tiết sau:

  • Element nvtracker dùng để tracker đối tượng được detect (secondary task). nvtracker lấy config từ file config. Cách chỉnh config đã được đề cập ở trên.
  • Element nvdsanalytics để cho việc phân tích dữ liệu, đã được đề cập ở trên. nvdsanalytics lấy config từ file config. Cách chỉnh config đã được đề cập ở trên.
  • Những dữ liệu được phân tích trong sample này: roi-filtering-stream, overcrowding-strean, line-crossing-stream, direction-detection-stream.

3.4. PPE

Thay vì viết toàn bộ source code trong một file python thì source ppe chia nhỏ nó ra. Phần hiểu code tương tự như trong các samples.

  • main.py: Ghép các pipeline lại với nhau (trong trường này chỉ có 1 pipeline), và có các hàm để bắt đầu, kết thúc pipeline.

    • class MainLoop:
      • add_pipeline: thêm pipeline vào. Trong source này chỉ thêm một pipeline.
      • start: Bắt đầu chạy tất cả các pipeline.
      • stop: Kết thúc chạy tất cả các pipeline.
    • def main: chạy trương trình.
  • pipelines.py: Sử dụng để tạo pipeline cho task PPE

    • class BasePipeline:
      • __init__: khởi tạo pipeline và thực hiện tạo elements, thêm elements vào pipeline, chỉnh config cho các elements, link các elements, thêm probe để lấy dữ liệu.
      • __init_elements: khởi tạo các elements cho pipeline ppe
      • __custom_configs: setup trực tiếp các config cho các elemets. Các setup thông qua file config (HumanDetector, Classifier, Tracker, nvdsanalytics) được thiết lập ở các file python khác (sẽ được nhắc đến sau).
      • __link_elements: link các elements lại với nhau.
      • __add_probe: add probe cho pipeline để có thể truy xuất dữ liệu từ pipeline.
      • get_bus: thêm bus vào pipeline
      • ready: giai đoạn chờ ban đầu để các pipeline có thể bắt đầu cùng một lúc.
      • start: bắt đầu pipeline
      • stop: dừng pipeline
  • detectors.py: Tạo elements nvinfer, nvinferserver (classifier) và chỉnh config cho nó thông qua 2 class HumanDetectorClassifier.

  • trackers.py: Tạo elements intracker và chỉnh config cho nó thông qua class KltTracker.

  • uri_utils.py: nối các sources vào pipeline (giống như deepstream-test-3)

  • gst_utils.py: utils giúp tạo các elements

    • create_gst_element: tạo element trong gstreamer.
    • create_pipeline: tạo pipeline trong gstreamer.
    • class MyStreammux: tạo nvstreamer và lấy ra thông qua lệnh get.
    • class Pipieline: khởi tạo pipeline, add các elements, gán bus cho pipeline và set state cho pipeline.
    • class MyAnalytic: tạo element nvdsanalytics, setup config thông qua file config.
  • probe_utils.py: sử dụng probe để lấy thông tin trong meta ra.

Note:

  • Nên đọc hết tất cả các samples còn lại trước khi đọc source PPE.
  • Nên đọc 4 samples (deepstream-test-[1,2,3,4]) trước rồi đọc các samples còn lại sẽ dễ hơn.
  • Trong quá trịnh đọc code, có thể tham khảo thêm các link sau [14][15][16][17][18][19][20][21][22][23].

Reference


  1. Install docker ↩︎

  2. Install nvidia-docker ↩︎

  3. Install deepstream guide ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  4. Install tensorflow pyimage ↩︎ ↩︎

  5. Download Cudnn ↩︎

  6. Install Tensorrt 7.2.3 documents ↩︎

  7. Git deepstream python apps ↩︎ ↩︎

  8. Deepstream Python API Reference ↩︎ ↩︎ ↩︎ ↩︎

  9. Gst-nvinfer ↩︎

  10. Performance Optimization ↩︎

  11. Elements gstreamer ↩︎ ↩︎

  12. Gst pad ↩︎

  13. GstPadCallback ↩︎

  14. Get_request_pad ↩︎

  15. h264parse ↩︎

  16. Accelerated Gstreamer ↩︎

  17. batch-push-timeout ↩︎

  18. Handy elements ↩︎

  19. queue element ↩︎

  20. Rtsp output ↩︎

  21. Different file formats ↩︎

  22. Deploy model on Triton ↩︎

  23. Tensorflow saved model to .graphdef ↩︎