Program Listing for File frame.hpp

Return to documentation for file (include/libcaercpp/events/frame.hpp)

#ifndef LIBCAER_EVENTS_FRAME_HPP_
#define LIBCAER_EVENTS_FRAME_HPP_

#include "../../libcaer/events/frame.h"

#include "../../libcaer/frame_utils.h"
#include "common.hpp"

// Separate define for getOpenCVMat() from main LIBCAER_HAVE_OPENCV,
// which is for the frame utils support. LIBCAER_FRAMECPP_OPENCV_INSTALLED
// is only for this one header here and the getOpenCVMat() functions, in
// which case you need to have OpenCV installed for the application
// using this only, not necessarily for libcaer.
#ifndef LIBCAER_FRAMECPP_OPENCV_INSTALLED
#   define LIBCAER_FRAMECPP_OPENCV_INSTALLED LIBCAER_HAVE_OPENCV
#endif

#if defined(LIBCAER_FRAMECPP_OPENCV_INSTALLED) && LIBCAER_FRAMECPP_OPENCV_INSTALLED == 1

#   include <opencv2/core.hpp>
#   include <opencv2/core/utility.hpp>

#endif

namespace libcaer {
namespace events {

struct FrameEvent : public caer_frame_event {
    FrameEvent()                                 = default;
    FrameEvent(const FrameEvent &rhs)            = delete;
    FrameEvent &operator=(const FrameEvent &rhs) = delete;
    FrameEvent(FrameEvent &&rhs)                 = delete;
    FrameEvent &operator=(FrameEvent &&rhs)      = delete;

    enum class colorChannels {
        GRAYSCALE = 1,
        RGB       = 3,
        RGBA      = 4,
    };

    enum class colorFilter {
        MONO = 0,
        RGBG = 1,
        GRGB = 2,
        GBGR = 3,
        BGRG = 4,
        RGBW = 5,
        GRWB = 6,
        WBGR = 7,
        BWRG = 8,
    };

    int32_t getTSStartOfFrame() const noexcept {
        return (caerFrameEventGetTSStartOfFrame(this));
    }

    int64_t getTSStartOfFrame64(const EventPacket &packet) const noexcept {
        return (caerFrameEventGetTSStartOfFrame64(
            this, reinterpret_cast<caerFrameEventPacketConst>(packet.getHeaderPointer())));
    }

    void setTSStartOfFrame(int32_t ts) {
        if (ts < 0) {
            throw std::invalid_argument("Negative timestamp not allowed.");
        }

        caerFrameEventSetTSStartOfFrame(this, ts);
    }

    int32_t getTSEndOfFrame() const noexcept {
        return (caerFrameEventGetTSEndOfFrame(this));
    }

    int64_t getTSEndOfFrame64(const EventPacket &packet) const noexcept {
        return (caerFrameEventGetTSEndOfFrame64(
            this, reinterpret_cast<caerFrameEventPacketConst>(packet.getHeaderPointer())));
    }

    void setTSEndOfFrame(int32_t ts) {
        if (ts < 0) {
            throw std::invalid_argument("Negative timestamp not allowed.");
        }

        caerFrameEventSetTSEndOfFrame(this, ts);
    }

    int32_t getTSStartOfExposure() const noexcept {
        return (caerFrameEventGetTSStartOfExposure(this));
    }

    int64_t getTSStartOfExposure64(const EventPacket &packet) const noexcept {
        return (caerFrameEventGetTSStartOfExposure64(
            this, reinterpret_cast<caerFrameEventPacketConst>(packet.getHeaderPointer())));
    }

    void setTSStartOfExposure(int32_t ts) {
        if (ts < 0) {
            throw std::invalid_argument("Negative timestamp not allowed.");
        }

        caerFrameEventSetTSStartOfExposure(this, ts);
    }

    int32_t getTSEndOfExposure() const noexcept {
        return (caerFrameEventGetTSEndOfExposure(this));
    }

    int64_t getTSEndOfExposure64(const EventPacket &packet) const noexcept {
        return (caerFrameEventGetTSEndOfExposure64(
            this, reinterpret_cast<caerFrameEventPacketConst>(packet.getHeaderPointer())));
    }

    void setTSEndOfExposure(int32_t ts) {
        if (ts < 0) {
            throw std::invalid_argument("Negative timestamp not allowed.");
        }

        caerFrameEventSetTSEndOfExposure(this, ts);
    }

    int32_t getTimestamp() const noexcept {
        return (caerFrameEventGetTimestamp(this));
    }

    int64_t getTimestamp64(const EventPacket &packet) const noexcept {
        return (
            caerFrameEventGetTimestamp64(this, reinterpret_cast<caerFrameEventPacketConst>(packet.getHeaderPointer())));
    }

    int32_t getExposureLength() const noexcept {
        return (caerFrameEventGetExposureLength(this));
    }

    bool isValid() const noexcept {
        return (caerFrameEventIsValid(this));
    }

    void validate(EventPacket &packet) noexcept {
        caerFrameEventValidate(this, reinterpret_cast<caerFrameEventPacket>(packet.getHeaderPointer()));
    }

    void invalidate(EventPacket &packet) noexcept {
        caerFrameEventInvalidate(this, reinterpret_cast<caerFrameEventPacket>(packet.getHeaderPointer()));
    }

    uint8_t getROIIdentifier() const noexcept {
        return (caerFrameEventGetROIIdentifier(this));
    }

    void setROIIdentifier(uint8_t roiIdent) noexcept {
        caerFrameEventSetROIIdentifier(this, roiIdent);
    }

    colorFilter getColorFilter() const noexcept {
        return (static_cast<colorFilter>(caerFrameEventGetColorFilter(this)));
    }

    void setColorFilter(colorFilter cFilter) noexcept {
        caerFrameEventSetColorFilter(this, static_cast<enum caer_frame_event_color_filter>(
                                               static_cast<typename std::underlying_type<colorFilter>::type>(cFilter)));
    }

    int32_t getLengthX() const noexcept {
        return (caerFrameEventGetLengthX(this));
    }

    int32_t getLengthY() const noexcept {
        return (caerFrameEventGetLengthY(this));
    }

    colorChannels getChannelNumber() const noexcept {
        return (static_cast<colorChannels>(caerFrameEventGetChannelNumber(this)));
    }

    void setLengthXLengthYChannelNumber(int32_t lenX, int32_t lenY, colorChannels cNumber, const EventPacket &packet) {
        // Verify lengths and color channels number don't exceed allocated space.
        enum caer_frame_event_color_channels cNumberEnum = static_cast<enum caer_frame_event_color_channels>(
            static_cast<typename std::underlying_type<colorChannels>::type>(cNumber));

        if (lenX <= 0 || lenY <= 0 || cNumberEnum <= 0) {
            throw std::invalid_argument("Negative lengths or channel number not allowed.");
        }

        size_t neededMemory = (sizeof(uint16_t) * static_cast<size_t>(lenX) * static_cast<size_t>(lenY) * cNumberEnum);

        if (neededMemory > caerFrameEventPacketGetPixelsSize(
                reinterpret_cast<caerFrameEventPacketConst>(packet.getHeaderPointer()))) {
            throw std::invalid_argument("Given values result in memory usage higher than allocated frame event size.");
        }

        caerFrameEventSetLengthXLengthYChannelNumber(
            this, lenX, lenY, cNumberEnum, reinterpret_cast<caerFrameEventPacketConst>(packet.getHeaderPointer()));
    }

    size_t getPixelsMaxIndex() const noexcept {
        return (caerFrameEventGetPixelsMaxIndex(this));
    }

    size_t getPixelsSize() const noexcept {
        return (caerFrameEventGetPixelsSize(this));
    }

    int32_t getPositionX() const noexcept {
        return (caerFrameEventGetPositionX(this));
    }

    void setPositionX(int32_t posX) noexcept {
        caerFrameEventSetPositionX(this, posX);
    }

    int32_t getPositionY() const noexcept {
        return (caerFrameEventGetPositionY(this));
    }

    void setPositionY(int32_t posY) noexcept {
        caerFrameEventSetPositionY(this, posY);
    }

    uint16_t getPixel(int32_t xAddress, int32_t yAddress) const {
        // Check frame bounds first.
        if (yAddress < 0 || yAddress >= caerFrameEventGetLengthY(this)) {
            throw std::invalid_argument("Invalid Y address.");
        }

        int32_t xLength = caerFrameEventGetLengthX(this);

        if (xAddress < 0 || xAddress >= xLength) {
            throw std::invalid_argument("Invalid X address.");
        }

        // Get pixel value at specified position.
        return (le16toh(this->pixels[(yAddress * xLength) + xAddress]));
    }

    void setPixel(int32_t xAddress, int32_t yAddress, uint16_t pixelValue) {
        // Check frame bounds first.
        if (yAddress < 0 || yAddress >= caerFrameEventGetLengthY(this)) {
            throw std::invalid_argument("Invalid Y address.");
        }

        int32_t xLength = caerFrameEventGetLengthX(this);

        if (xAddress < 0 || xAddress >= xLength) {
            throw std::invalid_argument("Invalid X address.");
        }

        // Set pixel value at specified position.
        this->pixels[(yAddress * xLength) + xAddress] = htole16(pixelValue);
    }

    uint16_t getPixel(int32_t xAddress, int32_t yAddress, uint8_t channel) const {
        // Check frame bounds first.
        if (yAddress < 0 || yAddress >= caerFrameEventGetLengthY(this)) {
            throw std::invalid_argument("Invalid Y address.");
        }

        int32_t xLength = caerFrameEventGetLengthX(this);

        if (xAddress < 0 || xAddress >= xLength) {
            throw std::invalid_argument("Invalid X address.");
        }

        uint8_t channelNumber = caerFrameEventGetChannelNumber(this);

        if (channel >= channelNumber) {
            throw std::invalid_argument("Invalid channel number.");
        }

        // Get pixel value at specified position.
        return (le16toh(this->pixels[(((yAddress * xLength) + xAddress) * channelNumber) + channel]));
    }

    void setPixel(int32_t xAddress, int32_t yAddress, uint8_t channel, uint16_t pixelValue) {
        // Check frame bounds first.
        if (yAddress < 0 || yAddress >= caerFrameEventGetLengthY(this)) {
            throw std::invalid_argument("Invalid Y address.");
        }

        int32_t xLength = caerFrameEventGetLengthX(this);

        if (xAddress < 0 || xAddress >= xLength) {
            throw std::invalid_argument("Invalid X address.");
        }

        uint8_t channelNumber = caerFrameEventGetChannelNumber(this);

        if (channel >= channelNumber) {
            throw std::invalid_argument("Invalid channel number.");
        }

        // Set pixel value at specified position.
        this->pixels[(((yAddress * xLength) + xAddress) * channelNumber) + channel] = htole16(pixelValue);
    }

    uint16_t getPixelUnsafe(int32_t xAddress, int32_t yAddress) const noexcept {
        // Get pixel value at specified position.
        return (le16toh(this->pixels[(yAddress * caerFrameEventGetLengthX(this)) + xAddress]));
    }

    void setPixelUnsafe(int32_t xAddress, int32_t yAddress, uint16_t pixelValue) noexcept {
        // Set pixel value at specified position.
        this->pixels[(yAddress * caerFrameEventGetLengthX(this)) + xAddress] = htole16(pixelValue);
    }

    uint16_t getPixelUnsafe(int32_t xAddress, int32_t yAddress, uint8_t channel) const noexcept {
        uint8_t channelNumber = caerFrameEventGetChannelNumber(this);
        // Get pixel value at specified position.
        return (le16toh(
            this->pixels[(((yAddress * caerFrameEventGetLengthX(this)) + xAddress) * channelNumber) + channel]));
    }

    void setPixelUnsafe(int32_t xAddress, int32_t yAddress, uint8_t channel, uint16_t pixelValue) noexcept {
        uint8_t channelNumber = caerFrameEventGetChannelNumber(this);
        // Set pixel value at specified position.
        this->pixels[(((yAddress * caerFrameEventGetLengthX(this)) + xAddress) * channelNumber) + channel]
            = htole16(pixelValue);
    }

    uint16_t *getPixelArrayUnsafe() noexcept {
        return (this->pixels);
    }

    const uint16_t *getPixelArrayUnsafe() const noexcept {
        return (this->pixels);
    }

#if defined(LIBCAER_FRAMECPP_OPENCV_INSTALLED) && LIBCAER_FRAMECPP_OPENCV_INSTALLED == 1

    cv::Mat getOpenCVMat() noexcept {
        const cv::Size frameSize(caerFrameEventGetLengthX(this), caerFrameEventGetLengthY(this));
        cv::Mat frameMat(
            frameSize, CV_16UC(caerFrameEventGetChannelNumber(this)), reinterpret_cast<void *>(this->pixels));
        return (frameMat);
    }

    const cv::Mat getOpenCVMat(bool copyPixels = true) const noexcept {
        const cv::Size frameSize(caerFrameEventGetLengthX(this), caerFrameEventGetLengthY(this));
        const cv::Mat frameMat(frameSize, CV_16UC(caerFrameEventGetChannelNumber(this)),
            reinterpret_cast<void *>(const_cast<uint16_t *>(this->pixels)));

        if (copyPixels) {
            return (frameMat.clone());
        }
        else {
            return (frameMat);
        }
    }

#endif
};

static_assert(std::is_standard_layout<FrameEvent>::value, "FrameEvent is not of standard layout.");

class FrameEventPacket : public EventPacketCommon<FrameEventPacket, FrameEvent> {
public:
    // Constructors.
    FrameEventPacket(size_type eventCapacity, int16_t eventSource, int32_t tsOverflow, int32_t maxLengthX,
        int32_t maxLengthY, int16_t maxChannelNumber) {
        constructorCheckCapacitySourceTSOverflow(eventCapacity, eventSource, tsOverflow);

        if (maxLengthX <= 0) {
            throw std::invalid_argument("Negative or zero maximum X length not allowed.");
        }
        if (maxLengthY <= 0) {
            throw std::invalid_argument("Negative or zero maximum Y length not allowed.");
        }
        if (maxChannelNumber <= 0) {
            throw std::invalid_argument("Negative or zero maximum number of channels not allowed.");
        }

        caerFrameEventPacket packet = caerFrameEventPacketAllocate(
            eventCapacity, eventSource, tsOverflow, maxLengthX, maxLengthY, maxChannelNumber);
        constructorCheckNullptr(packet);

        header        = &packet->packetHeader;
        isMemoryOwner = true; // Always owner on new allocation!
    }

    FrameEventPacket(size_type eventCapacity, int16_t eventSource, int32_t tsOverflow, int32_t maxNumPixels,
        int16_t maxChannelNumber) {
        constructorCheckCapacitySourceTSOverflow(eventCapacity, eventSource, tsOverflow);

        if (maxNumPixels <= 0) {
            throw std::invalid_argument("Negative or zero maximum number of pixels not allowed.");
        }
        if (maxChannelNumber <= 0) {
            throw std::invalid_argument("Negative or zero maximum number of channels not allowed.");
        }

        caerFrameEventPacket packet = caerFrameEventPacketAllocateNumPixels(
            eventCapacity, eventSource, tsOverflow, maxNumPixels, maxChannelNumber);
        constructorCheckNullptr(packet);

        header        = &packet->packetHeader;
        isMemoryOwner = true; // Always owner on new allocation!
    }

    FrameEventPacket(caerFrameEventPacket packet, bool takeMemoryOwnership = true) {
        constructorCheckNullptr(packet);

        constructorCheckEventType(&packet->packetHeader, FRAME_EVENT);

        header        = &packet->packetHeader;
        isMemoryOwner = takeMemoryOwnership;
    }

    FrameEventPacket(caerEventPacketHeader packetHeader, bool takeMemoryOwnership = true) {
        constructorCheckNullptr(packetHeader);

        constructorCheckEventType(packetHeader, FRAME_EVENT);

        header        = packetHeader;
        isMemoryOwner = takeMemoryOwnership;
    }

protected:
    // Event access methods.
    reference virtualGetEvent(size_type index) noexcept override {
        caerFrameEvent evtBase = caerFrameEventPacketGetEvent(reinterpret_cast<caerFrameEventPacket>(header), index);
        FrameEvent *evt        = static_cast<FrameEvent *>(evtBase);

        return (*evt);
    }

    const_reference virtualGetEvent(size_type index) const noexcept override {
        caerFrameEventConst evtBase
            = caerFrameEventPacketGetEventConst(reinterpret_cast<caerFrameEventPacketConst>(header), index);
        const FrameEvent *evt = static_cast<const FrameEvent *>(evtBase);

        return (*evt);
    }

public:
    size_t getPixelsSize() const noexcept {
        return (caerFrameEventPacketGetPixelsSize(reinterpret_cast<caerFrameEventPacketConst>(header)));
    }

    size_t getPixelsMaxIndex() const noexcept {
        return (caerFrameEventPacketGetPixelsMaxIndex(reinterpret_cast<caerFrameEventPacketConst>(header)));
    }

    enum class demosaicTypes {
        STANDARD = 0,
        TO_GRAY  = 1,
#if defined(LIBCAER_HAVE_OPENCV) && LIBCAER_HAVE_OPENCV == 1
        OPENCV_STANDARD   = 2,
        OPENCV_EDGE_AWARE = 3,
        OPENCV_TO_GRAY    = 4,
#endif
    };

    std::unique_ptr<FrameEventPacket> demosaic(demosaicTypes demosaicType) const {
        std::unique_ptr<FrameEventPacket> outPacket(new FrameEventPacket(
            this->getEventValid(), this->getEventSource(), this->getEventTSOverflow(), this->getEventSize(), RGB));

        size_t outIdx = 0;

        for (const auto &frame : *this) {
            // Only operate on valid frames.
            if (!frame.isValid()) {
                continue;
            }

            auto &outFrame = ((*outPacket)[outIdx]);

            // Copy header over. This will also copy validity information, so all copied frames are valid.
            memcpy(static_cast<void *>(&outFrame), &frame, (sizeof(struct caer_frame_event) - sizeof(uint16_t)));

            // Verify requirements for demosaicing operation.
            if ((frame.getChannelNumber() == libcaer::events::FrameEvent::colorChannels::GRAYSCALE)
                && (frame.getColorFilter() != libcaer::events::FrameEvent::colorFilter::MONO)) {
#if defined(LIBCAER_HAVE_OPENCV) && LIBCAER_HAVE_OPENCV == 1
                if ((demosaicType != demosaicTypes::TO_GRAY) && (demosaicType != demosaicTypes::OPENCV_TO_GRAY)) {
#else
                if (demosaicType != demosaicTypes::TO_GRAY) {
#endif
                    // Change channel to RGB. If color requested.
                    outFrame.setLengthXLengthYChannelNumber(frame.getLengthX(), frame.getLengthY(),
                        libcaer::events::FrameEvent::colorChannels::RGB, *outPacket);
                }

                // Do interpolation.
                caerFrameUtilsDemosaic(&frame, &outFrame,
                    static_cast<enum caer_frame_utils_demosaic_types>(
                        static_cast<typename std::underlying_type<demosaicTypes>::type>(demosaicType)));
            }
            else {
                // Just copy data over.
                memcpy(outFrame.getPixelArrayUnsafe(), frame.getPixelArrayUnsafe(), frame.getPixelsSize());
            }

            outIdx++;
        }

        // Set number of events in output packet correctly.
        outPacket->setEventNumber(outIdx);
        outPacket->setEventValid(outIdx);

        return (outPacket);
    }

    enum class contrastTypes {
        STANDARD = 0,
#if defined(LIBCAER_HAVE_OPENCV) && LIBCAER_HAVE_OPENCV == 1
        OPENCV_NORMALIZATION          = 1,
        OPENCV_HISTOGRAM_EQUALIZATION = 2,
        OPENCV_CLAHE                  = 3,
#endif
    };

    void contrast(contrastTypes contrastType) noexcept {
        for (auto &frame : *this) {
            // Only operate on valid frames.
            if (!frame.isValid()) {
                continue;
            }

            caerFrameUtilsContrast(&frame, &frame,
                static_cast<enum caer_frame_utils_contrast_types>(
                    static_cast<typename std::underlying_type<contrastTypes>::type>(contrastType)));
        }
    }
};
} // namespace events
} // namespace libcaer

#endif /* LIBCAER_EVENTS_FRAME_HPP_ */