From df45966c52bac2eb465cf05c1d6328bf0d00c5ac Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期六, 16 八月 2025 15:27:10 +0800
Subject: [PATCH] 1. 补提交添加保存图像的类

---
 EdgeInspector_App/Thread/Thread_SaveFullImg.h   |   45 +++++++++++
 EdgeInspector_App/Thread/Thread_SaveFullImg.cpp |  194 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 239 insertions(+), 0 deletions(-)

diff --git a/EdgeInspector_App/Thread/Thread_SaveFullImg.cpp b/EdgeInspector_App/Thread/Thread_SaveFullImg.cpp
new file mode 100644
index 0000000..f38de1d
--- /dev/null
+++ b/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;
+}
\ No newline at end of file
diff --git a/EdgeInspector_App/Thread/Thread_SaveFullImg.h b/EdgeInspector_App/Thread/Thread_SaveFullImg.h
new file mode 100644
index 0000000..172302f
--- /dev/null
+++ b/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;
+};
\ No newline at end of file

--
Gitblit v1.9.3