// B2saomiaotuxiangchuangkou.cpp : 实现文件
//
#include "stdafx.h"
#include "Imageprocessing.h"
#include "B2saomiaotuxiangchuangkou.h"
#include "afxdialogex.h"
#include <string>
#include <chrono>
#include <Gdiplus.h>
#include <shlwapi.h>
#pragma comment(lib, "Gdiplus.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "Shell32.lib")
// B2saomiaotuxiangchuangkou 对话框
IMPLEMENT_DYNAMIC(B2saomiaotuxiangchuangkou, CDialogEx)
B2saomiaotuxiangchuangkou::B2saomiaotuxiangchuangkou(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_B2saomiaotuxiangchuangkou, pParent)
{
}
B2saomiaotuxiangchuangkou::~B2saomiaotuxiangchuangkou()
{
Cleanup();
}
void B2saomiaotuxiangchuangkou::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_FOLDER_PATH, m_editFolderPath);
DDX_Control(pDX, IDC_LIST_IMAGES, m_listImages);
DDX_Control(pDX, IDC_COMBO_SAVE_OPTION, m_saveOption);
DDX_Control(pDX, IDC_BUTTON_START, m_btnStart);
DDX_Control(pDX, IDC_BUTTON_PAUSE, m_btnPause);
DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop);
DDX_Control(pDX, IDC_BUTTON_RESET, m_btnReset);
DDX_Control(pDX, IDC_BUTTON_SELECT_FOLDER, m_btnSelectFolder);
DDX_Control(pDX, IDC_PROGRESS_TOTAL, m_totalProgress);
DDX_Control(pDX, IDC_PROGRESS_FILE, m_fileProgress);
DDX_Control(pDX, IDC_STATIC_ORIGINAL_IMAGE, m_originalImage);
DDX_Control(pDX, IDC_STATIC_PROCESSED_IMAGE, m_processedImage);
DDX_Control(pDX, IDC_EDIT2, m_editSavePath);
}
BEGIN_MESSAGE_MAP(B2saomiaotuxiangchuangkou, CDialogEx)
ON_WM_SIZE()
ON_BN_CLICKED(IDC_BUTTON_SELECT_FOLDER, &B2saomiaotuxiangchuangkou::OnBnClickedButtonSelectFolder)
ON_BN_CLICKED(IDC_BUTTON_START, &B2saomiaotuxiangchuangkou::OnBnClickedButtonStart)
ON_BN_CLICKED(IDC_BUTTON_PAUSE, &B2saomiaotuxiangchuangkou::OnBnClickedButtonPause)
ON_BN_CLICKED(IDC_BUTTON_STOP, &B2saomiaotuxiangchuangkou::OnBnClickedButtonStop)
ON_BN_CLICKED(IDC_BUTTON_RESET, &B2saomiaotuxiangchuangkou::OnBnClickedButtonReset)
ON_CBN_SELCHANGE(IDC_COMBO_SAVE_OPTION, &B2saomiaotuxiangchuangkou::OnCbnSelchangeComboSaveOption)
ON_WM_TIMER()
ON_NOTIFY(LVN_ITEMCHANGED, IDC_LIST_IMAGES, &B2saomiaotuxiangchuangkou::OnLvnItemchangedListImages)
ON_MESSAGE(WM_LOAD_FIRST_IMAGE, &B2saomiaotuxiangchuangkou::OnLoadFirstImageMessage)
ON_MESSAGE(WM_UPDATE_DISPLAY_IMAGE, &B2saomiaotuxiangchuangkou::OnUpdateDisplayImage)
ON_WM_MOUSEWHEEL() // 添加鼠标滚轮消息映射
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
BOOL B2saomiaotuxiangchuangkou::OnInitDialog()
{
CDialogEx::OnInitDialog();
m_listImages.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
m_listImages.InsertColumn(0, _T("文件名"), LVCFMT_LEFT, 200);
m_listImages.InsertColumn(1, _T("大小"), LVCFMT_RIGHT, 100);
m_listImages.InsertColumn(2, _T("类型"), LVCFMT_LEFT, 100);
m_saveOption.AddString(_T("覆盖原图"));
m_saveOption.AddString(_T("新路径"));
m_saveOption.SetCurSel(0);
m_totalProgress.SetRange(0, 100);
m_fileProgress.SetRange(0, 100);
m_totalProgress.SetPos(0);
m_fileProgress.SetPos(0);
m_btnPause.EnableWindow(FALSE);
m_btnStop.EnableWindow(FALSE);
m_btnReset.EnableWindow(FALSE);
m_originalImage.ModifyStyle(0, SS_BITMAP | SS_CENTERIMAGE);
m_processedImage.ModifyStyle(0, SS_BITMAP| SS_CENTERIMAGE);
m_originalImage.GetWindowRect(&m_originalRect);
ScreenToClient(&m_originalRect);
m_processedImage.GetWindowRect(&m_processedRect);
ScreenToClient(&m_processedRect);
Gdiplus::GdiplusStartupInput gdiplusInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusInput, NULL);
if (!m_folderPath.IsEmpty()) {
PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0);
}
return TRUE;
}
void B2saomiaotuxiangchuangkou::SetControlPosition(CWnd* pCtrl, int x, int y, int width, int height)
{
if (pCtrl) {
CRect rect(x, y, x + width, y + height);
pCtrl->MoveWindow(rect);
}
}
void B2saomiaotuxiangchuangkou::AdjustLayoutWhenResized()
{
CRect clientRect;
GetClientRect(&clientRect);
int cx = clientRect.Width();
int cy = clientRect.Height();
// 获取屏幕尺寸(可选,若需要基于屏幕比例布局)
int sW = GetSystemMetrics(SM_CXSCREEN);
int sH = GetSystemMetrics(SM_CYSCREEN);
int pc_W1 = (cx - 530) / 3;//图像显示控件宽
int pc_H1;//图像显示控件高
int pc_W2 = (cy - 530) / 3 * 2;//图像显示控件宽
int pc_H2;//图像显示控件高
// 示例:调整子界面中控件的位置(类似你提供的代码)
SetControlPosition(GetDlgItem(IDC_STATIC1), 0, 5, 80, 30); /**/
SetControlPosition(GetDlgItem(IDC_STATIC2), 0, 50, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_EDIT_FOLDER_PATH), 85, 43, 280, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_SELECT_FOLDER), 366, 44, 70, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_START), 440, 43, 70, 30);/**/SetControlPosition(GetDlgItem(IDC_STATIC_ORIGINAL_IMAGE), 530, 5, pc_W1 - 1, 490);/**/SetControlPosition(GetDlgItem(IDC_STATIC_PROCESSED_IMAGE), 530 + pc_W1 + 2, 5, 2 * pc_W1 - 5, cy - 10);
SetControlPosition(GetDlgItem(IDC_STATIC3), 0, 90, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_EDIT2), 85, 83, 280, 30); /**/SetControlPosition(GetDlgItem(IDC_COMBO_SAVE_OPTION), 366, 89, 70, 30); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_PAUSE), 440, 84, 70, 30);
SetControlPosition(GetDlgItem(IDC_STATIC4), 0, 155, 80, 30); /**/ /**/SetControlPosition(GetDlgItem(IDC_BUTTON_STOP), 440, 124, 70, 30);
SetControlPosition(GetDlgItem(IDC_STATIC6), 0, 200, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_PROGRESS_TOTAL), 85, 197, 280, 20); /**/SetControlPosition(GetDlgItem(IDC_BUTTON_RESET), 440, 164, 70, 30);
SetControlPosition(GetDlgItem(IDC_STATIC7), 0, 240, 80, 30); /**/SetControlPosition(GetDlgItem(IDC_PROGRESS_FILE), 85, 237, 180, 20);
SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30); /**/
/*SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30); /**/
/*SetControlPosition(GetDlgItem(IDC_STATIC8), 0, 305, 80, 30);*/ /**/
SetControlPosition(GetDlgItem(IDC_LIST_IMAGES), 0, 500, 530 + pc_W1 - 1, cy - 500 - 5); /**/
}
void B2saomiaotuxiangchuangkou::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
if (nType != SIZE_MINIMIZED) {
AdjustLayoutWhenResized();
}
}
// 在实现文件中添加函数实现
void B2saomiaotuxiangchuangkou::LoadFilesIntoListControl()
{
// 清空列表
m_listImages.DeleteAllItems();
m_imageFiles.clear();
m_imageLoaded = false;
m_firstLoadCompleted = false;
// 获取文件夹中的所有文件
std::vector<CString> files;
CString searchPath = m_folderPath + _T("\\*.*");
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile(searchPath, &findFileData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if (!(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
CString fileName = findFileData.cFileName;
CString ext = fileName.Right(4).MakeLower();
if (ext == _T(".jpg") || ext == _T(".jpeg") ||
ext == _T(".png") || ext == _T(".bmp") ||
ext == _T(".tif") || ext == _T(".tiff"))
{
files.push_back(fileName);
}
}
} while (FindNextFile(hFind, &findFileData));
FindClose(hFind);
}
// 使用 StrCmpLogicalW 进行自然排序
std::sort(files.begin(), files.end(), [](const CString& a, const CString& b) {
return StrCmpLogicalW(a, b) < 0;
});
// 将排序后的文件添加到列表控件
for (size_t i = 0; i < files.size(); ++i) {
CString fileName = files[i];
CString filePath = m_folderPath + _T("\\") + fileName;
m_imageFiles.push_back(filePath);
// 添加到列表控件
int nIndex = m_listImages.InsertItem(i, fileName);
// 获取文件大小
WIN32_FILE_ATTRIBUTE_DATA fad;
ULONGLONG fileSize = 0;
if (GetFileAttributesEx(filePath, GetFileExInfoStandard, &fad)) {
fileSize = ((ULONGLONG)fad.nFileSizeHigh << 32) | fad.nFileSizeLow;
}
CString sizeStr;
if (fileSize < 1024)
sizeStr.Format(_T("%d B"), fileSize);
else if (fileSize < 1024 * 1024)
sizeStr.Format(_T("%.2f KB"), fileSize / 1024.0);
else
sizeStr.Format(_T("%.2f MB"), fileSize / (1024.0 * 1024.0));
m_listImages.SetItemText(nIndex, 1, sizeStr);
// 文件类型
CString typeStr = fileName.Right(4).Mid(1).MakeUpper();
m_listImages.SetItemText(nIndex, 2, typeStr);
}
}
void B2saomiaotuxiangchuangkou::OnBnClickedButtonSelectFolder()
{
CFolderPickerDialog dlg;
if (dlg.DoModal() == IDOK)
{
m_folderPath = dlg.GetFolderPath();
m_editFolderPath.SetWindowText(m_folderPath);
// 使用新的方法加载文件到列表控件
LoadFilesIntoListControl();
// 更新保存路径显示
OnCbnSelchangeComboSaveOption();
// 设置按钮状态
m_btnStart.EnableWindow(!m_imageFiles.empty());
// 显示第一张图片
if (!m_imageFiles.empty())
{
m_listImages.SetItemState(0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
m_listImages.EnsureVisible(0, FALSE);
m_listImages.UpdateWindow();
PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0);
}
}
}
LRESULT B2saomiaotuxiangchuangkou::OnLoadFirstImageMessage(WPARAM wParam, LPARAM lParam)
{
DisplayFirstImage();
return 0;
}
void B2saomiaotuxiangchuangkou::DisplayFirstImage()
{
if (m_imageFiles.empty()) return;
CString filePath = m_imageFiles[0];
if (!LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE)) {
m_loadMode = (m_loadMode == MODE_OPENCV) ? MODE_GDIPlus : MODE_OPENCV;
if (!LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE)) {
return;
}
}
m_firstImageLoaded = true;
m_firstLoadCompleted = true;
m_imageLoaded = true;
CWnd* pProcessed = GetDlgItem(IDC_STATIC_PROCESSED_IMAGE);
if (pProcessed) {
auto pStatic = reinterpret_cast<CStatic*>(pProcessed);
HBITMAP hOldBmp = pStatic->GetBitmap();
if (hOldBmp) {
DeleteObject(hOldBmp);
pStatic->SetBitmap(NULL);
}
}
}
bool B2saomiaotuxiangchuangkou::LoadImageToControl(const CString& filePath, UINT controlID)
{
std::lock_guard<std::mutex> lock(m_imageMutex);
DWORD fileAttr = GetFileAttributes(filePath);
if (fileAttr == INVALID_FILE_ATTRIBUTES) {
return false;
}
HBITMAP hBitmap = NULL;
if (m_loadMode == MODE_OPENCV) {
hBitmap = LoadImageWithOpenCV(filePath);
}
else {
hBitmap = LoadImageWithGDIPlus(filePath);
}
if (!hBitmap) {
return false;
}
CWnd* pWnd = GetDlgItem(controlID);
if (pWnd) {
CStatic* pStatic = dynamic_cast<CStatic*>(pWnd);
if (pStatic) {
HBITMAP oldBmp = pStatic->GetBitmap();
if (oldBmp) DeleteObject(oldBmp);
pStatic->SetBitmap(hBitmap);
pStatic->Invalidate();
return true;
}
}
DeleteObject(hBitmap);
return false;
}
// 禁用鼠标滚轮缩放功能
BOOL B2saomiaotuxiangchuangkou::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// 直接返回TRUE,阻止滚轮缩放
return TRUE;
}
// 修改 LoadImageWithOpenCV 函数
HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithOpenCV(const CString& filePath)
{
cv::Mat img;
#ifdef _UNICODE
int len = WideCharToMultiByte(CP_UTF8, 0, filePath, -1, NULL, 0, NULL, NULL);
std::vector<char> utf8Buf(len);
WideCharToMultiByte(CP_UTF8, 0, filePath, -1, utf8Buf.data(), len, NULL, NULL);
img = cv::imread(utf8Buf.data(), cv::IMREAD_COLOR);
#else
img = cv::imread(CW2A(filePath), cv::IMREAD_COLOR);
#endif
if (img.empty()) {
return NULL;
}
// 关键修改:添加BGR到RGB的颜色空间转换
cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
CRect rect;
m_originalImage.GetClientRect(&rect);
int dstWidth = rect.Width();
int dstHeight = rect.Height();
// 计算缩放比例和位置
double ratio = std::min(static_cast<double>(dstWidth) / img.cols,
static_cast<double>(dstHeight) / img.rows);
int drawWidth = static_cast<int>(img.cols * ratio);
int drawHeight = static_cast<int>(img.rows * ratio);
int x = (dstWidth - drawWidth) / 2;
int y = (dstHeight - drawHeight) / 2;
// 创建目标位图
HDC hdcScreen = ::GetDC(NULL);
HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, dstWidth, dstHeight);
HDC hdcMem = CreateCompatibleDC(hdcScreen);
HBITMAP hOldBmp = (HBITMAP)SelectObject(hdcMem, hBitmap);
// 填充白色背景
HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
RECT rc = { 0, 0, dstWidth, dstHeight };
FillRect(hdcMem, &rc, hBrush);
DeleteObject(hBrush);
// 创建缩放后的图像 - 使用高质量缩放算法
cv::Mat resized;
cv::resize(img, resized, cv::Size(drawWidth, drawHeight), 0, 0, cv::INTER_AREA);
// 转换为 BITMAPINFO
BITMAPINFOHEADER bmiHeader = { 0 };
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = resized.cols;
bmiHeader.biHeight = -resized.rows; // 负号表示从上到下的位图
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24;
bmiHeader.biCompression = BI_RGB;
// 在目标DC上绘制图像(确保居中)
SetStretchBltMode(hdcMem, HALFTONE);
StretchDIBits(
hdcMem,
x, y, drawWidth, drawHeight, // 目标位置和大小
0, 0, resized.cols, resized.rows, // 源位置和大小
resized.data,
(BITMAPINFO*)&bmiHeader,
DIB_RGB_COLORS,
SRCCOPY
);
// 清理资源
SelectObject(hdcMem, hOldBmp);
DeleteDC(hdcMem);
::ReleaseDC(NULL, hdcScreen);
return hBitmap;
}
//// 修改 MatToBitmap 函数(确保其他地方的转换也正确)
//HBITMAP B2saomiaotuxiangchuangkou::MatToBitmap(cv::Mat& mat)
//{
// if (mat.empty()) return NULL;
//
// cv::Mat rgb;
// // 添加颜色空间转换
// if (mat.channels() == 1) {
// cv::cvtColor(mat, rgb, cv::COLOR_GRAY2BGR);
// }
// else if (mat.channels() == 3) {
// cv::cvtColor(mat, rgb, cv::COLOR_BGR2RGB); // 关键修改
// }
// else if (mat.channels() == 4) {
// cv::cvtColor(mat, rgb, cv::COLOR_BGRA2BGR);
// }
// else {
// rgb = mat;
// }
//
// BITMAPINFOHEADER bmiHeader;
// bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// bmiHeader.biWidth = rgb.cols;
// bmiHeader.biHeight = -rgb.rows;
// bmiHeader.biPlanes = 1;
// bmiHeader.biBitCount = 24;
// bmiHeader.biCompression = BI_RGB;
// bmiHeader.biSizeImage = 0;
// bmiHeader.biXPelsPerMeter = 0;
// bmiHeader.biYPelsPerMeter = 0;
// bmiHeader.biClrUsed = 0;
// bmiHeader.biClrImportant = 0;
//
// HDC hDC = ::GetDC(NULL);
// HBITMAP hBitmap = CreateDIBitmap(hDC, &bmiHeader, CBM_INIT,
// rgb.data, (BITMAPINFO*)&bmiHeader, DIB_RGB_COLORS);
// ::ReleaseDC(NULL, hDC);
//
// return hBitmap;
//}
// 添加背景擦除处理,防止闪烁
BOOL B2saomiaotuxiangchuangkou::OnEraseBkgnd(CDC* pDC)
{
// 获取客户区矩形
CRect rect;
GetClientRect(&rect);
// 用白色填充背景
CBrush brush(RGB(255, 255, 255));
pDC->FillRect(&rect, &brush);
return TRUE; // 返回TRUE表示我们已经擦除了背景
}
//HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithGDIPlus(const CString& filePath) {
// Gdiplus::Image image(filePath);
// if (image.GetLastStatus() != Gdiplus::Ok) return NULL;
//
// // 获取控件尺寸
// CRect rect;
// m_originalImage.GetClientRect(&rect);
// int controlWidth = rect.Width();
// int controlHeight = rect.Height();
//
// // 计算缩放比例
// int srcWidth = image.GetWidth();
// int srcHeight = image.GetHeight();
// double ratio = std::min(
// static_cast<double>(controlWidth) / srcWidth,
// static_cast<double>(controlHeight) / srcHeight
// );
// int drawWidth = static_cast<int>(srcWidth * ratio);
// int drawHeight = static_cast<int>(srcHeight * ratio);
//
// // 计算起始坐标(如需左上角,设为 0)
// int x = (controlWidth - drawWidth) / 2; // 水平居中(可改为 0)
// int y = (controlHeight - drawHeight) / 2; // 垂直居中(可改为 0)
//
// // 创建位图并绘制
// Gdiplus::Bitmap bitmap(controlWidth, controlHeight, PixelFormat32bppARGB);
// Gdiplus::Graphics graphics(&bitmap);
// graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
// graphics.Clear(Gdiplus::Color(255, 255, 255)); // 白色背景
//
// // 关键:从 (x,y) 开始绘制图像
// graphics.DrawImage(
// &image,
// x, y,
// drawWidth, drawHeight
// );
//
// // 获取 HBITMAP 句柄
// HBITMAP hBitmap = NULL;
// bitmap.GetHBITMAP(Gdiplus::Color::White, &hBitmap);
// return hBitmap;
//}
HBITMAP B2saomiaotuxiangchuangkou::LoadImageWithGDIPlus(const CString& filePath)
{
Gdiplus::Image image(filePath);
if (image.GetLastStatus() != Gdiplus::Ok) {
return NULL;
}
CRect rect;
m_originalImage.GetClientRect(&rect);
int ctrlWidth = rect.Width();
int ctrlHeight = rect.Height();
int srcWidth = image.GetWidth();
int srcHeight = image.GetHeight();
// 计算缩放比例(保持原图比例)
double scale = std::min((double)ctrlWidth / srcWidth, (double)ctrlHeight / srcHeight);
int drawWidth = (int)(srcWidth * scale);
int drawHeight = (int)(srcHeight * scale);
// 原图目标点(如中心点)
int srcTargetX = srcWidth / 2; // 原图中心点X
int srcTargetY = srcHeight / 2; // 原图中心点Y
// 控件中心点
int ctrlCenterX = ctrlWidth / 2;
int ctrlCenterY = ctrlHeight / 2;
// 计算绘制起点(使原图目标点对齐控件中心点)
int x = ctrlCenterX - srcTargetX * scale;
int y = ctrlCenterY - srcTargetY * scale;
Gdiplus::Bitmap bitmap(ctrlWidth, ctrlHeight, PixelFormat32bppARGB);
Gdiplus::Graphics graphics(&bitmap);
graphics.SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
// 绘制图像(从原图(0,0)开始,按新起点绘制)
graphics.DrawImage(&image, x, y, drawWidth, drawHeight);
HBITMAP hBitmap = NULL;
bitmap.GetHBITMAP(Gdiplus::Color::White, &hBitmap);
return hBitmap;
}
HBITMAP B2saomiaotuxiangchuangkou::MatToBitmap(cv::Mat& mat)
{
if (mat.empty()) return NULL;
cv::Mat bgr;
if (mat.channels() == 1) {
cv::cvtColor(mat, bgr, cv::COLOR_GRAY2BGR);
}
else if (mat.channels() == 4) {
cv::cvtColor(mat, bgr, cv::COLOR_BGRA2BGR);
}
else {
bgr = mat;
}
// 确保图像尺寸正确
if (bgr.rows <= 0 || bgr.cols <= 0) {
return NULL;
}
BITMAPINFOHEADER bmiHeader;
bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiHeader.biWidth = bgr.cols;
bmiHeader.biHeight = -bgr.rows; // 负号表示从下往上绘制
bmiHeader.biPlanes = 1;
bmiHeader.biBitCount = 24;
bmiHeader.biCompression = BI_RGB;
bmiHeader.biSizeImage = 0;
bmiHeader.biXPelsPerMeter = 0;
bmiHeader.biYPelsPerMeter = 0;
bmiHeader.biClrUsed = 0;
bmiHeader.biClrImportant = 0;
HDC hDC = ::GetDC(NULL);
HBITMAP hBitmap = CreateDIBitmap(hDC, &bmiHeader, CBM_INIT,
bgr.data, (BITMAPINFO*)&bmiHeader, DIB_RGB_COLORS);
::ReleaseDC(NULL, hDC);
return hBitmap;
}
void B2saomiaotuxiangchuangkou::OnCbnSelchangeComboSaveOption()
{
int option = m_saveOption.GetCurSel();
if (option == 0) {
m_savePath = m_folderPath;
m_editSavePath.SetWindowText(m_savePath);
}
else if (option == 1) {
if (m_savePath.IsEmpty() || m_savePath == m_folderPath) {
CFolderPickerDialog dlg;
if (dlg.DoModal() == IDOK) {
m_savePath = dlg.GetFolderPath();
m_editSavePath.SetWindowText(m_savePath);
}
else {
m_saveOption.SetCurSel(0);
m_savePath = m_folderPath;
m_editSavePath.SetWindowText(m_savePath);
}
}
}
}
void B2saomiaotuxiangchuangkou::OnBnClickedButtonStart()
{
if (m_folderPath.IsEmpty()) {
AfxMessageBox(_T("请先选择文件夹!"));
return;
}
if (m_imageFiles.empty()) {
AfxMessageBox(_T("文件夹中没有图像文件!"));
return;
}
if (m_saveOption.GetCurSel() == 1 && m_savePath.IsEmpty()) {
AfxMessageBox(_T("请先选择保存路径!"));
return;
}
if (m_saveOption.GetCurSel() == 0) {
CreateBackup();
}
m_processing = true;
m_paused = false;
m_stopped = false;
m_currentIndex = 0;
m_btnStart.EnableWindow(FALSE);
m_btnPause.EnableWindow(TRUE);
m_btnStop.EnableWindow(TRUE);
m_btnReset.EnableWindow(FALSE);
m_totalProgress.SetPos(0);
m_fileProgress.SetPos(0);
if (m_processingThread.joinable()) {
m_processingThread.join();
}
m_processingThread = std::thread(&B2saomiaotuxiangchuangkou::ProcessImages, this);
SetTimer(1, 100, NULL);
}
void B2saomiaotuxiangchuangkou::OnBnClickedButtonPause()
{
m_paused = !m_paused;
m_btnPause.SetWindowText(m_paused ? _T("继续") : _T("暂停"));
}
void B2saomiaotuxiangchuangkou::OnBnClickedButtonStop()
{
m_stopped = true;
m_processing = false;
m_btnStart.EnableWindow(TRUE);
m_btnPause.EnableWindow(FALSE);
m_btnStop.EnableWindow(FALSE);
m_btnReset.EnableWindow(TRUE);
if (m_processingThread.joinable()) {
m_processingThread.join();
}
KillTimer(1);
}
void B2saomiaotuxiangchuangkou::OnBnClickedButtonReset()
{
OnBnClickedButtonStop();
if (m_saveOption.GetCurSel() == 0 && !m_backupPath.IsEmpty()) {
RestoreBackup();
}
m_currentIndex = 0;
m_totalProgress.SetPos(0);
m_fileProgress.SetPos(0);
m_backupPath.Empty();
m_btnReset.EnableWindow(FALSE);
if (!m_imageFiles.empty()) {
m_listImages.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
PostMessage(WM_LOAD_FIRST_IMAGE, 0, 0);
}
}
void B2saomiaotuxiangchuangkou::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == 1) {
UpdateProgressBars();
}
CDialogEx::OnTimer(nIDEvent);
}
void B2saomiaotuxiangchuangkou::OnLvnItemchangedListImages(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
int nItem = -1;
if (pNMLV) {
if ((pNMLV->uChanged & LVIF_STATE) && (pNMLV->uNewState & LVIS_SELECTED)) {
nItem = pNMLV->iItem;
}
}
else {
nItem = 0;
}
if (!m_firstLoadCompleted && nItem == 0) {
if (pResult) *pResult = 0;
return;
}
if (nItem >= 0 && nItem < (int)m_imageFiles.size()) {
CString filePath = m_imageFiles[nItem];
LoadImageToControl(filePath, IDC_STATIC_ORIGINAL_IMAGE);
CWnd* pProcessed = GetDlgItem(IDC_STATIC_PROCESSED_IMAGE);
if (pProcessed) {
auto pStatic = reinterpret_cast<CStatic*>(pProcessed);
HBITMAP hOldBmp = pStatic->GetBitmap();
if (hOldBmp) {
DeleteObject(hOldBmp);
pStatic->SetBitmap(NULL);
}
}
}
if (pResult) {
*pResult = 0;
}
}
void B2saomiaotuxiangchuangkou::ProcessImages()
{
for (m_currentIndex = 0; m_currentIndex < (int)m_imageFiles.size(); ++m_currentIndex)
{
if (m_stopped) break;
while (m_paused && !m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (m_stopped) break;
ProcessImage(m_imageFiles[m_currentIndex]);
{
std::lock_guard<std::mutex> lock(m_mutex);
int totalProgress = static_cast<int>((static_cast<double>(m_currentIndex + 1) / m_imageFiles.size()) * 100);
m_totalProgress.SetPos(totalProgress);
m_fileProgress.SetPos(100);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
m_processing = false;
m_stopped = true;
PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON_STOP, BN_CLICKED), (LPARAM)m_btnStop.m_hWnd);
AfxMessageBox(_T("处理完成!"));
}
void B2saomiaotuxiangchuangkou::ProcessImage(const CString& filePath)
{
CT2CA filePathConverted(filePath);
std::string filePathStr(filePathConverted);
cv::Mat image = cv::imread(filePathStr);
if (image.empty()) return;
// 更新显示当前处理的图片
UpdateDisplayImage(filePath);
cv::Mat processed;
SimpleBinarization(image, processed);
if (!processed.empty()) {
CString tempPath = m_folderPath + _T("\\temp_processed.jpg");
SaveImage(processed, tempPath);
LoadImageToControl(tempPath, IDC_STATIC_PROCESSED_IMAGE);
DeleteFile(tempPath);
}
if (m_saveOption.GetCurSel() == 0) {
SaveImage(processed, filePath);
}
else {
CString fileName = GetFileNameFromPath(filePath);
CString newPath = m_savePath + _T("\\") + fileName;
SaveImage(processed, newPath);
}
for (int progress = 0; progress <= 100; progress += 10) {
if (m_stopped) return;
while (m_paused && !m_stopped) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (m_stopped) return;
{
std::lock_guard<std::mutex> lock(m_mutex);
m_fileProgress.SetPos(progress);
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
}
// 更新显示图片的函数
void B2saomiaotuxiangchuangkou::UpdateDisplayImage(const CString& filePath)
{
// 使用PostMessage安全地更新UI
PostMessage(WM_UPDATE_DISPLAY_IMAGE, (WPARAM)(LPCTSTR)filePath, 0);
}
// 处理更新显示的消息
LRESULT B2saomiaotuxiangchuangkou::OnUpdateDisplayImage(WPARAM wParam, LPARAM lParam)
{
CString filePath = (LPCTSTR)wParam;
if (filePath.IsEmpty()) return 0;
std::lock_guard<std::mutex> lock(m_bitmapMutex);
// 先释放之前的位图资源
if (m_currentBitmap) {
DeleteObject(m_currentBitmap);
m_currentBitmap = NULL;
}
// 加载新的位图
HBITMAP hBitmap = NULL;
if (m_loadMode == MODE_OPENCV) {
hBitmap = LoadImageWithOpenCV(filePath);
}
else {
hBitmap = LoadImageWithGDIPlus(filePath);
}
if (hBitmap) {
m_currentBitmap = hBitmap;
CStatic* pStatic = dynamic_cast<CStatic*>(GetDlgItem(IDC_STATIC_ORIGINAL_IMAGE));
if (pStatic) {
HBITMAP oldBmp = pStatic->GetBitmap();
if (oldBmp) DeleteObject(oldBmp);
pStatic->SetBitmap(hBitmap);
pStatic->Invalidate();
}
}
return 0;
}
void B2saomiaotuxiangchuangkou::SimpleBinarization(cv::Mat& input, cv::Mat& output)
{
cv::Mat gray;
if (input.channels() == 3) {
cv::cvtColor(input, gray, cv::COLOR_BGR2GRAY);
}
else {
gray = input.clone();
}
cv::threshold(gray, output, 128, 255, cv::THRESH_BINARY);
}
void B2saomiaotuxiangchuangkou::SaveImage(cv::Mat& img, const CString& filePath)
{
if (img.empty()) return;
CT2CA filePathConverted(filePath);
std::string filePathStr(filePathConverted);
std::vector<int> compression_params;
compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
compression_params.push_back(95);
cv::imwrite(filePathStr, img, compression_params);
}
void B2saomiaotuxiangchuangkou::UpdateProgressBars()
{
int totalProgress = static_cast<int>((static_cast<double>(m_currentIndex) / m_imageFiles.size() * 100));
m_totalProgress.SetPos(totalProgress);
}
void B2saomiaotuxiangchuangkou::CreateBackup()
{
if (m_folderPath.IsEmpty()) return;
CTime time = CTime::GetCurrentTime();
CString timeStr = time.Format(_T("%Y%m%d%H%M%S"));
m_backupPath = m_folderPath + _T("\\backup_") + timeStr;
if (!CreateDirectory(m_backupPath, NULL)) {
return;
}
for (const auto& file : m_imageFiles) {
CString fileName = GetFileNameFromPath(file);
CString destPath = m_backupPath + _T("\\") + fileName;
CopyFile(file, destPath, FALSE);
}
}
void B2saomiaotuxiangchuangkou::RestoreBackup()
{
if (m_backupPath.IsEmpty()) return;
for (const auto& file : m_imageFiles) {
CString fileName = GetFileNameFromPath(file);
CString sourcePath = m_backupPath + _T("\\") + fileName;
CopyFile(sourcePath, file, FALSE);
}
RemoveDirectory(m_backupPath);
m_backupPath.Empty();
}
void B2saomiaotuxiangchuangkou::Cleanup()
{
m_stopped = true;
m_processing = false;
if (m_processingThread.joinable()) {
m_processingThread.join();
}
KillTimer(1);
// 释放位图资源
std::lock_guard<std::mutex> lock(m_bitmapMutex);
if (m_currentBitmap) {
DeleteObject(m_currentBitmap);
m_currentBitmap = NULL;
}
HBITMAP hBmp = m_originalImage.GetBitmap();
if (hBmp) DeleteObject(hBmp);
hBmp = m_processedImage.GetBitmap();
if (hBmp) DeleteObject(hBmp);
}
CString B2saomiaotuxiangchuangkou::GetFileNameFromPath(const CString& filePath)
{
int pos = filePath.ReverseFind('\\');
if (pos == -1) pos = filePath.ReverseFind('/');
if (pos != -1) {
return filePath.Mid(pos + 1);
}
return filePath;
}
该代码IDC_STATIC_ORIGINAL_IMAGE控件绘图时,绘制的图片相对于源图发生了水平偏移
最新发布