GrabCallback

GrabCallback.cpp
/***********************************************************************************
 *
 * Itala API - Copyright (C) 2025 Opto Engineering
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY SUFFERED BY LICENSE AS
 * A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 ***********************************************************************************/

/**
 * @example GrabCallback.cpp
 *
 * @brief "GrabCallback" is an extension of the simple Grab example which shows how to grab images
 * in an event-like fashion instead of using the native "polling" approach imposed by the IDevice
 * interface. The idea is to build the functionality over the IDevice.GetNextImage() function by
 * running it in a loop on a dedicated thread and passing each grabbed image to a callback function.
 * The synchronization logic is kept simple.
 */

#include <atomic>
#include <chrono>
#include <thread>

// Include Itala API
#include "ItalaApi/Itala.h"

// Include the GenICam library
#include "GenICam.h"

#define INDENT "\t"

// Define a convenient type for the callback function pointer
using GrabCallback_t = void (*)(Itala::IImage*);

// Grab images in a polling loop (until the "done" flag is false). Then call the callback function
// provided by the user. The "done" flag is passed by reference. Don't forget to catch exceptions,
// otherwise the thread will terminate. This function is meant to be ran in a dedicated thread.
void AcquisitionLoop(GrabCallback_t func, Itala::IDevice* pDevice, std::atomic<bool>& done) {
  while (!done) {
    try {
      Itala::IImage* pImage = pDevice->GetNextImage(1000);
      func(pImage);
    } catch (GenICam::TimeoutException& /*e*/) {
      std::cout << INDENT << "Timeout occurred!" << std::endl;
    } catch (GenICam::GenericException& e) {
      std::cout << INDENT << "\r" << e.what() << std::endl;
      break;
    }
  }
}

// The actual user callback, called when an image is available.
void GrabCallback(Itala::IImage* pImage) {
  // Simulate some image processing and then disposes the image
  std::this_thread::sleep_for(std::chrono::milliseconds(10));
  std::cout << INDENT << "Image with ID:" << pImage->GetFrameID() << " processed." << std::endl;
  pImage->Dispose();
}

void GrabCallback_Sample() {
  std::cout << "***** ThreadedGrabCallbackCallback example started. *****" << std::endl
            << std::endl;

  Itala::ISystem* pSystem = Itala::CreateSystem();
  Itala::DeviceInfoList deviceInfos = pSystem->EnumerateDevices(700);

  if (deviceInfos.size() == 0) throw GENERIC_EXCEPTION("No devices found. Example canceled.");
  if (deviceInfos[0].AccessStatus() != Itala::DeviceAccessStatus::AvailableReadWrite)
    throw GENERIC_EXCEPTION("Target device is unaccessible in RW mode. Example canceled.");
  Itala::IDevice* pDevice = pSystem->CreateDevice(deviceInfos[0]);

  std::cout << "First device initialized." << std::endl;
  pDevice->StartAcquisition();
  std::cout << "Acquisition started." << std::endl << std::endl;

  // Flag passed to the thread to stop grabbing images
  std::atomic<bool> done = false;

  // Start the acquisition thread. The "done" variable has to be wrapped with std::ref since it must
  // be passed by refence to the "AcquisitionLoop" function while the arguments given to the thread
  // get copied by value
  std::thread grabber(AcquisitionLoop, GrabCallback, pDevice, std::ref(done));

  // Wait for user input to set the "done" flag to true and stop the acquisition
  std::cout << "Press Enter to stop acquisition.." << std::endl << std::endl;
  std::cin.get();
  done = true;
  grabber.join();

  pDevice->StopAcquisition();
  std::cout << std::endl << "Acquisition stopped." << std::endl << std::endl;
  pDevice->Dispose();
  pDevice = nullptr;
  std::cout << "Device instance disposed." << std::endl;
  pSystem->Dispose();
  pSystem = nullptr;
  std::cout << "System instance disposed." << std::endl;
}

int main(int /*argc*/, char** /*argv*/) {
  try {
    GrabCallback_Sample();
  } catch (GenICam::GenericException& e) {
    std::cout << e.what() << std::endl;
  }
}