PyNvVideoCodec 2.0.0__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PyNvVideoCodec/PyNvVideoCodec_121.cp312-win_amd64.pyd +0 -0
- PyNvVideoCodec/PyNvVideoCodec_130.cp312-win_amd64.pyd +0 -0
- PyNvVideoCodec/VersionCheck.cp312-win_amd64.pyd +0 -0
- PyNvVideoCodec/__init__.py +237 -0
- PyNvVideoCodec/avcodec-58.dll +0 -0
- PyNvVideoCodec/avcodec-61.dll +0 -0
- PyNvVideoCodec/avcodec.dll +0 -0
- PyNvVideoCodec/avdevice-58.dll +0 -0
- PyNvVideoCodec/avdevice-61.dll +0 -0
- PyNvVideoCodec/avdevice.dll +0 -0
- PyNvVideoCodec/avfilter-10.dll +0 -0
- PyNvVideoCodec/avfilter-7.dll +0 -0
- PyNvVideoCodec/avfilter.dll +0 -0
- PyNvVideoCodec/avformat-58.dll +0 -0
- PyNvVideoCodec/avformat-61.dll +0 -0
- PyNvVideoCodec/avformat.dll +0 -0
- PyNvVideoCodec/avutil-56.dll +0 -0
- PyNvVideoCodec/avutil-59.dll +0 -0
- PyNvVideoCodec/avutil.dll +0 -0
- PyNvVideoCodec/decoders/SimpleDecoder.py +225 -0
- PyNvVideoCodec/decoders/ThreadedDecoder.py +119 -0
- PyNvVideoCodec/decoders/__init__.py +22 -0
- PyNvVideoCodec/swresample-3.dll +0 -0
- PyNvVideoCodec/swresample-5.dll +0 -0
- PyNvVideoCodec/swresample.dll +0 -0
- PyNvVideoCodec/swscale-5.dll +0 -0
- PyNvVideoCodec/swscale-8.dll +0 -0
- PyNvVideoCodec/swscale.dll +0 -0
- PyNvVideoCodec/transcoder/Transcoder.py +55 -0
- PyNvVideoCodec/transcoder/__init__.py +22 -0
- PyNvVideoCodec-2.0.0.data/data/external/ffmpeg/readme.txt +10 -0
- PyNvVideoCodec-2.0.0.data/data/external/ffmpeg/src/ffmpeg-7.0.2.tar.xz +0 -0
- PyNvVideoCodec-2.0.0.dist-info/METADATA +77 -0
- PyNvVideoCodec-2.0.0.dist-info/RECORD +64 -0
- PyNvVideoCodec-2.0.0.dist-info/WHEEL +5 -0
- PyNvVideoCodec-2.0.0.dist-info/licenses/LICENSE.txt +22 -0
- PyNvVideoCodec-2.0.0.dist-info/licenses/NOTICES.txt +167 -0
- PyNvVideoCodec-2.0.0.dist-info/top_level.txt +3 -0
- benchmarks/cached_decoder_benchmark.py +635 -0
- benchmarks/frame_sampling_benchmark.py +610 -0
- benchmarks/segmented_transcode_benchmark.py +1072 -0
- samples/Decode.py +194 -0
- samples/DecodeMultiprocessing.py +162 -0
- samples/DecodePerf.py +215 -0
- samples/DecodeSEIMsgExtraction.py +338 -0
- samples/DemuxFromByteArray.py +185 -0
- samples/Encode.py +189 -0
- samples/EncodeFromCPUBuffer.py +220 -0
- samples/EncodeMultiprocessing.py +268 -0
- samples/EncodePerf.py +380 -0
- samples/EncodeReconfigure.py +201 -0
- samples/EncodeSEIMsgInsertion.py +189 -0
- samples/ObjectDetection.py +218 -0
- samples/SimpleDecoder/EnsembleApp.py +416 -0
- samples/SimpleDecoder/SubsamplingAndReconfigure.py +341 -0
- samples/SimpleDecoder/fileLists.txt +4 -0
- samples/SimpleDecoder/segments.txt +3 -0
- samples/SimpleDecoder/timeListInSeconds.txt +5 -0
- samples/SimpleDecoder/transcode_config.json +6 -0
- samples/Utils.py +290 -0
- samples/coco_classes.pickle +0 -0
- samples/encode_config.json +16 -0
- samples/encode_config_lowlatency.json +11 -0
- samples/encode_config_perf.json +9 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# This copyright notice applies to this file only
|
|
2
|
+
#
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
7
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
8
|
+
# to deal in the Software without restriction, including without limitation
|
|
9
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
10
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
11
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
|
14
|
+
# all copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
19
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
22
|
+
# DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
import PyNvVideoCodec as nvc
|
|
25
|
+
import json
|
|
26
|
+
import argparse
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
import io
|
|
29
|
+
import sys
|
|
30
|
+
import os
|
|
31
|
+
from os.path import join, dirname, abspath
|
|
32
|
+
|
|
33
|
+
# Add the parent directory to Python path in a way that works from any working directory
|
|
34
|
+
current_dir = dirname(abspath(__file__))
|
|
35
|
+
parent_dir = dirname(current_dir)
|
|
36
|
+
if parent_dir not in sys.path:
|
|
37
|
+
sys.path.append(parent_dir)
|
|
38
|
+
|
|
39
|
+
from Utils import AppFrame
|
|
40
|
+
from Utils import FetchCPUFrame
|
|
41
|
+
from Utils import FetchGPUFrame
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Constants
|
|
45
|
+
TOTAL_NUM_FRAMES = 1000
|
|
46
|
+
SEI_MESSAGE_1 = [0xdc, 0x45, 0xe9, 0xbd, 0xe6, 0xd9, 0x48, 0xb7, 0x96, 0x2c, 0xd8, 0x20, 0xd9, 0x23, 0xee, 0xef]
|
|
47
|
+
SEI_MESSAGE_2 = [0x12, 0x67, 0x56, 0xda, 0xef, 0x99, 0x00, 0xbb, 0x6a, 0xc4, 0xd8, 0x10, 0xf9, 0xe3, 0x3e, 0x8f]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def encode(gpu_id, dec_file_path, enc_file_path, width, height, fmt, config_params):
|
|
51
|
+
"""
|
|
52
|
+
Encode frames with SEI message insertion into the bitstream using CUDA device buffers as input.
|
|
53
|
+
|
|
54
|
+
This function reads image data from a file and loads it to CUDA input buffers using FetchGPUFrame().
|
|
55
|
+
The encoder copies the CUDA buffers and SEI message data and submits them to NVENC hardware for encoding.
|
|
56
|
+
Video memory buffer is allocated to get the NVENC hardware output, which is then copied to host memory
|
|
57
|
+
for file storage.
|
|
58
|
+
|
|
59
|
+
Parameters:
|
|
60
|
+
gpu_id (int): Ordinal of GPU to use [Parameter not in use]
|
|
61
|
+
dec_file_path (str): Path to file to be decoded
|
|
62
|
+
enc_file_path (str): Path to output file for encoded frames
|
|
63
|
+
width (int): Width of encoded frame
|
|
64
|
+
height (int): Height of encoded frame
|
|
65
|
+
fmt (str): Surface format string in uppercase (e.g., "NV12")
|
|
66
|
+
config_params (dict): Key-value pairs providing fine-grained control on encoding
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
None
|
|
70
|
+
|
|
71
|
+
Example:
|
|
72
|
+
>>> encode(0, "input.yuv", "output.h264", 1920, 1080, "NV12", {"codec": "h264"})
|
|
73
|
+
Encode 1080p NV12 raw YUV into elementary bitstream using H.264 codec with SEI messages
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
# Determine SEI type based on codec
|
|
77
|
+
if config_params["codec"] in ["hevc", "h264"]:
|
|
78
|
+
sei_info = {"sei_type": 5}
|
|
79
|
+
elif config_params["codec"] == "av1":
|
|
80
|
+
sei_info = {"sei_type": 6}
|
|
81
|
+
else:
|
|
82
|
+
raise ValueError(f"Unsupported codec: {config_params['codec']}")
|
|
83
|
+
|
|
84
|
+
# Create SEI messages list
|
|
85
|
+
sei_messages = [(sei_info, SEI_MESSAGE_1), (sei_info, SEI_MESSAGE_2)]
|
|
86
|
+
|
|
87
|
+
with open(dec_file_path, "rb") as decFile, open(enc_file_path, "wb") as encFile:
|
|
88
|
+
# Create encoder object
|
|
89
|
+
config_params["gpu_id"] = gpu_id
|
|
90
|
+
nvenc = nvc.CreateEncoder(width, height, fmt, False, **config_params)
|
|
91
|
+
|
|
92
|
+
# Create input frame list
|
|
93
|
+
input_frame_list = [AppFrame(width, height, fmt) for _ in range(1, 5)]
|
|
94
|
+
|
|
95
|
+
# Process frames
|
|
96
|
+
for input_gpu_frame in FetchGPUFrame(
|
|
97
|
+
input_frame_list,
|
|
98
|
+
FetchCPUFrame(decFile, input_frame_list[0].frameSize),
|
|
99
|
+
TOTAL_NUM_FRAMES
|
|
100
|
+
):
|
|
101
|
+
bitstream = nvenc.Encode(input_gpu_frame, 0, sei_messages)
|
|
102
|
+
encFile.write(bytearray(bitstream))
|
|
103
|
+
|
|
104
|
+
# Flush encoder queue
|
|
105
|
+
bitstream = nvenc.EndEncode()
|
|
106
|
+
encFile.write(bytearray(bitstream))
|
|
107
|
+
|
|
108
|
+
print(f"Encoded frames written to {enc_file_path} with SEI messages")
|
|
109
|
+
|
|
110
|
+
except nvc.PyNvVCExceptionUnsupported as e:
|
|
111
|
+
print(f"CreateEncoder failure: {e}")
|
|
112
|
+
except Exception as e:
|
|
113
|
+
print(f"An unexpected error occurred: {e}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
parser = argparse.ArgumentParser(
|
|
118
|
+
description="This sample application illustrates encoding of frames with SEI message insertion."
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
parser.add_argument(
|
|
122
|
+
"-g", "--gpu_id",
|
|
123
|
+
type=int,
|
|
124
|
+
default=0,
|
|
125
|
+
help="Check nvidia-smi for available GPUs. Default: 0"
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"-i", "--raw_file_path",
|
|
129
|
+
type=Path,
|
|
130
|
+
required=True,
|
|
131
|
+
help="Raw video file (read from)"
|
|
132
|
+
)
|
|
133
|
+
parser.add_argument(
|
|
134
|
+
"-o", "--encoded_file_path",
|
|
135
|
+
type=Path,
|
|
136
|
+
help="Encoded video file (write to). Default: <input_file_name>.h264"
|
|
137
|
+
)
|
|
138
|
+
parser.add_argument(
|
|
139
|
+
"-s", "--size",
|
|
140
|
+
type=str,
|
|
141
|
+
required=True,
|
|
142
|
+
help="WidthxHeight of raw frame (e.g., 1920x1080)"
|
|
143
|
+
)
|
|
144
|
+
parser.add_argument(
|
|
145
|
+
"-if", "--format",
|
|
146
|
+
type=str,
|
|
147
|
+
default="NV12",
|
|
148
|
+
help="Format of input file (default is NV12)"
|
|
149
|
+
)
|
|
150
|
+
parser.add_argument(
|
|
151
|
+
"-c", "--codec",
|
|
152
|
+
type=str,
|
|
153
|
+
default="H264",
|
|
154
|
+
help="Video codec (HEVC, H264, AV1). Default is H264"
|
|
155
|
+
)
|
|
156
|
+
parser.add_argument(
|
|
157
|
+
"-json", "--config_file",
|
|
158
|
+
type=str,
|
|
159
|
+
default=join(current_dir, "encode_config.json"),
|
|
160
|
+
help="Path of JSON config file (default: encode_config.json in the same directory)"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
args = parser.parse_args()
|
|
164
|
+
config = {}
|
|
165
|
+
|
|
166
|
+
# Set default encoded_file_path if not provided
|
|
167
|
+
if args.encoded_file_path is None:
|
|
168
|
+
input_path = Path(args.raw_file_path)
|
|
169
|
+
args.encoded_file_path = input_path.with_suffix('.h264')
|
|
170
|
+
|
|
171
|
+
if args.config_file:
|
|
172
|
+
with open(args.config_file) as jsonFile:
|
|
173
|
+
config = json.loads(jsonFile.read())
|
|
174
|
+
config["preset"] = config["preset"].upper()
|
|
175
|
+
|
|
176
|
+
args.codec = args.codec.lower()
|
|
177
|
+
args.format = args.format.upper()
|
|
178
|
+
config["codec"] = args.codec
|
|
179
|
+
width, height = map(int, args.size.split("x"))
|
|
180
|
+
|
|
181
|
+
encode(
|
|
182
|
+
args.gpu_id,
|
|
183
|
+
args.raw_file_path.as_posix(),
|
|
184
|
+
args.encoded_file_path.as_posix(),
|
|
185
|
+
width,
|
|
186
|
+
height,
|
|
187
|
+
args.format,
|
|
188
|
+
config
|
|
189
|
+
)
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# This copyright notice applies to this file only
|
|
2
|
+
#
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
#
|
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
7
|
+
# copy of this software and associated documentation files (the "Software"),
|
|
8
|
+
# to deal in the Software without restriction, including without limitation
|
|
9
|
+
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
10
|
+
# and/or sell copies of the Software, and to permit persons to whom the
|
|
11
|
+
# Software is furnished to do so, subject to the following conditions:
|
|
12
|
+
#
|
|
13
|
+
# The above copyright notice and this permission notice shall be included in
|
|
14
|
+
# all copies or substantial portions of the Software.
|
|
15
|
+
#
|
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
19
|
+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
21
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
22
|
+
# DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
import sys
|
|
25
|
+
import argparse
|
|
26
|
+
import numpy as np
|
|
27
|
+
import torch
|
|
28
|
+
import torchvision
|
|
29
|
+
from pathlib import Path
|
|
30
|
+
import pycuda.driver as cuda
|
|
31
|
+
import PyNvVideoCodec as nvc
|
|
32
|
+
from pycuda.autoinit import context
|
|
33
|
+
import os
|
|
34
|
+
from os.path import join, dirname, abspath
|
|
35
|
+
from itertools import zip_longest
|
|
36
|
+
|
|
37
|
+
# Add the parent directory to Python path in a way that works from any working directory
|
|
38
|
+
current_dir = dirname(abspath(__file__))
|
|
39
|
+
parent_dir = dirname(current_dir)
|
|
40
|
+
if parent_dir not in sys.path:
|
|
41
|
+
sys.path.append(parent_dir)
|
|
42
|
+
|
|
43
|
+
# Torch and Torchvision related imports
|
|
44
|
+
import torch
|
|
45
|
+
from torchvision.utils import save_image
|
|
46
|
+
from torchvision.transforms import Normalize
|
|
47
|
+
import torchvision.models.detection as models
|
|
48
|
+
|
|
49
|
+
# Output Display related imports
|
|
50
|
+
import numpy as np
|
|
51
|
+
import matplotlib
|
|
52
|
+
import matplotlib.pyplot as plt
|
|
53
|
+
import torchvision.transforms.functional as F
|
|
54
|
+
from torchvision.utils import draw_bounding_boxes
|
|
55
|
+
import time
|
|
56
|
+
|
|
57
|
+
CLASSES = []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Label class mapping for COCO dataset
|
|
61
|
+
import pickle
|
|
62
|
+
script_dir = dirname(abspath(__file__))
|
|
63
|
+
coco_classes_path = join(script_dir, "coco_classes.pickle")
|
|
64
|
+
with open(coco_classes_path, "rb") as f:
|
|
65
|
+
CLASSES = pickle.load(f)
|
|
66
|
+
|
|
67
|
+
def show(imgs, fig = None, axs = None, im = None):
|
|
68
|
+
'''Function to show batch images. Takes in images as input.'''
|
|
69
|
+
if not isinstance(imgs, list):
|
|
70
|
+
imgs = [imgs]
|
|
71
|
+
|
|
72
|
+
img = F.to_pil_image(imgs[0])
|
|
73
|
+
if fig is None or axs is None or im is None:
|
|
74
|
+
plt.ion()
|
|
75
|
+
fig, axs = plt.subplots()
|
|
76
|
+
im = axs.imshow(np.asarray(img), cmap='viridis')
|
|
77
|
+
else:
|
|
78
|
+
im.set_data(np.asarray(img))
|
|
79
|
+
|
|
80
|
+
for i, img in enumerate(imgs):
|
|
81
|
+
img = img.detach()
|
|
82
|
+
img = F.to_pil_image(img)
|
|
83
|
+
im.set_data(np.asarray(img))
|
|
84
|
+
fig.canvas.flush_events()
|
|
85
|
+
time.sleep(0.1)
|
|
86
|
+
return fig, axs, im
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_filtered_output(input, confidence_threshold):
|
|
90
|
+
'''Function to filter out boxes based on confidence scores'''
|
|
91
|
+
output = {}
|
|
92
|
+
valid_indices = [i for i, conf in enumerate(input['scores']) if conf >= confidence_threshold]
|
|
93
|
+
|
|
94
|
+
if not valid_indices:
|
|
95
|
+
print("No boxes above the confidence threshold.")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
# Filter boxes, labels, and confidences
|
|
99
|
+
input_boxes = input["boxes"]
|
|
100
|
+
input_labels = input["labels"]
|
|
101
|
+
input_scores = input["scores"]
|
|
102
|
+
|
|
103
|
+
output["boxes"] = input_boxes[valid_indices]
|
|
104
|
+
output["labels"] = filtered_labels = [input_labels[i] for i in valid_indices]
|
|
105
|
+
output["scores"] = [input_scores[i] for i in valid_indices]
|
|
106
|
+
output["labels_confidence_string"] = [f"{CLASSES[label]} {confidence:.2f}" for label, confidence in zip(filtered_labels, output["scores"])]
|
|
107
|
+
|
|
108
|
+
return output
|
|
109
|
+
|
|
110
|
+
def print_and_write(message, file_path):
|
|
111
|
+
'''Function to print and write message '''
|
|
112
|
+
print(message)
|
|
113
|
+
|
|
114
|
+
with open(file_path, 'a') as file:
|
|
115
|
+
file.write(message + '\n')
|
|
116
|
+
|
|
117
|
+
def decode_and_detect(enc_file_path, detection_output_file_path, confidence_threshold, display_output, gpu_id):
|
|
118
|
+
'''Function to decode and detect the objects in the video frames.
|
|
119
|
+
This used ThreadedDecoder from PyNvC for batch decoding. ThreadedDecoder
|
|
120
|
+
runs in a separate thread and is launched on instance creation. Its aim
|
|
121
|
+
is to provide better pipelining.
|
|
122
|
+
It uses FasterRCNN(resnet50) model for object detection
|
|
123
|
+
'''
|
|
124
|
+
# Cuda initialization and threaded decoder creation
|
|
125
|
+
try:
|
|
126
|
+
cuda.init()
|
|
127
|
+
cudaDevice = cuda.Device(gpu_id)
|
|
128
|
+
cudaCtx = cudaDevice.retain_primary_context()
|
|
129
|
+
cudaCtx.push()
|
|
130
|
+
cudaStreamNvDec = cuda.Stream()
|
|
131
|
+
decoder = nvc.ThreadedDecoder(enc_file_path, buffer_size=12, gpu_id=gpu_id, cuda_context=cudaCtx.handle,
|
|
132
|
+
cuda_stream=cudaStreamNvDec.handle, use_device_memory = True,
|
|
133
|
+
output_color_type=nvc.OutputColorType.RGBP)
|
|
134
|
+
except Exception as e:
|
|
135
|
+
print(f"Error initializing CUDA: {e}")
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
# Loading FasterRCNN model
|
|
139
|
+
model = models.fasterrcnn_resnet50_fpn(pretrained=True)
|
|
140
|
+
model.to(torch.device('cuda'))
|
|
141
|
+
model.eval()
|
|
142
|
+
|
|
143
|
+
fig = None
|
|
144
|
+
axs = None
|
|
145
|
+
axs_im = None
|
|
146
|
+
# Decoder loop
|
|
147
|
+
while True:
|
|
148
|
+
frames = decoder.get_batch_frames(3)
|
|
149
|
+
src_tensor_list = []
|
|
150
|
+
image_list = []
|
|
151
|
+
for frame in frames:
|
|
152
|
+
# Get the interoperable tensor from PyNVC without any copy
|
|
153
|
+
# This tensor is in planar RGB format. That is R of all pixels followed
|
|
154
|
+
# by G of all pixels followed by B of all all pixels
|
|
155
|
+
src_tensor = torch.from_dlpack(frame)
|
|
156
|
+
# Store image for display if needed
|
|
157
|
+
if display_output:
|
|
158
|
+
image_list.append(src_tensor)
|
|
159
|
+
# Model expects float values in [0, 1]
|
|
160
|
+
src_tensor = src_tensor.float() / 255.0
|
|
161
|
+
src_tensor_list.append(src_tensor)
|
|
162
|
+
|
|
163
|
+
# Loop break conditions. No more frames to decode
|
|
164
|
+
if len(frames) == 0:
|
|
165
|
+
break
|
|
166
|
+
|
|
167
|
+
# Display needs images in bytes and 0-255 range.
|
|
168
|
+
if display_output:
|
|
169
|
+
image_for_disp = torch.stack(image_list)
|
|
170
|
+
else:
|
|
171
|
+
image_for_disp = torch.empty(1,1)
|
|
172
|
+
|
|
173
|
+
# Run the inference on batch input
|
|
174
|
+
with torch.no_grad():
|
|
175
|
+
outputs = model(src_tensor_list)
|
|
176
|
+
|
|
177
|
+
# Print the bounding box, associated class label and the confidence score
|
|
178
|
+
for output, im in zip_longest(outputs, image_for_disp):
|
|
179
|
+
filtered_output = get_filtered_output(output, confidence_threshold)
|
|
180
|
+
if filtered_output is None:
|
|
181
|
+
continue
|
|
182
|
+
for box, label, score in zip(filtered_output['boxes'], filtered_output['labels'], filtered_output['scores']):
|
|
183
|
+
print_and_write(f"Box: {box}, Label: {CLASSES[label]}, Score: {score}", detection_output_file_path)
|
|
184
|
+
if display_output:
|
|
185
|
+
print (display_output)
|
|
186
|
+
boxes = draw_bounding_boxes(im, boxes=filtered_output['boxes'], labels=filtered_output['labels_confidence_string'], width = 4)
|
|
187
|
+
fig, axs, axs_im = show(boxes, fig, axs, axs_im)
|
|
188
|
+
|
|
189
|
+
cudaCtx.pop()
|
|
190
|
+
print(f"Detection output written to {detection_output_file_path}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
parser = argparse.ArgumentParser(
|
|
195
|
+
" This sample application demonstartes Object Detection using PyNvC's ThreadedDecoder. It creates an instance of \
|
|
196
|
+
ThreadedDecoder class, and then loops over requesting for a batch frames in each iteration of the loop. The \
|
|
197
|
+
returned frames are directly interoperable with pytorch with zero copy. The frames are then used for object detection \
|
|
198
|
+
in a FasterRCNN model. The box coordinates, class label and confidence scores are printed."
|
|
199
|
+
)
|
|
200
|
+
parser.add_argument(
|
|
201
|
+
"-i", "--encoded_file_path", type=Path, required=True, help="Encoded video file (read from)")
|
|
202
|
+
parser.add_argument(
|
|
203
|
+
"-o", "--detection_output_file_path", type=Path, help="Detection output file path. Default: <input_file_name>_detection.txt")
|
|
204
|
+
parser.add_argument(
|
|
205
|
+
"-c", "--confidence_threshold", type=float, default=0.8, help="Confidence score above which the bounding box and label is considered a valid detection")
|
|
206
|
+
parser.add_argument(
|
|
207
|
+
"-d", "--display_output", action='store_true', help="Enable output display")
|
|
208
|
+
parser.add_argument(
|
|
209
|
+
"-g", "--gpu_id", type=int, default=0, help="Check nvidia-smi for available GPUs. Default: 0")
|
|
210
|
+
args = parser.parse_args()
|
|
211
|
+
|
|
212
|
+
# Set default detection_output_file_path if not provided
|
|
213
|
+
if args.detection_output_file_path is None:
|
|
214
|
+
input_path = Path(args.encoded_file_path)
|
|
215
|
+
args.detection_output_file_path = input_path.with_name(f"{input_path.stem}_detection.txt")
|
|
216
|
+
|
|
217
|
+
print ("Args value: ", args.display_output)
|
|
218
|
+
decode_and_detect(args.encoded_file_path.as_posix(), args.detection_output_file_path.as_posix(), args.confidence_threshold, args.display_output, args.gpu_id)
|