DefectivePixelCorrection#

/***********************************************************************************
*
* Itala API - Copyright (C) 2022 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 DefectivePixelCorrection.cpp
 *
 * @brief "DefectivePixelCorrection" example shows how to detect
 * defective pixels on the sensor given a set of images and how 
 * to correct them via the functionality implemented in the nodemap
 * of the device. The procedure should be performed at max resolution.
 *
 * @see LiquidLens.cpp
 */

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

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

#define INDENT "\t"
#define ACQ_COUNT 10

// Pixel correction process enclosed in a separate function for 
// better example readability.
void CorrectDetectedPixels(Itala::DefectivePixelList defectivePixels, GenApi::INodeMap& nodemap)
{
  // Start by setting the amount of pixel targeted for correction.
  GenApi::CIntegerPtr pDefectivePixelCount = nodemap.GetNode("oeDefectivePixelCount");
  if (!IsWritable(pDefectivePixelCount))
    throw GENERIC_EXCEPTION("Unable to set the number of pixels to be corrected. Aborting.");
  pDefectivePixelCount->SetValue(defectivePixels.size());

  std::cout << INDENT << "Correction procedure started with " << defectivePixels.size() << " pixels." << std::endl;

  // Get the pixel selector to iterate over the amount of targeted pixels.
  GenApi::CIntegerPtr pDefectivePixelSelector = nodemap.GetNode("oeDefectivePixelSelector");
  if (!IsWritable(pDefectivePixelSelector))
    throw GENERIC_EXCEPTION("Unable to set the pixel selector for correction. Aborting.");

  // Get the features which allow to set the X and Y coordinate of the target pixel 
  // identified by the selector.
  GenApi::CIntegerPtr pDefectivePixelXCoordinate = nodemap.GetNode("oeDefectivePixelXCoordinate");
  if (!IsWritable(pDefectivePixelXCoordinate))
    throw GENERIC_EXCEPTION("Unable to set the X coordinate of the target pixel. Aborting.");
  GenApi::CIntegerPtr pDefectivePixelYCoordinate = nodemap.GetNode("oeDefectivePixelYCoordinate");
  if (!IsWritable(pDefectivePixelYCoordinate))
    throw GENERIC_EXCEPTION("Unable to set the Y coordinate of the target pixel. Aborting.");

  // Set the coordinates of every targeted pixels.
  for (size_t i = 0; i < defectivePixels.size(); i++)
  {
    pDefectivePixelSelector->SetValue(i);
    pDefectivePixelXCoordinate->SetValue(defectivePixels[i].X());
    pDefectivePixelYCoordinate->SetValue(defectivePixels[i].Y());

    std::cout << INDENT << "Pixel [" << defectivePixels[i].X() << ","
      << defectivePixels[i].Y() << "]" << " registered for correction. Was " <<
      defectivePixels[i].TypeString() << "." << std::endl;
  }

  // Once that the pixel map has been set (i.e. the amount of pixels to be corrected
  // and their coordinates) the write function can be called to perform the
  // correction.
  GenApi::CCommandPtr pDefectivePixelWriteMap = nodemap.GetNode("oeDefectivePixelWriteMap");
  if (!IsWritable(pDefectivePixelWriteMap))
    throw GENERIC_EXCEPTION("Unable to write the pixel map to device memory. Aborting.");
  pDefectivePixelWriteMap->Execute();

  std::cout << std::endl << INDENT << "Correction command sent, procedure completed." << std::endl;
}

void DefectivePixelCorrection_Sample()
{
  std::cout << "***** DefectivePixelCorrection 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;

  // Start by setting a full resolution Mono8 image to perform the
  // to perform the correction on the whole sensor. Keep the original
  // values to restore the initial camera state at the end of the program.
  GenApi::CIntegerPtr pWidth = pDevice->GetNodeMap().GetNode("Width");
  if (!IsWritable(pWidth))
    throw GENERIC_EXCEPTION("Unable to configure the image width. Aborting.");
  int64_t originalWidth = pWidth->GetValue();
  int64_t maxWidth = pWidth->GetMax();
  pWidth->SetValue(maxWidth);
  std::cout << std::endl << INDENT << "Image width set to " << maxWidth << std::endl;

  GenApi::CIntegerPtr pHeight = pDevice->GetNodeMap().GetNode("Height");
  if (!IsWritable(pHeight))
    throw GENERIC_EXCEPTION("Unable to configure the image height. Aborting.");
  int64_t originalHeight = pHeight->GetValue();
  int64_t maxHeight = pHeight->GetMax();
  pHeight->SetValue(maxHeight);
  std::cout << INDENT << "Image height set to " << maxHeight << std::endl;

  GenApi::CEnumerationPtr pPixelFormat = pDevice->GetNodeMap().GetNode("PixelFormat");
  if (!IsWritable(pHeight))
    throw GENERIC_EXCEPTION("Unable to configure the image pixel format. Aborting.");
  int64_t originalPixelFormat = pPixelFormat->GetIntValue();
  GenApi::CEnumEntryPtr pMono8Entry = pPixelFormat->GetEntryByName("Mono8");
  int64_t mono8Format = pMono8Entry->GetValue();
  pPixelFormat->SetIntValue(mono8Format);
  std::cout << INDENT << "PixelFormat set to Mono8." << std::endl;

  // Create a detection interface for the current image size and pixel depth.
  Itala::IDefectDetection* mono8Detection =
    Itala::CreateDefectDetection(maxWidth, maxHeight, Itala::PixelDepth::d8Bit);

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

  // Acquire the dark images needed for both leaky and hot/cold 
  // detection. The image data is accumulated thanks to the interface
  // so that each image can be immediately disposed without wasting memory.
  std::cout << INDENT << "Accumulating dark data.." << std::endl;
  for (int i = 0; i < ACQ_COUNT; i++)
  {
    Itala::IImage* dark_image = pDevice->GetNextImage(500);
    
    // Accumulate the new dark image. Note that the function requires
    // the raw data pointer, not the IImage itself!
    mono8Detection->AccumulateDark((uint8_t*)dark_image->GetData());

    std::cout << INDENT << "Image " << i + 1 << " added." << " \r";
    std::cout.flush();

    dark_image->Dispose();
  }

  // Acquire the gray images needed for hot/cold detection.
  std::cout << std::endl << INDENT << "Accumulating gray data.." << std::endl;
  for (int i = 0; i < ACQ_COUNT; i++)
  {
    Itala::IImage* gray_image = pDevice->GetNextImage(500);

    // Accumulate the new gray image. Note that the function requires
    // the raw data pointer, not the IImage itself!
    mono8Detection->AccumulateGray((uint8_t*)gray_image->GetData());

    std::cout << INDENT << "Image " << i + 1 << " added." << " \r";
    std::cout.flush();

    gray_image->Dispose();
  }

  pDevice->StopAcquisition();
  std::cout << std::endl << INDENT << "Acquisition stopped." << std::endl;

  // Find and get the leaky pixels of the sensor. 
  Itala::DefectivePixelList leakyPixels = mono8Detection->FindLeaky(1024, 5);

  // Same for hot/cold pixels.
  Itala::DefectivePixelList hotNColdPixels = mono8Detection->FindHotNCold(1024, 5);

  std::cout << leakyPixels.size() << " leaky pixels found." << std::endl;
  std::cout << hotNColdPixels.size() << " hot/cold pixels found." << std::endl << std::endl;

  // Leaky and hot/cold pixels are equally defective for the purposes of
  // the correction algorithm. Append the two vectors and perform the correction
  // with the dedicated GenICam nodes.
  leakyPixels.insert(leakyPixels.end(), hotNColdPixels.begin(), hotNColdPixels.end());

  CorrectDetectedPixels(leakyPixels, pDevice->GetNodeMap());

  // Dispose the detection interface when done.
  mono8Detection->Dispose();
  mono8Detection = nullptr;
  std::cout << std::endl << "Mono8 detection instance disposed." << std::endl;

  // Restore the original image size and format.
  pWidth->SetValue(originalWidth);
  pHeight->SetValue(originalHeight);
  pPixelFormat->SetIntValue(originalPixelFormat);

  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
  {
    DefectivePixelCorrection_Sample();
  }
  catch (GenICam::GenericException& e)
  {
    std::cout << e.what() << std::endl;
  }
}