Skip to content

Commit

Permalink
#43 add opencv module "savant"
Browse files Browse the repository at this point in the history
  • Loading branch information
tomskikh committed Jan 12, 2023
1 parent d94b612 commit 252155b
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ build-pyds:
--build-arg BASE_IMAGE=$(PYDS_BASE_IMAGE) \
--build-arg PIP_PLATFORM=$(PYDS_PIP_PLATFORM) \
--build-arg PYDS_TAG=v$(PYDS_VERSION)-unmap \
--progress plain \
-f docker/Dockerfile.pyds \
-t savant-pyds$(PLATFORM_SUFFIX):$(SAVANT_VERSION)-$(DEEPSTREAM_VERSION) .
mkdir -p libs/wheels
Expand All @@ -45,6 +46,7 @@ build-pyds:
build: build-pyds
DOCKER_BUILDKIT=1 docker build \
--target base \
--progress plain \
--build-arg DEEPSTREAM_VERSION=$(DEEPSTREAM_VERSION) \
-f docker/$(DOCKER_FILE) \
-t savant-deepstream$(PLATFORM_SUFFIX):$(SAVANT_VERSION)-$(DEEPSTREAM_VERSION)-base .
Expand Down
38 changes: 38 additions & 0 deletions docker/Dockerfile.deepstream
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,43 @@ RUN python setup.py bdist_wheel && \
rm -rf _skbuild


FROM nvcr.io/nvidia/deepstream:$DEEPSTREAM_VERSION-devel AS opencv_build

# python to python3 by default
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 10

WORKDIR /opencv
RUN git clone --branch 4.6.0 --depth 1 https://github.com/opencv/opencv
RUN git clone --branch 4.6.0 --depth 1 https://github.com/opencv/opencv_contrib
WORKDIR /opencv/build

RUN pip install --no-cache-dir 'numpy~=1.22.4'

COPY libs/opencv/savant /opencv/opencv_contrib/modules/savant
RUN --mount=type=cache,target=/tmp/opencv-cache \
cmake \
-D CMAKE_BUILD_TYPE=RELEASE \
-D OPENCV_EXTRA_MODULES_PATH=/opencv/opencv_contrib/modules \
-D CMAKE_INSTALL_PREFIX=/opencv/dist \
-D OPENCV_DOWNLOAD_PATH=/tmp/opencv-cache \
-D PYTHON_DEFAULT_EXECUTABLE=$(which python3) \
-D OPENCV_PYTHON_INSTALL_PATH=lib/python3.8/dist-packages \
-D OPENCV_FORCE_PYTHON_LIBS=ON \
-D BUILD_LIST=core,python3,cudev,cudaimgproc,savant \
-D BUILD_opencv_apps=OFF \
-D BUILD_DOCS=OFF \
-D BUILD_EXAMPLES=OFF \
-D BUILD_JAVA=OFF \
-D BUILD_PERF_TESTS=OFF \
-D BUILD_SHARED_LIBS=ON \
-D WITH_CUDA=ON \
-D WITH_FFMPEG=OFF \
-D WITH_GSTREAMER=ON \
/opencv/opencv

RUN make VERBOSE=1 -j12 install


FROM nvcr.io/nvidia/deepstream:$DEEPSTREAM_VERSION-devel AS savantboost_build

# Newer cmake ver. needed for "FindCUDAToolkit"
Expand Down Expand Up @@ -69,6 +106,7 @@ RUN python -m pip install --no-cache-dir \
&& rm requirements-base.txt \
&& rm requirements-deepstream.txt

COPY --from=opencv_build /opencv/dist /usr/local
COPY --from=pygstsavantframemeta_build /libs/gstsavantframemeta/dist /libs/gstsavantframemeta/dist
RUN python -m pip install --no-cache-dir /libs/gstsavantframemeta/dist/*.whl

Expand Down
238 changes: 238 additions & 0 deletions gpumat/dsapp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
#!/usr/bin/env python3
import platform
import sys

sys.path.append('../../')

import cv2
import gi

gi.require_version('Gst', '1.0')
from gi.repository import GLib, Gst
import pyds


def is_aarch64():
return platform.uname()[4] == 'aarch64'


def bus_call(bus, message, loop):
t = message.type
if t == Gst.MessageType.EOS:
sys.stdout.write("End-of-stream\n")
loop.quit()
elif t == Gst.MessageType.WARNING:
err, debug = message.parse_warning()
sys.stderr.write("Warning: %s: %s\n" % (err, debug))
elif t == Gst.MessageType.ERROR:
err, debug = message.parse_error()
sys.stderr.write("Error: %s: %s\n" % (err, debug))
loop.quit()
return True


def log_nvds_buf_surface(element_name: str, buffer: Gst.Buffer):
# mapped, map_info = buffer.map(Gst.MapFlags.READ)
# assert mapped, f'failed to map buffer {buffer.pts}'
# nvbufsurface_p = ctypes.cast(map_info.data, ctypes.c_void_p)
# nvbufsurface = pyds.NvBufSurface.cast(nvbufsurface_p.value)
print()
print('log_nvds_buf_surface')
print()
gpu_mat = cv2.savant.createGpuMat(hash(buffer), 0)
print(gpu_mat)
print(gpu_mat.size())
print(gpu_mat.channels())

gpu_mat.rowRange(100, 400).colRange(300, 308).setTo((0, 255, 255, 255))
gpu_mat.rowRange(100, 400).colRange(792, 800).setTo((0, 255, 255, 255))
gpu_mat.rowRange(100, 108).colRange(300, 800).setTo((0, 255, 255, 255))
gpu_mat.rowRange(392, 400).colRange(300, 800).setTo((0, 255, 255, 255))


def pad_buffer_probe(pad: Gst.Pad, info: Gst.PadProbeInfo):
gst_buffer: Gst.Buffer = info.get_buffer()
log_nvds_buf_surface(pad.get_parent().get_name(), gst_buffer)
# batch_meta = pyds.gst_buffer_get_nvds_batch_meta(hash(gst_buffer))
# l_frame = batch_meta.frame_meta_list
# while l_frame is not None:
# try:
# frame_meta = pyds.NvDsFrameMeta.cast(l_frame.data)
# except StopIteration:
# break
#
# n_frame: np.ndarray = pyds.get_nvds_buf_surface(
# hash(gst_buffer), frame_meta.batch_id
# )
#
# cv2.rectangle(n_frame, (150, 50), (600, 300), (0, 255, 0, 0), 4)
# frame_bytes = n_frame.tobytes()
# print(
# f'{pad.get_parent().get_name()} | #{frame_meta.frame_num}'
# f' resolution: {n_frame.shape}'
# f' size: {len(frame_bytes)} bytes'
# )
#
# del n_frame
# del frame_bytes
#
# pyds.unmap_nvds_buf_surface(hash(gst_buffer), frame_meta.batch_id)
#
# try:
# l_frame = l_frame.next
# except StopIteration:
# break

return Gst.PadProbeReturn.OK


def main(args):
if len(args) > 1:
n_frames = int(args[0])
else:
n_frames = 1

Gst.init(None)

print("Creating Pipeline")
pipeline = Gst.Pipeline()
is_live = False

print("Creating streammux")
streammux = Gst.ElementFactory.make("nvstreammux", "streammux")
pipeline.add(streammux)

print("Creating source")
source = Gst.ElementFactory.make("videotestsrc", "source")
pipeline.add(source)

print("Creating source converter")
source_converter = Gst.ElementFactory.make("nvvideoconvert", "source-converter")
pipeline.add(source_converter)

print("Creating source capsfilter")
source_capsfilter = Gst.ElementFactory.make("capsfilter", "source-capsfilter")
pipeline.add(source_capsfilter)

print("Creating workload")
workload = Gst.ElementFactory.make("identity", "workload")
pipeline.add(workload)

print("Creating streamdemux")
streamdemux = Gst.ElementFactory.make("nvstreamdemux", "streamdemux")
pipeline.add(streamdemux)

print("Creating queue")
queue = Gst.ElementFactory.make("queue", "queue")
pipeline.add(queue)

print("Creating converter")
converter = Gst.ElementFactory.make("nvvideoconvert", "converter")
pipeline.add(converter)

print("Creating sink_capsfilter")
sink_capsfilter = Gst.ElementFactory.make("capsfilter", "sink_capsfilter")
pipeline.add(sink_capsfilter)

print("Creating encoder")
encoder = Gst.ElementFactory.make("nvv4l2h264enc", "encoder")
pipeline.add(encoder)

print("Creating parser")
parser = Gst.ElementFactory.make("h264parse", "parser")
pipeline.add(parser)

print("Creating sink")
sink = Gst.ElementFactory.make("filesink", "sink")
# sink = Gst.ElementFactory.make("fakesink", "sink")
pipeline.add(sink)

source.set_property('num-buffers', n_frames)

if is_live:
streammux.set_property('live-source', 1)
streammux.set_property('width', 1280)
streammux.set_property('height', 720)
streammux.set_property('batch-size', 1)
streammux.set_property('batched-push-timeout', 4000000)

sink.set_property("sync", 0)
sink.set_property("qos", 0)
sink.set_property("enable-last-sample", 0)
sink.set_property("location", "result.h264")

if not is_aarch64():
nv_buf_memory_type = int(pyds.NVBUF_MEM_CUDA_UNIFIED)
source_converter.set_property("nvbuf-memory-type", nv_buf_memory_type)
streammux.set_property("nvbuf-memory-type", nv_buf_memory_type)
converter.set_property("nvbuf-memory-type", nv_buf_memory_type)

source_capsfilter.set_property(
'caps',
Gst.Caps.from_string(
'video/x-raw(memory:NVMM), format=RGBA, width=1280, height=720'
),
)
sink_capsfilter.set_property(
'caps',
Gst.Caps.from_string(
'video/x-raw(memory:NVMM), format=RGBA, width=480, height=480'
),
)

print("Linking elements in the Pipeline")

assert source.link(source_converter)
assert source_converter.link(source_capsfilter)

assert (
source_capsfilter.get_static_pad('src').link(
streammux.get_request_pad('sink_0')
)
== Gst.PadLinkReturn.OK
)

assert streammux.link(workload)
assert workload.link(streamdemux)

streamdemux_src_pad = streamdemux.get_request_pad('src_0')
streamdemux.get_request_pad('src_1')
streamdemux.get_request_pad('src_2')
streamdemux.get_request_pad('src_3')
# workload_3_sink_pad = workload_3.get_static_pad('sink')
# assert streamdemux_src_pad.link(workload_3_sink_pad) == Gst.PadLinkReturn.OK
queue_sink_pad = queue.get_static_pad('sink')
assert streamdemux_src_pad.link(queue_sink_pad) == Gst.PadLinkReturn.OK

assert queue.link(converter)
# assert converter.link(sink_capsfilter)
# assert sink_capsfilter.link(sink)
assert converter.link(encoder)
assert encoder.link(parser)
assert parser.link(sink)
# assert streammux.link(sink)

# create an event loop and feed gstreamer bus messages to it
loop = GLib.MainLoop()
bus = pipeline.get_bus()
bus.add_signal_watch()
bus.connect("message", bus_call, loop)

sink_pad = workload.get_static_pad("sink")
if not sink_pad:
sys.stderr.write("Unable to get sink pad")
else:
sink_pad.add_probe(Gst.PadProbeType.BUFFER, pad_buffer_probe)

print("Starting pipeline")
pipeline.set_state(Gst.State.PLAYING)
try:
loop.run()
except:
pass
print("Exiting app\n")
pipeline.set_state(Gst.State.NULL)


if __name__ == '__main__':
sys.exit(main(sys.argv))
19 changes: 19 additions & 0 deletions libs/opencv/savant/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
set(the_description "Savant OpenCV module")

set(WITH_GSTREAMER ON)
message(STATUS "Using GStreamer: ${GSTREAMER_VERSION}")

# find_package(PkgConfig REQUIRED)
# find_package(CUDA REQUIRED)

if(NOT DEFINED DeepStream_DIR)
set(DeepStream_DIR /opt/nvidia/deepstream/deepstream)
endif()

# pkg_check_modules(GLIB REQUIRED glib-2.0)
# pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)

ocv_define_module(savant opencv_core cudev ocv.3rdparty.gstreamer "GStreamer" WRAP python)

ocv_include_directories(${DeepStream_DIR}/sources/includes)
ocv_target_link_libraries(${the_module} "${DeepStream_DIR}/lib")
18 changes: 18 additions & 0 deletions libs/opencv/savant/include/opencv2/savant.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// Created by pavel on 11/01/23.
//

#ifndef OPENCV_SAVANT_HPP
#define OPENCV_SAVANT_HPP

#include "opencv2/core.hpp"
#include "opencv2/core/cuda.hpp"

namespace cv {
namespace savant {
CV_EXPORTS_W cuda::GpuMat createGpuMat(int rows, int cols, int type, uint64 dataAddr, size_t step = Mat::AUTO_STEP);
CV_EXPORTS_W cuda::GpuMat createGpuMat(size_t gst_buffer, int batch_id);
}
}

#endif //OPENCV_SAVANT_HPP
30 changes: 30 additions & 0 deletions libs/opencv/savant/src/savant.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Created by pavel on 11/01/23.
//

#include "opencv2/savant.hpp"
#include "opencv2/core.hpp"
#include "opencv2/core/cuda.hpp"

#include <gst/gst.h>
#include "nvbufsurface.h"

namespace cv {
namespace savant {
cuda::GpuMat createGpuMat(int rows, int cols, int type, uint64 dataAddr, size_t step) {
cuda::GpuMat gpuMat(rows, cols, type, (void *) dataAddr, step);
return gpuMat;
}

cuda::GpuMat createGpuMat(size_t gst_buffer, int batch_id) {
auto * buffer = (GstBuffer *)(gst_buffer);
GstMapInfo map_info;
gst_buffer_map(buffer, &map_info, GST_MAP_READ);
auto * nvsurface = (NvBufSurface *)(map_info.data);
gst_buffer_unmap(buffer, &map_info);
const NvBufSurfaceParams &surface = nvsurface->surfaceList[batch_id];
cv::cuda::GpuMat gpuMat(surface.height, surface.width, CV_8UC4, (void *)surface.dataPtr);
return gpuMat;
}
}
}

0 comments on commit 252155b

Please sign in to comment.