Polarization#

/***********************************************************************************
 *
 * Itala API - Copyright (C) 2024 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 Polarization.cpp
 *
 * @brief "Polarization" example shows how to process images containing light polarization data.
 * Polarization images are similar to bayer color images since they need to be demosaiced and
 * processed to determine the state of polarized light.
 *
 * @warning
 * The following example shows a complete workflow for monochrome polarization images including
 * polarization components and Stokes vector computation. To get images like AoLP, DoLP and
 * intensity directly, a dedicated overload set is available.
 */

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

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

#define INDENT "\t"

#define PIXEL_FORMAT "Polarized00Mono8"

void Polarization_Sample() {
  std::cout << "***** Polarization 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]);
  GenApi::INodeMap& deviceNodeMap = pDevice->GetNodeMap();

  // Set the pixelformat to Polarized00Mono8 or whatever format is available on the camera.
  GenApi::CEnumerationPtr pPixelFormat = deviceNodeMap.GetNode("PixelFormat");
  if (!IsWritable(pPixelFormat))
    throw GENERIC_EXCEPTION("Unable to configure the pixel format. Aborting.");
  uint64_t originalPixelFormat = pPixelFormat->GetIntValue();
  pPixelFormat->FromString(PIXEL_FORMAT);
  std::cout << "PixelFormat set to " << PIXEL_FORMAT << std::endl;

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

  Itala::IImage* pImage = pDevice->GetNextImage(1000);

  // Clone the grabbed image for convenience so that it can be returned to the library and the
  // acquisition can be stopped.
  Itala::IImage* pPolarizationImage = pImage->Clone();
  pImage->Dispose();
  pImage = nullptr;
  pDevice->StopAcquisition();

  // Extract an image for each component (or angle) of the polarizer filter. Each resulting image
  // represents the captured light at 0, 45, 90 and 135 degrees respectively. Depending on the
  // demosaicing algorithm used, the pixel format of the resulting images can be of different types.
  // NearestNeighbour on Polarized00Mono8 produces Mono8 components.
  std::cout << "Extracting all polarization components.. ";
  Itala::PolarComponents polarizationComponents = Itala::ExtractAllPolarComponents(
      pPolarizationImage, Itala::PolarDemosaicingAlgorithm::NearestNeighbour);
  std::cout << "P45 format is "
            << GetPixelFormatDescription(polarizationComponents.P45->GetPixelFormat()) << std::endl;

  // display or process polarization components

  // Compute all the Stokes vectors to determine the polarization state of the light given the
  // intensities of the different polarizers on the sensor. Since we're dealing with linear
  // polarization data, only the first three (s0, s1 and s2) components of the vector are available.
  // Each resulting image represents the values of that particular component of the Stokes vector
  // across the whole sensor. For instance, the S0 image contains the s0 values of each pixel.
  std::cout << "Computing all stokes vectors.. ";
  Itala::StokesVectors stokesVectors = Itala::ComputeAllStokes(polarizationComponents);
  std::cout << "S0 format is " << GetPixelFormatDescription(stokesVectors.S0->GetPixelFormat())
            << std::endl;

  // display or process the Stokes vectors

  // Given the stokes vectors, compute the linear polarization images.
  // The DoLP (Degree of Linear Polarization) image indicates the ratio of the intensity of the
  // polarized part of the light to the intensity of the unpolarized one. When the light is totally
  // polarized, this corresponds to the total intensity of the light. When the light is unpolarized,
  // this corresponds to zero.
  // The AoLP (Angle of Linear Polarization) image indicates the polarization direction of the
  // incoming light.
  // The intensity image simply indicate the total intensity of the incoming light.
  std::cout << std::endl << "Computing linear polarization images.." << std::endl;
  Itala::IImage* pDolpImage = Itala::ComputeDoLP(stokesVectors);
  Itala::IImage* pAolpImage = Itala::ComputeAoLP(stokesVectors);
  Itala::IImage* pIntensityImage =
      Itala::ComputeIntensity(stokesVectors, pPolarizationImage->GetPixelFormat());
  std::cout << "DoLP format is " << GetPixelFormatDescription(pDolpImage->GetPixelFormat())
            << std::endl;
  std::cout << "AoLP format is " << GetPixelFormatDescription(pAolpImage->GetPixelFormat())
            << std::endl;
  std::cout << "Intensity format is "
            << GetPixelFormatDescription(pIntensityImage->GetPixelFormat()) << std::endl;

  // display or process the images

  // From the source image, compute an image of the same size divided in four quadrants containing
  // the four polarization components.
  std::cout << std::endl << "Computing quadrants image.. ";
  Itala::IImage* pQuadrantsImage = Itala::ComputePolarQuadrantsImage(pPolarizationImage);
  std::cout << "format is " << GetPixelFormatDescription(pQuadrantsImage->GetPixelFormat())
            << std::endl;

  // Display or process the quadrants image

  // If intermediate data like polar components or stokes vectors is not needed, monochrome AoLP,
  // DoLP and intensity can be directly computed with the dedicated functions given the polarized
  // image. For instance, DoLP:
  std::cout << std::endl << "Computing DoLP without intermediate steps.." << std::endl;
  Itala::IImage* pDirectDolp =
      Itala::ComputeDoLP(pPolarizationImage, Itala::PolarDemosaicingAlgorithm::NearestNeighbour);

  // Display or process DoLP

  // Restore the original pixel format value
  pPixelFormat->SetIntValue(originalPixelFormat);

  // Each image MUST be disposed so that its memory is released. In real scenarios it's a good idea
  // to release the images as long as they're no longer required to keep the memory footprint low.
  // To prevent leaks, a dedicated set of functions is available in the Smart.h header.
  pPolarizationImage->Dispose();
  polarizationComponents.P0->Dispose();
  polarizationComponents.P45->Dispose();
  polarizationComponents.P90->Dispose();
  polarizationComponents.P135->Dispose();
  stokesVectors.S0->Dispose();
  stokesVectors.S1->Dispose();
  stokesVectors.S2->Dispose();
  pDolpImage->Dispose();
  pAolpImage->Dispose();
  pIntensityImage->Dispose();
  pQuadrantsImage->Dispose();
  pDirectDolp->Dispose();
  std::cout << std::endl << "Image istances disposed." << 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 {
    Polarization_Sample();
  } catch (GenICam::GenericException& e) {
    std::cout << e.what() << std::endl;
  }
}