Skip to content

Commit 712b897

Browse files
committed
cudacodec: return luma hist from VideoReader::nextFrame if requested
1 parent 3d242a6 commit 712b897

File tree

7 files changed

+191
-34
lines changed

7 files changed

+191
-34
lines changed

modules/cudacodec/include/opencv2/cudacodec.hpp

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ enum ChromaFormat
295295

296296
/** @brief Deinterlacing mode used by decoder.
297297
* @param Weave Weave both fields (no deinterlacing). For progressive content and for content that doesn't need deinterlacing.
298-
* Bob Drop one field.
298+
* @param Bob Drop one field.
299299
* @param Adaptive Adaptive deinterlacing needs more video memory than other deinterlacing modes.
300300
* */
301301
enum DeinterlaceMode
@@ -305,12 +305,22 @@ enum DeinterlaceMode
305305
Adaptive = 2
306306
};
307307

308+
/** @brief Utility function demonstrating how to map the luma histogram when FormatInfo::videoFullRangeFlag == false
309+
@param hist Luma histogram \a hist returned from VideoReader::nextFrame(GpuMat& frame, GpuMat& hist, Stream& stream).
310+
@param histFull Host histogram equivelent to downloading \a hist after calling cuda::calcHist(InputArray frame, OutputArray hist, Stream& stream).
311+
312+
@note
313+
- This function demonstrates how to map the luma histogram back so that it is equivalent to the result obtained from cuda::calcHist()
314+
if the returned frame was colorFormat::GRAY.
315+
*/
316+
CV_EXPORTS_W void MapHist(const GpuMat& hist, CV_OUT Mat& histFull);
317+
308318
/** @brief Struct providing information about video file format. :
309319
*/
310320
struct CV_EXPORTS_W_SIMPLE FormatInfo
311321
{
312-
CV_WRAP FormatInfo() : nBitDepthMinus8(-1), nBitDepthChromaMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false),
313-
fps(0), ulNumDecodeSurfaces(0), videoFullRangeFlag(false) {};
322+
CV_WRAP FormatInfo() : nBitDepthMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false),
323+
fps(0), ulNumDecodeSurfaces(0), videoFullRangeFlag(false), histogramEnabled(false), nCounterBitDepth(0), nMaxHistogramBins(0){};
314324

315325
CV_PROP_RW Codec codec;
316326
CV_PROP_RW ChromaFormat chromaFormat;
@@ -331,6 +341,9 @@ struct CV_EXPORTS_W_SIMPLE FormatInfo
331341
CV_PROP_RW cv::Rect srcRoi;//!< Region of interest decoded from video source.
332342
CV_PROP_RW cv::Rect targetRoi;//!< Region of interest in the output frame containing the decoded frame.
333343
CV_PROP_RW bool videoFullRangeFlag;//!< Output value indicating if the black level, luma and chroma of the source are represented using the full or limited range (AKA TV or "analogue" range) of values as defined in Annex E of the ITU-T Specification. Internally the conversion from NV12 to BGR obeys ITU 709.
344+
CV_PROP_RW bool histogramEnabled;//!< Flag indicating whether histogram output has been enabled and is supported.
345+
CV_PROP_RW int nCounterBitDepth;//!< Bit depth of histogram bins.
346+
CV_PROP_RW int nMaxHistogramBins;//!< Max number of histogram bins.
334347
};
335348

336349
/** @brief cv::cudacodec::VideoReader generic properties identifier.
@@ -376,6 +389,20 @@ class CV_EXPORTS_W VideoReader
376389
*/
377390
CV_WRAP virtual bool nextFrame(CV_OUT GpuMat& frame, Stream &stream = Stream::Null()) = 0;
378391

392+
/** @brief Grabs, decodes and returns the next video frame and frame luma histogram.
393+
394+
@param [out] frame The video frame.
395+
@param [out] histogram Histogram of the luma component of the encoded frame, see note.
396+
@param stream Stream for the asynchronous version.
397+
@return `false` if no frames have been grabbed.
398+
399+
If no frames have been grabbed (there are no more frames in video file), the methods return false.
400+
The method throws an Exception if error occurs.
401+
402+
@note Histogram data is collected by NVDEC during the decoding process resulting in zero performance penalty. NVDEC computes the histogram data for only the luma component of decoded output, not on post-processed frame(i.e. when scaling, cropping, etc. applied). If the source is encoded using a limited range of luma values (FormatInfo::videoFullRangeFlag == false) then the histogram bin values will correspond to to this limited range of values and will need to be mapped to contain the same output as cuda::calcHist(). The MapHist() utility function can be used to perform this mapping on the host if required.
403+
*/
404+
CV_WRAP_AS(nextFrameWithHist) virtual bool nextFrame(CV_OUT GpuMat& frame, CV_OUT GpuMat& histogram, Stream& stream = Stream::Null()) = 0;
405+
379406
/** @brief Returns information about video file format.
380407
*/
381408
CV_WRAP virtual FormatInfo format() const = 0;
@@ -535,16 +562,18 @@ but it cannot go below the number determined by NVDEC.
535562
@param srcRoi Region of interest (x/width should be multiples of 4 and y/height multiples of 2) decoded from video source, defaults to the full frame.
536563
@param targetRoi Region of interest (x/width should be multiples of 4 and y/height multiples of 2) within the output frame to copy and resize the decoded frame to,
537564
defaults to the full frame.
565+
@param enableHistogramOutput Request output of decoded luma histogram \a hist from VideoReader::nextFrame(GpuMat& frame, GpuMat& hist, Stream& stream) if supported.
538566
*/
539567
struct CV_EXPORTS_W_SIMPLE VideoReaderInitParams {
540-
CV_WRAP VideoReaderInitParams() : udpSource(false), allowFrameDrop(false), minNumDecodeSurfaces(0), rawMode(0) {};
568+
CV_WRAP VideoReaderInitParams() : udpSource(false), allowFrameDrop(false), minNumDecodeSurfaces(0), rawMode(0), enableHistogramOutput(false){};
541569
CV_PROP_RW bool udpSource;
542570
CV_PROP_RW bool allowFrameDrop;
543571
CV_PROP_RW int minNumDecodeSurfaces;
544572
CV_PROP_RW bool rawMode;
545573
CV_PROP_RW cv::Size targetSz;
546574
CV_PROP_RW cv::Rect srcRoi;
547575
CV_PROP_RW cv::Rect targetRoi;
576+
CV_PROP_RW bool enableHistogramOutput;
548577
};
549578

550579
/** @brief Creates video reader.

modules/cudacodec/misc/python/test/test_cudacodec.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,43 +14,61 @@ def setUp(self):
1414
@unittest.skipIf('OPENCV_TEST_DATA_PATH' not in os.environ,
1515
"OPENCV_TEST_DATA_PATH is not defined")
1616
def test_reader(self):
17-
#Test the functionality but not the results of the video reader
17+
# Test the functionality but not the results of the VideoReader
1818

19-
vid_path = os.environ['OPENCV_TEST_DATA_PATH'] + '/cv/video/1920x1080.avi'
19+
vid_path = os.environ['OPENCV_TEST_DATA_PATH'] + '/highgui/video/big_buck_bunny.h264'
2020
try:
2121
reader = cv.cudacodec.createVideoReader(vid_path)
2222
format_info = reader.format()
2323
ret, gpu_mat = reader.nextFrame()
2424
self.assertTrue(ret)
25-
self.assertTrue('GpuMat' in str(type(gpu_mat)), msg=type(gpu_mat))
25+
self.assertTrue(isinstance(gpu_mat, cv.cuda.GpuMat), msg=type(gpu_mat))
2626
#TODO: print(cv.utils.dumpInputArray(gpu_mat)) # - no support for GpuMat
2727

28+
# Retrieve format info
2829
if(not format_info.valid):
2930
format_info = reader.format()
3031
sz = gpu_mat.size()
3132
self.assertTrue(sz[0] == format_info.width and sz[1] == format_info.height)
3233

3334
# not checking output, therefore sepearate tests for different signatures is unecessary
34-
ret, _gpu_mat2 = reader.nextFrame(gpu_mat)
35-
#TODO: self.assertTrue(gpu_mat == gpu_mat2)
36-
self.assertTrue(ret)
35+
ret, gpu_mat_ = reader.nextFrame(gpu_mat)
36+
self.assertTrue(ret and gpu_mat_.cudaPtr() == gpu_mat.cudaPtr())
3737

38+
# Pass VideoReaderInitParams to the decoder and initialization params to the source (cv::VideoCapture)
3839
params = cv.cudacodec.VideoReaderInitParams()
3940
params.rawMode = True
41+
params.enableHistogramOutput = True
4042
ms_gs = 1234
43+
post_processed_sz = (gpu_mat.size()[0]*2, gpu_mat.size()[1]*2)
44+
params.targetSz = post_processed_sz
4145
reader = cv.cudacodec.createVideoReader(vid_path,[cv.CAP_PROP_OPEN_TIMEOUT_MSEC, ms_gs], params)
4246
ret, ms = reader.get(cv.CAP_PROP_OPEN_TIMEOUT_MSEC)
4347
self.assertTrue(ret and ms == ms_gs)
4448
ret, raw_mode = reader.getVideoReaderProps(cv.cudacodec.VideoReaderProps_PROP_RAW_MODE)
4549
self.assertTrue(ret and raw_mode)
4650

51+
# Retrieve image histogram
52+
ret, gpu_mat, hist = reader.nextFrameWithHist()
53+
self.assertTrue(ret and not gpu_mat.empty() and hist.size() == (256,1))
54+
ret, gpu_mat_, hist_ = reader.nextFrameWithHist(gpu_mat, hist)
55+
self.assertTrue(ret and not gpu_mat.empty() and hist.size() == (256,1))
56+
self.assertTrue(gpu_mat_.cudaPtr() == gpu_mat.cudaPtr() and hist_.cudaPtr() == hist.cudaPtr())
57+
hist_host = cv.cudacodec.MapHist(hist)
58+
self.assertTrue(hist_host.shape == (1,256) and isinstance(hist_host, np.ndarray))
59+
60+
# Check post processing applied
61+
self.assertTrue(gpu_mat.size() == post_processed_sz)
62+
63+
# Change color format
4764
ret, colour_code = reader.getVideoReaderProps(cv.cudacodec.VideoReaderProps_PROP_COLOR_FORMAT)
4865
self.assertTrue(ret and colour_code == cv.cudacodec.ColorFormat_BGRA)
4966
colour_code_gs = cv.cudacodec.ColorFormat_GRAY
5067
reader.set(colour_code_gs)
5168
ret, colour_code = reader.getVideoReaderProps(cv.cudacodec.VideoReaderProps_PROP_COLOR_FORMAT)
5269
self.assertTrue(ret and colour_code == colour_code_gs)
5370

71+
# Read raw encoded bitstream
5472
ret, i_base = reader.getVideoReaderProps(cv.cudacodec.VideoReaderProps_PROP_RAW_PACKAGES_BASE_INDEX)
5573
self.assertTrue(ret and i_base == 2.0)
5674
self.assertTrue(reader.grab())
@@ -75,8 +93,8 @@ def test_reader(self):
7593
else:
7694
self.skipTest(e.err)
7795

78-
def test_writer_existence(self):
79-
#Test at least the existence of wrapped functions for now
96+
def test_writer(self):
97+
# Test the functionality but not the results of the VideoWriter
8098

8199
try:
82100
fd, fname = tempfile.mkstemp(suffix=".h264")
@@ -91,11 +109,12 @@ def test_writer_existence(self):
91109
writer.write(blankFrameIn)
92110
writer.release()
93111
encoder_params_out = writer.getEncoderParams()
94-
self.assert_true(encoder_params_in.gopLength == encoder_params_out.gopLength)
112+
self.assertTrue(encoder_params_in.gopLength == encoder_params_out.gopLength)
95113
cap = cv.VideoCapture(fname,cv.CAP_FFMPEG)
96-
self.assert_true(cap.isOpened())
114+
self.assertTrue(cap.isOpened())
97115
ret, blankFrameOut = cap.read()
98-
self.assert_true(ret and blankFrameOut.shape == blankFrameIn.download().shape)
116+
self.assertTrue(ret and blankFrameOut.shape == blankFrameIn.download().shape)
117+
cap.release()
99118
except cv.error as e:
100119
self.assertEqual(e.code, cv.Error.StsNotImplemented)
101120
self.skipTest("Either NVCUVENC or a GPU hardware encoder is missing or the encoding profile is not supported.")

modules/cudacodec/src/video_decoder.cpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,8 @@ static const char* GetVideoChromaFormatString(cudaVideoChromaFormat eChromaForma
6262
return "Unknown";
6363
}
6464

65-
void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat)
65+
void cv::cudacodec::detail::VideoDecoder::create(FormatInfo& videoFormat)
6666
{
67-
{
68-
AutoLock autoLock(mtx_);
69-
videoFormat_ = videoFormat;
70-
}
7167
const cudaVideoCodec _codec = static_cast<cudaVideoCodec>(videoFormat.codec);
7268
const cudaVideoChromaFormat _chromaFormat = static_cast<cudaVideoChromaFormat>(videoFormat.chromaFormat);
7369
if (videoFormat.nBitDepthMinus8 > 0) {
@@ -127,15 +123,41 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat)
127123
CV_LOG_ERROR(NULL, "Video source is not supported by hardware video decoder.");
128124
CV_Error(Error::StsUnsupportedFormat, "Video source is not supported by hardware video decoder");
129125
}
126+
127+
if (videoFormat.histogramEnabled){
128+
if (decodeCaps.bIsHistogramSupported) {
129+
if (decodeCaps.nCounterBitDepth != 32) {
130+
std::ostringstream warning;
131+
warning << "Luma histogram output disabled due to current device using " << decodeCaps.nCounterBitDepth << " bit bins. Histogram output only supports 32 bit bins.";
132+
CV_LOG_INFO(NULL, warning.str());
133+
videoFormat.histogramEnabled = false;
134+
}
135+
else {
136+
videoFormat.histogramEnabled = true;
137+
videoFormat.nCounterBitDepth = decodeCaps.nCounterBitDepth;
138+
videoFormat.nMaxHistogramBins = decodeCaps.nMaxHistogramBins;
139+
}
140+
}
141+
else {
142+
CV_LOG_INFO(NULL, "Luma histogram output is not supported for current codec and/or on current device.");
143+
videoFormat.histogramEnabled = false;
144+
}
145+
}
146+
130147
CV_Assert(videoFormat.ulWidth >= decodeCaps.nMinWidth &&
131148
videoFormat.ulHeight >= decodeCaps.nMinHeight &&
132149
videoFormat.ulWidth <= decodeCaps.nMaxWidth &&
133150
videoFormat.ulHeight <= decodeCaps.nMaxHeight);
134151

135152
CV_Assert((videoFormat.width >> 4)* (videoFormat.height >> 4) <= decodeCaps.nMaxMBCount);
136153
#endif
154+
{
155+
AutoLock autoLock(mtx_);
156+
videoFormat_ = videoFormat;
157+
}
137158
// Create video decoder
138159
CUVIDDECODECREATEINFO createInfo_ = {};
160+
createInfo_.enableHistogram = videoFormat.histogramEnabled;
139161
createInfo_.CodecType = _codec;
140162
createInfo_.ulWidth = videoFormat.ulWidth;
141163
createInfo_.ulHeight = videoFormat.ulHeight;

modules/cudacodec/src/video_decoder.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ namespace cv { namespace cudacodec { namespace detail {
4949
class VideoDecoder
5050
{
5151
public:
52-
VideoDecoder(const Codec& codec, const int minNumDecodeSurfaces, cv::Size targetSz, cv::Rect srcRoi, cv::Rect targetRoi, CUcontext ctx, CUvideoctxlock lock) :
52+
VideoDecoder(const Codec& codec, const int minNumDecodeSurfaces, cv::Size targetSz, cv::Rect srcRoi, cv::Rect targetRoi, const bool enableHistogramOutput, CUcontext ctx, CUvideoctxlock lock) :
5353
ctx_(ctx), lock_(lock), decoder_(0)
5454
{
5555
videoFormat_.codec = codec;
5656
videoFormat_.ulNumDecodeSurfaces = minNumDecodeSurfaces;
57+
videoFormat_.histogramEnabled = enableHistogramOutput;
5758
// alignment enforced by nvcuvid, likely due to chroma subsampling
5859
videoFormat_.targetSz.width = targetSz.width - targetSz.width % 2; videoFormat_.targetSz.height = targetSz.height - targetSz.height % 2;
5960
videoFormat_.srcRoi.x = srcRoi.x - srcRoi.x % 4; videoFormat_.srcRoi.width = srcRoi.width - srcRoi.width % 4;
@@ -67,7 +68,7 @@ class VideoDecoder
6768
release();
6869
}
6970

70-
void create(const FormatInfo& videoFormat);
71+
void create(FormatInfo& videoFormat);
7172
int reconfigure(const FormatInfo& videoFormat);
7273
void release();
7374
bool inited() { AutoLock autoLock(mtx_); return decoder_; }
@@ -88,13 +89,14 @@ class VideoDecoder
8889

8990
cudaVideoChromaFormat chromaFormat() const { return static_cast<cudaVideoChromaFormat>(videoFormat_.chromaFormat); }
9091
int nBitDepthMinus8() const { return videoFormat_.nBitDepthMinus8; }
92+
bool histogramEnabled() const { return videoFormat_.histogramEnabled; }
9193

9294
bool decodePicture(CUVIDPICPARAMS* picParams)
9395
{
9496
return cuvidDecodePicture(decoder_, picParams) == CUDA_SUCCESS;
9597
}
9698

97-
cuda::GpuMat mapFrame(int picIdx, CUVIDPROCPARAMS& videoProcParams)
99+
GpuMat mapFrame(int picIdx, CUVIDPROCPARAMS& videoProcParams)
98100
{
99101
CUdeviceptr ptr;
100102
unsigned int pitch;

modules/cudacodec/src/video_parser.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa
149149
maxH = format->coded_height;
150150
newFormat.ulMaxWidth = maxW;
151151
newFormat.ulMaxHeight = maxH;
152+
newFormat.histogramEnabled = thiz->videoDecoder_->histogramEnabled();
152153

153154
thiz->frameQueue_->waitUntilEmpty();
154155
int retVal = newFormat.ulNumDecodeSurfaces;

0 commit comments

Comments
 (0)