diff --git a/vrs/utils/PixelFrame.cpp b/vrs/utils/PixelFrame.cpp index 64a857d9..e782bb71 100644 --- a/vrs/utils/PixelFrame.cpp +++ b/vrs/utils/PixelFrame.cpp @@ -222,6 +222,14 @@ PixelFrame::PixelFrame(const ImageContentBlockSpec& spec) } } +PixelFrame::PixelFrame(PixelFormat pf, uint32_t w, uint32_t h, uint32_t stride) + : imageSpec_{pf, w, h, stride, stride} { + size_t size = imageSpec_.getRawImageSize(); + if (size != ContentBlock::kSizeUnknown) { + frameBytes_.resize(size); + } +} + PixelFrame::PixelFrame(const ImageContentBlockSpec& spec, vector&& frameBytes) : imageSpec_{spec}, frameBytes_{std::move(frameBytes)} {} @@ -253,14 +261,6 @@ void PixelFrame::init(PixelFormat pf, uint32_t w, uint32_t h, uint32_t stride, u } } -void PixelFrame::init(shared_ptr& inOutFrame, const ImageContentBlockSpec& spec) { - if (!inOutFrame) { - inOutFrame = make_shared(spec); - } else { - inOutFrame->init(spec); - } -} - void PixelFrame::swap(PixelFrame& other) noexcept { if (!hasSamePixels(other.imageSpec_)) { ImageContentBlockSpec tempSpec = other.imageSpec_; @@ -292,16 +292,6 @@ void PixelFrame::blankFrame() { } } -bool PixelFrame::readFrame( - shared_ptr& frame, - RecordReader* reader, - const ContentBlock& cb) { - if (!frame) { - frame = make_shared(); - } - return frame->readFrame(reader, cb); -} - bool PixelFrame::readFrame(RecordReader* reader, const ContentBlock& cb) { if (!XR_VERIFY(cb.getContentType() == ContentType::IMAGE)) { return false; @@ -324,16 +314,6 @@ bool PixelFrame::readFrame(RecordReader* reader, const ContentBlock& cb) { return false; } -bool PixelFrame::readDiskImageData( - shared_ptr& frame, - RecordReader* reader, - const ContentBlock& cb) { - if (!frame) { - frame = make_shared(); - } - return frame->readDiskImageData(reader, cb); -} - bool PixelFrame::readDiskImageData(RecordReader* reader, const ContentBlock& cb) { size_t blockSize = cb.getBlockSize(); if (cb.getContentType() != ContentType::IMAGE || blockSize == ContentBlock::kSizeUnknown) { @@ -433,16 +413,6 @@ bool PixelFrame::readCompressedFrame(const std::vector& pixels, ImageFo return false; } -bool PixelFrame::readRawFrame( - shared_ptr& frame, - RecordReader* reader, - const ImageContentBlockSpec& inputImageSpec) { - if (!frame) { - frame = make_shared(inputImageSpec); - } - return frame->readRawFrame(reader, inputImageSpec); -} - void PixelFrame::normalizeFrame( const shared_ptr& sourceFrame, shared_ptr& outFrame, @@ -528,33 +498,43 @@ inline uint8_t clipToUint8(int value) { } bool PixelFrame::normalizeFrame( - shared_ptr& normalizedFrame, + shared_ptr& outNormalizedFrame, bool grey16supported, const NormalizeOptions& options) const { - uint16_t bitsToShift = 0; - uint32_t componentCount = 0; - PixelFormat srcFormat = imageSpec_.getPixelFormat(); // See if we can convert to something simple enough using Ocean - PixelFormat targetPixelFormat = getNormalizedPixelFormat(srcFormat, grey16supported, options); - if (srcFormat == targetPixelFormat) { + PixelFormat targetPixelFormat = + getNormalizedPixelFormat(imageSpec_.getPixelFormat(), grey16supported, options); + if (imageSpec_.getPixelFormat() == targetPixelFormat) { return false; } - if (normalizedFrame.get() == this) { - normalizedFrame.reset(); + if (outNormalizedFrame.get() == this || !outNormalizedFrame) { + outNormalizedFrame = make_shared(); + } + return normalizeFrame(*outNormalizedFrame, grey16supported, options, targetPixelFormat); +} + +bool PixelFrame::normalizeFrame( + PixelFrame& outNormalizedFrame, + bool grey16supported, + const NormalizeOptions& options, + PixelFormat normalizedPixelFormat) const { + PixelFormat srcFormat = imageSpec_.getPixelFormat(); + if (normalizedPixelFormat == PixelFormat::UNDEFINED) { + normalizedPixelFormat = getNormalizedPixelFormat(srcFormat, grey16supported, options); } if (options.semantic == ImageSemantic::Depth && getPixelFormat() == PixelFormat::DEPTH32F && - targetPixelFormat == PixelFormat::GREY8) { + normalizedPixelFormat == PixelFormat::GREY8) { if (options.min < options.max) { - init(normalizedFrame, targetPixelFormat, getWidth(), getHeight()); + outNormalizedFrame.init(normalizedPixelFormat, getWidth(), getHeight()); normalizeBufferWithRange( - rdata(), normalizedFrame->wdata(), getWidth() * getHeight(), options.min, options.max); + rdata(), outNormalizedFrame.wdata(), getWidth() * getHeight(), options.min, options.max); return true; } } else if ( (options.semantic == ImageSemantic::ObjectClassSegmentation || options.semantic == ImageSemantic::ObjectIdSegmentation) && - getPixelFormat() == PixelFormat::GREY16 && targetPixelFormat == PixelFormat::RGB8) { - init(normalizedFrame, targetPixelFormat, getWidth(), getHeight()); + getPixelFormat() == PixelFormat::GREY16 && normalizedPixelFormat == PixelFormat::RGB8) { + outNormalizedFrame.init(normalizedPixelFormat, getWidth(), getHeight()); bool classSegmentation = options.semantic == ImageSemantic::ObjectClassSegmentation; const vector& colors = classSegmentation ? getGetObjectClassSegmentationColors() : getGetObjectIdSegmentationColors(); @@ -562,10 +542,10 @@ bool PixelFrame::normalizeFrame( unordered_set usedColors; uint16_t lastColor = 0xffff; uint32_t srcStride = getStride(); - uint32_t dstStride = normalizedFrame->getStride(); + uint32_t dstStride = outNormalizedFrame.getStride(); for (uint32_t h = 0; h < getHeight(); ++h) { const uint16_t* srcLine = data(srcStride * h); - RGBColor* dstLine = normalizedFrame->data(dstStride * h); + RGBColor* dstLine = outNormalizedFrame.data(dstStride * h); for (uint32_t w = 0; w < getWidth(); ++w) { uint16_t color = srcLine[w]; dstLine[w] = colors[color]; @@ -583,10 +563,12 @@ bool PixelFrame::normalizeFrame( return true; } } - if (normalizeToPixelFormat(normalizedFrame, targetPixelFormat, options)) { + if (normalizeToPixelFormat(outNormalizedFrame, normalizedPixelFormat, options)) { return true; } PixelFormat format = srcFormat; + uint16_t bitsToShift = 0; + uint32_t componentCount = 0; switch (srcFormat) { case PixelFormat::YUV_I420_SPLIT: case PixelFormat::YUV_420_NV21: @@ -595,9 +577,9 @@ bool PixelFrame::normalizeFrame( const uint32_t width = imageSpec_.getWidth(); const uint32_t height = imageSpec_.getHeight(); const uint32_t stride = imageSpec_.getStride(); - init(normalizedFrame, PixelFormat::GREY8, width, height); + outNormalizedFrame.init(PixelFormat::GREY8, width, height); const uint8_t* src = rdata(); - uint8_t* dst = normalizedFrame->wdata(); + uint8_t* dst = outNormalizedFrame.wdata(); for (uint32_t line = 0; line < height; line++) { memcpy(dst, src, width); src += stride; @@ -691,11 +673,11 @@ bool PixelFrame::normalizeFrame( if (format == srcFormat) { return false; // no conversion needed or supported } - init(normalizedFrame, format, getWidth(), getHeight()); + outNormalizedFrame.init(format, getWidth(), getHeight()); if (srcFormat == PixelFormat::BGR8) { // swap R & B const uint8_t* srcPtr = rdata(); - uint8_t* outPtr = normalizedFrame->wdata(); + uint8_t* outPtr = outNormalizedFrame.wdata(); const uint32_t pixelCount = getWidth() * getHeight(); for (uint32_t i = 0; i < pixelCount; ++i) { outPtr[2] = srcPtr[0]; @@ -706,20 +688,20 @@ bool PixelFrame::normalizeFrame( } } else if (srcFormat == PixelFormat::RGB32F) { // normalize float pixels to rgb8 - normalizeRGBXfloatToRGB8(rdata(), normalizedFrame->wdata(), getWidth() * getHeight(), 3); + normalizeRGBXfloatToRGB8(rdata(), outNormalizedFrame.wdata(), getWidth() * getHeight(), 3); } else if (srcFormat == PixelFormat::RGBA32F) { // normalize float pixels to rgb8, drop alpha channel - normalizeRGBXfloatToRGB8(rdata(), normalizedFrame->wdata(), getWidth() * getHeight(), 4); + normalizeRGBXfloatToRGB8(rdata(), outNormalizedFrame.wdata(), getWidth() * getHeight(), 4); } else if (srcFormat == PixelFormat::DEPTH32F) { // normalize float pixels to grey8 - normalizeBuffer(rdata(), normalizedFrame->wdata(), getWidth() * getHeight()); + normalizeBuffer(rdata(), outNormalizedFrame.wdata(), getWidth() * getHeight()); } else if (srcFormat == PixelFormat::SCALAR64F) { // normalize double pixels to grey8 - normalizeBuffer(rdata(), normalizedFrame->wdata(), getWidth() * getHeight()); + normalizeBuffer(rdata(), outNormalizedFrame.wdata(), getWidth() * getHeight()); } else if (srcFormat == PixelFormat::BAYER8_RGGB) { // display as grey8(copy) for now const uint8_t* srcPtr = rdata(); - uint8_t* outPtr = normalizedFrame->wdata(); + uint8_t* outPtr = outNormalizedFrame.wdata(); const uint32_t pixelCount = getWidth() * getHeight() * componentCount; for (uint32_t i = 0; i < pixelCount; ++i) { outPtr[i] = srcPtr[i]; @@ -730,10 +712,10 @@ bool PixelFrame::normalizeFrame( if (format == PixelFormat::GREY16) { // Convert from RAW10 to GREY10 directly into the output buffer if (!convertRaw10ToGrey10( - normalizedFrame->wdata(), rdata(), getWidth(), getHeight(), getStride())) { + outNormalizedFrame.wdata(), rdata(), getWidth(), getHeight(), getStride())) { return false; } - uint16_t* ptr = normalizedFrame->data(); + uint16_t* ptr = outNormalizedFrame.data(); const uint32_t pixelCount = getWidth() * getHeight() * componentCount; for (uint32_t i = 0; i < pixelCount; ++i) { ptr[i] <<= bitsToShift; @@ -743,8 +725,8 @@ bool PixelFrame::normalizeFrame( // convert to GREY8 by copying 4 bytes of msb data, and dropping the 5th of lsb data... const uint8_t* srcPtr = rdata(); const size_t srcStride = getStride(); - uint8_t* outPtr = normalizedFrame->wdata(); - const size_t outStride = normalizedFrame->getStride(); + uint8_t* outPtr = outNormalizedFrame.wdata(); + const size_t outStride = outNormalizedFrame.getStride(); const uint32_t width = getWidth(); for (uint32_t h = 0; h < getHeight(); h++, srcPtr += srcStride, outPtr += outStride) { const uint8_t* lineSrcPtr = srcPtr; @@ -765,8 +747,8 @@ bool PixelFrame::normalizeFrame( // This is a placeholder implementation that simply writes out the source data in R, G and B. const uint8_t* srcPtr = rdata(); const size_t srcStride = getStride(); - uint8_t* outPtr = normalizedFrame->wdata(); - const size_t outStride = normalizedFrame->getStride(); + uint8_t* outPtr = outNormalizedFrame.wdata(); + const size_t outStride = outNormalizedFrame.getStride(); const uint32_t width = getWidth(); for (uint32_t h = 0; h < getHeight(); h++, srcPtr += srcStride, outPtr += outStride) { const uint8_t* lineSrcPtr = srcPtr; @@ -781,8 +763,8 @@ bool PixelFrame::normalizeFrame( // Unoptimized default version of YUY2 to RGB8 conversion const uint8_t* srcPtr = rdata(); const size_t srcStride = getStride(); - uint8_t* outPtr = normalizedFrame->wdata(); - const size_t outStride = normalizedFrame->getStride(); + uint8_t* outPtr = outNormalizedFrame.wdata(); + const size_t outStride = outNormalizedFrame.getStride(); const uint32_t width = getWidth(); for (uint32_t h = 0; h < getHeight(); h++, srcPtr += srcStride, outPtr += outStride) { const uint8_t* lineSrcPtr = srcPtr; @@ -807,15 +789,15 @@ bool PixelFrame::normalizeFrame( } else if (format == PixelFormat::GREY16 && bitsToShift > 0) { // 12/10 bit pixel scaling to 16 bit const uint16_t* srcPtr = data(); - uint16_t* outPtr = normalizedFrame->data(); + uint16_t* outPtr = outNormalizedFrame.data(); const uint32_t pixelCount = getWidth() * getHeight() * componentCount; for (uint32_t i = 0; i < pixelCount; ++i) { outPtr[i] = static_cast(srcPtr[i] << bitsToShift); } - } else if (XR_VERIFY(this->size() == 2 * normalizedFrame->size())) { + } else if (XR_VERIFY(this->size() == 2 * outNormalizedFrame.size())) { // 16/12/10 bit pixel reduction to 8 bit const uint16_t* srcPtr = data(); - uint8_t* outPtr = normalizedFrame->wdata(); + uint8_t* outPtr = outNormalizedFrame.wdata(); const uint32_t pixelCount = getWidth() * getHeight() * componentCount; for (uint32_t i = 0; i < pixelCount; ++i) { outPtr[i] = (srcPtr[i] >> bitsToShift) & 0xFF; diff --git a/vrs/utils/PixelFrame.h b/vrs/utils/PixelFrame.h index 9fc66f12..2baf4321 100644 --- a/vrs/utils/PixelFrame.h +++ b/vrs/utils/PixelFrame.h @@ -85,21 +85,32 @@ class PixelFrame { PixelFrame() = default; explicit PixelFrame(const ImageContentBlockSpec& spec); PixelFrame(const ImageContentBlockSpec& spec, vector&& frameBytes); - PixelFrame(PixelFormat pf, uint32_t w, uint32_t h, uint32_t stride = 0) - : PixelFrame(ImageContentBlockSpec(pf, w, h, stride)) {} + PixelFrame(PixelFormat pf, uint32_t w, uint32_t h, uint32_t stride = 0); void init(const ImageContentBlockSpec& spec); void init(const ImageContentBlockSpec& spec, vector&& frameBytes); void init(PixelFormat pf, uint32_t w, uint32_t h, uint32_t stride = 0, uint32_t stride2 = 0); - static void init(shared_ptr& inOutFrame, const ImageContentBlockSpec& spec); - static inline void init( - shared_ptr& inOutFrame, - PixelFormat pf, - uint32_t w, - uint32_t h, - uint32_t stride = 0) { - init(inOutFrame, {pf, w, h, stride}); + // helpers to make sure the unique_ptr or shared_ptr is not nullptr + // and makes sure the frame is initialized with the given spec. + template + static inline PixelFrame& init(T& inOutFramePtr, const ImageContentBlockSpec& spec) { + if (!inOutFramePtr) { + inOutFramePtr.reset(new PixelFrame(spec)); + } else { + inOutFramePtr->init(spec); + } + return *inOutFramePtr; + } + template + static inline PixelFrame& + init(T& inOutFramePtr, PixelFormat pf, uint32_t w, uint32_t h, uint32_t stride = 0) { + if (!inOutFramePtr) { + inOutFramePtr.reset(new PixelFrame(pf, w, h, stride)); + } else { + inOutFramePtr->init(pf, w, h, stride); + } + return *inOutFramePtr; } #ifdef QOBJECT_H @@ -174,14 +185,18 @@ class PixelFrame { /// Read a RAW, PNG, or JPEG encoded frame into the internal buffer. /// @return True if the frame type is supported & the frame was read. - static bool - readFrame(shared_ptr& frame, RecordReader* reader, const ContentBlock& cb); + template + static inline bool readFrame(T& framePtr, RecordReader* reader, const ContentBlock& cb) { + return init(framePtr, cb.image()).readFrame(reader, cb); + } bool readFrame(RecordReader* reader, const ContentBlock& cb); /// Read a record's image data, merely reading the disk data without any decompression. /// The resulting PixelFrame will have an unmodified ImageFormat (raw, jpg, png, jxl, video). - static bool - readDiskImageData(shared_ptr& frame, RecordReader* reader, const ContentBlock& cb); + template + static inline bool readDiskImageData(T& framePtr, RecordReader* reader, const ContentBlock& cb) { + return init(framePtr, cb.image()).readDiskImageData(reader, cb); + } bool readDiskImageData(RecordReader* reader, const ContentBlock& cb); /// From any ImageFormat, decompress the image to ImageFormat::RAW if necessary. @@ -191,16 +206,16 @@ class PixelFrame { /// Read a RAW frame into the internal buffer. /// @return True if the frame type is supported & the frame was read. + template + static inline bool + readRawFrame(T& framePtr, RecordReader* reader, const ImageContentBlockSpec& inputImageSpec) { + return init(framePtr, inputImageSpec).readRawFrame(reader, inputImageSpec); + } bool readRawFrame(RecordReader* reader, const ImageContentBlockSpec& inputImageSpec); /// Decode compressed image data, except for video codec compression. bool readCompressedFrame(const vector& pixels, ImageFormat imageFormat); - static bool readRawFrame( - shared_ptr& frame, - RecordReader* reader, - const ImageContentBlockSpec& inputImageSpec); - /// Read a JPEG encoded frame into the internal buffer. /// @return True if the frame type is supported & the frame was read. bool readJpegFrame(RecordReader* reader, uint32_t sizeBytes); @@ -215,8 +230,10 @@ class PixelFrame { /// @return True if the frame type is supported & the frame was read. bool readJpegFrameFromFile(const string& path, bool decodePixels = true); - static bool - readJpegFrame(shared_ptr& frame, RecordReader* reader, uint32_t sizeBytes); + template + static inline bool readJpegFrame(T& framePtr, RecordReader* reader, uint32_t sizeBytes) { + return make(framePtr).readJpegFrame(reader, sizeBytes); + } /// Compress pixel frame to jpg. Supports ImageFormat::RAW and PixelFormat::RGB8 or GREY8 only. /// @param outBuffer: on exit, the jpg payload which can be saved as a .jpg file @@ -293,7 +310,10 @@ class PixelFrame { /// @return True if the frame type is supported & the frame was read. bool readPngFrame(const vector& pngBuffer, bool decodePixels = true); - static bool readPngFrame(shared_ptr& frame, RecordReader* reader, uint32_t sizeBytes); + template + static inline bool readPngFrame(T& framePtr, RecordReader* reader, uint32_t sizeBytes) { + return make(framePtr).readPngFrame(reader, sizeBytes); + } /// Save image as PNG /// @param path: path of the file to write, if no outBuffer is provided @@ -324,10 +344,22 @@ class PixelFrame { /// Convert the internal frame to a simpler format, if necessary. /// Returns false if the frame could not be converted, or the format doesn't need conversion. bool normalizeFrame( - shared_ptr& normalizedFrame, + shared_ptr& outNormalizedFrame, bool grey16supported, const NormalizeOptions& options = {}) const; + /// Convert the internal frame to a simpler format, if necessary. + /// Returns false if the frame could not be converted, or the format doesn't need conversion. + /// @param outNormalizedFrame: on exit, the normalized frame. Input value is ignored. + /// @param grey16supported: true to allow PixelFormat::GREY16. + /// @param normalizedPixelFormat: the pixel format to target. Expects GREY8, GREY16 and RGB8. + /// Only valid for what getNormalizedPixelFormat() returns! + bool normalizeFrame( + PixelFrame& outNormalizedFrame, + bool grey16supported, + const NormalizeOptions& options = {}, + PixelFormat normalizedPixelFormat = PixelFormat::UNDEFINED) const; + static PixelFormat getNormalizedPixelFormat( PixelFormat sourcePixelFormat, bool grey16supported, @@ -374,14 +406,24 @@ class PixelFrame { /// Clear the segmentation colors seen so far (loading a new file?) static void clearSegmentationColors(); + // Helper to make sure a unique_ptr or shared_ptr is set to a valid PixelFrame, + // but doesn't guarantee the frame is initialized to any particular state. + template + static inline PixelFrame& make(T& inOutFramePtr) { + if (!inOutFramePtr) { + inOutFramePtr.reset(new PixelFrame()); + } + return *inOutFramePtr; + } + private: /// Conversion from an external buffer - /// @param convertedFrame: frame to convert to. May not be allocated yet. + /// @param outNormalizedFrame: on exit, converted frame when returning true. /// @param targetPixelFormat: pixel format to target. Expect GREY8, GREY16 and RGB8 to work. /// @return True if the conversion happened, otherwise, the buffer is left in an undefined state. /// Note that the actual conversion format is set in imageSpec_, and it could be different... bool normalizeToPixelFormat( - shared_ptr& outFrame, + PixelFrame& outNormalizedFrame, PixelFormat targetPixelFormat, const NormalizeOptions& options) const; diff --git a/vrs/utils/PixelFrameJpeg.cpp b/vrs/utils/PixelFrameJpeg.cpp index 6c8dceca..cfa4f85a 100644 --- a/vrs/utils/PixelFrameJpeg.cpp +++ b/vrs/utils/PixelFrameJpeg.cpp @@ -91,16 +91,6 @@ bool PixelFrame::readJpegFrameFromFile(const std::string& path, bool decodePixel return success; } -bool PixelFrame::readJpegFrame( - shared_ptr& frame, - RecordReader* reader, - uint32_t sizeBytes) { - if (!frame) { - frame = make_shared(); - } - return frame->readJpegFrame(reader, sizeBytes); -} - bool PixelFrame::jpgCompress(vector& outBuffer, uint32_t quality) { return jpgCompress(imageSpec_, frameBytes_, outBuffer, quality); } diff --git a/vrs/utils/PixelFramePng.cpp b/vrs/utils/PixelFramePng.cpp index b58936b5..c333677f 100644 --- a/vrs/utils/PixelFramePng.cpp +++ b/vrs/utils/PixelFramePng.cpp @@ -394,16 +394,6 @@ bool PixelFrame::readPngFrame(const vector& pngBuffer, bool decodePixel return true; } -bool PixelFrame::readPngFrame( - shared_ptr& frame, - RecordReader* reader, - uint32_t sizeBytes) { - if (!frame) { - frame = make_shared(); - } - return frame->readPngFrame(reader, sizeBytes); -} - namespace { atomic sAllocSize{128 * 1024};