#include <mil.h>
#include <algorithm>
using namespace std;
#define EXAMPLE_IMAGE_DIR_PATH M_IMAGE_PATH MIL_TEXT("/Classification/PrintedChar/")
#define EXAMPLE_CLASS_CTX_PATH EXAMPLE_IMAGE_DIR_PATH MIL_TEXT("MatroxNet_PrintedCharEx.mclass")
#define EXAMPLE_STR_ELEM_PATH EXAMPLE_IMAGE_DIR_PATH MIL_TEXT("StructElement.mim")
#define TARGET_IMAGE_DIR_PATH EXAMPLE_IMAGE_DIR_PATH MIL_TEXT("Products/")
#define USE_EXAMPLE_IMAGE_FOLDER
#define DISP_WINDOW_SIZE_X 600
#define DISP_WINDOW_SIZE_Y 250
#define DISP_BAR_SIZE_X 110
#define START_Y_TILE 130
#define START_Y_CHAR 190
#define START_Y_SCORE 210
#define BUFFERING_SIZE_MAX 10
#define NORMALIZE_CHAR_WINDOW_SIZE 32
#define NORMALIZE_CHAR_WINDOW_MARGIN 9
#define INTENSITY_OFFSET 5
#define BUFF_SIZE 256
struct charBox
{
MIL_INT BBoxStx,
BBoxSty,
BBoxEnx,
BBoxEny;
};
#ifdef USE_EXAMPLE_IMAGE_FOLDER
#define SYSTEM_TO_USE M_SYSTEM_HOST
#define DCF_TO_USE TARGET_IMAGE_DIR_PATH
#else
#define SYSTEM_TO_USE M_SYSTEM_DEFAULT
#define DCF_TO_USE MIL_TEXT("M_DEFAULT")
#endif
void SetupDisplay(MIL_ID MilSystem,
MIL_ID MilDisplay,
MIL_ID ClassCtx,
MIL_ID &MilDispImage,
MIL_ID &MilOverlay);
void ResetDisplay(MIL_ID MilDisplay,
MIL_ID MilBlobRes,
MIL_ID MilImage,
MIL_ID MilDispImage);
MIL_DOUBLE Median(vector<MIL_INT> scores);
vector<charBox> BuildBboxDict(MIL_ID img,
vector<MIL_INT> &MilBoxMinX,
vector<MIL_INT> &MilBoxMaxX,
vector<MIL_INT> &MilBoxMinY,
vector<MIL_INT> &MilBoxMaxY);
void GetBbox(MIL_ID MilSystem,
MIL_ID MilImage,
MIL_ID MimCtx,
MIL_ID MilBlobCtx,
MIL_ID MilBlobRes,
MIL_ID MilAdaptiveCtx,
vector<MIL_INT> &MilBoxMinX,
vector<MIL_INT> &MilBoxMaxX,
vector<MIL_INT> &MilBoxMinY,
vector<MIL_INT> &MilBoxMaxY);
void Saturate(MIL_INT &sx,
MIL_INT &sy,
MIL_INT &ex,
MIL_INT &ey,
MIL_INT w,
MIL_INT h);
void GetNormalizedChar(MIL_ID MilSystem,
MIL_ID MilClassCtx,
MIL_ID MilClassRes,
MIL_ID MilImage,
MIL_ID MilDestImage,
MIL_INT StartX,
MIL_INT StartY,
MIL_INT EndX,
MIL_INT EndY);
void UpdateDispChar(MIL_ID MilDispImage,
MIL_ID MilPredImage,
MIL_ID MilOverlayImg,
MIL_STRING ReadChar,
MIL_DOUBLE BestScore,
MIL_INT StringLenght,
MIL_INT id);
int main(void)
{
MIL_ID MilApplication,
MilSystem,
MilDisplay,
MilOverlay,
MilImage,
MilPreProc,
MilDispImage,
MilMimCtx,
MilClassCtx,
MilClassRes,
MilBlobCtx,
MilBlobRes,
MilAdaptiveCtx,
MilPredInpImg,
MilStructElement;
MIL_INT NumberOfCategories,
InputSizeX,
InputSizeY,
BestClass;
MIL_DOUBLE BestScore;
std::vector<MIL_STRING> Images;
Images.reserve(16);
Images.push_back(MIL_TEXT("Img_01.bmp"));
Images.push_back(MIL_TEXT("Img_02.bmp"));
Images.push_back(MIL_TEXT("Img_03.bmp"));
Images.push_back(MIL_TEXT("Img_04.bmp"));
Images.push_back(MIL_TEXT("Img_05.bmp"));
Images.push_back(MIL_TEXT("Img_06.bmp"));
Images.push_back(MIL_TEXT("Img_07.bmp"));
Images.push_back(MIL_TEXT("Img_08.bmp"));
Images.push_back(MIL_TEXT("Img_09.bmp"));
Images.push_back(MIL_TEXT("Img_10.bmp"));
MosPrintf(MIL_TEXT("[EXAMPLE NAME]\n"));
MosPrintf(MIL_TEXT("ClassPrintedChar\n\n"));
MosPrintf(MIL_TEXT("[SYNOPSIS]\n"));
MosPrintf(MIL_TEXT("This example demonstrates the application of the classification module\n"));
MosPrintf(MIL_TEXT("in OCR. Characters are first detected and pre-processed. A pre-trained\n"));
MosPrintf(MIL_TEXT("classification context is then used to identify the character.\n"));
MosPrintf(MIL_TEXT("\n"));
MosPrintf(MIL_TEXT("[MODULES USED]\n"));
MosPrintf(MIL_TEXT("Classification, Blob Analysis, Buffer, Display, Graphics, Image Processing.\n\n"));
MappAlloc(M_NULL, M_DEFAULT, &MilApplication);
MsysAlloc(M_DEFAULT, SYSTEM_TO_USE, M_DEFAULT, M_DEFAULT, &MilSystem);
MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT, &MilDisplay);
MosPrintf(MIL_TEXT("Press <Enter> to continue.\n"));
MosGetchar();
MbufImport(EXAMPLE_STR_ELEM_PATH, M_DEFAULT, M_RESTORE + M_NO_GRAB + M_NO_COMPRESS, MilSystem, &MilStructElement);
MblobAlloc(MilSystem, M_DEFAULT, M_DEFAULT, &MilBlobCtx);
MblobAllocResult(MilSystem, M_DEFAULT, M_DEFAULT, &MilBlobRes);
MblobControl(MilBlobCtx, M_FOREGROUND_VALUE, M_ZERO);
MblobControl(MilBlobCtx, M_BOX, M_ENABLE);
MblobControl(MilBlobCtx, M_SORT1, M_BOX_X_MAX);
MblobControl(MilBlobCtx, M_SORT1_DIRECTION, M_SORT_UP);
MimAlloc(MilSystem, M_BINARIZE_ADAPTIVE_CONTEXT, M_DEFAULT, &MilAdaptiveCtx);
MimControl(MilAdaptiveCtx, M_FOREGROUND_VALUE, M_FOREGROUND_BLACK);
MimControl(MilAdaptiveCtx, M_AVERAGE_MODE, M_GAUSSIAN);
MimControl(MilAdaptiveCtx, M_MINIMUM_CONTRAST, 4);
MimControl(MilAdaptiveCtx, M_LOCAL_DIMENSION, 40);
MimAlloc(MilSystem, M_LINEAR_FILTER_IIR_CONTEXT, M_DEFAULT, &MilMimCtx);
MimControl(MilMimCtx, M_FILTER_OPERATION, M_SMOOTH);
MimControl(MilMimCtx, M_FILTER_TYPE, M_DERICHE);
MosPrintf(MIL_TEXT("Restoring the classification context from file.."));
MclassRestore(EXAMPLE_CLASS_CTX_PATH, MilSystem, M_DEFAULT, &MilClassCtx);
MosPrintf(MIL_TEXT("."));
MclassPreprocess(MilClassCtx, M_DEFAULT);
MosPrintf(MIL_TEXT(".ready.\n"));
MclassInquire(MilClassCtx, M_CONTEXT, M_NUMBER_OF_CLASSES + M_TYPE_MIL_INT, &NumberOfCategories);
MclassInquire(MilClassCtx, M_DEFAULT_SOURCE_LAYER, M_SIZE_X + M_TYPE_MIL_INT, &InputSizeX);
MclassInquire(MilClassCtx, M_DEFAULT_SOURCE_LAYER, M_SIZE_Y + M_TYPE_MIL_INT, &InputSizeY);
MosPrintf(MIL_TEXT(" - The classifier was trained to recognize %d categories.\n"), NumberOfCategories);
MosPrintf(MIL_TEXT(" - The classifier was trained for %dx%d source images.\n\n"), InputSizeX, InputSizeY);
MclassAllocResult(MilSystem, M_PREDICT_CNN_RESULT, M_DEFAULT, &MilClassRes);
MbufAlloc2d(MilSystem, InputSizeX, InputSizeY, 8 + M_UNSIGNED, M_IMAGE + M_PROC, &MilPredInpImg);
SetupDisplay(MilSystem, MilDisplay, MilClassCtx, MilDispImage, MilOverlay);
for(size_t i = 0; i < Images.size(); i++)
{
MdispControl(MilDisplay, M_UPDATE, M_DISABLE);
MIL_STRING filename = DCF_TO_USE + Images[i];
MbufRestore(filename, MilSystem, &MilImage);
MbufClone(MilImage, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, &MilPreProc);
MimMorphic(MilImage, MilPreProc, MilStructElement, M_BOTTOM_HAT, 1, M_GRAYSCALE);
MimArith(MilPreProc, INTENSITY_OFFSET, MilPreProc, M_SUB_CONST + M_SATURATION);
MimArith(MilPreProc, M_NULL, MilPreProc, M_NOT);
vector<MIL_INT> MilBoxMinX,
MilBoxMaxX,
MilBoxMinY,
MilBoxMaxY;
GetBbox(MilSystem,
MilImage,
MilMimCtx,
MilBlobCtx,
MilBlobRes,
MilAdaptiveCtx,
MilBoxMinX,
MilBoxMaxX,
MilBoxMinY,
MilBoxMaxY);
auto CharDictionary = BuildBboxDict(MilImage,
MilBoxMinX,
MilBoxMaxX,
MilBoxMinY,
MilBoxMaxY);
ResetDisplay(MilDisplay, MilBlobRes, MilImage, MilDispImage);
MIL_INT charId = 0;
MIL_STRING readChar;
MosPrintfA("\n String: ");
for(auto charInf : CharDictionary)
{
GetNormalizedChar(MilSystem,
MilClassCtx,
MilClassRes,
MilPreProc,
MilPredInpImg,
charInf.BBoxStx,
charInf.BBoxSty,
charInf.BBoxEnx,
charInf.BBoxEny);
MclassPredict(MilClassCtx, MilPredInpImg, MilClassRes, M_DEFAULT);
MclassGetResult(MilClassRes, M_DEFAULT, M_BEST_CLASS_INDEX + M_TYPE_MIL_INT, &BestClass);
MclassGetResult(MilClassRes, M_DEFAULT, M_BEST_CLASS_SCORE, &BestScore);
MclassInquire(MilClassCtx, M_CLASS_INDEX(BestClass), M_CLASS_NAME + M_TYPE_STRING, readChar);
UpdateDispChar(MilDispImage,
MilPredInpImg,
MilOverlay,
readChar,
BestScore,
CharDictionary.size(),
charId);
charId++;
MosPrintfA("%s", readChar.c_str());
}
MosPrintfA("\n\n");
MdispControl(MilDisplay, M_UPDATE, M_ENABLE);
MosPrintf(MIL_TEXT("Press <Enter> to continue.\n"));
MosGetch();
MbufFree(MilPreProc);
MbufFree(MilImage);
}
MbufFree(MilDispImage);
MbufFree(MilPredInpImg);
MbufFree(MilStructElement);
MclassFree(MilClassRes);
MclassFree(MilClassCtx);
MblobFree(MilBlobCtx);
MblobFree(MilBlobRes);
MimFree(MilMimCtx);
MimFree(MilAdaptiveCtx);
MdispFree(MilDisplay);
MsysFree(MilSystem);
MappFree(MilApplication);
return 0;
}
void SetupDisplay(MIL_ID MilSystem,
MIL_ID MilDisplay,
MIL_ID ClassCtx,
MIL_ID &MilDispImage,
MIL_ID &MilOverlay
)
{
MbufAllocColor(MilSystem, 3, DISP_WINDOW_SIZE_X, DISP_WINDOW_SIZE_Y, 8 + M_UNSIGNED, M_IMAGE + M_PROC + M_DISP, &MilDispImage);
MdispSelect(MilDisplay, MilDispImage);
MdispControl(MilDisplay, M_OVERLAY, M_ENABLE);
MilOverlay = MdispInquire(MilDisplay, M_OVERLAY_ID, M_NULL);
}
void ResetDisplay(MIL_ID MilDisplay,
MIL_ID MilBlobRes,
MIL_ID MilImage,
MIL_ID MilDispImage)
{
MIL_ID MilDispChild,
MilImageChild;
MIL_INT Sx = MbufInquire(MilImage, M_SIZE_X, M_NULL);
MIL_INT Sy = MbufInquire(MilImage, M_SIZE_Y, M_NULL);
MIL_INT offX = DISP_BAR_SIZE_X + (DISP_WINDOW_SIZE_X - DISP_BAR_SIZE_X - Sx) / 2;
MIL_INT offY = 30;
MbufClear(MilDispImage, M_COLOR_BRIGHT_GRAY);
MdispControl(MilDisplay, M_OVERLAY_CLEAR, M_DEFAULT);
MbufChild2d(MilDispImage, 0, 0, DISP_BAR_SIZE_X, DISP_WINDOW_SIZE_Y, &MilDispChild);
MbufClear(MilDispChild, M_COLOR_GRAY);
MgraColor(M_DEFAULT, M_COLOR_WHITE);
MgraControl(M_DEFAULT, M_BACKGROUND_MODE, M_TRANSPARENT);
MgraText(M_DEFAULT, MilDispChild, 8, offY + 30, MIL_TEXT("Input Image"));
MgraText(M_DEFAULT, MilDispChild, 8, START_Y_TILE + 25, MIL_TEXT(" Tiles"));
MgraText(M_DEFAULT, MilDispChild, 8, START_Y_CHAR, MIL_TEXT(" Characters"));
MgraText(M_DEFAULT, MilDispChild, 8, START_Y_SCORE, MIL_TEXT(" Scores"));
MbufChild2d(MilDispImage, offX, offY, Sx, Sy, &MilImageChild);
MbufCopy(MilImage, MilImageChild);
MgraColor(M_DEFAULT, M_COLOR_BLUE);
MblobDraw(M_DEFAULT, MilBlobRes, MilImageChild, M_DRAW_BOX, M_INCLUDED_BLOBS, M_DEFAULT);
MbufFree(MilDispChild);
MbufFree(MilImageChild);
}
void UpdateDispChar(MIL_ID MilDispImage,
MIL_ID MilPredImage,
MIL_ID MilOverlayImg,
MIL_STRING ReadChar,
MIL_DOUBLE BestScore,
MIL_INT StringLength,
MIL_INT id)
{
MIL_INT PredSizeX = MbufInquire(MilPredImage, M_SIZE_X, M_NULL);
MIL_INT PredSizeY = MbufInquire(MilPredImage, M_SIZE_Y, M_NULL);
MIL_INT Margin = 5;
MIL_INT cwl = (PredSizeX + (2 * Margin));
MIL_INT ReqSpace = StringLength * cwl;
MIL_INT ExtraSpace = DISP_WINDOW_SIZE_X - DISP_BAR_SIZE_X - ReqSpace;
MIL_INT sx = DISP_BAR_SIZE_X + (ExtraSpace / 2) + Margin + (id * (cwl));
MIL_INT sy = 130;
MIL_INT ex = sx + PredSizeX;
MIL_INT ey = sy + PredSizeY;
MgraColor(M_DEFAULT, M_COLOR_BLACK);
MgraRect(M_DEFAULT, MilDispImage, sx - 1, sy - 1, ex, ey);
MbufCopyColor2d(MilPredImage, MilDispImage, M_ALL_BANDS, 0, 0, M_ALL_BANDS, sx, sy, PredSizeX, PredSizeY);
MIL_TEXT_CHAR Char_text[256],
Accuracy_text[256];
MosSprintf(Char_text, BUFF_SIZE, MIL_TEXT(" %s"), ReadChar.c_str());
MosSprintf(Accuracy_text, BUFF_SIZE, MIL_TEXT(" %.0lf%%"), BestScore);
MgraColor(M_DEFAULT, M_COLOR_BLUE);
MgraFont(M_DEFAULT, M_FONT_DEFAULT_SMALL);
MgraText(M_DEFAULT, MilOverlayImg, sx, START_Y_CHAR, Char_text);
MgraText(M_DEFAULT, MilOverlayImg, sx, START_Y_SCORE, Accuracy_text);
return;
}
MIL_DOUBLE Median(vector<MIL_INT> scores)
{
MIL_INT size = scores.size();
if(size == 0)
return 0.0;
sort(scores.begin(), scores.end());
if(size % 2 == 0)
return MIL_DOUBLE(scores[size / 2 - 1] + scores[size / 2]) / 2;
return MIL_DOUBLE(scores[size / 2]);
}
vector<charBox> BuildBboxDict(MIL_ID img,
vector<MIL_INT> &MilBoxMinX,
vector<MIL_INT> &MilBoxMaxX,
vector<MIL_INT> &MilBoxMinY,
vector<MIL_INT> &MilBoxMaxY)
{
vector<charBox> charDict;
vector<MIL_INT> wBox, hBox;
MIL_INT max_margin_pix;
MIL_INT boxes = MilBoxMinX.size();
MIL_INT imgSy = MbufInquire(img, M_SIZE_Y, M_NULL);
MIL_INT bboxBaseline = 0;
MIL_INT bboxTopline = imgSy;
for(MIL_INT ibox = 0; ibox < boxes; ibox++)
{
bboxBaseline = max(bboxBaseline, MilBoxMaxY[ibox]);
bboxTopline = min(bboxTopline, MilBoxMinY[ibox]);
hBox.push_back(MilBoxMaxY[ibox] - MilBoxMinY[ibox]);
wBox.push_back(MilBoxMaxX[ibox] - MilBoxMinX[ibox]);
}
MIL_DOUBLE h_med = Median(hBox);
for(MIL_INT ibox = 0; ibox < boxes; ibox++)
{
MIL_BOOL BREAK_IT = false;
MIL_INT w_margin_pix = max(2, int(0.5 + (wBox[ibox]) * NORMALIZE_CHAR_WINDOW_MARGIN / 200.0));
MIL_INT h_margin_pix = max(2, int(0.5 + (hBox[ibox]) * NORMALIZE_CHAR_WINDOW_MARGIN / 200.0));
max_margin_pix = max(w_margin_pix, h_margin_pix);
if(hBox[ibox] < 0.5 * h_med)
{
MilBoxMaxY[ibox] = bboxBaseline;
MilBoxMinY[ibox] = bboxTopline;
}
else
{
MilBoxMaxY[ibox] = min(MilBoxMaxY[ibox] + max_margin_pix, imgSy);
MilBoxMinY[ibox] = max(MilBoxMinY[ibox] - max_margin_pix, MIL_INT(0));
}
}
vector<MIL_INT> box_margin;
for(MIL_INT ibox = 0; ibox < boxes; ibox++)
{
MIL_INT lmargin = max_margin_pix;
MIL_INT rmargin = max_margin_pix;
if(ibox > 0)
lmargin = min(max_margin_pix, MilBoxMinX[ibox] - MilBoxMaxX[ibox - 1]);
if(ibox < boxes - 1)
rmargin = min(max_margin_pix, MilBoxMinX[ibox + 1] - MilBoxMaxX[ibox]);
MIL_INT cur_margin = min(lmargin, rmargin);
box_margin.push_back(cur_margin);
}
for(MIL_INT ibox = 0; ibox < boxes; ibox++)
{
MilBoxMinX[ibox] -= box_margin[ibox];
MilBoxMaxX[ibox] += box_margin[ibox];
}
for(MIL_INT ibox = 0; ibox < boxes; ibox++)
{
charBox new_char;
new_char.BBoxStx = MilBoxMinX[ibox];
new_char.BBoxSty = MilBoxMinY[ibox];
new_char.BBoxEnx = MilBoxMaxX[ibox];
new_char.BBoxEny = MilBoxMaxY[ibox];
charDict.push_back(new_char);
}
return charDict;
}
void GetBbox(MIL_ID MilSystem,
MIL_ID MilImage,
MIL_ID MilMimCtx,
MIL_ID MilBlobCtx,
MIL_ID MilBlobRes,
MIL_ID MilAdaptiveCtx,
vector<MIL_INT> &MilBoxMinX,
vector<MIL_INT> &MilBoxMaxX,
vector<MIL_INT> &MilBoxMinY,
vector<MIL_INT> &MilBoxMaxY)
{
MIL_ID MilPrepImg,
MilBinarized,
MilThreshImg;
MIL_INT MilBlobCount;
MbufClone(MilImage, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_IMAGE + M_PROC, M_DEFAULT, &MilPrepImg);
MbufClone(MilImage, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_IMAGE + M_PROC, M_DEFAULT, &MilBinarized);
MbufClone(MilImage, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_DEFAULT, M_IMAGE + M_PROC, M_DEFAULT, &MilThreshImg);
MimConvolve(MilImage, MilPrepImg, MilMimCtx);
MimRemap(M_DEFAULT, MilPrepImg, MilPrepImg, M_FIT_SRC_DATA);
MimOpen(MilPrepImg, MilPrepImg, 1, M_GRAYSCALE);
MimBinarizeAdaptive(MilAdaptiveCtx, MilPrepImg, M_NULL, M_NULL, MilBinarized, MilThreshImg, M_DEFAULT);
MblobCalculate(MilBlobCtx, MilBinarized, M_NULL, MilBlobRes);
MblobSelect(MilBlobRes, M_EXCLUDE, M_AREA, M_LESS, 80, M_NULL);
MblobGetResult(MilBlobRes, M_GENERAL, M_NUMBER + M_TYPE_MIL_INT, &MilBlobCount);
MilBoxMinX.resize(MilBlobCount);
MilBoxMaxX.resize(MilBlobCount);
MilBoxMinY.resize(MilBlobCount);
MilBoxMaxY.resize(MilBlobCount);
MblobGetResult(MilBlobRes, M_INCLUDED_BLOBS, M_BOX_X_MIN, MilBoxMinX);
MblobGetResult(MilBlobRes, M_INCLUDED_BLOBS, M_BOX_X_MAX, MilBoxMaxX);
MblobGetResult(MilBlobRes, M_INCLUDED_BLOBS, M_BOX_Y_MIN, MilBoxMinY);
MblobGetResult(MilBlobRes, M_INCLUDED_BLOBS, M_BOX_Y_MAX, MilBoxMaxY);
MbufFree(MilPrepImg);
MbufFree(MilBinarized);
MbufFree(MilThreshImg);
}
void Saturate(MIL_INT &sx,
MIL_INT &sy,
MIL_INT &ex,
MIL_INT &ey,
MIL_INT w,
MIL_INT h)
{
sx = min(w, max(sx, MIL_INT(0)));
sy = min(h, max(sy, MIL_INT(0)));
ex = min(w, max(ex, MIL_INT(0)));
ey = min(h, max(ey, MIL_INT(0)));
return;
}
void GetNormalizedChar(MIL_ID MilSystem,
MIL_ID MilClassCtx,
MIL_ID MilClassRes,
MIL_ID MilImage,
MIL_ID MilDestImage,
MIL_INT StartX,
MIL_INT StartY,
MIL_INT EndX,
MIL_INT EndY)
{
MIL_ID MilImgChild,
MilDestChild;
MIL_INT ImageSizeX = MbufInquire(MilImage, M_SIZE_X, M_NULL);
MIL_INT ImageSizeY = MbufInquire(MilImage, M_SIZE_Y, M_NULL);
MIL_INT SizeX = EndX - StartX;
MIL_INT SizeY = EndY - StartY;
MIL_DOUBLE r = min((double(NORMALIZE_CHAR_WINDOW_SIZE) / double(SizeX)), (double(NORMALIZE_CHAR_WINDOW_SIZE) / double(SizeY)));
Saturate(StartX, StartY, EndX, EndY, ImageSizeX, ImageSizeY);
MbufChild2d(MilImage, StartX, StartY, SizeX, SizeY, &MilImgChild);
MbufClear(MilDestImage, M_COLOR_WHITE);
MbufChild2d(MilDestImage,
MIL_INT((NORMALIZE_CHAR_WINDOW_SIZE - SizeX * r) / 2) + NORMALIZE_CHAR_WINDOW_MARGIN,
MIL_INT((NORMALIZE_CHAR_WINDOW_SIZE - SizeY * r) / 2) + NORMALIZE_CHAR_WINDOW_MARGIN,
MIL_INT(SizeX * r),
MIL_INT(SizeY * r),
&MilDestChild);
MimResize(MilImgChild, MilDestChild, M_FILL_DESTINATION, M_FILL_DESTINATION, M_BICUBIC);
MbufFree(MilImgChild);
MbufFree(MilDestChild);
return;
}