c6e24163c3c852b4ac3e45d100b9253b3db0e182..df45966c52bac2eb465cf05c1d6328bf0d00c5ac
2025-08-16 mrDarker
1. 补提交添加保存图像的类
df4596 对比 | 目录
2025-08-16 mrDarker
1. 添加一个线程类,保存全图使用队列一个一个保存
5e802e 对比 | 目录
已添加2个文件
已修改11个文件
481 ■■■■ 文件已修改
EdgeInspector_App/EdgeInspector_App.rc 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/EdgeInspector_App.vcxproj 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/EdgeInspector_App.vcxproj.filters 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/InterfaceManager.cpp 120 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/InterfaceManager.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/MainFrm.cpp 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Recipe/HardwareSettings.cpp 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Recipe/HardwareSettings.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Thread/Thread_SaveFullImg.cpp 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/Thread/Thread_SaveFullImg.h 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/View/ViewMain_HWSetting.cpp 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/View/ViewMain_ScanImage.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/resource.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
EdgeInspector_App/EdgeInspector_App.rc
Binary files differ
EdgeInspector_App/EdgeInspector_App.vcxproj
@@ -323,6 +323,7 @@
    <ClInclude Include="Thread\PriorityThread.h" />
    <ClInclude Include="Thread\Thread_CheckSeq.h" />
    <ClInclude Include="Thread\Thread_ControlIF.h" />
    <ClInclude Include="Thread\Thread_SaveFullImg.h" />
    <ClInclude Include="Thread\Thread_Send.h" />
    <ClInclude Include="Thread\Thread_ViewRefresh.h" />
    <ClInclude Include="TriggerBoard\TriggerControl_Mvsol.h" />
@@ -407,6 +408,7 @@
    <ClCompile Include="Thread\PriorityThread.cpp" />
    <ClCompile Include="Thread\Thread_CheckSeq.cpp" />
    <ClCompile Include="Thread\Thread_ControlIF.cpp" />
    <ClCompile Include="Thread\Thread_SaveFullImg.cpp" />
    <ClCompile Include="Thread\Thread_Send.cpp" />
    <ClCompile Include="Thread\Thread_ViewRefresh.cpp" />
    <ClCompile Include="TriggerBoard\TriggerControl_Mvsol.cpp" />
EdgeInspector_App/EdgeInspector_App.vcxproj.filters
@@ -579,6 +579,9 @@
    <ClInclude Include="WebSocket\WebSocketClientPool.h">
      <Filter>WebSocket</Filter>
    </ClInclude>
    <ClInclude Include="Thread\Thread_SaveFullImg.h">
      <Filter>Thread</Filter>
    </ClInclude>
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="stdafx.cpp">
@@ -803,6 +806,9 @@
    <ClCompile Include="WebSocket\WebSocketClientPool.cpp">
      <Filter>WebSocket</Filter>
    </ClCompile>
    <ClCompile Include="Thread\Thread_SaveFullImg.cpp">
      <Filter>Thread</Filter>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <ResourceCompile Include="EdgeInspector_App.rc">
EdgeInspector_App/InterfaceManager.cpp
@@ -46,6 +46,7 @@
    m_pThread_SendMSG            = NULL;
    m_pThread_CheckSequence        = NULL;
    m_pThread_ViewRefresh        = NULL;
    m_pSaveFullImgThread        = NULL;
    m_nScanStartCount            = 0;
    m_bGlassLoadingEnd            = FALSE;
    m_pLicense                    = NULL;
@@ -117,26 +118,29 @@
BOOL CInterfaceManager::DestroyThread()
{
    if(m_pThread_SendMSG != NULL)
    {
    if(m_pThread_SendMSG != nullptr) {
        m_pThread_SendMSG->StopThread();
        delete m_pThread_SendMSG;    
    }
    m_pThread_SendMSG = NULL;
    m_pThread_SendMSG = nullptr;
    if(m_pThread_CheckSequence != NULL)
    {
    if(m_pThread_CheckSequence != nullptr) {
        m_pThread_CheckSequence->StopThread();
        delete m_pThread_CheckSequence;
    }
    m_pThread_CheckSequence = NULL;
    m_pThread_CheckSequence = nullptr;
    if(m_pThread_ViewRefresh != NULL)
    {
    if(m_pThread_ViewRefresh != nullptr) {
        m_pThread_ViewRefresh->StopThread();
        delete m_pThread_ViewRefresh;
    }
    m_pThread_ViewRefresh = NULL;
    m_pThread_ViewRefresh = nullptr;
    if (m_pSaveFullImgThread != nullptr) {
        m_pSaveFullImgThread->StopThread();
        delete m_pSaveFullImgThread;
    }
    m_pSaveFullImgThread = nullptr;
    return TRUE;
}
@@ -488,13 +492,13 @@
BOOL CInterfaceManager::PostProcess()
{
    g_pLog->DisplayMessage(_T("Start PostProcess()"));
    g_pLog->DisplayMessage(_T("Start PostProcess"));
    if(m_pThread_CheckSequence != NULL)
    if (m_pThread_CheckSequence != NULL) {
        m_pThread_CheckSequence->ClearMessage();
    }
    if(m_GlassData.IsPostProcStart() == TRUE)
    {
    if(m_GlassData.IsPostProcStart() == TRUE) {
        g_pLog->DisplayMessage(_T("Already Post Processing Start"));
        return TRUE;
    }
@@ -505,7 +509,6 @@
    m_PostProcess.SetView((void*) m_pView);
    m_PostProcess.RunPostProcessing();
    // é¦†é›çŸ« Scan End è„šé¾‹ ç„Šéƒ´æ‰ å‚ˆä¿Š ç§¦æ‹Žå…·èŒ„促... Scan End è„šé¾‹ ç„Šå°˜é”­ Loading捞 è‚¯ä¸° æ•‘登搁 æ•‘焊郴瘤霸 é˜œå›šä¹æ¾œ..
    m_bGlassLoadingEnd = FALSE;
    m_bPLCGlassLoading = FALSE;
@@ -522,34 +525,22 @@
    SetSlashText(GLOBAL_DEFINE::emHide, _T(""), RGB(0, 0, 0));
    
    if (m_pView != NULL)
    {
    if (m_pView != NULL) {
        m_pView->SetGlassData(&m_GlassData);
        m_pView->RefreshDefect(m_PostProcess.GetResultPath(EM_RESULT_DEFECT));
        if (m_pThread_ViewRefresh != NULL)
        {
        if (m_pThread_ViewRefresh != NULL) {
            m_pThread_ViewRefresh->AddViewMSG(0, 0, VIEW_REFRESH_INSPECT_END, 0);
        }
    }
    //CheckExpireDay();    // Check License Defect..
    WritePLCResult();
    // Loading Ack
    // m_pControlInterface->SendControlBit(emSnd_Short_ScanEnd_Ack, TRUE);
    if (m_pThread_SendMSG != NULL)
    if (m_pThread_SendMSG != NULL) {
        m_pThread_SendMSG->AddSendMSG(SEND_MESSAGE_CONTROLSIGNAL, IToC_Ack_ShortScanEndSuccess, TRUE);
    }
    if (0 == nDefectCount)
    {
        AfxBeginThread(CopyImageThreadFunc, this);
    }
    else
    {
        Save_FullImg();
    }
    Save_FullImg();
#if MINI_LED
    m_PostProcess.RunPostProcessing_SaveFiltImage();
@@ -571,7 +562,7 @@
{    
    CInterfaceManager *pMain = static_cast<CInterfaceManager*>(pParam);
    
    pMain->Save_FullImg();
    pMain->Save_FullImg();
    return 0;
}
@@ -615,9 +606,43 @@
            g_pLog->DisplayMessage(_T("Sava %s iCam=%d, iScan=%d, szImage.cx=%ld, pBuffer->szImage.cy=%ld"), PANEL_SIDE[pCamera->m_eDimension], iCam, iScan, pBuffer->szImage.cx, pBuffer->szImage.cy);
            if (pBuffer->pImage != NULL && pBuffer->szImage.cx > 100 && pBuffer->szImage.cy > 100) {
            if (m_pSaveFullImgThread != nullptr && pBuffer->pImage != nullptr && pBuffer->szImage.cx > 100 && pBuffer->szImage.cy > 100) {
                strFileJpg.Format(_T("%s\\%s_%s_%s.jpg"), strFolder, strPanelID, PANEL_SIDE[pCamera->m_eDimension], g_pBase->m_strLoadingTime);
                SaveFullImageModern(strFileJpg, pBuffer->pImage, pBuffer->szImage.cx, pBuffer->szImage.cy, (int)pCamera->m_eDimension, 0, nQuality);
                if (pHard->m_bAsyncFullSave) {
                    CT2A aPath(strFileJpg);
                    SaveImgJob job;
                    job.strPath = std::string(aPath);
                    job.nWidth = (int)pBuffer->szImage.cx;
                    job.nHeight = (int)pBuffer->szImage.cy;
                    job.nBpp = 1;
                    job.nStride = job.nWidth * job.nBpp;
                    job.nStartY = 0;
                    job.nDimension = (int)pCamera->m_eDimension;
                    job.nQuality = nQuality;
                    // æ·±æ‹·è´
                    DWORD dwCopyStart = GetTickCount();
                    Lock();
                    size_t nBytes = size_t(job.nStride) * job.nHeight;
                    job.vData.resize(nBytes);
                    memcpy(job.vData.data(), pBuffer->pImage, nBytes);
                    Unlock();
                    DWORD dwCopyEnd = GetTickCount();
                    g_pLog->DisplayMessage(_T("Full Image Copy Time = %d ms, Bytes=%zu, File=%s"), dwCopyEnd - dwCopyStart, nBytes, strFileJpg);
                    // å…¥é˜Ÿ
                    m_pSaveFullImgThread->Enqueue(std::move(job));
                    g_pLog->DisplayMessage(_T("Full Image Save : %s, Width=%d, Height=%d, Dimension=%s, Quality=%d"), strFileJpg, pBuffer->szImage.cx, pBuffer->szImage.cy, PANEL_SIDE[pCamera->m_eDimension], nQuality);
                }
                else {
                    DWORD dwCopyStart = GetTickCount();
                    Lock();
                    SaveFullImageModern(strFileJpg, pBuffer->pImage, pBuffer->szImage.cx, pBuffer->szImage.cy, (int)pCamera->m_eDimension, 0, nQuality);
                    Unlock();
                    DWORD dwCopyEnd = GetTickCount();
                    g_pLog->DisplayMessage(_T("Full Image Save Time = %d ms, File=%s"), dwCopyEnd - dwCopyStart, strFileJpg);
                }
            }
        }
    }
@@ -1784,42 +1809,27 @@
void CInterfaceManager::CreateThread()
{
    m_pThread_SendMSG = new CThread_SendMSG();
    if(m_pThread_SendMSG != NULL)
    {
    if(m_pThread_SendMSG != NULL) {
        m_pThread_SendMSG->SetIN2P(static_cast<IPLCSendInterface2Parent*>(this));
        m_pThread_SendMSG->CreateThread();
    }
    m_pThread_CheckSequence = new CThread_CheckSequence();
    if(m_pThread_CheckSequence != NULL)
    {
    if(m_pThread_CheckSequence != NULL) {
        m_pThread_CheckSequence->SetC2M(static_cast<ICheckSequenceInterface2Parent*>(this),&m_GlassData);
        m_pThread_CheckSequence->CreateThread();
    }
    m_pThread_ViewRefresh = new CThread_ViewRefresh();
    if(m_pThread_ViewRefresh != NULL)
    {
    if(m_pThread_ViewRefresh != NULL) {
        m_pThread_ViewRefresh->SetIN2P(static_cast<IViewRefreshInterface2Parent*>(this));
        m_pThread_ViewRefresh->CreateThread();
    }
    /*
    m_pThread_NetMSG = new CThread_NetMSG();
    if(m_pThread_NetMSG != NULL)
    {
        m_pThread_NetMSG->SetIN2P(static_cast<IPLCReceiveInterface2Parent*>(this));
        m_pThread_NetMSG->CreateThread();
    m_pSaveFullImgThread = new CThread_SaveFullImg();
    if (m_pSaveFullImgThread != NULL) {
        m_pSaveFullImgThread->CreateThread();
    }
    m_pThread_Light = new CThread_Light();
    if(m_pThread_Light != NULL)
    {
        m_pThread_Light->CreateThread();
    }
    */
}
LPBYTE CInterfaceManager::GetGrabBuffer(DimensionDir eDir,int iVline)
EdgeInspector_App/InterfaceManager.h
@@ -18,6 +18,7 @@
#include "LanguageControl.h"
#include "LicenseChecker.h"
#include "TriggerControl_Mvsol.h"
#include "Thread_SaveFullImg.h"
#if USE_WEBSOCKET
#include "WebSocketClientPool.h"
@@ -161,6 +162,7 @@
    CThread_SendMSG*            m_pThread_SendMSG;    
    CThread_CheckSequence*        m_pThread_CheckSequence;
    CThread_ViewRefresh*        m_pThread_ViewRefresh;
    CThread_SaveFullImg*        m_pSaveFullImgThread;
    CPostProcess                m_PostProcess;
    CCriticalSection            m_csScanStart;
    int                            m_nScanStartCount;
EdgeInspector_App/MainFrm.cpp
@@ -433,97 +433,41 @@
            std::vector<DimensionDir> vecDirs = {
                DIMENSION_A_RIP, DIMENSION_B_RIP, DIMENSION_C_RIP, DIMENSION_D_RIP
            };
            pMain->Simulate2PhaseScanByDirs(vecDirs, 1000);
            pMain->Simulate2PhaseScanByDirs(vecDirs, 3000);
        }
        break;
    case PROC_MODE_SIMULATION_SIDESHORT:
        {            
            //pMain->m_GlassData.SetScanStartCount(MAX_CAMERA_COUNT);
            //pMain->SendGlassRecipe();
            //for(iCam=0;iCam<MAX_CAMERA_COUNT;iCam++)
            //{
            //    pMain->ScanStartGrabManual(iCam,0);
            //    pMain->ScanStartManual(iCam,0);
            //}
            std::vector<DimensionDir> vecDirs = {
                DIMENSION_B, DIMENSION_D, DIMENSION_B_DN, DIMENSION_D_DN
            };
            pMain->Simulate2PhaseScanByDirs(vecDirs, 1000);
            pMain->Simulate2PhaseScanByDirs(vecDirs, 3000);
        }
        break;
    case PROC_MODE_SIMULATION_SIDELONG:
        {                
            //pMain->m_GlassData.SetScanStartCount(MAX_CAMERA_COUNT);
            //pMain->SendGlassRecipe();
            //for(iCam=0;iCam<MAX_CAMERA_COUNT;iCam++)
            //{
            //    pMain->ScanStartGrabManual(iCam,1);
            //    pMain->ScanStartManual(iCam,1);
            //}
            std::vector<DimensionDir> vecDirs = {
                DIMENSION_A, DIMENSION_C, DIMENSION_A_DN, DIMENSION_C_DN
            };
            pMain->Simulate2PhaseScanByDirs(vecDirs, 1000);
            pMain->Simulate2PhaseScanByDirs(vecDirs, 3000);
        }
        break;
    case PROC_MODE_SIMULATION_ALL:
        {
            //g_pLog->DisplayMessage(_T("Simulation Full "));
            //pMain->m_GlassData.SetScanStartCount(MAX_DIMENSION_COUNT);
            //pMain->SendGlassRecipe();
            //for(iCam=0;iCam<MAX_CAMERA_COUNT;iCam++)
            //{
            //    pMain->ScanStartGrabManual(iCam,0);
            //    pMain->ScanStartGrabManual(iCam,1);
            //}
            //if(pMain->m_pThread_CheckSequence != NULL)
            //{
            //    double dOneScanTime = pMain->m_GlassRecipe.m_RecieParm.m_dOneScanTime_sec;
            //
            //    pMain->m_pThread_CheckSequence->AddCheckSeqMSG(CHECKSEQ_GLASS_INSPECTION_OVERTIME,(int)(dOneScanTime* 1000.),pMain->m_HardwareRecipe.GetScanCount()-1);
            //    g_pLog->DisplayMessage(_T("Simulation Time Out Set : %.1f"),dOneScanTime);
            //}
            //for(iCam=0;iCam<MAX_CAMERA_COUNT;iCam++)
            //{
            //    pMain->ScanStartManual(iCam,0);
            //}
            //DWORD sTime = GetTickCount();
            //while(TRUE)
            //{
            //    if(pMain->m_GlassData.GetScanEnd(0) == TRUE)
            //        break;
            //    if(GetTickCount() - sTime >= 5000)
            //        break;
            //
            //    Sleep(10);
            //}
            //for(iCam=0;iCam<MAX_CAMERA_COUNT;iCam++)
            //{
            //    pMain->ScanStartManual(iCam,1);
            //}
            if (pMain->m_HardwareRecipe.m_bUseRipInspection) {
                std::vector<DimensionDir> vecDirs = {
                    DIMENSION_A, DIMENSION_C, DIMENSION_A_DN, DIMENSION_C_DN,
                    DIMENSION_B, DIMENSION_D, DIMENSION_B_DN, DIMENSION_D_DN,
                    DIMENSION_A_RIP, DIMENSION_B_RIP, DIMENSION_C_RIP, DIMENSION_D_RIP
                };
                pMain->Simulate2PhaseScanByDirs(vecDirs, 1000);
                pMain->Simulate2PhaseScanByDirs(vecDirs, 3000);
            }
            else {
                std::vector<DimensionDir> vecDirs = {
                    DIMENSION_A, DIMENSION_C, DIMENSION_A_DN, DIMENSION_C_DN,
                    DIMENSION_B, DIMENSION_D, DIMENSION_B_DN, DIMENSION_D_DN
                };
                pMain->Simulate2PhaseScanByDirs(vecDirs, 1000);
                pMain->Simulate2PhaseScanByDirs(vecDirs, 3000);
            }
        }
        break;
EdgeInspector_App/Recipe/HardwareSettings.cpp
@@ -991,7 +991,7 @@
{
    if(this != &rhs)
    {
        int                iLoop;
        int    iLoop;
        Reset();
        m_nLightCount_Trans = rhs.m_nLightCount_Trans;
@@ -1009,10 +1009,12 @@
        m_strMachineName = rhs.m_strMachineName;
        m_strLineID = rhs.m_strLineID;
        m_nPLCSignalRetry = rhs.m_nPLCSignalRetry;
        m_nSaveImageQuality = rhs.m_nSaveImageQuality;
        m_nPLCSignalRetry = rhs.m_nPLCSignalRetry;
        //        m_dOneScanTime = rhs.m_dOneScanTime;
        m_nSaveImageQuality = rhs.m_nSaveImageQuality;
        m_bAsyncFullSave = rhs.m_bAsyncFullSave;
        // m_dOneScanTime = rhs.m_dOneScanTime;
        m_nFolderRemoveDay = rhs.m_nFolderRemoveDay;
        m_PLCSettings = rhs.m_PLCSettings;
@@ -1050,7 +1052,7 @@
void CHardwareSettings::Reset()
{        
    //    m_dOneScanTime = 0.;
    // m_dOneScanTime = 0.;
    m_nLightCount_Trans = 0;    
    m_nLightCount_Reflect = 0;    
    m_nCameraCount = 0;
@@ -1058,6 +1060,7 @@
    m_nFolderRemoveDay = 0;
    m_nPLCSignalRetry = 0;
    m_nSaveImageQuality = 30;
    m_bAsyncFullSave = FALSE;
    m_strMachineName = _T("");
    m_strLineID = _T("");
    m_nBoardType = GRAB_LINK_BOARD;
@@ -1280,6 +1283,8 @@
        str = _T("SAVE_DEBUG_IMAGE");
    else if((void *)&m_nSaveImageQuality == pValue)
        str = _T("SAVEIMAGE_QUALITY");
    else if ((void*)&m_bAsyncFullSave == pValue)
        str = _T("ASYNC_FULL_SAVE");
    // ly,2025.07.10
    else if ((void*)&m_bSaveResultByHour == pValue)
@@ -1381,6 +1386,10 @@
        return FALSE;
    BasicInfoFile.GetItemValue((TCHAR*)(LPCTSTR)str,m_nSaveImageQuality,30);    
    
    str = GetFileString((void*)&m_bAsyncFullSave);
    if (str.IsEmpty() == TRUE)
        return FALSE;
    BasicInfoFile.GetItemValue((TCHAR*)(LPCTSTR)str, m_bAsyncFullSave, FALSE);
    SetCameraCount(m_nCameraCount,m_nScanCount);
    iLoop = 0;
@@ -1567,7 +1576,12 @@
    str = GetFileString((void *)&m_nSaveImageQuality);
    if(str.IsEmpty() == TRUE)
        return FALSE;
    Register.GetItemValue((TCHAR*)(LPCTSTR)str,m_nSaveImageQuality,30);
    Register.GetItemValue((TCHAR*)(LPCTSTR)str,m_nSaveImageQuality,30);
    str = GetFileString((void*)&m_bAsyncFullSave);
    if (str.IsEmpty() == TRUE)
        return FALSE;
    Register.GetItemValue((TCHAR*)(LPCTSTR)str, m_bAsyncFullSave, FALSE);
    SetCameraCount(m_nCameraCount,m_nScanCount);
    iLoop = 0;
@@ -1737,6 +1751,11 @@
    if(str.IsEmpty() == TRUE)
        return FALSE;
    Register.SetItemValue((TCHAR*)(LPCTSTR)str,m_nSaveImageQuality);
    str = GetFileString((void*)&m_bAsyncFullSave);
    if (str.IsEmpty() == TRUE)
        return FALSE;
    Register.SetItemValue((TCHAR*)(LPCTSTR)str, m_bAsyncFullSave);
    if(m_pCameraSettings != NULL)
    {
@@ -1926,7 +1945,13 @@
    if (str.IsEmpty() == TRUE) {
        return FALSE;
    }
    BasicInfoFile.SetItemValue((TCHAR*)(LPCTSTR)str,m_nSaveImageQuality);
    BasicInfoFile.SetItemValue((TCHAR*)(LPCTSTR)str,m_nSaveImageQuality);
    str = GetFileString((void*)&m_bAsyncFullSave);
    if (str.IsEmpty() == TRUE) {
        return FALSE;
    }
    BasicInfoFile.SetItemValue((TCHAR*)(LPCTSTR)str, m_bAsyncFullSave);
    if (m_pCameraSettings != NULL) {
        iLoop = 0;
EdgeInspector_App/Recipe/HardwareSettings.h
@@ -185,6 +185,7 @@
    int                            m_nFolderRemoveDay;
    BOOL                        m_bUseColorVisual;
    int                            m_nSaveImageQuality;
    BOOL                        m_bAsyncFullSave;
    // Software
public:
EdgeInspector_App/Thread/Thread_SaveFullImg.cpp
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,194 @@
#include "stdafx.h"
#include "Thread_SaveFullImg.h"
static void TightCopy(std::vector<BYTE>& vDst, const BYTE* pSrc, int nWidth, int nHeight, int nStride, int nBpp)
{
    const int nTight = nWidth * nBpp;
    vDst.resize(size_t(nTight) * nHeight);
    if (vDst.empty() || nHeight < 1 || nWidth < 1 || nBpp < 1) {
        g_pLog->DisplayMessage(_T("TightCopy: Invalid parameters, nWidth=%d, nHeight=%d, nBpp=%d"), nWidth, nHeight, nBpp);
        return;
    }
    // å¦‚æžœ nStride ç­‰äºŽ nTight,说明源数据是紧密行存储
    if (nStride == nTight) {
        memcpy(vDst.data(), pSrc, vDst.size());
        return;
    }
    // å¦‚æžœ nStride å°äºŽ nTight,说明源数据不是紧密行存储
    if (nStride < nTight) {
        g_pLog->DisplayMessage(_T("TightCopy: Invalid nStride %d < nTight %d"), nStride, nTight);
        return;
    }
    // å¦‚æžœ nStride å¤§äºŽ nTight,逐行复制
    for (int y = 0; y < nHeight; ++y) {
        memcpy(vDst.data() + size_t(y) * nTight, pSrc + size_t(y) * nStride, nTight);
    }
}
CThread_SaveFullImg::CThread_SaveFullImg()
{
    m_bStop = false;
}
CThread_SaveFullImg::~CThread_SaveFullImg()
{
    StopThread();
}
void CThread_SaveFullImg::CreateThread()
{
    if (m_Thread.joinable()) {
        return;
    }
    m_bStop = false;
    m_Thread = std::thread([this] { ThreadProc(); });
}
void CThread_SaveFullImg::StopThread()
{
    {
        std::lock_guard<std::mutex> oLock(m_Mtx);
        m_bStop = true;
    }
    m_Cv.notify_all();
    if (m_Thread.joinable()) {
        m_Thread.join();
    }
}
void CThread_SaveFullImg::Enqueue(SaveImgJob&& oJob)
{
    {
        std::lock_guard<std::mutex> oLock(m_Mtx);
        m_Q.push(std::move(oJob));
    }
    m_Cv.notify_one();
}
size_t CThread_SaveFullImg::GetPendingCount() const
{
    std::lock_guard<std::mutex> oLock(m_Mtx);
    return m_Q.size();
}
void CThread_SaveFullImg::ThreadProc()
{
    while (true) {
        SaveImgJob oJob; {
            std::unique_lock<std::mutex> oLock(m_Mtx);
            m_Cv.wait(oLock, [this] {
                return m_bStop || !m_Q.empty();
            });
            if (m_bStop && m_Q.empty()) {
                break;
            }
            oJob = std::move(m_Q.front());
            m_Q.pop();
        }
        try {
            SaveFullImageModern(oJob);
        }
        catch (...) {
            g_pLog->DisplayMessage(_T("Async save failed: %s"), oJob.strPath.c_str());
        }
    }
}
BOOL CThread_SaveFullImg::SaveFullImageModern(const SaveImgJob& job)
{
    int iSide = job.nDimension;
    if (iSide < 0 || iSide > MAX_DIMENSION_COUNT - 1) {
        g_pLog->DisplayMessage(_T("Save Full Image Fail(%S, %d), invalid side index"), job.strPath.c_str(), job.nDimension);
        return FALSE;
    }
    if (job.vData.empty() || job.nHeight < 100 || job.nWidth < 100 || job.nBpp < 0) {
        g_pLog->DisplayMessage(_T("Save Full Image Fail(%S, %s, %d, %d)"), job.strPath.c_str(), PANEL_SIDE[iSide], job.nWidth, job.nHeight);
        return FALSE;
    }
    if (job.nStride < job.nWidth * job.nBpp) {
        g_pLog->DisplayMessage(_T("Save Full Image Fail(%S), stride(%d) < width*bpp(%d)"), job.strPath.c_str(), job.nStride, job.nWidth * job.nBpp);
        return FALSE;
    }
    int nQuality = job.nQuality;
    if (nQuality < 0) {
        nQuality = 0;
    }
    if (nQuality > 100) {
        nQuality = 100;
    }
    double dTmp = 70 / 100.0;
    double dRatio = 1.0 - dTmp;
    dRatio = dRatio - 0.01 < 0.0 ? 1.0 : dRatio;
    g_pLog->DisplayMessage(_T("Save Full Image Start(%S, %s, %d, %d, %d)"), job.strPath.c_str(), PANEL_SIDE[job.nDimension], job.nStartY, job.nHeight, nQuality);
    int nStartY = job.nStartY;
    if (nStartY < 0) {
        nStartY = 0;
    }
    if (nStartY > job.nHeight - 1) {
        nStartY = job.nHeight - 1;
    }
    int cvType;
    switch (job.nBpp)
    {
    case 1: cvType = CV_8UC1; break;
    case 3: cvType = CV_8UC3; break;
    default:
        g_pLog->DisplayMessage(_T("Save Full Image Fail(%S), unsupported nBpp=%d"), job.strPath.c_str(), job.nBpp);
        return FALSE;
    }
    const BYTE* pBase = job.vData.data() + size_t(nStartY) * size_t(job.nStride);
    const int nHAvail = job.nHeight - nStartY;
    if (nHAvail < 100) {
        g_pLog->DisplayMessage(_T("Save Full Image Skip(%S, %s), nHAvail=%d too small"), job.strPath.c_str(), PANEL_SIDE[iSide], nHAvail);
        return FALSE;
    }
    // åŽŸå§‹å›¾åƒæ•°æ®è½¬ cv::Mat
    cv::Mat origin(nHAvail, job.nWidth, cvType, (void*)pBase, size_t(job.nStride));
    // ç¼©æ”¾å›¾åƒ
    const int nOutW = std::max(1, static_cast<int>(origin.cols * dRatio));
    const int nOutH = std::max(1, static_cast<int>(origin.rows * dRatio));
    cv::Mat img;
    if (nOutW == origin.cols && nOutH == origin.rows) {
        img = origin;
    }
    else {
        cv::resize(origin, img, cv::Size(nOutW, nOutH), 0, 0, cv::INTER_LINEAR);
    }
    // è®¾ç½® JPEG åŽ‹ç¼©å‚æ•°
    std::vector<int> params = { cv::IMWRITE_JPEG_QUALITY, nQuality };
    try {
        if (!cv::imwrite(job.strPath, img, params)) {
            g_pLog->DisplayMessage(_T("Save Full Image Fail(%S, %s, %d), imwrite failed"), job.strPath.c_str(), PANEL_SIDE[iSide], nStartY);
            return FALSE;
        }
    }
    catch (...) {
        g_pLog->DisplayMessage(_T("Save Full Image Fail(%S, %s, %d), exception during imwrite"), job.strPath.c_str(), PANEL_SIDE[iSide], nStartY);
        return FALSE;
    }
    g_pLog->DisplayMessage(_T("Save Full Image Success(%S, %s, %d, %d, %d)"), job.strPath.c_str(), PANEL_SIDE[iSide], nStartY, job.nHeight, nQuality);
    return TRUE;
}
EdgeInspector_App/Thread/Thread_SaveFullImg.h
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <atomic>
#include <vector>
#include <string>
#include <Windows.h>
struct SaveImgJob
{
    std::string        strPath;
    std::vector<BYTE>  vData;
    int                nWidth = 0;
    int                nHeight = 0;
    int                nStride = 0;
    int                nBpp = 1;
    int                nStartY = 0;
    int                nDimension = 0;
    int                nQuality = 80;
};
class CThread_SaveFullImg
{
public:
    CThread_SaveFullImg();
    ~CThread_SaveFullImg();
    void CreateThread();
    void StopThread();
    void Enqueue(SaveImgJob&& oJob);
    size_t GetPendingCount() const;
private:
    void ThreadProc();
    BOOL SaveFullImageModern(const SaveImgJob& job);
private:
    mutable std::mutex              m_Mtx;
    std::condition_variable         m_Cv;
    std::queue<SaveImgJob>          m_Q;
    std::thread                     m_Thread;
    std::atomic<bool>               m_bStop;
};
EdgeInspector_App/View/ViewMain_HWSetting.cpp
@@ -65,7 +65,8 @@
    DDX_Check(pDX,IDC_CHK_INS_REVERSE,m_pDlgHDSettingParm->m_bInsDirReverse);
    DDX_Text(pDX, IDC_EDIT_PLC_SIGNAL_RETRY_COUNT, m_pDlgHDSettingParm->m_nPLCSignalRetry);        
    DDX_Text(pDX, IDC_EDIT_SAVEIMAGE_QUALITY, m_pDlgHDSettingParm->m_nSaveImageQuality);
    DDX_Text(pDX, IDC_EDIT_SAVEIMAGE_QUALITY, m_pDlgHDSettingParm->m_nSaveImageQuality);
    DDX_Check(pDX, IDC_CHECK_USE_ASYNC_FULL_IMAGE_SAVE, m_pDlgHDSettingParm->m_bAsyncFullSave);
    
    DDX_Text(pDX,IDC_EDIT_FOLDER_REMOVE_DAY,m_pDlgHDSettingParm->m_nFolderRemoveDay);
    
EdgeInspector_App/View/ViewMain_ScanImage.cpp
@@ -949,7 +949,7 @@
BOOL CViewMain_ScanImage::ManualThreadStart(int nCmd,int nIndex /*= -1*/)
{
    if(m_pManualProcess) {
        DWORD dwWait = ::WaitForSingleObject(m_pManualProcess->m_hThread, 1000);
        DWORD dwWait = ::WaitForSingleObject(m_pManualProcess->m_hThread, 3000);
        if (dwWait == WAIT_TIMEOUT) {
            DWORD dwExitCode;
            if ((::GetExitCodeThread(m_pManualProcess->m_hThread, &dwExitCode)) && (dwExitCode == STILL_ACTIVE)) {
EdgeInspector_App/resource.h
@@ -363,6 +363,7 @@
#define IDC_CHK_USECOLORVISUAL          1072
#define IDC_STATIC_SUBJECT              1072
#define IDC_SCROLLBAR_V_9               1072
#define IDC_CHECK_USE_ASYNC_FULL_IMAGE_SAVE 1073
#define IDC_UPDN_WIDTH1                 1074
#define IDC_BTN_PROC                    1075
#define IDC_SLIDER_ZOOM                 1076