438 lines
20 KiB
C++
438 lines
20 KiB
C++
|
/**
|
||
|
* @file Image.hpp
|
||
|
*
|
||
|
* Purpose Class declaration of Image class
|
||
|
*
|
||
|
* Authors Dinesh Iyer
|
||
|
*
|
||
|
* Copyright 2019-2021 MathWorks, Inc.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#ifndef imagesdatatypesimage_header
|
||
|
#define imagesdatatypesimage_header
|
||
|
|
||
|
#include "datatypes_util.hpp"
|
||
|
#include "ImageDefs.hpp"
|
||
|
#include "ImageMetadata.hpp"
|
||
|
|
||
|
#include <memory>
|
||
|
|
||
|
namespace images
|
||
|
{
|
||
|
namespace datatypes
|
||
|
{
|
||
|
class IMAGESDATATYPESIMAGE_API Image
|
||
|
{
|
||
|
private:
|
||
|
// ------------------------------------------------
|
||
|
// Typedef's used
|
||
|
// ------------------------------------------------
|
||
|
typedef std::default_delete<BufferType[]> RawDataDeleter;
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Interface of class that manages the lifetime of
|
||
|
// the underlying data
|
||
|
// ------------------------------------------------
|
||
|
class IDataUnderlyingMgr
|
||
|
{
|
||
|
public:
|
||
|
virtual ~IDataUnderlyingMgr() noexcept {}
|
||
|
|
||
|
// Gain access to the raw data buffer
|
||
|
virtual BufferType* getDataUnderlying() const = 0;
|
||
|
|
||
|
// Give-up ownership of the underlying data buffer
|
||
|
virtual BufferType* releaseDataUnderlying() = 0;
|
||
|
};
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Takes ownership of the underling data.
|
||
|
// Memory for underlying data is de-allocated if no
|
||
|
// references to it remain.
|
||
|
// ------------------------------------------------
|
||
|
class DataUnderlyingMgrOwn : public IDataUnderlyingMgr
|
||
|
{
|
||
|
public:
|
||
|
DataUnderlyingMgrOwn(BufferType* src)
|
||
|
:fDataUnderlying(nullptr)
|
||
|
{
|
||
|
if( src == nullptr )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
fDataUnderlying.reset(src, RawDataDeleter());
|
||
|
}
|
||
|
|
||
|
~DataUnderlyingMgrOwn() noexcept {}
|
||
|
|
||
|
BufferType* getDataUnderlying() const override { return fDataUnderlying.get(); }
|
||
|
|
||
|
BufferType* releaseDataUnderlying() override
|
||
|
{
|
||
|
// @TODO
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
std::shared_ptr<BufferType> fDataUnderlying;
|
||
|
};
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Uses the pointer provided as the underlying data
|
||
|
// Does not dellocate memory.
|
||
|
// ------------------------------------------------
|
||
|
class DataUnderlyingMgrUseOnly : public IDataUnderlyingMgr
|
||
|
{
|
||
|
public:
|
||
|
DataUnderlyingMgrUseOnly(BufferType* src)
|
||
|
:fDataUnderlying(src)
|
||
|
{}
|
||
|
|
||
|
~DataUnderlyingMgrUseOnly() noexcept {}
|
||
|
|
||
|
BufferType* getDataUnderlying() const override { return fDataUnderlying; }
|
||
|
|
||
|
BufferType* releaseDataUnderlying() override
|
||
|
{
|
||
|
auto tempBuffer = fDataUnderlying;
|
||
|
fDataUnderlying = nullptr;
|
||
|
return tempBuffer;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
BufferType* fDataUnderlying;
|
||
|
};
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Takes ownership of the underling data.
|
||
|
// Supports relinquishing the underlying data without
|
||
|
// doing a deep copy of the data
|
||
|
// ------------------------------------------------
|
||
|
class DataUnderlyingMgrSingleUse : public IDataUnderlyingMgr
|
||
|
{
|
||
|
public:
|
||
|
DataUnderlyingMgrSingleUse(BufferType* src)
|
||
|
: fDataUnderlying( src, RawDataDeleter() )
|
||
|
{}
|
||
|
|
||
|
~DataUnderlyingMgrSingleUse() noexcept {}
|
||
|
|
||
|
BufferType* getDataUnderlying() const override { return fDataUnderlying.get(); }
|
||
|
|
||
|
BufferType* releaseDataUnderlying() override { return fDataUnderlying.release(); }
|
||
|
|
||
|
private:
|
||
|
std::unique_ptr<BufferType, RawDataDeleter> fDataUnderlying;
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
// ------------------------------------------------
|
||
|
// Life time
|
||
|
// ------------------------------------------------
|
||
|
|
||
|
/**
|
||
|
* Default constructor
|
||
|
* This represent an empty image
|
||
|
* Also required by MATLAB Coder
|
||
|
*/
|
||
|
Image();
|
||
|
|
||
|
/**
|
||
|
* Construct an image.
|
||
|
*
|
||
|
* Allow users to create an image with certain optimizations that
|
||
|
* can avoid unnecessary copies of the underlying data.
|
||
|
*
|
||
|
* The image created with RelinquishDataUnderlying::YES can be copied and assigned.
|
||
|
* However, when copied or assigned, the target image will contain a deep copy
|
||
|
* of the data of the source image. For IM1 created with this flag = TRUE
|
||
|
* Image im2 = im1; % IM2 will have its own copy of the data buffer i.e
|
||
|
* deep copy will be done
|
||
|
*
|
||
|
* @param - rows - Number of rows in the image
|
||
|
* @param - columns - Number of columns in the image
|
||
|
* @param - numChannels - Number of channels in the image
|
||
|
* @param - dtype - Datatype of the underlying data
|
||
|
* @param - format - ColorFormat of the image data
|
||
|
* @param - layout - Layout of the image data
|
||
|
* @param - relinquish - YES indicates the instance can give up its
|
||
|
* underlying data buffer without a data copy
|
||
|
*/
|
||
|
Image( DimType rows,
|
||
|
DimType columns,
|
||
|
DimType numChannels,
|
||
|
ClassUnderlying dtype,
|
||
|
ColorFormat format,
|
||
|
Layout layout,
|
||
|
RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO );
|
||
|
|
||
|
/**
|
||
|
* Construct an image.
|
||
|
*
|
||
|
* Allow users to create an image with certain optimizations that
|
||
|
* can avoid unnecessary copies of the underlying data.
|
||
|
*
|
||
|
* The image created with RelinquishDataUnderlying::YES can be copied and assigned.
|
||
|
* However, when copied or assigned, the target image will contain a deep copy
|
||
|
* of the data of the source image. For IM1 created with this flag = TRUE
|
||
|
* Image im2 = im1; % IM2 will have its own copy of the data buffer i.e
|
||
|
* deep copy will be done
|
||
|
*
|
||
|
* @param - metadata - Instance of ImageMetadata class
|
||
|
* @param - relinquish - YES indicates the instance can give up its
|
||
|
* underlying data buffer without a data copy
|
||
|
*/
|
||
|
Image( const ImageMetadata& metadata,
|
||
|
RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO );
|
||
|
|
||
|
/**
|
||
|
* Construct an image.
|
||
|
*
|
||
|
* Allow users to create an image by specifying the dimensions, datatype,
|
||
|
* memory arrangement and also provide the buffer containing the image
|
||
|
* data
|
||
|
*
|
||
|
*
|
||
|
* @param - src - Source buffer containing data. Assumed that src has
|
||
|
* sufficient number of bytes to store the entire image
|
||
|
* data
|
||
|
* @param - rows - Number of rows in the image
|
||
|
* @param - columns - Number of columns in the image
|
||
|
* @param - numChannels - Number of channels in the image
|
||
|
* @param - dtype - Datatype of the underlying data
|
||
|
* @param - format - ColorFormat of the image data
|
||
|
* @param - layout - Layout of the image data
|
||
|
* @param - mgmt - Determine how the input source of data needs to be
|
||
|
* handled by the image. By default, a deep copy of the
|
||
|
* source is made.
|
||
|
* @param - relinquish - YES indicates the instance can give up its
|
||
|
* underlying data buffer without a data copy
|
||
|
*/
|
||
|
Image( void* src,
|
||
|
DimType rows,
|
||
|
DimType columns,
|
||
|
DimType numChannels,
|
||
|
ClassUnderlying dtype,
|
||
|
ColorFormat format,
|
||
|
Layout layout,
|
||
|
SourceMgmt mgmt = SourceMgmt::COPY_SRC,
|
||
|
RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO );
|
||
|
|
||
|
/**
|
||
|
* Construct an image.
|
||
|
*
|
||
|
* Allow users to create an image by specifying the dimensions, datatype,
|
||
|
* memory arrangement and also provide the buffer containing the image
|
||
|
* data
|
||
|
*
|
||
|
*
|
||
|
* @param - src - Source buffer containing data. Assumed that src has
|
||
|
* sufficient number of bytes to store the entire image
|
||
|
* data
|
||
|
* @param - rows - Number of rows in the image
|
||
|
* @param - columns - Number of columns in the image
|
||
|
* @param - numChannels - Number of channels in the image
|
||
|
* @param - dtype - Datatype of the underlying data
|
||
|
* @param - format - ColorFormat of the image data
|
||
|
* @param - layout - Layout of the image data
|
||
|
*/
|
||
|
Image( void const* dataUnderlying,
|
||
|
DimType rows,
|
||
|
DimType columns,
|
||
|
DimType numChannels,
|
||
|
ClassUnderlying dtype,
|
||
|
ColorFormat format,
|
||
|
Layout layout );
|
||
|
|
||
|
/**
|
||
|
* Construct an image.
|
||
|
*
|
||
|
* Allow users to create an image by specifying the dimensions, datatype,
|
||
|
* memory arrangement and also provide the buffer containing the image
|
||
|
* data
|
||
|
*
|
||
|
*
|
||
|
* @param - src - Source buffer containing data. Assumed that src has
|
||
|
* sufficient number of bytes to store the entire image
|
||
|
* data
|
||
|
* @param - metadata - Instance of ImageMetadata class
|
||
|
* @param - mgmt - Determine how the input source of data needs to be
|
||
|
* handled by the image. By default, a deep copy of the
|
||
|
* source is made.
|
||
|
* @param - relinquish - YES indicates the instance can give up its
|
||
|
* underlying data buffer without a data copy
|
||
|
*/
|
||
|
Image( void* src,
|
||
|
const ImageMetadata& metadata,
|
||
|
SourceMgmt mgmt = SourceMgmt::COPY_SRC,
|
||
|
RelinquishDataUnderlying relinquish = RelinquishDataUnderlying::NO );
|
||
|
|
||
|
// Not really necessary. But adding for completeness
|
||
|
~Image() noexcept;
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Copy and assignment operations
|
||
|
// ------------------------------------------------
|
||
|
|
||
|
/**
|
||
|
* Perform a shallow copy of the image
|
||
|
*/
|
||
|
Image(const Image& otherImage);
|
||
|
|
||
|
Image& operator=(const Image& otherImage);
|
||
|
|
||
|
// Move operations
|
||
|
Image(Image&& otherImage) noexcept;
|
||
|
|
||
|
Image& operator=(Image&& otherImage) noexcept;
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Public methods
|
||
|
// ------------------------------------------------
|
||
|
/**
|
||
|
* Creates a deep copy i.e. allocates a new memory buffer
|
||
|
* and copies data into it.
|
||
|
*
|
||
|
* @param - relinquish - YES indicates the instance can give up its
|
||
|
* underlying data buffer without a data copy
|
||
|
* @return - Image
|
||
|
*/
|
||
|
Image deepCopy(RelinquishDataUnderlying relinquish =
|
||
|
RelinquishDataUnderlying::NO) const;
|
||
|
|
||
|
/**
|
||
|
* Access the underlying data buffer
|
||
|
*
|
||
|
* Using the data buffer, the user can modify the contents of the data.
|
||
|
* As the internal buffer is a shared resource, changing it can have
|
||
|
* indeterminate consequences.
|
||
|
* So, if the user wants to modify the buffer, the recommendation is
|
||
|
* to create a deepCopy() FIRST.
|
||
|
*
|
||
|
* @param - none
|
||
|
* @return - Pointer of type BufferType
|
||
|
*/
|
||
|
BufferType* getDataUnderlying() const;
|
||
|
|
||
|
BufferType* getUniqueDataUnderlying();
|
||
|
|
||
|
/**
|
||
|
* Query whether calling releaseDataUnderlying() makes a deep copy
|
||
|
* of the underlying data before passing it to the user
|
||
|
*
|
||
|
* This is necessary if the user wants to track when a memcpy is being
|
||
|
* done when working with Image instances. The table specified in this
|
||
|
* spec contains details.
|
||
|
*
|
||
|
* @param - none
|
||
|
* @return - bool. TRUE if a deep copy will be done when calling releaseDataUnderlying
|
||
|
*/
|
||
|
bool isDeepCopyOfDataOnRelease() const;
|
||
|
|
||
|
/**
|
||
|
* Give up ownership of the underlying data buffer
|
||
|
*
|
||
|
* This is useful if the user wants to pass the data to another
|
||
|
* object that can take ownership of data buffers.
|
||
|
|
||
|
* NOTE: Using the Image instance to access image data after calling
|
||
|
* this method have indeterminate behaviour.
|
||
|
*
|
||
|
* @param - none
|
||
|
* @return - Pointer of type BufferType
|
||
|
*/
|
||
|
BufferType* releaseDataUnderlying();
|
||
|
|
||
|
/**
|
||
|
* Resize the image
|
||
|
*
|
||
|
* This method "blows out" any valid image data that is held by the
|
||
|
* image instance and allocates a new buffer that has sufficient
|
||
|
* space to hold image data for the newer dimensions.
|
||
|
* This method allows changing only the number of rows and/or columns
|
||
|
* AND not the number of channels or any other properties.
|
||
|
*
|
||
|
* @param - newNumRows - Number of rows in the target image
|
||
|
* @param - newNumCols - Number of columns in the target image
|
||
|
* @return - none
|
||
|
*/
|
||
|
void resize(DimType newNumRows, DimType newNumCols);
|
||
|
|
||
|
size_t computeNumBytesInImage() const;
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Property Getters/Setters
|
||
|
// ------------------------------------------------
|
||
|
DimType getRows() const { return fMetadata.getRows(); }
|
||
|
DimType getColumns() const { return fMetadata.getColumns(); }
|
||
|
DimType getNumChannels() const { return fMetadata.getNumChannels(); }
|
||
|
|
||
|
ClassUnderlying getClassUnderlying() const { return fMetadata.getClassUnderlying(); }
|
||
|
ColorFormat getColorFormat() const { return fMetadata.getColorFormat(); }
|
||
|
Layout getLayout() const { return fMetadata.getLayout(); }
|
||
|
|
||
|
private:
|
||
|
// ------------------------------------------------
|
||
|
// Private Helper functions
|
||
|
// ------------------------------------------------
|
||
|
void copyProps(const Image& otherImage);
|
||
|
|
||
|
BufferType* allocateBufferForDataUnderlying() const;
|
||
|
|
||
|
static IDataUnderlyingMgr* createDataUnderlyingMgr( BufferType* src,
|
||
|
SourceMgmt mgmt = SourceMgmt::COPY_SRC,
|
||
|
RelinquishDataUnderlying relinquish =
|
||
|
RelinquishDataUnderlying::NO );
|
||
|
|
||
|
std::unique_ptr<BufferType, RawDataDeleter> copyRawData(const BufferType* src) const;
|
||
|
|
||
|
// ------------------------------------------------
|
||
|
// Properties
|
||
|
// ------------------------------------------------
|
||
|
ImageMetadata fMetadata;
|
||
|
|
||
|
// Instance of class that manages the raw image data
|
||
|
std::shared_ptr<IDataUnderlyingMgr> fDataUnderlyingMgr;
|
||
|
|
||
|
// Tracks whether the instance can relinquish underlying data
|
||
|
// without performing a deep copy.
|
||
|
RelinquishDataUnderlying fRelinquish;
|
||
|
};
|
||
|
|
||
|
inline Image makeImage(void const* dataUnderlying,
|
||
|
DimType rows,
|
||
|
DimType columns,
|
||
|
DimType numChannels,
|
||
|
ClassUnderlying classUnderlying,
|
||
|
ColorFormat colorFormat,
|
||
|
Layout layout) {
|
||
|
return Image(
|
||
|
dataUnderlying,
|
||
|
rows,
|
||
|
columns,
|
||
|
numChannels,
|
||
|
classUnderlying,
|
||
|
colorFormat,
|
||
|
layout);
|
||
|
}
|
||
|
|
||
|
inline Image makeImage(DimType rows,
|
||
|
DimType columns,
|
||
|
DimType numChannels,
|
||
|
ClassUnderlying classUnderlying,
|
||
|
ColorFormat colorFormat,
|
||
|
Layout layout) {
|
||
|
return Image(
|
||
|
rows,
|
||
|
columns,
|
||
|
numChannels,
|
||
|
classUnderlying,
|
||
|
colorFormat,
|
||
|
layout);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // imagesDatatypesimage_header
|