#ifdef PC_SIM
#include <mem.h>
#endif
#ifdef TARGET_PC
#include <mem.h>
#endif
#include <math.h>
#include "SideFaceShapeMatching.h"
#include "cpb_module.h"

//---------------------------------------------------------------------------
// 이 모듈 내에서만 사용되는 static 함수의 선언.
// static 선언은 컴파일러의 최적화에 도움을 준다.
//---------------------------------------------------------------------------
static void calcSideWallMaxMatchingValue(int startX, int endX, int startY, int endY, int SideAngle, int SideShapeMatchingXAngleStRange, int SideShapeMatchingXAngleEdRange, int SideShapeMatchingYAngleStRange, int SideShapeMatchingYAngleEdRange, int *RESTRICT(sideWallMaxMatchingValue));
static void matchSideWallRough(int startX, int endX, int startY, int endY, int SideAngle, int SideShapeMatchingXAngleStRange, int SideShapeMatchingXAngleEdRange, int SideShapeMatchingYAngleStRange, int SideShapeMatchingYAngleEdRange, int maxShiftX, int maxShiftY, int gradientDataThreshold, unsigned char *RESTRICT(tempGradientImage), TTabletMatchingResult &tempTabletMatchingResult);
static void matchSideWallFinal(int startX, int endX, int startY, int endY, int SideAngle, int SideShapeMatchingXAngleStRange, int SideShapeMatchingXAngleEdRange, int SideShapeMatchingYAngleStRange, int SideShapeMatchingYAngleEdRange, int gradientDataThreshold, unsigned char *RESTRICT(tempGradientImage), TTabletMatchingResult &tempTabletMatchingResult, TTabletMatchingResult &TabletMatchingResult);
//---------------------------------------------------------------------------
void SideFaceShapeMatchingForEachCase(int option, int cameraIndex)
{
	int tAngleX, tAngleY;
	int i, j, m, x, y;
	int maxValue;
	int shiftX, shiftY;
	int tempX, tempY;
	int tempX2, tempY2;
	int tempShiftX, tempShiftY;
	int beta;
	int gamma;
	int delta;
	int tempCount;
	int totalCount;
	int angle1Range;
	int angle2Range;
	int tempAddress;
	int startX, endX, startY, endY;
	int inspectionAreaStartX, inspectionAreaEndX;
#ifdef PC_SIM
	int minY = 480;
	int maxY = 0;
#endif
	int histo[3][256];
	int limitInspectionStartX, limitInspectionEndX;
	int binarySW;
	int normalCaptureSW1, normalCaptureSW2;
	int Space;
	int maxShiftX;
	int maxShiftY;
	int gradientDataThreshold;
	short tempSideShapeEdgePointTop[520 * 2];
	short tempSideShapeEdgePointBottom[520 * 2];
	int gradValue;
	int topLinePixelShift[520];
	int tempLinePixelShift[520];
	int SideAngle;
	int SideWallGradValue;
	int k;
	int sideWallSlope;
	TTabletMatchingResult tempTabletMatchingResult;
	int topLineMatchingValue;
	int bottomLineMatchingValue;
	int sideWallMaxMatchingValue;
	int n;
	int width, height;
	int edgeAreaShiftX;
	int sideWallMatchingXAngle;
	int sideWallMatchingYAngle;
	int sideWallMatchingShiftX;
	int sideWallMatchingShiftY;
	int SideShapeMatchingXAngleStRange;
	int SideShapeMatchingXAngleEdRange;
	int SideShapeMatchingYAngleStRange;
	int SideShapeMatchingYAngleEdRange;
	int maxAdjustLinePosSize;
	int startN, endN;
	int outofAreaSW;
	int startM, endM;
	int startShiftN;
	int colorValue[3];
	int expectedBottomLineColor;
	int multiLayerMinThickness;
	int value;
	unsigned char *tabletArea;
	unsigned char *expectedTopLineGrdImage;
	unsigned char *multiLayerBinaryArea;
	unsigned char *colorDiffArea;
	unsigned char *TabletShellInfoImage;
	unsigned char *SettedTabletBoundaryArea;
	unsigned char tempGradientImage[MAX_IMAGE_HEIGHT * MAX_IMAGE_WIDTH];
	int tryCount;
	int shellGradSum[MAX_IMAGE_HEIGHT];
	int maxShellGroupIdx;
	TTabletMultiLayerBoundaryInfo TabletMultiLayerBoundaryInfo[MAX_VALID_TABLET_LAYER_COUNT];
	int layerMaxHeightPosition;
	int multiLayerMaskSize;
	int layerBounaryPositionInfo[MAX_VALID_TABLET_LAYER_COUNT][MAX_IMAGE_WIDTH];
	int multiLayerArea1Color;
	int multiLayerArea2Color;
	int multiLayerArea1Count;
	int multiLayerArea2Count;
	int validLayerLabelN[2];
	int stdValue;
	int stdUpperValue;
	int stdLowerValue;
	int tripleLayerUpperBoundary, tripleLayerLowerBoundary;
	int tabletSideBoundary[MAX_IMAGE_HEIGHT];
	int r;
	int srcValue;
	int leftSW, rightSW;
	int topSW, bottomSW;
	int colorDiffCount;
	int protoTopLineXAngle;
	int protoTopLineYAngle;
	int secTopLineXAngle, secTopLineYAngle;
	int pointDistance;
	unsigned int minLength;
	unsigned short SideShapeEdgePointCount, SideShapeEdgePointCountBottom;
	unsigned short *SideShapeEdgePoint, *SideShapeEdgePointBottom;

	if (TabletCharacter.tabletLayerCount > MAX_VALID_TABLET_LAYER_COUNT)
	{
		DefectSW = 2;
		return;
	}

	if (cameraIndex == SD1_2D_SIDE_FACE_M45_CAMERA_INDEX ||
		cameraIndex == SD2_2D_SIDE_FACE_M45_CAMERA_INDEX ||
		cameraIndex == SD1_2D_OTHER_SIDE_FACE_M45_CAMERA_INDEX ||
		cameraIndex == SD2_2D_OTHER_SIDE_FACE_M45_CAMERA_INDEX)
	{
		SideAngle = MINUS_45_ANGLE_CAM;
	}
	else if (cameraIndex == SD1_2D_SIDE_FACE_00_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_SIDE_FACE_00_CAMERA_INDEX ||
			 cameraIndex == SD1_2D_OTHER_SIDE_FACE_00_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_OTHER_SIDE_FACE_00_CAMERA_INDEX)
	{
		SideAngle = ZERO_ANGLE_CAM;
	}
	else if (cameraIndex == SD1_2D_SIDE_FACE_P45_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_SIDE_FACE_P45_CAMERA_INDEX ||
			 cameraIndex == SD1_2D_OTHER_SIDE_FACE_P45_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_OTHER_SIDE_FACE_P45_CAMERA_INDEX)
	{
		SideAngle = PLUS_45_ANGLE_CAM;
	}

	startX = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS];
	endX = tabletAreaInfo[AREA_INFO_END_X_ADDRESS];
	startY = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS];
	endY = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS];

	multiLayerBinaryArea = TempImage;
	SettedTabletBoundaryArea = TempImage2;
	TabletShellInfoImage = TempImage3;
	colorDiffArea = BrightPixelNeighborPrint;

	memcpy(tempGradientImage, GradientImage, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	if (TabletCharacter.tabletLayerCount > 1)
	{
		memset(TabletMultiLayerBoundaryInfo, 0, sizeof(TTabletMultiLayerBoundaryInfo) * MAX_VALID_TABLET_LAYER_COUNT);

		// 다층정 경계에 바닥부분으로 매칭되는 것을 방지하기 위해 gradient 값이 발생되는 영역의 색상 유사도 확인
		expectedBottomLineColor = tempCount = 0;
		multiLayerMinThickness = 8;

		for (x = startX; x < endX; x++)
		{
			for (y = endY; y > startY ; --y)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (realBinaryArea[tempAddress] && realBinaryArea[tempAddress + multiLayerMinThickness * MAX_IMAGE_WIDTH] == 0)
				{
					expectedBottomLineColor += OneColorImage[tempAddress];
					tempCount++;
					break;
				}
			}
		}

		if (tempCount)
		{
			expectedBottomLineColor /= tempCount;
		}
		else
		{
			DefectSW = 2;
			return;
		}

		// 밝은 곳의 그레디언트를 낮춰서 밝기에 의한 그레디언트 증폭효과를 제거.
		for (y = startY; y < endY; y++)
		{
			for (x = startX; x < endX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (ShapeBinaryImage[tempAddress] && GradientImage[tempAddress])
				{
					value = OneColorImage[tempAddress];
					if (value > expectedBottomLineColor)
					{
						tempGradientImage[tempAddress] = GradientImage[tempAddress] * expectedBottomLineColor / value;
					}
				}
			}
		}
	}

	tempTabletMatchingResult.TabletSideShapeZTotion = TabletMatchingResult.TabletSideShapeZTotion = MAX_ANGLE3_RANGE;

	beta = TabletGradeData.side_Edge_Bottom_Neighbor_Intensity;
	gamma = TabletGradeData.SideFaceEdgeAreaSize * 2;
	delta = TabletGradeData.SideFaceEdgeUnInsAreaSize;

	if (TabletCharacter.shape == OBLONG || TabletCharacter.shape == OVAL)
	{
		angle1Range = 20; // MAX_ANGLE1_RANGE;
		angle2Range = MAX_ANGLE2_RANGE / 2;
	}
	else
	{
		angle1Range = MAX_ANGLE1_RANGE / 2;
		if (TabletGradeData.SideCamMathingOption == 2)
		{
			angle2Range = MAX_ANGLE2_RANGE / 1;
		}
		else if (TabletGradeData.SideCamMathingOption == 1)
		{
			angle2Range = MAX_ANGLE2_RANGE / 2; // 풍림 CSS 16번 카메라 측면 엣지를 못찾는 문제를 수정하기 위해 4로 나누지 않고 2로 나눈다.
		}
		else
		{
			// default set
			angle2Range = MAX_ANGLE2_RANGE / 4;
		}
	}

	if (TabletCharacter.plateInformation == PLATE)
	{
		angle1Range = MAX_ANGLE1_RANGE / 2;
		angle2Range = MAX_ANGLE2_RANGE / 2;
	}	

	SideShapeMatchingXAngleStRange = MAX_ANGLE1_RANGE - angle1Range;
	SideShapeMatchingXAngleEdRange = MAX_ANGLE1_RANGE + angle1Range;
	SideShapeMatchingYAngleStRange = MAX_ANGLE2_RANGE - angle2Range;
	SideShapeMatchingYAngleEdRange = MAX_ANGLE2_RANGE + angle2Range;

	if (ProcessingModeGlobal == INSPECTION_MODE)
	{
		if (TabletCharacter.shape == OBLONG || TabletCharacter.shape == OVAL)
		{
			maxShiftX = 12;
			maxShiftY = 12;
		}
		else if (TabletCharacter.shape == ETC)
		{
			maxShiftX = 12;
			maxShiftY = 12;
		}
		else
		{
			maxShiftX = 12;
			maxShiftY = 8;
		}
	}
	else
	{
		maxShiftX = 48;
		maxShiftY = 20;
	}

	// 각도만 대충 매칭하여 그레디언트 최대값만 구한다. 별로 오래걸리지 않는다. PC기준 0.06ms
	sideWallMaxMatchingValue = -1;
	calcSideWallMaxMatchingValue(startX, endX, startY, endY, SideAngle, SideShapeMatchingXAngleStRange, SideShapeMatchingXAngleEdRange, SideShapeMatchingYAngleStRange, SideShapeMatchingYAngleEdRange, &sideWallMaxMatchingValue);

	memcpy(&tempTabletMatchingResult, &TabletMatchingResult, sizeof(TTabletMatchingResult));	
	gradientDataThreshold = sideWallMaxMatchingValue * 50 / 100;

	// 각도와 shift를 옮기며 매칭. 꽤 오래 걸린다. PC기준 2.6ms. 전체 측면 프로세싱 타임의 10%정도가 여기에 요소된다.
	matchSideWallRough(startX, endX, startY, endY, SideAngle, SideShapeMatchingXAngleStRange, SideShapeMatchingXAngleEdRange, SideShapeMatchingYAngleStRange, SideShapeMatchingYAngleEdRange, maxShiftX, maxShiftY, gradientDataThreshold, tempGradientImage, tempTabletMatchingResult);

	memcpy(&TabletMatchingResult, &tempTabletMatchingResult, sizeof(TTabletMatchingResult));

	// 정밀 매칭. PC기준으로 5ms이상 소요. 전체 측면 프로세싱 시간의 20%정도가 여기서 소요된다.
	matchSideWallFinal(startX, endX, startY, endY, SideAngle, SideShapeMatchingXAngleStRange, SideShapeMatchingXAngleEdRange, SideShapeMatchingYAngleStRange, SideShapeMatchingYAngleEdRange, gradientDataThreshold, tempGradientImage, tempTabletMatchingResult, TabletMatchingResult);

	expectedTopLineGrdImage = PlanariztionThreeDEdgeImage;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			GradientImage[tempAddress] = max(GradientImage[tempAddress], expectedTopLineGrdImage[tempAddress]);
		}
	}

	// 측면 매칭. 별로 오래걸리지 않는다.
	maxValue = 0;
	for (tAngleY = TabletMatchingResult.TabletSideShapeYTotion - 4; tAngleY <= TabletMatchingResult.TabletSideShapeYTotion + 4; tAngleY += 2)
	{
		if (tAngleY < 0 || tAngleY >= MAX_ANGLE2_RANGE * 2 + 1)
			continue;
		for (tAngleX = TabletMatchingResult.TabletSideShapeXTotion - 4; tAngleX <= TabletMatchingResult.TabletSideShapeXTotion + 4; tAngleX += 2)
		{
			if (tAngleX < 0 || tAngleX >= MAX_ANGLE1_RANGE * 2 + 1)
				continue;

			if (SideAngle == PLUS_45_ANGLE_CAM)
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountRight;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointRight;
			}
			else
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountLeft;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointLeft;
			}

			for (i = TabletMatchingResult.TabletSideShapeShiftY - 8; i <= TabletMatchingResult.TabletSideShapeShiftY + 8; i += 2)
			{
				for (j = TabletMatchingResult.TabletSideShapeShiftX - 8; j <= TabletMatchingResult.TabletSideShapeShiftX + 8; j += 2)
				{
					tempCount = 0;
					for (m = 0; m < SideShapeEdgePointCount; m += 2)
					{
						tempX = SideShapeEdgePoint[m * 2] + j;
						tempY = SideShapeEdgePoint[m * 2 + 1] + i;

						//if (tempX > startX && tempX < endX && tempY > startY && tempY < endY) // 읽기만하므로 속도를 위해서 범위 무시.
						{
							tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

							tempCount += GradientImage[tempAddress];
						}
					}

					if (maxValue < tempCount)
					{
						maxValue = tempCount;

						sideWallMatchingXAngle = tAngleX;
						sideWallMatchingYAngle = tAngleY;
						sideWallMatchingShiftX = j;
						sideWallMatchingShiftY = i;
					}
				}
			}
		}
	}

	// 이제 매칭 각도가 정해졌으므로 하단 포인트들의 배열 주소를 고정한다.
	SideShapeEdgePointCountBottom = SideData[SideAngle].SideShapeData[TabletMatchingResult.TabletSideShapeYTotion][TabletMatchingResult.TabletSideShapeXTotion].SideShapeEdgePointCountBottom;
	SideShapeEdgePointBottom = SideData[SideAngle].SideShapeData[TabletMatchingResult.TabletSideShapeYTotion][TabletMatchingResult.TabletSideShapeXTotion].SideShapeEdgePointBottom;

	// 하단 매칭. 별로 오래 걸리지 않는다.
	bottomLineMatchingValue = 0;
	shiftX = shiftY = 0;
	for (i = -4; i <= 4; i += 2)
	{
		for (j = -6; j <= 6; j += 2)
		{
			tempCount = 0;
			for (m = 0; m < SideShapeEdgePointCountBottom; m++)
			{
				tempX = SideShapeEdgePointBottom[m * 2] + TabletMatchingResult.TabletSideShapeShiftX + i;
				tempY = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + j;

				//if (tempX > startX && tempX < endX && tempY > startY && tempY < endY) // 읽기만하므로 속도를 위해서 범위 무시.
				{
					tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

					tempCount += (tempGradientImage[tempAddress] + tempGradientImage[tempAddress + MAX_IMAGE_WIDTH] + tempGradientImage[tempAddress - MAX_IMAGE_WIDTH]);
				}
			}

			if (bottomLineMatchingValue < tempCount)
			{
				bottomLineMatchingValue = tempCount;

				shiftX = i;
				shiftY = j;
			}
		}
	}

	TabletMatchingResult.TabletSideShapeShiftX += shiftX;
	TabletMatchingResult.TabletSideShapeShiftY += shiftY;

	if (option == CASE1)
	{
		tempX = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startX1 + sideWallMatchingShiftX;
		tempY = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startY1 + sideWallMatchingShiftY;
		tempX2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startX2 + sideWallMatchingShiftX;
		tempY2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startY2 + sideWallMatchingShiftY;

		MaskingForLeftEdgeLine(tempX, tempY, tempX2, tempY2, cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 1, 0);
	}
	if (option == CASE2)
	{
		tempX = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endX1 + sideWallMatchingShiftX;
		tempY = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endY1 + sideWallMatchingShiftY;
		tempX2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endX2 + sideWallMatchingShiftX;
		tempY2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endY2 + sideWallMatchingShiftY;

		MaskingForRightEdgeLine(tempX, tempY, tempX2, tempY2, cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 1, 0);
	}

	memset(tabletSideBoundary, 0, sizeof(int) * MAX_IMAGE_HEIGHT);
	if (option == CASE1)
	{
		for (y = startY; y < endY; y++)
		{
			for (x = endX - 1; x > startX; x--)
			{
				if (InspectionArea[MAX_IMAGE_WIDTH * y + x])
				{
					tabletSideBoundary[y] = x;
					break;
				}
			}
		}
	}
	else
	{
		for (y = startY; y < endY; y++)
		{
			for (x = startX; x < endX; x++)
			{
				if (InspectionArea[MAX_IMAGE_WIDTH * y + x])
				{
					tabletSideBoundary[y] = x;
					break;
				}
			}
		}
	}

	memset(InspectionArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	if (SideAngle == ZERO_ANGLE_CAM)
	{
		tabletArea = ShapeBinaryImage;
	}
	else
	{
		tabletArea = realBinaryArea;
	}

	topLineMatchingValue = 0;
	startShiftN = 0;

	for (n = 0; n < MAX_TABLET_HEIGHT; n += 2)
	{
		shiftX = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].shiftTopLineX[n];
		shiftY = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].shiftTopLineY[n];

		if (shiftY)
		{
			outofAreaSW = 0;

			startM = 0;
			endM = SideShapeEdgePointCountBottom / 4;
			for (m = startM; m < endM; m += 4)
			{
				tempX = SideShapeEdgePointBottom[m * 2] + TabletMatchingResult.TabletSideShapeShiftX + shiftX;
				tempY = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + shiftY;

				if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
				{
					tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

					if (tabletSideBoundary[tempY])
					{
						if (option == CASE1 && tempX > tabletSideBoundary[tempY])
						{
							if (tabletArea[tempAddress] == 0)
							{
								outofAreaSW = 1;
								break;
							}
						}
						else if (option == CASE2 && tempX < tabletSideBoundary[tempY])
						{
							if (tabletArea[tempAddress] == 0)
							{
								outofAreaSW = 1;
								break;
							}
						}
					}
					else if (tabletArea[tempAddress] == 0)
					{
						outofAreaSW = 1;
						break;
					}
				}
				else
				{
					outofAreaSW = 1;
					break;
				}
			}

			startM = SideShapeEdgePointCountBottom - 1;
			endM = SideShapeEdgePointCountBottom - SideShapeEdgePointCountBottom / 4;
			for (m = startM; m >= endM; m -= 4)
			{
				tempX = SideShapeEdgePointBottom[m * 2] + TabletMatchingResult.TabletSideShapeShiftX + shiftX;
				tempY = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + shiftY;

				if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
				{
					tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

					if (tabletSideBoundary[tempY])
					{
						if (option == CASE1 && tempX > tabletSideBoundary[tempY])
						{
							if (tabletArea[tempAddress] == 0)
							{
								outofAreaSW = 1;
								break;
							}
						}
						else if (option == CASE2 && tempX < tabletSideBoundary[tempY])
						{
							if (tabletArea[tempAddress] == 0)
							{
								outofAreaSW = 1;
								break;
							}
						}
					}
					else if (tabletArea[tempAddress] == 0)
					{
						outofAreaSW = 1;
						break;
					}
				}
				else
				{
					outofAreaSW = 1;
					break;
				}
			}

			if (outofAreaSW == 0)
			{
				startShiftN = n;
				break;
			}
		}
	}

	if (option == CASE1)
	{
		tempX = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startX1 + sideWallMatchingShiftX;
		tempY = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startY1 + sideWallMatchingShiftY;
		tempX2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startX2 + sideWallMatchingShiftX;
		tempY2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].startY2 + sideWallMatchingShiftY;

		MaskingForLeftEdgeLine(tempX, tempY, tempX2, tempY2, cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 1);
	}
	if (option == CASE2)
	{
		tempX = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endX1 + sideWallMatchingShiftX;
		tempY = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endY1 + sideWallMatchingShiftY;
		tempX2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endX2 + sideWallMatchingShiftX;
		tempY2 = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].endY2 + sideWallMatchingShiftY;

		MaskingForRightEdgeLine(tempX, tempY, tempX2, tempY2, cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 1);
	}

	TabletSlopeInfoStartPoint[0] = tempX;
	TabletSlopeInfoStartPoint[1] = tempY;

	TabletSlopeInfoEndPoint[0] = tempX2;
	TabletSlopeInfoEndPoint[1] = tempY2;

	// 상단 매칭 전에 다층정 경계처리
	memset(SettedTabletBoundaryArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	if (TabletCharacter.tabletLayerCount > 1)
	{
		memset(tempGradientImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		memset(multiLayerBinaryArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

		shiftX = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].shiftTopLineX[startShiftN];
		shiftY = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].shiftTopLineY[startShiftN];
		for (m = 0; m < SideShapeEdgePointCountBottom; m++)
		{
			tempX = SideShapeEdgePointBottom[m * 2 + 0] + TabletMatchingResult.TabletSideShapeShiftX;
			tempY = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY;

			for (j = tempY - 8; j <= tempY + 8; j++)
			{
				for (i = tempX - 2; i <= tempX + 2; i++)
				{
					tempAddress = MAX_IMAGE_WIDTH * j + i;
					if (i > startX && i < endX && j > startY && j < endY)
					{
						SettedTabletBoundaryArea[tempAddress] = 1;
					}
				}
			}

			tempX2 = SideShapeEdgePointBottom[m * 2 + 0] + TabletMatchingResult.TabletSideShapeShiftX + shiftX;
			tempY2 = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + shiftY;

			if (tempY2 - tempY)
			{
				for (y = tempY - 4; y >= tempY2; y--) // - 4 바텀으로 매칭된 영역은 제외
				{
					x = (tempX - tempX2) * (y - tempY) / (tempY - tempY2) + tempX;

					for (j = y - 2; j <= y + 2; j++)
					{
						for (i = x - 2; i <= x + 2; i++)
						{
							if (i > startX && i < endX && j > startY && j < endY)
							{
								if (ShapeBinaryImage[MAX_IMAGE_WIDTH * j + i])
								{
									multiLayerBinaryArea[MAX_IMAGE_WIDTH * j + i] = 1;
								}
							}
						}
					}
				}
			}
		}

		// filtering color impulse
		memset(colorDiffArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		for (y = startY; y < endY; y++)
		{
			for (x = startX; x < endX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (tabletArea[tempAddress] && !InspectionArea[tempAddress])
				{
					srcValue = OneColorImage[tempAddress];
					colorDiffCount = 0;
					leftSW = rightSW = topSW = bottomSW = 0;
					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress - r * MAX_IMAGE_WIDTH] - srcValue > 8)
						{
							colorDiffCount++;
							topSW = 1;
							break;
						}
					}

					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress + r * MAX_IMAGE_WIDTH] - srcValue > 8)
						{
							colorDiffCount++;
							bottomSW = 1;
							break;
						}
					}

					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress - r] - srcValue > 8)
						{
							colorDiffCount++;
							leftSW = 1;
							break;
						}
					}

					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress + r] - srcValue > 8)
						{
							colorDiffCount++;
							rightSW = 1;
							break;
						}
					}

					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress - r * MAX_IMAGE_WIDTH - r] - srcValue > 8)
						{
							colorDiffCount++;
							leftSW = 1;
							break;
						}
					}

					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress + r * MAX_IMAGE_WIDTH - r] - srcValue > 8)
						{
							colorDiffCount++;
							leftSW = 1;
							break;
						}
					}

					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress - r * MAX_IMAGE_WIDTH + r] - srcValue > 8)
						{
							colorDiffCount++;
							rightSW = 1;
							break;
						}
					}

					for (r = 8; r >= 2; r -= 2)
					{
						if (OneColorImage[tempAddress + r * MAX_IMAGE_WIDTH + r] - srcValue > 8)
						{
							colorDiffCount++;
							rightSW = 1;
							break;
						}
					}

					if (colorDiffCount >= 5 && topSW && bottomSW && leftSW && rightSW)
					{
						colorDiffArea[tempAddress] = 1;
					}
				}
			}
		}

		// get gradient info
		for (y = startY; y < endY; y++)
		{
			for (x = startX; x < endX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (multiLayerBinaryArea[tempAddress] && !SettedTabletBoundaryArea[tempAddress] && !InspectionArea[tempAddress])
				{
					value = 0;
					for (r = 6; r >= 2; r -= 2)
					{
						if (!colorDiffArea[tempAddress - r * MAX_IMAGE_WIDTH])
						{
							for (m = 6; m >= 2; m -= 2)
							{
								if (!colorDiffArea[tempAddress + m * MAX_IMAGE_WIDTH])
								{
									value += abs(OneColorImage[tempAddress - r * MAX_IMAGE_WIDTH] - OneColorImage[tempAddress + m * MAX_IMAGE_WIDTH]);
								}
							}
						}
					}

					tempGradientImage[tempAddress] = min(255, value);
				}
			}
		}

		memset(TabletShellInfoImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		for (m = 0; m < SideShapeEdgePointCountBottom; m++)
		{
			tempX = SideShapeEdgePointBottom[m * 2 + 0] + TabletMatchingResult.TabletSideShapeShiftX;
			tempY = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY;

			tempX2 = SideShapeEdgePointBottom[m * 2 + 0] + TabletMatchingResult.TabletSideShapeShiftX + shiftX;
			tempY2 = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + shiftY;

			if (tempY2 - tempY)
			{
				tempCount = 1;
				for (y = tempY - 4; y >= tempY2; y--) // - 4 바텀으로 매칭된 영역은 제외
				{
					x = (tempX - tempX2) * (y - tempY) / (tempY - tempY2) + tempX;

					if (x > startX && x < endX && y > startY && y < endY)
					{
						tempAddress = MAX_IMAGE_WIDTH * y + x;
						TabletShellInfoImage[tempAddress] = tempCount;
						tempCount++;
					}
				}
			}
		}

		layerMaxHeightPosition = 0;
		for (tryCount = 0; tryCount < TabletCharacter.tabletLayerCount; tryCount++)
		{
			memset(shellGradSum, 0, sizeof(int) * MAX_IMAGE_HEIGHT);
			for (y = startY; y < endY; y++)
			{
				for (x = startX; x < endX; x++)
				{
					tempAddress = MAX_IMAGE_WIDTH * y + x;

					if (TabletShellInfoImage[tempAddress] && !SettedTabletBoundaryArea[tempAddress])
					{
						value = TabletShellInfoImage[tempAddress];

						shellGradSum[value] += tempGradientImage[tempAddress];
					}
				}
			}

			maxValue = 0;
			for (m = 3; m < MAX_IMAGE_HEIGHT - 3; m++)
			{
				value = shellGradSum[m - 2] + shellGradSum[m - 1] + shellGradSum[m] + shellGradSum[m + 1] + shellGradSum[m + 2];

				if (maxValue < value)
				{
					maxValue = value;
					maxShellGroupIdx = m;
				}
			}

			tempCount = 0;
			for (y = startY; y < endY; y++)
			{
				for (x = startX; x < endX; x++)
				{
					tempAddress = MAX_IMAGE_WIDTH * y + x;

					if (TabletShellInfoImage[tempAddress] == maxShellGroupIdx)
					{
						if (tempCount < 520)
						{
							TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfo[tempCount * 2 + 0] = x;
							TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfo[tempCount * 2 + 1] = y;
							tempCount++;
						}

						for (j = y - 8; j <= y + 8; j++)
						{
							for (i = x - 2; i <= x + 2; i++)
							{
								if (i > startX && i < endX && j > startY && j < endY)
								{
									tempAddress = MAX_IMAGE_WIDTH * j + i;
									SettedTabletBoundaryArea[tempAddress] = 1;
								}
							}
						}
					}
				}
			}
			TabletMultiLayerBoundaryInfo[tryCount].labelN = maxShellGroupIdx;
			TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfoCount = tempCount;

			if (layerMaxHeightPosition < TabletMultiLayerBoundaryInfo[tryCount].labelN)
			{
				layerMaxHeightPosition = TabletMultiLayerBoundaryInfo[tryCount].labelN;
			}
		}

		memset(SettedTabletBoundaryArea, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		for (tryCount = 0; tryCount < TabletCharacter.tabletLayerCount; tryCount++)
		{
			if (TabletMultiLayerBoundaryInfo[tryCount].labelN != layerMaxHeightPosition)
			{
				for (m = 0; m < TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfoCount; m++)
				{
					tempX = TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfo[m * 2 + 0];
					tempY = TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfo[m * 2 + 1];

					for (j = tempY - 2; j <= tempY + 2; j++)
					{
						for (i = tempX - 2; i <= tempX + 2; i++)
						{
							if (i > startX && i < endX && j > startY && j < endY)
							{
								tempAddress = MAX_IMAGE_WIDTH * j + i;
								SettedTabletBoundaryArea[tempAddress] = 1;
							}
						}
					}
				}
			}
		}
	}

	// 탑 라인을 제대로 찾기 위하여 밝아지는 정도를 위쪽으로 누적시키는 작업을 한다. 시간이 꽤 걸린다. PC기준으로 1ms 정도 소요.
	memset(tempGradientImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;
			if (GradientImage[tempAddress] && realBinaryArea[tempAddress])
			{
				srcValue = OneColorImage[tempAddress];

				maxValue = 0;
				//for (r = 20; r >= 4; r -= 4) // 범위가 너무 크면 빛 반사가 심한 작은 정제에서 Top 라인이 빛 반사 영역으로 밀려 올라가는 문제가 생긴다.
				for (r = 10; r >= 2; r -= 2)
				{
					if (y - r > startY)
					{
						if (realBinaryArea[tempAddress - r * MAX_IMAGE_WIDTH])
						{
							value = OneColorImage[tempAddress - r * MAX_IMAGE_WIDTH] - srcValue;
							if (value > 0)
							{
								maxValue += value; // y축 방향으로 올라가면서 값을 누적한다. 이렇게하면 y위치 이후에 값이 급격하게 변하는 것을 추출할 수 있다.
							}
						}
					}
				}

				if (maxValue > 0)
				{
					tempGradientImage[tempAddress] = min(255, maxValue);
				}
			}
		}
	}

	for (n = startShiftN; n < MAX_TABLET_HEIGHT; n++)
	{
		shiftX = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].shiftTopLineX[n];
		shiftY = SideData[SideAngle].SideShapeData[sideWallMatchingYAngle][sideWallMatchingXAngle].shiftTopLineY[n];

		if (shiftY)
		{
			for (k = -6; k <= 6; k += 2)
			{
				tempCount = 0;
				for (m = 0; m < SideShapeEdgePointCountBottom; m++)
				{
					tempX = SideShapeEdgePointBottom[m * 2] + TabletMatchingResult.TabletSideShapeShiftX + shiftX + k;
					tempY = SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + shiftY;

					if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
					{
						tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

						if (!SettedTabletBoundaryArea[tempAddress])
						{
							tempCount += (tempGradientImage[tempAddress] + tempGradientImage[tempAddress + MAX_IMAGE_WIDTH] + tempGradientImage[tempAddress - MAX_IMAGE_WIDTH]);
						}
					}
				}

				if (topLineMatchingValue < tempCount)
				{
					topLineMatchingValue = tempCount;

					TabletMatchingResult.TabletSideShapeTopLineShiftX = shiftX + k;
					TabletMatchingResult.TabletSideShapeTopLineShiftY = shiftY;
				}
			}
		}
	}

	memcpy(&finalMatchingShapeData, &SideData[SideAngle].SideShapeData[TabletMatchingResult.TabletSideShapeYTotion][TabletMatchingResult.TabletSideShapeXTotion], sizeof(TSideShapeData));

	if (SideAngle == MINUS_45_ANGLE_CAM)
	{
		inspectionAreaStartX = finalMatchingShapeData.startX2 - 15 + TabletMatchingResult.TabletSideShapeShiftX;
		inspectionAreaEndX = (1 * finalMatchingShapeData.startX2 + 6 * finalMatchingShapeData.endX2) / 7 + TabletMatchingResult.TabletSideShapeShiftX;
	}
	else if (SideAngle == ZERO_ANGLE_CAM)
	{
		inspectionAreaStartX = (5 * finalMatchingShapeData.startX2 + 1 * finalMatchingShapeData.endX2) / 6 + TabletMatchingResult.TabletSideShapeShiftX - 5;
		inspectionAreaEndX = (1 * finalMatchingShapeData.startX2 + 5 * finalMatchingShapeData.endX2) / 6 + TabletMatchingResult.TabletSideShapeShiftX + 5;
	}
	else if (SideAngle == PLUS_45_ANGLE_CAM)
	{
		inspectionAreaEndX = finalMatchingShapeData.endX2 + 15 + TabletMatchingResult.TabletSideShapeShiftX;
		inspectionAreaStartX = (6 * finalMatchingShapeData.startX2 + 1 * finalMatchingShapeData.endX2) / 7 + TabletMatchingResult.TabletSideShapeShiftX;
	}

	if (TabletGradeData.ETC_AdjustUnalignedTabletAlgorithm)
	{
		binarySW = 0;
		if (ShapeBinaryImage[MAX_IMAGE_WIDTH * ProtoTabletCenterY + ProtoTabletCenterX])
			binarySW = 1;

		if (binarySW)
		{
			// Binary 영역이 끝나는 지점을 찾음
			normalCaptureSW1 = normalCaptureSW2 = 0;

			limitInspectionStartX = inspectionAreaStartX;
			for (x = ProtoTabletCenterX - 1; x > startX; x--)
			{
				tempAddress = MAX_IMAGE_WIDTH * ProtoTabletCenterY + x;

				if (!ShapeBinaryImage[tempAddress]) // Binary 영역이 아니라면
				{
					limitInspectionStartX = x;
					normalCaptureSW1 = 1;
					break;
				}
			}

			limitInspectionEndX = inspectionAreaEndX;
			for (x = ProtoTabletCenterX + 1; x < endX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * ProtoTabletCenterY + x;

				if (!ShapeBinaryImage[tempAddress]) // Binary 영역이 아니라면
				{
					limitInspectionEndX = x;
					normalCaptureSW2 = 1;
					break;
				}
			}

			// normalCaptureSW1, normalCaptureSW2 -> 이미지가 정상촬영된 경우에만 검사 영역을 제한을 진행한다.
			// 촬영된 이미지가 Start, End 라인에 걸쳐 있는 경우, 정제가 붙어 있는 경우 등에서는 에러가 발생할 수 있다.

			if (normalCaptureSW1 && normalCaptureSW2)
			{
				if (SideAngle == PLUS_45_ANGLE_CAM)
				{
					Space = abs(inspectionAreaEndX - limitInspectionEndX);

					if (inspectionAreaStartX < limitInspectionStartX + Space)
						inspectionAreaStartX = limitInspectionStartX + Space;
				}
				else if (SideAngle == ZERO_ANGLE_CAM)
				{
					Space = abs(inspectionAreaStartX - limitInspectionStartX);

					if (inspectionAreaEndX > limitInspectionEndX - Space)
						inspectionAreaEndX = limitInspectionEndX - Space;
				}
				else if (SideAngle == MINUS_45_ANGLE_CAM)
				{
					Space = abs(inspectionAreaStartX - limitInspectionStartX);

					if (inspectionAreaEndX > limitInspectionEndX - Space)
						inspectionAreaEndX = limitInspectionEndX - Space;
				}
			}
		}
	}

	memset(tempGradientImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;
			if (GradientImage[tempAddress] && realBinaryArea[tempAddress])
			{
				srcValue = OneColorImage[tempAddress + 2 * MAX_IMAGE_WIDTH];

				value = OneColorImage[tempAddress - 2 * MAX_IMAGE_WIDTH] - srcValue;

				if (value > 0)
				{
					tempGradientImage[tempAddress] = value;
				}
			}
		}
	}

	// adjust top line
	memset(topLinePixelShift, 0, sizeof(int) * 520);
	maxAdjustLinePosSize = 6;

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX = finalMatchingShapeData.SideShapeEdgePointBottom[m * 2 + 0] + TabletMatchingResult.TabletSideShapeShiftX + TabletMatchingResult.TabletSideShapeTopLineShiftX;
		tempY = finalMatchingShapeData.SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + TabletMatchingResult.TabletSideShapeTopLineShiftY;

		maxValue = 0;
		for (j = -maxAdjustLinePosSize; j <= maxAdjustLinePosSize; j += 2)
		{
			tempAddress = MAX_IMAGE_WIDTH * (tempY + j) + tempX;

			if (!SettedTabletBoundaryArea[tempAddress])
			{
				gradValue = OneColorImage[tempAddress - 2 * MAX_IMAGE_WIDTH] - OneColorImage[tempAddress + 2 * MAX_IMAGE_WIDTH];

				if (maxValue < gradValue)
				{
					maxValue = gradValue;
					topLinePixelShift[m] = j;
				}
			}
		}
	}

	memset(tempSideShapeEdgePointTop, 0, sizeof(short) * 520 * 2);
	memset(tempSideShapeEdgePointBottom, 0, sizeof(short) * 520 * 2);

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempSideShapeEdgePointTop[m * 2 + 0] = finalMatchingShapeData.SideShapeEdgePointBottom[m * 2 + 0];
		tempSideShapeEdgePointTop[m * 2 + 1] = finalMatchingShapeData.SideShapeEdgePointBottom[m * 2 + 1] + topLinePixelShift[m];
	}

	// topline 2차 검증 (정밀매칭)
	protoTopLineXAngle = TabletMatchingResult.TabletSideShapeXTotion;
	protoTopLineYAngle = TabletMatchingResult.TabletSideShapeYTotion;

	minLength = 0xFFFFFFFF;
	for (tAngleY = protoTopLineYAngle - 4; tAngleY <= protoTopLineYAngle + 4; tAngleY++)
	{
		if (tAngleY < 0 || tAngleY >= MAX_ANGLE2_RANGE * 2 + 1)
			continue;
		for (tAngleX = protoTopLineXAngle - 8; tAngleX <= protoTopLineXAngle + 8; tAngleX++)
		{
			if (tAngleX < 0 || tAngleX >= MAX_ANGLE1_RANGE * 2 + 1)
				continue;

			tempCount = min(SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountBottom, finalMatchingShapeData.SideShapeEdgePointCountBottom); // 원래는 두개 값이 같아야함, 검증된바없으므로 예외처리

			pointDistance = 0;
			for (m = 0; m < tempCount; m++)
			{
				tempX = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointBottom[m * 2 + 0];
				tempY = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointBottom[m * 2 + 1];

				tempX2 = tempSideShapeEdgePointTop[m * 2 + 0];
				tempY2 = tempSideShapeEdgePointTop[m * 2 + 1];

				pointDistance += (tempX - tempX2) * (tempX - tempX2) + (tempY - tempY2) * (tempY - tempY2);
			}

			if (minLength > pointDistance)
			{
				minLength = pointDistance;
				secTopLineXAngle = tAngleX;
				secTopLineYAngle = tAngleY;
			}
		}
	}

	memset(tempSideShapeEdgePointTop, 0, sizeof(short) * 520 * 2);
	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempSideShapeEdgePointTop[m * 2 + 0] = SideData[SideAngle].SideShapeData[secTopLineYAngle][secTopLineXAngle].SideShapeEdgePointBottom[m * 2 + 0] + TabletMatchingResult.TabletSideShapeShiftX + TabletMatchingResult.TabletSideShapeTopLineShiftX;
		tempSideShapeEdgePointTop[m * 2 + 1] = SideData[SideAngle].SideShapeData[secTopLineYAngle][secTopLineXAngle].SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY + TabletMatchingResult.TabletSideShapeTopLineShiftY;
	}

	// adjust top line
	memset(topLinePixelShift, 0, sizeof(int) * 520);
	maxAdjustLinePosSize = 4;

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX = tempSideShapeEdgePointTop[m * 2 + 0];
		tempY = tempSideShapeEdgePointTop[m * 2 + 1];

		maxValue = 0;
		for (j = -maxAdjustLinePosSize; j <= maxAdjustLinePosSize; j++)
		{
			tempAddress = MAX_IMAGE_WIDTH * (tempY + j) + tempX;

			if (!SettedTabletBoundaryArea[tempAddress])
			{
				gradValue = OneColorImage[tempAddress - 2 * MAX_IMAGE_WIDTH] - OneColorImage[tempAddress + 2 * MAX_IMAGE_WIDTH];

				if (maxValue < gradValue)
				{
					maxValue = gradValue;
					topLinePixelShift[m] = j;
				}
			}
		}
	}

	// shift 값에 평균값을 적용
	memcpy(tempLinePixelShift, topLinePixelShift, sizeof(int) * 520);
	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		startN = m - 10;
		endN = m + 10;

		if (startN < 0)
			startN = 0;
		if (endN > m - 1)
			endN = m - 1;

		shiftY = tempCount = 0;
		for (n = startN; n <= endN; n++)
		{
			if (n != m)
			{
				shiftY += tempLinePixelShift[n];
				tempCount++;
			}
		}

		if (tempCount)
		{
			shiftY /= tempCount;
			topLinePixelShift[m] = shiftY;
		}
	}

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempSideShapeEdgePointTop[m * 2 + 0] = tempSideShapeEdgePointTop[m * 2 + 0];
		tempSideShapeEdgePointTop[m * 2 + 1] = tempSideShapeEdgePointTop[m * 2 + 1] + topLinePixelShift[m];

		/*
		tempAddress = MAX_IMAGE_WIDTH * tempSideShapeEdgePointTop[m * 2 + 1] + tempSideShapeEdgePointTop[m * 2 + 0];

		if(InspectionArea[tempAddress] == 0)
		{
		  SideShapeEdgeMatchingPointTop[m * 2 + 0] = tempSideShapeEdgePointTop[m * 2 + 0];
		  SideShapeEdgeMatchingPointTop[m * 2 + 1] = tempSideShapeEdgePointTop[m * 2 + 1];
		}
		else
		{
		  SideShapeEdgeMatchingPointTop[m * 2 + 0] = -1;
		  SideShapeEdgeMatchingPointTop[m * 2 + 1] = -1;
		}
		*/
	}
	// SideShapeEdgeMatchingPointTopCount = finalMatchingShapeData.SideShapeEdgePointCountBottom;

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempSideShapeEdgePointBottom[m * 2 + 0] = finalMatchingShapeData.SideShapeEdgePointBottom[m * 2 + 0] + TabletMatchingResult.TabletSideShapeShiftX;
		tempSideShapeEdgePointBottom[m * 2 + 1] = finalMatchingShapeData.SideShapeEdgePointBottom[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY;

		/*
		tempAddress = MAX_IMAGE_WIDTH * tempSideShapeEdgePointBottom[m * 2 + 1] + tempSideShapeEdgePointBottom[m * 2 + 0];

		if(InspectionArea[tempAddress] == 0)
		{
		  SideShapeEdgeMatchingPointBottom[m * 2 + 0] = tempSideShapeEdgePointBottom[m * 2 + 0];
		  SideShapeEdgeMatchingPointBottom[m * 2 + 1] = tempSideShapeEdgePointBottom[m * 2 + 1];
		}
		else
		{
		  SideShapeEdgeMatchingPointBottom[m * 2 + 0] = -1;
		  SideShapeEdgeMatchingPointBottom[m * 2 + 1] = -1;
		}
		*/
	}
	// SideShapeEdgeMatchingPointBottomCount = finalMatchingShapeData.SideShapeEdgePointCountBottom;

#ifdef PC_SIM
	memcpy(DentedArea, tempGradientImage, 640 * 480);
	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountLeft; m++)
	{
		tempX = finalMatchingShapeData.SideShapeEdgePointLeft[m * 2] + TabletMatchingResult.TabletSideShapeShiftX;
		tempY = finalMatchingShapeData.SideShapeEdgePointLeft[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY;

		if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
			tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

			DentedArea[tempAddress] = 255;
		}
	}

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountRight; m++)
	{
		tempX = finalMatchingShapeData.SideShapeEdgePointRight[m * 2] + TabletMatchingResult.TabletSideShapeShiftX;
		tempY = finalMatchingShapeData.SideShapeEdgePointRight[m * 2 + 1] + TabletMatchingResult.TabletSideShapeShiftY;

		if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
			tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

			DentedArea[tempAddress] = 255;
		}
	}

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX = tempSideShapeEdgePointTop[m * 2 + 0];
		tempY = tempSideShapeEdgePointTop[m * 2 + 1];

		if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
			tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

			DentedArea[tempAddress] = 255;
		}
	}

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX = tempSideShapeEdgePointBottom[m * 2 + 0];
		tempY = tempSideShapeEdgePointBottom[m * 2 + 1];

		if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
			tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

			DentedArea[tempAddress] = 255;
		}
	}
#endif

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX = tempSideShapeEdgePointTop[m * 2 + 0];
		tempY = tempSideShapeEdgePointTop[m * 2 + 1];

		if (tempX >= inspectionAreaStartX && tempX <= inspectionAreaEndX)
		{
			tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
			tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];
			LineInspectionAreaSetting(tempX, tempY, tempX2, tempY2, m, cameraIndex); // 함수 반복 호출이라서 느릴 것 같지만 생각외로 그렇게 느리지는 않다. PC에서 0.63ms
#ifdef PC_SIM
			if (tempY < minY)
				minY = tempY;
			if (tempY2 > maxY)
				maxY = tempY2;
#endif
		}
	}

	if (SideAngle == PLUS_45_ANGLE_CAM)
	{
		width = finalMatchingShapeData.endX1 - finalMatchingShapeData.endX2;
		height = finalMatchingShapeData.endY1 - finalMatchingShapeData.endY2;
	}
	else
	{
		width = finalMatchingShapeData.startX1 - finalMatchingShapeData.startX2;
		height = finalMatchingShapeData.startY1 - finalMatchingShapeData.startY2;
	}

	if (width > 0)
	{
		sideWallSlope = 10000 * height / width;
		if (sideWallSlope > 0)
			width = 10000 * gamma / sideWallSlope;
		else 
			width = 0;

		edgeAreaShiftX = width;
	}
	else
	{
		edgeAreaShiftX = 0;
	}

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX = tempSideShapeEdgePointTop[m * 2 + 0] - edgeAreaShiftX;
		tempY = tempSideShapeEdgePointTop[m * 2 + 1] - gamma;

		if (tempX >= inspectionAreaStartX && tempX <= inspectionAreaEndX)
		{
			tempX2 = tempSideShapeEdgePointTop[m * 2 + 0];
			tempY2 = tempSideShapeEdgePointTop[m * 2 + 1];
			SideFaceEdgeAreaSetting(tempX, tempY, tempX2, tempY2);
		}
	}

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX2 = tempSideShapeEdgePointTop[m * 2 + 0];
		tempY2 = tempSideShapeEdgePointTop[m * 2 + 1];

		for (i = tempY2 - delta; i <= tempY2; i++)
		{
			for (j = tempX2 - 4; j <= tempX2 + 4; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j] == SIDE_SHAPE_TOP_EDGE_UPPER_NEIGHBOR)
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_TOP_NORMAL_BOUNDARY_AREA;
				}
			}
		}
	}

	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++) // 검사 불가 영역 설정
	{
		tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
		tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];
		for (i = tempY2 - 2 - beta; i <= tempY2; i++)
		{
			for (j = tempX2 - 4; j <= tempX2 + 4; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j])
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_UNINSPECTION_AREA;
				}
			}
		}
	}
	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
		tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];
		for (i = tempY2 - 5; i <= tempY2; i++)
		{
			for (j = tempX2 - 4; j <= tempX2 + 4; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j] == NORMAL_INPECTION_AREA)
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_BOTTOM_EDGE_SHORT_NEIGHBOR;
				}
			}
		}
	}
	for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
	{
		tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
		tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];
		for (i = tempY2 - 7; i <= tempY2; i++)
		{
			for (j = tempX2 - 4; j <= tempX2 + 4; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j] == NORMAL_INPECTION_AREA)
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_BOTTOM_EDGE_LONG_NEIGHBOR;
				}
			}
		}
	}

	if (TabletCharacter.tabletLayerCount > 1)
	{
		multiLayerMaskSize = TabletGradeData.ETC_SideFaceMultiTabletUnInspectionSize;

		validLayerLabelN[0] = validLayerLabelN[1] = -1;
		memset(layerBounaryPositionInfo, 0, sizeof(int) * MAX_VALID_TABLET_LAYER_COUNT * MAX_IMAGE_WIDTH);
		for (tryCount = 0; tryCount < TabletCharacter.tabletLayerCount; tryCount++)
		{
			if (TabletMultiLayerBoundaryInfo[tryCount].labelN != layerMaxHeightPosition)
			{
				memset(TempImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
				for (m = 0; m < TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfoCount; m++)
				{
					tempX = TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfo[m * 2 + 0];
					tempY = TabletMultiLayerBoundaryInfo[tryCount].tabletLayerBoundaryPositionInfo[m * 2 + 1];

					for (x = tempX - 3 - multiLayerMaskSize; x <= tempX + 3 + multiLayerMaskSize; x++)
					{
						for (y = tempY - 3 - multiLayerMaskSize; y <= tempY + 3 + multiLayerMaskSize; y++)
						{
							tempAddress = MAX_IMAGE_WIDTH * y + x;

							if (x > startX && x < endX && y > startY && y < endY)
							{
								if (InspectionArea[tempAddress] && InspectionArea[tempAddress] != SIDE_SHAPE_UNINSPECTION_AREA)
								{
									InspectionArea[tempAddress] = SIDE_SHAPE_MULTI_TABLET_BOUNDARY_AREA;
									TempImage[tempAddress] = 1;
								}
							}
						}
					}
				}

				for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
				{
					tempX = tempSideShapeEdgePointTop[m * 2 + 0];
					tempY = tempSideShapeEdgePointTop[m * 2 + 1];

					tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
					tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];

					if (tempY2 - tempY)
					{
						value = tempCount = 0;
						for (y = tempY; y <= tempY2; y++)
						{
							x = (tempX - tempX2) * (y - tempY) / (tempY - tempY2) + tempX;

							tempAddress = MAX_IMAGE_WIDTH * y + x;

							if (TempImage[tempAddress])
							{
								value += y;
								tempCount++;
							}
						}

						if (tempCount)
						{
							layerBounaryPositionInfo[tryCount][m] = value / tempCount;
						}
					}
				}

				if (TabletCharacter.tabletLayerCount == 2)
				{
					validLayerLabelN[0] = tryCount;
				}
				else if (TabletCharacter.tabletLayerCount == 3)
				{
					if (validLayerLabelN[0] == -1)
					{
						validLayerLabelN[0] = tryCount;
					}
					else if (validLayerLabelN[1] == -1)
					{
						validLayerLabelN[1] = tryCount;
					}
				}
			}
		}

		// NORMAL_INPECTION_AREA2는 NORMAL_INPECTION_AREA 보다 어두운 영역으로 설정됨
		multiLayerArea1Color = multiLayerArea2Color = multiLayerArea1Count = multiLayerArea2Count = 0;
		if (TabletCharacter.tabletLayerCount == 2)
		{
			for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
			{
				tempX = tempSideShapeEdgePointTop[m * 2 + 0];
				tempY = tempSideShapeEdgePointTop[m * 2 + 1];

				tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
				tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];

				if (tempY2 - tempY)
				{
					for (y = tempY; y <= tempY2; y += 2)
					{
						x = (tempX - tempX2) * (y - tempY) / (tempY - tempY2) + tempX;

						tempAddress = MAX_IMAGE_WIDTH * y + x;

						if (InspectionArea[tempAddress] && InspectionArea[tempAddress] != SIDE_SHAPE_TOP_EDGE_UPPER_NEIGHBOR &&
							layerBounaryPositionInfo[validLayerLabelN[0]][m])
						{
							if (y < layerBounaryPositionInfo[validLayerLabelN[0]][m])
							{
								multiLayerArea1Color += OneColorImage[tempAddress];
								multiLayerArea1Count++;
							}
							else
							{
								multiLayerArea2Color += OneColorImage[tempAddress];
								multiLayerArea2Count++;
							}
						}
					}
				}
			}

			if (multiLayerArea1Count && multiLayerArea2Count)
			{
				multiLayerArea1Color /= multiLayerArea1Count;
				multiLayerArea2Color /= multiLayerArea2Count;

				for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
				{
					tempX = tempSideShapeEdgePointTop[m * 2 + 0];
					tempY = tempSideShapeEdgePointTop[m * 2 + 1];

					tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
					tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];

					if (tempY2 - tempY)
					{
						for (y = tempY; y <= tempY2; y++)
						{
							x = (tempX - tempX2) * (y - tempY) / (tempY - tempY2) + tempX;

							for (j = y - 2; j <= y + 2; j++)
							{
								for (i = x - 2; i <= x + 2; i++)
								{
									if (i > startX && i < endX && j > startY && j < endY)
									{
										tempAddress = MAX_IMAGE_WIDTH * j + i;

										if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
										{
											stdValue = layerBounaryPositionInfo[validLayerLabelN[0]][m];

											if (stdValue)
											{
												if (multiLayerArea1Color > multiLayerArea2Color)
												{
													if (y > stdValue)
													{
														InspectionArea[tempAddress] = NORMAL_INPECTION_AREA2;
													}
												}
												else
												{
													if (y < stdValue)
													{
														InspectionArea[tempAddress] = NORMAL_INPECTION_AREA2;
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		else if (TabletCharacter.tabletLayerCount == 3)
		{
			for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
			{
				tempX = tempSideShapeEdgePointTop[m * 2 + 0];
				tempY = tempSideShapeEdgePointTop[m * 2 + 1];

				tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
				tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];

				if (tempY2 - tempY)
				{
					for (y = tempY; y <= tempY2; y += 2)
					{
						x = (tempX - tempX2) * (y - tempY) / (tempY - tempY2) + tempX;

						tempAddress = MAX_IMAGE_WIDTH * y + x;

						if (InspectionArea[tempAddress] && InspectionArea[tempAddress] != SIDE_SHAPE_TOP_EDGE_UPPER_NEIGHBOR)
						{
							if (layerBounaryPositionInfo[validLayerLabelN[0]][m] && layerBounaryPositionInfo[validLayerLabelN[1]][m])
							{
								tripleLayerUpperBoundary = min(layerBounaryPositionInfo[validLayerLabelN[0]][m], layerBounaryPositionInfo[validLayerLabelN[1]][m]);
								tripleLayerLowerBoundary = max(layerBounaryPositionInfo[validLayerLabelN[0]][m], layerBounaryPositionInfo[validLayerLabelN[1]][m]);

								if (y > tripleLayerUpperBoundary && y < tripleLayerLowerBoundary)
								{
									multiLayerArea1Color += OneColorImage[tempAddress];
									multiLayerArea1Count++;
								}
								else
								{
									multiLayerArea2Color += OneColorImage[tempAddress];
									multiLayerArea2Count++;
								}
							}
						}
					}
				}
			}

			if (multiLayerArea1Count && multiLayerArea2Count)
			{
				multiLayerArea1Color /= multiLayerArea1Count;
				multiLayerArea2Color /= multiLayerArea2Count;

				for (m = 0; m < finalMatchingShapeData.SideShapeEdgePointCountBottom; m++)
				{
					tempX = tempSideShapeEdgePointTop[m * 2 + 0];
					tempY = tempSideShapeEdgePointTop[m * 2 + 1];

					tempX2 = tempSideShapeEdgePointBottom[m * 2 + 0];
					tempY2 = tempSideShapeEdgePointBottom[m * 2 + 1];

					if (tempY2 - tempY)
					{
						for (y = tempY; y <= tempY2; y++)
						{
							x = (tempX - tempX2) * (y - tempY) / (tempY - tempY2) + tempX;

							for (j = y - 2; j <= y + 2; j++)
							{
								for (i = x - 2; i <= x + 2; i++)
								{
									if (i > startX && i < endX && j > startY && j < endY)
									{
										tempAddress = MAX_IMAGE_WIDTH * j + i;

										if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
										{
											stdUpperValue = layerBounaryPositionInfo[validLayerLabelN[0]][m];
											stdLowerValue = layerBounaryPositionInfo[validLayerLabelN[1]][m];

											if (stdUpperValue && stdLowerValue)
											{
												if (stdUpperValue > stdLowerValue)
												{
													value = stdUpperValue;
													stdUpperValue = stdLowerValue;
													stdLowerValue = value;
												}

												if (multiLayerArea1Color > multiLayerArea2Color)
												{
													if (y < stdUpperValue || y > stdLowerValue)
													{
														InspectionArea[tempAddress] = NORMAL_INPECTION_AREA2;
													}
												}
												else
												{
													if (y > stdUpperValue && y < stdLowerValue)
													{
														InspectionArea[tempAddress] = NORMAL_INPECTION_AREA2;
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}

	MeanColorRForNormal1 = 0;
	MeanColorGForNormal1 = 0;
	MeanColorBForNormal1 = 0;

	memset(histo, 0, sizeof(int) * 3 * 256);
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
			{
				histo[0][ColorSourceImage[tempAddress * 3 + 0]]++;
				histo[1][ColorSourceImage[tempAddress * 3 + 1]]++;
				histo[2][ColorSourceImage[tempAddress * 3 + 2]]++;
			}
		}
	}

	totalCount = tempCount = 0;
	for (m = 0; m < 256; m++)
	{
		totalCount += histo[0][m];
	}

	for (m = 255; m >= 0; m--)
	{
		if (tempCount < totalCount / 2)
		{
			tempCount += histo[0][m];
			MeanColorBForNormal1 += histo[0][m] * m;
		}
	}

	if (tempCount)
		MeanColorBForNormal1 /= tempCount;
	else
		MeanColorBForNormal1 = 128;

	totalCount = tempCount = 0;
	for (m = 0; m < 256; m++)
	{
		totalCount += histo[1][m];
	}

	for (m = 255; m >= 0; m--)
	{
		if (tempCount < totalCount / 2)
		{
			tempCount += histo[1][m];
			MeanColorGForNormal1 += histo[1][m] * m;
		}
	}

	if (tempCount)
		MeanColorGForNormal1 /= tempCount;
	else
		MeanColorGForNormal1 = 128;

	totalCount = tempCount = 0;
	for (m = 0; m < 256; m++)
	{
		totalCount += histo[2][m];
	}

	for (m = 255; m >= 0; m--)
	{
		if (tempCount < totalCount / 2)
		{
			tempCount += histo[2][m];
			MeanColorRForNormal1 += histo[2][m] * m;
		}
	}

	if (tempCount)
		MeanColorRForNormal1 /= tempCount;
	else
		MeanColorRForNormal1 = 128;

	if (selectedRGB == 0)
		MeanOneColor = MeanColorBForNormal1;
	else if (selectedRGB == 1)
		MeanOneColor = MeanColorGForNormal1;
	else if (selectedRGB == 2)
		MeanOneColor = MeanColorRForNormal1;

	MeanColorRForNormal2 = 0;
	MeanColorGForNormal2 = 0;
	MeanColorBForNormal2 = 0;
	if (TabletCharacter.tabletLayerCount > 1)
	{
		MeanColorRForNormal2 = 0;
		MeanColorGForNormal2 = 0;
		MeanColorBForNormal2 = 0;

		memset(histo, 0, sizeof(int) * 3 * 256);
		for (y = startY; y < endY; y++)
		{
			for (x = startX; x < endX; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA2)
				{
					histo[0][ColorSourceImage[tempAddress * 3 + 0]]++;
					histo[1][ColorSourceImage[tempAddress * 3 + 1]]++;
					histo[2][ColorSourceImage[tempAddress * 3 + 2]]++;
				}
			}
		}

		totalCount = tempCount = 0;
		for (m = 0; m < 256; m++)
		{
			totalCount += histo[0][m];
		}

		for (m = 255; m >= 0; m--)
		{
			if (tempCount < totalCount / 2)
			{
				tempCount += histo[0][m];
				MeanColorBForNormal2 += histo[0][m] * m;
			}
		}

		if (tempCount)
			MeanColorBForNormal2 /= tempCount;
		else
			MeanColorBForNormal2 = 128;

		totalCount = tempCount = 0;
		for (m = 0; m < 256; m++)
		{
			totalCount += histo[1][m];
		}

		for (m = 255; m >= 0; m--)
		{
			if (tempCount < totalCount / 2)
			{
				tempCount += histo[1][m];
				MeanColorGForNormal2 += histo[1][m] * m;
			}
		}

		if (tempCount)
			MeanColorGForNormal2 /= tempCount;
		else
			MeanColorGForNormal2 = 128;

		totalCount = tempCount = 0;
		for (m = 0; m < 256; m++)
		{
			totalCount += histo[2][m];
		}

		for (m = 255; m >= 0; m--)
		{
			if (tempCount < totalCount / 2)
			{
				tempCount += histo[2][m];
				MeanColorRForNormal2 += histo[2][m] * m;
			}
		}

		if (tempCount)
			MeanColorRForNormal2 /= tempCount;
		else
			MeanColorRForNormal2 = 128;
	}

	if (ProcessingModeGlobal == STUDY_MODE)
		TabletStudyData.middleHeight = 0; // 20200205 무슨용도였더라..

	memset(TempImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

#ifdef PC_SIM
	memcpy(InspectionArea_Virtual, InspectionArea, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
#endif
}
//---------------------------------------------------------------------------
void SideFaceShapeMatchingForSugarCoating(int cameraIndex)
{
	int y, x;
	int i, j;
	int tempAddress;
	int meanColorCountForNormal1;
	int startX, endX, startY, endY;
	int startX2, endX2, startY2, endY2;
	unsigned short CoatedTabletBottomLine[MAX_IMAGE_WIDTH * 2];
	int CoatedTabletBottomLineCount;
	int m;
	int bottomEdgeLineSize;
	int tempX, tempY;
	int tempY2;
	int bottomCenterX, bottomCenterY;
	int binStartPos[2], binEndX[2];
	int maskWidth;
	int tempCount1;
	int tempCount2;
	int tabletHeight;

	bottomEdgeLineSize = TabletGradeData.side_Edge_Bottom_Neighbor_Intensity + 1;

	memset(CoatedTabletBottomLine, 0, sizeof(unsigned short) * 2);
	CoatedTabletBottomLineCount = 0;

	startX = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS];
	endX = tabletAreaInfo[AREA_INFO_END_X_ADDRESS];
	startY = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS];
	endY = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS];

	startX2 = TabletSetupData.ImageCutStartX[cameraIndex - 1];
	endX2 = TabletSetupData.ImageCutEndX[cameraIndex - 1];
	startY2 = TabletSetupData.ImageCutStartY[cameraIndex - 1];
	endY2 = TabletSetupData.ImageCutEndY[cameraIndex - 1];

	binStartPos[0] = MAX_IMAGE_WIDTH;
	binEndX[0] = 0;

	bottomCenterX = bottomCenterY = 0;
	tempCount1 = tempCount2 = 0;
	for (x = startX; x < endX; x++)
	{
		for (y = endY; y >= startY; y--)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;
			if (ShapeBinaryImage[tempAddress])
			{
				if (CoatedTabletBottomLineCount < MAX_IMAGE_WIDTH)
				{
					CoatedTabletBottomLine[CoatedTabletBottomLineCount * 2 + 0] = x;
					CoatedTabletBottomLine[CoatedTabletBottomLineCount * 2 + 1] = y;
					CoatedTabletBottomLineCount++;

					bottomCenterX += x;
					bottomCenterY += y;

					if (binStartPos[0] > x)
					{
						binStartPos[0] = x;
						binStartPos[1] = y;
						tempCount1 = 1;
					}
					else if (binStartPos[0] == x)
					{
						binStartPos[0] = x;
						binStartPos[1] += y;
						tempCount1++;
					}

					if (binEndX[0] < x)
					{
						binEndX[0] = x;
						binEndX[1] = y;
						tempCount2 = 1;
					}
					else if (binEndX[0] == x)
					{
						binEndX[0] = x;
						binEndX[1] += y;
						tempCount2++;
					}
				}
				break;
			}
		}
	}

	if (CoatedTabletBottomLineCount)
	{
		bottomCenterX /= CoatedTabletBottomLineCount;
		bottomCenterY /= CoatedTabletBottomLineCount;
	}

	if (tempCount1)
	{
		binStartPos[1] /= tempCount1;
	}

	if (tempCount2)
	{
		binEndX[1] /= tempCount2;
	}

	for (m = 0; m < CoatedTabletBottomLineCount; m++)
	{
		tempX = CoatedTabletBottomLine[m * 2 + 0];
		tempY = CoatedTabletBottomLine[m * 2 + 1];

		for (i = tempY - bottomEdgeLineSize; i <= tempY + 2; i++)
		{
			for (j = tempX - bottomEdgeLineSize; j <= tempX + bottomEdgeLineSize; j++)
			{
				if (j > startX && j < endX && i > startY && i < endY)
					InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_UNINSPECTION_AREA;
			}
		}
	}

	// side wall mask
	maskWidth = 10;
	tabletHeight = (TabletGradeData.CoatedTablet_SideInspectionAreaSize - 20) + (TabletCharacter.height * TabletSetupData.cameraZoom[0] / TabletSetupData.cameraZoom[cameraIndex - 1]) / 5;
	if (tabletHeight < 3)
		tabletHeight = 3;

	tempY = binStartPos[1] - tabletHeight * 130 / 100;
	tempY2 = binStartPos[1] + tabletHeight;

	for (y = tempY - 14; y <= tempY2 + 14; y++)
	{
		if (binStartPos[0] - binEndX[0])
		{
			x = (binStartPos[1] - binEndX[1]) * (y - binStartPos[1]) / -(binStartPos[0] - binEndX[0]) + binStartPos[0];
			for (i = y - 5; i <= y + 5; i++)
			{
				for (j = x - 12; j <= x + maskWidth; j++)
				{
					if (j > startX2 && j < endX2 && i > startY2 && i < endY2)
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_UNINSPECTION_AREA;
				}
			}
		}
	}

	tempY = binEndX[1] - tabletHeight * 130 / 100;
	tempY2 = binEndX[1] + tabletHeight;

	for (y = tempY - 14; y <= tempY2 + 14; y++)
	{
		if (binStartPos[0] - binEndX[0])
		{
			x = (binStartPos[1] - binEndX[1]) * (y - binEndX[1]) / -(binStartPos[0] - binEndX[0]) + binEndX[0];
			for (i = y - 5; i <= y + 5; i++)
			{
				for (j = x - maskWidth; j <= x + 12; j++)
				{
					if (j > startX2 && j < endX2 && i > startY2 && i < endY2)
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_UNINSPECTION_AREA;
				}
			}
		}
	}

	for (m = 0; m < CoatedTabletBottomLineCount; m++)
	{
		tempX = CoatedTabletBottomLine[m * 2 + 0];
		tempY = CoatedTabletBottomLine[m * 2 + 1];

		for (y = tempY - tabletHeight; y <= tempY; y++)
		{
			if (binStartPos[0] - binEndX[0])
			{
				x = (binStartPos[1] - binEndX[1]) * (y - tempY) / -(binStartPos[0] - binEndX[0]) + tempX;
				for (i = y - 1; i <= y + 1; i++)
				{
					for (j = x - 1; j <= x + 1; j++)
					{
						if (j > startX2 && j < endX2 && i > startY2 && i < endY2)
						{
							tempAddress = MAX_IMAGE_WIDTH * i + j;
							if (InspectionArea[tempAddress] != SIDE_SHAPE_UNINSPECTION_AREA && ShapeBinaryImage[tempAddress])
								InspectionArea[tempAddress] = NORMAL_INPECTION_AREA;
						}
					}
				}
			}
		}
	}

	// 다층정 관련 변수, 기타모양 정제는 다층정 로직 반영안되있음, 초기화만 진행
	MeanColorRForNormal2 = 0;
	MeanColorGForNormal2 = 0;
	MeanColorBForNormal2 = 0;

	MeanColorRForNormal1 = 0;
	MeanColorGForNormal1 = 0;
	MeanColorBForNormal1 = 0;
	meanColorCountForNormal1 = 0;

	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;
			if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
			{
				MeanColorBForNormal1 += ColorSourceImage[tempAddress * 3 + 0];
				MeanColorGForNormal1 += ColorSourceImage[tempAddress * 3 + 1];
				MeanColorRForNormal1 += ColorSourceImage[tempAddress * 3 + 2];
				meanColorCountForNormal1++;
			}
		}
	}

	if (meanColorCountForNormal1)
	{
		MeanColorBForNormal1 /= meanColorCountForNormal1;
		MeanColorGForNormal1 /= meanColorCountForNormal1;
		MeanColorRForNormal1 /= meanColorCountForNormal1;
	}
}
//---------------------------------------------------------------------------
void SideFaceShapeMatchingForETC(int cameraIndex)
{
	int tempX, tempY;
	int m, n;
	unsigned char *tabletShapeImage;
	int stPoint, endPoint;
	int jump;
	int startX, endX, startY, endY;
	int x, y;
	int weight;
	int tempCount;
	int tempAddress;
	int SideWallMatchingStartPos[2];
	int SideWallMatchingEndPos[2];
	int tabletROIStartX, tabletROIEndX;
	int currentTabletWidth;
	int SideAngle;
	int maxMatchingValue;
	int shiftX, shiftY;
	int matchingValue;
	int tempX2, tempY2;
	int topShiftX, topShiftY;
	int i;
	int gradValue;
	int srcValue;
	int maxValue;
	int minX, minY;
	int tabletStartX;
	int tabletEndX;
	int topDarkSW, topBrightSW, botDarkSW, botBrightSW;
	int colorDiff;
	int r;
	int deltaThreshold;
	int gradX;
	int gradY;
	int avgValue;
	short temptabletShapeTopLine[MAX_IMAGE_WIDTH * 2];
	int shiftXY[40];
	int beta;
	int gamma;
	int delta;
	int j;
	int width, height;
	int sideWallSlope;
	int edgeAreaShiftX;
	int tempX3, tempY3;
	int histo[3][256];
	int totalCount;
	int outofAreaSW;
	int alpha;
	short expectedTableBottomLine[MAX_IMAGE_WIDTH][MAX_EXPECTED_BOTTOM_LINE_COUNT * 2];
	short expectedTableBottomLineCount[MAX_IMAGE_WIDTH];
	int fstPosSW;
	int length;
	int prevX, prevY;
	int deltaX;
	int currentY;
	int minLength;
	int closedPos;
	int platShift;

	memset(expectedTableBottomLine, 0, sizeof(short) * MAX_IMAGE_WIDTH * MAX_EXPECTED_BOTTOM_LINE_COUNT * 2);
	memset(expectedTableBottomLineCount, 0, sizeof(short) * MAX_IMAGE_WIDTH);

	startX = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS];
	endX = tabletAreaInfo[AREA_INFO_END_X_ADDRESS];
	startY = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS];
	endY = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS];

	tabletStartX = TabletETCMatchingResult.botLeftWallInfo[0];
	tabletEndX = TabletETCMatchingResult.botRightWallInfo[0];

	int sideROICutRateA = 0; // = 2 * TabletGradeData.ETC_SideFaceROICutA; // sideROICutRateA, sideROICutRateB의 기본 값은 0이 되어야 함.
	int sideROICutRateB = 0; // = 2 * TabletGradeData.ETC_SideFaceROICutB; // mint 정제의 경우 sideROICutRateB가 10정도 되면 적당함.

	currentTabletWidth = abs(tabletStartX - tabletEndX);
	if (cameraIndex == SD1_2D_SIDE_FACE_M45_CAMERA_INDEX ||
		cameraIndex == SD2_2D_SIDE_FACE_M45_CAMERA_INDEX ||
		cameraIndex == SD1_2D_OTHER_SIDE_FACE_M45_CAMERA_INDEX ||
		cameraIndex == SD2_2D_OTHER_SIDE_FACE_M45_CAMERA_INDEX)
	{
		SideAngle = MINUS_45_ANGLE_CAM;

		tabletROIStartX = tabletStartX;
		tabletROIEndX = tabletEndX - currentTabletWidth / 4;
		if (cameraIndex == SD1_2D_SIDE_FACE_M45_CAMERA_INDEX || cameraIndex == SD2_2D_SIDE_FACE_M45_CAMERA_INDEX)
		{
			tabletROIEndX -= currentTabletWidth * sideROICutRateA / 100;
		}
		else if (cameraIndex == SD1_2D_OTHER_SIDE_FACE_M45_CAMERA_INDEX || cameraIndex == SD2_2D_OTHER_SIDE_FACE_M45_CAMERA_INDEX)
		{
			tabletROIEndX -= currentTabletWidth * sideROICutRateB / 100;
		}
	}
	else if (cameraIndex == SD1_2D_SIDE_FACE_00_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_SIDE_FACE_00_CAMERA_INDEX ||
			 cameraIndex == SD1_2D_OTHER_SIDE_FACE_00_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_OTHER_SIDE_FACE_00_CAMERA_INDEX)
	{
		SideAngle = ZERO_ANGLE_CAM;

		tabletROIStartX = tabletStartX + currentTabletWidth / 6;
		tabletROIEndX = tabletEndX - currentTabletWidth / 6;
	}
	else if (cameraIndex == SD1_2D_SIDE_FACE_P45_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_SIDE_FACE_P45_CAMERA_INDEX ||
			 cameraIndex == SD1_2D_OTHER_SIDE_FACE_P45_CAMERA_INDEX ||
			 cameraIndex == SD2_2D_OTHER_SIDE_FACE_P45_CAMERA_INDEX)
	{
		SideAngle = PLUS_45_ANGLE_CAM;

		tabletROIEndX = tabletEndX;
		tabletROIStartX = tabletStartX + currentTabletWidth / 4;
		if (cameraIndex == SD1_2D_SIDE_FACE_P45_CAMERA_INDEX || cameraIndex == SD2_2D_SIDE_FACE_P45_CAMERA_INDEX)
		{
			tabletROIStartX += currentTabletWidth * sideROICutRateB / 100;
		}
		else if (cameraIndex == SD1_2D_OTHER_SIDE_FACE_P45_CAMERA_INDEX || cameraIndex == SD2_2D_OTHER_SIDE_FACE_P45_CAMERA_INDEX)
		{
			tabletROIStartX += currentTabletWidth * sideROICutRateA / 100;
		}
	}

	tabletShapeImage = ShapeBinaryImageForPrintArea;
	memset(tabletShapeImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	/*
	for(m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
	  tempX = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
	  tempY = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

	  InspectionArea[MAX_IMAGE_WIDTH * tempY + tempX] = SIDE_SHAPE_TOP_EDGE_LONG_NEIGHBOR;
	}
	*/

	for (m = 0; m < MAX_IMAGE_WIDTH; m++)
	{
		limitBinaryAreaInfo[m] = MAX_IMAGE_HEIGHT;
	}

	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		if (limitBinaryAreaInfo[tempX] > tempY)
		{
			limitBinaryAreaInfo[tempX] = tempY;
		}

		for (y = tempY - 3; y <= tempY + 3; y++)
		{
			for (x = tempX - 3; x <= tempX + 3; x++)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;
				noneGradientArea[tempAddress] = 1;
			}
		}
	}

	for (x = startX; x < endX; x++)
	{
		for (y = startY; y < endY; y++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (ShapeBinaryImage[tempAddress])
			{
				for (tempY = y - 10; tempY <= y + 10; tempY++)
				{
					for (tempX = x - 2; tempX <= x + 2; tempX++)
					{
						tempAddress = MAX_IMAGE_WIDTH * tempY + tempX;
						noneGradientArea[tempAddress] = 1;
					}
				}
				break;
			}
		}
	}

	// top line matching

	// 밝기 변화가 발생하는 px에 대한 마스킹 - 최종 판정
	memset(shiftXY, 0, sizeof(int) * 40);
	for (r = 1; r <= 20; r++)
	{
		if (TabletETCMatchingResult.topLeftWallInfo[1] - TabletETCMatchingResult.botLeftWallInfo[1])
		{
			tempX = (TabletETCMatchingResult.topLeftWallInfo[0] - TabletETCMatchingResult.botLeftWallInfo[0]) * r / (TabletETCMatchingResult.topLeftWallInfo[1] - TabletETCMatchingResult.botLeftWallInfo[1]);

			if (tempX)
			{
				shiftXY[r] = tempX;
			}
		}
	}

	deltaThreshold = 12;
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			if (y > startY + 12 && y < endY - 12)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;
				if (GradientImage[tempAddress])
				{
					srcValue = OneColorImage[tempAddress];

					topDarkSW = topBrightSW = botDarkSW = botBrightSW = 0;
					for (r = 2; r <= 12; r += 2)
					{
						colorDiff = srcValue - OneColorImage[tempAddress - r * MAX_IMAGE_WIDTH - shiftXY[r]];
						if (colorDiff > deltaThreshold)
						{
							topBrightSW = 1;
						}
						else if (-colorDiff > deltaThreshold)
						{
							topDarkSW = 1;
						}

						colorDiff = srcValue - OneColorImage[tempAddress + r * MAX_IMAGE_WIDTH + shiftXY[r]];
						if (colorDiff > deltaThreshold)
						{
							botBrightSW = 1;
						}
						else if (-colorDiff > deltaThreshold)
						{
							botDarkSW = 1;
						}
					}

					if ((topDarkSW && botDarkSW))
					{
						GradientImage[tempAddress] = 0;
					}
				}
			}
		}
	}

	/*
	memset(GradientImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
	for (x = TabletETCMatchingResult.topLeftWallInfo[0] + 10; x < TabletETCMatchingResult.topRightWallInfo[0] - 10; x++)
	{
	  for (y = startY; y < endY; y++)
	  {
		tempAddress = MAX_IMAGE_WIDTH * y + x;

		if(!noneGradientArea[tempAddress] &&
			!noneGradientArea[tempAddress - 4] &&
			!noneGradientArea[tempAddress + 4] &&
			!noneGradientArea[tempAddress - 4 * MAX_IMAGE_WIDTH])
		{
		  if(limitBinaryAreaInfo[x] > y)
		  {
			if(ShapeBinaryImage[tempAddress])
			{
			  gradValue = tempCount = 0;
			  srcValue = OneColorImage[tempAddress];
			  for(r = 4; r <= 20; r += 4)
			  {
				if(y - r > startY)
				{
				  if(!noneGradientArea[tempAddress - r * MAX_IMAGE_WIDTH - shiftXY[r]])
				  {
					colorDiff = OneColorImage[tempAddress - r * MAX_IMAGE_WIDTH - shiftXY[r]] - srcValue;
					if(colorDiff > 0)
					{
					  gradValue += colorDiff;
					  tempCount++;
					}
				  }
				}
			  }

			  avgValue = 0;
			  if(tempCount)
			  {
				avgValue = gradValue / tempCount;
			  }
			  for(r = 4; r <= 20; r += 4)
			  {
				if(y - r <= startY || noneGradientArea[tempAddress - r * MAX_IMAGE_WIDTH - shiftXY[r]])
				{
				  gradValue += avgValue;
				}
			  }

			  GradientImage[tempAddress] = min(255, gradValue);
			}
		  }
		}
	  }
	}
	*/

	platShift = 0;
	if (TabletCharacter.plateInformation)
	{
		platShift = 10;
	}

	// 기타모양 정제도 탑 영역에 매칭 시도하는거 작업하긴해야하는데...
	// -> 할필요 없을듯. 기타모양은 기본적으로 정제 바이너리 기준으로 매칭을 시도하기때문에
	topShiftX = topShiftY = 0;
	if (SideAngle == ZERO_ANGLE_CAM || SideAngle == MINUS_45_ANGLE_CAM)
	{
		SideWallMatchingStartPos[0] = TabletETCMatchingResult.topLeftWallInfo[0];
		SideWallMatchingStartPos[1] = TabletETCMatchingResult.topLeftWallInfo[1];
		SideWallMatchingEndPos[0] = TabletETCMatchingResult.botLeftWallInfo[0];
		SideWallMatchingEndPos[1] = TabletETCMatchingResult.botLeftWallInfo[1];

		maxMatchingValue = 0;
		int bottomX = TabletETCMatchingResult.tabletShapeLeftLine[0];
		int bottomY = TabletETCMatchingResult.tabletShapeLeftLine[1];
		int topY = TabletETCMatchingResult.topLeftWallInfo[1] + platShift;

		int middleHeight = TabletCharacter.middle_height * TabletSetupData.cameraZoom[0] / TabletSetupData.cameraZoom[cameraIndex - 1];
		int heightMaxY = middleHeight * 110 / 100;
		for (int k = TabletETCMatchingResult.tabletShapeLeftLineCount / 4; k < TabletETCMatchingResult.tabletShapeLeftLineCount; k++)
		{
			tempX = TabletETCMatchingResult.tabletShapeLeftLine[k * 2 + 0];
			tempY = TabletETCMatchingResult.tabletShapeLeftLine[k * 2 + 1];

			shiftX = bottomX - tempX;
			shiftY = bottomY - tempY;

			if (tempY < topY || shiftY > heightMaxY)
				break;

			outofAreaSW = 0;
			matchingValue = 0;
			for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
			{
				tempX2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0] - shiftX;
				tempY2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1] - shiftY;

				if (tempX2 > startX && tempX2 < endX && tempY2 > startY && tempY2 < endY)
				{
					tempAddress = MAX_IMAGE_WIDTH * tempY2 + tempX2;

					matchingValue += GradientImage[tempAddress];

					if (ShapeBinaryImage[tempAddress] == 0)
					{
						outofAreaSW = 1;
					}
				}
			}

			if (outofAreaSW == 0)
			{
				if (maxMatchingValue < matchingValue)
				{
					maxMatchingValue = matchingValue;

					topShiftX = shiftX;
					topShiftY = shiftY - 4;
				}
			}
		}
	}
	else
	{
		SideWallMatchingStartPos[0] = TabletETCMatchingResult.topRightWallInfo[0];
		SideWallMatchingStartPos[1] = TabletETCMatchingResult.topRightWallInfo[1];
		SideWallMatchingEndPos[0] = TabletETCMatchingResult.botRightWallInfo[0];
		SideWallMatchingEndPos[1] = TabletETCMatchingResult.botRightWallInfo[1];

		maxMatchingValue = 0;
		int bottomX = TabletETCMatchingResult.tabletShapeRightLine[0];
		int bottomY = TabletETCMatchingResult.tabletShapeRightLine[1];
		int topY = TabletETCMatchingResult.topRightWallInfo[1] + platShift;

		int middleHeight = TabletCharacter.middle_height * TabletSetupData.cameraZoom[0] / TabletSetupData.cameraZoom[cameraIndex - 1];
		int heightMaxY = middleHeight * 110 / 100;
		for (int k = TabletETCMatchingResult.tabletShapeRightLineCount / 4; k < TabletETCMatchingResult.tabletShapeRightLineCount; k++)
		{
			tempX = TabletETCMatchingResult.tabletShapeRightLine[k * 2 + 0];
			tempY = TabletETCMatchingResult.tabletShapeRightLine[k * 2 + 1];

			shiftX = bottomX - tempX;
			shiftY = bottomY - tempY;

			if (tempY < topY || shiftY > heightMaxY)
				break;

			outofAreaSW = 0;
			matchingValue = 0;
			for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
			{
				tempX2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0] - shiftX;
				tempY2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1] - shiftY;

				if (tempX2 > startX && tempX2 < endX && tempY2 > startY && tempY2 < endY)
				{
					tempAddress = MAX_IMAGE_WIDTH * tempY2 + tempX2;

					matchingValue += GradientImage[tempAddress];

					if (ShapeBinaryImage[tempAddress] == 0)
					{
						outofAreaSW = 1;
					}
				}
			}

			if (outofAreaSW == 0)
			{
				if (maxMatchingValue < matchingValue)
				{
					maxMatchingValue = matchingValue;

					topShiftX = shiftX;
					topShiftY = shiftY - 4;
				}
			}
		}
	}

	TabletETCMatchingResult.tabletShapeTopLineCount = TabletETCMatchingResult.tabletShapeBottomLineCount;
	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0] - topShiftX;
		tempY = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1] - topShiftY;

		TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0] = tempX;
		TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1] = tempY;
	}

	if (0) // 상하단 평행이 아닌경우, grade 적용할것, 보완해야함
	{
		for (m = 0; m < TabletETCMatchingResult.tabletShapeTopLineCount; m++)
		{
			tempX = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
			tempY = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

			maxValue = 0;
			for (tempY2 = tempY - 8; tempY2 <= tempY + 8; tempY2 += 2)
			{
				tempAddress = MAX_IMAGE_WIDTH * tempY2 + tempX;
				if (maxValue < GradientImage[tempAddress])
				{
					maxValue = GradientImage[tempAddress];
					TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1] = tempY2;
				}
			}
		}

		memcpy(temptabletShapeTopLine, TabletETCMatchingResult.tabletShapeTopLine, sizeof(short) * MAX_IMAGE_WIDTH);

		for (m = 0; m < TabletETCMatchingResult.tabletShapeTopLineCount; m++)
		{
			avgValue = tempCount = 0;
			for (n = m - 10; n <= m + 10; n++)
			{
				if (n >= 0 && n < TabletETCMatchingResult.tabletShapeTopLineCount)
				{
					avgValue += temptabletShapeTopLine[n * 2 + 1];
					tempCount++;
				}
				else if (n < 0)
				{
					avgValue += temptabletShapeTopLine[0 * 2 + 1];
					tempCount++;
				}
				else if (n >= TabletETCMatchingResult.tabletShapeTopLineCount)
				{
					avgValue += temptabletShapeTopLine[(TabletETCMatchingResult.tabletShapeTopLineCount - 1) * 2 + 1];
					tempCount++;
				}
			}

			if (tempCount)
			{
				avgValue /= tempCount;

				TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1] = avgValue;
			}
		}

		minX = MAX_IMAGE_WIDTH;
		minY = MAX_IMAGE_HEIGHT;

		memset(tabletShapeImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);
		for (m = 0; m < TabletETCMatchingResult.tabletShapeTopLineCount; m++)
		{
			tempX = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
			tempY = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

			if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
			{
				tabletShapeImage[MAX_IMAGE_WIDTH * tempY + tempX]++;

				if (tempX < minX)
					minX = tempX;
				if (tempY < minY)
					minY = tempY;
			}
		}
		TabletETCMatchingResult.tabletShapeTopLineMinX = minX;
		TabletETCMatchingResult.tabletShapeTopLineMinY = minY;

		for (jump = 4; jump <= 32; jump *= 2)
		{
			for (m = -jump; m < TabletETCMatchingResult.tabletShapeTopLineCount + jump; m += jump)
			{
				stPoint = max(0, m - jump);
				endPoint = min(TabletETCMatchingResult.tabletShapeTopLineCount, m + jump);
				tabletShapeInfoModeling(TabletETCMatchingResult.tabletShapeTopLine, stPoint, endPoint, TabletETCMatchingResult.tabletShapeTopLineMinX, TabletETCMatchingResult.tabletShapeTopLineMinY, tabletShapeImage);
			}
		}

		TabletETCMatchingResult.tabletShapeTopLineCount = 0;
		for (x = startX; x < endX; x++)
		{
			tempY = tempCount = 0;
			for (y = endY; y > startY; y--)
			{
				tempAddress = MAX_IMAGE_WIDTH * y + x;

				weight = tabletShapeImage[tempAddress];
				if (weight)
				{
					tempY += (y * weight);
					tempCount += weight;
				}
			}

			if (tempCount)
			{
				tempY /= tempCount;

				if (TabletETCMatchingResult.tabletShapeTopLineCount < MAX_IMAGE_WIDTH)
				{
					TabletETCMatchingResult.tabletShapeTopLine[TabletETCMatchingResult.tabletShapeTopLineCount * 2 + 0] = x;
					TabletETCMatchingResult.tabletShapeTopLine[TabletETCMatchingResult.tabletShapeTopLineCount * 2 + 1] = tempY;
					TabletETCMatchingResult.tabletShapeTopLineCount++;
				}
			}
		}
	}

	// Inspection Area Setting
	if (SideAngle == MINUS_45_ANGLE_CAM)
	{
		MaskingForLeftEdgeLine(TabletETCMatchingResult.topLeftWallInfo[0], TabletETCMatchingResult.topLeftWallInfo[1], TabletETCMatchingResult.botLeftWallInfo[0], TabletETCMatchingResult.botLeftWallInfo[1], cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 1);
		MaskingForRightEdgeLine(TabletETCMatchingResult.topRightWallInfo[0], TabletETCMatchingResult.topRightWallInfo[1], TabletETCMatchingResult.botRightWallInfo[0], TabletETCMatchingResult.botRightWallInfo[1], cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 0);
	}
	else if (SideAngle == PLUS_45_ANGLE_CAM)
	{
		MaskingForLeftEdgeLine(TabletETCMatchingResult.topLeftWallInfo[0], TabletETCMatchingResult.topLeftWallInfo[1], TabletETCMatchingResult.botLeftWallInfo[0], TabletETCMatchingResult.botLeftWallInfo[1], cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 0);
		MaskingForRightEdgeLine(TabletETCMatchingResult.topRightWallInfo[0], TabletETCMatchingResult.topRightWallInfo[1], TabletETCMatchingResult.botRightWallInfo[0], TabletETCMatchingResult.botRightWallInfo[1], cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 1);
	}
	else
	{
		MaskingForLeftEdgeLine(TabletETCMatchingResult.topLeftWallInfo[0], TabletETCMatchingResult.topLeftWallInfo[1], TabletETCMatchingResult.botLeftWallInfo[0], TabletETCMatchingResult.botLeftWallInfo[1], cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 0);
		MaskingForRightEdgeLine(TabletETCMatchingResult.topRightWallInfo[0], TabletETCMatchingResult.topRightWallInfo[1], TabletETCMatchingResult.botRightWallInfo[0], TabletETCMatchingResult.botRightWallInfo[1], cameraIndex, SIDE_SHAPE_UNINSPECTION_AREA, 0, 0);
	}

	if (SideAngle == ZERO_ANGLE_CAM || SideAngle == MINUS_45_ANGLE_CAM)
	{
		TabletSlopeInfoStartPoint[0] = TabletETCMatchingResult.topLeftWallInfo[0];
		TabletSlopeInfoStartPoint[1] = TabletETCMatchingResult.topLeftWallInfo[1];

		TabletSlopeInfoEndPoint[0] = TabletETCMatchingResult.botLeftWallInfo[0];
		TabletSlopeInfoEndPoint[1] = TabletETCMatchingResult.botLeftWallInfo[1];
	}
	else
	{
		TabletSlopeInfoStartPoint[0] = TabletETCMatchingResult.topRightWallInfo[0];
		TabletSlopeInfoStartPoint[1] = TabletETCMatchingResult.topRightWallInfo[1];

		TabletSlopeInfoEndPoint[0] = TabletETCMatchingResult.botRightWallInfo[0];
		TabletSlopeInfoEndPoint[1] = TabletETCMatchingResult.botRightWallInfo[1];
	}

	beta = TabletGradeData.side_Edge_Bottom_Neighbor_Intensity;
	gamma = TabletGradeData.SideFaceEdgeAreaSize * 2;
	delta = TabletGradeData.SideFaceEdgeUnInsAreaSize + 1;

	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
		tempY = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

		tempX2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		if (tempX2 > tabletROIStartX && tempX2 < tabletROIEndX && tempY2 >= startY && tempY2 <= endY)
			makeSideTiltCameraInspectionArea(tempX, tempY, tempX2, tempY2, NORMAL_INPECTION_AREA);
	}

	/*
	for(m = 0; m < TabletETCMatchingResult.tabletShapeTopLineCount; m++)
	{
	  tempX2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
	  tempY2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

	  for(i = tempY2; i <=tempY2 + 5; i++)
	  {
		for(j = tempX2 - 5; j <= tempX2 + 5; j++)
		{
		  if(j >= startX && j <= endX && i >= startY && i <= endY)
		  {
			if(InspectionArea[MAX_IMAGE_WIDTH*i + j] != SIDE_SHAPE_UNINSPECTION_AREA && InspectionArea[MAX_IMAGE_WIDTH*i + j])
			  InspectionArea[MAX_IMAGE_WIDTH*i + j] = SIDE_SHAPE_TOP_EDGE_LONG_NEIGHBOR;
		  }
		}
	  }
	}

	for(m = 0; m < TabletETCMatchingResult.tabletShapeTopLineCount; m++)
	{
	  tempX2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
	  tempY2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

	  for(i = tempY2; i <=tempY2 + 3; i++)
	  {
		for(j = tempX2 - 5; j <=tempX2 + 5; j++)
		{
		  if(j >= startX && j <= endX && i >= startY && i <= endY)
		  {
			if(InspectionArea[MAX_IMAGE_WIDTH*i + j] != SIDE_SHAPE_UNINSPECTION_AREA && InspectionArea[MAX_IMAGE_WIDTH*i + j])
			  InspectionArea[MAX_IMAGE_WIDTH*i + j] = SIDE_SHAPE_TOP_EDGE_SHORT_NEIGHBOR;
		  }
		}
	  }
	}
	*/

	alpha = TabletGradeData.side_Edge_Top_Neighbor_Intensity;
	for (m = 0; m < TabletETCMatchingResult.tabletShapeTopLineCount; m++)
	{
		tempX2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
		tempY2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

		for (i = tempY2; i <= tempY2 + alpha; i++)
		{
			for (j = tempX2 - 5; j <= tempX2 + 5; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j] != SIDE_SHAPE_UNINSPECTION_AREA && InspectionArea[MAX_IMAGE_WIDTH * i + j])
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_TOP_NORMAL_BOUNDARY_AREA;
				}
			}
		}
	}

	width = SideWallMatchingStartPos[0] - SideWallMatchingEndPos[0];
	height = SideWallMatchingStartPos[1] - SideWallMatchingEndPos[1];

	if (width > 0)
	{
		sideWallSlope = 10000 * height / width;
		if (sideWallSlope > 0)
			width = 10000 * gamma / sideWallSlope;
		else 
			width = 0;

		edgeAreaShiftX = width;
	}
	else
	{
		edgeAreaShiftX = 0;
	}

	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0] - edgeAreaShiftX;
		tempY = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1] - gamma;

		tempX3 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY3 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		if (tempX3 > tabletROIStartX && tempX3 < tabletROIEndX && tempY3 >= startY && tempY3 <= endY)
		{
			tempX2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
			tempY2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

			SideFaceEdgeAreaSetting(tempX, tempY, tempX2, tempY2);
		}
	}

	width = SideWallMatchingStartPos[0] - SideWallMatchingEndPos[0];
	height = SideWallMatchingStartPos[1] - SideWallMatchingEndPos[1];

	if (width > 0)
	{
		sideWallSlope = 10000 * height / width;
		if (sideWallSlope > 0)
			width = 10000 * delta / sideWallSlope;
		else 
			width = 0;

		edgeAreaShiftX = width;
	}
	else
	{
		edgeAreaShiftX = 0;
	}

	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0] - edgeAreaShiftX;
		tempY = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1] - delta;

		tempX2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
		tempY2 = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

		tempX3 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY3 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		if (tempX3 > tabletROIStartX && tempX3 < tabletROIEndX && tempY3 >= startY && tempY3 <= endY)
			makeSideTiltCameraInspectionArea(tempX, tempY, tempX2, tempY2, SIDE_SHAPE_TOP_NORMAL_BOUNDARY_AREA);
	}

	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		for (i = tempY2 - 2 - beta; i <= tempY2; i++)
		{
			for (j = tempX2 - 4; j <= tempX2 + 4; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j])
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_UNINSPECTION_AREA;
				}
			}
		}
	}

	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		for (i = tempY2 - 5; i <= tempY2; i++)
		{
			for (j = tempX2 - 4; j <= tempX2 + 4; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j] == NORMAL_INPECTION_AREA)
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_BOTTOM_EDGE_SHORT_NEIGHBOR;
				}
			}
		}
	}

	for (m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	{
		tempX2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY2 = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		for (i = tempY2 - 7; i <= tempY2; i++)
		{
			for (j = tempX2 - 4; j <= tempX2 + 4; j++)
			{
				if (j >= startX && j <= endX && i >= startY && i <= endY)
				{
					if (InspectionArea[MAX_IMAGE_WIDTH * i + j] == NORMAL_INPECTION_AREA)
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = SIDE_SHAPE_BOTTOM_EDGE_LONG_NEIGHBOR;
				}
			}
		}
	}

	MeanColorRForNormal1 = 0;
	MeanColorGForNormal1 = 0;
	MeanColorBForNormal1 = 0;

	memset(histo, 0, sizeof(int) * 3 * 256);
	for (y = startY; y < endY; y++)
	{
		for (x = startX; x < endX; x++)
		{
			tempAddress = MAX_IMAGE_WIDTH * y + x;

			if (InspectionArea[tempAddress] == NORMAL_INPECTION_AREA)
			{
				histo[0][ColorSourceImage[tempAddress * 3 + 0]]++;
				histo[1][ColorSourceImage[tempAddress * 3 + 1]]++;
				histo[2][ColorSourceImage[tempAddress * 3 + 2]]++;
			}
		}
	}

	totalCount = tempCount = 0;
	for (m = 0; m < 256; m++)
	{
		totalCount += histo[0][m];
	}

	for (m = 255; m >= 0; m--)
	{
		if (tempCount < totalCount / 2)
		{
			tempCount += histo[0][m];
			MeanColorBForNormal1 += histo[0][m] * m;
		}
	}

	if (tempCount)
		MeanColorBForNormal1 /= tempCount;
	else
		MeanColorBForNormal1 = 128;

	totalCount = tempCount = 0;
	for (m = 0; m < 256; m++)
	{
		totalCount += histo[1][m];
	}

	for (m = 255; m >= 0; m--)
	{
		if (tempCount < totalCount / 2)
		{
			tempCount += histo[1][m];
			MeanColorGForNormal1 += histo[1][m] * m;
		}
	}

	if (tempCount)
		MeanColorGForNormal1 /= tempCount;
	else
		MeanColorGForNormal1 = 128;

	totalCount = tempCount = 0;
	for (m = 0; m < 256; m++)
	{
		totalCount += histo[2][m];
	}

	for (m = 255; m >= 0; m--)
	{
		if (tempCount < totalCount / 2)
		{
			tempCount += histo[2][m];
			MeanColorRForNormal1 += histo[2][m] * m;
		}
	}

	if (tempCount)
		MeanColorRForNormal1 /= tempCount;
	else
		MeanColorRForNormal1 = 128;

	if (selectedRGB == 0)
		MeanOneColor = MeanColorBForNormal1;
	else if (selectedRGB == 1)
		MeanOneColor = MeanColorGForNormal1;
	else if (selectedRGB == 2)
		MeanOneColor = MeanColorRForNormal1;

	memset(TempImage, 0, MAX_IMAGE_WIDTH * MAX_IMAGE_HEIGHT);

	/*
	#ifdef PC_SIM
	  for(m = 0; m < TabletETCMatchingResult.tabletShapeBottomLineCount; m++)
	  {
		tempX = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 0];
		tempY = TabletETCMatchingResult.tabletShapeBottomLine[m * 2 + 1];

		if(tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
		  tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

		  InspectionArea[tempAddress] = 1;
		  InspectionArea[tempAddress - 1] = 1;
		  InspectionArea[tempAddress + 1] = 1;
		  InspectionArea[tempAddress - MAX_IMAGE_WIDTH] = 1;
		  InspectionArea[tempAddress + MAX_IMAGE_WIDTH] = 1;
		}

		tempX = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 0];
		tempY = TabletETCMatchingResult.tabletShapeTopLine[m * 2 + 1];

		if(tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
		  tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

		  InspectionArea[tempAddress] = 1;
		  InspectionArea[tempAddress - 1] = 1;
		  InspectionArea[tempAddress + 1] = 1;
		  InspectionArea[tempAddress - MAX_IMAGE_WIDTH] = 1;
		  InspectionArea[tempAddress + MAX_IMAGE_WIDTH] = 1;
		}
	  }

	  for(m = 0; m < TabletETCMatchingResult.tabletShapeLeftLineCount; m++)
	  {
		tempX = TabletETCMatchingResult.tabletShapeLeftLine[m * 2 + 0];
		tempY = TabletETCMatchingResult.tabletShapeLeftLine[m * 2 + 1];

		if(tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
		  tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

		  InspectionArea[tempAddress] = 1;
		  InspectionArea[tempAddress - 1] = 1;
		  InspectionArea[tempAddress + 1] = 1;
		  InspectionArea[tempAddress - MAX_IMAGE_WIDTH] = 1;
		  InspectionArea[tempAddress + MAX_IMAGE_WIDTH] = 1;
		}
	  }

	  for(m = 0; m < TabletETCMatchingResult.tabletShapeRightLineCount; m++)
	  {
		tempX = TabletETCMatchingResult.tabletShapeRightLine[m * 2 + 0];
		tempY = TabletETCMatchingResult.tabletShapeRightLine[m * 2 + 1];

		if(tempX > startX && tempX < endX && tempY > startY && tempY < endY)
		{
		  tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

		  InspectionArea[tempAddress] = 1;
		  InspectionArea[tempAddress - 1] = 1;
		  InspectionArea[tempAddress + 1] = 1;
		  InspectionArea[tempAddress - MAX_IMAGE_WIDTH] = 1;
		  InspectionArea[tempAddress + MAX_IMAGE_WIDTH] = 1;
		}
	  }
	#endif
	*/

	/*
	추후 작업 내역
	1. top 라인 매칭 잘안됨 어떻게 할것인가
	  2) bottom 라인을 reverse 시켜야하는데.. 단순 대칭이동으로는 안된다 -> 일단 패스
	  3) 인터럽스 성격의 line data를 어떻게 처리할것이냐

	3. 영역별 검사 방식 재검토
	  1) 밝은, 어두운 이물이 정제 상에 존재할 때 영역이 중첩되는 검사 영역에 대해 양쪽 모두의 등급을 조절해야함
	  2) 밝은, 어두운 이물이 정제 상에 존재할 때 어두운, 밝은 등급 모두 조절해야함


	6. 할선, 각인 등에 불량으로 판정되는 경우... 어떻게 처리할꺼냐.. 기존 할선매칭은 효과가 없음.. 정제 모양에 따라 측면에서 본 할선은 일직선이 아닌 곡면이 될수가 있음, gradient 값도 잘안나오고... 탑쪽에 빛반사에 의해 gradient 값 추출도 잘안됨
	*/
}
//---------------------------------------------------------------------------
void tabletShapeInfoModeling(short *RESTRICT(shapeData), int stPoint, int endPoint, int shapeMinX, int shapeMinY, unsigned char *RESTRICT(dstImage))
{
	long long xPositionMat[MAX_IMAGE_WIDTH][3];
	long long xPositionTransMat[3][MAX_IMAGE_WIDTH];
	long long yPositionMat[MAX_IMAGE_WIDTH];
	long long squareMultipleMat[3][3];
	long long inverseMat[3][3];
	long long resultMultipleMat[3][MAX_IMAGE_WIDTH];
	long long detA;
	long long modelingConstA, modelingConstB, modelingConstC;
	int tempX, tempY;
	int tempX2, tempY2;
	int k;
	int startX, endX, startY, endY;

	startX = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS];
	endX = tabletAreaInfo[AREA_INFO_END_X_ADDRESS];
	startY = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS];
	endY = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS];

	memset(xPositionMat, 0, sizeof(long long) * MAX_IMAGE_WIDTH * 3);
	memset(xPositionTransMat, 0, sizeof(long long) * MAX_IMAGE_WIDTH * 3);
	memset(yPositionMat, 0, sizeof(long long) * MAX_IMAGE_WIDTH);
	memset(squareMultipleMat, 0, sizeof(long long) * 3 * 3);
	memset(inverseMat, 0, sizeof(long long) * 3 * 3);
	memset(resultMultipleMat, 0, sizeof(long long) * 3 * MAX_IMAGE_WIDTH);

	for (k = stPoint; k < endPoint; k++)
	{
		tempX = shapeData[k * 2 + 0] - shapeMinX;
		tempY = shapeData[k * 2 + 1] - shapeMinY;

		xPositionMat[k][0] = tempX * tempX;
		xPositionMat[k][1] = tempX;
		xPositionMat[k][2] = 1;

		xPositionTransMat[0][k] = tempX * tempX;
		xPositionTransMat[1][k] = tempX;
		xPositionTransMat[2][k] = 1;

		yPositionMat[k] = tempY;
	}

	for (k = stPoint; k < endPoint; k++)
	{
		squareMultipleMat[0][0] += xPositionTransMat[0][k] * xPositionMat[k][0];
		squareMultipleMat[0][1] += xPositionTransMat[0][k] * xPositionMat[k][1];
		squareMultipleMat[0][2] += xPositionTransMat[0][k] * xPositionMat[k][2];

		squareMultipleMat[1][0] += xPositionTransMat[1][k] * xPositionMat[k][0];
		squareMultipleMat[1][1] += xPositionTransMat[1][k] * xPositionMat[k][1];
		squareMultipleMat[1][2] += xPositionTransMat[1][k] * xPositionMat[k][2];

		squareMultipleMat[2][0] += xPositionTransMat[2][k] * xPositionMat[k][0];
		squareMultipleMat[2][1] += xPositionTransMat[2][k] * xPositionMat[k][1];
		squareMultipleMat[2][2] += xPositionTransMat[2][k] * xPositionMat[k][2];
	}

	detA = squareMultipleMat[0][0] * (squareMultipleMat[1][1] * squareMultipleMat[2][2] - squareMultipleMat[1][2] * squareMultipleMat[2][1]) - squareMultipleMat[0][1] * (squareMultipleMat[1][0] * squareMultipleMat[2][2] - squareMultipleMat[1][2] * squareMultipleMat[2][0]) + squareMultipleMat[0][2] * (squareMultipleMat[1][0] * squareMultipleMat[2][1] - squareMultipleMat[1][1] * squareMultipleMat[2][0]);

	if (detA == 0)
	{
		return;
	}

	inverseMat[0][0] = (squareMultipleMat[1][1] * squareMultipleMat[2][2] - squareMultipleMat[2][1] * squareMultipleMat[1][2]);
	inverseMat[0][1] = (squareMultipleMat[0][2] * squareMultipleMat[2][1] - squareMultipleMat[0][1] * squareMultipleMat[2][2]);
	inverseMat[0][2] = (squareMultipleMat[0][1] * squareMultipleMat[1][2] - squareMultipleMat[0][2] * squareMultipleMat[1][1]);

	inverseMat[1][0] = (squareMultipleMat[1][2] * squareMultipleMat[2][0] - squareMultipleMat[1][0] * squareMultipleMat[2][2]);
	inverseMat[1][1] = (squareMultipleMat[0][0] * squareMultipleMat[2][2] - squareMultipleMat[0][2] * squareMultipleMat[2][0]);
	inverseMat[1][2] = (squareMultipleMat[1][0] * squareMultipleMat[0][2] - squareMultipleMat[0][0] * squareMultipleMat[1][2]);

	inverseMat[2][0] = (squareMultipleMat[1][0] * squareMultipleMat[2][1] - squareMultipleMat[2][0] * squareMultipleMat[1][1]);
	inverseMat[2][1] = (squareMultipleMat[2][0] * squareMultipleMat[0][1] - squareMultipleMat[0][0] * squareMultipleMat[2][1]);
	inverseMat[2][2] = (squareMultipleMat[0][0] * squareMultipleMat[1][1] - squareMultipleMat[1][0] * squareMultipleMat[0][1]);

	for (k = stPoint; k < endPoint; k++)
	{
		resultMultipleMat[0][k] += (inverseMat[0][0] * xPositionTransMat[0][k] + inverseMat[0][1] * xPositionTransMat[1][k] + inverseMat[0][2] * xPositionTransMat[2][k]);
		resultMultipleMat[1][k] += (inverseMat[1][0] * xPositionTransMat[0][k] + inverseMat[1][1] * xPositionTransMat[1][k] + inverseMat[1][2] * xPositionTransMat[2][k]);
		resultMultipleMat[2][k] += (inverseMat[2][0] * xPositionTransMat[0][k] + inverseMat[2][1] * xPositionTransMat[1][k] + inverseMat[2][2] * xPositionTransMat[2][k]);
	}

	modelingConstA = modelingConstB = modelingConstC = 0;

	for (k = stPoint; k < endPoint; k++)
	{
		modelingConstA += resultMultipleMat[0][k] * yPositionMat[k];
		modelingConstB += resultMultipleMat[1][k] * yPositionMat[k];
		modelingConstC += resultMultipleMat[2][k] * yPositionMat[k];
	}

	for (k = stPoint; k < endPoint; k++)
	{
		tempX = shapeData[k * 2 + 0] - shapeMinX;
		tempY = (modelingConstA * tempX * tempX + modelingConstB * tempX + modelingConstC) / detA;

		tempX2 = tempX + shapeMinX;
		tempY2 = tempY + shapeMinY;

		if (tempX2 > startX && tempX2 < endX && tempY2 > startY && tempY2 < endY)
		{
			dstImage[MAX_IMAGE_WIDTH * tempY2 + tempX2] = min(255, dstImage[MAX_IMAGE_WIDTH * tempY2 + tempX2] + 1);
		}
	}
}
//---------------------------------------------------------------------------
int SideFaceEdgeAreaSetting(int stX, int stY, int endX, int endY)
{
	int x, y, i, j;
	int tempAddress;
	int stStartX, stEndX, stStartY, stEndY;

	stStartX = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS];
	stEndX = tabletAreaInfo[AREA_INFO_END_X_ADDRESS];
	stStartY = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS];
	stEndY = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS];

	if (stY - endY)
	{
		for (y = stY; y <= endY; y++)
		{
			x = (stX - endX) * (y - stY) / (stY - endY) + stX;

			for (j = y - 2; j <= y + 2; j++)
			{
				for (i = x - 2; i <= x + 2; i++)
				{
					if (j >= stY && j <= endY && i >= stStartX && i <= stEndX && j >= stStartY && j <= stEndY &&
						InspectionArea[MAX_IMAGE_WIDTH * j + i] == 0)
					{
						InspectionArea[MAX_IMAGE_WIDTH * j + i] = SIDE_SHAPE_TOP_EDGE_UPPER_NEIGHBOR;
					}
				}
			}
		}
	}

	return 0;
}
//---------------------------------------------------------------------------
int LineInspectionAreaSetting(int stX, int stY, int endX, int endY, int m, int cameraIndex)
{
	int x, y, i, j;
	int tempAddress;
	int stStartX, stEndX, stStartY, stEndY;
	int areaValue;
	int alpha;
	int xsize;
	int ysize;

	stStartX = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS];
	stEndX = tabletAreaInfo[AREA_INFO_END_X_ADDRESS];
	stStartY = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS];
	stEndY = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS];

	alpha = TabletGradeData.side_Edge_Top_Neighbor_Intensity;

	xsize = 2;
	ysize = 2;

	for (y = stY; y <= endY; y++)
	{
		if (y < stY + 1 + alpha)
		{
			areaValue = SIDE_SHAPE_TOP_NORMAL_BOUNDARY_AREA;
		}
		else
		{
			areaValue = NORMAL_INPECTION_AREA;
		}

		if (stY - endY)
		{
			x = (stX - endX) * (y - stY) / (stY - endY) + stX;

			if (areaValue == NORMAL_INPECTION_AREA)
			{
				for (j = y - ysize; j <= y + ysize; j++)
				{
					for (i = x - xsize; i <= x + xsize; i++)
					{
						if (j > stY && j < endY && i > stStartX && i < stEndX && j > stStartY && j < stEndY && 
							InspectionArea[MAX_IMAGE_WIDTH * j + i] == 0)
						{
							InspectionArea[MAX_IMAGE_WIDTH * j + i] = areaValue;
						}
					}
				}
			}
			else
			{
				for (j = y - ysize; j <= y + ysize; j++)
				{
					for (i = x - xsize; i <= x + xsize; i++)
					{
						if (j < endY && i > stStartX && i < stEndX && j > stStartY && j < stEndY &&
							(InspectionArea[MAX_IMAGE_WIDTH * j + i] == 0 || InspectionArea[MAX_IMAGE_WIDTH * j + i] == NORMAL_INPECTION_AREA))
						{
							InspectionArea[MAX_IMAGE_WIDTH * j + i] = areaValue;
						}
					}
				}
			}
		}
	}

	return 0;
}
//---------------------------------------------------------------------------
void MaskingForLeftEdgeLine(int stX, int stY, int endX, int endY, int cameraIndex, int areaValue, int makeBoundary, int maskArea)
{
	int x, y;
	int i, j;
	int maskWidth;
	int startX2, endX2, startY2, endY2;
	int tempAddress;

	startX2 = TabletSetupData.ImageCutStartX[cameraIndex - 1];
	endX2 = TabletSetupData.ImageCutEndX[cameraIndex - 1];
	startY2 = TabletSetupData.ImageCutStartY[cameraIndex - 1];
	endY2 = TabletSetupData.ImageCutEndY[cameraIndex - 1];

	if (!(TabletCharacter.shape == ROUND || TabletCharacter.shape == OBLONG || TabletCharacter.shape == OVAL))
	{
		maskWidth = 12;
	}
	else
	{
		maskWidth = 10;
	}

	if (makeBoundary)
	{
		for (y = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS]; y <= tabletAreaInfo[AREA_INFO_END_Y_ADDRESS]; y++)
		{
			x = (stX - endX) * (y - stY) / (stY - endY) + stX;
			for (i = y - 2; i <= y + 2; i++)
			{
				for (j = x - 2; j <= x + 2; j++)
				{
					if (j > tabletAreaInfo[AREA_INFO_ST_X_ADDRESS] && j < tabletAreaInfo[AREA_INFO_END_X_ADDRESS] && i > tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS] && i < tabletAreaInfo[AREA_INFO_END_Y_ADDRESS])
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = areaValue;
				}
			}
		}
	}
	else
	{
		for (y = stY - 18; y <= endY + 18; y++)
		{
			x = (stX - endX) * (y - stY) / (stY - endY) + stX;
			for (i = y - 5; i <= y + 5; i++)
			{
				for (j = x - 30; j <= x + maskWidth; j++)
				{
					if (j > startX2 && j < endX2 && i > startY2 && i < endY2)
					{
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = areaValue;

						if (maskArea && j >= x - maskWidth && j <= x + maskWidth)
						{
							SideEdgeMaskingArea[MAX_IMAGE_WIDTH * i + j] = 1;
						}
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void MaskingForRightEdgeLine(int stX, int stY, int endX, int endY, int cameraIndex, int areaValue, int makeBoundary, int maskArea)
{
	int x, y;
	int i, j;
	int maskWidth;
	int startX2, endX2, startY2, endY2;
	int tempAddress;

	startX2 = TabletSetupData.ImageCutStartX[cameraIndex - 1];
	endX2 = TabletSetupData.ImageCutEndX[cameraIndex - 1];
	startY2 = TabletSetupData.ImageCutStartY[cameraIndex - 1];
	endY2 = TabletSetupData.ImageCutEndY[cameraIndex - 1];

	if (!(TabletCharacter.shape == ROUND || TabletCharacter.shape == OBLONG || TabletCharacter.shape == OVAL))
	{
		maskWidth = 12;
	}
	else
	{
		maskWidth = 10;
	}

	if (makeBoundary)
	{
		for (y = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS]; y <= tabletAreaInfo[AREA_INFO_END_Y_ADDRESS]; y++)
		{
			x = (stX - endX) * (y - stY) / (stY - endY) + stX;
			for (i = y - 2; i <= y + 2; i++)
			{
				for (j = x - 2; j <= x + 2; j++)
				{
					if (j > tabletAreaInfo[AREA_INFO_ST_X_ADDRESS] && j < tabletAreaInfo[AREA_INFO_END_X_ADDRESS] && i > tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS] && i < tabletAreaInfo[AREA_INFO_END_Y_ADDRESS])
						InspectionArea[MAX_IMAGE_WIDTH * i + j] = areaValue;
				}
			}
		}
	}
	else
	{
		for (y = stY - 18; y <= endY + 18; y++)
		{
			x = (stX - endX) * (y - stY) / (stY - endY) + stX;
			for (i = y - 5; i <= y + 5; i++)
			{
				for (j = x - maskWidth; j <= x + 30; j++)
				{
					if (j > startX2 && j < endX2 && i > startY2 && i < endY2)
					{
						tempAddress = MAX_IMAGE_WIDTH * i + j;
						InspectionArea[tempAddress] = areaValue;

						if (maskArea && j >= x - maskWidth && j <= x + maskWidth)
						{
							SideEdgeMaskingArea[tempAddress] = 1;
						}
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void makeSideTiltCameraInspectionArea(int stX, int stY, int endX, int endY, int areaValue)
{
	int x, y, i, j;
	int tempAddress;
	int stStartX, stEndX, stStartY, stEndY;

	stStartX = tabletAreaInfo[AREA_INFO_ST_X_ADDRESS];
	stEndX = tabletAreaInfo[AREA_INFO_END_X_ADDRESS];
	stStartY = tabletAreaInfo[AREA_INFO_ST_Y_ADDRESS];
	stEndY = tabletAreaInfo[AREA_INFO_END_Y_ADDRESS];

	for (y = stY; y <= endY; y++)
	{
		if (stY - endY)
		{
			x = (stX - endX) * (y - stY) / (stY - endY) + stX;

			if (areaValue == NORMAL_INPECTION_AREA)
			{
				for (j = y - 2; j <= y + 2; j++)
				{
					for (i = x - 2; i <= x + 2; i++)
					{
						if (j >= stY && j <= endY)
						{
							if (i >= stStartX && i <= stEndX && j >= stStartY && j <= stEndY &&
								!InspectionArea[MAX_IMAGE_WIDTH * j + i])
							{
								InspectionArea[MAX_IMAGE_WIDTH * j + i] = areaValue;
							}
						}
					}
				}
			}
			else
			{
				for (j = y - 1; j <= y + 1; j++)
				{
					for (i = x - 3; i <= x + 3; i++)
					{
						if (i >= stStartX && i <= stEndX && j >= stStartY && j <= stEndY &&
							InspectionArea[MAX_IMAGE_WIDTH * j + i] != SIDE_SHAPE_UNINSPECTION_AREA)
						{
							InspectionArea[MAX_IMAGE_WIDTH * j + i] = areaValue;
						}
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void calcSideWallMaxMatchingValue(int startX, int endX, int startY, int endY, int SideAngle, int SideShapeMatchingXAngleStRange, int SideShapeMatchingXAngleEdRange, int SideShapeMatchingYAngleStRange, int SideShapeMatchingYAngleEdRange, int *RESTRICT(sideWallMaxMatchingValue))
{	
	int tAngleX, tAngleY;
	unsigned short SideShapeEdgePointCount;
	unsigned short *SideShapeEdgePoint;
	int tempShiftX, tempShiftY;
	int SideWallGradValue;
	int gradSum;
	int m;
	int tempX, tempY;
	int tempAddress;

	for (tAngleY = SideShapeMatchingYAngleStRange; tAngleY <= SideShapeMatchingYAngleEdRange; tAngleY += 4)
	{
		for (tAngleX = SideShapeMatchingXAngleStRange; tAngleX <= SideShapeMatchingXAngleEdRange; tAngleX += 4)
		{
			if (SideAngle == PLUS_45_ANGLE_CAM)
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountRight;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointRight;

				tempShiftX = TabletMatchingResult.SearchStartXForSideShape - SideData[SideAngle].SideShapeData[tAngleY][tAngleX].endX2;
			}
			else
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountLeft;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointLeft;

				tempShiftX = TabletMatchingResult.SearchStartXForSideShape - SideData[SideAngle].SideShapeData[tAngleY][tAngleX].startX2;
			}

			tempShiftY = TabletMatchingResult.SideShapeEndY - SideData[SideAngle].SideShapeData[tAngleY][tAngleX].ShapeEndY;

			gradSum = 0;
			for (m = 0; m < SideShapeEdgePointCount; m += 4)
			{
				tempX = SideShapeEdgePoint[m * 2] + tempShiftX;
				tempY = SideShapeEdgePoint[m * 2 + 1] + tempShiftY;

				if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
				{
					tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;

					SideWallGradValue = GradientImage[tempAddress];

					if (SideWallGradValue < GradientImage[tempAddress - 8])
						SideWallGradValue = GradientImage[tempAddress - 8];
					if (SideWallGradValue < GradientImage[tempAddress - 6])
						SideWallGradValue = GradientImage[tempAddress - 6];
					if (SideWallGradValue < GradientImage[tempAddress - 4])
						SideWallGradValue = GradientImage[tempAddress - 4];
					if (SideWallGradValue < GradientImage[tempAddress - 2])
						SideWallGradValue = GradientImage[tempAddress - 2];
					if (SideWallGradValue < GradientImage[tempAddress + 2])
						SideWallGradValue = GradientImage[tempAddress + 2];
					if (SideWallGradValue < GradientImage[tempAddress + 4])
						SideWallGradValue = GradientImage[tempAddress + 4];
					if (SideWallGradValue < GradientImage[tempAddress + 6])
						SideWallGradValue = GradientImage[tempAddress + 6];
					if (SideWallGradValue < GradientImage[tempAddress + 8])
						SideWallGradValue = GradientImage[tempAddress + 8];

					gradSum += SideWallGradValue;
				}
			}

			if (*sideWallMaxMatchingValue < gradSum)
			{
				*sideWallMaxMatchingValue = gradSum;
			}
		}
	}
}
//---------------------------------------------------------------------------
void matchSideWallRough(int startX, int endX, int startY, int endY, int SideAngle, int SideShapeMatchingXAngleStRange, int SideShapeMatchingXAngleEdRange, int SideShapeMatchingYAngleStRange, int SideShapeMatchingYAngleEdRange, int maxShiftX, int maxShiftY, int gradientDataThreshold, unsigned char *RESTRICT(tempGradientImage), TTabletMatchingResult &tempTabletMatchingResult)
{
	int tAngleX, tAngleY;
	unsigned short SideShapeEdgePointCount, SideShapeEdgePointCountBottom;
	unsigned short *SideShapeEdgePoint, *SideShapeEdgePointBottom;
	int tempShiftX, tempShiftY;
	int SideWallGradValue;
	int gradSum;
	int m;
	int i, j;
	int tempX, tempY;
	int tempAddress;

	int maxValue = -1;
	for (tAngleY = SideShapeMatchingYAngleStRange; tAngleY <= SideShapeMatchingYAngleEdRange; tAngleY += 2)
	{
		for (tAngleX = SideShapeMatchingXAngleStRange; tAngleX <= SideShapeMatchingXAngleEdRange; tAngleX += 4)
		{
			if (SideAngle == PLUS_45_ANGLE_CAM)
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountRight;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointRight;
				SideShapeEdgePointCountBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountBottom;
				SideShapeEdgePointBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointBottom;

				tempShiftX = TabletMatchingResult.SearchStartXForSideShape - SideData[SideAngle].SideShapeData[tAngleY][tAngleX].endX2;
			}
			else
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountLeft;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointLeft;
				SideShapeEdgePointCountBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountBottom;
				SideShapeEdgePointBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointBottom;

				tempShiftX = TabletMatchingResult.SearchStartXForSideShape - SideData[SideAngle].SideShapeData[tAngleY][tAngleX].startX2;
			}

			tempShiftY = TabletMatchingResult.SideShapeEndY - SideData[SideAngle].SideShapeData[tAngleY][tAngleX].ShapeEndY;

			for (i = tempShiftY - maxShiftY; i <= tempShiftY + maxShiftY; i += 4)
			{
				for (j = tempShiftX - maxShiftX; j <= tempShiftX + maxShiftX; j += 4)
				{
					gradSum = 0;
					for (m = 0; m < SideShapeEdgePointCount; m += 4)
					{
						//tempX = SideShapeEdgePoint[m * 2] + j;
						//tempY = SideShapeEdgePoint[m * 2 + 1] + i;

						// 읽기만하므로 속도를 위해서 범위 무시.
						// 정제는 보통 어느정도 중앙에 위치하기 때문에 회전과 약간의 이동을 하는 것으로 범위를 넘는 경우는 거의 없다.
						// 문제가 된다면 GradientImage의 상하에 패딩을 붙이면 된다.
						//if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
						//{
						//tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;
						tempAddress = (SideShapeEdgePoint[m * 2 + 1] + i) * MAX_IMAGE_WIDTH + (SideShapeEdgePoint[m * 2] + j);

						//gradSum += max(GradientImage[tempAddress], max(max(GradientImage[tempAddress + 2], GradientImage[tempAddress + 4]), max(GradientImage[tempAddress - 2], GradientImage[tempAddress - 4])));
						// 위의 코드와 아래의 코드는 속도가 같다. 어설프게 두개씩만 묶어서 max를 쓴것은 약간 더 느렸다.

						SideWallGradValue = GradientImage[tempAddress];
						if (SideWallGradValue < GradientImage[tempAddress - 2])
							SideWallGradValue = GradientImage[tempAddress - 2];
						if (SideWallGradValue < GradientImage[tempAddress - 4])
							SideWallGradValue = GradientImage[tempAddress - 4];
						if (SideWallGradValue < GradientImage[tempAddress + 2])
							SideWallGradValue = GradientImage[tempAddress + 2];
						if (SideWallGradValue < GradientImage[tempAddress + 4])
							SideWallGradValue = GradientImage[tempAddress + 4];

						gradSum += SideWallGradValue;
						//}
					}

					if (gradSum < gradientDataThreshold)
						continue;

					for (m = 0; m < SideShapeEdgePointCountBottom; m += 4)
					{
						//tempX = SideShapeEdgePointBottom[m * 2] + j;
						//tempY = SideShapeEdgePointBottom[m * 2 + 1] + i;

						//if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
						//{
						//tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;
						tempAddress = (SideShapeEdgePointBottom[m * 2 + 1] + i) * MAX_IMAGE_WIDTH + (SideShapeEdgePointBottom[m * 2] + j);

						gradSum += tempGradientImage[tempAddress] + tempGradientImage[tempAddress - 2 * MAX_IMAGE_WIDTH] + tempGradientImage[tempAddress + 2 * MAX_IMAGE_WIDTH] + tempGradientImage[tempAddress - 4 * MAX_IMAGE_WIDTH] + tempGradientImage[tempAddress + 4 * MAX_IMAGE_WIDTH];
						//}
					}

					if (maxValue < gradSum)
					{
						maxValue = gradSum;

						tempTabletMatchingResult.TabletSideShapeShiftX = j;
						tempTabletMatchingResult.TabletSideShapeShiftY = i;
						tempTabletMatchingResult.TabletSideShapeXTotion = tAngleX;
						tempTabletMatchingResult.TabletSideShapeYTotion = tAngleY;
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------
void matchSideWallFinal(int startX, int endX, int startY, int endY, int SideAngle, int SideShapeMatchingXAngleStRange, int SideShapeMatchingXAngleEdRange, int SideShapeMatchingYAngleStRange, int SideShapeMatchingYAngleEdRange, int gradientDataThreshold, unsigned char *RESTRICT(tempGradientImage), TTabletMatchingResult &tempTabletMatchingResult, TTabletMatchingResult &TabletMatchingResult)
{
	int tAngleX, tAngleY;
	unsigned short SideShapeEdgePointCount, SideShapeEdgePointCountBottom;
	unsigned short *SideShapeEdgePoint, *SideShapeEdgePointBottom;
	int tempShiftX, tempShiftY;
	int SideWallGradValue;
	int gradSum;
	int m;
	int i, j;
	int tempX, tempY;
	int tempAddress;

	int maxValue = -1;
	for (tAngleY = tempTabletMatchingResult.TabletSideShapeYTotion - 3; tAngleY <= tempTabletMatchingResult.TabletSideShapeYTotion + 3; tAngleY++)
	{
		if (tAngleY < 0 || tAngleY >= MAX_ANGLE2_RANGE * 2 + 1)
			continue;
		for (tAngleX = tempTabletMatchingResult.TabletSideShapeXTotion - 6; tAngleX <= tempTabletMatchingResult.TabletSideShapeXTotion + 6; tAngleX++)
		{
			if (tAngleX < 0 || tAngleX >= MAX_ANGLE1_RANGE * 2 + 1)
				continue;

			if (SideAngle == PLUS_45_ANGLE_CAM)
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountRight;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointRight;
				SideShapeEdgePointCountBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountBottom;
				SideShapeEdgePointBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointBottom;
			}
			else
			{
				SideShapeEdgePointCount = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountLeft;
				SideShapeEdgePoint = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointLeft;
				SideShapeEdgePointCountBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointCountBottom;
				SideShapeEdgePointBottom = SideData[SideAngle].SideShapeData[tAngleY][tAngleX].SideShapeEdgePointBottom;
			}

			for (i = tempTabletMatchingResult.TabletSideShapeShiftY - 4; i <= tempTabletMatchingResult.TabletSideShapeShiftY + 4; i += 2)
			{
				for (j = tempTabletMatchingResult.TabletSideShapeShiftX - 4; j <= tempTabletMatchingResult.TabletSideShapeShiftX + 4; j += 2)
				{
					gradSum = 0;
					for (m = 0; m < SideShapeEdgePointCount; m += 4)
					{
						//tempX = SideShapeEdgePoint[m * 2] + j;
						//tempY = SideShapeEdgePoint[m * 2 + 1] + i;

						// 읽기만하므로 속도를 위해서 범위 무시.
						// 정제는 보통 어느정도 중앙에 위치하기 때문에 회전과 약간의 이동을 하는 것으로 범위를 넘는 경우는 거의 없다.
						// 문제가 된다면 GradientImage의 상하에 패딩을 붙이면 된다.
						//if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
						//{
						//tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;
						tempAddress = (SideShapeEdgePoint[m * 2 + 1] + i) * MAX_IMAGE_WIDTH + (SideShapeEdgePoint[m * 2] + j);

						//gradSum += max(GradientImage[tempAddress], max(max(GradientImage[tempAddress + 2], GradientImage[tempAddress + 4]), max(GradientImage[tempAddress - 2], GradientImage[tempAddress - 4])));
						// 위의 코드와 아래의 코드는 속도가 같다. 어설프게 두개씩만 묶어서 max를 쓴것은 약간 더 느렸다.

						SideWallGradValue = GradientImage[tempAddress];
						if (SideWallGradValue < GradientImage[tempAddress - 2])
							SideWallGradValue = GradientImage[tempAddress - 2];
						if (SideWallGradValue < GradientImage[tempAddress - 4])
							SideWallGradValue = GradientImage[tempAddress - 4];
						if (SideWallGradValue < GradientImage[tempAddress + 2])
							SideWallGradValue = GradientImage[tempAddress + 2];
						if (SideWallGradValue < GradientImage[tempAddress + 4])
							SideWallGradValue = GradientImage[tempAddress + 4];

						gradSum += SideWallGradValue;
						//}
					}

					if (gradSum < gradientDataThreshold)
						continue;

					for (m = 0; m < SideShapeEdgePointCountBottom; m += 4)
					{
						//tempX = SideShapeEdgePointBottom[m * 2] + j;
						//tempY = SideShapeEdgePointBottom[m * 2 + 1] + i;

						//if (tempX > startX && tempX < endX && tempY > startY && tempY < endY)
						//{
						//tempAddress = tempY * MAX_IMAGE_WIDTH + tempX;
						tempAddress = (SideShapeEdgePointBottom[m * 2 + 1] + i) * MAX_IMAGE_WIDTH + (SideShapeEdgePointBottom[m * 2] + j);

						gradSum += tempGradientImage[tempAddress] + tempGradientImage[tempAddress - 2 * MAX_IMAGE_WIDTH] + tempGradientImage[tempAddress + 2 * MAX_IMAGE_WIDTH];
						//}
					}

					if (maxValue < gradSum)
					{
						maxValue = gradSum;

						TabletMatchingResult.TabletSideShapeShiftX = j;
						TabletMatchingResult.TabletSideShapeShiftY = i;
						TabletMatchingResult.TabletSideShapeXTotion = tAngleX;
						TabletMatchingResult.TabletSideShapeYTotion = tAngleY;
					}
				}
			}
		}
	}
}
//---------------------------------------------------------------------------