Taking image with Sapera and copying data to vector

1.9k views Asked by At

How do I take images with Sapera SDK and transfer the image data from the SapBuffer object to vector?

1

There are 1 answers

0
VLL On BEST ANSWER

To handle images taken by camera using Sapera, you should make specialization of SapProcessing class, which is used to process buffers. Otherwise the buffer is cleared automatically after each frame, and you lose the data.

The imaging process goes as follows:

  1. You call Grab() on the camera object to start imaging
  2. After each frame has been taken, transfer callback is called. Here you request your SapProcessing object to process the next frame.
  3. Run() function of your SapProcessing object is called. Here you can read data from the buffer.
  4. After Run() function, processing callback is called.
  5. When you have received enough frames, call Freeze() to stop imaging.

This example code takes images using default settings on the camera (monochrome 8-bit pixel format).

#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
#include <iostream>
#include <iomanip>
#include <atomic>
#include "SapClassBasic.h"

// Helper function to find the camera by its serial number
SapAcqDevice getDeviceBySN(const std::string& sn)
{
    char serverName[CORSERVER_MAX_STRLEN];
    char serialNumberName[2048];

    const int serverCount = SapManager::GetServerCount();
    for (int i = 0; i < serverCount; i++) {
        if (SapManager::GetResourceCount(i, SapManager::ResourceAcqDevice) != 0)
        {
            SapManager::GetServerName(i, serverName, sizeof(serverName));

            SapAcqDevice camera(serverName);
            if (!camera.Create()) {
                throw std::runtime_error("Failed to create camera object.");
            }
            int featureCount;
            if (camera.GetFeatureCount(&featureCount) && featureCount > 0)
            {
                if (camera.GetFeatureValue("DeviceID", serialNumberName, sizeof(serialNumberName))
                    && serialNumberName == sn)
                {
                    return camera;
                }
            }
            camera.Destroy();
        }
    }
    const auto errorStr = "Camera \"" + sn + "\" was not found.";
    throw std::runtime_error(errorStr.c_str());
}

class SapMyProcessing : public SapProcessing
{
public:
    SapMyProcessing(SapBuffer* pBuffers, SapProCallback pCallback, void* pContext);
    virtual ~SapMyProcessing();

protected:
    virtual BOOL Run();
};

SapMyProcessing::SapMyProcessing(SapBuffer* pBuffers, SapProCallback pCallback, void* pContext)
    : SapProcessing(pBuffers, pCallback, pContext)
{}

SapMyProcessing::~SapMyProcessing()
{
    if (m_bInitOK) Destroy();
}

BOOL SapMyProcessing::Run()
{
    // Get the current buffer index
    const int proIndex = GetIndex();

    // If this is not true, buffer has overflown
    SapBuffer::State state;
    bool goodContent = m_pBuffers->GetState(proIndex, &state)
        && state == SapBuffer::StateFull;

    if (goodContent) {
        void *inAddress = nullptr;
        m_pBuffers->GetAddress(proIndex, &inAddress);
        int inSize = 0;
        m_pBuffers->GetSpaceUsed(proIndex, &inSize);

        // Width, height and pixel format are received from the camera
        const int width = m_pBuffers->GetWidth();
        const int height = m_pBuffers->GetHeight();
        const auto format = m_pBuffers->GetFormat();
        const int outSize = width * height;

        // Skip unexpected pixel format or incomplete frame
        goodContent = format == SapFormatMono8
            && inSize == outSize;

        if (goodContent) {
            // Copy data to vector
            std::vector<uint8_t> outBuffer(outSize);
            std::copy((uint8_t*)inAddress, (uint8_t*)(inAddress) + outSize, outBuffer.begin());

            // Print the first line
            for (int i = 0; i < width; i++) {
                std::cout << std::hex << int(outBuffer[i]);
            }
            std::cout << std::endl << std::endl;
        }
    }

    return TRUE;
}

// Information to pass to callbacks
struct TransferContext {
    std::atomic_int frameGrabCount = 0, frameProcessingCount = 0;
    std::shared_ptr<SapMyProcessing> processing;
};

void transferCallback(SapXferCallbackInfo *info)
{
    auto context = (TransferContext*)info->GetContext();

    context->frameGrabCount++;
    if (!info->IsTrash()) {
        // Execute Run() for this frame
        context->processing->ExecuteNext();
    }
}

// Processing callback is called after Run()
void processingCallback(SapProCallbackInfo* info)
{
    auto context = (TransferContext*)info->GetContext();

    // Processing has finished
    context->frameProcessingCount++;
}

// The main imaging function
void grab(const std::string& serialNumber)
{
    // Number of frames to receive from the camera
    const int maxFrameCount = 10;

    TransferContext context;

    auto camera = getDeviceBySN(serialNumber);
    std::unique_ptr<SapBuffer> buffer
        = std::make_unique<SapBufferWithTrash>(maxFrameCount, &camera);
    std::unique_ptr<SapTransfer> transfer
        = std::make_unique<SapAcqDeviceToBuf>(&camera, buffer.get(), transferCallback, &context);
    context.processing = std::make_shared<SapMyProcessing>(buffer.get(), processingCallback, &context);

    auto cleanup = [&]() {
            if (context.processing) context.processing->Destroy();
            if (transfer) transfer->Destroy();
            if (buffer) buffer->Destroy();
            camera.Destroy();
        };

    try {
        if (!buffer->Create()) {
            throw std::runtime_error("Failed to create buffer object.");
        }
        if (!transfer->Create()) {
            throw std::runtime_error("Failed to create transfer object.");
        }
        if (!context.processing->Create()) {
            throw std::runtime_error("Failed to create processing object.");
        }
        transfer->SetAutoEmpty(false);
        context.processing->SetAutoEmpty(true);
        context.processing->Init();

        transfer->Grab();

        // Wait for the camera to grab all frames
        while (context.frameGrabCount < maxFrameCount);

        transfer->Freeze();
        if (!transfer->Wait(5000)) {
            throw std::runtime_error("Failed to stop grab.");
        }

        // Wait for processing to complete
        while (context.frameProcessingCount < maxFrameCount);

        cleanup();
    }
    catch (...) {
        cleanup();
        throw;
    }
}