OpenCV与MFC实战之图像处理 样本采集小工具制作 c++MFC课程设计

原文作者:aircraft

原文链接:https://www.cnblogs.com/DOMLX/p/12111102.html

    入门不久的人可以通过opencv实战来锻炼一下学习opencv的成果,百度云链接:

链接:https://pan.baidu.com/s/1jGOD97Zx96ZDAvlkQtaPYQ
提取码:afip

运行环境VS2017,需要配置库为:opencv

题目:样本采集小工具

需求:

用MFC和opencv完成样本采集小工具。

界面功能

1、选中原图片集的目录。

2、选择当前是正样本还是负样本?并选中其目录。

3、通过上一张下一张更换原图片集的图片显示。

鼠标点击图片显示区域功能

1、左击图片选中,以鼠标点击处为中心,宽W*高H的区域。

2、鼠标滚轮上滚扩大选中区域。

3、鼠标滚轮下滚缩小选中区域。

4、右击保存选中区域的图片在正样本或负样本的目录下,取决于当前选中正样本还是负样本。

完成界面如图:

第一步:把MFC界面的那些控件都拖动好并且绑定好opencv图形框

在MFC的初始化函数中添加我们的绑定代码:

BOOL CpicroiDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);            // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    //InitializeSkin(("Minimized.ssk"));//初始化

    // TODO: 在此添加额外的初始化代码
    namedWindow("ImageShow", CV_WINDOW_KEEPRATIO);          // 用OpenCV创建一个窗口
    CRect cWindowRect;
    m_PictureControl.GetClientRect(&cWindowRect);           // 获取控件窗口大小
    //int nWindowWidth = cWindowRect.Width();
    //int nWindowHeight = cWindowRect.Height();
    //resizeWindow("ImageShow", 200, 100);
    HWND hPictureWindow = (HWND)cvGetWindowHandle("ImageShow");        //  获取OpenCV窗口的句柄
    HWND hParentWindow = ::GetParent(hPictureWindow);
    ::SetParent(hPictureWindow, GetDlgItem(IDC_PIC)->m_hWnd);   //  关联OpenCV窗口和MFC的控件窗口
    ::ShowWindow(hParentWindow, SW_HIDE);
    GetDlgItem(IDC_PIC)->ShowWindow(0);                        // 开始不显示图片控件

    setMouseCallback("ImageShow", onMouse, 0);

    pcom.InsertString(0, "正样本");
    pcom.InsertString(1, "负样本");
    pcom.SetCurSel(0);

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

第二步:选中原图片集的目录

这样接下来操作的图片都从这个目录顺序读取,并且切换上下张

添加好对话框类之后:

双击这个控件,进入相应的编辑函数内部编写事件处理代码:

void CpicroiDlg::OnBnClickedMainFilePath()
{
    // TODO: 在此添加控件通知处理程序代

    CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, _T("image files (*.jpeg; *.jpg; *.bmp;*.png)  All Files (*.*) |*.*||"));
    CString m_strPath;
    CString m_folderpath;
    if (IDOK == dlg.DoModal()) {
        m_strPath = dlg.GetPathName();
        m_folderpath = dlg.GetFolderPath();
    }
    string strName = CT2A(m_strPath.GetString());              // CString和string之间的类型转换
    string strPathNames = CT2A(m_folderpath.GetString());
    sourceImage = imread(strName);
    nWindowWidth = sourceImage.cols;
    nWindowHeight = sourceImage.rows;
    resizeWindow("ImageShow", nWindowWidth, nWindowHeight);
    if (strName == "")return;

    tempImage = sourceImage.clone();
    imshow("ImageShow", sourceImage);
    GetDlgItem(IDC_PIC)->ShowWindow(1);

    SetDlgItemText(IDC_MAIN_FILE, m_folderpath);
    UpdateWindow();
    getFiles1(strPathNames, files);
    filesLen = files.size()-1;
    for (auto path : files) {
        index++;
        if (path == strName)break;
    }
}

同样的也要选择保存的文件路径:

void CpicroiDlg::OnBnClickedMainFilePath2()
{
    // TODO: 在此添加控件通知处理程序代码
    if (m_dlgMainFile.DoModal() != IDOK)
        return;

    CString cstrFile;                //文件全名
    string strFile;
    cstrFile = m_dlgMainFile.GetFolderPath();
    strFile = CT2A(cstrFile.GetString());
    SetDlgItemText(IDC_MAIN_FILE2, cstrFile);
    UpdateWindow();
    getFiles1(strFile, filesPath2);
    picIndex = filesPath2.size();
    GetDlgItem(IDC_MAIN_FILE2)->GetWindowTextA(cs_pcomValue);
    s_pcomComValue = CT2A(cs_pcomValue.GetString());
}

void CpicroiDlg::OnBnClickedMainFilePath3()
{
    // TODO: 在此添加控件通知处理程序代码
    if (m_dlgMainFile.DoModal() != IDOK)
        return;

    CString strFile;                //文件全名
    strFile = m_dlgMainFile.GetFolderPath() + "\\";
    SetDlgItemText(IDC_MAIN_FILE3, strFile);
    UpdateWindow();
}

这样只要选择好了正负样本点击右键就可以自动保存到相应的选择好的目录了

第三步:鼠标控制图像的裁剪和选定

这里我们要实现的是鼠标点击出现一个矩形框,然后用鼠标的滚轮去滚动,让矩形框围绕中心点改变大小,然后点击右键则保存图形

void onMouse(int event, int x, int y, int flag, void*) {

    //CDC *pDC = GetDC();
    //CString str; str.Format(TEXT("%d,%d"), x, y);
    //pDC->FillSolidRect(0, 0, 100, 100, GetSysColor(COLOR_WINDOW));
    //pDC->TextOut(1, 0, str);
    switch (event)
    {
    case CV_EVENT_LBUTTONDOWN://左键按下
        flag = true;
        mousFlag = 1;
        moux = x;
        mouy = y;
        if ((x - width / 2) < 0)Lu.x = 0;
        else Lu.x = x - width / 2;
        if ((y - height / 2) < 0)Lu.y = 0;
        else Lu.y = y - height / 2;
        if ((x + width / 2) > nWindowWidth - 1)Rd.x = nWindowWidth - 1;
        else Rd.x = x + width / 2;
        if ((y + height / 2) > nWindowHeight - 1 )Rd.y = nWindowHeight - 1;
        else Rd.y = y + height / 2;
        lastImage = tempImage.clone();
        rectangle(tempImage, Lu, Rd, Scalar(0, 255, 0), 1, 0, 0);
        imshow("ImageShow", tempImage);
        break;
    case CV_EVENT_RBUTTONDOWN://右键按下
        {    Rect rect(Lu.x, Lu.y, Rd.x-Lu.x, Rd.y-Lu.y);
        g_rect = rect;
        }
        if (s_pcomComValue == "")
        {
            MessageBox(AfxGetMainWnd()->m_hWnd, "存放目录未填写","警告", MB_OK);
            break;
        }
        s_save = s_pcomComValue +"\\"+ to_string(picIndex) + ".jpg";
        ROI = sourceImage(g_rect);
        imwrite(s_save, ROI);
        picIndex++;
        break;
    case CV_EVENT_MOUSEWHEEL:
        int mousWhellFlag;
        int value;
        mousWhellFlag = 1;
        value = getMouseWheelDelta(flag);
        if (value > 0) {
            Lu.x = Lu.x > 0 ? Lu.x -= step : Lu.x;
            Lu.y = Lu.y > 0 ? Lu.y -= step : Lu.y;

            Rd.x = Rd.x < (nWindowWidth - 1) ? Rd.x += step : (nWindowWidth - 1);
            Rd.y = Rd.y < (nWindowHeight - 1) ? Rd.y += step : (nWindowHeight - 1);
        }
        else {
            Lu.x = Lu.x < x ? Lu.x += step : x;
            Lu.y = Lu.y < y ? Lu.y += step : y;

            Rd.x = Rd.x > x ? Rd.x -= step : x;
            Rd.y = Rd.y > y ? Rd.y -= step : y;
        }
        {    Rect rect(Lu.x, Lu.y, Rd.x - Lu.x, Rd.y - Lu.y); g_rect = rect;    }
        tempImage = lastImage.clone();
        rectangle(tempImage, Lu, Rd, Scalar(0, 255, 0), 1, 0, 0);
        imshow("ImageShow", tempImage);
        break;

    default:
        break;
    }

}

因为我们裁剪已经画矩形框都不能在原图上画,所以我们复制一份图像显示,所有的操作都是在复制 的临时图像上操作的,然后在根据缓冲刷新,将图像替换就行了

最后说一句,这个是直接显示原图的,如果原图过大只能看到部分,这时候在代码里面加个判断,然后用opencv的图像归一化的函数去改变一下图像大小即可。

因为不想写的很详细,(别问为什么,问就是因为最近很懒!!!),所以我给出了项目源码的百度云在文章的开头

若有兴趣交流分享技术,可关注本人公众号,里面会不定期的分享各种编程教程,和共享源码,诸如研究分享关于c/c++,python,前端,后端,opencv,halcon,opengl,机器学习深度学习之类有关于基础编程,图像处理和机器视觉开发的知识

原文地址:https://www.cnblogs.com/DOMLX/p/12111102.html

时间: 2024-11-05 13:37:55

OpenCV与MFC实战之图像处理 样本采集小工具制作 c++MFC课程设计的相关文章

我为什么要写《OpenCV Android 开发实战》这本书

我为什么要写<OpenCV Android 开发实战>这本书 2015年我出版了个人第一本关于图像处理方面的书籍<Java图像处理-编程技巧与应用实践>,这本书主要是从理论与编码上面详细阐述了图像处理基础算法以及它们在编码实现上的技巧.一转眼已经三年过去了,在这三年的时光里我无时无刻都在关注图像处理与计算机视觉技术发展与未来,同时渐渐萌发了再写一本图像处理相关技术书籍的念头,主要是因为<Java图像处理-编程技巧与应用实践>一书主要不是针对工程应用场景,读者在学完之后很

VS2015 全套C/C++、MFC实战、视频教程 最好的基础入门教程

VS2015 全套C/C++.MFC实战. 最好的基础入门教程课程 本课程包括: [1]C语言(1个月) [2]C++语法与数据结构(1个月)) [3]MFC项目开发(1个月) 往届的授课视频都已经上传到百度网盘,请同学们按照视频教程提前掌握课程的进度情况. VS2015系列视频教程包括: <VS2015--0基础C语言视频教程> <VS2015--C++视频教程与数据结构> <VS2015--MFC项目开发视频教程> 等完整的C++就 业培训内容.真正的C语言是从二进

mfc小工具开发之定时闹钟之---功能介绍

使用背景: 之前在xp上用过飞雪日历,感觉挺好用的,还有在音频上的兴趣,促使了我也要自己做一个简单的定时闹钟. 之前开发过图片格式的小工具,没来的急分享,后期整理后,一块奉上,写这篇介绍的时候已近完成定时闹钟的demo部分,时间是凌晨0点30 功能需求: 1.守护进程,进行全天候开启定时闹钟deamon 2.初期暂时定时,然后 准确报时 3.音乐提醒 4.能够后台挂起,无需打扰用户 mfc小工具开发之定时闹钟之---功能介绍,布布扣,bubuko.com

mfc小工具开发之定时闹钟之---多线程急线程同步

一.MFC对多线程编程的支持 MFC中有两类线程,分别称之为工作者线程和用户界面线程.二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环. 工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等.用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等.但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务. 在MFC中,一般用全局函数Afx

java架构师课程、性能调优、高并发、tomcat负载均衡、大型电商项目实战、高可用、高可扩展、数据库架构设计、Solr集群与应用、分布式实战、主从复制、高可用集群、大数据

15套Java架构师详情 * { font-family: "Microsoft YaHei" !important } h1 { background-color: #006; color: #FF0 } 15套java架构师.集群.高可用.高可扩展.高性能.高并发.性能优化.Spring boot.Redis.ActiveMQ.Nginx.Mycat.Netty.Jvm大型分布式项目实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  clo

Tsar 服务器系统和应用信息的采集报告工具

Tsar介绍 Tsar是淘宝的一个用来收集服务器系统和应用信息的采集报告工具,如收集服务器的系统信息(cpu,mem等),以及应用数据(nginx.swift等),收集到的数据存储在服务器磁盘上,可以随时查询历史信息,也可以将数据发送到nagios报警. Tsar能够比较方便的增加模块,只需要按照tsar的要求编写数据的采集函数和展现函数,就可以把自定义的模块加入到tsar中. Tsar安装 Tsar目前托管在github上,下载编译安装步骤: $git clone git://github.c

Skype For Business 2015实战系列10:安装管理工具

Skype For Business 2015实战系列10:安装管理工具 今天要给大家介绍的是Skype for Business Server 2015安装前的准备工作-安装管理工具.Skype for Business Server 2015 的安装介质提供了灵活的体验.用户第一次运行Setup时,唯一安装的工具是 Skype for Business Server 部署向导和 Skype for Business Server 命令行管理程序.通过使用这两个工具(即核心组件),我们可以继续

基于MFC的Media Player的播放器的制作(1---播放器界面的布局)

|   版权声明:本文为博主原创文章,未经博主允许不得转载. 通过上面的一些预备知识,我们现在就可以自己来制作基于MFC的播放器了,接下来我们讲的是使用MFC制作我们播放器 的界面. 首先,我们我们打开VC++ 6.0.找到工具条:  文件 ---> 新建--->在接下的操作根据图片来 上面的操作完成之后,我们可以在我们设置的保存路径处,找到我们的过程文件夹,并且可以看到VC++6.0的面板了. 如下图: 接下来,介绍界面组件的添加:如图操作 如上图将所有的组件添加好,并设置好ID和名称,我的

如何制作带MFC界面的MFC DLL

最近在做基于组件化MFC界面的开发,需要把界面封装到动态库中. 一:工程创建步骤 1.创建MFC DLL工程,选择 “在共享 DLL 中使用 MFC”. 2.运行时库选择:c/c++-->代码生成-->运行库  “多线程调试 DLL (/MDd)”. 二:定义导出界面基类 定义导出界面接口类,待导出界面需要继承该接口类,并实现该接口定义的虚函数.如下: class IHpDllWin :public CDialog { public: // 构造函数,ID为窗口资源ID IHpDllWin(i