Click here to show toolbars of the Web Online Help System: show toolbars |
/************************************************************************************/ /* * File name: BaslerToFExample.cpp * Location: See Matrox Example Launcher in the MIL Control Center * * * Synopsis: This file contains the implementation of the general CBaslerToFExample class that * manages the example as well as the derived CBaslerToFImageExample and * CBaslerToFPointCloudExample. * * Copyright (C) Matrox Electronic Systems Ltd., 1992-2016. * All Rights Reserved */ #include <mil.h> #include <math.h> #include <vector> using std::vector; #include "BoxLocator.h" #include "DepthMapGenerator.h" #include "RangeValidator.h" #include "BaslerToFExample.h" #include "RangeToPointCloudConverter.h" //***************************************************************************** // Constants. //***************************************************************************** // The calibration grid parameters constants. static const MIL_INT GRID_TYPE = M_CIRCLE_GRID; static const MIL_INT GRID_NB_ROWS = 11; static const MIL_INT GRID_NB_COLUMNS = 15; static const MIL_DOUBLE GRID_ROW_SPACING = 300.5 / 6.0; // in mm static const MIL_DOUBLE GRID_COL_SPACING = 300.5 / 6.0; // in mm // The scale and offset of the range image. static const MIL_DOUBLE BASLER_TOF_WORKING_RANGE = 6663.0; // in mm static const MIL_DOUBLE RANGE_SCALE = BASLER_TOF_WORKING_RANGE / 65535; // in mm/gray static const MIL_DOUBLE RANGE_OFFSET = -18; // in mm // The validation parameters of the range image. static const MIL_INT DEPTH_THIRD_DERIV_FACTOR = 16; static const MIL_INT VALIDATOR_MORPH_ITER = 1; // Determine the size Z range of the box. static const MIL_DOUBLE MAX_BOX_SIZE_Z = 300; // in mm static const MIL_DOUBLE MIN_BOX_SIZE_Z = 50; // in mm // Parameters to find the rough rectangle. static const MIL_DOUBLE TOP_REFINE_RANGE = 25; // in mm static const MIL_DOUBLE MIN_RECTANGULARITY_ROUGH = 0.40; static const MIL_DOUBLE MIN_RECTANGULARITY_REFINE = 0.75; static const MIL_DOUBLE MIN_FERET_MIN = 75; // in mm // Depth map generation parameters. static const MIL_INT SMOOTH_KERNEL_SIZE = 7; static const MIL_DOUBLE DEPTH_MAP_PIXEL_SIZE = 2.5; // in mm/pixel // The display text line offset. static const MIL_INT DISPLAY_TEXT_LINE_OFFSET = 16; // Offsets of the windows. static const MIL_INT WINDOWS_OFFSET_X = 15; static const MIL_INT WINDOWS_OFFSET_Y = 38; // The BaslerToF camera NaN value casted to MIL_UINT32. static const MIL_UINT32 BASLER_NAN_UINT32 = 2143289344; // The standalone payload size of the camera. static const MIL_INT DEFAULT_PAYLOAD_SIZE = 6451200; static const MIL_DOUBLE DIV_180_PI = 57.295779513082320866997945294156; //***************************************************************************** // CBaslerToFExample class implementation. //***************************************************************************** //***************************************************************************** // Constructor. //***************************************************************************** CBaslerToFExample::CBaslerToFExample(MIL_ID MilSystem, MIL_ID MilDig) : m_MilSystem(MilSystem), m_pBoxLocator(NULL), m_pDepthMapGenerator(NULL), m_DepthDisplay(MilSystem, MIL_TEXT("Depth")), m_IntensityDisplay(MilSystem, MIL_TEXT("Intensity")), m_RangeDisplay(MilSystem, MIL_TEXT("Range")) { // Set whether the example uses the real camera. // // WARNING! This example was validated with an engineering sample camera and API. // The release camera and API may require an update to this example. // m_IsRealCamera = MsysInquire(MilSystem, M_SYSTEM_TYPE, M_NULL) != M_SYSTEM_HOST_TYPE; m_MilGrabImages[0] = M_NULL; m_MilGrabImages[1] = M_NULL; // Set the Basler ToF camera to grab the intensity image if using a real camera. EnableDisableComponent(MilDig, MIL_TEXT("Confidence"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilDig, MIL_TEXT("Range"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilDig, MIL_TEXT("Intensity"), MIL_TEXT("Mono16"), true); // Get the information of the image output by the camera. MdigInquire(MilDig, M_SIZE_X, &m_ImageSizeX); MdigInquire(MilDig, M_SIZE_Y, &m_ImageSizeY); // Allocate a point cloud container. M3dmapAllocResult(MilSystem, M_POINT_CLOUD_CONTAINER, M_DEFAULT, &m_MilPointCloud); // Set the depth display LUT. m_DepthDisplay.SetDisplayLut(MIL_UINT16_MAX, 1, MIL_UINT16_MAX); } //***************************************************************************** // Destructor. //***************************************************************************** CBaslerToFExample::~CBaslerToFExample() { M3dmapFree(m_MilPointCloud); if (m_MilGrabImages[0] != M_NULL) MbufFree(m_MilGrabImages[0]); if (m_MilGrabImages[1] != M_NULL) MbufFree(m_MilGrabImages[1]); } //***************************************************************************** // AllocateGrabImages. Allocates the images used for the grab. //***************************************************************************** void CBaslerToFExample::AllocateGrabImages(MIL_INT GrabImageSizeX, MIL_INT GrabImageSizeY, MIL_INT GrabImageType) { MbufAlloc2d(m_MilSystem, GrabImageSizeX, GrabImageSizeY, GrabImageType, M_IMAGE + M_GRAB, &m_MilGrabImages[0]); MbufAlloc2d(m_MilSystem, GrabImageSizeX, GrabImageSizeY, GrabImageType, M_IMAGE + M_GRAB, &m_MilGrabImages[1]); } //***************************************************************************** // EnableDisableComponent. Enable and disable an image component of the camera. //***************************************************************************** void CBaslerToFExample::EnableDisableComponent(MIL_ID MilDig, MIL_CONST_TEXT_PTR Component, MIL_CONST_TEXT_PTR PixelFormat, bool Enable) { if (m_IsRealCamera) { MdigControlFeature(MilDig, M_FEATURE_VALUE, MIL_TEXT("ImageComponentSelector"), M_TYPE_STRING, Component); MdigControlFeature(MilDig, M_FEATURE_VALUE, MIL_TEXT("ImageComponentEnable"), M_TYPE_STRING, Enable ? MIL_TEXT("1") : MIL_TEXT("0")); MdigControlFeature(MilDig, M_FEATURE_VALUE, MIL_TEXT("PixelFormat"), M_TYPE_STRING, PixelFormat); } } //***************************************************************************** // LocateBoxes. Starts continuous processing of range data to locate a box. //***************************************************************************** void CBaslerToFExample::LocateBoxes(MIL_ID MilBoxDig) { // Allocate the box locator. m_pBoxLocator = new CBoxLocator(m_pDepthMapGenerator->MilDepthMap(), MIN_FERET_MIN); // Locate boxes. Print the expected box parameters. MdigProcess(MilBoxDig, m_MilGrabImages, 2, M_START, M_DEFAULT, LocateBoxHook, this); MosPrintf(MIL_TEXT("Please place a box on the reference plane.\n\n") MIL_TEXT("EXPECTED BOX PARAMETERS:\n") MIL_TEXT("------------------------\n") MIL_TEXT("Box height range: [%.2f, %.2f] mm\n") MIL_TEXT("Min top surface dimension: %.2f mm\n") MIL_TEXT("Min top surface area: %.2f mm\n") MIL_TEXT("Min top rectangularity (SurfaceArea/BoundingRectangleArea): %.2f \n\n") MIL_TEXT("The range data is transformed into a MIL 3d point cloud.\n") MIL_TEXT("The depth map, extracted from the 3d point cloud, is displayed.\n") MIL_TEXT("The extraction box used is above and parallel to the reference plane.\n") MIL_TEXT("The box is located and measured using the depth map information.\n\n") MIL_TEXT("Press <Enter> to stop.\n\n"), MIN_BOX_SIZE_Z, MAX_BOX_SIZE_Z, m_pBoxLocator->MinFeretMin(), m_pBoxLocator->MinSurfaceArea(), MIN_RECTANGULARITY_REFINE); MosGetch(); // Stop the process. MdigProcess(MilBoxDig, m_MilGrabImages, 2, M_STOP + M_WAIT, M_DEFAULT, LocateBoxHook, this); delete m_pBoxLocator; } //***************************************************************************** // LocateBoxHook. Processing callback of the MdigProcess called by the LocateBoxes // function. //***************************************************************************** MIL_INT MFTYPE CBaslerToFExample::LocateBoxHook(MIL_INT HookType, MIL_ID MilHook, void* pHookData) { CBaslerToFExample* pExample = (CBaslerToFExample*)pHookData; return pExample->LocateBox(HookType, MilHook); } //***************************************************************************** // LocateBox. Processing function that locates a box from the range image. // If found, the box and its dimensions are drawn in the intensity display. // Here are the steps of the algorithm to locate a box: // // 1- The range data is retrieved from the camera. // For a range image source // -The data is transformed into a 3d point cloud using the calibration. // For a point cloud source // -The data is retrieved as is and put in a point cloud container. // 2- Based on the size of the grid or of the fitted plane data and the expected // height range of the box, a depth map is extracted from the 3d point cloud // from above the calibrated plane. // 3- Using the expected parameters of the top surface // (min dimension, area, and height sigma), the box is located using // a combination of blob and measurement: // - A rough box is found using blob in a large height range. // - The rough box is refined using blob in a tighter height range. // - The X and Y dimensions are further refined using measurement. // // See BoxLocator.cpp for more details on the blob and measurement usage. // //***************************************************************************** MIL_INT CBaslerToFExample::LocateBox(MIL_INT HookType, MIL_ID MilHook) { // Disable the display updates and clear the annotations. m_IntensityDisplay.Update(M_DISABLE, true); m_DepthDisplay.Update(M_DISABLE, true); m_RangeDisplay.Update(M_DISABLE); // Retrieve the MIL_ID of the grabbed buffer. MIL_ID ModifiedBufferId; MdigGetHookInfo(MilHook, M_MODIFIED_BUFFER + M_BUFFER_ID, &ModifiedBufferId); // Get the 3d data from the grab buffer. Get3dDataFromGrabBuffer(ModifiedBufferId); // Create the depth map. m_pDepthMapGenerator->CreateDepthMap(m_MilPointCloud, M_MAX); // Preprocess the depth map. m_pDepthMapGenerator->PreprocessDepthMap(SMOOTH_KERNEL_SIZE, MIN_FERET_MIN); // Find the rough location of the box in the processed depth map. SBox FoundBox = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; bool BoxIsFound = false; MgraColor(M_DEFAULT, M_COLOR_RED); if (m_pBoxLocator->FindRoughBox(m_pDepthMapGenerator->MilPreprocessedDepthMap(), MIN_BOX_SIZE_Z, MAX_BOX_SIZE_Z, MIN_RECTANGULARITY_ROUGH)) { // Refine the rough box. if (m_pBoxLocator->FindRoughBox(m_pDepthMapGenerator->MilPreprocessedDepthMap(), m_pBoxLocator->FoundBox().Height - TOP_REFINE_RANGE, m_pBoxLocator->FoundBox().Height + TOP_REFINE_RANGE, MIN_RECTANGULARITY_REFINE)) { // Refine the box. if (m_pBoxLocator->RefineBox(m_pDepthMapGenerator->MilPreprocessedDepthMap())) { // Draw the refine step annotations. MgraColor(M_DEFAULT, M_COLOR_CYAN); m_pBoxLocator->DrawDepthBox(M_DEFAULT, m_DepthDisplay.MilGraList()); MgraColor(M_DEFAULT, M_COLOR_MAGENTA); m_pBoxLocator->DrawDepthRefine(M_DEFAULT, m_DepthDisplay.MilGraList()); } else { MgraColor(M_DEFAULT, M_COLOR_CYAN); m_pBoxLocator->DrawDepthRoughBox(M_DEFAULT, m_DepthDisplay.MilGraList()); } // Set the found box. FoundBox = m_pBoxLocator->FoundBox(); BoxIsFound = true; } } // Display the results. DisplayResult(&FoundBox, BoxIsFound); // Print the results. if(BoxIsFound) MgraColor(M_DEFAULT, M_COLOR_CYAN); PrintResults(FoundBox, GetPrintDisplay()); m_RangeDisplay.Update(M_ENABLE); m_DepthDisplay.Update(M_ENABLE); m_IntensityDisplay.Update(M_ENABLE); return 0; } //***************************************************************************** // PrintResults. Print the results in console and in a display. //***************************************************************************** void CBaslerToFExample::PrintResults(const SBox& rFoundBox, const CDisplay& rPrintDisplay) { // Draw the calculated volume. MgraControl(M_DEFAULT, M_INPUT_UNITS, M_DISPLAY); MIL_DOUBLE BoxVolume = rFoundBox.Volume() / 1000.0; MIL_TEXT_CHAR Text[256]; MosSprintf(Text, 256, MIL_TEXT(" Box length: %8.1f cm "), rFoundBox.Length / 10.0); MgraText(M_DEFAULT, rPrintDisplay.MilGraList(), 0, 0, Text); MosSprintf(Text, 256, MIL_TEXT(" Box width: %8.1f cm "), rFoundBox.Width / 10.0); MgraText(M_DEFAULT, rPrintDisplay.MilGraList(), 0, DISPLAY_TEXT_LINE_OFFSET, Text); MosSprintf(Text, 256, MIL_TEXT(" Box height: %8.1f cm "), rFoundBox.Height / 10.0); MgraText(M_DEFAULT, rPrintDisplay.MilGraList(), 0, 2 * DISPLAY_TEXT_LINE_OFFSET, Text); MosSprintf(Text, 256, MIL_TEXT(" Box volume: %8.1f cm^3 "), BoxVolume); MgraText(M_DEFAULT, rPrintDisplay.MilGraList(), 0, 3 * DISPLAY_TEXT_LINE_OFFSET, Text); MgraControl(M_DEFAULT, M_INPUT_UNITS, M_PIXEL); } //***************************************************************************** // CBaslerToFImageExample class implementation. //***************************************************************************** //***************************************************************************** // Constructor. //***************************************************************************** CBaslerToFImageExample::CBaslerToFImageExample(MIL_ID MilSystem, MIL_ID MilGridDig, MIL_ID MilBoxDig) : CBaslerToFExample(MilSystem, MilGridDig), m_pRangeValidator(NULL) { // Set the Basler ToF camera to grab the intensity image if using a real camera. EnableDisableComponent(MilGridDig, MIL_TEXT("Confidence"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilGridDig, MIL_TEXT("Range"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilGridDig, MIL_TEXT("Intensity"), MIL_TEXT("Mono16"), true); // Get the information of the image output by the camera. MdigInquire(MilGridDig, M_SIZE_X, &m_ImageSizeX); MdigInquire(MilGridDig, M_SIZE_Y, &m_ImageSizeY); MIL_INT IntImageType = MdigInquire(MilGridDig, M_TYPE, M_NULL); // Allocate the grab buffer to hold up to 2 components. AllocateGrabImages(m_ImageSizeX, m_ImageSizeY * 2, IntImageType); // Allocate the intensity image. MbufAlloc2d(MilSystem, m_ImageSizeX, m_ImageSizeY, IntImageType, M_IMAGE + M_PROC + M_DISP, &m_MilIntImage); // Set the Basler ToF camera to grab the intensity and range component. EnableDisableComponent(MilBoxDig, MIL_TEXT("Confidence"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilBoxDig, MIL_TEXT("Intensity"), MIL_TEXT("Mono16"), true); EnableDisableComponent(MilBoxDig, MIL_TEXT("Range"), MIL_TEXT("Mono16"), true); // Allocate the range image. MIL_INT RangeImageType = MdigInquire(MilBoxDig, M_TYPE, M_NULL); MIL_INT RangeSizeBit = MdigInquire(MilBoxDig, M_SIZE_BIT, M_NULL); MbufAlloc2d(MilSystem, m_ImageSizeX, m_ImageSizeY, RangeImageType, M_IMAGE + M_PROC + M_DISP + M_GRAB, &m_MilRangeImage); // Allocate the calibration. McalAlloc(MilSystem, M_TSAI_BASED, M_DEFAULT, &m_MilCal); } //***************************************************************************** // Destructor. //***************************************************************************** CBaslerToFImageExample::~CBaslerToFImageExample() { McalFree(m_MilCal); MbufFree(m_MilIntImage); MbufFree(m_MilRangeImage); } //***************************************************************************** // CalibrateCamera. Calibrates the camera according to the constant parameters // defined by the application. //***************************************************************************** bool CBaslerToFImageExample::CalibrateCamera(MIL_ID MilGridDig) { // Select the intensity image. m_IntensityDisplay.Select(m_MilIntImage); // Put the display in bit shift mode. m_IntensityDisplay.SetDisplayBitShift(8); // Set the Basler ToF camera to grab the intensity image if using a real camera. EnableDisableComponent(MilGridDig, MIL_TEXT("Confidence"),MIL_TEXT("Mono16"), false); EnableDisableComponent(MilGridDig, MIL_TEXT("Range"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilGridDig, MIL_TEXT("Intensity"), MIL_TEXT("Mono16"), true); // Ask the user to place a calibration grid in front of the Basler ToF camera. MdigProcess(MilGridDig, m_MilGrabImages, 2, M_START, M_DEFAULT, GrabContinuousHook, this); MosPrintf(MIL_TEXT("Please place the calibration grid in front of the camera.\n\n")); if (m_IsRealCamera) MosPrintf(MIL_TEXT("EXPECTED GRID PARAMETERS:\n") MIL_TEXT("-------------------------\n") MIL_TEXT("GridType : %s\n") MIL_TEXT("Nb of rows : %d\n") MIL_TEXT("Nb of columns : %d\n") MIL_TEXT("Grid row spacing : %.2f\n") MIL_TEXT("Grid column spacing : %.2f\n\n") MIL_TEXT("Please modify the calibration grid parameters constants at the beginning\n") MIL_TEXT("of the example code according to your calibration grid.\n\n"), GRID_TYPE == M_CIRCLE_GRID ? MIL_TEXT("M_CIRCLE_GRID") : MIL_TEXT("M_CHESSBOARD_GRID"), GRID_NB_ROWS, GRID_NB_COLUMNS, GRID_ROW_SPACING, GRID_COL_SPACING); MosPrintf(MIL_TEXT("Press <Enter> to grab.\n\n")); MosGetch(); // Stop the continuous grab. MdigProcess(MilGridDig, m_MilGrabImages, 2, M_STOP + M_WAIT, M_DEFAULT, GrabContinuousHook, this); // Calibrate the camera. MIL_DOUBLE GridSizeX = (GRID_NB_COLUMNS - 1) * GRID_COL_SPACING; MIL_DOUBLE GridSizeY = (GRID_NB_ROWS - 1) * GRID_ROW_SPACING; MIL_DOUBLE GridWorldOffsetX = -GridSizeX / 2; MIL_DOUBLE GridWorldOffsetY = -GridSizeY / 2; McalGrid(m_MilCal, m_MilIntImage, GridWorldOffsetX, GridWorldOffsetY, 0.0, GRID_NB_ROWS, GRID_NB_COLUMNS, GRID_ROW_SPACING, GRID_COL_SPACING, M_DEFAULT, GRID_TYPE); if (McalInquire(m_MilCal, M_CALIBRATION_STATUS, M_NULL) == M_CALIBRATED) { // Display the calibration points and coordinate system. MgraColor(M_DEFAULT, M_COLOR_CYAN); McalDraw(M_DEFAULT, m_MilCal, m_IntensityDisplay.MilGraList(), M_DRAW_ABSOLUTE_COORDINATE_SYSTEM, M_DEFAULT, M_DEFAULT); MgraColor(M_DEFAULT, M_COLOR_GREEN); McalDraw(M_DEFAULT, m_MilCal, m_IntensityDisplay.MilGraList(), M_DRAW_WORLD_POINTS, M_DEFAULT, M_DEFAULT); MosPrintf(MIL_TEXT("The calibration of the camera is displayed.\n\n") MIL_TEXT("Press <Enter> to continue.\n\n")); MosGetch(); // Clear the graphic list. MgraClear(M_DEFAULT, m_IntensityDisplay.MilGraList()); // Put the relative coordinate system on the camera coordinate system. McalSetCoordinateSystem(m_MilCal, M_RELATIVE_COORDINATE_SYSTEM, M_CAMERA_COORDINATE_SYSTEM, M_IDENTITY, M_NULL, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT); // Set the LUT of the range display according to the calibration. MIL_DOUBLE GridPosX; MIL_DOUBLE GridPosY; MIL_DOUBLE GridPosZ; McalGetCoordinateSystem(m_MilCal, M_CAMERA_COORDINATE_SYSTEM, M_ABSOLUTE_COORDINATE_SYSTEM, M_TRANSLATION, M_NULL, &GridPosX, &GridPosY, &GridPosZ, M_NULL); MIL_DOUBLE GridDist = sqrt(GridPosX*GridPosX + GridPosY*GridPosY + GridPosZ*GridPosZ); MIL_INT MaxGray = (MIL_INT)(2 * GridDist / RANGE_SCALE - RANGE_OFFSET); MaxGray = MaxGray > (MIL_UINT16_MAX - 1) ? MIL_UINT16_MAX - 1 : MaxGray; m_RangeDisplay.SetDisplayLut(MIL_UINT16_MAX, 1, MaxGray); return true; } else { MosPrintf(MIL_TEXT("Calibration generated an exception.\n")); MosPrintf(MIL_TEXT("See User Guide to resolve the situation.\n\n")); return false; } } //***************************************************************************** // LocateBoxes. Starts continuous processing of range image to locate a box. //***************************************************************************** void CBaslerToFImageExample::LocateBoxes(MIL_ID MilBoxDig) { // Allocate the range validator and depth generator. m_pRangeValidator = new CRangeValidator(m_MilRangeImage); MIL_DOUBLE ExtendedGridSizeX = (GRID_NB_COLUMNS + 1) * GRID_COL_SPACING; MIL_DOUBLE ExtendedGridSizeY = (GRID_NB_ROWS + 1) * GRID_ROW_SPACING; m_pRangeToPointCloud = new CRangeToPointCloudConverter(m_MilCal, m_ImageSizeX, m_ImageSizeY); m_pDepthMapGenerator = new CDepthMapGenerator(m_MilSystem, DEPTH_MAP_PIXEL_SIZE, -ExtendedGridSizeX / 2, -ExtendedGridSizeY / 2, -MIN_BOX_SIZE_Z / 2, ExtendedGridSizeX, ExtendedGridSizeY, MAX_BOX_SIZE_Z - MIN_BOX_SIZE_Z / 2, MIN_FERET_MIN); // Set the position of the displays. m_RangeDisplay.SetInitialWindowPos(m_ImageSizeX + WINDOWS_OFFSET_X, 0); m_DepthDisplay.SetInitialWindowPos(m_ImageSizeX - (m_pDepthMapGenerator->DepthMapSizeX() - WINDOWS_OFFSET_X) / 2, m_ImageSizeY / 2 + WINDOWS_OFFSET_Y); // Select the range and depth images. m_RangeDisplay.Select(m_MilRangeImage); m_DepthDisplay.Select(m_pDepthMapGenerator->MilPreprocessedDepthMap()); // Set the BaslerToF camera to grab the intensity and range component. EnableDisableComponent(MilBoxDig, MIL_TEXT("Confidence"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilBoxDig, MIL_TEXT("Intensity"), MIL_TEXT("Mono16"), true); EnableDisableComponent(MilBoxDig, MIL_TEXT("Range"), MIL_TEXT("Mono16"), true); // Locate the boxes. CBaslerToFExample::LocateBoxes(MilBoxDig); delete m_pRangeValidator; delete m_pDepthMapGenerator; delete m_pRangeToPointCloud; } //***************************************************************************** // DisplayResult. Displays the found box. //***************************************************************************** void CBaslerToFImageExample::DisplayResult(const SBox* pFoundBox, bool BoxIsFound) { if(BoxIsFound) { // Draw the 3d box and a coordinate system on top of the box. MgraColor(M_DEFAULT, M_COLOR_CYAN); m_pBoxLocator->Draw3dBox(M_DEFAULT, m_MilCal, m_IntensityDisplay.MilGraList()); m_pBoxLocator->Draw3dBoxCoordinateSystem(M_DEFAULT, m_MilCal, m_IntensityDisplay.MilGraList()); } } //***************************************************************************** // GrabContinuousHook. Processing callback of MdigProcess to grab the intensity image. //***************************************************************************** MIL_INT MFTYPE CBaslerToFImageExample::GrabContinuousHook(MIL_INT HookType, MIL_ID MilHook, void* pHookData) { CBaslerToFImageExample* pExample = (CBaslerToFImageExample*)pHookData; return pExample->GrabContinuous(HookType, MilHook); } //***************************************************************************** // GrabContinuous. Processing function that copies the grab buffer to the // displayed buffer. //***************************************************************************** MIL_INT CBaslerToFImageExample::GrabContinuous(MIL_INT HookType, MIL_ID MilHook) { MIL_ID ModifiedBufferId; // Retrieve the MIL_ID of the grabbed buffer. MdigGetHookInfo(MilHook, M_MODIFIED_BUFFER + M_BUFFER_ID, &ModifiedBufferId); // Update the displayed image. MbufCopy(ModifiedBufferId, m_MilIntImage); return 0; } //***************************************************************************** // Get3dDataFromGrabBuffer. Gets the 3d data from the grabbed image. Preprocesses // the range image and creates the point cloud from the // result. //***************************************************************************** void CBaslerToFImageExample::Get3dDataFromGrabBuffer(MIL_ID MilModifiedBuffer) { // Get the range and intensity image. MbufCopy(MilModifiedBuffer, m_MilRangeImage); MbufCopyColor2d(MilModifiedBuffer, m_MilIntImage, 0, 0, m_ImageSizeY, 0, 0, 0, m_ImageSizeX, m_ImageSizeY); // Validate the range values of the range image. m_pRangeValidator->PreprocessRange(m_MilRangeImage, DEPTH_THIRD_DERIV_FACTOR, VALIDATOR_MORPH_ITER); // Set the corresponding pixels as invalid data in the original image. MbufClearCond(m_MilRangeImage, 0, 0, 0, m_pRangeValidator->MilRangeValidImage(), M_EQUAL, 0); // Generate the point cloud. m_pRangeToPointCloud->CreatePointCloud(m_MilRangeImage, m_MilPointCloud, m_MilCal, RANGE_OFFSET, RANGE_SCALE); } //***************************************************************************** // CBaslerToFPointCloudExample class implementation. //***************************************************************************** //***************************************************************************** // Constructor. //***************************************************************************** CBaslerToFPointCloudExample::CBaslerToFPointCloudExample(MIL_ID MilSystem, MIL_ID MilIntensityDig, MIL_ID MilPlanePointCloudDig) : CBaslerToFExample(MilSystem, MilIntensityDig) { // Allocate the grab buffer to hold the point cloud. MIL_INT PayloadSize = DEFAULT_PAYLOAD_SIZE; if(m_IsRealCamera) MdigInquireFeature(MilPlanePointCloudDig, M_FEATURE_VALUE, MIL_TEXT("PayloadSize"), M_TYPE_INT64, &PayloadSize); MIL_INT GrabImageSizeY = PayloadSize / m_ImageSizeX / 3 / 4; AllocateGrabImages(m_ImageSizeX * 3, GrabImageSizeY, 32 + M_FLOAT); // Allocate the plane geometry. M3dmapAlloc(MilSystem, M_GEOMETRY, M_DEFAULT, &m_Mil3dPlaneGeometry); // Allocate the camera depth map. MbufAlloc2d(MilSystem, m_ImageSizeX/2, m_ImageSizeY/2, 16 + M_UNSIGNED, M_IMAGE + M_PROC + M_DISP, &m_MilCameraDepthMap); // Allocate the matrix. MbufAlloc2d(MilSystem, 4, 4, 32 + M_FLOAT, M_ARRAY, &m_MilHomogeneousMatrix); // Set the depth display LUT. m_DepthDisplay.SetDisplayLut(MIL_UINT16_MAX, 1, MIL_UINT16_MAX); #if ENABLE_DISPLAY_3D m_MilDispD3d = M_NULL; #endif } //***************************************************************************** // Destructor. //***************************************************************************** CBaslerToFPointCloudExample::~CBaslerToFPointCloudExample() { MbufFree(m_MilHomogeneousMatrix); MbufFree(m_MilCameraDepthMap); M3dmapFree(m_Mil3dPlaneGeometry); #if ENABLE_DISPLAY_3D if (m_MilDispD3d) MdispD3DFree(m_MilDispD3d); #endif } //***************************************************************************** // LearnPlane. Learn the plane from the point cloud of the 3d camera. //***************************************************************************** bool CBaslerToFPointCloudExample::LearnPlane(MIL_ID MilPlaneDig) { // Set the Basler ToF camera to grab the point cloud image if using a real camera. EnableDisableComponent(MilPlaneDig, MIL_TEXT("Confidence"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilPlaneDig, MIL_TEXT("Intensity"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilPlaneDig, MIL_TEXT("Range"), MIL_TEXT("Coord3D_ABC32f"), true); // Ask the user to place a calibration grid in front of the Basler ToF camera. MdigProcess(MilPlaneDig, m_MilGrabImages, 2, M_START, M_DEFAULT, GrabPointCloudContinuousHook, this); MosPrintf(MIL_TEXT("Please place the camera to view a flat horizontal surface.\n\n")); MosPrintf(MIL_TEXT("Press <Enter> to grab.\n\n")); MosGetch(); // Stop the continuous grab. MdigProcess(MilPlaneDig, m_MilGrabImages, 2, M_STOP + M_WAIT, M_DEFAULT, GrabPointCloudContinuousHook, this); // Learn the plane from the image. M3dmapSetGeometry(m_Mil3dPlaneGeometry, M_PLANE, M_FIT, (MIL_DOUBLE)m_MilCameraDepthMap, M_NULL, M_INFINITE, M_DEFAULT, M_DEFAULT); // Set the point cloud relative coordinate system on the plane. // Get the parameters of the plane. MIL_DOUBLE Ax; MIL_DOUBLE Ay; MIL_DOUBLE Z0; M3dmapInquire(m_Mil3dPlaneGeometry, M_DEFAULT, M_FIT_PARAM_AX, &Ax); M3dmapInquire(m_Mil3dPlaneGeometry, M_DEFAULT, M_FIT_PARAM_AY, &Ay); M3dmapInquire(m_Mil3dPlaneGeometry, M_DEFAULT, M_FIT_PARAM_Z0, &Z0); // Use the plane parameters to move the output coordinate system of the point cloud container. MIL_DOUBLE Norm = sqrt((Ax * Ax) + (Ay * Ay) + 1); MIL_DOUBLE AxisX = -Ay; MIL_DOUBLE AxisY = Ax; MIL_DOUBLE AxisZ = 0; MIL_DOUBLE CrossNorm = sqrt((AxisX * AxisX) + (AxisY * AxisY) + (AxisZ * AxisZ)); MIL_DOUBLE RotationAngleRad = asin(CrossNorm / Norm); MIL_DOUBLE RotationAngle = DIV_180_PI * RotationAngleRad; McalSetCoordinateSystem(m_MilPointCloud, M_RELATIVE_COORDINATE_SYSTEM, M_ABSOLUTE_COORDINATE_SYSTEM, M_TRANSLATION, M_NULL, 0.0, 0.0, Z0, M_DEFAULT); McalSetCoordinateSystem(m_MilPointCloud, M_RELATIVE_COORDINATE_SYSTEM, M_RELATIVE_COORDINATE_SYSTEM, M_ROTATION_AXIS_ANGLE + M_COMPOSE_WITH_CURRENT, M_NULL, AxisX / CrossNorm, AxisY / CrossNorm, AxisZ / CrossNorm, -RotationAngle); return true; } //***************************************************************************** // LocateBoxes. Starts continuous processing of camera point cloud to locate a box. //***************************************************************************** void CBaslerToFPointCloudExample::LocateBoxes(MIL_ID MilBoxDig) { // Allocate the depth map generator. m_pDepthMapGenerator = new CDepthMapGenerator(m_MilPointCloud, DEPTH_MAP_PIXEL_SIZE, -MIN_BOX_SIZE_Z / 2, MAX_BOX_SIZE_Z - MIN_BOX_SIZE_Z / 2, MIN_FERET_MIN); // Set the position of the display. m_DepthDisplay.SetInitialWindowPos(DEPTH_DISPLAY_OFFSET_X, 0); // Select the depth images. m_DepthDisplay.Select(m_pDepthMapGenerator->MilPreprocessedDepthMap()); #if ENABLE_DISPLAY_3D // Allocate images to display the box in the 3d display. MbufClone(m_pDepthMapGenerator->MilPreprocessedDepthMap(), M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, &m_MilBoxDepthMap); MbufClone(m_pDepthMapGenerator->MilPreprocessedDepthMap(), M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, &m_MilContourDepthMap); MIL_INT DepthMapSizeX = MbufInquire(m_MilBoxDepthMap, M_SIZE_X, M_NULL); MIL_INT DepthMapSizeY = MbufInquire(m_MilBoxDepthMap, M_SIZE_Y, M_NULL); MbufAllocColor(m_MilSystem, 3, DepthMapSizeX, DepthMapSizeY, 8 + M_UNSIGNED, M_IMAGE + M_PROC, &m_MilBoxColorMap); #endif // Set the Basler ToF camera to grab the intensity and range component. EnableDisableComponent(MilBoxDig, MIL_TEXT("Confidence"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilBoxDig, MIL_TEXT("Intensity"), MIL_TEXT("Mono16"), false); EnableDisableComponent(MilBoxDig, MIL_TEXT("Range"), MIL_TEXT("Coord3D_ABC32f"), true); // Locate the boxes. CBaslerToFExample::LocateBoxes(MilBoxDig); #if ENABLE_DISPLAY_3D // Free the images to display the box in the 3d display. MbufFree(m_MilBoxDepthMap); MbufFree(m_MilContourDepthMap); MbufFree(m_MilBoxColorMap); #endif delete m_pDepthMapGenerator; } //***************************************************************************** // DisplayResult. Displays the found box. //***************************************************************************** void CBaslerToFPointCloudExample::DisplayResult(const SBox* pFoundBox, bool BoxIsFound) { #if ENABLE_DISPLAY_3D // Display the depth map and found box. MIL_ID MilDepthMaps[2] = {m_MilCameraDepthMap, M_NULL}; MIL_ID MilColorMaps[2] = {M_NULL, M_NULL}; MIL_INT NbSystems = 1; if(BoxIsFound) { // Clear the previously found box from the depth map. Put to invalid data. MbufClear(m_MilBoxDepthMap, MIL_UINT16_MAX); MbufClear(m_MilBoxColorMap, M_COLOR_CYAN); // Associate the calibration from the preprocessed depth map. Set the graylevel size z // so that the box height is the maximum value of the depth map. McalAssociate(m_pDepthMapGenerator->MilPreprocessedDepthMap(), m_MilBoxDepthMap, M_DEFAULT); McalAssociate(m_pDepthMapGenerator->MilPreprocessedDepthMap(), m_MilBoxColorMap, M_DEFAULT); McalControl(m_MilBoxDepthMap, M_GRAY_LEVEL_SIZE_Z, -pFoundBox->Height / (MIL_UINT16_MAX - 1)); McalControl(m_MilBoxDepthMap, M_WORLD_POS_Z, 0); // Draw the found box top face in the depth map. MgraColor(M_DEFAULT, MIL_UINT16_MAX -1); MgraControl(M_DEFAULT, M_INPUT_UNITS, M_WORLD); MgraRectAngle(M_DEFAULT, m_MilBoxDepthMap, pFoundBox->CenterPosX, pFoundBox->CenterPosY, pFoundBox->Length, pFoundBox->Width, pFoundBox->ZAxisAngle, M_CENTER_AND_DIMENSION + M_FILLED); MgraControl(M_DEFAULT, M_INPUT_UNITS, M_PIXEL); // Add a contour around the top face that is on the learned plane. MimErode(m_MilBoxDepthMap, m_MilContourDepthMap, 1, M_GRAYSCALE); MimArith(m_MilBoxDepthMap, m_MilContourDepthMap, m_MilContourDepthMap, M_EQUAL); MimArith(m_MilBoxDepthMap, m_MilContourDepthMap, m_MilBoxDepthMap, M_AND); MilDepthMaps[0] = m_MilBoxDepthMap; MilDepthMaps[1] = m_MilCameraDepthMap; MilColorMaps[0] = m_MilBoxColorMap; MilColorMaps[1] = M_NULL; NbSystems = 2; } if(!m_MilDispD3d) { m_MilDispD3d = MdepthSysD3DAlloc(M_NULL, M_NULL, M_NULL, M_NULL, MilDepthMaps, MilColorMaps, NbSystems, DISPLAY_3D_SIZE_X, DISPLAY_3D_SIZE_Y, M_DEFAULT, M_NULL); // Set camera pose. MdispD3DControl(m_MilDispD3d, MD3D_LOOK_AT_X, DISPLAY_3D_LOOK_AT_X); MdispD3DControl(m_MilDispD3d, MD3D_LOOK_AT_Y, DISPLAY_3D_LOOK_AT_Y); MdispD3DControl(m_MilDispD3d, MD3D_LOOK_AT_Z, DISPLAY_3D_LOOK_AT_Z); MdispD3DControl(m_MilDispD3d, MD3D_EYE_THETA, DISPLAY_3D_EYE_THETA); MdispD3DControl(m_MilDispD3d, MD3D_EYE_PHI, DISPLAY_3D_EYE_PHI); MdispD3DControl(m_MilDispD3d, MD3D_EYE_DIST, DISPLAY_3D_EYE_DIST); // Set to display the depth map in point mode. MdispD3DControl(m_MilDispD3d, MD3D_POINT, MD3D_ENABLE); } else MdepthSysD3DSetSystems(m_MilDispD3d, M_NULL, M_NULL, M_NULL, M_NULL, MilDepthMaps, MilColorMaps, NbSystems, M_DEFAULT); MdispD3DShow(m_MilDispD3d); #endif } //***************************************************************************** // Get3dDataFromGrabBuffer. Gets the 3d data from the grabbed image. Manages // the conversion of Basler invalid point value to // MIL invalid point value, then puts the grabbed data // right into the point cloud container. This function // also generates the camera depth map which is a depth // map projected from the point of view of the camera. //***************************************************************************** void CBaslerToFPointCloudExample::Get3dDataFromGrabBuffer(MIL_ID MilModifiedBuffer) { // Process the modified buffer to set invalid data. MIL_ID MilSystem = MbufInquire(MilModifiedBuffer, M_OWNER_SYSTEM, M_NULL); MIL_ID MilPointCloudUnsignedImage = MbufCreate2d(MilSystem, m_ImageSizeX * 3, m_ImageSizeY, 32 + M_UNSIGNED, M_IMAGE + M_PROC, M_MIL_ID + M_PITCH, M_DEFAULT, MilModifiedBuffer, M_NULL); MIL_FLOAT InvalidPoint = M_INVALID_POINT_FLOAT; MIL_UINT32* pInvalidPointUint32 = (MIL_UINT32*)(&InvalidPoint); MbufClearCond(MilPointCloudUnsignedImage, *pInvalidPointUint32, *pInvalidPointUint32, *pInvalidPointUint32, MilPointCloudUnsignedImage, M_EQUAL, BASLER_NAN_UINT32); MbufFree(MilPointCloudUnsignedImage); // Put the point cloud in the container. MIL_FLOAT* pPointCloudData = (MIL_FLOAT*)MbufInquire(MilModifiedBuffer, M_HOST_ADDRESS, M_NULL); M3dmapPut(m_MilPointCloud, M_POINT_CLOUD_LABEL(1), M_XYZ, 32 + M_FLOAT, m_ImageSizeX * m_ImageSizeY * 3, pPointCloudData, M_NULL, M_NULL, M_NULL, M_DEFAULT); // Generate a depth map from the absolute coordinate system point of view. Save the relative and put it back in case it was moved. McalGetCoordinateSystem(m_MilPointCloud, M_RELATIVE_COORDINATE_SYSTEM, M_ABSOLUTE_COORDINATE_SYSTEM, M_HOMOGENEOUS_MATRIX, m_MilHomogeneousMatrix, M_NULL, M_NULL, M_NULL, M_NULL); McalSetCoordinateSystem(m_MilPointCloud, M_RELATIVE_COORDINATE_SYSTEM, M_ABSOLUTE_COORDINATE_SYSTEM, M_IDENTITY, M_NULL, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT); M3dmapSetBox(m_MilPointCloud, M_EXTRACTION_BOX, M_BOUNDING_BOX, M_POINT_CLOUD_LABEL(1), M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT); M3dmapExtract(m_MilPointCloud, m_MilCameraDepthMap, M_NULL, M_CORRECTED_DEPTH_MAP, M_POINT_CLOUD_LABEL(1), M_DEFAULT); McalSetCoordinateSystem(m_MilPointCloud, M_RELATIVE_COORDINATE_SYSTEM, M_ABSOLUTE_COORDINATE_SYSTEM, M_HOMOGENEOUS_MATRIX, m_MilHomogeneousMatrix, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT); } //***************************************************************************** // GrabPointCloudContinuousHook. Processing callback of MdigProcess to grab the // point cloud. //***************************************************************************** MIL_INT MFTYPE CBaslerToFPointCloudExample::GrabPointCloudContinuousHook(MIL_INT HookType, MIL_ID MilHook, void* pHookData) { CBaslerToFPointCloudExample* pExample = (CBaslerToFPointCloudExample*)pHookData; return pExample->GrabPointCloudContinuous(HookType, MilHook); } //***************************************************************************** // GrabPointCloudContinuous. Processing function that gets the point cloud from // the grab buffer. //***************************************************************************** MIL_INT CBaslerToFPointCloudExample::GrabPointCloudContinuous(MIL_INT HookType, MIL_ID MilHook) { MIL_ID ModifiedBufferId; // Retrieve the MIL_ID of the grabbed buffer. MdigGetHookInfo(MilHook, M_MODIFIED_BUFFER + M_BUFFER_ID, &ModifiedBufferId); // Get the point cloud in the container. this->Get3dDataFromGrabBuffer(ModifiedBufferId); // Update the display. DisplayResult(NULL, false); return 0; } //***************************************************************************** // CDisplay class implementation. //***************************************************************************** //***************************************************************************** // Constructor. //***************************************************************************** CDisplay::CDisplay(MIL_ID MilSystem, MIL_CONST_TEXT_PTR DisplayName) : m_MilLut(M_NULL), m_MilSystem(MilSystem) { MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_WINDOWED, &m_MilDisp); MgraAllocList(MilSystem, M_DEFAULT, &m_MilGraList); MdispControl(m_MilDisp, M_ASSOCIATED_GRAPHIC_LIST_ID, m_MilGraList); MdispControl(m_MilDisp, M_TITLE, M_PTR_TO_DOUBLE(DisplayName)); } //***************************************************************************** // Destructor. //***************************************************************************** CDisplay::~CDisplay() { MgraFree(m_MilGraList); MdispFree(m_MilDisp); if (m_MilLut) MbufFree(m_MilLut); } //***************************************************************************** // SetInitialWindowPos. Sets the initial position of the display window. //***************************************************************************** void CDisplay::SetInitialWindowPos(MIL_INT WindowX, MIL_INT WindowY) { MdispControl(m_MilDisp, M_WINDOW_INITIAL_POSITION_X, WindowX); MdispControl(m_MilDisp, M_WINDOW_INITIAL_POSITION_Y, WindowY); } //***************************************************************************** // Update. Sets the update(M_ENABLE/M_DISABLE) parameter of the display. Optionally // clears the graphic list. //***************************************************************************** void CDisplay::Update(MIL_INT Update, bool ClearAnnotations /* = false */) { MdispControl(m_MilDisp, M_UPDATE, Update); if (ClearAnnotations) MgraClear(M_DEFAULT, m_MilGraList); } //***************************************************************************** // Select. Selects the image on the display. //***************************************************************************** void CDisplay::Select(MIL_ID MilImage) { MdispSelect(m_MilDisp, MilImage); } //***************************************************************************** // SetDisplayLut. Set a color LUT on the display. The color LUT range is defined // based on the MinGray and Maxgray parameters. //***************************************************************************** void CDisplay::SetDisplayLut(MIL_INT MaxImageValue, MIL_INT MinGray, MIL_INT MaxGray) { // Reallocate the LUT. if (m_MilLut) MbufFree(m_MilLut); MbufAllocColor(m_MilSystem, 3, MaxImageValue, 1, 8 + M_UNSIGNED, M_LUT, &m_MilLut); // Create the color map LUT. MbufClear(m_MilLut, M_COLOR_BLACK); MIL_ID MilDispLutChild = MbufChild1d(m_MilLut, MinGray, MaxGray - MinGray, M_NULL); MgenLutFunction(MilDispLutChild, M_COLORMAP_JET, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT); MbufFree(MilDispLutChild); // Set the LUT to the display. MdispLut(m_MilDisp, m_MilLut); } //***************************************************************************** // SetDisplayBitShift. Set the display to use a bit shift view mode. //***************************************************************************** void CDisplay::SetDisplayBitShift(MIL_INT ViewBitShift) { MdispControl(m_MilDisp, M_VIEW_MODE, M_BIT_SHIFT); MdispControl(m_MilDisp, M_VIEW_BIT_SHIFT, ViewBitShift); }