//***************************************************************************************/
//
// File name: Meas2StepOverview.cpp
// Location: See Matrox Example Launcher in the MIL Control Center
// 
//
// Synopsis:  This program illustrates how a 2 step measurement
//            approach can be used to improve the accuracy
//
//            See the PrintHeader() function below for a detailed description.
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
// All Rights Reserved

#include <mil.h>
#include <math.h>
#include "MeasOverviewExample.h"
#include "ProfileDisplay.h"


///***************************************************************************
// Example description.
///***************************************************************************
void PrintHeader()   
   {
   MosPrintf(MIL_TEXT("[EXAMPLE NAME]\n")
             MIL_TEXT("Meas2StepOverview\n\n")

             MIL_TEXT("[SYNOPSIS]\n")
             MIL_TEXT("This program illustrates how a 2 step measurement approach can be used to\n")
             MIL_TEXT("improve accuracy. The 2 step approach will be performed in the following cases:\n")
             MIL_TEXT("   1. To improve the accuracy of the edge position and angle.\n")
             MIL_TEXT("   2. To improve the accuracy of the stripe width.\n\n")
             
             MIL_TEXT("[MODULES USED]\n")
             MIL_TEXT("Modules used: application, system, display, buffer,\n")
             MIL_TEXT("graphic, image processing, calibration, measurement.\n\n"));

   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();
   }

//***************************************************************************
// Example images.
//***************************************************************************
static const MIL_CONST_TEXT_PTR IMAGE_FILE = EXAMPLE_IMAGE_PATH MIL_TEXT("MetalPieceRotatedThinned.tif");

//***************************************************************************
// Example declarations.
//***************************************************************************
void TwoStepPositionAngleAccuracyExample(MIL_ID MilSystem, 
                                         MIL_ID MilDisplay,
                                         MIL_ID MilGraList,
                                         MIL_ID MilImage,
                                         CProfileDisplay* pProfileDisplay);

void TwoStepWidthAccuracyExample(MIL_ID MilSystem, 
                                 MIL_ID MilDisplay,
                                 MIL_ID MilGraList,
                                 MIL_ID MilImage,
                                 CProfileDisplay* pProfileDisplay);

//*****************************************************************************
// Main.
//*****************************************************************************
int MosMain(void)
   {
   // Allocate the MIL objects.
   MIL_ID MilApplication      = MappAlloc(M_NULL, M_DEFAULT, M_NULL);
   MIL_ID MilSystem           = MsysAlloc(M_DEFAULT, M_SYSTEM_HOST, M_DEFAULT, M_DEFAULT, M_NULL);
   MIL_ID MilDisplay          = MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_WINDOWED, M_NULL);
   MIL_ID MilGraList = MgraAllocList(MilSystem, M_DEFAULT, M_NULL);

   // Associate the graphic list to the display.
   MdispControl(MilDisplay, M_ASSOCIATED_GRAPHIC_LIST_ID, MilGraList);

   // Allocate the profile display.
   CProfileDisplay* pProfileDisplay = new CProfileDisplay(MilSystem);

   // Move the display.
   MdispControl(MilDisplay, M_WINDOW_INITIAL_POSITION_Y, pProfileDisplay->ProfileImageSizeY() + WINDOWS_OFFSET_Y);

   // Print Header.
   PrintHeader();

   // Restore the image and calibrate it.
   MIL_ID MilImage = MbufRestore(IMAGE_FILE, MilSystem, M_NULL);
   McalUniform(MilImage, 0.0, 0.0, 1.0, 1.0, 0.0, M_DEFAULT);
   MdispSelect(MilDisplay, MilImage);

   // Start the two step position accuracy case.
   TwoStepPositionAngleAccuracyExample(MilSystem, MilDisplay, MilGraList, MilImage, pProfileDisplay);

   // Clear the annotations.
   MgraClear(M_DEFAULT, MilGraList);
   pProfileDisplay->Clear();

   // Start the two step width accuracy case.
   TwoStepWidthAccuracyExample(MilSystem, MilDisplay, MilGraList, MilImage, pProfileDisplay);

   // Delete the profile display.
   delete pProfileDisplay;

   // Free other allocations.
   MbufFree(MilImage);
   MgraFree(MilGraList);
   MdispFree(MilDisplay);
   MsysFree(MilSystem);
   MappFree(MilApplication);

   return 0;
   }

//*****************************************************************************
// Constants for position and angle accuracy case.
//*****************************************************************************
static const MIL_DOUBLE ROUGH_BOX_CENTER_X = 242;
static const MIL_DOUBLE ROUGH_BOX_CENTER_Y = 108;
static const MIL_DOUBLE ROUGH_BOX_WIDTH = 104;
static const MIL_DOUBLE ROUGH_BOX_HEIGHT = 286;
static const MIL_DOUBLE FINE_BOX_WIDTH = 30;
static const MIL_DOUBLE ROUGH_BOX_ANGLE = 277.5;
static const MIL_DOUBLE ROUGH_FILTER_SMOOTHNESS = 50;
static const MIL_DOUBLE ROUGH_MAX_ASSOCIATION_DISTANCE = 10;
static const MIL_DOUBLE FINE_MAX_ASSOCIATION_DISTANCE = 3;
static const MIL_DOUBLE NB_SUB_REGIONS = 7;
static const MIL_DOUBLE POSITION_ZOOM = 8;

//*****************************************************************************
// Position and angle accuracy case.
//*****************************************************************************
void TwoStepPositionAngleAccuracyExample(MIL_ID MilSystem, 
                                         MIL_ID MilDisplay,
                                         MIL_ID MilGraList,
                                         MIL_ID MilImage,
                                         CProfileDisplay* pProfileDisplay)
   {
   MosPrintf(MIL_TEXT("1. To improve the accuracy of the edge position and angle.\n\n")
             
             MIL_TEXT("In this case, the edge transition is not exactly aligned\n")
             MIL_TEXT("with the search region. This affects the precision of the position\n")
             MIL_TEXT("and angle found. Using a 2 step measurement approach helps to improve\n")
             MIL_TEXT("both the accuracy of the found position and angle.\n\n")
             MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();             

   // Allocate the measurement marker.
   MIL_ID MilEdgeMarker = MmeasAllocMarker(MilSystem, M_EDGE, M_DEFAULT, M_NULL);

   // Set up the marker.
   MmeasSetMarker(MilEdgeMarker, M_BOX_CENTER, ROUGH_BOX_CENTER_X, ROUGH_BOX_CENTER_Y);
   MmeasSetMarker(MilEdgeMarker, M_BOX_SIZE, ROUGH_BOX_WIDTH, ROUGH_BOX_HEIGHT);
   MmeasSetMarker(MilEdgeMarker, M_BOX_ANGLE, ROUGH_BOX_ANGLE, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_FILTER_TYPE, M_SHEN, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_FILTER_SMOOTHNESS, ROUGH_FILTER_SMOOTHNESS, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_POLARITY, M_POSITIVE, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_SUB_REGIONS_NUMBER, NB_SUB_REGIONS, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_MAX_ASSOCIATION_DISTANCE, ROUGH_MAX_ASSOCIATION_DISTANCE, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_SEARCH_REGION_INPUT_UNITS, M_WORLD, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_DRAW_PROFILE_SCALE_OFFSET, M_AUTO_SCALE_PROFILE, M_DEFAULT);

   // Find the marker.
   MmeasFindMarker(M_DEFAULT, MilImage, MilEdgeMarker, M_DEFAULT);

   // Get the status.
   MIL_INT ValidFlag;
   MmeasGetResult(MilEdgeMarker, M_VALID_FLAG + M_TYPE_MIL_INT, &ValidFlag, M_NULL);
   if(ValidFlag == M_TRUE)
      {
      // Draw the edge annotation in the image.
      EDGE_DRAW_LIST.DrawList(MilEdgeMarker, MilGraList);

      // Create the profile.
      pProfileDisplay->CreateProfile(MilImage, MilEdgeMarker);

      MosPrintf(MIL_TEXT("The rough position and angle of the edge was found.\n\n")
                MIL_TEXT("Press <Enter> to continue.\n\n"));
      MosGetch();

      // Get the position and angle of the marker.
      MIL_DOUBLE RoughPosX;
      MIL_DOUBLE RoughPosY;
      MIL_DOUBLE RoughAngle;
      MmeasGetResult(MilEdgeMarker, M_POSITION, &RoughPosX, &RoughPosY);
      MmeasGetResult(MilEdgeMarker, M_ANGLE, &RoughAngle, M_NULL);

      // Move the box at the rough location.
      MmeasSetMarker(MilEdgeMarker, M_BOX_CENTER, RoughPosX, RoughPosY);
      MmeasSetMarker(MilEdgeMarker, M_BOX_SIZE, FINE_BOX_WIDTH, ROUGH_BOX_HEIGHT);
      MmeasSetMarker(MilEdgeMarker, M_BOX_ANGLE, RoughAngle-90, M_NULL);
      MmeasSetMarker(MilEdgeMarker, M_MAX_ASSOCIATION_DISTANCE, FINE_MAX_ASSOCIATION_DISTANCE, M_NULL);

      // Find the edge with high precision.
      MmeasFindMarker(M_DEFAULT, MilImage, MilEdgeMarker, M_DEFAULT);

      // Get the status.
      MmeasGetResult(MilEdgeMarker, M_VALID_FLAG + M_TYPE_MIL_INT, &ValidFlag, M_NULL);
      if(ValidFlag == M_TRUE)
         {
         // Draw the edge annotations in the image.
         EDGE_DRAW_LIST.DrawList(MilEdgeMarker, MilGraList);

         // Create the profile.
         pProfileDisplay->ClearAnnotations();
         pProfileDisplay->CreateProfile(MilImage, MilEdgeMarker);

         // Get the position and angle of the marker.
         MIL_DOUBLE FinePosX;
         MIL_DOUBLE FinePosY;
         MIL_DOUBLE FineAngle;
         MmeasGetResult(MilEdgeMarker, M_POSITION, &FinePosX, &FinePosY);
         MmeasGetResult(MilEdgeMarker, M_ANGLE, &FineAngle, M_NULL);

         MosPrintf(MIL_TEXT("The precise position and angle of the edge was found\n")
                   MIL_TEXT("in a second region whose position is based on the rough edge found.\n\n")
                   MIL_TEXT("Press <Enter> to continue.\n\n"));
         MosGetch();

         // Print the result.
         MosPrintf(MIL_TEXT("          |-------------------|-------------------|\n")
                   MIL_TEXT("          |       Rough       |      Refined      |\n")
                   MIL_TEXT("|---------|-------------------|-------------------|\n")
                   MIL_TEXT("|   Pos   |  (%6.2f,%-6.2f)  |  (%6.2f,%-6.2f)  |\n")
                   MIL_TEXT("|---------|-------------------|-------------------|\n")
                   MIL_TEXT("|  Angle  |%12.2f       |%12.2f       |\n")
                   MIL_TEXT("|-----------------------------|-------------------|\n\n"),
                   RoughPosX, RoughPosY, FinePosX, FinePosY,
                   RoughAngle, FineAngle);
         }
      }

   if(ValidFlag == M_FALSE)
      MosPrintf(MIL_TEXT("Unable to find the marker...\n\n"));

   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

   // Free the marker.
   MmeasFree(MilEdgeMarker);
   };


//***************************************************************************
// Constants for width and accuracy case.
//***************************************************************************
static const MIL_DOUBLE STRIPE_BOX_CENTER_X = 269;
static const MIL_DOUBLE STRIPE_BOX_CENTER_Y = 175;
static const MIL_DOUBLE STRIPE_BOX_WIDTH = 72;
static const MIL_DOUBLE STRIPE_BOX_HEIGHT = 29;
static const MIL_DOUBLE STRIPE_BOX_ANGLE = 5;
static const MIL_DOUBLE STRIPE_FILTER_SMOOTHNESS= 90;
static const MIL_DOUBLE DISPLAY_ZOOM = 16;

//***************************************************************************
// Width accuracy case.
//***************************************************************************
void TwoStepWidthAccuracyExample(MIL_ID MilSystem, 
                                 MIL_ID MilDisplay,
                                 MIL_ID MilGraList,
                                 MIL_ID MilImage,
                                 CProfileDisplay* pProfileDisplay)
                                 
   {
   MosPrintf(MIL_TEXT("2. To improve the accuracy of the stripe width.\n\n")
             
             MIL_TEXT("In this case, a really thin stripe is found using a marker whose\n")
             MIL_TEXT("smoothness parameter, given the size of the stripe, causes edge\n")
             MIL_TEXT("displacement. The width is then overestimated. By using a second step to\n")
             MIL_TEXT("measure each edge individually, a more accurate width can be calculated.\n\n")
             MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch(); 

   // Get the size of the image.
   MIL_INT ImageSizeX = MbufInquire(MilImage, M_SIZE_X, M_NULL);
   MIL_INT ImageSizeY = MbufInquire(MilImage, M_SIZE_Y, M_NULL);

   // Allocate the stripe measurement marker.
   MIL_ID MilStripeMarker = MmeasAllocMarker(MilSystem, M_STRIPE, M_DEFAULT, M_NULL);
   MIL_ID MilEdgeMarker = MmeasAllocMarker(MilSystem, M_EDGE, M_DEFAULT, M_NULL);

   // Set up the marker.
   MmeasSetMarker(MilStripeMarker, M_BOX_CENTER, STRIPE_BOX_CENTER_X, STRIPE_BOX_CENTER_Y);
   MmeasSetMarker(MilStripeMarker, M_BOX_SIZE, STRIPE_BOX_WIDTH, STRIPE_BOX_HEIGHT);
   MmeasSetMarker(MilStripeMarker, M_BOX_ANGLE, STRIPE_BOX_ANGLE, M_NULL);
   MmeasSetMarker(MilStripeMarker, M_FILTER_TYPE, M_SHEN, M_NULL);
   MmeasSetMarker(MilStripeMarker, M_FILTER_SMOOTHNESS, STRIPE_FILTER_SMOOTHNESS, M_NULL);
   MmeasSetMarker(MilStripeMarker, M_SEARCH_REGION_INPUT_UNITS, M_WORLD, M_NULL);
   MmeasSetMarker(MilStripeMarker, M_DRAW_PROFILE_SCALE_OFFSET, M_AUTO_SCALE_PROFILE, M_DEFAULT);
   MmeasSetMarker(MilEdgeMarker, M_FILTER_TYPE, M_SHEN, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_FILTER_SMOOTHNESS, STRIPE_FILTER_SMOOTHNESS, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_SEARCH_REGION_INPUT_UNITS, M_WORLD, M_NULL);
   MmeasSetMarker(MilEdgeMarker, M_DRAW_PROFILE_SCALE_OFFSET, M_AUTO_SCALE_PROFILE, M_DEFAULT);
   
   // Find the marker.
   MmeasFindMarker(M_DEFAULT, MilImage, MilStripeMarker, M_DEFAULT);

   // Get the status.
   MIL_INT ValidFlag;
   MmeasGetResult(MilStripeMarker, M_VALID_FLAG + M_TYPE_MIL_INT, &ValidFlag, M_NULL);
   if(ValidFlag == M_TRUE)
      {
      // Draw the edge annotations in the image.
      STRIPE_SIMPLE_DRAW_LIST.DrawList(MilStripeMarker, MilGraList);

      // Create the profile.
      pProfileDisplay->CreateProfile(MilImage, MilStripeMarker);

      MosPrintf(MIL_TEXT("The position and width of the stripe was found.\n\n")
                MIL_TEXT("Press <Enter> to continue.\n\n"));
      MosGetch();

      // Get the position of the edges and the angle of the stripe.
      MIL_DOUBLE StripePosX;
      MIL_DOUBLE StripePosY;
      MIL_DOUBLE EdgePosX[2];
      MIL_DOUBLE EdgePosY[2];
      MIL_DOUBLE StripeEdgeStartX[2];
      MIL_DOUBLE StripeEdgeStartY[2];
      MIL_DOUBLE StripeEdgeEndX[2];
      MIL_DOUBLE StripeEdgeEndY[2];
      MIL_DOUBLE StripeAngle;
      MIL_DOUBLE StripeWidth;
      MmeasGetResult(MilStripeMarker, M_POSITION, &StripePosX, &StripePosY);
      MmeasGetResult(MilStripeMarker, M_EDGE_START + M_EDGE_FIRST, &StripeEdgeStartX[0], &StripeEdgeStartY[0]);
      MmeasGetResult(MilStripeMarker, M_EDGE_START + M_EDGE_SECOND, &StripeEdgeStartX[1], &StripeEdgeStartY[1]);
      MmeasGetResult(MilStripeMarker, M_EDGE_END + M_EDGE_FIRST, &StripeEdgeEndX[0], &StripeEdgeEndY[0]);
      MmeasGetResult(MilStripeMarker, M_EDGE_END + M_EDGE_SECOND, &StripeEdgeEndX[1], &StripeEdgeEndY[1]);
      MmeasGetResult(MilStripeMarker, M_ANGLE, &StripeAngle, M_NULL);
      MmeasGetResult(MilStripeMarker, M_STRIPE_WIDTH, &StripeWidth, M_NULL);

      // Find the first edge accurately. The box width is from the start of the first transition 
      // to the start of the second transition. One pixel is added on each side to have enough data.
      MIL_DOUBLE Dx = StripeEdgeStartX[1] - StripeEdgeStartX[0];
      MIL_DOUBLE Dy = StripeEdgeStartY[1] - StripeEdgeStartY[0];
      MIL_DOUBLE BoxWidth = sqrt(Dx*Dx + Dy*Dy) + 2;
      MmeasSetMarker(MilEdgeMarker, M_BOX_SIZE, BoxWidth, STRIPE_BOX_HEIGHT);
      MmeasSetMarker(MilEdgeMarker, M_BOX_CENTER, (StripeEdgeStartX[0] + StripeEdgeStartX[1])/2, (StripeEdgeStartY[0] + StripeEdgeStartY[1])/2);
      MmeasSetMarker(MilEdgeMarker, M_BOX_ANGLE, StripeAngle-90, M_NULL);
      MmeasFindMarker(M_DEFAULT, MilImage, MilEdgeMarker, M_DEFAULT);

      // Get the status.
      MmeasGetResult(MilEdgeMarker, M_VALID_FLAG + M_TYPE_MIL_INT, &ValidFlag, M_NULL);
      if(ValidFlag == M_TRUE)
         {
         EDGE_DRAW_LIST.DrawList(MilEdgeMarker, MilGraList);

         // Create the profile.
         pProfileDisplay->ClearAnnotations();
         pProfileDisplay->CreateProfile(MilImage, MilEdgeMarker);

         // Get the position of the first edge.
         MmeasGetResult(MilEdgeMarker, M_POSITION, &EdgePosX[0], &EdgePosY[0]);

         // Zoom on the stripe.
         MdispZoom(MilDisplay, DISPLAY_ZOOM, DISPLAY_ZOOM);
         MdispPan(MilDisplay, StripePosX - ImageSizeX*0.5/DISPLAY_ZOOM, StripePosY - ImageSizeY*0.5/DISPLAY_ZOOM);

         MosPrintf(MIL_TEXT("The first edge position was refined.\n\n")
                   MIL_TEXT("Press <Enter> to continue.\n\n"));
         MosGetch();

         // Find the second edge accurately.
         Dx = StripeEdgeEndX[1] - StripeEdgeEndX[0];
         Dy = StripeEdgeEndY[1] - StripeEdgeEndY[0];
         BoxWidth = sqrt(Dx*Dx + Dy*Dy) + 2;
         MmeasSetMarker(MilEdgeMarker, M_BOX_SIZE, BoxWidth, STRIPE_BOX_HEIGHT);
         MmeasSetMarker(MilEdgeMarker, M_BOX_CENTER, (StripeEdgeEndX[0] + StripeEdgeEndX[1])/2, (StripeEdgeEndY[0] + StripeEdgeEndY[1])/2);
         MmeasFindMarker(M_DEFAULT, MilImage, MilEdgeMarker, M_DEFAULT);

         // Get the status.
         MmeasGetResult(MilEdgeMarker, M_VALID_FLAG + M_TYPE_MIL_INT, &ValidFlag, M_NULL);
         if(ValidFlag == M_TRUE)
            {
            EDGE_DRAW_LIST.DrawList(MilEdgeMarker, MilGraList);
            
            // Create the profile.
            pProfileDisplay->ClearAnnotations();
            pProfileDisplay->CreateProfile(MilImage, MilEdgeMarker);

            // Get the position of the second edge.
            MmeasGetResult(MilEdgeMarker, M_POSITION, &EdgePosX[1], &EdgePosY[1]);
            Dx = EdgePosX[1] - EdgePosX[0];
            Dy = EdgePosY[1] - EdgePosY[0];
            MIL_DOUBLE FineStripeWidth = sqrt(Dx*Dx + Dy*Dy);

            MosPrintf(MIL_TEXT("The second edge position was refined.\n\n")
                      MIL_TEXT("Press <Enter> to continue.\n\n"));
            MosGetch();

            // Print the result.
            MosPrintf(MIL_TEXT("          |-------------------|-------------------|\n")
                      MIL_TEXT("          |       Rough       |      ReFined      |\n")
                      MIL_TEXT("|---------|-------------------|-------------------|\n")
                      MIL_TEXT("|  Width  |%12.2f       |%12.2f       |\n")
                      MIL_TEXT("|---------|-------------------|-------------------|\n\n"),
                      StripeWidth, FineStripeWidth);
            }
         }
      }

    
   if(ValidFlag == M_FALSE)
      MosPrintf(MIL_TEXT("Unable to find the marker...\n\n"));

   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

   // Free the marker.
   MmeasFree(MilEdgeMarker);
   MmeasFree(MilStripeMarker);
   }