Click here to show toolbars of the Web Online Help System: show toolbars |
//*************************************************************************************** // // File name: CalGenChessGrid.cpp // Location: See Matrox Example Launcher in the MIL Control Center // // // Synopsis: This example generates an image of a calibration grid according to // the specifications in gridconfig.h. It can generate chessboard // grids with or without fiducials. // // Copyright (C) Matrox Electronic Systems Ltd., 1992-2016. // All Rights Reserved #include "common.h" //**************************************************************************** // Example description. //**************************************************************************** void PrintHeader() { MosPrintf(MIL_TEXT("[EXAMPLE NAME]\n") MIL_TEXT("CalGenChessGrid\n\n") MIL_TEXT("[SYNOPSIS]\n") MIL_TEXT("This example demonstrates how to generate an image of a\n") MIL_TEXT("calibration grid according to the user-defined specifications in\n") MIL_TEXT("gridconfig.h. It can generate chessboard grids with or without\n") MIL_TEXT("fiducials. The resulting grid can be used with the MIL camera\n") MIL_TEXT("calibration module (Mcal).\n\n") MIL_TEXT("[MODULES USED]\n") MIL_TEXT("Modules used: application, buffer, code, display, graphics, image\n") MIL_TEXT("processing, system.\n\n") MIL_TEXT("Press <Enter> to continue.\n\n")); MosGetch(); } //***************************************************************************** // Draws a thick rectangle. The start and end coordinates determine the outer // corners of the rectangle. //***************************************************************************** inline void DrawThickRect(MIL_ID ContextGraId, MIL_ID ImageId, MIL_DOUBLE Color, MIL_INT ThicknessX, MIL_INT ThicknessY, MIL_INT StartX, MIL_INT StartY, MIL_INT EndX, MIL_INT EndY) { MgraColor(ContextGraId, Color); MgraRectFill(ContextGraId, ImageId, StartX , StartY , EndX , StartY+ThicknessY-1); MgraRectFill(ContextGraId, ImageId, StartX , EndY-ThicknessY+1, EndX , EndY ); MgraRectFill(ContextGraId, ImageId, StartX , StartY , StartX+ThicknessX-1, EndY ); MgraRectFill(ContextGraId, ImageId, EndX-ThicknessX+1, StartY , EndX , EndY ); } //***************************************************************************** // Contains all necessary pixel dimensions to draw the grid image and its // annotations. //***************************************************************************** struct AnnotationStruct { MIL_DOUBLE PixelsPerSquareX; // number of pixels in the X direction for each grid square MIL_DOUBLE PixelsPerSquareY; // number of pixels in the Y direction for each grid square MIL_INT MaxPixelsPerSquareInt; // maximum of PixelsPerSquareX and PixelsPerSquareY, rounded up; used as a base unit for some annotations MIL_INT ImageSizeX; // width (in pixels) of the grid image, without annotations MIL_INT ImageSizeY; // height (in pixels) of the grid image, without annotations MIL_INT FullSizeX; // width (in pixels) of the grid image, with annotations MIL_INT FullSizeY; // height (in pixels) of the grid image, with annotations MIL_INT LeftBorder; // number of pixels reserved for annotations on the left MIL_INT RightBorder; // number of pixels reserved for annotations on the right MIL_INT TopBorder; // number of pixels reserved for annotations on the top MIL_INT BottomBorder; // number of pixels reserved for annotations on the bottom MIL_INT BorderThickness; // thickness in pixels of the border separating the grid from the annotations MIL_INT LastPixelOfGridX; // X pixel coordinate of the lower-right corner of the grid (start of annotations) MIL_INT LastPixelOfGridY; // Y pixel coordinate of the lower-right corner of the grid (start of annotations) }; //***************************************************************************** // Computes all necessary pixel dimensions to draw the grid image and its // annotations, according to the grid parameters previously computed. //***************************************************************************** AnnotationStruct ComputeAnnotationParameters(const GridInfoStruct& GridInfo) { AnnotationStruct Annotation; // Compute the number of pixels per square. if (DPI <= 0.0) throw MIL_TEXT("DPI must be positive"); MIL_DOUBLE PixelsPerInch = DPI; MIL_DOUBLE PixelsPerWorldUnit = GetInchesPerWorldUnit(UNIT) * PixelsPerInch; Annotation.PixelsPerSquareX = GridInfo.SpacingX * PixelsPerWorldUnit; Annotation.PixelsPerSquareY = GridInfo.SpacingY * PixelsPerWorldUnit; Annotation.MaxPixelsPerSquareInt = static_cast<MIL_INT>(ceil( Annotation.PixelsPerSquareX > Annotation.PixelsPerSquareY ? Annotation.PixelsPerSquareX : Annotation.PixelsPerSquareY)); // Compute the grid image size. Annotation.ImageSizeX = static_cast<MIL_INT>(ceil(GridInfo.NumSquaresX * Annotation.PixelsPerSquareX)); Annotation.ImageSizeY = static_cast<MIL_INT>(ceil(GridInfo.NumSquaresY * Annotation.PixelsPerSquareY)); // Compute the annotation border size. if (DRAW_ANNOTATIONS) { Annotation.BorderThickness = static_cast<MIL_INT>(ceil(BORDER_THICKNESS * Annotation.MaxPixelsPerSquareInt)); Annotation.LeftBorder = Annotation.MaxPixelsPerSquareInt; // 1 square worth of space for symmetry Annotation.RightBorder = Annotation.MaxPixelsPerSquareInt; // 1 square worth of space for the arrow indicator Annotation.TopBorder = Annotation.MaxPixelsPerSquareInt; // 1 square worth of space for the arrow indicator // Since ImageSizeX is used to determine the legend font size, and we want the vertical // space to be proportional to the font size, compute a bottom border size proportional // to ImageSizeX. Annotation.BottomBorder = static_cast<MIL_INT>(Annotation.ImageSizeX * BOTTOM_SPACE_FACTOR); } else { // No borders, since there are no annotations. Annotation.BorderThickness = 0; Annotation.LeftBorder = 0; Annotation.RightBorder = 0; Annotation.TopBorder = 0; Annotation.BottomBorder = 0; } // Compute the full image size (grid + annotations). Annotation.FullSizeX = Annotation.ImageSizeX + Annotation.LeftBorder + Annotation.RightBorder; Annotation.FullSizeY = Annotation.ImageSizeY + Annotation.TopBorder + Annotation.BottomBorder; // Compute lower-right corner of grid. Annotation.LastPixelOfGridX = Annotation.LeftBorder + Annotation.ImageSizeX - 1; Annotation.LastPixelOfGridY = Annotation.TopBorder + Annotation.ImageSizeY - 1; return Annotation; } //***************************************************************************** // Draws the border, the legend and reference point indicators. //***************************************************************************** void DrawAnnotations(MIL_ID ContextGraId, MIL_ID FullImageId, const GridInfoStruct& GridInfo, const AnnotationStruct& Annotation) { // Draw the border. DrawThickRect(ContextGraId, FullImageId, BORDER_COLOR, Annotation.BorderThickness, Annotation.BorderThickness, Annotation.LeftBorder - Annotation.BorderThickness, Annotation.TopBorder - Annotation.BorderThickness, Annotation.LastPixelOfGridX + Annotation.BorderThickness, Annotation.LastPixelOfGridY + Annotation.BorderThickness); // Determine the font size for the legend. MIL_INT FontSize = static_cast<MIL_INT>(FONT_SIZE_FACTOR * Annotation.ImageSizeX); if (FontSize <= 9) throw MIL_TEXT("Font size is too small, use a higher DPI"); MgraFont(ContextGraId, MIL_FONT_NAME(FONT_NAME)); MgraControl(ContextGraId, M_FONT_SIZE, FontSize); MgraColor(ContextGraId, FOREGROUND_COLOR); MgraControl(ContextGraId, M_BACKCOLOR, BACKGROUND_COLOR); // Prepare the legend text. const MIL_INT MAX_LEGEND_LENGTH = 256; MIL_TEXT_CHAR Legend[MAX_LEGEND_LENGTH]; MIL_CONST_TEXT_PTR UnitName = GetUnitName(UNIT); MosSprintf(Legend, MAX_LEGEND_LENGTH, MIL_TEXT("Grid size: %g %s x %g %s%sRow/column number: %d x %d%sRow/column spacing: %g %s x %g %s"), GridInfo.GridSizeX, UnitName, GridInfo.GridSizeY, UnitName, SEPARATOR, (int)(GridInfo.NumSquaresY - 2 * NUM_SQUARES_FOR_QUIET_ZONE + 1), (int)(GridInfo.NumSquaresX - 2 * NUM_SQUARES_FOR_QUIET_ZONE + 1), SEPARATOR, GridInfo.SpacingY, UnitName, GridInfo.SpacingX, UnitName); // Draw the legend. MIL_INT TextVerticalOffset = static_cast<MIL_INT>(TEXT_VERTICAL_OFFSET * Annotation.ImageSizeX); MgraText(ContextGraId, FullImageId, Annotation.PixelsPerSquareX, Annotation.LastPixelOfGridY + TextVerticalOffset, Legend); #if NUM_FIDUCIALS > 0 const MIL_INT TRIANGLE_LENGTH_TO_WIDTH_RATIO = 2; // Draw triangles to indicate the grid reference point. MIL_INT RefPointSquareNoX = GridInfo.GetReferencePositionX(); MIL_INT RefPointSquareNoY = GridInfo.GetReferencePositionY(); MIL_INT RefPointPixelPosX = static_cast<MIL_INT>(RefPointSquareNoX * Annotation.PixelsPerSquareX) + Annotation.LeftBorder; MIL_INT RefPointPixelPosY = static_cast<MIL_INT>(RefPointSquareNoY * Annotation.PixelsPerSquareY) + Annotation.TopBorder; MIL_INT TriangleOffset = static_cast<MIL_INT>(ceil(TRIANGLE_OFFSET * Annotation.MaxPixelsPerSquareInt)); MIL_INT TriangleLength = static_cast<MIL_INT>(ceil(TRIANGLE_LENGTH * Annotation.MaxPixelsPerSquareInt)); MgraColor(ContextGraId, FOREGROUND_COLOR); MIL_INT TriangleVerticesX[3], TriangleVerticesY[3]; // Draw the triangle to the top. TriangleVerticesX[0] = RefPointPixelPosX; TriangleVerticesX[1] = TriangleVerticesX[0] - TriangleLength / (2 * TRIANGLE_LENGTH_TO_WIDTH_RATIO); TriangleVerticesX[2] = TriangleVerticesX[0] + TriangleLength / (2 * TRIANGLE_LENGTH_TO_WIDTH_RATIO); TriangleVerticesY[0] = Annotation.TopBorder - TriangleOffset; TriangleVerticesY[1] = TriangleVerticesY[0] - TriangleLength; TriangleVerticesY[2] = TriangleVerticesY[1]; MgraLines(ContextGraId, FullImageId, 3, TriangleVerticesX, TriangleVerticesY, M_NULL, M_NULL, M_POLYGON+M_FILLED); // Draw the triangle to the right or to the left, whichever is closest. if (RefPointSquareNoX <= GridInfo.NumSquaresX / 2) { // To the left. TriangleVerticesX[0] = Annotation.LeftBorder - TriangleOffset; TriangleVerticesX[1] = TriangleVerticesX[0] - TriangleLength; } else { // To the right. TriangleVerticesX[0] = Annotation.LastPixelOfGridX + TriangleOffset; TriangleVerticesX[1] = TriangleVerticesX[0] + TriangleLength; } TriangleVerticesX[2] = TriangleVerticesX[1]; TriangleVerticesY[0] = RefPointPixelPosY; TriangleVerticesY[1] = TriangleVerticesY[0] - TriangleLength / (2 * TRIANGLE_LENGTH_TO_WIDTH_RATIO); TriangleVerticesY[2] = TriangleVerticesY[0] + TriangleLength / (2 * TRIANGLE_LENGTH_TO_WIDTH_RATIO); MgraLines(ContextGraId, FullImageId, 3, TriangleVerticesX, TriangleVerticesY, M_NULL, M_NULL, M_POLYGON+M_FILLED); #endif } //***************************************************************************** // Compute a zoom factor so that the grid image can fit in the screen. //***************************************************************************** void SetZoomFactor(MIL_ID DispId, MIL_INT GridSizeX, MIL_INT GridSizeY) { MIL_DOUBLE ZoomFactor = 1.0; if (GridSizeX > MAX_DISPLAY_SIZE_X) ZoomFactor = static_cast<MIL_DOUBLE>(MAX_DISPLAY_SIZE_X) / GridSizeX; if (GridSizeY > MAX_DISPLAY_SIZE_Y) { MIL_DOUBLE MaxZoomFactor = static_cast<MIL_DOUBLE>(MAX_DISPLAY_SIZE_Y) / GridSizeY; if (MaxZoomFactor < ZoomFactor) ZoomFactor = MaxZoomFactor; } if (ZoomFactor < 1.0) MdispZoom(DispId, ZoomFactor, ZoomFactor); } //***************************************************************************** // Structure containing all MIL objects. Ensures that all objects will be // correctly freed, even in the presence of exceptions. //***************************************************************************** struct MILObjectsStruture { MIL_ID AppId; // application context MIL_ID SysId; // system MIL_ID ContextGraId; // graphics context MIL_ID FullImageId; // image buffer of the grid, with annotations if applicable MIL_ID GridImageId; // child buffer of FullImageId, contains only the grid MIL_ID DispId; // display // Constructor cleanly initializes all identifiers. MILObjectsStruture() : AppId (M_NULL), SysId (M_NULL), ContextGraId(M_NULL), FullImageId (M_NULL), GridImageId (M_NULL), DispId (M_NULL) { } // Destructor ensures all objects are freed, even in the presence of exceptions. ~MILObjectsStruture() { if (DispId != M_NULL) MdispFree(DispId ); if (GridImageId != M_NULL) MbufFree(GridImageId ); if (FullImageId != M_NULL) MbufFree(FullImageId ); if (ContextGraId != M_NULL) MgraFree(ContextGraId); if (SysId != M_NULL) MsysFree(SysId ); if (AppId != M_NULL) MappFree(AppId ); } }; //***************************************************************************** // Main. //***************************************************************************** int MosMain(void) { PrintHeader(); try { // Allocate MIL objects. MILObjectsStruture MILObjects; MappAlloc(M_NULL, M_DEFAULT, &MILObjects.AppId); MsysAlloc(MILObjects.AppId, M_SYSTEM_HOST, M_DEFAULT, M_DEFAULT, &MILObjects.SysId); MgraAlloc(MILObjects.SysId, &MILObjects.ContextGraId); // Compute all grid and annotation parameters according to gridconfig.h. GridInfoStruct GridInfo = ComputeGridParameters(); AnnotationStruct Annotation = ComputeAnnotationParameters(GridInfo); // Allocate the full image. MbufAlloc2d(MILObjects.SysId, Annotation.FullSizeX, Annotation.FullSizeY, 8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, &MILObjects.FullImageId); MbufChild2d(MILObjects.FullImageId, Annotation.LeftBorder, Annotation.TopBorder, Annotation.ImageSizeX, Annotation.ImageSizeY, &MILObjects.GridImageId); MbufClear(MILObjects.FullImageId, BACKGROUND_COLOR); // Create the chessboard pattern. MgraColor(MILObjects.ContextGraId, FOREGROUND_COLOR); for (MIL_INT y = 0; y < GridInfo.NumSquaresY; ++y) { for (MIL_INT x = 0; x < GridInfo.NumSquaresX; ++x) { if ((x & 0x1) == (y & 0x1)) // if this is a black cell { MgraRectFill(MILObjects.ContextGraId, MILObjects.GridImageId, x * Annotation.PixelsPerSquareX, y * Annotation.PixelsPerSquareY, (x+1) * Annotation.PixelsPerSquareX - 1, (y+1) * Annotation.PixelsPerSquareY - 1); } } } // Create the quiet zone. DrawThickRect(MILObjects.ContextGraId, MILObjects.GridImageId, BACKGROUND_COLOR, static_cast<MIL_INT>(QUIET_ZONE_BORDER * Annotation.PixelsPerSquareX), static_cast<MIL_INT>(QUIET_ZONE_BORDER * Annotation.PixelsPerSquareY), 0, 0, Annotation.ImageSizeX-1, Annotation.ImageSizeY-1); // Draw fiducials, if any. AddFiducials(MILObjects.GridImageId, GridInfo, Annotation.PixelsPerSquareX, Annotation.PixelsPerSquareY); // If enabled, draw the grid border, the reference point indicators and the legend. if (DRAW_ANNOTATIONS) DrawAnnotations(MILObjects.ContextGraId, MILObjects.FullImageId, GridInfo, Annotation); // Save the grid image with the correct DPI. MbufControl(MILObjects.FullImageId, M_RESOLUTION_X, DPI); MbufControl(MILObjects.FullImageId, M_RESOLUTION_Y, DPI); MbufExport(OUTPUT_GRID_NAME, OUTPUT_FILE_FORMAT, MILObjects.FullImageId); // Show the image and print some information. MdispAlloc(MILObjects.SysId, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_WINDOWED, &MILObjects.DispId); SetZoomFactor(MILObjects.DispId, Annotation.FullSizeX, Annotation.FullSizeY); MdispSelect(MILObjects.DispId, MILObjects.FullImageId); MosPrintf(MIL_TEXT("Image saved:\n")); MosPrintf(MIL_TEXT("------------\n")); MosPrintf(MIL_TEXT(" Name: '%s'\n"), OUTPUT_GRID_NAME); MosPrintf(MIL_TEXT(" Size: %d x %d\n"), (int)Annotation.FullSizeX, (int)Annotation.FullSizeY); MosPrintf(MIL_TEXT("\n")); MosPrintf(MIL_TEXT("To print this image correctly:\n")); MosPrintf(MIL_TEXT(" - Set your printer resolution to %d DPI or higher.\n"), (int)DPI); MosPrintf(MIL_TEXT(" - Print with software that takes the DPI into account.\n")); MosPrintf(MIL_TEXT(" - Disable any 'fit' or 'scale' option in the print dialog.\n")); MosPrintf(MIL_TEXT(" - Verify the printed grid dimensions.\n")); MosPrintf(MIL_TEXT("\n")); MosPrintf(MIL_TEXT("Press <Enter> to end.\n")); MosGetch(); // All MIL objects are freed here, in MILObjectsStruture's destructor. } catch (MIL_CONST_TEXT_PTR ErrorMessage) { MosPrintf(MIL_TEXT("\nERROR:\n %s.\n\n"), ErrorMessage); MosPrintf(MIL_TEXT("Press <Enter> to end.\n")); MosGetch(); } return 0; }