mrDarker
2025-07-16 1dbe46cd9d0f181d08d5a69f72d8548628a13b9d
EdgeInspector_App/Process/InspectCamera.cpp
@@ -1,4 +1,4 @@
#include "StdAfx.h"
#include "StdAfx.h"
#include "InspectCamera.h"
#include "GlassRecipe.h"
#include "HardwareSettings.h"
@@ -15,7 +15,7 @@
#endif // USE_WEBSOCKET
#define BOUNDARY_GLASSSTARTWIDTH   160
#define BOUNDARYHORIZ_LEVELDIST      8   // 荐乞 版拌急阑 茫阑 锭狼 厚背芭府
#define BOUNDARYHORIZ_LEVELDIST      8   // 수평 경계선을 찾을 때의 비교거리
#define TIME_WAIT_GLASS_START      3000
#define align_4byte(in) ((in + 3)/4)*4
@@ -188,7 +188,7 @@
   if(m_bFindGlassStart[stFrame.nScanIdx] == TRUE)
      return ERR_FINDGLASSSTART_SUCCESS;
   // 磊悼老锭 橇饭烙 荐甫 面盒洒 棱栏搁 巩力啊 救登绰芭 鞍促父... 橇饭烙 荐啊 何练窍搁 咯扁辑 公茄栏肺 吧覆.. 弊府绊 矫累急 茫疽栏搁 弊成 逞绢啊搁 登瘤 臼唱?
   // 자동일때 프레임 수를 충분히 잡으면 문제가 안되는거 같다만... 프레임 수가 부족하면 여기서 무한으로 걸림.. 그리고 시작선 찾았으면 그냥 넘어가면 되지 않나?
   if(m_pGrabber->GetGrabFrameCount() < 1)      
   {
      Sleep(0);
@@ -250,7 +250,7 @@
   while(TRUE)
   {
      Sleep(1);      // Sleep 1肺 吧绢拎具 茄促... 0栏肺 吧搁 八荤啊 救瞪嫐啊 乐澜.. 恐?
      Sleep(1);      // Sleep 1로 걸어줘야 한다... 0으로 걸면 검사가 안될‹š가 있음.. 왜?
      if(GetCheckExit() == TRUE)
      {
@@ -258,7 +258,7 @@
         break;
      }      
      // 1. 0锅 Thread肺 Start Line 茫扁
      // 1. 0번 Thread로 Start Line 찾기
      int iRetScan;
      switch(IsGlassStartLine(iThread,iRetScan))
      {
@@ -279,7 +279,7 @@
         break;
      }      
      
      // 2. Start Line阑 茫疽栏搁, 茄橇饭烙究 波郴辑 八荤 矫累
      // 2. Start Line을 찾았으면, 한프레임씩 꺼내서 검사 시작
      stFrameIndex stFrame = m_pGrabber->GetGrabFrame();
      if(stFrame.nScanIdx < 0 || stFrame.nFrameIdx < 0)
         continue;
@@ -291,7 +291,7 @@
      m_pGlassData->GetSideData(emDim)->SetFrameProc(stFrame.nFrameIdx);
      // 3. Start Line Frame 焊促 捞傈牢瘤 眉农..
      // 3. Start Line Frame 보다 이전인지 체크..
      if(CheckStartLineFrame(emDim,stFrame.nFrameIdx) == FALSE)
      {
         g_pLog->DisplayMessage(_T("Check Start Line Frame.. %s : %d"),g_SideName[(int) emDim], stFrame.nFrameIdx);
@@ -386,7 +386,7 @@
      pBlSideData->m_mBotMark.y = pSideData->m_ptBotMark_FindResult.y;
   }
   //添加此功能
   //警속늪묘콘
   pBlSideData->m_nStartLine = pSideData->m_nGlassStartLine;
   pBlSideData->m_nEndLine = pSideData->m_nGlassEndLine;
   pBlSideData->m_vDispVisionResult.clear();
@@ -400,8 +400,8 @@
   for (int i = 0; i < ngNum; i++) {
      NgInfo ng = ngArray[i];
      //if (ng.isRes)  continue;   //结果OK,继续
      //这里添加结果
      //if (ng.isRes)  continue;   //써벎OK,셨崎
      //侶쟁警속써벎
      if (5 == ng.ngType && (ng.result < ng.minValue || ng.result > ng.maxValue))
      {
@@ -428,37 +428,29 @@
BOOL CInspectCamera::CheckThreadEnd(int iThread,stFrameIndex stFrame)
{
   int         iScan = stFrame.nScanIdx;
   CSingleLock   myLoc(&m_csThreadEnd);   
   myLoc.Lock();
   // 捞固 场抄 Thread..
   if(m_iThreadEnd[iScan][iThread] == 1)
   {
   int        iScan = stFrame.nScanIdx;
   if (m_iThreadEnd[iScan][iThread] == 1) {
      myLoc.Unlock();
      return FALSE;   
   }
   BOOL      bEnd = FALSE;
   if(CheckProcessEnd(iThread,stFrame) == TRUE)
   {
   if (CheckProcessEnd(iThread, stFrame) == TRUE) {
      m_iThreadEnd[iScan][iThread] = 1;
      bEnd = TRUE;
      myLoc.Unlock();
   }
   if(bEnd == TRUE)
   {
   if (bEnd == TRUE) {
      DimensionDir emDim = GetDimension(iScan);
      m_pGlassData->GetSideData(emDim)->m_bInspection_Complete = TRUE;
            
      g_pLog->DisplayMessage(_T("%s Thread %d Process End : Scan %d, Frame %d"),PANEL_SIDE[emDim],iThread,iScan,stFrame.nFrameIdx);      
      if(GetThreadEndCount(iScan) == MAX_THREAD)
      {
      if (GetThreadEndCount(iScan) == MAX_THREAD) {
         myLoc.Lock();
         SaveFullImageCopy(iScan);
         myLoc.Unlock();
@@ -466,23 +458,9 @@
         g_pLog->DisplayMessage(_T("%s Scan Process All End"),PANEL_SIDE[emDim]);
         FinallyVisionProc(emDim);
         if(m_pII2S != NULL)
         if (m_pII2S != NULL) {
            m_pII2S->II2S_InspectionEnd(m_iCamera,iScan);      
         /*
         // Waiting All Frame Finish..
         DWORD dwTickCount = GetTickCount();
         while(GetTickCount() - dwTickCount < 5000)
         {
            if(CheckAllFrameFinish(iScan) == TRUE)
               break;
         }
         */
          //m_MsgJob.nState = 2;
          //m_MsgJob.nSide = (int)emDim;
         //m_MsgJob.nDispLine = stFrame.nFrameIdx * m_nFrameHeight;
          //::SendMessage(m_hWndViewScan, WM_POCESS_STATUS,(WPARAM)&m_MsgJob, (int)emDim);
      }      
   }
@@ -508,8 +486,9 @@
   m_nFrameFinishIdx[nCurrentScanIdx] = -1;
   for(int i=0; i<MAX_FRAM_COUNT; i++)
   for (int i = 0; i < MAX_FRAM_COUNT; i++) {
      m_bFrameFinish[nCurrentScanIdx][i] = FALSE;
   }
   cslocalLock.Unlock();
}
@@ -539,13 +518,19 @@
   CSingleLock cslocalLock(&m_csFrameFinishCheck);
   cslocalLock.Lock();
   if(m_nFrameFinishIdx[nCurrentScanIdx] == -1)
   if (m_nFrameFinishIdx[nCurrentScanIdx] == -1) {
      cslocalLock.Unlock();
      return FALSE;
   }
   for(int i=0; i<m_nFrameFinishIdx[nCurrentScanIdx]; i++)
      if(m_bFrameFinish[nCurrentScanIdx][i] == FALSE)
   for (int i = 0; i < m_nFrameFinishIdx[nCurrentScanIdx]; i++) {
      if (m_bFrameFinish[nCurrentScanIdx][i] == FALSE) {
         cslocalLock.Unlock();
         return FALSE;
      }
   }
   cslocalLock.Unlock();
   return TRUE;
}
@@ -620,8 +605,8 @@
   double dRotate_X_pxl = sin(dRotateArea_T_Rad) * dMarkToArea_Distance_pxl * dDirection;
   double dRotate_Y_pxl = cos(dRotateArea_T_Rad) * dMarkToArea_Distance_pxl * dDirection;
   bRotateArea.left = bRotateArea.right = (int) ptFind_TopMark.x + dRotate_X_pxl;
   bRotateArea.top = bRotateArea.bottom = (int) ptFind_TopMark.y + dRotate_Y_pxl;
   bRotateArea.left = bRotateArea.right = ptFind_TopMark.x + (LONG)dRotate_X_pxl;
   bRotateArea.top = bRotateArea.bottom = ptFind_TopMark.y + (LONG)dRotate_Y_pxl;
   bRotateArea.InflateRect(rtSetArea.Width()/2, rtSetArea.Height()/2);
@@ -703,21 +688,23 @@
void CInspectCamera::SaveDebugImage(DimensionDir eDim, stFrameIndex stFrame, cv::Mat image, CString strFileName)
{
   if (m_pHardparm == NULL)
   if (m_pHardparm == NULL) {
      return;
   }
   if (m_pHardparm->m_bUse_SaveDebugImage == FALSE)
   if (m_pHardparm->m_bUse_SaveDebugImage == FALSE) {
      return;
   }
   // #ifdef _DEBUG
   if (image.empty() || image.data == NULL)
   if (image.empty() || image.data == NULL) {
      return;
   }
   CString str;
   str.Format(_T("D:\\Inspection\\DebugFullImg\\%s_Side[%s]_Frame[%d].jpg"), strFileName, GetSideName(eDim), stFrame.nFrameIdx);
   try
   {
   try {
      g_pStatus->CheckDirectory(str);
      USES_CONVERSION;
@@ -725,8 +712,7 @@
      std::string filePath(asciiStr);
      cv::imwrite(filePath, image);
   }
   catch (...)
   {
   catch (...) {
      g_pLog->DisplayMessage(_T("SaveDebugImage error..."));
   }
@@ -760,14 +746,13 @@
void CInspectCamera::SaveFullImageCopy(int iScan)
{
   // 4/17
   // 畴飘合 皋葛府啊 叠妨辑 促款登绰淀..
   // return;
   if (m_pGlassData == NULL) {
      return;
   }
   
   if(m_pGlassData == NULL)
   if (m_pGrabber == NULL) {
      return;
   if(m_pGrabber == NULL)
      return;
   }
   CSingleLock      cLock(&m_csSaveimage);
   cLock.Lock();
@@ -775,8 +760,10 @@
   DimensionDir eDim = GetDimension(iScan);
   CSide_Data* pSideData = m_pGlassData->GetSideData(eDim);
   if(pSideData == NULL)
   if (pSideData == NULL) {
      cLock.Unlock();
      return;
   }
   int   nSetEnd = (pSideData->m_nGlassEndLine <= 0) ? pSideData->m_nPreGlassEndLine : pSideData->m_nGlassEndLine;
   int   nOffset = 1000;
@@ -786,30 +773,38 @@
   g_pLog->DisplayMessage(_T("%s Copy Full Image : nStartLine=%d, nEndLine=%d, nFullSize=%d"), PANEL_SIDE[eDim], nStartLine, nEndLine, nFullSize);
   if(nStartLine < 0)
   {
   if (nStartLine < 0) {
      nStartLine = 0;
   }
   if(nFullSize <= 0 || nEndLine < 0)
   if (nFullSize <= 0 || nEndLine < 0) {
      cLock.Unlock();
      return;
   }
   //if(nFullSize <= nStartLine)
   //   return;
   if (nFullSize <= nStartLine) {
      cLock.Unlock();
      return;
   }
   pINSPECTFULLIMAGE_BUFFER pImgBuf = GetFullImgBuffer(iScan);
   int   nFrameWidth,nFrameHeight;
   GetFrameSize(m_iCamera,iScan,nFrameWidth,nFrameHeight);
   if(nFrameWidth <= 0 || nFrameHeight <= 0)
   if (nFrameWidth <= 0 || nFrameHeight <= 0) {
      cLock.Unlock();
      return;
   }
   if(nFullSize/nFrameHeight > m_pGrabber->GetFrameBuffer()->GetFrameCount())
   if (nFullSize / nFrameHeight > m_pGrabber->GetFrameBuffer()->GetFrameCount()) {
      nFullSize = (m_pGrabber->GetFrameBuffer()->GetFrameCount()-1)*nFrameHeight;
   }
   if(nFullSize <= 100)
   if (nFullSize <= 100) {
      cLock.Unlock();
      return;
   }
   pImgBuf->iCamIdx = m_iCamera;
   pImgBuf->iScanIdx = iScan;
@@ -820,8 +815,8 @@
   g_pLog->DisplayMessage(_T("%s Full Image Memory Copy Start : Start[%d]-End[%d]"),PANEL_SIDE[eDim],nStartLine,nEndLine);
   LPBYTE lpHeader = m_pGrabber->GetFrameHeaderLine(iScan,nStartLine);
   if(lpHeader == NULL)
   {
   if (lpHeader == NULL) {
      cLock.Unlock();
      g_pLog->DisplayMessage(_T("%s Full Image Memory Copy fail : Start[%d]-End[%d]"),PANEL_SIDE[eDim],nStartLine,nEndLine);
      return;
   }
@@ -851,7 +846,7 @@
    if(pSideData->m_nPreGlassEndFrame+MAX_THREAD <= stFrame.nFrameIdx)
       return TRUE;
   // Glass End Line阑 茫绰 Thread 鳖瘤 柳青 吝捞搁 促 场抄芭瘤..
   // Glass End Line을 찾는 Thread 까지 진행 중이면 다 끝난거지..
   if(pSideData->m_bFindGlassEndLine == TRUE || pSideData->GetFrameProc(pSideData->m_nPreGlassEndFrame) == TRUE)
      return TRUE;
@@ -912,7 +907,7 @@
   ::SendMessage(m_hWndViewScan, WM_POCESS_STATUS,(WPARAM)&m_MsgJob, (int)emDim);
#define BOUNDARY_GLASSSTARTWIDTH_ENDLINE   140
#define BOUNDARYHORIZ_LEVELDIST_ENDLINE      50   // 荐乞 版拌急阑 茫阑 锭狼 厚背芭府
#define BOUNDARYHORIZ_LEVELDIST_ENDLINE      50   // 수평 경계선을 찾을 때의 비교거리
   int         nFrameWidth      = m_nFrameWidth;
   int         nFrameHeight   = m_nFrameHeight;
@@ -962,7 +957,7 @@
      if (j <= (pVertBuffer.GetHeight()-1-BOUNDARYHORIZ_LEVELDIST_ENDLINE))
      {
         if(pnSum[j + BOUNDARYHORIZ_LEVELDIST_ENDLINE] == 0 || pnSum[j] == 0)   // 肋 给 等 捞固瘤..
         if (pnSum[j + BOUNDARYHORIZ_LEVELDIST_ENDLINE] == 0 || pnSum[j] == 0)   // 잘 못 된 이미지..
            continue;
         nGab = abs(pnSum[j + BOUNDARYHORIZ_LEVELDIST_ENDLINE] - pnSum[j]);            
@@ -973,7 +968,7 @@
         if (nCountinueCount >= 2)
         {
            // Glass狼 矫累瘤痢阑 茫酒辑 备炼眉俊 持绰促.
            // Glass의 시작지점을 찾아서 구조체에 넣는다.
            pSideData->m_nGlassEndLine      = nStartFrame*nFrameHeight + j + 2;
            pSideData->m_nGlassEndFrame      = pSideData->m_nGlassEndLine/nFrameHeight;
            pSideData->m_bFindGlassEndLine   = TRUE;
@@ -1107,7 +1102,7 @@
      pSideData->m_nSideLineFrame[stFrame.nFrameIdx] = nFindLine;
      pSideData->m_nSideLinePosY[stFrame.nFrameIdx] = rtROI.CenterPoint().y + nFrameYPos;
      // Side 茫绊, Chamfer 茫绰芭 眠啊 鞘夸...
      // Side 찾고, Chamfer 찾는거 추가 필요...
      pSideData->m_nSide_Chamfer_LineFrame[stFrame.nFrameIdx] = nFindLine + 0;
      g_pLog->DisplayMessage(_T("Find Side Line Frame[%d], Side[%d]"), stFrame.nFrameIdx, nFindLine);
@@ -1120,8 +1115,7 @@
{
   CSide_Data*      pSideData = m_pGlassData->GetSideData(emDim);
   if(pSideData == NULL)
   {
   if (pSideData == NULL) {
      g_pLog->DisplayMessage(_T("Side Line Find Fail - Glass Data or HW Setting Error"));
      return FALSE;
   }
@@ -1161,8 +1155,9 @@
   CRect rtROI;
   rtROI.IntersectRect(rtFrame_pxl, rtAllSide_pxl);
   if(rtROI.IsRectEmpty() || rtROI.IsRectNull())
   if (rtROI.IsRectEmpty() || rtROI.IsRectNull()) {
      return FALSE;
   }
   // 4. Exception Notch Area...
   std::vector<CRect> vectorInspectAreaList_New;
@@ -1176,8 +1171,7 @@
   std::vector<CRect> vecInspectArea;
   for(int i=0; i<nNotchCount; i++)
   {
   for (int i = 0; i < nNotchCount; i++) {
      CRect rtNotchArea = pSideData->m_rtNotchArea_pxl[i];
      rtNotchArea.left = GetLeftMargin(m_iCamera,stFrame.nScanIdx);
      rtNotchArea.right = m_nFrameWidth-1;
@@ -1186,30 +1180,27 @@
      std::copy(vectorInspectAreaList_New.begin(),  vectorInspectAreaList_New.end(), vectorInspectAreaList_Old.begin());
      vectorInspectAreaList_New.clear();
      for(int j=0; j<vectorInspectAreaList_Old.size(); j++)
      {
      for (int j = 0; j < vectorInspectAreaList_Old.size(); j++) {
         CRect rtTemp = vectorInspectAreaList_Old[j];
         CRect rtNotchInterSectRect;
         rtNotchInterSectRect.IntersectRect(rtTemp,  rtNotchArea);
         if(rtNotchInterSectRect.IsRectEmpty() || rtNotchInterSectRect.IsRectNull())                        // 畴摹啊 救吧府绰 版快俊绰 促矫 持绢霖促..
         {
         if (rtNotchInterSectRect.IsRectEmpty() || rtNotchInterSectRect.IsRectNull()) {
            vectorInspectAreaList_New.push_back(rtTemp);
            continue;
         }
         if(rtTemp.Height() == rtNotchInterSectRect.Height())                                       // Notch啊 康开狼 傈眉牢 版快
         if (rtTemp.Height() == rtNotchInterSectRect.Height()) {
            continue;
         }
         if(rtTemp.top == rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom)            // 1. Notch Frame 困俊 吧赴 版快
         {
         if (rtTemp.top == rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom) {
            CRect rtNew = rtTemp;
            rtNew.top = rtNotchInterSectRect.bottom;
            vectorInspectAreaList_New.push_back(rtNew);
         }
         else if(rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom)         // 2. Notch Frame 吝埃俊 吧赴 版快
         {
         else if (rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom) {
            CRect rtNewTop = rtTemp;
            rtNewTop.top = rtNotchInterSectRect.bottom;
            vectorInspectAreaList_New.push_back(rtNewTop);
@@ -1218,8 +1209,7 @@
            rtNewBot.bottom = rtNotchInterSectRect.top;
            vectorInspectAreaList_New.push_back(rtNewBot);
         }
         else if(rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom == rtTemp.bottom)         // 3. Notch Frame 酒贰俊 吧赴 版快
         {
         else if (rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom == rtTemp.bottom) {
            CRect rtNew = rtTemp;
            rtNew.bottom = rtNotchInterSectRect.top;
            vectorInspectAreaList_New.push_back(rtNew);
@@ -1227,12 +1217,9 @@
      }
   }
   // 5. Side List..
   vecInspectArea.clear();
   for(int i=0; i<vectorInspectAreaList_New.size(); i++)
   {
   for (int i = 0; i < vectorInspectAreaList_New.size(); i++) {
      CRect rtInspectArea = vectorInspectAreaList_New[i];
      vecInspectArea.push_back(rtInspectArea);
   }
@@ -1253,13 +1240,12 @@
#endif
   int stepSide = m_pGrabber->GetFrameBuffer()->GetFrameHeight();
   for(int i=0; i<vecInspectArea.size(); i++)
   {
   for (int i = 0; i < vecInspectArea.size(); i++) {
      CSISBuffer frameBuffer(m_pGrabber->GetFrameHeaderLine(stFrame.nScanIdx, vecInspectArea[i].top),nFrameWidth,vecInspectArea[i].Height());
      if(frameBuffer.IsValidBuffer() == FALSE)
      if (frameBuffer.IsValidBuffer() == FALSE) {
         continue;
      }
#ifdef _DEBUG
      CString str;
@@ -1271,8 +1257,7 @@
      CRect rtFindROI = vecInspectArea[i];
      rtFindROI.OffsetRect(0, -rtFindROI.top);
      if(nSearchHeight < rtFindROI.Height())
      {
      if (nSearchHeight < rtFindROI.Height()) {
         CPoint ptCenter = rtFindROI.CenterPoint();
         rtFindROI.top = ptCenter.y - (nSearchHeight/2);
         rtFindROI.bottom = ptCenter.y + (nSearchHeight/2);
@@ -1281,8 +1266,7 @@
      int         nSideLine = -1;
      EdgeFind.FindEdge_ToRightROI(&frameBuffer,nSideLine,DEFECT_EDGE_AUTO_PITCH,nThres,DEFECT_EDGE_AUTO_RATIO,0,rtFindROI);
      if(nSideLine != -1)
      {
      if (nSideLine != -1) {
#if HALCON_VISION_KEY
         if (NULL != pBlSideData){
            SideLineInf inf;
@@ -1295,7 +1279,7 @@
            int idx = (int)(vecInspectArea[i].top / stepSide);
            pBlSideData->m_mapSideLineInf[idx] = inf;
            //打印边界数据
            //댔丹긋썹鑒앴
            //Log_GetDebug()->TraceInfo("Find Side Line %d: %d, %d, (%d, %d, %d, %d), %d",
            //   idx,
            //   inf.nSideLine, inf.nThres,
@@ -1314,24 +1298,21 @@
         chamferIns.Binarization(frameBuffer,ImgInsBin,nChamferLineThres);
         chamferIns.FindRightLine_Bin(ImgInsBin,nSideLine,dLine);
         if(dLine != -1)
         {
         if (dLine != -1) {
            nFindSideChamferLineSum += (int)dLine;
            nFindSideChamferLineCount++;
         }
      }
   }
   if(nFindSideLineCount > 0)
   {
      int nAvgSideLine = (int) (nFindSideLineSum / nFindSideLineCount);   // Average..
   if (nFindSideLineCount > 0) {
      int nAvgSideLine = (int)(nFindSideLineSum / nFindSideLineCount);
      m_iSideLine[stFrame.nScanIdx] = nAvgSideLine;
      pSideData->m_nSideLineFrame[stFrame.nFrameIdx] = nAvgSideLine;      
      pSideData->m_nSideLinePosY[stFrame.nFrameIdx] = rtROI.CenterPoint().y + nFrameYPos;
   }
   if(nFindSideChamferLineCount > 0)
   {
   if (nFindSideChamferLineCount > 0) {
      int nAvgChamferLine = (int) (nFindSideChamferLineSum / nFindSideChamferLineCount);   // Average..
      pSideData->m_nSide_Chamfer_LineFrame[stFrame.nFrameIdx] = nAvgChamferLine;            // Chamfer Line
   }
@@ -1344,53 +1325,57 @@
   CSingleLock localLock(&m_csTopCorner);
   localLock.Lock();
   if(m_pGlassData == NULL)
   if (m_pGlassData == NULL) {
      localLock.Unlock();
      return FALSE;
   }
   CSide_Data* pSideData = m_pGlassData->GetSideData(emDim);
   CSIDE_PARM* pSideParam = &m_pRecipe->m_SideParam[(int) emDim];
   if(pSideData == NULL || pSideParam == NULL)
   if (pSideData == NULL || pSideParam == NULL) {
      localLock.Unlock();
      return FALSE;   
   }
   if(pSideData->m_bFindGlassStartLine == FALSE)
   if (pSideData->m_bFindGlassStartLine == FALSE) {
      localLock.Unlock();
      return FALSE;
   }
   if(pSideData->m_bTopCorner_Find == TRUE)
   if (pSideData->m_bTopCorner_Find == TRUE) {
      localLock.Unlock();
      return TRUE;
   }
   /*
   BOOL bTopCornerUse = (BOOL) (m_pRecipe->m_SideParam[emDim].m_nTopCornerShape == 0) ? FALSE : TRUE;
   // Not Use Corner Inspection
   if(bTopCornerUse == FALSE)
   if (bTopCornerUse == FALSE) {
      localLock.Unlock();
      return FALSE;
   */
   }
   double dCornerSizeY_Um   = (double) m_pRecipe->m_SideParam[emDim].m_nTopCornerSizeY_um;
   int nCornerSizeY_Pixel   = (int) GetUmToPixel_Y(dCornerSizeY_Um);
   int nFrameHeight      = m_nFrameHeight;
   int nFindStartLine      = pSideData->m_nGlassStartLine;
   int nTopCornerFrameIndex = (nFindStartLine + nCornerSizeY_Pixel) / nFrameHeight;
   if(stFrame.nFrameIdx < nTopCornerFrameIndex+1)
   if (stFrame.nFrameIdx < nTopCornerFrameIndex + 1) {
      localLock.Unlock();
      return FALSE;
   }
   int nSideLine = pSideData->m_nSideLineFrame[nTopCornerFrameIndex+1];
   int nSideLine = (int)pSideData->m_nSideLineFrame[nTopCornerFrameIndex + 1];
   if(nSideLine == -1)
   if (nSideLine == -1) {
      localLock.Unlock();
      return FALSE;
   }
   double dCornerSizeX_Um = (double) m_pRecipe->m_SideParam[emDim].m_nTopCornerSizeX_um;
   int nCornerSizeX_Pixel = (int) GetUmToPixel_X(dCornerSizeX_Um);
   CRect rtTopCornerArea(0, 0, nCornerSizeX_Pixel, nCornerSizeY_Pixel);
   rtTopCornerArea.OffsetRect(nSideLine, nFindStartLine);
   pSideData->m_bTopCorner_Find = TRUE;
@@ -1399,8 +1384,12 @@
   int nTopCornerType = m_pRecipe->m_SideParam[emDim].m_nTopCornerShape;
   pSideData->m_nTopCornerShape = m_pRecipe->m_SideParam[emDim].m_nTopCornerShape;;
   if(pSideParam->m_bTopCornerFindDefect == FALSE)
   if (pSideParam->m_bTopCornerFindDefect == FALSE) {
      localLock.Unlock();
      return FALSE;
   }
   localLock.Unlock();
   if(nTopCornerType == (int) 1)                     return FindTopCorner_CCut(iThread, emDim, stFrame);
   else if(nTopCornerType == (int) 2)                  return FindTopCorner_RCut(iThread, emDim, stFrame);
@@ -1715,33 +1704,46 @@
   CSingleLock localLock(&m_csThreadTopAlignMark);
   localLock.Lock();
   if(m_pGlassData == NULL)
   if (m_pGlassData == NULL) {
      localLock.Unlock();
      return FALSE;
   }
   CSide_Data*   pSideData = m_pGlassData->GetSideData(eDim);
   if(pSideData == NULL)
   if (pSideData == NULL) {
      localLock.Unlock();
      return FALSE;
   }
   if(pSideData->m_bFindGlassStartLine == FALSE)
   if (pSideData->m_bFindGlassStartLine == FALSE) {
      localLock.Unlock();
      return FALSE;
   }
   if(pSideData->m_bTopMark_Find == TRUE)
   if (pSideData->m_bTopMark_Find == TRUE) {
      localLock.Unlock();
      return TRUE;
   }
    if(pSideData->m_bTopCorner_Find == FALSE)
   if (pSideData->m_bTopCorner_Find == FALSE) {
      localLock.Unlock();
       return FALSE;
   }
   BOOL bTopAlignMark = (BOOL) (m_pRecipe->m_SideParam[eDim].m_bTopMark_Use == 1) ? TRUE : FALSE;
   if(bTopAlignMark == FALSE)
   if (bTopAlignMark == FALSE) {
      localLock.Unlock();
      return FindTopAlignMark_Virtual(iThread, eDim, stFrame);
   else
   {
      if(FindTopAlignMark_TempleteMatching(iThread, eDim, stFrame) == FALSE)
   }
   else {
      if (FindTopAlignMark_TempleteMatching(iThread, eDim, stFrame) == FALSE) {
         localLock.Unlock();
         return FindTopAlignMark_Virtual(iThread, eDim, stFrame);
      else
      }
      else {
         localLock.Unlock();
         return TRUE;
      }
   }
}
@@ -1777,7 +1779,7 @@
   if((int) stFrame.nFrameIdx < nTopAlignMarkYPramIdx + 1)
      return FALSE;
   int nSideLine               = pSideData->m_nSideLineFrame[nTopAlignMarkYPramIdx + 1];
   int nSideLine = (int)pSideData->m_nSideLineFrame[nTopAlignMarkYPramIdx + 1];
   double nEdgeToTopAlignMarkX_Um   = m_pRecipe->m_SideParam[eDim].m_nTopMarkToEdgeX_um;
   int nEdgeToTopAlignMarkX_Pixel  = (int) GetUmToPixel_X(nEdgeToTopAlignMarkX_Um);
   int nTopAlignMarkX_Pixel      = nSideLine + nEdgeToTopAlignMarkX_Pixel;
@@ -1824,7 +1826,7 @@
    if((int) stFrame.nFrameIdx < nTopAlignMarkYPramIdx + 1)
       return FALSE;
   int nSideLine               = pSideData->m_nSideLineFrame[nTopAlignMarkYPramIdx + 1];
   int nSideLine = (int)pSideData->m_nSideLineFrame[nTopAlignMarkYPramIdx + 1];
   if(nSideLine <= 0)
   {
@@ -1940,55 +1942,60 @@
   CSingleLock localLock(&m_csBotCorner);
   localLock.Lock();
   if(m_pGlassData == NULL)
   if (m_pGlassData == NULL) {
      localLock.Unlock();
      return FALSE;
   }
   CSide_Data* pSideData = m_pGlassData->GetSideData(emDim);
   CSIDE_PARM* pSideParam = &m_pRecipe->m_SideParam[(int) emDim];
   if(pSideData == NULL || pSideParam == NULL)
   if (pSideData == NULL || pSideParam == NULL) {
      localLock.Unlock();
      return FALSE;
   }
   if (pSideData->m_bFindGlassStartLine == FALSE || pSideData->m_bFindGlassEndLine == FALSE)
   if (pSideData->m_bFindGlassStartLine == FALSE || pSideData->m_bFindGlassEndLine == FALSE) {
      localLock.Unlock();
      return FALSE;
   }
   if(pSideData->m_bBotCorner_Find == TRUE)
   if (pSideData->m_bBotCorner_Find == TRUE) {
      localLock.Unlock();
      return TRUE;
   }
   /*
   BOOL bBotCornerUse = (BOOL) (m_pRecipe->m_SideParam[emDim].m_nBottomCornerShape == 0) ? FALSE : TRUE;
   if(bBotCornerUse == FALSE)
   if (bBotCornerUse == FALSE) {
      localLock.Unlock();
      return FALSE;
   */
   }
   double dCornerSizeY_Um = (double) m_pRecipe->m_SideParam[emDim].m_nBottomCornerSizeY_um;
   int nCornerSizeY_Pixel = (int) GetUmToPixel_Y(dCornerSizeY_Um);
   int nFrameHeight      = m_nFrameHeight;
   int nFindBottomLine      = (pSideData->m_bFindGlassEndLine == TRUE) ? pSideData->m_nGlassEndLine : pSideData->m_nPreGlassEndLine;
   int nBottomCornerFrameIndex = (nFindBottomLine - nCornerSizeY_Pixel) / nFrameHeight;
   if((int) stFrame.nFrameIdx < nBottomCornerFrameIndex)
   if ((int)stFrame.nFrameIdx < nBottomCornerFrameIndex) {
      localLock.Unlock();
      return FALSE;
   }
   // 澜.. 内呈 Side 扼牢捞 捞傈 橇饭烙俊 乐栏搁 绢录瘤.. 茄搁俊 Thread 啊 咯矾俺 倒搁?... 困 橇饭烙狼 Thread 啊 酒流 Side 扼牢阑 给茫疽栏搁.. 绊刮 粱 秦焊磊..
   // 荤捞靛喊 Thread绰 窍唱父 倒绊 八荤 Thread甫 咯矾俺 倒府绰 备炼肺 官层具摆促
   int nSideLine = pSideData->m_nSideLineFrame[nBottomCornerFrameIndex];
   nSideLine = (nSideLine == -1) ? pSideData->m_nSideLineFrame[nBottomCornerFrameIndex] : nSideLine;
   // 음.. 코너 Side 라인이 이전 프레임에 있으면 어쩌지.. 한면에 Thread 가 여러개 돌면?... 위 프레임의 Thread 가 아직 Side 라인을 못찾았으면.. 고민 좀 해보자..
   // 사이드별 Thread는 하나만 돌고 검사 Thread를 여러개 돌리는 구조로 바꿔야겠다
   int nSideLine = (int)pSideData->m_nSideLineFrame[nBottomCornerFrameIndex];
   nSideLine = (nSideLine == -1) ? (int)pSideData->m_nSideLineFrame[nBottomCornerFrameIndex] : nSideLine;
   if(nSideLine == -1)
   if (nSideLine == -1) {
      localLock.Unlock();
      return FALSE;
   }
   double dCornerSizeX_Um = (double) m_pRecipe->m_SideParam[emDim].m_nBottomCornerSizeX_um;
   int nCornerSizeX_Pixel = (int) GetUmToPixel_X(dCornerSizeX_Um);
   CRect rtBottomCornerArea(0, 0, nCornerSizeX_Pixel, nCornerSizeY_Pixel);
   rtBottomCornerArea.OffsetRect(nSideLine, nFindBottomLine-nCornerSizeY_Pixel);
   pSideData->m_bBotCorner_Find = TRUE;
@@ -1999,8 +2006,12 @@
   int nBottomCornerType = m_pRecipe->m_SideParam[emDim].m_nBottomCornerShape;
   if(pSideParam->m_bBottomCornerFindDefect == FALSE)
   if (pSideParam->m_bBottomCornerFindDefect == FALSE) {
      localLock.Unlock();
      return FALSE;
   }
   localLock.Unlock();
   if(nBottomCornerType == (int) 1)                  return FindBotCorner_CCut(iThread, emDim, stFrame);
   else if(nBottomCornerType == (int) 2)               return FindBotCorner_RCut(iThread, emDim, stFrame);
@@ -2306,30 +2317,43 @@
   CSingleLock localLock(&m_csThreadBotAlignMark);
   localLock.Lock();
   if(m_pGlassData == NULL)
   if (m_pGlassData == NULL) {
      localLock.Unlock();
      return FALSE;
   }
   CSide_Data*   pSideData = m_pGlassData->GetSideData(eDim);
   if(pSideData == NULL)
   if (pSideData == NULL) {
      localLock.Unlock();
      return FALSE;
   }
    //if(pSideData->m_bBotCorner_Find == FALSE)
    //   return FALSE;
   if (pSideData->m_bBotCorner_Find == FALSE) {
      localLock.Unlock();
      return FALSE;
   }
   if(pSideData->m_bBotMark_Find == TRUE)
   if (pSideData->m_bBotMark_Find == TRUE) {
      localLock.Unlock();
      return TRUE;
   }
   BOOL bBotAlignMark = (BOOL) (m_pRecipe->m_SideParam[eDim].m_bBottomMark_Use == 1) ? TRUE : FALSE;
   if(bBotAlignMark == FALSE)
   if (bBotAlignMark == FALSE) {
      localLock.Unlock();
      return FindBotAlignMark_Virtual(iThread, eDim, stFrame);
   else
   {
      if(FindBotAlignMark_TempleteMatching(iThread, eDim, stFrame) == FALSE)
   }
   else {
      if (FindBotAlignMark_TempleteMatching(iThread, eDim, stFrame) == FALSE) {
         localLock.Unlock();
         return FindBotAlignMark_Virtual(iThread, eDim, stFrame);
      else
      }
      else {
         localLock.Unlock();
         return TRUE;
      }
   }
}
@@ -2366,7 +2390,7 @@
   if((int) stFrame.nFrameIdx < nGlassEndLineFrame)
      return FALSE;
   int nSideLine               = pSideData->m_nSideLineFrame[nBottomCornerTopFrameIndex];
   int nSideLine = (int)pSideData->m_nSideLineFrame[nBottomCornerTopFrameIndex];
   double nEdgeToBotAlignMarkX_Um   = m_pRecipe->m_SideParam[eDim].m_nBottomMarkToEdgeX_um;
   int nEdgeToBotAlignMarkX_Pixel  = (int) GetUmToPixel_X(nEdgeToBotAlignMarkX_Um);
   int nBottomAlignMarkX_Pixel      = nSideLine + nEdgeToBotAlignMarkX_Pixel;
@@ -2414,7 +2438,7 @@
   if((int) stFrame.nFrameIdx < nGlassEndLineFrame)
      return FALSE;
   int nSideLine               = pSideData->m_nSideLineFrame[nBottomCornerTopFrameIndex];
   int nSideLine = (int)pSideData->m_nSideLineFrame[nBottomCornerTopFrameIndex];
   if(nSideLine <= 0)
   {
      CRect      rectSide = CRect(0,nBottomAlignMarkY_Pixel,nFrameWidth-1,nBottomAlignMarkY_Pixel);
@@ -2577,7 +2601,7 @@
   if(pSideData == NULL)
      return FALSE;
   if(pSideData->m_nSideLineFrame[iFrame] < 0)      // Side Line阑 给茫篮 版快..
   if (pSideData->m_nSideLineFrame[iFrame] < 0)      // Side Line을 못찾은 경우..
      return FALSE;
   // 1. Make Corner Area
@@ -2593,13 +2617,13 @@
   // 2. Max Inspect Area..
   CRect rtFrame;
   rtFrame.left               = pSideData->m_nSide_Chamfer_LineFrame[iFrame];
   rtFrame.left = (LONG)pSideData->m_nSide_Chamfer_LineFrame[iFrame];
   rtFrame.top                  = iFrame * m_nFrameHeight;
   rtFrame.right               = rtFrame.left;
   rtFrame.bottom               = rtFrame.top + m_nFrameHeight;
   
   //rtFrame.top                  = rtFrame.top;   // 困肺 炼陛 歹 八荤秦辑 搬窃捞 钦媚瘤霸 秦焊磊..
   rtFrame.top                  = rtFrame.top - 100;   // 100 Pixel Frame 唱床柳 版快 巩力啊 登聪, 吝汗 八荤窍磊..
   //rtFrame.top                  = rtFrame.top;   // 위로 조금 더 검사해서 결함이 합쳐지게 해보자..
   rtFrame.top = rtFrame.top - 100;   // 100 Pixel Frame 나눠진 경우 문제가 되니, 중복 검사하자..
   int nMaxInspectRange         = 0;
@@ -2642,7 +2666,7 @@
   if(pSideData == NULL)
      return FALSE;
   if(pSideData->m_nSideLineFrame[iFrame] < 0)      // Side Line阑 给茫篮 版快..
   if (pSideData->m_nSideLineFrame[iFrame] < 0)      // Side Line을 못찾은 경우..
      return FALSE;
   // 1. Make Corner Area
@@ -2658,13 +2682,13 @@
   // 2. Max Inspect Area..
   CRect rtFrame;
   rtFrame.left               = pSideData->m_nSide_Chamfer_LineFrame[iFrame];
   rtFrame.left = (LONG)pSideData->m_nSide_Chamfer_LineFrame[iFrame];
   rtFrame.top                  = iFrame * m_nFrameHeight;
   rtFrame.right               = rtFrame.left;
   rtFrame.bottom               = rtFrame.top + m_nFrameHeight;
   //rtFrame.top                  = rtFrame.top;   // 困肺 炼陛 歹 八荤秦辑 搬窃捞 钦媚瘤霸 秦焊磊..
   rtFrame.top                  = rtFrame.top - 100;   // 100 Pixel Frame 唱床柳 版快 巩力啊 登聪, 吝汗 八荤窍磊..
   //rtFrame.top                  = rtFrame.top;   // 위로 조금 더 검사해서 결함이 합쳐지게 해보자..
   rtFrame.top = rtFrame.top - 100;   // 100 Pixel Frame 나눠진 경우 문제가 되니, 중복 검사하자..
   int nMaxInspectRange         = 0;
@@ -2728,22 +2752,22 @@
         CRect rtNotchInterSectRect;
         rtNotchInterSectRect.IntersectRect(rtTemp,  rtNotchArea);
         if(rtNotchInterSectRect.IsRectEmpty() || rtNotchInterSectRect.IsRectNull())      // 畴摹啊 救吧府绰 版快俊绰 促矫 持绢霖促..
         if (rtNotchInterSectRect.IsRectEmpty() || rtNotchInterSectRect.IsRectNull())      // 노치가 안걸리는 경우에는 다시 넣어준다..
         {
            vectorInspectAreaList_New.push_back(rtTemp);
            continue;
         }
         if(rtTemp.Height() == rtNotchInterSectRect.Height())   // Notch啊 康开狼 傈眉牢 版快
         if (rtTemp.Height() == rtNotchInterSectRect.Height())   // Notch가 영역의 전체인 경우
            continue;
         
         if(rtTemp.top == rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom)            // 1. Notch Frame 困俊 吧赴 版快
         if (rtTemp.top == rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom)            // 1. Notch Frame 위에 걸린 경우
         {
            CRect rtNew = rtTemp;
            rtNew.top = rtNotchInterSectRect.bottom;
            vectorInspectAreaList_New.push_back(rtNew);
         }
         else if(rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom)         // 2. Notch Frame 吝埃俊 吧赴 版快
         else if (rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom < rtTemp.bottom)         // 2. Notch Frame 중간에 걸린 경우
         {
            CRect rtNewTop = rtTemp;
            rtNewTop.top = rtNotchInterSectRect.bottom;
@@ -2753,7 +2777,7 @@
            rtNewBot.bottom = rtNotchInterSectRect.top;
            vectorInspectAreaList_New.push_back(rtNewBot);
         }
         else if(rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom == rtTemp.bottom)         // 3. Notch Frame 酒贰俊 吧赴 版快
         else if (rtTemp.top < rtNotchInterSectRect.top && rtNotchInterSectRect.bottom == rtTemp.bottom)         // 3. Notch Frame 아래에 걸린 경우
         {
            CRect rtNew = rtTemp;
            rtNew.bottom = rtNotchInterSectRect.top;
@@ -2781,7 +2805,7 @@
   if (rtInspectArea.IsRectEmpty() == TRUE || m_pGlassData == NULL)
      return FALSE;
   int nSideLine = m_pGlassData->GetSideData(eDim)->m_nSideLineFrame[iFrame];
   int nSideLine = (int)m_pGlassData->GetSideData(eDim)->m_nSideLineFrame[iFrame];
   if (nSideLine < 0)
      return FALSE;
@@ -3400,7 +3424,7 @@
   cv::Mat pBinMat = cv::Mat::zeros(pBinImage->GetHeight(), pBinImage->GetWidth(), CV_8UC1);
   // OwnerBuffer 郴何肺 16硅荐 皋葛府 爱绊 乐绢辑 茄锅俊 墨乔 救蹬..
   // OwnerBuffer 내부로 16배수 메모리 갖고 있어서 한번에 카피 안돼..
   for(int i=0; i<pBinImage->GetHeight(); i++)
      CopyMemory(&pBinMat.data[pBinMat.step1() * i], pBinImage->GetDataAddress(0, i), pBinImage->GetWidth());
@@ -3426,7 +3450,7 @@
   int numOfLables = cv::connectedComponentsWithStats(pBinMat, matLabel, matStats, matCentrois, 8);
   if(numOfLables <= 1)   // 硅版 窍唱父 乐澜..
   if (numOfLables <= 1)   // 배경 하나만 있음..
      return TRUE;
   std::vector<int> vecMin, vecMax, vecSum, vecPixelCount;
@@ -3480,7 +3504,7 @@
      int width = matStats.at<int>(nIdx, cv::CC_STAT_WIDTH);
      int height = matStats.at<int>(nIdx, cv::CC_STAT_HEIGHT);
      int x = (int)matCentrois.at<double>(nIdx, 0); //吝缴谅钎
      int x = (int)matCentrois.at<double>(nIdx, 0); //중심좌표
      int y = (int)matCentrois.at<double>(nIdx, 1);
      if(MAX_DEFECT_COUNT_SIDE < nIdx)
@@ -3564,7 +3588,7 @@
   cv::Mat pBinMat = cv::Mat::zeros(pBinImage->height, pBinImage->width, CV_8UC1);
   // OwnerBuffer 郴何肺 16硅荐 皋葛府 爱绊 乐绢辑 茄锅俊 墨乔 救蹬..
   // OwnerBuffer 내부로 16배수 메모리 갖고 있어서 한번에 카피 안돼..
   for(int i=0; i<pBinImage->height; i++)
      CopyMemory(&pBinMat.data[pBinMat.step1() * i], &pBinImage->imageData[pBinImage->widthStep*i], pBinImage->width);
@@ -3590,7 +3614,7 @@
   int numOfLables = cv::connectedComponentsWithStats(pBinMat, matLabel, matStats, matCentrois, 8);
   if(numOfLables <= 1)   // 硅版 窍唱父 乐澜..
   if (numOfLables <= 1)   // 배경 하나만 있음..
      return TRUE;
   std::vector<int> vecMin, vecMax, vecSum, vecPixelCount;
@@ -3644,7 +3668,7 @@
      int width = matStats.at<int>(nIdx, cv::CC_STAT_WIDTH);
      int height = matStats.at<int>(nIdx, cv::CC_STAT_HEIGHT);
      int x = (int)matCentrois.at<double>(nIdx, 0); //吝缴谅钎
      int x = (int)matCentrois.at<double>(nIdx, 0); //중심좌표
      int y = (int)matCentrois.at<double>(nIdx, 1);
      if(MAX_DEFECT_COUNT_SIDE < nIdx)
@@ -3728,7 +3752,7 @@
   if(pSideData == NULL)
      return FALSE;
   if(pSideData->m_bFindGlassEndLine == FALSE)      // Notch Processing篮 End Line 茫篮 Frame捞..
   if (pSideData->m_bFindGlassEndLine == FALSE)      // Notch Processing은 End Line 찾은 Frame이..
      return FALSE;
   if(m_pRecipe == NULL)
@@ -3763,7 +3787,7 @@
      g_pLog->DisplayMessage(_T("Notch_Process %s : %d, idx %d - %d,%d,%d,%d"),g_SideName[(int) emDim], stFrame.nFrameIdx,i,rtRotateArea.left,rtRotateArea.top,rtRotateArea.right,rtRotateArea.bottom);
      
      pSideData->m_rtNotchArea_pxl[i] = rtRotateArea;
      //屏蔽Notch   区域上下位置
      //팁귁Notch   혐堵 龜쫍뿟?
      rtRotateArea.top += 15;
      rtRotateArea.bottom -= 15;
      IplImage* pOriginImage;
@@ -3844,30 +3868,30 @@
      return;
   }
   // 中值滤波器大小,用于去噪,必须为奇数(若偶数会自动减1)
   // 櫓令쫀꺼포댕鬼,痰黨혼臀,극伎槨펜鑒(흼탉鑒삔菱땡숑1)
   const int nSmoothFilter = m_pRecipe->m_SideParam[(int)emDim].m_NotchPrm[nNotchIdx].m_nSmooth_Filter;
   // 磨边区域阈值,控制反二值化阈值,主要用于提取 Notch 边缘特征
   // 칠긋혐堵埴令,왠齡럽랗令뺏埴令,寮狼痰黨瓊혤 Notch 긋鍍景瀝
   const int nGrindThreshold = m_pRecipe->m_SideParam[(int)emDim].m_NotchPrm[nNotchIdx].m_nGrind_Threshold;
   // 玻璃区域阈值,控制正二值化阈值,用于提取玻璃主体区域
   // 깼젝혐堵埴令,왠齡攣랗令뺏埴令,痰黨瓊혤깼젝寮竟혐堵
   const int nGlassThreshold = m_pRecipe->m_SideParam[(int)emDim].m_NotchPrm[nNotchIdx].m_nGlass_Threshold;
   // 确保奇数滤波器大小
   // 횅괏펜鑒쫀꺼포댕鬼
   int nFilterSize = (nSmoothFilter % 2 == 0) ? (nSmoothFilter - 1) : nSmoothFilter;
   if (nFilterSize > 1) {
      // 中值滤波降噪
      // 櫓令쫀꺼슉臀
      cvSmooth(scr, img_Bin, CV_MEDIAN, nFilterSize);
   }
   // Edge图:反二值化阈值处理
   // Edge暠:럽랗令뺏埴令뇹잿
   cvThreshold(scr, img_Edge, nGrindThreshold, 255, CV_THRESH_BINARY_INV);
   CString strEdgeFileName;
   strEdgeFileName.Format(_T("Notch\\Notch_%d_Edge.jpg"), nNotchIdx);
   SaveDebugImage(emDim, stFrame, img_Edge, strEdgeFileName);
   // Bin图:正常二值化
   // Bin暠:攣끽랗令뺏
   cvThreshold(scr, img_Bin, nGlassThreshold, 255, CV_THRESH_BINARY);
   CString strBinFileName;
   strBinFileName.Format(_T("Notch\\Notch_%d_Bin.jpg"), nNotchIdx);
@@ -3897,13 +3921,13 @@
   ptSeed.y = img_Bin_MeasureLine->height / 2;
   img_Find_MeasureLine = cvCloneImage(img_Bin_MeasureLine);
   // cvFloodFill(img_Find_MeasureLine, ptSeed, CV_RGB(255,255,255));      // 拱 锭巩俊 急捞 谗绢瘤绰 版快啊 乐绢辑 救茄促.
   // cvFloodFill(img_Find_MeasureLine, ptSeed, CV_RGB(255,255,255));      // 물 때문에 선이 끊어지는 경우가 있어서 안한다.
   // cvThreshold(img_Find_MeasureLine, img_Find_MeasureLine, 100, 255, CV_THRESH_BINARY);
   // 2. Find Curve Line
   double dStepAngle = 1.0;
   int nFindStep = (int) (360.0 / dStepAngle);
   int nSkipStep = 30;                                          // 贸澜 30档, 场 30档绰 胶诺茄促.
   int nSkipStep = 30;                                          // 처음 30도, 끝 30도는 스킵한다.
   IplImage* img_Find_MeasureLine_Point = cvCloneImage(scr);
@@ -3941,7 +3965,7 @@
   // 3. Meausre
   int nCount = vecPointList.size()-2;
   if(nCount < MAX_SIDE_NOTCH_MEASURE_COUNT)   // 螟沥 器牢飘 俊矾
   if (nCount < MAX_SIDE_NOTCH_MEASURE_COUNT)   // 측정 포인트 에러
   {
      cvReleaseImage(&img_Bin_MeasureLine);
      cvReleaseImage(&img_Find_MeasureLine);
@@ -3950,11 +3974,11 @@
      return;
   }
   double dPointStep = (double) nCount / (MAX_SIDE_NOTCH_MEASURE_COUNT-1);      // 16 Point 螟沥..
   double dPointStep = (double)nCount / (MAX_SIDE_NOTCH_MEASURE_COUNT - 1);      // 16 Point 측정..
   for(int nMeasureIdx=0; nMeasureIdx < MAX_SIDE_NOTCH_MEASURE_COUNT; nMeasureIdx++)
   {
      int nPointIdx = nMeasureIdx * dPointStep;
      int nPointIdx = nMeasureIdx * (int)dPointStep;
      if(nPointIdx == 0)                     nPointIdx = 1;
      else if(vecPointList.size() <= nPointIdx)   nPointIdx = vecPointList.size() - 1;
@@ -3993,8 +4017,8 @@
      {
         double dMeasureRadius = dSearchRadius - dRadius;
         int nX = pt_Measure.x - (cos(dAngle_Radian) * dMeasureRadius);
         int nY = pt_Measure.y + (sin(dAngle_Radian) * dMeasureRadius);
         int nX = (int)(pt_Measure.x - (cos(dAngle_Radian) * dMeasureRadius));
         int nY = (int)(pt_Measure.y + (sin(dAngle_Radian) * dMeasureRadius));
         img_Result->imageData[pt_Measure.y * img_Result->widthStep + pt_Measure.x] = 0;
@@ -4182,7 +4206,7 @@
         if(pSideData->m_nNotch_Dimension_Edge_Judge[nNotchIdx][nMeasureIdx] != 1)
         {
            // Make 搬窃..
            // Make 결함..
            CRect rtDefectArea = CRect(pSideData->m_ptNotch_Dimension_Edge[nNotchIdx][nMeasureIdx].x,
                                 pSideData->m_ptNotch_Dimension_Edge[nNotchIdx][nMeasureIdx].y,
                                 pSideData->m_ptNotch_Dimension_Edge[nNotchIdx][nMeasureIdx].x,
@@ -4216,7 +4240,7 @@
         pSideData->m_nNotch_Chamfer_Edge_Judge[nNotchIdx][nMeasureIdx] = 1;
#if MINI_LED
         //选取测量研磨尺寸最大值
         //朞혤꿎좆桔칠넹당離댕令
         if (m_nChamferOffset_um < dChamfer_um) {
            m_nChamferOffset_um = dChamfer_um;
         }
@@ -4234,7 +4258,7 @@
         if(pSideData->m_nNotch_Chamfer_Edge_Judge[nNotchIdx][nMeasureIdx] != 1)
         {
            // Make 搬窃..
            // Make 결함..
            CRect rtDefectArea = CRect(pSideData->m_ptNotch_Chamfer_Edge[nNotchIdx][nMeasureIdx].x,
                                 pSideData->m_ptNotch_Chamfer_Edge[nNotchIdx][nMeasureIdx].y,
                                 pSideData->m_ptNotch_Chamfer_Edge[nNotchIdx][nMeasureIdx].x,
@@ -4288,7 +4312,7 @@
   int nThreshold = pNotchParam->m_nNotch_Inspect_Defect_Threshold;
   int nOffset = pNotchParam->m_nNotch_Inspect_Defect_Offset;
#if MINI_NOTCH   //Defect检测开关, jiang
#if MINI_NOTCH   //Defect쇱꿎역밑, jiang
   int nEdgeThres = pNotchParam->m_nGrind_Threshold;
   int nNgType = pNotchParam->m_nNotch_Inspect_Defect_Judge_And;
   int xNgSize = pNotchParam->m_nNotch_Inspect_Defect_Judge_X_um;
@@ -4298,7 +4322,7 @@
      nNotchIdx, nEdgeThres, nThreshold, nOffset, nNgType,
      rtROI.left, rtROI.top, rtROI.right, rtROI.bottom, __LINE__);
   //输出计算信息
   //渴놔셕炬斤口
   std::vector<Point2D> vNorchPoints;
   for (int nMeasureIdx = 0; nMeasureIdx < MAX_SIDE_NOTCH_MEASURE_COUNT; nMeasureIdx++){
      CPoint ptChamfer_pxl = m_pGlassData->GetSideData(emDim)->m_ptNotch_Chamfer_Edge[nNotchIdx][nMeasureIdx];
@@ -4308,7 +4332,7 @@
        vNorchPoints.push_back(point);
   }
   CRect defectResult[100];
   //nRet为缺陷数量,当前缺陷数 < 1时,表示为OK
   //nRet槨홧屈鑒좆,뎠품홧屈鑒 < 1珂,깊刻槨OK
   int nRet = BlVision_GetSoftVisionApp()->findNorchDefect(nSide, vNorchPoints, rtROI, nEdgeThres, nOffset, nThreshold, nNgType, xNgSize, yNgSize, defectResult);
   printf("Norch Defect Number:%d\n", nRet);
@@ -4336,7 +4360,7 @@
   m_nChamferOffset_um = 0;
#endif // MINI_LED
   // 防止过大导致耗时或内存问题
   // 렝岺법댕돔鈴봬珂샀코닸狂痙
   if (nOffset > 50) {
      g_pLog->DisplayMessage(_T("[WARN] Notch defect offset capped to 50 pixels, original: %d"), nOffset);
      nOffset = 50;
@@ -4417,7 +4441,7 @@
   SaveDebugImage(emDim, stFrame, pProcBinImage, strTemp);
#if   MINI_LED == 0
   // 限制膨胀次数,防止过大导致耗时或内存问题
   // 掘齡툇郞늴鑒,렝岺법댕돔鈴봬珂샀코닸狂痙
   int nDilateCount = pNotchParam->m_nNotch_Inspect_Defect_dilate;
   nDilateCount = std::max(0, std::min(nDilateCount, 50));
   if (pNotchParam->m_nNotch_Inspect_Defect_dilate != nDilateCount) {
@@ -4444,8 +4468,8 @@
   int iFrame = stFrame.nFrameIdx;
   CRect rtProcessArea = rtROI;
   
   int nMinSize_X = pNotchParam->m_nNotch_Inspect_Defect_Min_X_um / m_pGlassData->GetSideData(emDim)->m_dPixelSizeX;
   int nMinSize_Y = pNotchParam->m_nNotch_Inspect_Defect_Min_Y_um / m_pGlassData->GetSideData(emDim)->m_dPixelSizeY;
   int nMinSize_X = (pNotchParam->m_nNotch_Inspect_Defect_Min_X_um / (int)m_pGlassData->GetSideData(emDim)->m_dPixelSizeX);
   int nMinSize_Y = (pNotchParam->m_nNotch_Inspect_Defect_Min_Y_um / (int)m_pGlassData->GetSideData(emDim)->m_dPixelSizeY);
   int nMinSize = 0;
   int nDilation = 0;
@@ -4481,7 +4505,7 @@
   if(pSideData == NULL)
      return;
   if(pSideData->m_bFindGlassEndLine == FALSE)      // Notch Processing篮 End Line 茫篮 Frame捞..
   if (pSideData->m_bFindGlassEndLine == FALSE)      // Notch Processing은 End Line 찾은 Frame이..
      return;
   if(m_pRecipe == NULL)
@@ -4598,8 +4622,8 @@
         double dCenterPos_Y_pxl = rtROI.top + (dCenterPos_Y_um / m_dPixelSizeY);
         pSideData->m_nNotchCircle_Radius_Judge[nNotchIdx][nCircleIdx] = 1;
         pSideData->m_ptNotchCircle_Center_pxl[nNotchIdx][nCircleIdx].x = dCenterPos_X_pxl;
         pSideData->m_ptNotchCircle_Center_pxl[nNotchIdx][nCircleIdx].y = dCenterPos_Y_pxl;
         pSideData->m_ptNotchCircle_Center_pxl[nNotchIdx][nCircleIdx].x = (LONG)dCenterPos_X_pxl;
         pSideData->m_ptNotchCircle_Center_pxl[nNotchIdx][nCircleIdx].y = (LONG)dCenterPos_Y_pxl;
         pSideData->m_ptNotchCircle_Radius_Result_X_pxl[nNotchIdx][nCircleIdx] = dRadiuse_um / m_dPixelSizeX;
         pSideData->m_ptNotchCircle_Radius_Result_Y_pxl[nNotchIdx][nCircleIdx] = dRadiuse_um / m_dPixelSizeY;
         pSideData->m_ptNotchCircle_Radius_Result_um[nNotchIdx][nCircleIdx] = dRadiuse_um;
@@ -4667,7 +4691,7 @@
   Measure_BotCorner(iThread, emDim, stFrame);
   if(pSideData->m_bFindGlassEndLine == FALSE)      // Side Line篮 End Line 茫篮 Frame 捞 螟沥 茄促..
   if (pSideData->m_bFindGlassEndLine == FALSE)      // Side Line은 End Line 찾은 Frame 이 측정 한다..
      return FALSE;
   g_pLog->DisplayMessage(_T("Side Measure Start %s - %d"),g_SideName[(int) emDim],stFrame.nFrameIdx);
@@ -4698,7 +4722,7 @@
   ///////////////////////////////////////////////////////////////////////////////////////
#if HALCON_VISION_KEY
   //Set Info, 将MARK信息输入到SDK
   //Set Info, 쉥MARK斤口渴흙돕SDK
   CBlSideData* pBlSideData = BlVision_GetVisionRecipe()->getSideData(emDim); // ->m_bInspection_Complete = TRUE;
   pBlSideData->m_bInspection_Complete = TRUE;
   pBlSideData->m_dPixelSizeX = pSideData->m_dPixelSizeX;
@@ -4843,7 +4867,7 @@
      int nSideLine = rtFindPos.left + nFindEdgeLine;
      int nChamferLine = rtFindPos.left + ((nFindChamferLine != -1 && pSideParam->m_bSideChamfer_Use[nMeasurePointIdx] == TRUE) ? nFindChamferLine : nFindEdgeLine);
      // Frame俊辑 措钎肺 茫篮吧肺 螟沥 搬苞肺 静霸 沁绰单.... 捞固瘤俊辑 促矫 茫档废 荐沥捞 鞘夸..且鳖??
      // Frame에서 대표로 찾은걸로 측정 결과로 쓰게 했는데.... 이미지에서 다시 찾도록 수정이 필요..할까??
      int nFrameIdx = (int)(nEndPointPosY / m_nFrameHeight);
      /*
      int nSideLine = pSideData->m_nSideLineFrame[nFrameIdx];
@@ -4867,7 +4891,7 @@
      
      pSideData->m_bSideMeasureLine[nMeasurePointIdx] = TRUE;
      // 烙矫肺 阜澜. SW 促款.
      // 임시로 막음. SW 다운.
      // continue;
      // Dimension
@@ -4922,7 +4946,7 @@
      }
      else if (pSideParam->m_bSideDimension_Use[nMeasurePointIdx] == TRUE && dTempValue < -0.01) {
#if HALCON_VISION_KEY
         //切割线的长度计算,  jiang, 09-27, 2024
         //학몇窟돨낀똑셕炬,  jiang, 09-27, 2024
         int nThres = pSideParam->m_nSideDimensionSpec_Thres[nMeasurePointIdx];
         int toLineDist = 1000 * pSideParam->m_dSideDimensionSpec_mm_Std[nMeasurePointIdx];
         int toTopY = pSideParam->m_nSideDimensionPos_TopMarkToDistance_um[nMeasurePointIdx];
@@ -5120,7 +5144,7 @@
   if(iCornerFrame > stFrame.nFrameIdx)
      return FALSE;
   // Corner 八荤 父甸扁..
   // Corner 검사 만들기..
   if(FindCorner(iThread,emDim,stFrame,0,rtTopCornerArea) == TRUE)
   {
      double dXSizeStd_um = pSideParam->m_nTopCorner_Measure_Judge_Std_um_X;
@@ -5387,8 +5411,8 @@
      int nSearchRange = 8;
      int nSearchRangeX = (int) ((double)nCornerSizeX_Pixel * 0.10);   // 10%俊辑 茫澜..
      int nSearchRangeY = (int) ((double)nCornerSizeY_Pixel * 0.10);   // 10%俊辑 茫澜..
      int nSearchRangeX = (int)((double)nCornerSizeX_Pixel * 0.10);   // 10%에서 찾음..
      int nSearchRangeY = (int)((double)nCornerSizeY_Pixel * 0.10);   // 10%에서 찾음..
      nSearchRangeX = nSearchRangeY = 10;
      
@@ -5568,7 +5592,7 @@
   if(pSideData == NULL || pSideParam == NULL)
      return FALSE;
   // R Corner 螟沥
   // R Corner 측정
   return TRUE;
}
@@ -5622,7 +5646,7 @@
   if(iCornerFrame > stFrame.nFrameIdx)
      return FALSE;
   // Corner 八荤 父甸扁..
   // Corner 검사 만들기..
   if(FindCorner(iThread,emDim,stFrame,1,rtBottomCornerArea) == TRUE)
   {
      double dXSizeStd_um = pSideParam->m_nBottomCorner_Measure_Judge_Std_um_X;
@@ -5696,7 +5720,7 @@
   if(pSideData == NULL)
      return FALSE;
   if(pSideData->m_bFindGlassEndLine == FALSE)      // User Defect Processing篮 End Line 茫篮 Frame捞..
   if (pSideData->m_bFindGlassEndLine == FALSE)      // User Defect Processing은 End Line 찾은 Frame이..
      return FALSE;
   if(m_pRecipe == NULL)
@@ -5814,7 +5838,7 @@
   if(pSideData == NULL)
      return FALSE;
   if(pSideData->m_bFindGlassEndLine == FALSE)      // Exception Area Processing篮 End Line 茫篮 Frame捞..
   if (pSideData->m_bFindGlassEndLine == FALSE)      // Exception Area Processing은 End Line 찾은 Frame이..
      return FALSE;
   if(m_pRecipe == NULL)
@@ -6084,7 +6108,7 @@
      while (TRUE)
      {
         nRet = m_pGrabber->IsAcqFrame(iFrame);
         if (nRet < 2)      // 酒流 救嘛躯栏骨肺 措扁.
         if (nRet < 2)      // 아직 안찍혔으므로 대기.
         {
            if (GetTickCount() - dwT1 > TIME_WAIT_GLASS_START)
            {
@@ -6119,7 +6143,7 @@
      {
         for (k = nHStart; k < nHStart + BOUNDARY_GLASSSTARTWIDTH; k++)
         {
            // Grab牢 版快 滚欺狼 版拌俊辑 1024扼牢阑 逞绢哎 版快 促澜 滚欺甫 啊廉客具 茄促.
            // Grab인 경우 버퍼의 경계에서 1024라인을 넘어갈 경우 다음 버퍼를 가져와야 한다.
            if (j == nFrameHeight && nLine != 0 && bChangeBuffer == FALSE)
            {               
               DWORD dwTick = GetTickCount();
@@ -6161,7 +6185,7 @@
            if (nCountinueCount >= 2)
            {
               g_pLog->DisplayMessage(_T("%s Start Line Frame %d, Line %d, Gab %d, Thres %d"), PANEL_SIDE[emDim],iFrame, j, nGab, nThreshold);
               // Glass狼 矫累瘤痢阑 茫酒辑 备炼眉俊 持绰促.
               // Glass의 시작지점을 찾아서 구조체에 넣는다.
               pSideData->m_nGlassStartLine = iFrame * nFrameHeight + j - 2;
               pSideData->m_nGlassStartFrame = pSideData->m_nGlassStartLine/nFrameHeight;
               pSideData->m_bFindGlassStartLine = TRUE;
@@ -6229,10 +6253,10 @@
BOOL CInspectCamera::ProcessFrame(int iThread,DimensionDir emDim,stFrameIndex stFrame)
{
   try {
      // 1. Find End Line.. (Corner甫 力寇茄 Side扼牢阑 茫扁 困秦 End Line何磐 茫绰促)
      // 1. Find End Line.. (Corner를 제외한 Side라인을 찾기 위해 End Line부터 찾는다)
      FindEndLine(iThread, emDim, stFrame);
      // 2. Find Side Line.. (秦寸 橇饭烙俊辑 Corner/Notch 甫 力寇茄 Side 扼牢 茫扁)
      // 2. Find Side Line.. (해당 프레임에서 Corner/Notch 를 제외한 Side 라인 찾기)
#if USE_AI_DETECT
      FindSideLine(iThread, emDim, stFrame);
#else
@@ -6341,7 +6365,7 @@
   if (m_wsClients != nullptr)
   {
      m_wsClients->set_message_handler([this](const std::string& msg) {
         // JSON 格式
         // JSON 목駕
         if (!msg.empty() && msg.size() > 3) {
            try {
               nlohmann::json json_data = nlohmann::json::parse(msg);
@@ -6349,7 +6373,7 @@
               DimensionDir emDim = (DimensionDir)(json_data.value("nLineType", 0) - 1);
               WSReceiveData wsReceiveData;
               // 解析 JSON 数据并填充 wsReceiveData
               // 썩驕 JSON 鑒앴깻輕념 wsReceiveData
               wsReceiveData.nRecipe = json_data.value("Reciepe", 0);
               wsReceiveData.nLineType = json_data.value("nLineType", 0);
               wsReceiveData.nIndex = json_data.value("nIndex", 0);
@@ -6357,9 +6381,9 @@
               wsReceiveData.nFrameIdx = json_data.value("nFrameIdx", 0);
               wsReceiveData.strSN = json_data.value("SN", "");
               wsReceiveData.dTimeStamp = json_data.value("TimeStamp", 0.0);
               wsReceiveData.nGNum = json_data.value("NGnum", 0);  // NG 数量
               wsReceiveData.nGNum = json_data.value("NGnum", 0);  // NG 鑒좆
               // 格式化并显示基本信息
               // 목駕뺏깻鞫刻샘굶斤口
               CString strText;
               strText.Format(_T("Reciepe: %d, LineType: %d, Index: %d, SN: %s, NGNum: %d, TimeStamp: %f\n"),
                  wsReceiveData.nRecipe, wsReceiveData.nLineType, wsReceiveData.nIndex, wsReceiveData.strSN.c_str(), wsReceiveData.nGNum, wsReceiveData.dTimeStamp);
@@ -6369,7 +6393,7 @@
                  && wsReceiveData.nGNum != 0
                  && g_pBase->m_strHPanelID.Compare(CString(wsReceiveData.strSN.c_str())) == 0)
               {
                  // 遍历并填充 ngPosArray
                  // 깁저깻輕념 ngPosArray
                  auto ngPosArray = json_data["NGPosArray"];
                  for (auto& ngPos : ngPosArray) {
                     WSReceiveData::NGPosition ngPosition;
@@ -6403,12 +6427,12 @@
               g_pLog->DisplayMessage(strText);
            }
            catch (const nlohmann::json::parse_error& e) {
               // 处理 JSON 解析错误
               // 뇹잿 JSON 썩驕댄轎
               std::cerr << "JSON parse error: " << e.what() << std::endl;
            }
         }
         else {
            // 处理空消息
            // 뇹잿왕句口
            std::cerr << "Empty message" << std::endl;
         }
      });
@@ -6448,15 +6472,15 @@
   std::string jsonMetadata = CreateJsonWSSendData(wsData);
   // 预先分配内存,减少动态扩容
   // 渡邱롸토코닸,숑 帽檄윈휭
   std::vector<char> message;
   message.reserve(jsonMetadata.size() + width * height);
   // 插入 JSON 数据和图像数据
   // 꿨흙 JSON 鑒앴뵨暠獗鑒앴
   message.insert(message.end(), jsonMetadata.begin(), jsonMetadata.end());
   message.insert(message.end(), data, data + width * height);
   // 使用 std::move 传递 message,避免拷贝
   // 賈痰 std::move 눈뒵 message,긁출옙굔
   bool isSucceed = m_wsClients->send_binary_to_idle_client_with_retry(std::move(message), 1000, 50);
   if (!isSucceed)
   {   
@@ -6583,7 +6607,7 @@
      for (int i = 0; i < rtProcessArea.Height(); i++)
         memcpy(pDefectProcessImage.GetDataAddress(0, i), frameBuffer.GetDataAddress(rtProcessArea.left, i), rtProcessArea.Width());
      // 发送从临时缓冲区中获取的数据
      // 랙箇닖줄珂뻠녑혐櫓삿혤돨鑒앴
      int state = (stFrame.nFrameIdx == 0) ? 1 : (pDefectProcessImage.GetHeight() < IMAGE_HEIGHT ? 2 : 3);
      SendImageDataOverWebSocket(eDim, stFrame, stFrame.nFrameIdx, pDefectProcessImage.GetDataAddress(0, 0), state, nCutLine, vecInspectAreaList[nRegionIdx].top, pDefectProcessImage.GetWidth(), pDefectProcessImage.GetHeight());
@@ -6676,7 +6700,7 @@
         rtProcessArea.bottom += nHeightOff;
      }
      // 保证图像高度为4的倍数
      // 괏聯暠獗멕똑槨4돨굡鑒
      nHeightOff = rtProcessArea.Height() % 4;
      if (nHeightOff != 0)
      {
@@ -6704,7 +6728,7 @@
      myLoc.Lock();
      long long dStartTime = GetCurrentTimestamp();
      // 发送从临时缓冲区中获取的数据
      // 랙箇닖줄珂뻠녑혐櫓삿혤돨鑒앴
      std::vector<AiDetectResult> results;
      if (m_pAiDetectEx->detect(frame, results, m_strChannel)) {
         for (auto& result : results) {