text: #include <VX/vx.h>
  #include <VX/vx_compatibility.h>
  #include "opencv2/opencv.hpp"
  #include <string>
  
  using namespace cv;
  using namespace std;
  
  #define ERROR_CHECK_STATUS( status ) { \
          vx_status status_ = (status); \
          if(status_ != VX_SUCCESS) { \
              printf("ERROR: failed with status = (%d) at " __FILE__ "#%d\n", status_, __LINE__); \
              exit(1); \
          } \
  }
  
  #define ERROR_CHECK_OBJECT( obj ) { \
          vx_status status_ = vxGetStatus((vx_reference)(obj)); \
          if(status_ != VX_SUCCESS) { \
              printf("ERROR: failed with status = (%d) at " __FILE__ "#%d\n", status_, __LINE__); \
              exit(1); \
          } \
  }
  
  static void VX_CALLBACK log_callback(vx_context context, vx_reference ref, vx_status status, const vx_char string[])
  {
      size_t len = strlen(string);
      if (len > 0) {
          printf("%s", string);
          if (string[len - 1] != '\n')
              printf("\n");
          fflush(stdout);
      }
  }
  
  int main(int argc, char **argv)
  {
      if (argc < 2) {
          printf("Usage:\n"
                  "./skinToneDetect --image <imageName>\n"
                  "./skinToneDetect --live \n");
          return 0;
      }
      // set image height & width
      int width = 720, height = 480;
  
      // create OpenVX context
      vx_context context = vxCreateContext();
      ERROR_CHECK_OBJECT(context);
      vxRegisterLogCallback(context, log_callback, vx_false_e);
      
      // create OpenVX Graph using the created context
      vx_graph graph = vxCreateGraph(context);
      ERROR_CHECK_OBJECT(graph);
      
      // create input & output images
      vx_image input_rgb_image        = vxCreateImage(context, width, height, VX_DF_IMAGE_RGB);
      vx_image output_skinTone_image  = vxCreateImage(context, width, height, VX_DF_IMAGE_U8);
      ERROR_CHECK_OBJECT(input_rgb_image);
      ERROR_CHECK_OBJECT(output_skinTone_image);
  
      // create intermediate images which are not accessed by the user to be mem optimized
      vx_image R_image        = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image G_image        = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image B_image        = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image RmG_image      = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image RmB_image      = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image R95_image      = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image G40_image      = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image B20_image      = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image RmG15_image    = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image RmB0_image     = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image and1_image     = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image and2_image     = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      vx_image and3_image     = vxCreateVirtualImage(graph, width, height, VX_DF_IMAGE_U8);
      ERROR_CHECK_OBJECT(R_image);
      ERROR_CHECK_OBJECT(G_image);
      ERROR_CHECK_OBJECT(B_image);
      ERROR_CHECK_OBJECT(RmG_image);
      ERROR_CHECK_OBJECT(RmB_image);
      ERROR_CHECK_OBJECT(R95_image);
      ERROR_CHECK_OBJECT(G40_image);
      ERROR_CHECK_OBJECT(B20_image);
      ERROR_CHECK_OBJECT(RmG15_image);
      ERROR_CHECK_OBJECT(RmB0_image);
      ERROR_CHECK_OBJECT(and1_image);
      ERROR_CHECK_OBJECT(and2_image);
      ERROR_CHECK_OBJECT(and3_image);
  
      // create threshold values
      vx_threshold thresh95 = vxCreateThreshold(context, VX_THRESHOLD_TYPE_BINARY, VX_TYPE_UINT8);
      vx_int32 thresValue95 = 95;
      vxSetThresholdAttribute(thresh95, VX_THRESHOLD_THRESHOLD_VALUE, &thresValue95, sizeof(vx_int32));
      ERROR_CHECK_OBJECT(thresh95);
      vx_threshold thresh40 = vxCreateThreshold(context, VX_THRESHOLD_TYPE_BINARY, VX_TYPE_UINT8);
      vx_int32 thresValue40 = 40;
      vxSetThresholdAttribute(thresh40, VX_THRESHOLD_THRESHOLD_VALUE, &thresValue40, sizeof(vx_int32));
      ERROR_CHECK_OBJECT(thresh40);
      vx_threshold thresh20 = vxCreateThreshold(context, VX_THRESHOLD_TYPE_BINARY, VX_TYPE_UINT8);
      vx_int32 thresValue20 = 20;
      vxSetThresholdAttribute(thresh20, VX_THRESHOLD_THRESHOLD_VALUE, &thresValue20, sizeof(vx_int32));
      ERROR_CHECK_OBJECT(thresh20);
      vx_threshold thresh15 = vxCreateThreshold(context, VX_THRESHOLD_TYPE_BINARY, VX_TYPE_UINT8);
      vx_int32 thresValue15 = 15;
      vxSetThresholdAttribute(thresh15, VX_THRESHOLD_THRESHOLD_VALUE, &thresValue15, sizeof(vx_int32));
      ERROR_CHECK_OBJECT(thresh15);
      vx_threshold thresh0 = vxCreateThreshold(context, VX_THRESHOLD_TYPE_BINARY, VX_TYPE_UINT8);
      vx_int32 thresValue0 = 0;
      vxSetThresholdAttribute(thresh0, VX_THRESHOLD_THRESHOLD_VALUE, &thresValue0, sizeof(vx_int32));
      ERROR_CHECK_OBJECT(thresh0);
  
      // add nodes to the graph
      vx_node nodes[] =
      {
          // extract R,G,B channels and compute R-G and R-B
          vxChannelExtractNode(graph, input_rgb_image, VX_CHANNEL_R, R_image),
          vxChannelExtractNode(graph, input_rgb_image, VX_CHANNEL_G, G_image),
          vxChannelExtractNode(graph, input_rgb_image, VX_CHANNEL_B, B_image),
          vxSubtractNode(graph, R_image, G_image, VX_CONVERT_POLICY_SATURATE, RmG_image),
          vxSubtractNode(graph, R_image, B_image, VX_CONVERT_POLICY_SATURATE, RmB_image),
          // compute threshold
          vxThresholdNode(graph, R_image, thresh95, R95_image),
          vxThresholdNode(graph, G_image, thresh40, G40_image),
          vxThresholdNode(graph, B_image, thresh20, B20_image),
          vxThresholdNode(graph, RmG_image, thresh15, RmG15_image),
          vxThresholdNode(graph, RmB_image, thresh0, RmB0_image),
          // aggregate all thresholded values to produce SKIN pixels
          vxAndNode(graph, R95_image, G40_image, and1_image),
          vxAndNode(graph, and1_image, B20_image, and2_image),
          vxAndNode(graph, RmG15_image, RmB0_image, and3_image),
          vxAndNode(graph, and2_image, and3_image, output_skinTone_image)
      };
  
      for( vx_size i = 0; i < sizeof( nodes ) / sizeof( nodes[0] ); i++ )
      {
          ERROR_CHECK_OBJECT( nodes[i] );
      }
  
      // verify graph
      ERROR_CHECK_STATUS( vxVerifyGraph( graph ) );
      
      string option = argv[1];
      Mat input, input_rgb;
      cv::namedWindow("VX SkinTone Detect", cv::WINDOW_GUI_EXPANDED);
  
      if (option == "--image")
      {
          string imageLocation = argv[2];
          input = imread(imageLocation.c_str());
          if (input.empty()) {
             printf("Image not found\n");
             return -1;
          }
          resize(input, input, Size(width, height));
          cvtColor(input, input_rgb, COLOR_BGR2RGB);
          imshow("inputWindow", input);
          vx_rectangle_t cv_rgb_image_region;
          cv_rgb_image_region.start_x    = 0;
          cv_rgb_image_region.start_y    = 0;
          cv_rgb_image_region.end_x      = width;
          cv_rgb_image_region.end_y      = height;
          vx_imagepatch_addressing_t cv_rgb_image_layout{};
          cv_rgb_image_layout.dim_x = input_rgb.cols;
          cv_rgb_image_layout.dim_y = input_rgb.rows;
          cv_rgb_image_layout.stride_x = input_rgb.elemSize();
          cv_rgb_image_layout.stride_y = input_rgb.step;
          vx_uint8 * cv_rgb_image_buffer = input_rgb.data;
          ERROR_CHECK_STATUS( vxCopyImagePatch( input_rgb_image, &cv_rgb_image_region, 0,
                                              &cv_rgb_image_layout, cv_rgb_image_buffer,
                                              VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST ) );
          ERROR_CHECK_STATUS( vxProcessGraph( graph ) );
          vx_rectangle_t rect = { 0, 0, (vx_uint32)width, (vx_uint32)height };
          vx_map_id map_id;
          vx_imagepatch_addressing_t addr;
          void * ptr;
          ERROR_CHECK_STATUS( vxMapImagePatch( output_skinTone_image, &rect, 0, &map_id, &addr, &ptr,
                                              VX_READ_ONLY, VX_MEMORY_TYPE_HOST, VX_NOGAP_X ) );
          Mat mat( height, width, CV_8U, ptr, addr.stride_y );
          imshow( "VX SkinTone Detect", mat );
          waitKey(0);
          ERROR_CHECK_STATUS( vxUnmapImagePatch( output_skinTone_image, map_id ) );
      }
      else if (option == "--live")
      {
          VideoCapture cap(0);
          if (!cap.isOpened()) {
              printf("Unable to open camera\n");
              return -1;
          }
          for(;;) {
              cap >> input;
              resize(input, input, Size(width, height));
              cvtColor(input, input_rgb, COLOR_BGR2RGB);
              imshow("inputWindow", input);
              if(waitKey(30) >= 0) break;
              vx_rectangle_t cv_rgb_image_region;
              cv_rgb_image_region.start_x    = 0;
              cv_rgb_image_region.start_y    = 0;
              cv_rgb_image_region.end_x      = width;
              cv_rgb_image_region.end_y      = height;
              vx_imagepatch_addressing_t cv_rgb_image_layout;
              cv_rgb_image_layout.stride_x   = 3;
              cv_rgb_image_layout.stride_y   = input_rgb.step;
              vx_uint8 * cv_rgb_image_buffer = input_rgb.data;
              ERROR_CHECK_STATUS( vxCopyImagePatch( input_rgb_image, &cv_rgb_image_region, 0,
                                                  &cv_rgb_image_layout, cv_rgb_image_buffer,
                                                  VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST ) );
              ERROR_CHECK_STATUS( vxProcessGraph( graph ) );
              vx_rectangle_t rect = { 0, 0, (vx_uint32)width, (vx_uint32)height };
              vx_map_id map_id;
              vx_imagepatch_addressing_t addr;
              void * ptr;
              ERROR_CHECK_STATUS( vxMapImagePatch( output_skinTone_image, &rect, 0, &map_id, &addr, &ptr,
                                              VX_READ_ONLY, VX_MEMORY_TYPE_HOST, VX_NOGAP_X ) );
              Mat mat( height, width, CV_8U, ptr, addr.stride_y );
              imshow( "VX SkinTone Detect", mat );
              if(waitKey(30) >= 0) break;
              ERROR_CHECK_STATUS( vxUnmapImagePatch( output_skinTone_image, map_id ) );
          }
      }
      else {
          printf("Usage:\n"
                  "./skinToneDetect --image <imageName>\n"
                  "./skinToneDetect --live \n");
          return -1;
      }
  
      // release objects
      for( vx_size i = 0; i < sizeof( nodes ) / sizeof( nodes[0] ); i++ )
      {
          ERROR_CHECK_STATUS( vxReleaseNode( &nodes[i] ) );
      }
      ERROR_CHECK_STATUS(vxReleaseGraph( &graph ));
      ERROR_CHECK_STATUS(vxReleaseThreshold( &thresh95 ));
      ERROR_CHECK_STATUS(vxReleaseThreshold( &thresh40 ));
      ERROR_CHECK_STATUS(vxReleaseThreshold( &thresh20 ));
      ERROR_CHECK_STATUS(vxReleaseThreshold( &thresh15 ));
      ERROR_CHECK_STATUS(vxReleaseThreshold( &thresh0 ));
      ERROR_CHECK_STATUS(vxReleaseImage( &R_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &G_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &B_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &RmG_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &RmB_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &R95_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &G40_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &B20_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &RmG15_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &RmB0_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &and1_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &and2_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &and3_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &input_rgb_image ));
      ERROR_CHECK_STATUS(vxReleaseImage( &output_skinTone_image ));
      ERROR_CHECK_STATUS(vxReleaseContext( &context ));
      return 0;
  }
  