Squashed 'third_party/allwpilib_2016/' content from commit 7f61816

Change-Id: If9d9245880859cdf580f5d7f77045135d0521ce7
git-subtree-dir: third_party/allwpilib_2016
git-subtree-split: 7f618166ed253a24629934fcf89c3decb0528a3b
diff --git a/wpilibc/Athena/src/Vision/AxisCamera.cpp b/wpilibc/Athena/src/Vision/AxisCamera.cpp
new file mode 100644
index 0000000..990c68a
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/AxisCamera.cpp
@@ -0,0 +1,597 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/AxisCamera.h"
+
+#include "WPIErrors.h"
+
+#include <cstring>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <Timer.h>
+#include <iostream>
+#include <sstream>
+
+static const unsigned int kMaxPacketSize = 1536;
+static const unsigned int kImageBufferAllocationIncrement = 1000;
+
+static const std::string kWhiteBalanceStrings[] = {
+    "auto",         "hold",         "fixed_outdoor1", "fixed_outdoor2",
+    "fixed_indoor", "fixed_fluor1", "fixed_fluor2",
+};
+
+static const std::string kExposureControlStrings[] = {
+    "auto", "hold", "flickerfree50", "flickerfree60",
+};
+
+static const std::string kResolutionStrings[] = {
+    "640x480", "480x360", "320x240", "240x180", "176x144", "160x120",
+};
+
+static const std::string kRotationStrings[] = {
+    "0", "180",
+};
+
+/**
+ * AxisCamera constructor
+ * @param cameraHost The host to find the camera at, typically an IP address
+ */
+AxisCamera::AxisCamera(std::string const &cameraHost)
+    : m_cameraHost(cameraHost) {
+  m_captureThread = std::thread(&AxisCamera::Capture, this);
+}
+
+AxisCamera::~AxisCamera() {
+  m_done = true;
+  m_captureThread.join();
+}
+
+/*
+ * Return true if the latest image from the camera has not been retrieved by
+ * calling GetImage() yet.
+ * @return true if the image has not been retrieved yet.
+ */
+bool AxisCamera::IsFreshImage() const { return m_freshImage; }
+
+/**
+ * Get an image from the camera and store it in the provided image.
+ * @param image The imaq image to store the result in. This must be an HSL or
+ * RGB image.
+ * @return 1 upon success, zero on a failure
+ */
+int AxisCamera::GetImage(Image *image) {
+  if (m_imageData.size() == 0) {
+    return 0;
+  }
+
+  std::lock_guard<priority_mutex> lock(m_imageDataMutex);
+
+  Priv_ReadJPEGString_C(image, m_imageData.data(), m_imageData.size());
+
+  m_freshImage = false;
+
+  return 1;
+}
+
+/**
+ * Get an image from the camera and store it in the provided image.
+ * @param image The image to store the result in. This must be an HSL or RGB
+ * image
+ * @return 1 upon success, zero on a failure
+ */
+int AxisCamera::GetImage(ColorImage *image) {
+  return GetImage(image->GetImaqImage());
+}
+
+/**
+ * Instantiate a new image object and fill it with the latest image from the
+ * camera.
+ *
+ * The returned pointer is owned by the caller and is their responsibility to
+ * delete.
+ * @return a pointer to an HSLImage object
+ */
+HSLImage *AxisCamera::GetImage() {
+  auto image = new HSLImage();
+  GetImage(image);
+  return image;
+}
+
+/**
+ * Copy an image into an existing buffer.
+ * This copies an image into an existing buffer rather than creating a new image
+ * in memory. That way a new image is only allocated when the image being copied
+ * is
+ * larger than the destination.
+ * This method is called by the PCVideoServer class.
+ * @param imageData The destination image.
+ * @param numBytes The size of the destination image.
+ * @return 0 if failed (no source image or no memory), 1 if success.
+ */
+int AxisCamera::CopyJPEG(char **destImage, unsigned int &destImageSize,
+                         unsigned int &destImageBufferSize) {
+  std::lock_guard<priority_mutex> lock(m_imageDataMutex);
+  if (destImage == nullptr) {
+    wpi_setWPIErrorWithContext(NullParameter, "destImage must not be nullptr");
+    return 0;
+  }
+
+  if (m_imageData.size() == 0) return 0;  // if no source image
+
+  if (destImageBufferSize <
+      m_imageData.size())  // if current destination buffer too small
+  {
+    if (*destImage != nullptr) delete[] * destImage;
+    destImageBufferSize = m_imageData.size() + kImageBufferAllocationIncrement;
+    *destImage = new char[destImageBufferSize];
+    if (*destImage == nullptr) return 0;
+  }
+  // copy this image into destination buffer
+  if (*destImage == nullptr) {
+    wpi_setWPIErrorWithContext(NullParameter, "*destImage must not be nullptr");
+  }
+
+  std::copy(m_imageData.begin(), m_imageData.end(), *destImage);
+  destImageSize = m_imageData.size();
+  ;
+  return 1;
+}
+
+/**
+ * Request a change in the brightness of the camera images.
+ * @param brightness valid values 0 .. 100
+ */
+void AxisCamera::WriteBrightness(int brightness) {
+  if (brightness < 0 || brightness > 100) {
+    wpi_setWPIErrorWithContext(ParameterOutOfRange,
+                               "Brightness must be from 0 to 100");
+    return;
+  }
+
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_brightness != brightness) {
+    m_brightness = brightness;
+    m_parametersDirty = true;
+  }
+}
+
+/**
+ * @return The configured brightness of the camera images
+ */
+int AxisCamera::GetBrightness() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_brightness;
+}
+
+/**
+ * Request a change in the white balance on the camera.
+ * @param whiteBalance Valid values from the <code>WhiteBalance</code> enum.
+ */
+void AxisCamera::WriteWhiteBalance(AxisCamera::WhiteBalance whiteBalance) {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_whiteBalance != whiteBalance) {
+    m_whiteBalance = whiteBalance;
+    m_parametersDirty = true;
+  }
+}
+
+/**
+ * @return The configured white balances of the camera images
+ */
+AxisCamera::WhiteBalance AxisCamera::GetWhiteBalance() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_whiteBalance;
+}
+
+/**
+ * Request a change in the color level of the camera images.
+ * @param colorLevel valid values are 0 .. 100
+ */
+void AxisCamera::WriteColorLevel(int colorLevel) {
+  if (colorLevel < 0 || colorLevel > 100) {
+    wpi_setWPIErrorWithContext(ParameterOutOfRange,
+                               "Color level must be from 0 to 100");
+    return;
+  }
+
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_colorLevel != colorLevel) {
+    m_colorLevel = colorLevel;
+    m_parametersDirty = true;
+  }
+}
+
+/**
+ * @return The configured color level of the camera images
+ */
+int AxisCamera::GetColorLevel() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_colorLevel;
+}
+
+/**
+ * Request a change in the camera's exposure mode.
+ * @param exposureControl A mode to write in the <code>Exposure</code> enum.
+ */
+void AxisCamera::WriteExposureControl(
+    AxisCamera::ExposureControl exposureControl) {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_exposureControl != exposureControl) {
+    m_exposureControl = exposureControl;
+    m_parametersDirty = true;
+  }
+}
+
+/**
+ * @return The configured exposure control mode of the camera
+ */
+AxisCamera::ExposureControl AxisCamera::GetExposureControl() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_exposureControl;
+}
+
+/**
+ * Request a change in the exposure priority of the camera.
+ * @param exposurePriority Valid values are 0, 50, 100.
+ * 0 = Prioritize image quality
+ * 50 = None
+ * 100 = Prioritize frame rate
+ */
+void AxisCamera::WriteExposurePriority(int exposurePriority) {
+  if (exposurePriority != 0 && exposurePriority != 50 &&
+      exposurePriority != 100) {
+    wpi_setWPIErrorWithContext(ParameterOutOfRange,
+                               "Exposure priority must be from 0, 50, or 100");
+    return;
+  }
+
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_exposurePriority != exposurePriority) {
+    m_exposurePriority = exposurePriority;
+    m_parametersDirty = true;
+  }
+}
+
+/**
+ * @return The configured exposure priority of the camera
+ */
+int AxisCamera::GetExposurePriority() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_exposurePriority;
+}
+
+/**
+ * Write the maximum frames per second that the camera should send
+ * Write 0 to send as many as possible.
+ * @param maxFPS The number of frames the camera should send in a second,
+ * exposure permitting.
+ */
+void AxisCamera::WriteMaxFPS(int maxFPS) {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_maxFPS != maxFPS) {
+    m_maxFPS = maxFPS;
+    m_parametersDirty = true;
+    m_streamDirty = true;
+  }
+}
+
+/**
+ * @return The configured maximum FPS of the camera
+ */
+int AxisCamera::GetMaxFPS() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_maxFPS;
+}
+
+/**
+ * Write resolution value to camera.
+ * @param resolution The camera resolution value to write to the camera.
+ */
+void AxisCamera::WriteResolution(AxisCamera::Resolution resolution) {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_resolution != resolution) {
+    m_resolution = resolution;
+    m_parametersDirty = true;
+    m_streamDirty = true;
+  }
+}
+
+/**
+ * @return The configured resolution of the camera (not necessarily the same
+ * resolution as the most recent image, if it was changed recently.)
+ */
+AxisCamera::Resolution AxisCamera::GetResolution() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_resolution;
+}
+
+/**
+ * Write the rotation value to the camera.
+ * If you mount your camera upside down, use this to adjust the image for you.
+ * @param rotation The angle to rotate the camera
+ * (<code>AxisCamera::Rotation::k0</code>
+ * or <code>AxisCamera::Rotation::k180</code>)
+ */
+void AxisCamera::WriteRotation(AxisCamera::Rotation rotation) {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_rotation != rotation) {
+    m_rotation = rotation;
+    m_parametersDirty = true;
+    m_streamDirty = true;
+  }
+}
+
+/**
+ * @return The configured rotation mode of the camera
+ */
+AxisCamera::Rotation AxisCamera::GetRotation() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_rotation;
+}
+
+/**
+ * Write the compression value to the camera.
+ * @param compression Values between 0 and 100.
+ */
+void AxisCamera::WriteCompression(int compression) {
+  if (compression < 0 || compression > 100) {
+    wpi_setWPIErrorWithContext(ParameterOutOfRange,
+                               "Compression must be from 0 to 100");
+    return;
+  }
+
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+
+  if (m_compression != compression) {
+    m_compression = compression;
+    m_parametersDirty = true;
+    m_streamDirty = true;
+  }
+}
+
+/**
+ * @return The configured compression level of the camera
+ */
+int AxisCamera::GetCompression() {
+  std::lock_guard<priority_mutex> lock(m_parametersMutex);
+  return m_compression;
+}
+
+/**
+ * Method called in the capture thread to receive images from the camera
+ */
+void AxisCamera::Capture() {
+  int consecutiveErrors = 0;
+
+  // Loop on trying to setup the camera connection. This happens in a background
+  // thread so it shouldn't effect the operation of user programs.
+  while (!m_done) {
+    std::string requestString =
+        "GET /mjpg/video.mjpg HTTP/1.1\n"
+        "User-Agent: HTTPStreamClient\n"
+        "Connection: Keep-Alive\n"
+        "Cache-Control: no-cache\n"
+        "Authorization: Basic RlJDOkZSQw==\n\n";
+    m_captureMutex.lock();
+    m_cameraSocket = CreateCameraSocket(requestString, consecutiveErrors > 5);
+    if (m_cameraSocket != -1) {
+      ReadImagesFromCamera();
+      consecutiveErrors = 0;
+    } else {
+      consecutiveErrors++;
+    }
+    m_captureMutex.unlock();
+    Wait(0.5);
+  }
+}
+
+/**
+ * This function actually reads the images from the camera.
+ */
+void AxisCamera::ReadImagesFromCamera() {
+  char *imgBuffer = nullptr;
+  int imgBufferLength = 0;
+
+  // TODO: these recv calls must be non-blocking. Otherwise if the camera
+  // fails during a read, the code hangs and never retries when the camera comes
+  // back up.
+
+  int counter = 2;
+  while (!m_done) {
+    char initialReadBuffer[kMaxPacketSize] = "";
+    char intermediateBuffer[1];
+    char *trailingPtr = initialReadBuffer;
+    int trailingCounter = 0;
+    while (counter) {
+      // TODO: fix me... this cannot be the most efficient way to approach this,
+      // reading one byte at a time.
+      if (recv(m_cameraSocket, intermediateBuffer, 1, 0) == -1) {
+        wpi_setErrnoErrorWithContext("Failed to read image header");
+        close(m_cameraSocket);
+        return;
+      }
+      strncat(initialReadBuffer, intermediateBuffer, 1);
+      // trailingCounter ensures that we start looking for the 4 byte string
+      // after
+      // there is at least 4 bytes total. Kind of obscure.
+      // look for 2 blank lines (\r\n)
+      if (nullptr != strstr(trailingPtr, "\r\n\r\n")) {
+        --counter;
+      }
+      if (++trailingCounter >= 4) {
+        trailingPtr++;
+      }
+    }
+    counter = 1;
+    char *contentLength = strstr(initialReadBuffer, "Content-Length: ");
+    if (contentLength == nullptr) {
+      wpi_setWPIErrorWithContext(IncompatibleMode,
+                                 "No content-length token found in packet");
+      close(m_cameraSocket);
+      if (imgBuffer) delete[] imgBuffer;
+      return;
+    }
+    contentLength = contentLength + 16;    // skip past "content length"
+    int readLength = atol(contentLength);  // get the image byte count
+
+    // Make sure buffer is large enough
+    if (imgBufferLength < readLength) {
+      if (imgBuffer) delete[] imgBuffer;
+      imgBufferLength = readLength + kImageBufferAllocationIncrement;
+      imgBuffer = new char[imgBufferLength];
+      if (imgBuffer == nullptr) {
+        imgBufferLength = 0;
+        continue;
+      }
+    }
+
+    // Read the image data for "Content-Length" bytes
+    int bytesRead = 0;
+    int remaining = readLength;
+    while (bytesRead < readLength) {
+      int bytesThisRecv =
+          recv(m_cameraSocket, &imgBuffer[bytesRead], remaining, 0);
+      bytesRead += bytesThisRecv;
+      remaining -= bytesThisRecv;
+    }
+
+    // Update image
+    {
+      std::lock_guard<priority_mutex> lock(m_imageDataMutex);
+
+      m_imageData.assign(imgBuffer, imgBuffer + imgBufferLength);
+      m_freshImage = true;
+    }
+
+    if (WriteParameters()) {
+      break;
+    }
+  }
+
+  close(m_cameraSocket);
+}
+
+/**
+ * Send a request to the camera to set all of the parameters.  This is called
+ * in the capture thread between each frame. This strategy avoids making lots
+ * of redundant HTTP requests, accounts for failed initial requests, and
+ * avoids blocking calls in the main thread unless necessary.
+ *
+ * This method does nothing if no parameters have been modified since it last
+ * completely successfully.
+ *
+ * @return <code>true</code> if the stream should be restarted due to a
+ * parameter changing.
+ */
+bool AxisCamera::WriteParameters() {
+  if (m_parametersDirty) {
+    std::stringstream request;
+    request << "GET /axis-cgi/admin/param.cgi?action=update";
+
+    m_parametersMutex.lock();
+    request << "&ImageSource.I0.Sensor.Brightness=" << m_brightness;
+    request << "&ImageSource.I0.Sensor.WhiteBalance="
+            << kWhiteBalanceStrings[m_whiteBalance];
+    request << "&ImageSource.I0.Sensor.ColorLevel=" << m_colorLevel;
+    request << "&ImageSource.I0.Sensor.Exposure="
+            << kExposureControlStrings[m_exposureControl];
+    request << "&ImageSource.I0.Sensor.ExposurePriority=" << m_exposurePriority;
+    request << "&Image.I0.Stream.FPS=" << m_maxFPS;
+    request << "&Image.I0.Appearance.Resolution="
+            << kResolutionStrings[m_resolution];
+    request << "&Image.I0.Appearance.Compression=" << m_compression;
+    request << "&Image.I0.Appearance.Rotation=" << kRotationStrings[m_rotation];
+    m_parametersMutex.unlock();
+
+    request << " HTTP/1.1" << std::endl;
+    request << "User-Agent: HTTPStreamClient" << std::endl;
+    request << "Connection: Keep-Alive" << std::endl;
+    request << "Cache-Control: no-cache" << std::endl;
+    request << "Authorization: Basic RlJDOkZSQw==" << std::endl;
+    request << std::endl;
+
+    int socket = CreateCameraSocket(request.str(), false);
+    if (socket == -1) {
+      wpi_setErrnoErrorWithContext("Error setting camera parameters");
+    } else {
+      close(socket);
+      m_parametersDirty = false;
+
+      if (m_streamDirty) {
+        m_streamDirty = false;
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+/**
+ * Create a socket connected to camera
+ * Used to create a connection to the camera for both capturing images and
+ * setting parameters.
+ * @param requestString The initial request string to send upon successful
+ * connection.
+ * @param setError If true, rais an error if there's a problem creating the
+ * connection.
+ * This is only enabled after several unsucessful connections, so a single one
+ * doesn't
+ * cause an error message to be printed if it immediately recovers.
+ * @return -1 if failed, socket handle if successful.
+ */
+int AxisCamera::CreateCameraSocket(std::string const &requestString,
+                                   bool setError) {
+  struct addrinfo *address = nullptr;
+  int camSocket;
+
+  /* create socket */
+  if ((camSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+    if (setError)
+      wpi_setErrnoErrorWithContext("Failed to create the camera socket");
+    return -1;
+  }
+
+  if (getaddrinfo(m_cameraHost.c_str(), "80", nullptr, &address) == -1) {
+    if (setError) {
+      wpi_setErrnoErrorWithContext("Failed to create the camera socket");
+      close(camSocket);
+    }
+    return -1;
+  }
+
+  /* connect to server */
+  if (connect(camSocket, address->ai_addr, address->ai_addrlen) == -1) {
+    if (setError)
+      wpi_setErrnoErrorWithContext("Failed to connect to the camera");
+    freeaddrinfo(address);
+    close(camSocket);
+    return -1;
+  }
+
+  freeaddrinfo(address);
+
+  int sent = send(camSocket, requestString.c_str(), requestString.size(), 0);
+  if (sent == -1) {
+    if (setError)
+      wpi_setErrnoErrorWithContext("Failed to send a request to the camera");
+    close(camSocket);
+    return -1;
+  }
+
+  return camSocket;
+}
diff --git a/wpilibc/Athena/src/Vision/BaeUtilities.cpp b/wpilibc/Athena/src/Vision/BaeUtilities.cpp
new file mode 100644
index 0000000..f09ec64
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/BaeUtilities.cpp
@@ -0,0 +1,369 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.
+ */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "Vision/BaeUtilities.h"
+#include "Servo.h"
+#include "Timer.h"
+
+/** @file
+ *   Utility functions
+ */
+
+/**
+ * debug output flag options:
+ * DEBUG_OFF, DEBUG_MOSTLY_OFF, DEBUG_SCREEN_ONLY, DEBUG_FILE_ONLY,
+ * DEBUG_SCREEN_AND_FILE
+ */
+static DebugOutputType dprintfFlag = DEBUG_OFF;
+
+/**
+ * Set the debug flag to print to screen, file on cRIO, both or neither
+ * @param tempString The format string.
+ */
+void SetDebugFlag(DebugOutputType flag) { dprintfFlag = flag; }
+
+/**
+ * Debug print to a file and/or a terminal window.
+ * Call like you would call printf.
+ * Set functionName in the function if you want the correct function name to
+ * print out.
+ * The file line number will also be printed.
+ * @param tempString The format string.
+ */
+void dprintf(const char *tempString, ...) /* Variable argument list */
+{
+  va_list args;    /* Input argument list */
+  int line_number; /* Line number passed in argument */
+  int type;
+  const char *functionName; /* Format passed in argument */
+  const char *fmt;          /* Format passed in argument */
+  char text[512];           /* Text string */
+  char outtext[512];        /* Text string */
+  FILE *outfile_fd;         /* Output file pointer */
+  char filepath[128];       /* Text string */
+  int fatalFlag = 0;
+  const char *filename;
+  int index;
+  int tempStringLen;
+
+  if (dprintfFlag == DEBUG_OFF) {
+    return;
+  }
+
+  va_start(args, tempString);
+
+  tempStringLen = strlen(tempString);
+  filename = tempString;
+  for (index = 0; index < tempStringLen; index++) {
+    if (tempString[index] == ' ') {
+      printf("ERROR in dprintf: malformed calling sequence (%s)\n", tempString);
+      va_end(args);
+      return;
+    }
+    if (tempString[index] == '\\' || tempString[index] == '/')
+      filename = tempString + index + 1;
+  }
+
+  /* Extract function name */
+  functionName = va_arg(args, const char *);
+
+  /* Extract line number from argument list */
+  line_number = va_arg(args, int);
+
+  /* Extract information type from argument list */
+  type = va_arg(args, int);
+
+  /* Extract format from argument list */
+  fmt = va_arg(args, const char *);
+
+  vsprintf(text, fmt, args);
+
+  va_end(args);
+
+  /* Format output statement */
+  switch (type) {
+    case DEBUG_TYPE:
+      sprintf(outtext, "[%s:%s@%04d] DEBUG %s\n", filename, functionName,
+              line_number, text);
+      break;
+    case INFO_TYPE:
+      sprintf(outtext, "[%s:%s@%04d] INFO %s\n", filename, functionName,
+              line_number, text);
+      break;
+    case ERROR_TYPE:
+      sprintf(outtext, "[%s:%s@%04d] ERROR %s\n", filename, functionName,
+              line_number, text);
+      break;
+    case CRITICAL_TYPE:
+      sprintf(outtext, "[%s:%s@%04d] CRITICAL %s\n", filename, functionName,
+              line_number, text);
+      break;
+    case FATAL_TYPE:
+      fatalFlag = 1;
+      sprintf(outtext, "[%s:%s@%04d] FATAL %s\n", filename, functionName,
+              line_number, text);
+      break;
+    default:
+      printf("ERROR in dprintf: malformed calling sequence\n");
+      return;
+      break;
+  }
+
+  sprintf(filepath, "%s.debug", filename);
+
+  /* Write output statement */
+  switch (dprintfFlag) {
+    default:
+    case DEBUG_OFF:
+      break;
+    case DEBUG_MOSTLY_OFF:
+      if (fatalFlag) {
+        if ((outfile_fd = fopen(filepath, "a+")) != nullptr) {
+          fwrite(outtext, sizeof(char), strlen(outtext), outfile_fd);
+          fclose(outfile_fd);
+        }
+      }
+      break;
+    case DEBUG_SCREEN_ONLY:
+      printf("%s", outtext);
+      break;
+    case DEBUG_FILE_ONLY:
+      if ((outfile_fd = fopen(filepath, "a+")) != nullptr) {
+        fwrite(outtext, sizeof(char), strlen(outtext), outfile_fd);
+        fclose(outfile_fd);
+      }
+      break;
+    case DEBUG_SCREEN_AND_FILE:  // BOTH
+      printf("%s", outtext);
+      if ((outfile_fd = fopen(filepath, "a+")) != nullptr) {
+        fwrite(outtext, sizeof(char), strlen(outtext), outfile_fd);
+        fclose(outfile_fd);
+      }
+      break;
+  }
+}
+
+/**
+ * @brief Normalizes a value in a range, used for drive input
+ * @param position The position in the range, starting at 0
+ * @param range The size of the range that position is in
+ * @return The normalized position from -1 to +1
+ */
+double RangeToNormalized(double position, int range) {
+  return (((position * 2.0) / (double)range) - 1.0);
+}
+
+/**
+ * @brief Convert a normalized value to the corresponding value in a range.
+ * This is used to convert normalized values to the servo command range.
+ * @param normalizedValue The normalized value (in the -1 to +1 range)
+ * @param minRange The minimum of the range (0 is default)
+ * @param maxRange The maximum of the range (1 is default)
+ * @return The value in the range corresponding to the input normalized value
+ */
+float NormalizeToRange(float normalizedValue, float minRange, float maxRange) {
+  float range = maxRange - minRange;
+  float temp = (float)((normalizedValue / 2.0) + 0.5) * range;
+  return (temp + minRange);
+}
+float NormalizeToRange(float normalizedValue) {
+  return (float)((normalizedValue / 2.0) + 0.5);
+}
+
+/**
+ * @brief Displays an activity indicator to console.
+ * Call this function like you would call printf.
+ * @param fmt The format string
+*/
+void ShowActivity(char *fmt, ...) {
+  static char activity_indication_string[] = "|/-\\";
+  static int ai = 3;
+  va_list args;
+  char text[1024];
+
+  va_start(args, fmt);
+
+  vsprintf(text, fmt, args);
+
+  ai = ai == 3 ? 0 : ai + 1;
+
+  printf("%c %s \r", activity_indication_string[ai], text);
+  fflush(stdout);
+
+  va_end(args);
+}
+
+#define PI 3.14159265358979
+/**
+ * @brief Calculate sine wave increments (-1.0 to 1.0).
+ * The first time this is called, it sets up the time increment. Subsequent
+ * calls
+ * will give values along the sine wave depending on current time. If the wave
+ * is
+ * stopped and restarted, it must be reinitialized with a new "first call".
+ *
+ * @param period length of time to complete a complete wave
+ * @param sinStart Where to start the sine wave (0.0 = 2 pi, pi/2 = 1.0, etc.)
+ */
+double SinPosition(double *period, double sinStart) {
+  double rtnVal;
+  static double sinePeriod = 0.0;
+  static double timestamp;
+  double sinArg;
+
+  // 1st call
+  if (period != nullptr) {
+    sinePeriod = *period;
+    timestamp = GetTime();
+    return 0.0;
+  }
+
+  // Multiplying by 2*pi to the time difference makes sinePeriod work if it's
+  // measured in seconds.
+  // Adding sinStart to the part multiplied by PI, but not by 2, allows it to
+  // work as described in the comments.
+  sinArg = PI * ((2.0 * (GetTime() - timestamp)) + sinStart) / sinePeriod;
+  rtnVal = sin(sinArg);
+  return (rtnVal);
+}
+
+/**
+ * @brief Find the elapsed time since a specified time.
+ * @param startTime The starting time
+ * @return How long it has been since the starting time
+ */
+double ElapsedTime(double startTime) {
+  double realTime = GetTime();
+  return (realTime - startTime);
+}
+
+/**
+ * @brief Initialize pan parameters
+ * @param period The number of seconds to complete one pan
+ */
+void panInit() {
+  double period = 3.0;        // number of seconds for one complete pan
+  SinPosition(&period, 0.0);  // initial call to set up time
+}
+
+void panInit(double period) {
+  if (period < 0.0) period = 3.0;
+  SinPosition(&period, 0.0);  // initial call to set up time
+}
+
+/**
+ * @brief Move the horizontal servo back and forth.
+ * @param panServo The servo object to move
+ * @param sinStart The position on the sine wave to begin the pan
+ */
+void panForTarget(Servo *panServo) { panForTarget(panServo, 0.0); }
+
+void panForTarget(Servo *panServo, double sinStart) {
+  float normalizedSinPosition = (float)SinPosition(nullptr, sinStart);
+  float newServoPosition = NormalizeToRange(normalizedSinPosition);
+  panServo->Set(newServoPosition);
+  // ShowActivity ("pan x: normalized %f newServoPosition = %f" ,
+  //		normalizedSinPosition, newServoPosition );
+}
+
+/** @brief Read a file and return non-comment output string
+
+Call the first time with 0 lineNumber to get the number of lines to read
+Then call with each lineNumber to get one camera parameter. There should
+be one property=value entry on each line, i.e. "exposure=auto"
+
+ * @param inputFile filename to read
+ * @param outputString one string
+ * @param lineNumber if 0, return number of lines; else return that line number
+ * @return int number of lines or -1 if error
+ **/
+int processFile(char *inputFile, char *outputString, int lineNumber) {
+  FILE *infile;
+  const int stringSize = 80;  // max size of one line in file
+  char inputStr[stringSize];
+  inputStr[0] = '\0';
+  int lineCount = 0;
+
+  if (lineNumber < 0) return (-1);
+
+  if ((infile = fopen(inputFile, "r")) == nullptr) {
+    printf("Fatal error opening file %s\n", inputFile);
+    return (0);
+  }
+
+  while (!feof(infile)) {
+    if (fgets(inputStr, stringSize, infile) != nullptr) {
+      // Skip empty lines
+      if (emptyString(inputStr)) continue;
+      // Skip comment lines
+      if (inputStr[0] == '#' || inputStr[0] == '!') continue;
+
+      lineCount++;
+      if (lineNumber == 0)
+        continue;
+      else {
+        if (lineCount == lineNumber) break;
+      }
+    }
+  }
+
+  // close file
+  fclose(infile);
+  // if number lines requested return the count
+  if (lineNumber == 0) return (lineCount);
+  // check for input out of range
+  if (lineNumber > lineCount) return (-1);
+  // return the line selected; lineCount guaranteed to be greater than zero
+  stripString(inputStr);
+  strcpy(outputString, inputStr);
+  return (lineCount);
+}
+
+/** Ignore empty string
+ * @param string to check if empty
+ **/
+int emptyString(char *string) {
+  int i, len;
+
+  if (string == nullptr) return (1);
+
+  len = strlen(string);
+  for (i = 0; i < len; i++) {
+    // Ignore the following characters
+    if (string[i] == '\n' || string[i] == '\r' || string[i] == '\t' ||
+        string[i] == ' ')
+      continue;
+    return (0);
+  }
+  return (1);
+}
+
+/** Remove special characters from string
+ * @param string to process
+ **/
+void stripString(char *string) {
+  int i, j, len;
+
+  if (string == nullptr) return;
+
+  len = strlen(string);
+  for (i = 0, j = 0; i < len; i++) {
+    // Remove the following characters from the string
+    if (string[i] == '\n' || string[i] == '\r' || string[i] == '\"') continue;
+    // Copy anything else
+    string[j++] = string[i];
+  }
+  string[j] = '\0';
+}
diff --git a/wpilibc/Athena/src/Vision/BinaryImage.cpp b/wpilibc/Athena/src/Vision/BinaryImage.cpp
new file mode 100644
index 0000000..ea14cbc
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/BinaryImage.cpp
@@ -0,0 +1,228 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.
+ */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/BinaryImage.h"
+#include "WPIErrors.h"
+#include <cstring>
+
+using namespace std;
+
+/**
+ * Get then number of particles for the image.
+ * @returns the number of particles found for the image.
+ */
+int BinaryImage::GetNumberParticles() {
+  int numParticles = 0;
+  int success = imaqCountParticles(m_imaqImage, 1, &numParticles);
+  wpi_setImaqErrorWithContext(success, "Error counting particles");
+  return numParticles;
+}
+
+/**
+ * Get a single particle analysis report.
+ * Get one (of possibly many) particle analysis reports for an image.
+ * @param particleNumber Which particle analysis report to return.
+ * @returns the selected particle analysis report
+ */
+ParticleAnalysisReport BinaryImage::GetParticleAnalysisReport(
+    int particleNumber) {
+  ParticleAnalysisReport par;
+  GetParticleAnalysisReport(particleNumber, &par);
+  return par;
+}
+
+/**
+ * Get a single particle analysis report.
+ * Get one (of possibly many) particle analysis reports for an image.
+ * This version could be more efficient when copying many reports.
+ * @param particleNumber Which particle analysis report to return.
+ * @param par the selected particle analysis report
+ */
+void BinaryImage::GetParticleAnalysisReport(int particleNumber,
+                                            ParticleAnalysisReport *par) {
+  int success;
+  int numParticles = 0;
+
+  success = imaqGetImageSize(m_imaqImage, &par->imageWidth, &par->imageHeight);
+  wpi_setImaqErrorWithContext(success, "Error getting image size");
+  if (StatusIsFatal()) return;
+
+  success = imaqCountParticles(m_imaqImage, 1, &numParticles);
+  wpi_setImaqErrorWithContext(success, "Error counting particles");
+  if (StatusIsFatal()) return;
+
+  if (particleNumber >= numParticles) {
+    wpi_setWPIErrorWithContext(ParameterOutOfRange, "particleNumber");
+    return;
+  }
+
+  par->particleIndex = particleNumber;
+  // Don't bother measuring the rest of the particle if one fails
+  bool good = ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_X,
+                                  &par->center_mass_x);
+  good = good && ParticleMeasurement(particleNumber, IMAQ_MT_CENTER_OF_MASS_Y,
+                                     &par->center_mass_y);
+  good = good &&
+         ParticleMeasurement(particleNumber, IMAQ_MT_AREA, &par->particleArea);
+  good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_TOP,
+                                     &par->boundingRect.top);
+  good = good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_LEFT,
+                                     &par->boundingRect.left);
+  good =
+      good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_HEIGHT,
+                                  &par->boundingRect.height);
+  good =
+      good && ParticleMeasurement(particleNumber, IMAQ_MT_BOUNDING_RECT_WIDTH,
+                                  &par->boundingRect.width);
+  good = good && ParticleMeasurement(particleNumber, IMAQ_MT_AREA_BY_IMAGE_AREA,
+                                     &par->particleToImagePercent);
+  good = good && ParticleMeasurement(particleNumber,
+                                     IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA,
+                                     &par->particleQuality);
+
+  if (good) {
+    /* normalized position (-1 to 1) */
+    par->center_mass_x_normalized =
+        NormalizeFromRange(par->center_mass_x, par->imageWidth);
+    par->center_mass_y_normalized =
+        NormalizeFromRange(par->center_mass_y, par->imageHeight);
+  }
+}
+
+/**
+ * Get an ordered vector of particles for the image.
+ * Create a vector of particle analysis reports sorted by size for an image.
+ * The vector contains the actual report structures.
+ * @returns a pointer to the vector of particle analysis reports. The caller
+ * must delete the
+ * vector when finished using it.
+ */
+vector<ParticleAnalysisReport> *
+BinaryImage::GetOrderedParticleAnalysisReports() {
+  auto particles = new vector<ParticleAnalysisReport>;
+  int particleCount = GetNumberParticles();
+  for (int particleIndex = 0; particleIndex < particleCount; particleIndex++) {
+    particles->push_back(GetParticleAnalysisReport(particleIndex));
+  }
+  // TODO: This is pretty inefficient since each compare in the sort copies
+  //   both reports being compared... do it manually instead... while we're
+  //   at it, we should provide a version that allows a preallocated buffer of
+  //   ParticleAnalysisReport structures
+  sort(particles->begin(), particles->end(), CompareParticleSizes);
+  return particles;
+}
+
+/**
+ * Write a binary image to flash.
+ * Writes the binary image to flash on the cRIO for later inspection.
+ * @param fileName the name of the image file written to the flash.
+ */
+void BinaryImage::Write(const char *fileName) {
+  RGBValue colorTable[256];
+  memset(colorTable, 0, sizeof(colorTable));
+  colorTable[0].R = 0;
+  colorTable[1].R = 255;
+  colorTable[0].G = colorTable[1].G = 0;
+  colorTable[0].B = colorTable[1].B = 0;
+  colorTable[0].alpha = colorTable[1].alpha = 0;
+  imaqWriteFile(m_imaqImage, fileName, colorTable);
+}
+
+/**
+ * Measure a single parameter for an image.
+ * Get the measurement for a single parameter about an image by calling the
+ * imaqMeasureParticle
+ * function for the selected parameter.
+ * @param particleNumber which particle in the set of particles
+ * @param whatToMeasure the imaq MeasurementType (what to measure)
+ * @param result the value of the measurement
+ * @returns false on failure, true on success
+ */
+bool BinaryImage::ParticleMeasurement(int particleNumber,
+                                      MeasurementType whatToMeasure,
+                                      int *result) {
+  double resultDouble;
+  bool success =
+      ParticleMeasurement(particleNumber, whatToMeasure, &resultDouble);
+  *result = (int)resultDouble;
+  return success;
+}
+
+/**
+ * Measure a single parameter for an image.
+ * Get the measurement for a single parameter about an image by calling the
+ * imaqMeasureParticle
+ * function for the selected parameter.
+ * @param particleNumber which particle in the set of particles
+ * @param whatToMeasure the imaq MeasurementType (what to measure)
+ * @param result the value of the measurement
+ * @returns true on failure, false on success
+ */
+bool BinaryImage::ParticleMeasurement(int particleNumber,
+                                      MeasurementType whatToMeasure,
+                                      double *result) {
+  int success;
+  success = imaqMeasureParticle(m_imaqImage, particleNumber, 0, whatToMeasure,
+                                result);
+  wpi_setImaqErrorWithContext(success, "Error measuring particle");
+  return !StatusIsFatal();
+}
+
+// Normalizes to [-1,1]
+double BinaryImage::NormalizeFromRange(double position, int range) {
+  return (position * 2.0 / (double)range) - 1.0;
+}
+
+/**
+ * The compare helper function for sort.
+ * This function compares two particle analysis reports as a helper for the sort
+ * function.
+ * @param particle1 The first particle to compare
+ * @param particle2 the second particle to compare
+ * @returns true if particle1 is greater than particle2
+ */
+bool BinaryImage::CompareParticleSizes(ParticleAnalysisReport particle1,
+                                       ParticleAnalysisReport particle2) {
+  // we want descending sort order
+  return particle1.particleToImagePercent > particle2.particleToImagePercent;
+}
+
+BinaryImage *BinaryImage::RemoveSmallObjects(bool connectivity8, int erosions) {
+  auto result = new BinaryImage();
+  int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage,
+                               connectivity8, erosions, IMAQ_KEEP_LARGE, nullptr);
+  wpi_setImaqErrorWithContext(success, "Error in RemoveSmallObjects");
+  return result;
+}
+
+BinaryImage *BinaryImage::RemoveLargeObjects(bool connectivity8, int erosions) {
+  auto result = new BinaryImage();
+  int success = imaqSizeFilter(result->GetImaqImage(), m_imaqImage,
+                               connectivity8, erosions, IMAQ_KEEP_SMALL, nullptr);
+  wpi_setImaqErrorWithContext(success, "Error in RemoveLargeObjects");
+  return result;
+}
+
+BinaryImage *BinaryImage::ConvexHull(bool connectivity8) {
+  auto result = new BinaryImage();
+  int success =
+      imaqConvexHull(result->GetImaqImage(), m_imaqImage, connectivity8);
+  wpi_setImaqErrorWithContext(success, "Error in convex hull operation");
+  return result;
+}
+
+BinaryImage *BinaryImage::ParticleFilter(ParticleFilterCriteria2 *criteria,
+                                         int criteriaCount) {
+  auto result = new BinaryImage();
+  int numParticles;
+  ParticleFilterOptions2 filterOptions = {0, 0, 0, 1};
+  int success =
+      imaqParticleFilter4(result->GetImaqImage(), m_imaqImage, criteria,
+                          criteriaCount, &filterOptions, nullptr, &numParticles);
+  wpi_setImaqErrorWithContext(success, "Error in particle filter operation");
+  return result;
+}
diff --git a/wpilibc/Athena/src/Vision/ColorImage.cpp b/wpilibc/Athena/src/Vision/ColorImage.cpp
new file mode 100644
index 0000000..bbbc242
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/ColorImage.cpp
@@ -0,0 +1,444 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/ColorImage.h"
+
+#include "WPIErrors.h"
+
+ColorImage::ColorImage(ImageType type) : ImageBase(type) {}
+
+/**
+ * Perform a threshold operation on a ColorImage.
+ * Perform a threshold operation on a ColorImage using the ColorMode supplied
+ * as a parameter.
+ * @param colorMode The type of colorspace this operation should be performed in
+ * @returns a pointer to a binary image
+ */
+BinaryImage *ColorImage::ComputeThreshold(ColorMode colorMode, int low1,
+                                          int high1, int low2, int high2,
+                                          int low3, int high3) {
+  auto result = new BinaryImage();
+  Range range1 = {low1, high1}, range2 = {low2, high2}, range3 = {low3, high3};
+
+  int success = imaqColorThreshold(result->GetImaqImage(), m_imaqImage, 1,
+                                   colorMode, &range1, &range2, &range3);
+  wpi_setImaqErrorWithContext(success, "ImaqThreshold error");
+  return result;
+}
+
+/**
+ * Perform a threshold in RGB space.
+ * @param redLow Red low value
+ * @param redHigh Red high value
+ * @param greenLow Green low value
+ * @param greenHigh Green high value
+ * @param blueLow Blue low value
+ * @param blueHigh Blue high value
+ * @returns A pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdRGB(int redLow, int redHigh, int greenLow,
+                                      int greenHigh, int blueLow,
+                                      int blueHigh) {
+  return ComputeThreshold(IMAQ_RGB, redLow, redHigh, greenLow, greenHigh,
+                          blueLow, blueHigh);
+}
+
+/**
+ * Perform a threshold in RGB space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdRGB(Threshold &t) {
+  return ComputeThreshold(IMAQ_RGB, t.plane1Low, t.plane1High, t.plane2Low,
+                          t.plane2High, t.plane3Low, t.plane3High);
+}
+
+/**
+ * Perform a threshold in HSL space.
+ * @param hueLow Low value for hue
+ * @param hueHigh High value for hue
+ * @param saturationLow Low value for saturation
+ * @param saturationHigh High value for saturation
+ * @param luminenceLow Low value for luminence
+ * @param luminenceHigh High value for luminence
+ * @returns a pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSL(int hueLow, int hueHigh,
+                                      int saturationLow, int saturationHigh,
+                                      int luminenceLow, int luminenceHigh) {
+  return ComputeThreshold(IMAQ_HSL, hueLow, hueHigh, saturationLow,
+                          saturationHigh, luminenceLow, luminenceHigh);
+}
+
+/**
+ * Perform a threshold in HSL space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSL(Threshold &t) {
+  return ComputeThreshold(IMAQ_HSL, t.plane1Low, t.plane1High, t.plane2Low,
+                          t.plane2High, t.plane3Low, t.plane3High);
+}
+
+/**
+ * Perform a threshold in HSV space.
+ * @param hueLow Low value for hue
+ * @param hueHigh High value for hue
+ * @param saturationLow Low value for saturation
+ * @param saturationHigh High value for saturation
+ * @param valueLow Low value
+ * @param valueHigh High value
+ * @returns a pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSV(int hueLow, int hueHigh,
+                                      int saturationLow, int saturationHigh,
+                                      int valueLow, int valueHigh) {
+  return ComputeThreshold(IMAQ_HSV, hueLow, hueHigh, saturationLow,
+                          saturationHigh, valueLow, valueHigh);
+}
+
+/**
+ * Perform a threshold in HSV space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSV(Threshold &t) {
+  return ComputeThreshold(IMAQ_HSV, t.plane1Low, t.plane1High, t.plane2Low,
+                          t.plane2High, t.plane3Low, t.plane3High);
+}
+
+/**
+ * Perform a threshold in HSI space.
+ * @param hueLow Low value for hue
+ * @param hueHigh High value for hue
+ * @param saturationLow Low value for saturation
+ * @param saturationHigh High value for saturation
+ * @param valueLow Low intensity
+ * @param valueHigh High intensity
+ * @returns a pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSI(int hueLow, int hueHigh,
+                                      int saturationLow, int saturationHigh,
+                                      int intensityLow, int intensityHigh) {
+  return ComputeThreshold(IMAQ_HSI, hueLow, hueHigh, saturationLow,
+                          saturationHigh, intensityLow, intensityHigh);
+}
+
+/**
+ * Perform a threshold in HSI space.
+ * @param threshold a reference to the Threshold object to use.
+ * @returns A pointer to a BinaryImage that represents the result of the
+ * threshold operation.
+ */
+BinaryImage *ColorImage::ThresholdHSI(Threshold &t) {
+  return ComputeThreshold(IMAQ_HSI, t.plane1Low, t.plane1High, t.plane2Low,
+                          t.plane2High, t.plane3Low, t.plane3High);
+}
+
+/**
+ * Extract a color plane from the image
+ * @param mode The ColorMode to use for the plane extraction
+ * @param planeNumber Which plane is to be extracted
+ * @returns A pointer to a MonoImage that represents the extracted plane.
+ */
+MonoImage *ColorImage::ExtractColorPlane(ColorMode mode, int planeNumber) {
+  auto result = new MonoImage();
+  if (m_imaqImage == nullptr) wpi_setWPIError(NullParameter);
+  int success = imaqExtractColorPlanes(
+      m_imaqImage, mode, (planeNumber == 1) ? result->GetImaqImage() : nullptr,
+      (planeNumber == 2) ? result->GetImaqImage() : nullptr,
+      (planeNumber == 3) ? result->GetImaqImage() : nullptr);
+  wpi_setImaqErrorWithContext(success, "Imaq ExtractColorPlanes failed");
+  return result;
+}
+
+/*
+ * Extract the first color plane for an image.
+ * @param mode The color mode in which to operate
+ * @returns a pointer to a MonoImage that is the extracted plane.
+ */
+MonoImage *ColorImage::ExtractFirstColorPlane(ColorMode mode) {
+  return ExtractColorPlane(mode, 1);
+}
+
+/*
+ * Extract the second color plane for an image.
+ * @param mode The color mode in which to operate
+ * @returns a pointer to a MonoImage that is the extracted plane.
+ */
+MonoImage *ColorImage::ExtractSecondColorPlane(ColorMode mode) {
+  return ExtractColorPlane(mode, 2);
+}
+
+/*
+ * Extract the third color plane for an image.
+ * @param mode The color mode in which to operate
+ * @returns a pointer to a MonoImage that is the extracted plane.
+ */
+MonoImage *ColorImage::ExtractThirdColorPlane(ColorMode mode) {
+  return ExtractColorPlane(mode, 3);
+}
+
+/*
+ * Extract the red plane from an RGB image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetRedPlane() {
+  return ExtractFirstColorPlane(IMAQ_RGB);
+}
+
+/*
+ * Extract the green plane from an RGB image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetGreenPlane() {
+  return ExtractSecondColorPlane(IMAQ_RGB);
+}
+
+/*
+ * Extract the blue plane from an RGB image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetBluePlane() {
+  return ExtractThirdColorPlane(IMAQ_RGB);
+}
+
+/*
+ * Extract the Hue plane from an HSL image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetHSLHuePlane() {
+  return ExtractFirstColorPlane(IMAQ_HSL);
+}
+
+/*
+ * Extract the Hue plane from an HSV image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetHSVHuePlane() {
+  return ExtractFirstColorPlane(IMAQ_HSV);
+}
+
+/*
+ * Extract the Hue plane from an HSI image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetHSIHuePlane() {
+  return ExtractFirstColorPlane(IMAQ_HSI);
+}
+
+/*
+ * Extract the Luminance plane from an HSL image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetLuminancePlane() {
+  return ExtractThirdColorPlane(IMAQ_HSL);
+}
+
+/*
+ * Extract the Value plane from an HSV image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetValuePlane() {
+  return ExtractThirdColorPlane(IMAQ_HSV);
+}
+
+/*
+ * Extract the Intensity plane from an HSI image.
+ * @returns a pointer to a MonoImage that is the extraced plane.
+ */
+MonoImage *ColorImage::GetIntensityPlane() {
+  return ExtractThirdColorPlane(IMAQ_HSI);
+}
+
+/**
+ * Replace a plane in the ColorImage with a MonoImage
+ * Replaces a single plane in the image with a MonoImage
+ * @param mode The ColorMode in which to operate
+ * @param plane The pointer to the replacement plane as a MonoImage
+ * @param planeNumber The plane number (1, 2, 3) to replace
+ */
+void ColorImage::ReplacePlane(ColorMode mode, MonoImage *plane,
+                              int planeNumber) {
+  int success =
+      imaqReplaceColorPlanes(m_imaqImage, (const Image *)m_imaqImage, mode,
+                             (planeNumber == 1) ? plane->GetImaqImage() : nullptr,
+                             (planeNumber == 2) ? plane->GetImaqImage() : nullptr,
+                             (planeNumber == 3) ? plane->GetImaqImage() : nullptr);
+  wpi_setImaqErrorWithContext(success, "Imaq ReplaceColorPlanes failed");
+}
+
+/**
+ * Replace the first color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceFirstColorPlane(ColorMode mode, MonoImage *plane) {
+  ReplacePlane(mode, plane, 1);
+}
+
+/**
+ * Replace the second color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceSecondColorPlane(ColorMode mode, MonoImage *plane) {
+  ReplacePlane(mode, plane, 2);
+}
+
+/**
+ * Replace the third color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceThirdColorPlane(ColorMode mode, MonoImage *plane) {
+  ReplacePlane(mode, plane, 3);
+}
+
+/**
+ * Replace the red color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceRedPlane(MonoImage *plane) {
+  ReplaceFirstColorPlane(IMAQ_RGB, plane);
+}
+
+/**
+ * Replace the green color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceGreenPlane(MonoImage *plane) {
+  ReplaceSecondColorPlane(IMAQ_RGB, plane);
+}
+
+/**
+ * Replace the blue color plane with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceBluePlane(MonoImage *plane) {
+  ReplaceThirdColorPlane(IMAQ_RGB, plane);
+}
+
+/**
+ * Replace the Hue color plane in a HSL image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceHSLHuePlane(MonoImage *plane) {
+  return ReplaceFirstColorPlane(IMAQ_HSL, plane);
+}
+
+/**
+ * Replace the Hue color plane in a HSV image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceHSVHuePlane(MonoImage *plane) {
+  return ReplaceFirstColorPlane(IMAQ_HSV, plane);
+}
+
+/**
+ * Replace the first Hue plane in a HSI image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceHSIHuePlane(MonoImage *plane) {
+  return ReplaceFirstColorPlane(IMAQ_HSI, plane);
+}
+
+/**
+ * Replace the Saturation color plane in an HSL image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceHSLSaturationPlane(MonoImage *plane) {
+  return ReplaceSecondColorPlane(IMAQ_HSL, plane);
+}
+
+/**
+ * Replace the Saturation color plane in a HSV image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceHSVSaturationPlane(MonoImage *plane) {
+  return ReplaceSecondColorPlane(IMAQ_HSV, plane);
+}
+
+/**
+ * Replace the Saturation color plane in a HSI image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceHSISaturationPlane(MonoImage *plane) {
+  return ReplaceSecondColorPlane(IMAQ_HSI, plane);
+}
+
+/**
+ * Replace the Luminance color plane in an HSL image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceLuminancePlane(MonoImage *plane) {
+  return ReplaceThirdColorPlane(IMAQ_HSL, plane);
+}
+
+/**
+ * Replace the Value color plane in an HSV with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceValuePlane(MonoImage *plane) {
+  return ReplaceThirdColorPlane(IMAQ_HSV, plane);
+}
+
+/**
+ * Replace the Intensity color plane in a HSI image with a MonoImage.
+ * @param mode The color mode in which to operate.
+ * @param plane A pointer to a MonoImage that will replace the specified color
+ * plane.
+ */
+void ColorImage::ReplaceIntensityPlane(MonoImage *plane) {
+  return ReplaceThirdColorPlane(IMAQ_HSI, plane);
+}
+
+// TODO: frcColorEqualize(Image* dest, const Image* source, int
+// colorEqualization) needs to be modified
+// The colorEqualization parameter is discarded and is set to TRUE in the call
+// to imaqColorEqualize.
+void ColorImage::Equalize(bool allPlanes) {
+  // Note that this call uses NI-defined TRUE and FALSE
+  int success = imaqColorEqualize(m_imaqImage, (const Image *)m_imaqImage,
+                                  (allPlanes) ? TRUE : FALSE);
+  wpi_setImaqErrorWithContext(success, "Imaq ColorEqualize error");
+}
+
+void ColorImage::ColorEqualize() { Equalize(true); }
+
+void ColorImage::LuminanceEqualize() { Equalize(false); }
diff --git a/wpilibc/Athena/src/Vision/FrcError.cpp b/wpilibc/Athena/src/Vision/FrcError.cpp
new file mode 100644
index 0000000..f0b4725
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/FrcError.cpp
@@ -0,0 +1,2403 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "nivision.h"
+#include "Vision/FrcError.h"
+
+/**
+ * Get the error code returned from the NI Vision library
+ * @return The last error code.
+ */
+int GetLastVisionError() {
+  // int errorCode = imaqGetLastVisionError();     // error code: 0 = no error
+  // char* errorText = GetVisionErrorText(errorCode);
+  // dprintf (LOG_DEBUG, "Error = %i  %s ", errorCode, errorText);
+  return imaqGetLastError();
+}
+
+/**
+* Get the error text for an NI Vision error code.
+* Note: imaqGetErrorText() is not supported on real time system, so
+* so relevant strings are hardcoded here - the maintained version is
+* in the LabWindows/CVI help file.
+* @param errorCode The error code to find the text for.
+* @return The error text
+*/
+const char* GetVisionErrorText(int errorCode) {
+  const char* errorText;
+
+  switch (errorCode) {
+    default: {
+      errorText = "UNKNOWN_ERROR";
+      break;
+    }
+    case -1074395138: {
+      errorText = "ERR_OCR_REGION_TOO_SMALL";
+      break;
+    }
+    case -1074395139: {
+      errorText = "ERR_IMAQ_QR_DIMENSION_INVALID";
+      break;
+    }
+    case -1074395140: {
+      errorText = "ERR_OCR_CHAR_REPORT_CORRUPTED";
+      break;
+    }
+    case -1074395141: {
+      errorText = "ERR_OCR_NO_TEXT_FOUND";
+      break;
+    }
+    case -1074395142: {
+      errorText = "ERR_QR_DETECTION_MODELTYPE";
+      break;
+    }
+    case -1074395143: {
+      errorText = "ERR_QR_DETECTION_MODE";
+      break;
+    }
+    case -1074395144: {
+      errorText = "ERR_QR_INVALID_BARCODE";
+      break;
+    }
+    case -1074395145: {
+      errorText = "ERR_QR_INVALID_READ";
+      break;
+    }
+    case -1074395146: {
+      errorText = "ERR_QR_DETECTION_VERSION";
+      break;
+    }
+    case -1074395147: {
+      errorText = "ERR_BARCODE_RSSLIMITED";
+      break;
+    }
+    case -1074395148: {
+      errorText = "ERR_OVERLAY_GROUP_NOT_FOUND";
+      break;
+    }
+    case -1074395149: {
+      errorText = "ERR_DUPLICATE_TRANSFORM_TYPE";
+      break;
+    }
+    case -1074395151: {
+      errorText = "ERR_OCR_CORRECTION_FAILED";
+      break;
+    }
+    case -1074395155: {
+      errorText = "ERR_OCR_ORIENT_DETECT_FAILED";
+      break;
+    }
+    case -1074395156: {
+      errorText = "ERR_OCR_SKEW_DETECT_FAILED";
+      break;
+    }
+    case -1074395158: {
+      errorText = "ERR_OCR_INVALID_CONTRASTMODE";
+      break;
+    }
+    case -1074395159: {
+      errorText = "ERR_OCR_INVALID_TOLERANCE";
+      break;
+    }
+    case -1074395160: {
+      errorText = "ERR_OCR_INVALID_MAXPOINTSIZE";
+      break;
+    }
+    case -1074395161: {
+      errorText = "ERR_OCR_INVALID_CORRECTIONLEVEL";
+      break;
+    }
+    case -1074395162: {
+      errorText = "ERR_OCR_INVALID_CORRECTIONMODE";
+      break;
+    }
+    case -1074395163: {
+      errorText = "ERR_OCR_INVALID_CHARACTERPREFERENCE";
+      break;
+    }
+    case -1074395164: {
+      errorText = "ERR_OCR_ADD_WORD_FAILED";
+      break;
+    }
+    case -1074395165: {
+      errorText = "ERR_OCR_WTS_DIR_NOT_FOUND";
+      break;
+    }
+    case -1074395166: {
+      errorText = "ERR_OCR_BIN_DIR_NOT_FOUND";
+      break;
+    }
+    case -1074395167: {
+      errorText = "ERR_OCR_INVALID_OUTPUTDELIMITER";
+      break;
+    }
+    case -1074395168: {
+      errorText = "ERR_OCR_INVALID_AUTOCORRECTIONMODE";
+      break;
+    }
+    case -1074395169: {
+      errorText = "ERR_OCR_INVALID_RECOGNITIONMODE";
+      break;
+    }
+    case -1074395170: {
+      errorText = "ERR_OCR_INVALID_CHARACTERTYPE";
+      break;
+    }
+    case -1074395171: {
+      errorText = "ERR_OCR_INI_FILE_NOT_FOUND";
+      break;
+    }
+    case -1074395172: {
+      errorText = "ERR_OCR_INVALID_CHARACTERSET";
+      break;
+    }
+    case -1074395173: {
+      errorText = "ERR_OCR_INVALID_LANGUAGE";
+      break;
+    }
+    case -1074395174: {
+      errorText = "ERR_OCR_INVALID_AUTOORIENTMODE";
+      break;
+    }
+    case -1074395175: {
+      errorText = "ERR_OCR_BAD_USER_DICTIONARY";
+      break;
+    }
+    case -1074395178: {
+      errorText = "ERR_OCR_RECOGNITION_FAILED";
+      break;
+    }
+    case -1074395179: {
+      errorText = "ERR_OCR_PREPROCESSING_FAILED";
+      break;
+    }
+    case -1074395200: {
+      errorText = "ERR_OCR_INVALID_PARAMETER";
+      break;
+    }
+    case -1074395201: {
+      errorText = "ERR_OCR_LOAD_LIBRARY";
+      break;
+    }
+    case -1074395203: {
+      errorText = "ERR_OCR_LIB_INIT";
+      break;
+    }
+    case -1074395210: {
+      errorText = "ERR_OCR_CANNOT_MATCH_TEXT_TEMPLATE";
+      break;
+    }
+    case -1074395211: {
+      errorText = "ERR_OCR_BAD_TEXT_TEMPLATE";
+      break;
+    }
+    case -1074395212: {
+      errorText = "ERR_OCR_TEMPLATE_WRONG_SIZE";
+      break;
+    }
+    case -1074395233: {
+      errorText = "ERR_TEMPLATE_IMAGE_TOO_LARGE";
+      break;
+    }
+    case -1074395234: {
+      errorText = "ERR_TEMPLATE_IMAGE_TOO_SMALL";
+      break;
+    }
+    case -1074395235: {
+      errorText = "ERR_TEMPLATE_IMAGE_CONTRAST_TOO_LOW";
+      break;
+    }
+    case -1074395237: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_SHIFT_1";
+      break;
+    }
+    case -1074395238: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_NOSHIFT";
+      break;
+    }
+    case -1074395239: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_SHIFT";
+      break;
+    }
+    case -1074395240: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_ROTATION_1";
+      break;
+    }
+    case -1074395241: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_NOROTATION";
+      break;
+    }
+    case -1074395242: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_ROTATION";
+      break;
+    }
+    case -1074395243: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_4";
+      break;
+    }
+    case -1074395244: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_3";
+      break;
+    }
+    case -1074395245: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_2";
+      break;
+    }
+    case -1074395246: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR_1";
+      break;
+    }
+    case -1074395247: {
+      errorText = "ERR_TEMPLATE_DESCRIPTOR";
+      break;
+    }
+    case -1074395248: {
+      errorText = "ERR_TOO_MANY_ROTATION_ANGLE_RANGES";
+      break;
+    }
+    case -1074395249: {
+      errorText = "ERR_ROTATION_ANGLE_RANGE_TOO_LARGE";
+      break;
+    }
+    case -1074395250: {
+      errorText = "ERR_MATCH_SETUP_DATA";
+      break;
+    }
+    case -1074395251: {
+      errorText = "ERR_INVALID_MATCH_MODE";
+      break;
+    }
+    case -1074395252: {
+      errorText = "ERR_LEARN_SETUP_DATA";
+      break;
+    }
+    case -1074395253: {
+      errorText = "ERR_INVALID_LEARN_MODE";
+      break;
+    }
+    case -1074395256: {
+      errorText = "ERR_EVEN_WINDOW_SIZE";
+      break;
+    }
+    case -1074395257: {
+      errorText = "ERR_INVALID_EDGE_DIR";
+      break;
+    }
+    case -1074395258: {
+      errorText = "ERR_BAD_FILTER_WIDTH";
+      break;
+    }
+    case -1074395260: {
+      errorText = "ERR_HEAP_TRASHED";
+      break;
+    }
+    case -1074395261: {
+      errorText = "ERR_GIP_RANGE";
+      break;
+    }
+    case -1074395262: {
+      errorText = "ERR_LCD_BAD_MATCH";
+      break;
+    }
+    case -1074395263: {
+      errorText = "ERR_LCD_NO_SEGMENTS";
+      break;
+    }
+    case -1074395265: {
+      errorText = "ERR_BARCODE";
+      break;
+    }
+    case -1074395267: {
+      errorText = "ERR_COMPLEX_ROOT";
+      break;
+    }
+    case -1074395268: {
+      errorText = "ERR_LINEAR_COEFF";
+      break;
+    }
+    case -1074395269: {
+      errorText = "ERR_nullptr_POINTER";
+      break;
+    }
+    case -1074395270: {
+      errorText = "ERR_DIV_BY_ZERO";
+      break;
+    }
+    case -1074395275: {
+      errorText = "ERR_INVALID_BROWSER_IMAGE";
+      break;
+    }
+    case -1074395276: {
+      errorText = "ERR_LINES_PARALLEL";
+      break;
+    }
+    case -1074395277: {
+      errorText = "ERR_BARCODE_CHECKSUM";
+      break;
+    }
+    case -1074395278: {
+      errorText = "ERR_LCD_NOT_NUMERIC";
+      break;
+    }
+    case -1074395279: {
+      errorText = "ERR_ROI_NOT_POLYGON";
+      break;
+    }
+    case -1074395280: {
+      errorText = "ERR_ROI_NOT_RECT";
+      break;
+    }
+    case -1074395281: {
+      errorText = "ERR_IMAGE_SMALLER_THAN_BORDER";
+      break;
+    }
+    case -1074395282: {
+      errorText = "ERR_CANT_DRAW_INTO_VIEWER";
+      break;
+    }
+    case -1074395283: {
+      errorText = "ERR_INVALID_RAKE_DIRECTION";
+      break;
+    }
+    case -1074395284: {
+      errorText = "ERR_INVALID_EDGE_PROCESS";
+      break;
+    }
+    case -1074395285: {
+      errorText = "ERR_INVALID_SPOKE_DIRECTION";
+      break;
+    }
+    case -1074395286: {
+      errorText = "ERR_INVALID_CONCENTRIC_RAKE_DIRECTION";
+      break;
+    }
+    case -1074395287: {
+      errorText = "ERR_INVALID_LINE";
+      break;
+    }
+    case -1074395290: {
+      errorText = "ERR_SHAPEMATCH_BADTEMPLATE";
+      break;
+    }
+    case -1074395291: {
+      errorText = "ERR_SHAPEMATCH_BADIMAGEDATA";
+      break;
+    }
+    case -1074395292: {
+      errorText = "ERR_POINTS_ARE_COLLINEAR";
+      break;
+    }
+    case -1074395293: {
+      errorText = "ERR_CONTOURID_NOT_FOUND";
+      break;
+    }
+    case -1074395294: {
+      errorText = "ERR_CONTOUR_INDEX_OUT_OF_RANGE";
+      break;
+    }
+    case -1074395295: {
+      errorText = "ERR_INVALID_INTERPOLATIONMETHOD_INTERPOLATEPOINTS";
+      break;
+    }
+    case -1074395296: {
+      errorText = "ERR_INVALID_BARCODETYPE";
+      break;
+    }
+    case -1074395297: {
+      errorText = "ERR_INVALID_PARTICLEINFOMODE";
+      break;
+    }
+    case -1074395298: {
+      errorText = "ERR_COMPLEXPLANE_NOT_REAL_OR_IMAGINARY";
+      break;
+    }
+    case -1074395299: {
+      errorText = "ERR_INVALID_COMPLEXPLANE";
+      break;
+    }
+    case -1074395300: {
+      errorText = "ERR_INVALID_METERARCMODE";
+      break;
+    }
+    case -1074395301: {
+      errorText = "ERR_ROI_NOT_2_LINES";
+      break;
+    }
+    case -1074395302: {
+      errorText = "ERR_INVALID_THRESHOLDMETHOD";
+      break;
+    }
+    case -1074395303: {
+      errorText = "ERR_INVALID_NUM_OF_CLASSES";
+      break;
+    }
+    case -1074395304: {
+      errorText = "ERR_INVALID_MATHTRANSFORMMETHOD";
+      break;
+    }
+    case -1074395305: {
+      errorText = "ERR_INVALID_REFERENCEMODE";
+      break;
+    }
+    case -1074395306: {
+      errorText = "ERR_INVALID_TOOL";
+      break;
+    }
+    case -1074395307: {
+      errorText = "ERR_PRECISION_NOT_GTR_THAN_0";
+      break;
+    }
+    case -1074395308: {
+      errorText = "ERR_INVALID_COLORSENSITIVITY";
+      break;
+    }
+    case -1074395309: {
+      errorText = "ERR_INVALID_WINDOW_THREAD_POLICY";
+      break;
+    }
+    case -1074395310: {
+      errorText = "ERR_INVALID_PALETTE_TYPE";
+      break;
+    }
+    case -1074395311: {
+      errorText = "ERR_INVALID_COLOR_SPECTRUM";
+      break;
+    }
+    case -1074395312: {
+      errorText = "ERR_LCD_CALIBRATE";
+      break;
+    }
+    case -1074395313: {
+      errorText = "ERR_WRITE_FILE_NOT_SUPPORTED";
+      break;
+    }
+    case -1074395316: {
+      errorText = "ERR_INVALID_KERNEL_CODE";
+      break;
+    }
+    case -1074395317: {
+      errorText = "ERR_UNDEF_POINT";
+      break;
+    }
+    case -1074395318: {
+      errorText = "ERR_INSF_POINTS";
+      break;
+    }
+    case -1074395319: {
+      errorText = "ERR_INVALID_SUBPIX_TYPE";
+      break;
+    }
+    case -1074395320: {
+      errorText = "ERR_TEMPLATE_EMPTY";
+      break;
+    }
+    case -1074395321: {
+      errorText = "ERR_INVALID_MORPHOLOGYMETHOD";
+      break;
+    }
+    case -1074395322: {
+      errorText = "ERR_INVALID_TEXTALIGNMENT";
+      break;
+    }
+    case -1074395323: {
+      errorText = "ERR_INVALID_FONTCOLOR";
+      break;
+    }
+    case -1074395324: {
+      errorText = "ERR_INVALID_SHAPEMODE";
+      break;
+    }
+    case -1074395325: {
+      errorText = "ERR_INVALID_DRAWMODE";
+      break;
+    }
+    case -1074395326: {
+      errorText = "ERR_INVALID_DRAWMODE_FOR_LINE";
+      break;
+    }
+    case -1074395327: {
+      errorText = "ERR_INVALID_SCALINGMODE";
+      break;
+    }
+    case -1074395328: {
+      errorText = "ERR_INVALID_INTERPOLATIONMETHOD";
+      break;
+    }
+    case -1074395329: {
+      errorText = "ERR_INVALID_OUTLINEMETHOD";
+      break;
+    }
+    case -1074395330: {
+      errorText = "ERR_INVALID_BORDER_SIZE";
+      break;
+    }
+    case -1074395331: {
+      errorText = "ERR_INVALID_BORDERMETHOD";
+      break;
+    }
+    case -1074395332: {
+      errorText = "ERR_INVALID_COMPAREFUNCTION";
+      break;
+    }
+    case -1074395333: {
+      errorText = "ERR_INVALID_VERTICAL_TEXT_ALIGNMENT";
+      break;
+    }
+    case -1074395334: {
+      errorText = "ERR_INVALID_CONVERSIONSTYLE";
+      break;
+    }
+    case -1074395335: {
+      errorText = "ERR_DISPATCH_STATUS_CONFLICT";
+      break;
+    }
+    case -1074395336: {
+      errorText = "ERR_UNKNOWN_ALGORITHM";
+      break;
+    }
+    case -1074395340: {
+      errorText = "ERR_INVALID_SIZETYPE";
+      break;
+    }
+    case -1074395343: {
+      errorText = "ERR_FILE_FILENAME_nullptr";
+      break;
+    }
+    case -1074395345: {
+      errorText = "ERR_INVALID_FLIPAXIS";
+      break;
+    }
+    case -1074395346: {
+      errorText = "ERR_INVALID_INTERPOLATIONMETHOD_FOR_ROTATE";
+      break;
+    }
+    case -1074395347: {
+      errorText = "ERR_INVALID_3DDIRECTION";
+      break;
+    }
+    case -1074395348: {
+      errorText = "ERR_INVALID_3DPLANE";
+      break;
+    }
+    case -1074395349: {
+      errorText = "ERR_INVALID_SKELETONMETHOD";
+      break;
+    }
+    case -1074395350: {
+      errorText = "ERR_INVALID_VISION_INFO";
+      break;
+    }
+    case -1074395351: {
+      errorText = "ERR_INVALID_RECT";
+      break;
+    }
+    case -1074395352: {
+      errorText = "ERR_INVALID_FEATURE_MODE";
+      break;
+    }
+    case -1074395353: {
+      errorText = "ERR_INVALID_SEARCH_STRATEGY";
+      break;
+    }
+    case -1074395354: {
+      errorText = "ERR_INVALID_COLOR_WEIGHT";
+      break;
+    }
+    case -1074395355: {
+      errorText = "ERR_INVALID_NUM_MATCHES_REQUESTED";
+      break;
+    }
+    case -1074395356: {
+      errorText = "ERR_INVALID_MIN_MATCH_SCORE";
+      break;
+    }
+    case -1074395357: {
+      errorText = "ERR_INVALID_COLOR_IGNORE_MODE";
+      break;
+    }
+    case -1074395360: {
+      errorText = "ERR_COMPLEX_PLANE";
+      break;
+    }
+    case -1074395361: {
+      errorText = "ERR_INVALID_STEEPNESS";
+      break;
+    }
+    case -1074395362: {
+      errorText = "ERR_INVALID_WIDTH";
+      break;
+    }
+    case -1074395363: {
+      errorText = "ERR_INVALID_SUBSAMPLING_RATIO";
+      break;
+    }
+    case -1074395364: {
+      errorText = "ERR_IGNORE_COLOR_SPECTRUM_SET";
+      break;
+    }
+    case -1074395365: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSPECTRUM";
+      break;
+    }
+    case -1074395366: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSHAPE";
+      break;
+    }
+    case -1074395367: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_5";
+      break;
+    }
+    case -1074395368: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_4";
+      break;
+    }
+    case -1074395369: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_3";
+      break;
+    }
+    case -1074395370: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_2";
+      break;
+    }
+    case -1074395371: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION_1";
+      break;
+    }
+    case -1074395372: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOROTATION";
+      break;
+    }
+    case -1074395373: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_ROTATION";
+      break;
+    }
+    case -1074395374: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT_2";
+      break;
+    }
+    case -1074395375: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT_1";
+      break;
+    }
+    case -1074395376: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_NOSHIFT";
+      break;
+    }
+    case -1074395377: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_SHIFT";
+      break;
+    }
+    case -1074395378: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_6";
+      break;
+    }
+    case -1074395379: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_5";
+      break;
+    }
+    case -1074395380: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_4";
+      break;
+    }
+    case -1074395381: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_3";
+      break;
+    }
+    case -1074395382: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_2";
+      break;
+    }
+    case -1074395383: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR_1";
+      break;
+    }
+    case -1074395384: {
+      errorText = "ERR_COLOR_TEMPLATE_DESCRIPTOR";
+      break;
+    }
+    case -1074395385: {
+      errorText = "ERR_COLOR_ROTATION_REQUIRES_SHAPE_FEATURE";
+      break;
+    }
+    case -1074395386: {
+      errorText = "ERR_COLOR_MATCH_SETUP_DATA_SHAPE";
+      break;
+    }
+    case -1074395387: {
+      errorText = "ERR_COLOR_MATCH_SETUP_DATA";
+      break;
+    }
+    case -1074395388: {
+      errorText = "ERR_COLOR_LEARN_SETUP_DATA_SHAPE";
+      break;
+    }
+    case -1074395389: {
+      errorText = "ERR_COLOR_LEARN_SETUP_DATA";
+      break;
+    }
+    case -1074395390: {
+      errorText = "ERR_COLOR_TEMPLATE_IMAGE_LUMINANCE_CONTRAST_TOO_LOW";
+      break;
+    }
+    case -1074395391: {
+      errorText = "ERR_COLOR_TEMPLATE_IMAGE_HUE_CONTRAST_TOO_LOW";
+      break;
+    }
+    case -1074395392: {
+      errorText = "ERR_COLOR_TEMPLATE_IMAGE_TOO_LARGE";
+      break;
+    }
+    case -1074395393: {
+      errorText = "ERR_COLOR_TEMPLATE_IMAGE_TOO_SMALL";
+      break;
+    }
+    case -1074395394: {
+      errorText = "ERR_COLOR_SPECTRUM_MASK";
+      break;
+    }
+    case -1074395395: {
+      errorText = "ERR_COLOR_IMAGE_REQUIRED";
+      break;
+    }
+    case -1074395397: {
+      errorText = "ERR_COMPLEX_IMAGE_REQUIRED";
+      break;
+    }
+    case -1074395399: {
+      errorText = "ERR_MULTICORE_INVALID_ARGUMENT";
+      break;
+    }
+    case -1074395400: {
+      errorText = "ERR_MULTICORE_OPERATION";
+      break;
+    }
+    case -1074395401: {
+      errorText = "ERR_INVALID_MATCHFACTOR";
+      break;
+    }
+    case -1074395402: {
+      errorText = "ERR_INVALID_MAXPOINTS";
+      break;
+    }
+    case -1074395403: {
+      errorText = "ERR_EXTRAINFO_VERSION";
+      break;
+    }
+    case -1074395404: {
+      errorText = "ERR_INVALID_INTERPOLATIONMETHOD_FOR_UNWRAP";
+      break;
+    }
+    case -1074395405: {
+      errorText = "ERR_INVALID_TEXTORIENTATION";
+      break;
+    }
+    case -1074395406: {
+      errorText = "ERR_COORDSYS_NOT_FOUND";
+      break;
+    }
+    case -1074395407: {
+      errorText = "ERR_INVALID_CONTRAST";
+      break;
+    }
+    case -1074395408: {
+      errorText = "ERR_INVALID_DETECTION_MODE";
+      break;
+    }
+    case -1074395409: {
+      errorText = "ERR_INVALID_SUBPIXEL_DIVISIONS";
+      break;
+    }
+    case -1074395410: {
+      errorText = "ERR_INVALID_ICONS_PER_LINE";
+      break;
+    }
+    case -1074395549: {
+      errorText = "ERR_NIOCR_INVALID_NUMBER_OF_OBJECTS_TO_VERIFY";
+      break;
+    }
+    case -1074395550: {
+      errorText = "ERR_NIOCR_INVALID_CHARACTER_VALUE";
+      break;
+    }
+    case -1074395551: {
+      errorText = "ERR_NIOCR_RENAME_REFCHAR";
+      break;
+    }
+    case -1074395552: {
+      errorText = "ERR_NIOCR_NOT_A_VALID_CHARACTER_SET";
+      break;
+    }
+    case -1074395553: {
+      errorText = "ERR_NIOCR_INVALID_MIN_BOUNDING_RECT_HEIGHT";
+      break;
+    }
+    case -1074395554: {
+      errorText = "ERR_NIOCR_INVALID_READ_RESOLUTION";
+      break;
+    }
+    case -1074395555: {
+      errorText = "ERR_NIOCR_INVALID_SPACING_RANGE";
+      break;
+    }
+    case -1074395556: {
+      errorText = "ERR_NIOCR_INVALID_BOUNDING_RECT_HEIGHT_RANGE";
+      break;
+    }
+    case -1074395557: {
+      errorText = "ERR_NIOCR_INVALID_BOUNDING_RECT_WIDTH_RANGE";
+      break;
+    }
+    case -1074395558: {
+      errorText = "ERR_NIOCR_INVALID_CHARACTER_SIZE_RANGE";
+      break;
+    }
+    case -1074395559: {
+      errorText = "ERR_NIOCR_INVALID_READ_OPTION";
+      break;
+    }
+    case -1074395560: {
+      errorText = "ERR_NIOCR_INVALID_OBJECT_INDEX";
+      break;
+    }
+    case -1074395561: {
+      errorText = "ERR_NIOCR_INVALID_NUMBER_OF_CHARACTERS";
+      break;
+    }
+    case -1074395562: {
+      errorText = "ERR_NIOCR_BOOLEAN_VALUE_FOR_STRING_ATTRIBUTE";
+      break;
+    }
+    case -1074395563: {
+      errorText = "ERR_NIOCR_UNLICENSED";
+      break;
+    }
+    case -1074395564: {
+      errorText = "ERR_NIOCR_INVALID_PREDEFINED_CHARACTER";
+      break;
+    }
+    case -1074395565: {
+      errorText = "ERR_NIOCR_MUST_BE_SINGLE_CHARACTER";
+      break;
+    }
+    case -1074395566: {
+      errorText = "ERR_NIOCR_BOOLEAN_VALUE_FOR_INTEGER_ATTRIBUTE";
+      break;
+    }
+    case -1074395567: {
+      errorText = "ERR_NIOCR_STRING_VALUE_FOR_BOOLEAN_ATTRIBUTE";
+      break;
+    }
+    case -1074395568: {
+      errorText = "ERR_NIOCR_STRING_VALUE_FOR_INTEGER_ATTRIBUTE";
+      break;
+    }
+    case -1074395569: {
+      errorText = "ERR_NIOCR_INVALID_ATTRIBUTE";
+      break;
+    }
+    case -1074395570: {
+      errorText = "ERR_NIOCR_INTEGER_VALUE_FOR_BOOLEAN_ATTRIBUTE";
+      break;
+    }
+    case -1074395571: {
+      errorText = "ERR_NIOCR_GET_ONLY_ATTRIBUTE";
+      break;
+    }
+    case -1074395572: {
+      errorText = "ERR_NIOCR_INTEGER_VALUE_FOR_STRING_ATTRIBUTE";
+      break;
+    }
+    case -1074395573: {
+      errorText = "ERR_NIOCR_INVALID_CHARACTER_SET_FILE_VERSION";
+      break;
+    }
+    case -1074395574: {
+      errorText = "ERR_NIOCR_CHARACTER_SET_DESCRIPTION_TOO_LONG";
+      break;
+    }
+    case -1074395575: {
+      errorText = "ERR_NIOCR_INVALID_NUMBER_OF_EROSIONS";
+      break;
+    }
+    case -1074395576: {
+      errorText = "ERR_NIOCR_CHARACTER_VALUE_TOO_LONG";
+      break;
+    }
+    case -1074395577: {
+      errorText = "ERR_NIOCR_CHARACTER_VALUE_CANNOT_BE_EMPTYSTRING";
+      break;
+    }
+    case -1074395578: {
+      errorText = "ERR_NIOCR_INVALID_CHARACTER_SET_FILE";
+      break;
+    }
+    case -1074395579: {
+      errorText = "ERR_NIOCR_INVALID_ASPECT_RATIO";
+      break;
+    }
+    case -1074395580: {
+      errorText = "ERR_NIOCR_INVALID_MIN_BOUNDING_RECT_WIDTH";
+      break;
+    }
+    case -1074395581: {
+      errorText = "ERR_NIOCR_INVALID_MAX_VERT_ELEMENT_SPACING";
+      break;
+    }
+    case -1074395582: {
+      errorText = "ERR_NIOCR_INVALID_MAX_HORIZ_ELEMENT_SPACING";
+      break;
+    }
+    case -1074395583: {
+      errorText = "ERR_NIOCR_INVALID_MIN_CHAR_SPACING";
+      break;
+    }
+    case -1074395584: {
+      errorText = "ERR_NIOCR_INVALID_THRESHOLD_LIMITS";
+      break;
+    }
+    case -1074395585: {
+      errorText = "ERR_NIOCR_INVALID_UPPER_THRESHOLD_LIMIT";
+      break;
+    }
+    case -1074395586: {
+      errorText = "ERR_NIOCR_INVALID_LOWER_THRESHOLD_LIMIT";
+      break;
+    }
+    case -1074395587: {
+      errorText = "ERR_NIOCR_INVALID_THRESHOLD_RANGE";
+      break;
+    }
+    case -1074395588: {
+      errorText = "ERR_NIOCR_INVALID_HIGH_THRESHOLD_VALUE";
+      break;
+    }
+    case -1074395589: {
+      errorText = "ERR_NIOCR_INVALID_LOW_THRESHOLD_VALUE";
+      break;
+    }
+    case -1074395590: {
+      errorText = "ERR_NIOCR_INVALID_NUMBER_OF_VALID_CHARACTER_POSITIONS";
+      break;
+    }
+    case -1074395591: {
+      errorText = "ERR_NIOCR_INVALID_CHARACTER_INDEX";
+      break;
+    }
+    case -1074395592: {
+      errorText = "ERR_NIOCR_INVALID_READ_STRATEGY";
+      break;
+    }
+    case -1074395593: {
+      errorText = "ERR_NIOCR_INVALID_NUMBER_OF_BLOCKS";
+      break;
+    }
+    case -1074395594: {
+      errorText = "ERR_NIOCR_INVALID_SUBSTITUTION_CHARACTER";
+      break;
+    }
+    case -1074395595: {
+      errorText = "ERR_NIOCR_INVALID_THRESHOLD_MODE";
+      break;
+    }
+    case -1074395596: {
+      errorText = "ERR_NIOCR_INVALID_CHARACTER_SIZE";
+      break;
+    }
+    case -1074395597: {
+      errorText = "ERR_NIOCR_NOT_A_VALID_SESSION";
+      break;
+    }
+    case -1074395598: {
+      errorText = "ERR_NIOCR_INVALID_ACCEPTANCE_LEVEL";
+      break;
+    }
+    case -1074395600: {
+      errorText = "ERR_INFO_NOT_FOUND";
+      break;
+    }
+    case -1074395601: {
+      errorText = "ERR_INVALID_EDGE_THRESHOLD";
+      break;
+    }
+    case -1074395602: {
+      errorText = "ERR_INVALID_MINIMUM_CURVE_LENGTH";
+      break;
+    }
+    case -1074395603: {
+      errorText = "ERR_INVALID_ROW_STEP";
+      break;
+    }
+    case -1074395604: {
+      errorText = "ERR_INVALID_COLUMN_STEP";
+      break;
+    }
+    case -1074395605: {
+      errorText = "ERR_INVALID_MAXIMUM_END_POINT_GAP";
+      break;
+    }
+    case -1074395606: {
+      errorText = "ERR_INVALID_MINIMUM_FEATURES_TO_MATCH";
+      break;
+    }
+    case -1074395607: {
+      errorText = "ERR_INVALID_MAXIMUM_FEATURES_PER_MATCH";
+      break;
+    }
+    case -1074395608: {
+      errorText = "ERR_INVALID_SUBPIXEL_ITERATIONS";
+      break;
+    }
+    case -1074395609: {
+      errorText = "ERR_INVALID_SUBPIXEL_TOLERANCE";
+      break;
+    }
+    case -1074395610: {
+      errorText = "ERR_INVALID_INITIAL_MATCH_LIST_LENGTH";
+      break;
+    }
+    case -1074395611: {
+      errorText = "ERR_INVALID_MINIMUM_RECTANGLE_DIMENSION";
+      break;
+    }
+    case -1074395612: {
+      errorText = "ERR_INVALID_MINIMUM_FEATURE_RADIUS";
+      break;
+    }
+    case -1074395613: {
+      errorText = "ERR_INVALID_MINIMUM_FEATURE_LENGTH";
+      break;
+    }
+    case -1074395614: {
+      errorText = "ERR_INVALID_MINIMUM_FEATURE_ASPECT_RATIO";
+      break;
+    }
+    case -1074395615: {
+      errorText = "ERR_INVALID_MINIMUM_FEATURE_STRENGTH";
+      break;
+    }
+    case -1074395616: {
+      errorText = "ERR_INVALID_EDGE_FILTER_SIZE";
+      break;
+    }
+    case -1074395617: {
+      errorText = "ERR_INVALID_NUMBER_OF_FEATURES_RANGE";
+      break;
+    }
+    case -1074395618: {
+      errorText = "ERR_TOO_MANY_SCALE_RANGES";
+      break;
+    }
+    case -1074395619: {
+      errorText = "ERR_TOO_MANY_OCCLUSION_RANGES";
+      break;
+    }
+    case -1074395620: {
+      errorText = "ERR_INVALID_CURVE_EXTRACTION_MODE";
+      break;
+    }
+    case -1074395621: {
+      errorText = "ERR_INVALID_LEARN_GEOMETRIC_PATTERN_SETUP_DATA";
+      break;
+    }
+    case -1074395622: {
+      errorText = "ERR_INVALID_MATCH_GEOMETRIC_PATTERN_SETUP_DATA";
+      break;
+    }
+    case -1074395623: {
+      errorText = "ERR_INVALID_SCALE_RANGE";
+      break;
+    }
+    case -1074395624: {
+      errorText = "ERR_INVALID_OCCLUSION_RANGE";
+      break;
+    }
+    case -1074395625: {
+      errorText = "ERR_INVALID_MATCH_CONSTRAINT_TYPE";
+      break;
+    }
+    case -1074395626: {
+      errorText = "ERR_NOT_ENOUGH_TEMPLATE_FEATURES";
+      break;
+    }
+    case -1074395627: {
+      errorText = "ERR_NOT_ENOUGH_TEMPLATE_FEATURES_1";
+      break;
+    }
+    case -1074395628: {
+      errorText = "ERR_INVALID_GEOMETRIC_MATCHING_TEMPLATE";
+      break;
+    }
+    case -1074395629: {
+      errorText = "ERR_INVALID_MAXIMUM_PIXEL_DISTANCE_FROM_LINE";
+      break;
+    }
+    case -1074395630: {
+      errorText = "ERR_INVALID_MAXIMUM_FEATURES_LEARNED";
+      break;
+    }
+    case -1074395631: {
+      errorText = "ERR_INVALID_MIN_MATCH_SEPARATION_DISTANCE";
+      break;
+    }
+    case -1074395632: {
+      errorText = "ERR_INVALID_MIN_MATCH_SEPARATION_ANGLE";
+      break;
+    }
+    case -1074395633: {
+      errorText = "ERR_INVALID_MIN_MATCH_SEPARATION_SCALE";
+      break;
+    }
+    case -1074395634: {
+      errorText = "ERR_INVALID_MAX_MATCH_OVERLAP";
+      break;
+    }
+    case -1074395635: {
+      errorText = "ERR_INVALID_SHAPE_DESCRIPTOR";
+      break;
+    }
+    case -1074395636: {
+      errorText = "ERR_DIRECTX_NOT_FOUND";
+      break;
+    }
+    case -1074395637: {
+      errorText = "ERR_HARDWARE_DOESNT_SUPPORT_NONTEARING";
+      break;
+    }
+    case -1074395638: {
+      errorText = "ERR_INVALID_FILL_STYLE";
+      break;
+    }
+    case -1074395639: {
+      errorText = "ERR_INVALID_HATCH_STYLE";
+      break;
+    }
+    case -1074395640: {
+      errorText = "ERR_TOO_MANY_ZONES";
+      break;
+    }
+    case -1074395641: {
+      errorText = "ERR_DUPLICATE_LABEL";
+      break;
+    }
+    case -1074395642: {
+      errorText = "ERR_LABEL_NOT_FOUND";
+      break;
+    }
+    case -1074395643: {
+      errorText = "ERR_INVALID_NUMBER_OF_MATCH_OPTIONS";
+      break;
+    }
+    case -1074395644: {
+      errorText = "ERR_LABEL_TOO_LONG";
+      break;
+    }
+    case -1074395645: {
+      errorText = "ERR_INVALID_NUMBER_OF_LABELS";
+      break;
+    }
+    case -1074395646: {
+      errorText = "ERR_NO_TEMPLATE_TO_LEARN";
+      break;
+    }
+    case -1074395647: {
+      errorText = "ERR_INVALID_MULTIPLE_GEOMETRIC_TEMPLATE";
+      break;
+    }
+    case -1074395648: {
+      errorText = "ERR_TEMPLATE_NOT_LEARNED";
+      break;
+    }
+    case -1074395649: {
+      errorText = "ERR_INVALID_GEOMETRIC_FEATURE_TYPE";
+      break;
+    }
+    case -1074395650: {
+      errorText = "ERR_CURVE_EXTRACTION_MODE_MUST_BE_SAME";
+      break;
+    }
+    case -1074395651: {
+      errorText = "ERR_EDGE_FILTER_SIZE_MUST_BE_SAME";
+      break;
+    }
+    case -1074395652: {
+      errorText = "ERR_OPENING_NEWER_GEOMETRIC_MATCHING_TEMPLATE";
+      break;
+    }
+    case -1074395653: {
+      errorText = "ERR_OPENING_NEWER_MULTIPLE_GEOMETRIC_TEMPLATE";
+      break;
+    }
+    case -1074395654: {
+      errorText = "ERR_GRADING_INFORMATION_NOT_FOUND";
+      break;
+    }
+    case -1074395655: {
+      errorText = "ERR_ENABLE_CALIBRATION_SUPPORT_MUST_BE_SAME";
+      break;
+    }
+    case -1074395656: {
+      errorText = "ERR_SMOOTH_CONTOURS_MUST_BE_SAME";
+      break;
+    }
+    case -1074395700: {
+      errorText = "ERR_REQUIRES_WIN2000_OR_NEWER";
+      break;
+    }
+    case -1074395701: {
+      errorText = "ERR_INVALID_MATRIX_SIZE_RANGE";
+      break;
+    }
+    case -1074395702: {
+      errorText = "ERR_INVALID_LENGTH";
+      break;
+    }
+    case -1074395703: {
+      errorText = "ERR_INVALID_TYPE_OF_FLATTEN";
+      break;
+    }
+    case -1074395704: {
+      errorText = "ERR_INVALID_COMPRESSION_TYPE";
+      break;
+    }
+    case -1074395705: {
+      errorText = "ERR_DATA_CORRUPTED";
+      break;
+    }
+    case -1074395706: {
+      errorText = "ERR_AVI_SESSION_ALREADY_OPEN";
+      break;
+    }
+    case -1074395707: {
+      errorText = "ERR_AVI_WRITE_SESSION_REQUIRED";
+      break;
+    }
+    case -1074395708: {
+      errorText = "ERR_AVI_READ_SESSION_REQUIRED";
+      break;
+    }
+    case -1074395709: {
+      errorText = "ERR_AVI_UNOPENED_SESSION";
+      break;
+    }
+    case -1074395710: {
+      errorText = "ERR_TOO_MANY_PARTICLES";
+      break;
+    }
+    case -1074395711: {
+      errorText = "ERR_NOT_ENOUGH_REGIONS";
+      break;
+    }
+    case -1074395712: {
+      errorText = "ERR_WRONG_REGION_TYPE";
+      break;
+    }
+    case -1074395713: {
+      errorText = "ERR_VALUE_NOT_IN_ENUM";
+      break;
+    }
+    case -1074395714: {
+      errorText = "ERR_INVALID_AXIS_ORIENTATION";
+      break;
+    }
+    case -1074395715: {
+      errorText = "ERR_INVALID_CALIBRATION_UNIT";
+      break;
+    }
+    case -1074395716: {
+      errorText = "ERR_INVALID_SCALING_METHOD";
+      break;
+    }
+    case -1074395717: {
+      errorText = "ERR_INVALID_RANGE";
+      break;
+    }
+    case -1074395718: {
+      errorText = "ERR_LAB_VERSION";
+      break;
+    }
+    case -1074395719: {
+      errorText = "ERR_BAD_ROI_BOX";
+      break;
+    }
+    case -1074395720: {
+      errorText = "ERR_BAD_ROI";
+      break;
+    }
+    case -1074395721: {
+      errorText = "ERR_INVALID_BIT_DEPTH";
+      break;
+    }
+    case -1074395722: {
+      errorText = "ERR_CLASSIFIER_CLASSIFY_IMAGE_WITH_CUSTOM_SESSION";
+      break;
+    }
+    case -1074395723: {
+      errorText = "ERR_CUSTOMDATA_KEY_NOT_FOUND";
+      break;
+    }
+    case -1074395724: {
+      errorText = "ERR_CUSTOMDATA_INVALID_SIZE";
+      break;
+    }
+    case -1074395725: {
+      errorText = "ERR_DATA_VERSION";
+      break;
+    }
+    case -1074395726: {
+      errorText = "ERR_MATCHFACTOR_OBSOLETE";
+      break;
+    }
+    case -1074395727: {
+      errorText = "ERR_UNSUPPORTED_2D_BARCODE_SEARCH_MODE";
+      break;
+    }
+    case -1074395728: {
+      errorText = "ERR_INVALID_2D_BARCODE_SEARCH_MODE";
+      break;
+    }
+    case -1074395754: {
+      errorText = "ERR_TRIG_TIMEOUT";
+      break;
+    }
+    case -1074395756: {
+      errorText = "ERR_DLL_FUNCTION_NOT_FOUND";
+      break;
+    }
+    case -1074395757: {
+      errorText = "ERR_DLL_NOT_FOUND";
+      break;
+    }
+    case -1074395758: {
+      errorText = "ERR_BOARD_NOT_OPEN";
+      break;
+    }
+    case -1074395760: {
+      errorText = "ERR_BOARD_NOT_FOUND";
+      break;
+    }
+    case -1074395762: {
+      errorText = "ERR_INVALID_NIBLACK_DEVIATION_FACTOR";
+      break;
+    }
+    case -1074395763: {
+      errorText = "ERR_INVALID_NORMALIZATION_METHOD";
+      break;
+    }
+    case -1074395766: {
+      errorText = "ERR_DEPRECATED_FUNCTION";
+      break;
+    }
+    case -1074395767: {
+      errorText = "ERR_INVALID_ALIGNMENT";
+      break;
+    }
+    case -1074395768: {
+      errorText = "ERR_INVALID_SCALE";
+      break;
+    }
+    case -1074395769: {
+      errorText = "ERR_INVALID_EDGE_THICKNESS";
+      break;
+    }
+    case -1074395770: {
+      errorText = "ERR_INVALID_INSPECTION_TEMPLATE";
+      break;
+    }
+    case -1074395771: {
+      errorText = "ERR_OPENING_NEWER_INSPECTION_TEMPLATE";
+      break;
+    }
+    case -1074395772: {
+      errorText = "ERR_INVALID_REGISTRATION_METHOD";
+      break;
+    }
+    case -1074395773: {
+      errorText = "ERR_NO_DEST_IMAGE";
+      break;
+    }
+    case -1074395774: {
+      errorText = "ERR_NO_LABEL";
+      break;
+    }
+    case -1074395775: {
+      errorText = "ERR_ROI_HAS_OPEN_CONTOURS";
+      break;
+    }
+    case -1074395776: {
+      errorText = "ERR_INVALID_USE_OF_COMPACT_SESSION_FILE";
+      break;
+    }
+    case -1074395777: {
+      errorText = "ERR_INCOMPATIBLE_CLASSIFIER_TYPES";
+      break;
+    }
+    case -1074395778: {
+      errorText = "ERR_INVALID_KERNEL_SIZE";
+      break;
+    }
+    case -1074395779: {
+      errorText = "ERR_CANNOT_COMPACT_UNTRAINED";
+      break;
+    }
+    case -1074395780: {
+      errorText = "ERR_INVALID_PARTICLE_TYPE";
+      break;
+    }
+    case -1074395781: {
+      errorText = "ERR_CLASSIFIER_INVALID_ENGINE_TYPE";
+      break;
+    }
+    case -1074395782: {
+      errorText = "ERR_DESCRIPTION_TOO_LONG";
+      break;
+    }
+    case -1074395783: {
+      errorText = "ERR_BAD_SAMPLE_INDEX";
+      break;
+    }
+    case -1074395784: {
+      errorText = "ERR_INVALID_LIMITS";
+      break;
+    }
+    case -1074395785: {
+      errorText = "ERR_NO_PARTICLE";
+      break;
+    }
+    case -1074395786: {
+      errorText = "ERR_INVALID_PARTICLE_OPTIONS";
+      break;
+    }
+    case -1074395787: {
+      errorText = "ERR_INVALID_CLASSIFIER_TYPE";
+      break;
+    }
+    case -1074395788: {
+      errorText = "ERR_NO_SAMPLES";
+      break;
+    }
+    case -1074395789: {
+      errorText = "ERR_OPENING_NEWER_CLASSIFIER_SESSION";
+      break;
+    }
+    case -1074395790: {
+      errorText = "ERR_INVALID_DISTANCE_METRIC";
+      break;
+    }
+    case -1074395791: {
+      errorText = "ERR_CLASSIFIER_INVALID_SESSION_TYPE";
+      break;
+    }
+    case -1074395792: {
+      errorText = "ERR_CLASSIFIER_SESSION_NOT_TRAINED";
+      break;
+    }
+    case -1074395793: {
+      errorText = "ERR_INVALID_OPERATION_ON_COMPACT_SESSION_ATTEMPTED";
+      break;
+    }
+    case -1074395794: {
+      errorText = "ERR_K_TOO_HIGH";
+      break;
+    }
+    case -1074395795: {
+      errorText = "ERR_K_TOO_LOW";
+      break;
+    }
+    case -1074395796: {
+      errorText = "ERR_INVALID_KNN_METHOD";
+      break;
+    }
+    case -1074395797: {
+      errorText = "ERR_INVALID_CLASSIFIER_SESSION";
+      break;
+    }
+    case -1074395798: {
+      errorText = "ERR_INVALID_CUSTOM_SAMPLE";
+      break;
+    }
+    case -1074395799: {
+      errorText = "ERR_INTERNAL";
+      break;
+    }
+    case -1074395800: {
+      errorText = "ERR_PROTECTION";
+      break;
+    }
+    case -1074395801: {
+      errorText = "ERR_TOO_MANY_CONTOURS";
+      break;
+    }
+    case -1074395837: {
+      errorText = "ERR_INVALID_COMPRESSION_RATIO";
+      break;
+    }
+    case -1074395840: {
+      errorText = "ERR_BAD_INDEX";
+      break;
+    }
+    case -1074395875: {
+      errorText = "ERR_BARCODE_PHARMACODE";
+      break;
+    }
+    case -1074395876: {
+      errorText = "ERR_UNSUPPORTED_COLOR_MODE";
+      break;
+    }
+    case -1074395877: {
+      errorText = "ERR_COLORMODE_REQUIRES_CHANGECOLORSPACE2";
+      break;
+    }
+    case -1074395878: {
+      errorText = "ERR_PROP_NODE_WRITE_NOT_SUPPORTED";
+      break;
+    }
+    case -1074395879: {
+      errorText = "ERR_BAD_MEASURE";
+      break;
+    }
+    case -1074395880: {
+      errorText = "ERR_PARTICLE";
+      break;
+    }
+    case -1074395920: {
+      errorText = "ERR_NUMBER_CLASS";
+      break;
+    }
+    case -1074395953: {
+      errorText = "ERR_INVALID_WAVELET_TRANSFORM_MODE";
+      break;
+    }
+    case -1074395954: {
+      errorText = "ERR_INVALID_QUANTIZATION_STEP_SIZE";
+      break;
+    }
+    case -1074395955: {
+      errorText = "ERR_INVALID_MAX_WAVELET_TRANSFORM_LEVEL";
+      break;
+    }
+    case -1074395956: {
+      errorText = "ERR_INVALID_QUALITY";
+      break;
+    }
+    case -1074395957: {
+      errorText = "ERR_ARRAY_SIZE_MISMATCH";
+      break;
+    }
+    case -1074395958: {
+      errorText = "ERR_WINDOW_ID";
+      break;
+    }
+    case -1074395959: {
+      errorText = "ERR_CREATE_WINDOW";
+      break;
+    }
+    case -1074395960: {
+      errorText = "ERR_INIT";
+      break;
+    }
+    case -1074395971: {
+      errorText = "ERR_INVALID_OFFSET";
+      break;
+    }
+    case -1074395972: {
+      errorText = "ERR_DIRECTX_ENUMERATE_FILTERS";
+      break;
+    }
+    case -1074395973: {
+      errorText = "ERR_JPEG2000_UNSUPPORTED_MULTIPLE_LAYERS";
+      break;
+    }
+    case -1074395974: {
+      errorText = "ERR_UNSUPPORTED_JPEG2000_COLORSPACE_METHOD";
+      break;
+    }
+    case -1074395975: {
+      errorText = "ERR_AVI_TIMEOUT";
+      break;
+    }
+    case -1074395976: {
+      errorText = "ERR_NUMBER_OF_PALETTE_COLORS";
+      break;
+    }
+    case -1074395977: {
+      errorText = "ERR_AVI_VERSION";
+      break;
+    }
+    case -1074395978: {
+      errorText = "ERR_INVALID_PARTICLE_NUMBER";
+      break;
+    }
+    case -1074395979: {
+      errorText = "ERR_INVALID_PARTICLE_INFO";
+      break;
+    }
+    case -1074395980: {
+      errorText = "ERR_COM_INITIALIZE";
+      break;
+    }
+    case -1074395981: {
+      errorText = "ERR_INSUFFICIENT_BUFFER_SIZE";
+      break;
+    }
+    case -1074395982: {
+      errorText = "ERR_INVALID_FRAMES_PER_SECOND";
+      break;
+    }
+    case -1074395983: {
+      errorText = "ERR_FILE_NO_SPACE";
+      break;
+    }
+    case -1074395984: {
+      errorText = "ERR_FILE_INVALID_DATA_TYPE";
+      break;
+    }
+    case -1074395985: {
+      errorText = "ERR_FILE_OPERATION";
+      break;
+    }
+    case -1074395986: {
+      errorText = "ERR_FILE_FORMAT";
+      break;
+    }
+    case -1074395987: {
+      errorText = "ERR_FILE_EOF";
+      break;
+    }
+    case -1074395988: {
+      errorText = "ERR_FILE_WRITE";
+      break;
+    }
+    case -1074395989: {
+      errorText = "ERR_FILE_READ";
+      break;
+    }
+    case -1074395990: {
+      errorText = "ERR_FILE_GET_INFO";
+      break;
+    }
+    case -1074395991: {
+      errorText = "ERR_FILE_INVALID_TYPE";
+      break;
+    }
+    case -1074395992: {
+      errorText = "ERR_FILE_PERMISSION";
+      break;
+    }
+    case -1074395993: {
+      errorText = "ERR_FILE_IO_ERR";
+      break;
+    }
+    case -1074395994: {
+      errorText = "ERR_FILE_TOO_MANY_OPEN";
+      break;
+    }
+    case -1074395995: {
+      errorText = "ERR_FILE_NOT_FOUND";
+      break;
+    }
+    case -1074395996: {
+      errorText = "ERR_FILE_OPEN";
+      break;
+    }
+    case -1074395997: {
+      errorText = "ERR_FILE_ARGERR";
+      break;
+    }
+    case -1074395998: {
+      errorText = "ERR_FILE_COLOR_TABLE";
+      break;
+    }
+    case -1074395999: {
+      errorText = "ERR_FILE_FILE_TYPE";
+      break;
+    }
+    case -1074396000: {
+      errorText = "ERR_FILE_FILE_HEADER";
+      break;
+    }
+    case -1074396001: {
+      errorText = "ERR_TOO_MANY_AVI_SESSIONS";
+      break;
+    }
+    case -1074396002: {
+      errorText = "ERR_INVALID_LINEGAUGEMETHOD";
+      break;
+    }
+    case -1074396003: {
+      errorText = "ERR_AVI_DATA_EXCEEDS_BUFFER_SIZE";
+      break;
+    }
+    case -1074396004: {
+      errorText = "ERR_DIRECTX_CERTIFICATION_FAILURE";
+      break;
+    }
+    case -1074396005: {
+      errorText = "ERR_INVALID_AVI_SESSION";
+      break;
+    }
+    case -1074396006: {
+      errorText = "ERR_DIRECTX_UNKNOWN_COMPRESSION_FILTER";
+      break;
+    }
+    case -1074396007: {
+      errorText = "ERR_DIRECTX_INCOMPATIBLE_COMPRESSION_FILTER";
+      break;
+    }
+    case -1074396008: {
+      errorText = "ERR_DIRECTX_NO_FILTER";
+      break;
+    }
+    case -1074396009: {
+      errorText = "ERR_DIRECTX";
+      break;
+    }
+    case -1074396010: {
+      errorText = "ERR_INVALID_FRAME_NUMBER";
+      break;
+    }
+    case -1074396011: {
+      errorText = "ERR_RPC_BIND";
+      break;
+    }
+    case -1074396012: {
+      errorText = "ERR_RPC_EXECUTE";
+      break;
+    }
+    case -1074396013: {
+      errorText = "ERR_INVALID_VIDEO_MODE";
+      break;
+    }
+    case -1074396014: {
+      errorText = "ERR_INVALID_VIDEO_BLIT";
+      break;
+    }
+    case -1074396015: {
+      errorText = "ERR_RPC_EXECUTE_IVB";
+      break;
+    }
+    case -1074396016: {
+      errorText = "ERR_NO_VIDEO_DRIVER";
+      break;
+    }
+    case -1074396017: {
+      errorText = "ERR_OPENING_NEWER_AIM_GRADING_DATA";
+      break;
+    }
+    case -1074396018: {
+      errorText = "ERR_INVALID_EDGE_POLARITY_SEARCH_MODE";
+      break;
+    }
+    case -1074396019: {
+      errorText = "ERR_INVALID_THRESHOLD_PERCENTAGE";
+      break;
+    }
+    case -1074396020: {
+      errorText = "ERR_INVALID_GRADING_MODE";
+      break;
+    }
+    case -1074396021: {
+      errorText = "ERR_INVALID_KERNEL_SIZE_FOR_EDGE_DETECTION";
+      break;
+    }
+    case -1074396022: {
+      errorText = "ERR_INVALID_SEARCH_MODE_FOR_STRAIGHT_EDGE";
+      break;
+    }
+    case -1074396023: {
+      errorText = "ERR_INVALID_ANGLE_TOL_FOR_STRAIGHT_EDGE";
+      break;
+    }
+    case -1074396024: {
+      errorText = "ERR_INVALID_MIN_COVERAGE_FOR_STRAIGHT_EDGE";
+      break;
+    }
+    case -1074396025: {
+      errorText = "ERR_INVALID_ANGLE_RANGE_FOR_STRAIGHT_EDGE";
+      break;
+    }
+    case -1074396026: {
+      errorText = "ERR_INVALID_PROCESS_TYPE_FOR_EDGE_DETECTION";
+      break;
+    }
+    case -1074396032: {
+      errorText = "ERR_TEMPLATEDESCRIPTOR_ROTATION_SEARCHSTRATEGY";
+      break;
+    }
+    case -1074396033: {
+      errorText = "ERR_TEMPLATEDESCRIPTOR_LEARNSETUPDATA";
+      break;
+    }
+    case -1074396034: {
+      errorText = "ERR_TEMPLATEIMAGE_EDGEINFO";
+      break;
+    }
+    case -1074396035: {
+      errorText = "ERR_TEMPLATEIMAGE_NOCIRCLE";
+      break;
+    }
+    case -1074396036: {
+      errorText = "ERR_INVALID_SKELETONMODE";
+      break;
+    }
+    case -1074396037: {
+      errorText = "ERR_TIMEOUT";
+      break;
+    }
+    case -1074396038: {
+      errorText = "ERR_FIND_COORDSYS_MORE_THAN_ONE_EDGE";
+      break;
+    }
+    case -1074396039: {
+      errorText = "ERR_IO_ERROR";
+      break;
+    }
+    case -1074396040: {
+      errorText = "ERR_DRIVER";
+      break;
+    }
+    case -1074396041: {
+      errorText = "ERR_INVALID_2D_BARCODE_TYPE";
+      break;
+    }
+    case -1074396042: {
+      errorText = "ERR_INVALID_2D_BARCODE_CONTRAST";
+      break;
+    }
+    case -1074396043: {
+      errorText = "ERR_INVALID_2D_BARCODE_CELL_SHAPE";
+      break;
+    }
+    case -1074396044: {
+      errorText = "ERR_INVALID_2D_BARCODE_SHAPE";
+      break;
+    }
+    case -1074396045: {
+      errorText = "ERR_INVALID_2D_BARCODE_SUBTYPE";
+      break;
+    }
+    case -1074396046: {
+      errorText = "ERR_INVALID_2D_BARCODE_CONTRAST_FOR_ROI";
+      break;
+    }
+    case -1074396047: {
+      errorText = "ERR_INVALID_LINEAR_AVERAGE_MODE";
+      break;
+    }
+    case -1074396048: {
+      errorText = "ERR_INVALID_CELL_SAMPLE_SIZE";
+      break;
+    }
+    case -1074396049: {
+      errorText = "ERR_INVALID_MATRIX_POLARITY";
+      break;
+    }
+    case -1074396050: {
+      errorText = "ERR_INVALID_ECC_TYPE";
+      break;
+    }
+    case -1074396051: {
+      errorText = "ERR_INVALID_CELL_FILTER_MODE";
+      break;
+    }
+    case -1074396052: {
+      errorText = "ERR_INVALID_DEMODULATION_MODE";
+      break;
+    }
+    case -1074396053: {
+      errorText = "ERR_INVALID_BORDER_INTEGRITY";
+      break;
+    }
+    case -1074396054: {
+      errorText = "ERR_INVALID_CELL_FILL_TYPE";
+      break;
+    }
+    case -1074396055: {
+      errorText = "ERR_INVALID_ASPECT_RATIO";
+      break;
+    }
+    case -1074396056: {
+      errorText = "ERR_INVALID_MATRIX_MIRROR_MODE";
+      break;
+    }
+    case -1074396057: {
+      errorText = "ERR_INVALID_SEARCH_VECTOR_WIDTH";
+      break;
+    }
+    case -1074396058: {
+      errorText = "ERR_INVALID_ROTATION_MODE";
+      break;
+    }
+    case -1074396059: {
+      errorText = "ERR_INVALID_MAX_ITERATIONS";
+      break;
+    }
+    case -1074396060: {
+      errorText = "ERR_JPEG2000_LOSSLESS_WITH_FLOATING_POINT";
+      break;
+    }
+    case -1074396061: {
+      errorText = "ERR_INVALID_WINDOW_SIZE";
+      break;
+    }
+    case -1074396062: {
+      errorText = "ERR_INVALID_TOLERANCE";
+      break;
+    }
+    case -1074396063: {
+      errorText = "ERR_EXTERNAL_ALIGNMENT";
+      break;
+    }
+    case -1074396064: {
+      errorText = "ERR_EXTERNAL_NOT_SUPPORTED";
+      break;
+    }
+    case -1074396065: {
+      errorText = "ERR_CANT_RESIZE_EXTERNAL";
+      break;
+    }
+    case -1074396066: {
+      errorText = "ERR_INVALID_POINTSYMBOL";
+      break;
+    }
+    case -1074396067: {
+      errorText = "ERR_IMAGES_NOT_DIFF";
+      break;
+    }
+    case -1074396068: {
+      errorText = "ERR_INVALID_ACTION";
+      break;
+    }
+    case -1074396069: {
+      errorText = "ERR_INVALID_COLOR_MODE";
+      break;
+    }
+    case -1074396070: {
+      errorText = "ERR_INVALID_FUNCTION";
+      break;
+    }
+    case -1074396071: {
+      errorText = "ERR_INVALID_SCAN_DIRECTION";
+      break;
+    }
+    case -1074396072: {
+      errorText = "ERR_INVALID_BORDER";
+      break;
+    }
+    case -1074396073: {
+      errorText = "ERR_MASK_OUTSIDE_IMAGE";
+      break;
+    }
+    case -1074396074: {
+      errorText = "ERR_INCOMP_SIZE";
+      break;
+    }
+    case -1074396075: {
+      errorText = "ERR_COORD_SYS_SECOND_AXIS";
+      break;
+    }
+    case -1074396076: {
+      errorText = "ERR_COORD_SYS_FIRST_AXIS";
+      break;
+    }
+    case -1074396077: {
+      errorText = "ERR_INCOMP_TYPE";
+      break;
+    }
+    case -1074396079: {
+      errorText = "ERR_INVALID_METAFILE_HANDLE";
+      break;
+    }
+    case -1074396080: {
+      errorText = "ERR_INVALID_IMAGE_TYPE";
+      break;
+    }
+    case -1074396081: {
+      errorText = "ERR_BAD_PASSWORD";
+      break;
+    }
+    case -1074396082: {
+      errorText = "ERR_PALETTE_NOT_SUPPORTED";
+      break;
+    }
+    case -1074396083: {
+      errorText = "ERR_ROLLBACK_TIMEOUT";
+      break;
+    }
+    case -1074396084: {
+      errorText = "ERR_ROLLBACK_DELETE_TIMER";
+      break;
+    }
+    case -1074396085: {
+      errorText = "ERR_ROLLBACK_INIT_TIMER";
+      break;
+    }
+    case -1074396086: {
+      errorText = "ERR_ROLLBACK_START_TIMER";
+      break;
+    }
+    case -1074396087: {
+      errorText = "ERR_ROLLBACK_STOP_TIMER";
+      break;
+    }
+    case -1074396088: {
+      errorText = "ERR_ROLLBACK_RESIZE";
+      break;
+    }
+    case -1074396089: {
+      errorText = "ERR_ROLLBACK_RESOURCE_REINITIALIZE";
+      break;
+    }
+    case -1074396090: {
+      errorText = "ERR_ROLLBACK_RESOURCE_ENABLED";
+      break;
+    }
+    case -1074396091: {
+      errorText = "ERR_ROLLBACK_RESOURCE_UNINITIALIZED_ENABLE";
+      break;
+    }
+    case -1074396092: {
+      errorText = "ERR_ROLLBACK_RESOURCE_NON_EMPTY_INITIALIZE";
+      break;
+    }
+    case -1074396093: {
+      errorText = "ERR_ROLLBACK_RESOURCE_LOCKED";
+      break;
+    }
+    case -1074396094: {
+      errorText = "ERR_ROLLBACK_RESOURCE_CANNOT_UNLOCK";
+      break;
+    }
+    case -1074396095: {
+      errorText = "ERR_CALIBRATION_DUPLICATE_REFERENCE_POINT";
+      break;
+    }
+    case -1074396096: {
+      errorText = "ERR_NOT_AN_OBJECT";
+      break;
+    }
+    case -1074396097: {
+      errorText = "ERR_INVALID_PARTICLE_PARAMETER_VALUE";
+      break;
+    }
+    case -1074396098: {
+      errorText = "ERR_RESERVED_MUST_BE_nullptr";
+      break;
+    }
+    case -1074396099: {
+      errorText = "ERR_CALIBRATION_INFO_SIMPLE_TRANSFORM";
+      break;
+    }
+    case -1074396100: {
+      errorText = "ERR_CALIBRATION_INFO_PERSPECTIVE_PROJECTION";
+      break;
+    }
+    case -1074396101: {
+      errorText = "ERR_CALIBRATION_INFO_MICRO_PLANE";
+      break;
+    }
+    case -1074396102: {
+      errorText = "ERR_CALIBRATION_INFO_6";
+      break;
+    }
+    case -1074396103: {
+      errorText = "ERR_CALIBRATION_INFO_5";
+      break;
+    }
+    case -1074396104: {
+      errorText = "ERR_CALIBRATION_INFO_4";
+      break;
+    }
+    case -1074396105: {
+      errorText = "ERR_CALIBRATION_INFO_3";
+      break;
+    }
+    case -1074396106: {
+      errorText = "ERR_CALIBRATION_INFO_2";
+      break;
+    }
+    case -1074396107: {
+      errorText = "ERR_CALIBRATION_INFO_1";
+      break;
+    }
+    case -1074396108: {
+      errorText = "ERR_CALIBRATION_ERRORMAP";
+      break;
+    }
+    case -1074396109: {
+      errorText = "ERR_CALIBRATION_INVALID_SCALING_FACTOR";
+      break;
+    }
+    case -1074396110: {
+      errorText = "ERR_CALIBRATION_INFO_VERSION";
+      break;
+    }
+    case -1074396111: {
+      errorText = "ERR_CALIBRATION_FAILED_TO_FIND_GRID";
+      break;
+    }
+    case -1074396112: {
+      errorText = "ERR_INCOMP_MATRIX_SIZE";
+      break;
+    }
+    case -1074396113: {
+      errorText = "ERR_CALIBRATION_IMAGE_UNCALIBRATED";
+      break;
+    }
+    case -1074396114: {
+      errorText = "ERR_CALIBRATION_INVALID_ROI";
+      break;
+    }
+    case -1074396115: {
+      errorText = "ERR_CALIBRATION_IMAGE_CORRECTED";
+      break;
+    }
+    case -1074396116: {
+      errorText = "ERR_CALIBRATION_INSF_POINTS";
+      break;
+    }
+    case -1074396117: {
+      errorText = "ERR_MATRIX_SIZE";
+      break;
+    }
+    case -1074396118: {
+      errorText = "ERR_INVALID_STEP_SIZE";
+      break;
+    }
+    case -1074396119: {
+      errorText = "ERR_CUSTOMDATA_INVALID_KEY";
+      break;
+    }
+    case -1074396120: {
+      errorText = "ERR_NOT_IMAGE";
+      break;
+    }
+    case -1074396121: {
+      errorText = "ERR_SATURATION_THRESHOLD_OUT_OF_RANGE";
+      break;
+    }
+    case -1074396122: {
+      errorText = "ERR_DRAWTEXT_COLOR_MUST_BE_GRAYSCALE";
+      break;
+    }
+    case -1074396123: {
+      errorText = "ERR_INVALID_CALIBRATION_MODE";
+      break;
+    }
+    case -1074396124: {
+      errorText = "ERR_INVALID_CALIBRATION_ROI_MODE";
+      break;
+    }
+    case -1074396125: {
+      errorText = "ERR_INVALID_CONTRAST_THRESHOLD";
+      break;
+    }
+    case -1074396126: {
+      errorText = "ERR_ROLLBACK_RESOURCE_CONFLICT_1";
+      break;
+    }
+    case -1074396127: {
+      errorText = "ERR_ROLLBACK_RESOURCE_CONFLICT_2";
+      break;
+    }
+    case -1074396128: {
+      errorText = "ERR_ROLLBACK_RESOURCE_CONFLICT_3";
+      break;
+    }
+    case -1074396129: {
+      errorText = "ERR_ROLLBACK_UNBOUNDED_INTERFACE";
+      break;
+    }
+    case -1074396130: {
+      errorText = "ERR_NOT_RECT_OR_ROTATED_RECT";
+      break;
+    }
+    case -1074396132: {
+      errorText = "ERR_MASK_NOT_TEMPLATE_SIZE";
+      break;
+    }
+    case -1074396133: {
+      errorText = "ERR_THREAD_COULD_NOT_INITIALIZE";
+      break;
+    }
+    case -1074396134: {
+      errorText = "ERR_THREAD_INITIALIZING";
+      break;
+    }
+    case -1074396135: {
+      errorText = "ERR_INVALID_BUTTON_LABEL";
+      break;
+    }
+    case -1074396136: {
+      errorText = "ERR_DIRECTX_INVALID_FILTER_QUALITY";
+      break;
+    }
+    case -1074396137: {
+      errorText = "ERR_DIRECTX_DLL_NOT_FOUND";
+      break;
+    }
+    case -1074396138: {
+      errorText = "ERR_ROLLBACK_NOT_SUPPORTED";
+      break;
+    }
+    case -1074396139: {
+      errorText = "ERR_ROLLBACK_RESOURCE_OUT_OF_MEMORY";
+      break;
+    }
+    case -1074396140: {
+      errorText = "ERR_BARCODE_CODE128_SET";
+      break;
+    }
+    case -1074396141: {
+      errorText = "ERR_BARCODE_CODE128_FNC";
+      break;
+    }
+    case -1074396142: {
+      errorText = "ERR_BARCODE_INVALID";
+      break;
+    }
+    case -1074396143: {
+      errorText = "ERR_BARCODE_TYPE";
+      break;
+    }
+    case -1074396144: {
+      errorText = "ERR_BARCODE_CODE93_SHIFT";
+      break;
+    }
+    case -1074396145: {
+      errorText = "ERR_BARCODE_UPCA";
+      break;
+    }
+    case -1074396146: {
+      errorText = "ERR_BARCODE_MSI";
+      break;
+    }
+    case -1074396147: {
+      errorText = "ERR_BARCODE_I25";
+      break;
+    }
+    case -1074396148: {
+      errorText = "ERR_BARCODE_EAN13";
+      break;
+    }
+    case -1074396149: {
+      errorText = "ERR_BARCODE_EAN8";
+      break;
+    }
+    case -1074396150: {
+      errorText = "ERR_BARCODE_CODE128";
+      break;
+    }
+    case -1074396151: {
+      errorText = "ERR_BARCODE_CODE93";
+      break;
+    }
+    case -1074396152: {
+      errorText = "ERR_BARCODE_CODE39";
+      break;
+    }
+    case -1074396153: {
+      errorText = "ERR_BARCODE_CODABAR";
+      break;
+    }
+    case -1074396154: {
+      errorText = "ERR_IMAGE_TOO_SMALL";
+      break;
+    }
+    case -1074396155: {
+      errorText = "ERR_UNINIT";
+      break;
+    }
+    case -1074396156: {
+      errorText = "ERR_NEED_FULL_VERSION";
+      break;
+    }
+    case -1074396157: {
+      errorText = "ERR_UNREGISTERED";
+      break;
+    }
+    case -1074396158: {
+      errorText = "ERR_MEMORY_ERROR";
+      break;
+    }
+    case -1074396159: {
+      errorText = "ERR_OUT_OF_MEMORY";
+      break;
+    }
+    case -1074396160: {
+      errorText = "ERR_SYSTEM_ERROR";
+      break;
+    }
+    case 0: {
+      errorText = "ERR_SUCCESS";
+      break;
+    }
+    // end National Instruments defined errors
+
+    // begin BAE defined errors
+    case ERR_VISION_GENERAL_ERROR: {
+      errorText = "ERR_VISION_GENERAL_ERROR";
+      break;
+    }
+    case ERR_COLOR_NOT_FOUND: {
+      errorText = "ERR_COLOR_NOT_FOUND";
+      break;
+    }
+    case ERR_PARTICLE_TOO_SMALL: {
+      errorText = "ERR_PARTICLE_TOO_SMALL";
+      break;
+    }
+    case ERR_CAMERA_FAILURE: {
+      errorText = "ERR_CAMERA_FAILURE";
+      break;
+    }
+    case ERR_CAMERA_SOCKET_CREATE_FAILED: {
+      errorText = "ERR_CAMERA_SOCKET_CREATE_FAILED";
+      break;
+    }
+    case ERR_CAMERA_CONNECT_FAILED: {
+      errorText = "ERR_CAMERA_CONNECT_FAILED";
+      break;
+    }
+    case ERR_CAMERA_STALE_IMAGE: {
+      errorText = "ERR_CAMERA_STALE_IMAGE";
+      break;
+    }
+    case ERR_CAMERA_NOT_INITIALIZED: {
+      errorText = "ERR_CAMERA_NOT_INITIALIZED";
+      break;
+    }
+    case ERR_CAMERA_NO_BUFFER_AVAILABLE: {
+      errorText = "ERR_CAMERA_NO_BUFFER_AVAILABLE";
+      break;
+    }
+    case ERR_CAMERA_HEADER_ERROR: {
+      errorText = "ERR_CAMERA_HEADER_ERROR";
+      break;
+    }
+    case ERR_CAMERA_BLOCKING_TIMEOUT: {
+      errorText = "ERR_CAMERA_BLOCKING_TIMEOUT";
+      break;
+    }
+    case ERR_CAMERA_AUTHORIZATION_FAILED: {
+      errorText = "ERR_CAMERA_AUTHORIZATION_FAILED";
+      break;
+    }
+    case ERR_CAMERA_TASK_SPAWN_FAILED: {
+      errorText = "ERR_CAMERA_TASK_SPAWN_FAILED";
+      break;
+    }
+    case ERR_CAMERA_TASK_INPUT_OUT_OF_RANGE: {
+      errorText = "ERR_CAMERA_TASK_INPUT_OUT_OF_RANGE";
+      break;
+    }
+    case ERR_CAMERA_COMMAND_FAILURE: {
+      errorText = "ERR_CAMERA_COMMAND_FAILURE";
+      break;
+    }
+  }
+
+  return errorText;
+}
diff --git a/wpilibc/Athena/src/Vision/HSLImage.cpp b/wpilibc/Athena/src/Vision/HSLImage.cpp
new file mode 100644
index 0000000..5b114c4
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/HSLImage.cpp
@@ -0,0 +1,21 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/HSLImage.h"
+
+/**
+ * Create a new image that uses the Hue, Saturation, and Luminance planes.
+ */
+HSLImage::HSLImage() : ColorImage(IMAQ_IMAGE_HSL) {}
+
+/**
+ * Create a new image by loading a file.
+ * @param fileName The path of the file to load.
+ */
+HSLImage::HSLImage(const char *fileName) : ColorImage(IMAQ_IMAGE_HSL) {
+  int success = imaqReadFile(m_imaqImage, fileName, nullptr, nullptr);
+  wpi_setImaqErrorWithContext(success, "Imaq ReadFile error");
+}
diff --git a/wpilibc/Athena/src/Vision/ImageBase.cpp b/wpilibc/Athena/src/Vision/ImageBase.cpp
new file mode 100644
index 0000000..f35234a
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/ImageBase.cpp
@@ -0,0 +1,63 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/ImageBase.h"
+#include "nivision.h"
+
+/**
+ * Create a new instance of an ImageBase.
+ * Imagebase is the base of all the other image classes. The constructor
+ * creates any type of image and stores the pointer to it in the class.
+ * @param type The type of image to create
+ */
+ImageBase::ImageBase(ImageType type) {
+  m_imaqImage = imaqCreateImage(type, DEFAULT_BORDER_SIZE);
+}
+
+/**
+ * Frees memory associated with an ImageBase.
+ * Destructor frees the imaq image allocated with the class.
+ */
+ImageBase::~ImageBase() {
+  if (m_imaqImage) imaqDispose(m_imaqImage);
+}
+
+/**
+ * Writes an image to a file with the given filename.
+ * Write the image to a file in the flash on the cRIO.
+ * @param fileName The name of the file to write
+ */
+void ImageBase::Write(const char *fileName) {
+  int success = imaqWriteFile(m_imaqImage, fileName, nullptr);
+  wpi_setImaqErrorWithContext(success, "Imaq Image writeFile error");
+}
+
+/**
+ * Gets the height of an image.
+ * @return The height of the image in pixels.
+ */
+int ImageBase::GetHeight() {
+  int height;
+  imaqGetImageSize(m_imaqImage, nullptr, &height);
+  return height;
+}
+
+/**
+ * Gets the width of an image.
+ * @return The width of the image in pixels.
+ */
+int ImageBase::GetWidth() {
+  int width;
+  imaqGetImageSize(m_imaqImage, &width, nullptr);
+  return width;
+}
+
+/**
+ * Access the internal IMAQ Image data structure.
+ *
+ * @return A pointer to the internal IMAQ Image data structure.
+ */
+Image *ImageBase::GetImaqImage() { return m_imaqImage; }
diff --git a/wpilibc/Athena/src/Vision/MonoImage.cpp b/wpilibc/Athena/src/Vision/MonoImage.cpp
new file mode 100644
index 0000000..90703c0
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/MonoImage.cpp
@@ -0,0 +1,46 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/MonoImage.h"
+#include "nivision.h"
+
+using namespace std;
+
+MonoImage::MonoImage() : ImageBase(IMAQ_IMAGE_U8) {}
+
+/**
+ * Look for ellipses in an image.
+ * Given some input parameters, look for any number of ellipses in an image.
+ * @param ellipseDescriptor Ellipse descriptor
+ * @param curveOptions Curve options
+ * @param shapeDetectionOptions Shape detection options
+ * @param roi Region of Interest
+ * @returns a vector of EllipseMatch structures (0 length vector on no match)
+ */
+vector<EllipseMatch> *MonoImage::DetectEllipses(
+    EllipseDescriptor *ellipseDescriptor, CurveOptions *curveOptions,
+    ShapeDetectionOptions *shapeDetectionOptions, ROI *roi) {
+  int numberOfMatches;
+  EllipseMatch *e =
+      imaqDetectEllipses(m_imaqImage, ellipseDescriptor, curveOptions,
+                         shapeDetectionOptions, roi, &numberOfMatches);
+  auto ellipses = new vector<EllipseMatch>;
+  if (e == nullptr) {
+    return ellipses;
+  }
+  for (int i = 0; i < numberOfMatches; i++) {
+    ellipses->push_back(e[i]);
+  }
+  imaqDispose(e);
+  return ellipses;
+}
+
+vector<EllipseMatch> *MonoImage::DetectEllipses(
+    EllipseDescriptor *ellipseDescriptor) {
+  vector<EllipseMatch> *ellipses =
+      DetectEllipses(ellipseDescriptor, nullptr, nullptr, nullptr);
+  return ellipses;
+}
diff --git a/wpilibc/Athena/src/Vision/RGBImage.cpp b/wpilibc/Athena/src/Vision/RGBImage.cpp
new file mode 100644
index 0000000..5469122
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/RGBImage.cpp
@@ -0,0 +1,21 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/RGBImage.h"
+
+/**
+ * Create a new image that uses Red, Green, and Blue planes.
+ */
+RGBImage::RGBImage() : ColorImage(IMAQ_IMAGE_RGB) {}
+
+/**
+ * Create a new image by loading a file.
+ * @param fileName The path of the file to load.
+ */
+RGBImage::RGBImage(const char *fileName) : ColorImage(IMAQ_IMAGE_RGB) {
+  int success = imaqReadFile(m_imaqImage, fileName, nullptr, nullptr);
+  wpi_setImaqErrorWithContext(success, "Imaq ReadFile error");
+}
diff --git a/wpilibc/Athena/src/Vision/Threshold.cpp b/wpilibc/Athena/src/Vision/Threshold.cpp
new file mode 100644
index 0000000..2e17243
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/Threshold.cpp
@@ -0,0 +1,18 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include "Vision/Threshold.h"
+
+Threshold::Threshold(int new_plane1Low, int new_plane1High, int new_plane2Low,
+                     int new_plane2High, int new_plane3Low,
+                     int new_plane3High) {
+  plane1Low = new_plane1Low;
+  plane1High = new_plane1High;
+  plane2Low = new_plane2Low;
+  plane2High = new_plane2High;
+  plane3Low = new_plane3Low;
+  plane3High = new_plane3High;
+}
diff --git a/wpilibc/Athena/src/Vision/VisionAPI.cpp b/wpilibc/Athena/src/Vision/VisionAPI.cpp
new file mode 100644
index 0000000..163721d
--- /dev/null
+++ b/wpilibc/Athena/src/Vision/VisionAPI.cpp
@@ -0,0 +1,821 @@
+/*----------------------------------------------------------------------------*/
+/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
+/* Open Source Software - may be modified and shared by FRC teams. The code   */
+/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib.  */
+/*----------------------------------------------------------------------------*/
+
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "Vision/BaeUtilities.h"
+#include "Vision/FrcError.h"
+#include "Vision/VisionAPI.h"
+
+int VisionAPI_debugFlag = 1;
+#define DPRINTF \
+  if (VisionAPI_debugFlag) dprintf
+
+/** @file
+ *    Image Management functions
+ */
+
+/**
+* @brief Create an image object
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_COMPLEX,
+* IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64
+* The border size is defaulted to 3 so that convolutional algorithms work at the
+* edges.
+* When you are finished with the created image, dispose of it by calling
+* frcDispose().
+* To get extended error information, call GetLastError().
+*
+* @param type Type of image to create
+* @return Image* On success, this function returns the created image. On
+* failure, it returns nullptr.
+*/
+Image* frcCreateImage(ImageType type) {
+  return imaqCreateImage(type, DEFAULT_BORDER_SIZE);
+}
+
+/**
+* @brief Dispose of one object. Supports any object created on the heap.
+*
+* @param object object to dispose of
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcDispose(void* object) { return imaqDispose(object); }
+/**
+* @brief Dispose of a list of objects. Supports any object created on the heap.
+*
+* @param functionName The name of the function
+* @param ... A list of pointers to structures that need to be disposed of.
+* The last pointer in the list should always be set to nullptr.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcDispose(const char* functionName, ...) /* Variable argument list */
+{
+  va_list disposalPtrList; /* Input argument list */
+  void* disposalPtr;       /* For iteration */
+  int success, returnValue = 1;
+
+  va_start(disposalPtrList, functionName); /* start of variable list */
+  disposalPtr = va_arg(disposalPtrList, void*);
+  while (disposalPtr != nullptr) {
+    success = imaqDispose(disposalPtr);
+    if (!success) {
+      returnValue = 0;
+    }
+    disposalPtr = va_arg(disposalPtrList, void*);
+  }
+  va_end(disposalPtrList);
+  return returnValue;
+}
+
+/**
+* @brief Copy an image object.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_RGB,
+* IMAQ_IMAGE_HSL.
+*
+* @param dest Copy of image. On failure, dest is nullptr. Must have already been
+* created using frcCreateImage().
+* When you are finished with the created image, dispose of it by calling
+* frcDispose().
+* @param source Image to copy
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcCopyImage(Image* dest, const Image* source) {
+  return imaqDuplicate(dest, source);
+}
+
+/**
+* @brief Crop image without changing the scale.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_RGB,
+* IMAQ_IMAGE_HSL.
+*
+* @param dest Modified image
+* @param source Image to crop
+* @param rect region to process, or IMAQ_NO_RECT
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcCrop(Image* dest, const Image* source, Rect rect) {
+  return imaqScale(dest, source, 1, 1, IMAQ_SCALE_LARGER, rect);
+}
+
+/**
+* @brief Scales the entire image larger or smaller.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_RGB,
+* IMAQ_IMAGE_HSL.
+*
+* @param dest Modified image
+* @param source Image to scale
+* @param xScale the horizontal reduction ratio
+* @param yScale the vertical reduction ratio
+* @param scaleMode IMAQ_SCALE_LARGER or IMAQ_SCALE_SMALLER
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcScale(Image* dest, const Image* source, int xScale, int yScale,
+             ScalingMode scaleMode) {
+  Rect rect = IMAQ_NO_RECT;
+  return imaqScale(dest, source, xScale, yScale, scaleMode, rect);
+}
+
+/**
+ * @brief Creates image object from the information in a file. The file can be
+ * in one of the following formats:
+ * PNG, JPEG, JPEG2000, TIFF, AIPD, or BMP.
+ * Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_COMPLEX,
+ * IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64.
+ *
+ * @param image Image read in
+ * @param fileName File to read. Cannot be nullptr
+ *
+ * @return On success: 1. On failure: 0. To get extended error information, call
+ * GetLastError().
+ */
+int frcReadImage(Image* image, const char* fileName) {
+  return imaqReadFile(image, fileName, nullptr, nullptr);
+}
+
+/**
+* @brief Write image to a file.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL, IMAQ_IMAGE_COMPLEX,
+* IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64.
+*
+* The file type is determined by the extension, as follows:
+*
+* 		Extension 					File Type
+* 		aipd or .apd 				AIPD
+* 		.bmp 						BMP
+* 		.jpg or .jpeg 				JPEG
+* 		.jp2 						JPEG2000
+* 		.png 						PNG
+* 		.tif or .tiff 				TIFF
+*
+*
+* The following are the supported image types for each file type:
+*
+* 		File Types 					Image Types
+* 		AIPD 						all image types
+* 		BMP, JPEG 					8-bit, RGB
+* 		PNG, TIFF, JPEG2000 		8-bit, 16-bit, RGB, RGBU64
+*
+* @param image Image to write
+* @param fileName File to read. Cannot be nullptr. The extension determines the
+* file format that is written.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcWriteImage(const Image* image, const char* fileName) {
+  RGBValue* colorTable = nullptr;
+  return imaqWriteFile(image, fileName, colorTable);
+}
+
+/*  Measure Intensity functions */
+
+/**
+* @brief Measures the pixel intensities in a rectangle of an image.
+* Outputs intensity based statistics about an image such as Max, Min, Mean and
+* Std Dev of pixel value.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+*
+*  Parameter Discussion	:
+* 		Relevant parameters of the HistogramReport include:
+* 			min, max, mean and stdDev
+*		min/max —Setting both min and max to 0 causes the function to set
+* min to 0
+* 			and the max to 255 for 8-bit images and to the actual
+* minimum value and
+* 			maximum value of the image for all other image types.
+*		max—Setting both min and max to 0 causes the function to set max
+* to 255
+* 			for 8-bit images and to the actual maximum value of the
+* image for
+* 			all other image types.
+*
+* @param image Image whose histogram the function calculates.
+* @param numClasses The number of classes into which the function separates the
+* pixels.
+* Determines the number of elements in the histogram array returned
+* @param min The minimum pixel value to consider for the histogram.
+* The function does not count pixels with values less than min.
+* @param max The maximum pixel value to consider for the histogram.
+* The function does not count pixels with values greater than max.
+* @param rect Region of interest in the image. If not included, the entire image
+* is used.
+* @return On success, this function returns a report describing the pixel value
+* classification.
+* When you are finished with the report, dispose of it by calling frcDispose().
+* On failure, this function returns nullptr. To get extended error information,
+* call GetLastError().
+*
+*/
+HistogramReport* frcHistogram(const Image* image, int numClasses, float min,
+                              float max) {
+  Rect rect = IMAQ_NO_RECT;
+  return frcHistogram(image, numClasses, min, max, rect);
+}
+HistogramReport* frcHistogram(const Image* image, int numClasses, float min,
+                              float max, Rect rect) {
+  int success;
+  int fillValue = 1;
+
+  /* create the region of interest */
+  ROI* pRoi = imaqCreateROI();
+  success = imaqAddRectContour(pRoi, rect);
+  if (!success) {
+    GetLastVisionError();
+    return nullptr;
+  }
+
+  /* make a mask from the ROI */
+  Image* pMask = frcCreateImage(IMAQ_IMAGE_U8);
+  success = imaqROIToMask(pMask, pRoi, fillValue, nullptr, nullptr);
+  if (!success) {
+    GetLastVisionError();
+    frcDispose(__FUNCTION__, pRoi, nullptr);
+    return nullptr;
+  }
+
+  /* get a histogram report */
+  HistogramReport* pHr = nullptr;
+  pHr = imaqHistogram(image, numClasses, min, max, pMask);
+
+  /* clean up */
+  frcDispose(__FUNCTION__, pRoi, pMask, nullptr);
+
+  return pHr;
+}
+
+/**
+* @brief Calculates the histogram, or pixel distribution, of a color image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+*
+* @param image Image whose histogram the function calculates.
+* @param numClasses The number of classes into which the function separates the
+* pixels.
+* Determines the number of elements in the histogram array returned
+* @param mode The color space in which to perform the histogram. Possible values
+* include IMAQ_RGB and IMAQ_HSL.
+* @param mask An optional mask image. This image must be an IMAQ_IMAGE_U8 image.
+* The function calculates the histogram using only those pixels in the image
+* whose
+* corresponding pixels in the mask are non-zero. Set this parameter to nullptr to
+* calculate
+* the histogram of the entire image, or use the simplified call.
+*
+* @return On success, this function returns a report describing the
+* classification
+* of each plane in a HistogramReport.
+* When you are finished with the report, dispose of it by calling frcDispose().
+* On failure, this function returns nullptr.
+* To get extended error information, call imaqGetLastError().
+*/
+ColorHistogramReport* frcColorHistogram(const Image* image, int numClasses,
+                                        ColorMode mode) {
+  return frcColorHistogram(image, numClasses, mode, nullptr);
+}
+
+ColorHistogramReport* frcColorHistogram(const Image* image, int numClasses,
+                                        ColorMode mode, Image* mask) {
+  return imaqColorHistogram2((Image*)image, numClasses, mode, nullptr, mask);
+}
+
+/**
+* @brief Measures the pixel intensities in a rectangle of an image.
+* Outputs intensity based statistics about an image such as Max, Min, Mean and
+* Std Dev of pixel value.
+* Supports IMAQ_IMAGE_U8 (grayscale) IMAQ_IMAGE_RGB (color) IMAQ_IMAGE_HSL
+* (color-HSL).
+*
+* @param image The image whose pixel value the function queries
+* @param pixel The coordinates of the pixel that the function queries
+* @param value On return, the value of the specified image pixel. This parameter
+* cannot be nullptr.
+* This data structure contains either grayscale, RGB, HSL, Complex or
+* RGBU64Value depending on the type of image.
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcGetPixelValue(const Image* image, Point pixel, PixelValue* value) {
+  return imaqGetPixel(image, pixel, value);
+}
+
+/*   Particle Analysis functions */
+
+/**
+* @brief Filters particles out of an image based on their measurements.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+*
+* @param dest The destination image. If dest is used, it must be the same size
+* as the Source image. It will contain only the filtered particles.
+* @param source The image containing the particles to filter.
+* @param criteria An array of criteria to apply to the particles in the source
+* image. This array cannot be nullptr.
+* See the NIVisionCVI.chm help file for definitions of criteria.
+* @param criteriaCount The number of elements in the criteria array.
+* @param options Binary filter options, including rejectMatches, rejectBorder,
+* and connectivity8.
+* @param rect Area of image to filter. If omitted, the default is entire image.
+* @param numParticles On return, the number of particles left in the image
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcParticleFilter(Image* dest, Image* source,
+                      const ParticleFilterCriteria2* criteria,
+                      int criteriaCount, const ParticleFilterOptions* options,
+                      int* numParticles) {
+  Rect rect = IMAQ_NO_RECT;
+  return frcParticleFilter(dest, source, criteria, criteriaCount, options, rect,
+                           numParticles);
+}
+
+int frcParticleFilter(Image* dest, Image* source,
+                      const ParticleFilterCriteria2* criteria,
+                      int criteriaCount, const ParticleFilterOptions* options,
+                      Rect rect, int* numParticles) {
+  ROI* roi = imaqCreateROI();
+  imaqAddRectContour(roi, rect);
+  return imaqParticleFilter3(dest, source, criteria, criteriaCount, options,
+                             roi, numParticles);
+}
+
+/**
+* @brief Performs morphological transformations on binary images.
+* Supports IMAQ_IMAGE_U8.
+*
+* @param dest The destination image. The border size of the destination image is
+* not important.
+* @param source The image on which the function performs the morphological
+* operations. The calculation
+* modifies the border of the source image. The border must be at least half as
+* large as the larger
+* dimension  of the structuring element.  The connected source image for a
+* morphological transformation
+* must have been created with a border capable of supporting the size of the
+* structuring element.
+* A 3 by 3 structuring element requires a minimal border of 1, a 5 by 5
+* structuring element requires a minimal border of 2, and so on.
+* @param method The morphological transform to apply.
+* @param structuringElement The structuring element used in the operation. Omit
+* this parameter if you do not want a custom structuring element.
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcMorphology(Image* dest, Image* source, MorphologyMethod method) {
+  return imaqMorphology(dest, source, method, nullptr);
+}
+
+int frcMorphology(Image* dest, Image* source, MorphologyMethod method,
+                  const StructuringElement* structuringElement) {
+  return imaqMorphology(dest, source, method, structuringElement);
+}
+
+/**
+* @brief Eliminates particles that touch the border of the image.
+* Supports IMAQ_IMAGE_U8.
+*
+* @param dest The destination image.
+* @param source The source image. If the image has a border, the function sets
+* all border pixel values to 0.
+* @param connectivity8 specifies the type of connectivity used by the algorithm
+* for particle detection.
+* The connectivity mode directly determines whether an adjacent pixel belongs to
+* the same particle or a
+* different particle. Set to TRUE to use connectivity-8 to determine whether
+* particles are touching
+* Set to FALSE to use connectivity-4 to determine whether particles are
+* touching.
+* The default setting for the simplified call is TRUE
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcRejectBorder(Image* dest, Image* source) {
+  return imaqRejectBorder(dest, source, TRUE);
+}
+
+int frcRejectBorder(Image* dest, Image* source, int connectivity8) {
+  return imaqRejectBorder(dest, source, connectivity8);
+}
+
+/**
+* @brief Counts the number of particles in a binary image.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+* @param image binary (thresholded) image
+* @param numParticles On return, the number of particles.
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcCountParticles(Image* image, int* numParticles) {
+  return imaqCountParticles(image, 1, numParticles);
+}
+
+/**
+* @brief Conduct measurements for a single particle in an images.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16, IMAQ_IMAGE_SGL.
+*
+* @param image image with the particle to analyze. This function modifies the
+* source image.
+* If you need the original image, create a copy of the image using frcCopy()
+* before using this function.
+* @param particleNumber The number of the particle to get information on
+* @param par on return, a particle analysis report containing information about
+* the particle. This structure must be created by the caller.
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcParticleAnalysis(Image* image, int particleNumber,
+                        ParticleAnalysisReport* par) {
+  int success = 0;
+
+  /* image information */
+  int height, width;
+  if (!imaqGetImageSize(image, &width, &height)) {
+    return success;
+  }
+  par->imageWidth = width;
+  par->imageHeight = height;
+  par->particleIndex = particleNumber;
+
+  /* center of mass point of the largest particle	*/
+  double returnDouble;
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_CENTER_OF_MASS_X, &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->center_mass_x = (int)returnDouble;  // pixel
+
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_CENTER_OF_MASS_Y, &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->center_mass_y = (int)returnDouble;  // pixel
+
+  /* particle size statistics */
+  success = imaqMeasureParticle(image, particleNumber, 0, IMAQ_MT_AREA,
+                                &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->particleArea = returnDouble;
+
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_BOUNDING_RECT_TOP, &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->boundingRect.top = (int)returnDouble;
+
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_BOUNDING_RECT_LEFT, &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->boundingRect.left = (int)returnDouble;
+
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_BOUNDING_RECT_HEIGHT, &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->boundingRect.height = (int)returnDouble;
+
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_BOUNDING_RECT_WIDTH, &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->boundingRect.width = (int)returnDouble;
+
+  /* particle quality statistics */
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_AREA_BY_IMAGE_AREA, &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->particleToImagePercent = returnDouble;
+
+  success = imaqMeasureParticle(image, particleNumber, 0,
+                                IMAQ_MT_AREA_BY_PARTICLE_AND_HOLES_AREA,
+                                &returnDouble);
+  if (!success) {
+    return success;
+  }
+  par->particleQuality = returnDouble;
+
+  /* normalized position (-1 to 1) */
+  par->center_mass_x_normalized = RangeToNormalized(par->center_mass_x, width);
+  par->center_mass_y_normalized = RangeToNormalized(par->center_mass_y, height);
+
+  return success;
+}
+
+/*   Image Enhancement functions */
+
+/**
+* @brief Improves contrast on a grayscale image.
+* Supports IMAQ_IMAGE_U8, IMAQ_IMAGE_I16.
+* @param dest The destination image.
+* @param source The image to equalize
+* @param min the smallest value used for processing. After processing, all pixel
+* values that are less than or equal to the Minimum in the original image are set
+* to 0 for an 8-bit image. In 16-bit and floating-point images, these pixel
+* values are set to the smallest pixel value found in the original image.
+* @param max the largest value used for processing. After processing, all pixel
+* values that are greater than or equal to the Maximum in the original image are
+* set to 255 for an 8-bit image. In 16-bit and floating-point images, these pixel
+* values are set to the largest pixel value found in the original image.
+* @param mask an 8-bit image that specifies the region of the small image that
+* will be copied. Only those pixels in the Image Src (Small) image that
+* correspond to an equivalent non-zero pixel in the mask image are copied. All
+* other pixels keep their original values. The entire image is processed if Image
+* Mask is nullptr or this parameter is omitted.
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*
+*  option defaults:
+*       searchRect = IMAQ_NO_RECT
+* 		minMatchScore = DEFAULT_MINMAX_SCORE (800)
+*/
+int frcEqualize(Image* dest, const Image* source, float min, float max) {
+  return frcEqualize(dest, source, min, max, nullptr);
+}
+
+int frcEqualize(Image* dest, const Image* source, float min, float max,
+                const Image* mask) {
+  return imaqEqualize(dest, source, min, max, mask);
+}
+
+/**
+* @brief Improves contrast on a color image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL
+*
+* option defaults: colorEqualization = TRUE to equalize all three planes of the
+* image
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+* @param dest The destination image.
+* @param source The image to equalize
+* @param colorEqualization Set this parameter to TRUE to equalize all three
+* planes of the image (the default). Set this parameter to FALSE to equalize only
+* the luminance plane.
+*/
+int frcColorEqualize(Image* dest, const Image* source) {
+  return imaqColorEqualize(dest, source, TRUE);
+}
+
+int frcColorEqualize(Image* dest, const Image* source, int colorEqualization) {
+  return imaqColorEqualize(dest, source, TRUE);
+}
+
+/*   Image Conversion functions */
+
+/**
+* @brief Automatically thresholds a grayscale image into a binary image for
+* Particle Analysis based on a smart threshold.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_I16
+* @param dest The destination image.
+* @param source The image to threshold
+* @param windowWidth The width of the rectangular window around the pixel on
+* which the function
+*  performs the local threshold. This number must be at least 3 and cannot be
+* larger than the width of source
+* @param windowHeight The height of the rectangular window around the pixel on
+* which the function
+* performs the local threshold. This number must be at least 3 and cannot be
+* larger than the height of source
+* @param method Specifies the local thresholding method the function uses. Value
+* can be IMAQ_NIBLACK
+* (which computes thresholds for each pixel based on its local statistics using
+* the Niblack local thresholding
+* algorithm.), or IMAQ_BACKGROUND_CORRECTION (which does background correction
+* first to eliminate non-uniform
+* lighting effects, then performs thresholding using the Otsu thresholding
+* algorithm)
+* @param deviationWeight Specifies the k constant used in the Niblack local
+* thresholding algorithm, which
+* determines the weight applied to the variance calculation. Valid k constants
+* range from 0 to 1. Setting
+* this value to 0 will increase the performance of the function because the
+* function will not calculate the
+* variance for any of the pixels. The function ignores this value if method is
+* not set to IMAQ_NIBLACK
+* @param type Specifies the type of objects for which you want to look. Values
+* can be IMAQ_BRIGHT_OBJECTS
+* or IMAQ_DARK_OBJECTS.
+* @param replaceValue Specifies the replacement value the function uses for the
+* pixels of the kept objects
+* in the destination image.
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcSmartThreshold(Image* dest, const Image* source,
+                      unsigned int windowWidth, unsigned int windowHeight,
+                      LocalThresholdMethod method, double deviationWeight,
+                      ObjectType type) {
+  float replaceValue = 1.0;
+  return imaqLocalThreshold(dest, source, windowWidth, windowHeight, method,
+                            deviationWeight, type, replaceValue);
+}
+
+int frcSmartThreshold(Image* dest, const Image* source,
+                      unsigned int windowWidth, unsigned int windowHeight,
+                      LocalThresholdMethod method, double deviationWeight,
+                      ObjectType type, float replaceValue) {
+  return imaqLocalThreshold(dest, source, windowWidth, windowHeight, method,
+                            deviationWeight, type, replaceValue);
+}
+
+/**
+* @brief Converts a grayscale image to a binary image for Particle Analysis
+* based on a fixed threshold.
+* The function sets pixels values outside of the given range to 0. The function
+* sets pixel values
+* within the range to a given value or leaves the values unchanged.
+* Use the simplified call to leave pixel values unchanged.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_I16.
+*
+* @param dest The destination image.
+* @param source The image to threshold
+* @param rangeMin The lower boundary of the range of pixel values to keep
+* @param rangeMax The upper boundary of the range of pixel values to keep.
+*
+* @return int - error code: 0 = error. To get extended error information, call
+* GetLastError().
+*/
+int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin,
+                       float rangeMax) {
+  int newValue = 255;
+  return frcSimpleThreshold(dest, source, rangeMin, rangeMax, newValue);
+}
+
+/**
+* @brief Converts a grayscale image to a binary image for Particle Analysis
+* based on a fixed threshold.
+* The function sets pixels values outside of the given range to 0. The function
+* sets
+* pixel values within the range to the given value.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_I16.
+*
+* @param dest The destination image.
+* @param source The image to threshold
+* @param rangeMin The lower boundary of the range of pixel values to keep
+* @param rangeMax The upper boundary of the range of pixel values to keep.
+* @param newValue The replacement value for pixels within the range. Use the
+* simplified call to leave the pixel values unchanged
+*
+* @return int - error code: 0 = error. To get extended error information, call
+* GetLastError().
+*/
+int frcSimpleThreshold(Image* dest, const Image* source, float rangeMin,
+                       float rangeMax, float newValue) {
+  int useNewValue = TRUE;
+  return imaqThreshold(dest, source, rangeMin, rangeMax, useNewValue, newValue);
+}
+
+/**
+* @brief Applies a threshold to the Red, Green, and Blue values of a RGB image
+* or the Hue,
+* Saturation, Luminance values for a HSL image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+* This simpler version filters based on a hue range in the HSL mode.
+*
+* @param dest The destination image. This must be a IMAQ_IMAGE_U8 image.
+* @param source The image to threshold
+* @param mode The color space to perform the threshold in. valid values are:
+* IMAQ_RGB, IMAQ_HSL.
+* @param plane1Range The selection range for the first plane of the image. Set
+* this parameter to nullptr to use a selection range from 0 to 255.
+* @param plane2Range The selection range for the second plane of the image. Set
+* this parameter to nullptr to use a selection range from 0 to 255.
+* @param plane3Range The selection range for the third plane of the image. Set
+* this parameter to nullptr to use a selection range from 0 to 255.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+* */
+int frcColorThreshold(Image* dest, const Image* source, ColorMode mode,
+                      const Range* plane1Range, const Range* plane2Range,
+                      const Range* plane3Range) {
+  int replaceValue = 1;
+  return imaqColorThreshold(dest, source, replaceValue, mode, plane1Range,
+                            plane2Range, plane3Range);
+}
+
+/**
+* @brief Applies a threshold to the Red, Green, and Blue values of a RGB image
+* or the Hue,
+* Saturation, Luminance values for a HSL image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+* The simpler version filters based on a hue range in the HSL mode.
+*
+* @param dest The destination image. This must be a IMAQ_IMAGE_U8 image.
+* @param source The image to threshold
+* @param replaceValue Value to assign to selected pixels. Defaults to 1 if
+* simplified call is used.
+* @param mode The color space to perform the threshold in. valid values are:
+* IMAQ_RGB, IMAQ_HSL.
+* @param plane1Range The selection range for the first plane of the image. Set
+* this parameter to nullptr to use a selection range from 0 to 255.
+* @param plane2Range The selection range for the second plane of the image. Set
+* this parameter to nullptr to use a selection range from 0 to 255.
+* @param plane3Range The selection range for the third plane of the image. Set
+* this parameter to nullptr to use a selection range from 0 to 255.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcColorThreshold(Image* dest, const Image* source, int replaceValue,
+                      ColorMode mode, const Range* plane1Range,
+                      const Range* plane2Range, const Range* plane3Range) {
+  return imaqColorThreshold(dest, source, replaceValue, mode, plane1Range,
+                            plane2Range, plane3Range);
+}
+
+/**
+* @brief A simpler version of ColorThreshold that thresholds hue range in the
+* HSL mode. Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL.
+* @param dest The destination image.
+* @param source The image to threshold
+* @param hueRange The selection range for the hue (color).
+* @param minSaturation The minimum saturation value (1-255).  If not used,
+* DEFAULT_SATURATION_THRESHOLD is the default.
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange) {
+  return frcHueThreshold(dest, source, hueRange, DEFAULT_SATURATION_THRESHOLD);
+}
+
+int frcHueThreshold(Image* dest, const Image* source, const Range* hueRange,
+                    int minSaturation) {
+  // assume HSL mode
+  ColorMode mode = IMAQ_HSL;
+  // Set saturation 100 - 255
+  Range satRange;
+  satRange.minValue = minSaturation;
+  satRange.maxValue = 255;
+  // Set luminance 100 - 255
+  Range lumRange;
+  lumRange.minValue = 100;
+  lumRange.maxValue = 255;
+  // Replace pixels with 1 if pass threshold filter
+  int replaceValue = 1;
+  return imaqColorThreshold(dest, source, replaceValue, mode, hueRange,
+                            &satRange, &lumRange);
+}
+
+/**
+* @brief Extracts the Red, Green, Blue, or Hue, Saturation or Luminance
+* information from a color image.
+* Supports IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64.
+*
+* @param image The source image that the function extracts the planes from.
+* @param mode The color space that the function extracts the planes from. Valid
+* values are IMAQ_RGB, IMAQ_HSL, IMAQ_HSV, IMAQ_HSI.
+* @param plane1 On return, the first extracted plane. Set this parameter to nullptr
+* if you do not need this information. RGB-Red, HSL/HSV/HSI-Hue.
+* @param plane2 On return, the second extracted plane. Set this parameter to
+* nullptr if you do not need this information. RGB-Green, HSL/HSV/HSI-Saturation.
+* @param plane3 On return, the third extracted plane. Set this parameter to nullptr
+* if you do not need this information. RGB-Blue, HSL-Luminance, HSV-Value,
+* HSI-Intensity.
+*
+* @return error code: 0 = error. To get extended error information, call
+* GetLastError().
+*/
+int frcExtractColorPlanes(const Image* image, ColorMode mode, Image* plane1,
+                          Image* plane2, Image* plane3) {
+  return imaqExtractColorPlanes(image, mode, plane1, plane2, plane3);
+}
+
+/**
+* @brief Extracts the Hue information from a color image. Supports
+* IMAQ_IMAGE_RGB, IMAQ_IMAGE_HSL, IMAQ_IMAGE_RGB_U64
+*
+* @param image The source image that the function extracts the plane from.
+* @param huePlane On return, the extracted hue plane.
+* @param minSaturation the minimum saturation level required 0-255 (try 50)
+*
+* @return On success: 1. On failure: 0. To get extended error information, call
+* GetLastError().
+*/
+int frcExtractHuePlane(const Image* image, Image* huePlane) {
+  return frcExtractHuePlane(image, huePlane, DEFAULT_SATURATION_THRESHOLD);
+}
+
+int frcExtractHuePlane(const Image* image, Image* huePlane, int minSaturation) {
+  return frcExtractColorPlanes(image, IMAQ_HSL, huePlane, nullptr, nullptr);
+}