//*****************************************************************************
//
// File name: Mcol.cs
// Location: See Matrox Example Launcher in the MIL Control Center
// 
//
// Synopsis:  This program contains 4 examples using the MIL Color module.
//
//            The first example performs color segmentation of an image
//            by classifying each pixel with one out of 6 color samples.
//            The ratio of each color in the image is then calculated.
//
//            The second example performs color matching of circular regions
//            in objects located with model finder.
//
//            The third example performs color separation in order to
//            separate 2 types of ink on a piece of paper.
//
//            The fourth example performs a color to grayscale conversion
//            using the principal component projection functionality.
//
// Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
// All Rights Reserved
//*****************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using Matrox.MatroxImagingLibrary;

namespace MCol
{
    class Program
    {
        // Display image margin
        const int DISPLAY_CENTER_MARGIN_X = 5;
        const int DISPLAY_CENTER_MARGIN_Y = 5;

        // Color patch sizes
        const int COLOR_PATCH_SIZEX = 30;
        const int COLOR_PATCH_SIZEY = 40;

        static void Main(string[] args)
        {
            //****************************************************************************
            // Main.
            //****************************************************************************

            MIL_ID MilApplication = MIL.M_NULL;     // Application identifier.
            MIL_ID MilSystem = MIL.M_NULL;          // System identifier.
            MIL_ID MilDisplay = MIL.M_NULL;         // Display identifier.

            // Allocate defaults.
            MIL.MappAllocDefault(MIL.M_DEFAULT, ref MilApplication, ref MilSystem, ref MilDisplay, MIL.M_NULL, MIL.M_NULL);

            // Run the color segmentation example.
            ColorSegmentationExample(MilSystem, MilDisplay);

            // Run the color matching example.
            ColorMatchingExample(MilSystem, MilDisplay);

            // Run the color projection example.
            ColorSeparationExample(MilSystem, MilDisplay);

            // Run the RGB to grayscale conversion example.
            RGBtoGrayscaleExample(MilSystem, MilDisplay);

            // Free defaults.
            MIL.MappFreeDefault(MilApplication, MilSystem, MilDisplay, MIL.M_NULL, MIL.M_NULL);
        }

        //***************************************************************************
        // Color Segmentation using color samples. 
        //***************************************************************************

        // Image filenames
        const string CANDY_SAMPLE_IMAGE_FILE = MIL.M_IMAGE_PATH + "CandySamples.mim";
        const string CANDY_TARGET_IMAGE_FILE = MIL.M_IMAGE_PATH + "Candy.mim";

        // Number of samples
        const int NUM_SAMPLES = 6;

        // Draw spacing and offset
        const int CANDY_SAMPLES_XSPACING = 35;
        const int CANDY_SAMPLES_YOFFSET  = 145;

        // Match parameters
        const int MATCH_MODE                = MIL.M_MIN_DIST_VOTE;  // Minimal distance vote mode.
        const int DISTANCE_TYPE             = MIL.M_MAHALANOBIS;    // Mahalanobis distance.
        const int TOLERANCE_MODE            = MIL.M_SAMPLE_STDDEV;  // Standard deviation tolerance mode.
        const double TOLERANCE_VALUE        = 6.0;                  // Mahalanobis tolerance value.
        const double RED_TOLERANCE_VALUE    = 6.0;
        const double YELLOW_TOLERANCE_VALUE = 12.0;
        const double PINK_TOLERANCE_VALUE   = 5.0;

        static void ColorSegmentationExample(MIL_ID MilSystem, MIL_ID MilDisplay)
        {
            MIL_ID SourceChild     = MIL.M_NULL;           // Source image buffer identifier.
            MIL_ID DestChild       = MIL.M_NULL;           // Dest image buffer identifier.
            MIL_ID MatchContext    = MIL.M_NULL;           // Color matching context identifier.
            MIL_ID MatchResult     = MIL.M_NULL;           // Color matching result identifier.
            MIL_ID DisplayImage    = MIL.M_NULL;           // Display image buffer identifier.

            MIL_INT SourceSizeX    = 0;
            MIL_INT SourceSizeY    = 0;                    // Source image sizes
            MIL_INT SampleIndex    = 0;
            MIL_INT SpacesIndex    = 0;                    // Indices

            double MatchScore      = 0.0;                  // Color matching score.

            // Blank spaces to align the samples names evenly.
            string[] Spaces = { "", " ", "  ", "   " };

            // Color samples names.
            string[] SampleNames = { "Green", "Red", "Yellow", "Purple", "Blue", "Pink" };

            // Color samples position: {OffsetX, OffsetY} 
            double[,] SamplesROI = new double[,] { { 58, 143 }, { 136, 148 }, { 217, 144 }, { 295, 142 }, { 367, 143 }, { 442, 147 } };

            // Color samples size.
            const double SampleSizeX = 36, SampleSizeY = 32;

            // Array for match sample colors.
            MIL_INT[,] SampleMatchColor = new MIL_INT[NUM_SAMPLES, 3];

            Console.Write("\nCOLOR SEGMENTATION:\n");
            Console.Write("-------------------\n");

            // Allocate the parent display image.
            MIL.MbufDiskInquire(CANDY_SAMPLE_IMAGE_FILE, MIL.M_SIZE_X, ref SourceSizeX);
            MIL.MbufDiskInquire(CANDY_SAMPLE_IMAGE_FILE, MIL.M_SIZE_Y, ref SourceSizeY);
            MIL.MbufAllocColor(MilSystem, 3, 2 * SourceSizeX + DISPLAY_CENTER_MARGIN_X, SourceSizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_DISP + MIL.M_PROC, ref DisplayImage);
            MIL.MbufClear(DisplayImage, MIL.M_COLOR_BLACK);

            // Create a source and dest child in the display image.
            MIL.MbufChild2d(DisplayImage, 0, 0, SourceSizeX, SourceSizeY, ref SourceChild);
            MIL.MbufChild2d(DisplayImage, SourceSizeX + DISPLAY_CENTER_MARGIN_X, 0, SourceSizeX, SourceSizeY, ref DestChild);

            // Load the source image into the source child.
            MIL.MbufLoad(CANDY_SAMPLE_IMAGE_FILE, SourceChild);

            // Allocate a color matching context.
            MIL.McolAlloc(MilSystem, MIL.M_COLOR_MATCHING, MIL.M_RGB, MIL.M_DEFAULT, MIL.M_DEFAULT, ref MatchContext);

            // Define each color sample in the context.
            for (int i = 0; i < NUM_SAMPLES; i++)
            {
                MIL.McolDefine(MatchContext, SourceChild, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_IMAGE, SamplesROI[i, 0], SamplesROI[i, 1], SampleSizeX, SampleSizeY);
            }

            // Set the color matching parameters.
            MIL.McolSetMethod(MatchContext, MATCH_MODE, DISTANCE_TYPE, MIL.M_DEFAULT, MIL.M_DEFAULT);
            MIL.McolControl(MatchContext, MIL.M_CONTEXT, MIL.M_DISTANCE_TOLERANCE_MODE, TOLERANCE_MODE);
            MIL.McolControl(MatchContext, MIL.M_ALL, MIL.M_DISTANCE_TOLERANCE, TOLERANCE_VALUE);

            // Adjust tolerances for the red, yellow and pink samples.
            MIL.McolControl(MatchContext, MIL.M_SAMPLE_INDEX(1), MIL.M_DISTANCE_TOLERANCE, RED_TOLERANCE_VALUE);
            MIL.McolControl(MatchContext, MIL.M_SAMPLE_INDEX(2), MIL.M_DISTANCE_TOLERANCE, YELLOW_TOLERANCE_VALUE);
            MIL.McolControl(MatchContext, MIL.M_SAMPLE_INDEX(5), MIL.M_DISTANCE_TOLERANCE, PINK_TOLERANCE_VALUE);

            // Preprocess the context.
            MIL.McolPreprocess(MatchContext, MIL.M_DEFAULT);

            // Fill the samples colors array.
            for (int i = 0; i < NUM_SAMPLES; i++)
            {
                MIL.McolInquire(MatchContext, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_MATCH_SAMPLE_COLOR_BAND_0 + MIL.M_TYPE_MIL_INT, ref SampleMatchColor[i, 0]);
                MIL.McolInquire(MatchContext, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_MATCH_SAMPLE_COLOR_BAND_1 + MIL.M_TYPE_MIL_INT, ref SampleMatchColor[i, 1]);
                MIL.McolInquire(MatchContext, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_MATCH_SAMPLE_COLOR_BAND_2 + MIL.M_TYPE_MIL_INT, ref SampleMatchColor[i, 2]);
            }

            // Draw the samples.
            DrawSampleColors(DestChild, SampleMatchColor, SampleNames, NUM_SAMPLES, CANDY_SAMPLES_XSPACING, CANDY_SAMPLES_YOFFSET);

            // Select the image buffer for display.
            MIL.MdispSelect(MilDisplay, DisplayImage);

            // Pause to show the original image.
            Console.Write("Color samples are defined for each possible candy color.\n");
            Console.Write("Press <Enter> to do color matching.\n\n");
            Console.ReadKey();

            // Load the target image.*/
            MIL.MbufClear(DisplayImage, MIL.M_COLOR_BLACK);
            MIL.MbufLoad(CANDY_TARGET_IMAGE_FILE, SourceChild);

            // Allocate a color matching result buffer.
            MIL.McolAllocResult(MilSystem, MIL.M_COLOR_MATCHING_RESULT, MIL.M_DEFAULT, ref MatchResult);

            // Enable controls to draw the labeled color image.
            MIL.McolControl(MatchContext, MIL.M_CONTEXT, MIL.M_GENERATE_PIXEL_MATCH, MIL.M_ENABLE);
            MIL.McolControl(MatchContext, MIL.M_CONTEXT, MIL.M_GENERATE_SAMPLE_COLOR_LUT, MIL.M_ENABLE);

            // Match with target image.
            MIL.McolMatch(MatchContext, SourceChild, MIL.M_DEFAULT, MIL.M_NULL, MatchResult, MIL.M_DEFAULT);

            // Retrieve and display results.
            Console.Write("Each pixel of the mixture is matched " +
                      "with one of the color samples.\n");
            Console.Write("\nColor segmentation results:\n");
            Console.Write("---------------------------\n");

            for (SampleIndex = 0; SampleIndex < NUM_SAMPLES; SampleIndex++)
            {
                MIL.McolGetResult(MatchResult, MIL.M_DEFAULT, MIL.M_SAMPLE_INDEX((int)SampleIndex), MIL.M_SCORE, ref MatchScore);
                SpacesIndex = 6 - SampleNames[SampleIndex].Length;
                Console.Write("Ratio of {0}{1} sample = {2,5:0.00}%\n", SampleNames[SampleIndex], Spaces[SpacesIndex], MatchScore);
            }
            Console.Write("\nResults reveal the low proportion of Blue candy.\n");

            // Draw the colored label image in the destination child.
            MIL.McolDraw(MIL.M_DEFAULT, MatchResult, DestChild, MIL.M_DRAW_PIXEL_MATCH_USING_COLOR, MIL.M_ALL, MIL.M_ALL, MIL.M_DEFAULT);

            // Pause to show the result image.
            Console.Write("\nPress <Enter> to end.\n\n");
            Console.ReadKey();

            // Free all allocations.
            MIL.MbufFree(DestChild);
            MIL.MbufFree(SourceChild);
            MIL.MbufFree(DisplayImage);
            MIL.McolFree(MatchResult);
            MIL.McolFree(MatchContext);
        }

        //****************************************************************************
        // Color matching in labeled regions.
        //****************************************************************************
        // Image filenames
        const string FUSE_SAMPLES_IMAGE = MIL.M_IMAGE_PATH + "FuseSamples.mim";
        const string FUSE_TARGET_IMAGE  = MIL.M_IMAGE_PATH + "Fuse.mim";

        // Model Finder context filename
        const string FINDER_CONTEXT = MIL.M_IMAGE_PATH + "FuseModel.mmf";

        // Number of fuse sample objects
        const int NUM_FUSES = 4;

        // Draw spacing and offset
        const int FUSE_SAMPLES_XSPACING = 40;
        const int FUSE_SAMPLES_YOFFSET  = 145;

        static void ColorMatchingExample(MIL_ID MilSystem, MIL_ID MilDisplay)
        {
            MIL_ID DisplayImage       = MIL.M_NULL;    // Display image buffer identifier.
            MIL_ID SourceChild        = MIL.M_NULL;    // Source image buffer identifier.
            MIL_ID DestChild          = MIL.M_NULL;    // Dest image buffer identifier.
            MIL_ID ColMatchContext    = MIL.M_NULL;    // Color matching context identifier.
            MIL_ID ColMatchResult     = MIL.M_NULL;    // Color matching result identifier.
            MIL_ID ModelImage         = MIL.M_NULL;    // Model image buffer identifier.
            MIL_ID AreaImage          = MIL.M_NULL;    // Area  image buffer identifier.
            MIL_ID OverlayID          = MIL.M_NULL;    // Overlay image buffer identifier.
            MIL_ID OverlaySourceChild = MIL.M_NULL;    // Overlay source child identifier.
            MIL_ID OverlayDestChild   = MIL.M_NULL;    // Overlay dest child identifier.
            MIL_ID FuseFinderCtx      = MIL.M_NULL;    // Model finder context identifier.
            MIL_ID FuseFinderRes      = MIL.M_NULL;    // Model finder result identifier.

            // Image sizes
            MIL_INT SizeX = 0;
            MIL_INT SizeY = 0;

            // Color sample names
            string[] SampleNames    =    { "Green", " Blue", " Red", "Yellow" };

            // Sample ROIs coordinates: OffsetX, OffsetY, SizeX, SizeY
            MIL_INT[,] SampleROIs   = new MIL_INT[,]{{ 54, 139, 28, 14},
                                               {172, 137, 30, 23},
                                               {296, 135, 31, 23},
                                               {417, 134, 27, 22}};

            // Array of match sample colors.
            MIL_INT[,] SampleMatchColor = new MIL_INT[NUM_FUSES, 3];

            Console.Write("\nCOLOR IDENTIFICATION:\n");
            Console.Write("---------------------\n");

            // Allocate the parent display image.
            MIL.MbufDiskInquire(FUSE_TARGET_IMAGE, MIL.M_SIZE_X, ref SizeX);
            MIL.MbufDiskInquire(FUSE_TARGET_IMAGE, MIL.M_SIZE_Y, ref SizeY);
            MIL.MbufAllocColor(MilSystem, 3, 2 * SizeX + DISPLAY_CENTER_MARGIN_X, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_DISP + MIL.M_PROC, ref DisplayImage);
            MIL.MbufClear(DisplayImage, MIL.M_COLOR_BLACK);

            // Allocate the model, area and label images.
            MIL.MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP, ref ModelImage);
            MIL.MbufAlloc2d(MilSystem, SizeX, SizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP, ref AreaImage);

            // Create a source and destination child in the display image.
            MIL.MbufChild2d(DisplayImage, 0, 0, SizeX, SizeY, ref SourceChild);
            MIL.MbufChild2d(DisplayImage, SizeX + DISPLAY_CENTER_MARGIN_X, 0, SizeX, SizeY, ref DestChild);

            // Load the sample source image.
            MIL.MbufLoad(FUSE_SAMPLES_IMAGE, SourceChild);

            // Display the image buffer.
            MIL.MdispSelect(MilDisplay, DisplayImage);

            // Prepare the overlay.
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY, MIL.M_ENABLE);
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY_CLEAR, MIL.M_DEFAULT);
            MIL.MdispInquire(MilDisplay, MIL.M_OVERLAY_ID, ref OverlayID);
            MIL.MbufChild2d(OverlayID, 0, 0, SizeX, SizeY, ref OverlaySourceChild);
            MIL.MbufChild2d(OverlayID, SizeX + DISPLAY_CENTER_MARGIN_X, 0, SizeX, SizeY, ref OverlayDestChild);

            // Prepare the model finder context and result.
            MIL.MmodRestore(FINDER_CONTEXT, MilSystem, MIL.M_DEFAULT, ref FuseFinderCtx);
            MIL.MmodPreprocess(FuseFinderCtx, MIL.M_DEFAULT);
            MIL.MmodAllocResult(MilSystem, MIL.M_DEFAULT, ref FuseFinderRes);

            // Allocate a color match context and result.
            MIL.McolAlloc(MilSystem, MIL.M_COLOR_MATCHING, MIL.M_RGB, MIL.M_DEFAULT, MIL.M_DEFAULT, ref ColMatchContext);
            MIL.McolAllocResult(MilSystem, MIL.M_COLOR_MATCHING_RESULT, MIL.M_DEFAULT, ref ColMatchResult);

            // Define the color samples in the context.
            for (int i = 0; i < NUM_FUSES; i++)
            {
                MIL.McolDefine(ColMatchContext, SourceChild, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_IMAGE, (double)SampleROIs[i, 0], (double)SampleROIs[i, 1], (double)SampleROIs[i, 2], (double)SampleROIs[i, 3]);
            }

            // Preprocess the context.
            MIL.McolPreprocess(ColMatchContext, MIL.M_DEFAULT);

            // Fill the samples colors array.
            for (int i = 0; i < NUM_FUSES; i++)
            {
                MIL.McolInquire(ColMatchContext, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_MATCH_SAMPLE_COLOR_BAND_0 + MIL.M_TYPE_MIL_INT, ref SampleMatchColor[i, 0]);
                MIL.McolInquire(ColMatchContext, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_MATCH_SAMPLE_COLOR_BAND_1 + MIL.M_TYPE_MIL_INT, ref SampleMatchColor[i, 1]);
                MIL.McolInquire(ColMatchContext, MIL.M_SAMPLE_LABEL(i + 1), MIL.M_MATCH_SAMPLE_COLOR_BAND_2 + MIL.M_TYPE_MIL_INT, ref SampleMatchColor[i, 2]);
            }

            // Draw the color samples.
            DrawSampleColors(DestChild, SampleMatchColor, SampleNames, NUM_FUSES, FUSE_SAMPLES_XSPACING, FUSE_SAMPLES_YOFFSET);

            // Draw the sample ROIs in the source image overlay.
            MIL.MgraColor(MIL.M_DEFAULT, MIL.M_COLOR_RED);
            for (MIL_INT SampleIndex = 0; SampleIndex < NUM_FUSES; SampleIndex++)
            {
                MIL_INT XEnd = SampleROIs[SampleIndex, 0] + SampleROIs[SampleIndex, 2] - 1;
                MIL_INT YEnd = SampleROIs[SampleIndex, 1] + SampleROIs[SampleIndex, 3] - 1;
                MIL.MgraRect(MIL.M_DEFAULT, OverlaySourceChild, SampleROIs[SampleIndex, 0], SampleROIs[SampleIndex, 1], XEnd, YEnd);
            }

            // Pause to show the source image.
            Console.Write("Colors are defined using one color sample region per fuse.\n");
            Console.Write("Press <Enter> to process the target image.\n");
            Console.ReadKey();

            // Clear the overlay.
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY_CLEAR, MIL.M_DEFAULT);

            // Disable the display update.
            MIL.MdispControl(MilDisplay, MIL.M_UPDATE, MIL.M_DISABLE);

            // Load the target image into the source child.
            MIL.MbufLoad(FUSE_TARGET_IMAGE, SourceChild);

            // Get the grayscale model image and copy it into the display dest child.
            MIL.MimConvert(SourceChild, ModelImage, MIL.M_RGB_TO_L);
            MIL.MbufCopy(ModelImage, DestChild);

            // Find the Model.
            MIL.MmodFind(FuseFinderCtx, ModelImage, FuseFinderRes);

            // Draw the blob image: labeled circular areas centered at each found fuse occurrence.
            MIL_INT Number = 0;
            MIL.MmodGetResult(FuseFinderRes, MIL.M_DEFAULT, MIL.M_NUMBER + MIL.M_TYPE_MIL_INT, ref Number);
            MIL.MbufClear(AreaImage, 0);
            for (MIL_INT ii = 0; ii < Number; ii++)
            {
                double X = 0.0;
                double Y = 0.0;

                // Get the position
                MIL.MmodGetResult(FuseFinderRes, ii, MIL.M_POSITION_X, ref X);
                MIL.MmodGetResult(FuseFinderRes, ii, MIL.M_POSITION_Y, ref Y);

                // Set the label color
                MIL.MgraColor(MIL.M_DEFAULT, (double)ii + 1);

                // Draw the filled circle
                MIL.MgraArcFill(MIL.M_DEFAULT, AreaImage, X, Y, 20, 20, 0, 360);
            }

            // Enable controls to draw the labeled color image.
            MIL.McolControl(ColMatchContext, MIL.M_CONTEXT, MIL.M_SAVE_AREA_IMAGE, MIL.M_ENABLE);
            MIL.McolControl(ColMatchContext, MIL.M_CONTEXT, MIL.M_GENERATE_SAMPLE_COLOR_LUT, MIL.M_ENABLE);

            // Perform the color matching.
            MIL.McolMatch(ColMatchContext, SourceChild, MIL.M_DEFAULT, AreaImage, ColMatchResult, MIL.M_DEFAULT);

            // Draw the label image into the overlay child.
            MIL.McolDraw(MIL.M_DEFAULT, ColMatchResult, OverlayDestChild, MIL.M_DRAW_AREA_MATCH_USING_COLOR, MIL.M_ALL, MIL.M_ALL, MIL.M_DEFAULT);

            // Draw the model position over the colored areas.
            MIL.MgraColor(MIL.M_DEFAULT, MIL.M_COLOR_BLUE);
            MIL.MmodDraw(MIL.M_DEFAULT, FuseFinderRes, OverlayDestChild, MIL.M_DRAW_BOX + MIL.M_DRAW_POSITION, MIL.M_ALL, MIL.M_DEFAULT);

            // Enable the display update.
            MIL.MdispControl(MilDisplay, MIL.M_UPDATE, MIL.M_ENABLE);

            // Pause to show the resulting image.
            Console.Write("\nFuses are located using the Model Finder tool.\n");
            Console.Write("The color of each target area is identified.\n");
            Console.Write("Press <Enter> to end.\n\n");
            Console.ReadKey();

            // Free all allocations.
            MIL.MmodFree(FuseFinderRes);
            MIL.MmodFree(FuseFinderCtx);
            MIL.MbufFree(AreaImage);
            MIL.MbufFree(ModelImage);
            MIL.MbufFree(SourceChild);
            MIL.MbufFree(DestChild);
            MIL.MbufFree(OverlaySourceChild);
            MIL.MbufFree(OverlayDestChild);
            MIL.MbufFree(DisplayImage);
            MIL.McolFree(ColMatchContext);
            MIL.McolFree(ColMatchResult);
        }

        //****************************************************************************
        // Perform color separation of colored inks on a piece of paper.
        //****************************************************************************
        // Source image
        const string WRITING_IMAGE_FILE = MIL.M_IMAGE_PATH + "stamp.mim";

        // Color triplets
        static readonly MIL_INT[] BACKGROUND_COLOR ={ 245, 234, 206 };
        static readonly MIL_INT[] WRITING_COLOR    ={ 141, 174, 174 };
        static readonly MIL_INT[] STAMP_COLOR      = { 226, 150, 118 };

        // Drawing spacing
        const int PATCHES_XSPACING = 70;

        static void ColorSeparationExample(MIL_ID MilSystem, MIL_ID MilDisplay)
        {
            MIL_ID DisplayImage     = MIL.M_NULL;    // Display image buffer identifier.
            MIL_ID SourceChild      = MIL.M_NULL;    // Source image buffer identifier.
            MIL_ID DestChild        = MIL.M_NULL;    // Destination image buffer identifier.
            MIL_ID Child            = MIL.M_NULL;    // Child buffer identifier.
            MIL_ID ColorsArray      = MIL.M_NULL;    // Array buffer identifier.

            // Source image sizes.
            MIL_INT SourceSizeX = 0;
            MIL_INT SourceSizeY = 0;

            // Color samples' names
            string[] ColorNames = { "BACKGROUND", "WRITING", "STAMP" };

            // Array with color patches to draw.
            MIL_INT[,] Colors = new MIL_INT[,] { { 245, 234, 206 }, { 141, 174, 174 }, { 226, 150, 118 } };

            // Samples' color coordinates
            byte[] BackgroundColor = new byte[3] { 245, 234, 206 };
            byte[] SelectedColor   = new byte[3] { 141, 174, 174 };
            byte[] RejectedColor   = new byte[3] { 226, 150, 118 };

            Console.Write("\nCOLOR SEPARATION:\n");
            Console.Write("-----------------\n");

            // Allocate an array buffer and fill it with the color coordinates.
            MIL.MbufAlloc2d(MilSystem, 3, 3, 8 + MIL.M_UNSIGNED, MIL.M_ARRAY, ref ColorsArray);
            MIL.MbufPut2d(ColorsArray, 0, 0, 3, 1, BackgroundColor);
            MIL.MbufPut2d(ColorsArray, 0, 1, 3, 1, SelectedColor);
            MIL.MbufPut2d(ColorsArray, 0, 2, 3, 1, RejectedColor);

            // Allocate the parent display image.
            MIL.MbufDiskInquire(WRITING_IMAGE_FILE, MIL.M_SIZE_X, ref SourceSizeX);
            MIL.MbufDiskInquire(WRITING_IMAGE_FILE, MIL.M_SIZE_Y, ref SourceSizeY);
            MIL.MbufAllocColor(MilSystem, 3, 2 * SourceSizeX + DISPLAY_CENTER_MARGIN_X, SourceSizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_DISP + MIL.M_PROC, ref DisplayImage);
            MIL.MbufClear(DisplayImage, MIL.M_COLOR_BLACK);

            // Clear the overlay.
            MIL.MdispControl(MilDisplay, MIL.M_OVERLAY_CLEAR, MIL.M_DEFAULT);

            // Create a source and dest child in the display image
            MIL.MbufChild2d(DisplayImage, 0, 0, SourceSizeX, SourceSizeY, ref SourceChild);
            MIL.MbufChild2d(DisplayImage, SourceSizeX + DISPLAY_CENTER_MARGIN_X, 0, SourceSizeX, SourceSizeY, ref DestChild);

            // Load the source image into the display image source child.
            MIL.MbufLoad(WRITING_IMAGE_FILE, SourceChild);

            // Draw the color patches.
            DrawSampleColors(DestChild, Colors, ColorNames, 3, PATCHES_XSPACING, -1);

            // Display the image.
            MIL.MdispSelect(MilDisplay, DisplayImage);

            // Pause to show the source image and color patches.
            Console.Write("The writing will be separated from the stamp using the following triplets:\n");
            Console.Write("the background color: beige [{0}, {1}, {2}],\n", BackgroundColor[0], BackgroundColor[1], BackgroundColor[2]);
            Console.Write("the writing color   : green [{0}, {1}, {2}],\n", SelectedColor[0], SelectedColor[1], SelectedColor[2]);
            Console.Write("the stamp color     : red   [{0}, {1}, {2}].\n\n", RejectedColor[0], RejectedColor[1], RejectedColor[2]);
            Console.Write("Press <Enter> to extract the writing.\n\n");
            Console.ReadKey();

            // Perform the color projection.
            MIL.McolProject(SourceChild, ColorsArray, DestChild, MIL.M_NULL, MIL.M_COLOR_SEPARATION, MIL.M_DEFAULT, MIL.M_NULL);

            // Wait for a key.
            Console.Write("Press <Enter> to extract the stamp.\n\n");
            Console.ReadKey();

            // Switch the order of the selected vs rejected colors in the color array.
            MIL.MbufPut2d(ColorsArray, 0, 2, 3, 1, SelectedColor);
            MIL.MbufPut2d(ColorsArray, 0, 1, 3, 1, RejectedColor);

            // Perform the color projection.
            MIL.McolProject(SourceChild, ColorsArray, DestChild, MIL.M_NULL, MIL.M_COLOR_SEPARATION, MIL.M_DEFAULT, MIL.M_NULL);

            // Wait for a key.
            Console.Write("Press <Enter> to end.\n\n");
            Console.ReadKey();

            // Free all allocations.
            MIL.MbufFree(ColorsArray);
            MIL.MbufFree(SourceChild);
            MIL.MbufFree(DestChild);
            MIL.MbufFree(DisplayImage);
        }

        //*****************************************************************************
        // Perform RGB to grayscale conversion
        //*****************************************************************************/
        // Source image
        const string RGB_IMAGE_FILE = MIL.M_IMAGE_PATH + "BinderClip.mim";

        static void RGBtoGrayscaleExample(MIL_ID MilSystem, MIL_ID MilDisplay)
            {
            Console.Write("\nCONVERSION FROM RGB TO GRAYSCALE:\n");
            Console.Write("---------------------------------\n");
            Console.Write("The example compares principal component projection to ");
            Console.Write("luminance based\nconversion.\n\n");

            MIL_ID DisplayImage     = MIL.M_NULL,     // Display image buffer identifier.
                   SourceChild      = MIL.M_NULL,     // Source image child buffer identifier.
                   LuminanceChild   = MIL.M_NULL,     // Luminance image child buffer identifier.
                   PCPchild         = MIL.M_NULL,     // PCP image child buffer identifier.      
                   LuminanceResult  = MIL.M_NULL,     // Luminance result identifier.
                   PCPresult        = MIL.M_NULL,     // PCP result identifier.            
                   Mask             = MIL.M_NULL,     // Mask identifier to perform PCP. 
                   ChildMask        = MIL.M_NULL;     // Child mask identifier to perform PCP. 

            // Inquire size and type of the source image.
            MIL_INT SourceSizeX = MIL.MbufDiskInquire(RGB_IMAGE_FILE, MIL.M_SIZE_X, MIL.M_NULL);
            MIL_INT SourceSizeY = MIL.MbufDiskInquire(RGB_IMAGE_FILE, MIL.M_SIZE_Y, MIL.M_NULL);
            MIL_INT Type        = MIL.MbufDiskInquire(RGB_IMAGE_FILE, MIL.M_TYPE, MIL.M_NULL);

            // Allocate buffer to display the input image and the results.
            MIL.MbufAllocColor(MilSystem, 3, SourceSizeX,
                               3 * SourceSizeY + 2 * DISPLAY_CENTER_MARGIN_Y, Type,
                               MIL.M_IMAGE + MIL.M_PROC + MIL.M_DISP, ref DisplayImage);
            MIL.MbufClear(DisplayImage, 0);

            // Create a source child in the display image.
            MIL.MbufChildColor2d(DisplayImage, MIL.M_ALL_BANDS, 0, 0, SourceSizeX, SourceSizeY,
                                 ref SourceChild);

            // Create a destination child in the display image for luminance based conversion.
            MIL.MbufChildColor2d(DisplayImage, MIL.M_ALL_BANDS, 0,
                                 SourceSizeY + DISPLAY_CENTER_MARGIN_Y, SourceSizeX,
                                 SourceSizeY, ref LuminanceChild);

            // Create a destination child in the display image for principal component projection.
            MIL.MbufChildColor2d(DisplayImage, MIL.M_ALL_BANDS, 0,
                                 2 * SourceSizeY + 2 * DISPLAY_CENTER_MARGIN_Y, SourceSizeX,
                                 SourceSizeY, ref PCPchild);

            // Load the source image into the display image source child.
            MIL.MbufLoad(RGB_IMAGE_FILE, SourceChild);
            MIL.MdispSelect(MilDisplay, DisplayImage);

            // Extract luminance channel from source image and copy the result in the display image.
            MIL.MbufAlloc2d(MilSystem, SourceSizeX, SourceSizeY, Type, MIL.M_IMAGE + MIL.M_PROC, ref LuminanceResult);
            MIL.MimConvert(SourceChild, LuminanceResult, MIL.M_RGB_TO_L);
            MIL.MbufCopy(LuminanceResult, LuminanceChild);

            Console.Write("Color image converted to grayscale using luminance based ");
            Console.Write("conversion\ntechnique.\n\n");
            Console.Write("Press <Enter> to convert the image using principal ");
            Console.Write("component projection\ntechnique. \n\n");
            Console.ReadKey();

            // Create a mask to identify important colors in the source image.
            MIL.MbufAlloc2d(MIL.M_DEFAULT_HOST, SourceSizeX, SourceSizeY, 8 + MIL.M_UNSIGNED, MIL.M_IMAGE + MIL.M_PROC,
                            ref Mask);
            MIL.MbufClear(Mask, 0);

            // Define regions of interest (pixel colors with which to perform the color projection).
            MIL_INT OffsetX   = 105,
                    OffsetY   = 45,
                    MaskSizeX = 60,
                    MaskSizeY = 20;
            MIL.MbufChild2d(Mask, OffsetX, OffsetY, MaskSizeX, MaskSizeY, ref ChildMask);
            MIL.MbufClear(ChildMask, MIL.M_SOURCE_LABEL);
            MIL.MbufFree(ChildMask);

            OffsetX = 220;
            MIL.MbufChild2d(Mask, OffsetX, OffsetY, MaskSizeX, MaskSizeY, ref ChildMask);
            MIL.MbufClear(ChildMask, MIL.M_SOURCE_LABEL);
            MIL.MbufFree(ChildMask);

            OffsetX = 335;
            MIL.MbufChild2d(Mask, OffsetX, OffsetY, MaskSizeX, MaskSizeY, ref ChildMask);
            MIL.MbufClear(ChildMask, MIL.M_SOURCE_LABEL);

            // Perform principal component projection and copy the result in the display image.
            MIL.MbufAlloc2d(MilSystem, SourceSizeX, SourceSizeY, Type, MIL.M_IMAGE + MIL.M_PROC, ref PCPresult);
            MIL.McolProject(SourceChild, Mask, PCPresult, MIL.M_NULL,
                            MIL.M_PRINCIPAL_COMPONENT_PROJECTION, MIL.M_DEFAULT, MIL.M_NULL);
            MIL.MbufCopy(PCPresult, PCPchild);

            Console.Write("Color image converted to grayscale using principal ");
            Console.Write("component projection\ntechnique.\n");
            Console.Write("Press <Enter> to end.\n\n");
            Console.ReadKey();

            // Free all allocations.
            MIL.MbufFree(ChildMask);
            MIL.MbufFree(Mask);
            MIL.MbufFree(PCPresult);
            MIL.MbufFree(LuminanceResult);
            MIL.MbufFree(PCPchild);
            MIL.MbufFree(LuminanceChild);
            MIL.MbufFree(SourceChild);
            MIL.MbufFree(DisplayImage);
            }

        //****************************************************************************
        // Draw the samples as color patches.
        static void DrawSampleColors(MIL_ID DestImage, MIL_INT[,] pSamplesColors, string[] pSampleNames, MIL_INT NumSamples, MIL_INT XSpacing, MIL_INT YOffset)
        {
            MIL_INT DestSizeX = MIL.MbufInquire(DestImage, MIL.M_SIZE_X, MIL.M_NULL);
            MIL_INT DestSizeY = MIL.MbufInquire(DestImage, MIL.M_SIZE_Y, MIL.M_NULL);
            double OffsetX = (DestSizeX - (NumSamples * COLOR_PATCH_SIZEX) - ((NumSamples - 1) * XSpacing)) / 2.0;
            double OffsetY = YOffset > 0 ? YOffset : (DestSizeY - COLOR_PATCH_SIZEY) / 2.0;
            double TextOffsetX;
            MIL.MgraFont(MIL.M_DEFAULT, MIL.M_FONT_DEFAULT_SMALL);

            for (MIL_INT SampleIndex = 0; SampleIndex < NumSamples; SampleIndex++)
            {
                MIL.MgraColor(MIL.M_DEFAULT, MIL.M_RGB888((int)pSamplesColors[SampleIndex, 0], (int)pSamplesColors[SampleIndex, 1], (int)pSamplesColors[SampleIndex, 2]));
                MIL.MgraRectFill(MIL.M_DEFAULT, DestImage, OffsetX, OffsetY, OffsetX + COLOR_PATCH_SIZEX, OffsetY + COLOR_PATCH_SIZEY);
                MIL.MgraColor(MIL.M_DEFAULT, MIL.M_COLOR_YELLOW);
                TextOffsetX = OffsetX + COLOR_PATCH_SIZEX / 2.0 - 4.0 * pSampleNames[SampleIndex].Length + 0.5;
                MIL.MgraText(MIL.M_DEFAULT, DestImage, TextOffsetX, OffsetY - 20, pSampleNames[SampleIndex]);
                OffsetX += (COLOR_PATCH_SIZEX + XSpacing);
            }
        }

    }
}