iis服务器助手广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >OpenCV实现简单套索工具
  • 699
分享到

OpenCV实现简单套索工具

2024-04-02 19:04:59 699人浏览 泡泡鱼
摘要

Photoshop中的套索工具通过鼠标多次点击可以选中一个任意多边形的区域,然后单独对这块区域进行编辑,下面就使用OpenCV实现一个简单的功能,模拟Photoshop中的套索工具。

Photoshop中的套索工具通过鼠标多次点击可以选中一个任意多边形的区域,然后单独对这块区域进行编辑,下面就使用OpenCV实现一个简单的功能,模拟Photoshop中的套索工具。

这里的套索工具通过鼠标左键在图片上多次点击创建任意多个点,右键点击后将这些点连成封闭的多边形,形成一块待编辑的区域,键盘方向键控制该区域的移动,从而将该区域内的图像复制到原图像的其他地方。

首先定义下列全局变量

const char* winName = "TaoSuoTool";//窗口名称
cv::Mat resultImg;//最终在OpenCV窗口上显示的图像
cv::Mat foregroundImg;//编辑前的图像
cv::Mat areaMask;//蒙版,多边形区域实际绘制在该蒙版上
cv::Point maskLocation;//蒙版位置,通过方向键移动后蒙版位置随之变化
std::vector<cv::Point> drawingPoints;//区域完成前正在点击的所有点
std::vector<cv::Point> areaPoints;//区域完成后其多边形顶点

main函数

int main(int arGC, char **arv)
{
    foregroundImg = cv::imread("test.jpg");
    foregroundImg.copyTo(resultImg);
    areaMask = cv::Mat::zeros(foregroundImg.size(), CV_8U);
    cv::imshow(winName, resultImg);
 
    maskLocation.x = maskLocation.y = 0;
    cv::setMouseCallback(winName, OnMouseEvent);
    int key = cv::waiTKEyEx(0);
    while (key != VK_ESCAPE)
    {
        key = cv::waitKeyEx(0);
    }
    return 0;
}

在鼠标回调函数OnMouseEvent中处理三个消息:鼠标左键按下,鼠标右键按下和鼠标移动

void OnMouseEvent(int event, int x, int y, int flags, void* userdata)
{
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        OnLeftMouseButtonDown(x,y);
    }
    else if (event == cv::EVENT_RBUTTONDOWN)
    {
        OnRightMouseButtonDown(x,y);
    }
    if (event == cv::EVENT_MOUSEMOVE)
    {
        OnMouseMove(x,y);
    }
}

在编写鼠标事件前先定义一个函数

void OnCompleteArea(bool bDrawOutline);

它表示完成当前区域的编辑,包括右键点击完成封闭多边形、移动区域以及合成最终图片。参数bDrawOutline表示绘制区域多边形的外轮廓,右键点击完成封闭多边形和移动区域过程中都要显示轮廓(bDrawOutline=true),合成最终图片后就不需要显示轮廓了(bDrawOutline=false)。
鼠标左键按下事件:先判断是否有前一个区域存在,存在则先完成前一个区域并且不显示区域轮廓,然后开始绘制新的区域多边形的点,点与点之间用蓝色线连接,点位置处绘制一个4X4的红色矩形。

void OnLeftMouseButtonDown(int x,int y)
{
    if (drawingPoints.empty() && areaPoints.size() > 0)
    {
        OnCompleteArea(false);
    }
    drawingPoints.push_back(cv::Point(x, y));
    cv::rectangle(resultImg, cv::Rect(x - 2, y - 2, 4, 4), CV_RGB(255, 0, 0), -1);
    if (drawingPoints.size() >= 2)
    {
        cv::line(resultImg, drawingPoints[drawingPoints.size() - 2], cv::Point(x, y), CV_RGB(0, 0, 255), 1);
    }
    cv::imshow(winName, resultImg);
}

鼠标移动事件:判断drawingPoints是否为空,如果已经存在点则绘制线和点,并且还要绘制一根连接到鼠标当前位置的线。

void OnMouseMove(int x,int y)
{
    if (drawingPoints.size() > 0)
    {
        foregroundImg.copyTo(resultImg);
        for (int i = 0; i < drawingPoints.size() - 1; i++)
        {
            cv::rectangle(resultImg, cv::Rect(drawingPoints[i].x - 2, drawingPoints[i].y - 2, 4, 4), CV_RGB(255, 0, 0), -1);
            cv::line(resultImg, drawingPoints[i], drawingPoints[i + 1], CV_RGB(0, 0, 255), 1);
        }
        cv::rectangle(resultImg, cv::Rect(drawingPoints[drawingPoints.size() - 1].x - 2, drawingPoints[drawingPoints.size() - 1].y - 2, 4, 4), CV_RGB(255, 0, 0), -1);
        cv::line(resultImg, drawingPoints[drawingPoints.size() - 1], cv::Point(x, y), CV_RGB(0, 0, 255), 1);
        cv::imshow(winName, resultImg);
    }
}

鼠标右键按下事件:如果点个数少于2,不能形成有效区域则不做处理(不考虑多个点共线),否则就在蒙版Area上绘制一个多边形区域,然后调用OnCompleteArea完成区域编辑,这里需要画多边形轮廓,参数传入true。

void OnRightMouseButtonDown(int x,int y)
{
    if (drawingPoints.size() >= 3)
    {
        areaPoints = drawingPoints;
        std::vector<std::vector<cv::Point>> polys;
        polys.push_back(areaPoints);
        cv::fillPoly(areaMask, polys, cv::Scalar::all(255));
        OnCompleteArea(true);
    }
    else
    {
        foregroundImg.copyTo(resultImg);
    }
    drawingPoints.clear();
    cv::imshow(winName, resultImg);
}

下面是OnCompleteArea函数的实现,其中MergeImages函数通过蒙版以及蒙版的位置合成最终的图像,蒙版中区域内的像素值大于0,其他像素值都为0,默认图像是三通道(destImg.at<cv::Vec3b>)

void MergeImages(cv::Mat& destImg, const cv::Mat& srcImg, const cv::Mat& maskImg, int x, int y)
{
    int top = y > 0 ? y : 0;
    int left = x > 0 ? x : 0;
    int right = destImg.cols > x + srcImg.cols ? x + srcImg.cols : destImg.cols;
    int bottom = destImg.rows > y + srcImg.rows ? y + srcImg.rows : destImg.rows;
    for (int i = top; i < bottom; i++)
    {
        for (int j = left; j < right; j++)
        {
            int destIndex = i * destImg.cols + j;
            int srcIndex = (i - top)*srcImg.cols + j - left;
            int channel = destImg.channels();
            if (maskImg.at<uchar>(i - y, j - x) > 0)
            {
                destImg.at<cv::Vec3b>(i, j)[0] = srcImg.at<cv::Vec3b>(i - y, j - x)[0];
                destImg.at<cv::Vec3b>(i, j)[1] = srcImg.at<cv::Vec3b>(i - y, j - x)[1];
                destImg.at<cv::Vec3b>(i, j)[2] = srcImg.at<cv::Vec3b>(i - y, j - x)[2];
            }
        }
    }
}
void OnCompleteArea(bool bDrawOutline)
{
    foregroundImg.copyTo(resultImg);
    MergeImages(resultImg, foregroundImg, areaMask, maskLocation.x, maskLocation.y);
    if (bDrawOutline)
    {
        if (areaPoints.size() >= 3)
        {
            std::vector<std::vector<cv::Point>> polys;
            polys.push_back(areaPoints);
            cv::polylines(resultImg, polys, true, cv::Scalar::all(255));
        }
    }
    else
    {
        resultImg.copyTo(foregroundImg);
        areaPoints.clear();
        memset(areaMask.data, 0, areaMask.rows*areaMask.cols*areaMask.elemSize());
        maskLocation.x = maskLocation.y = 0;
    }
}

绘制区域之后就可以通过方向按键控制区域图像的移动了(也可以实现为鼠标左键按下拖动来移动区域),移动主要是更新maskLocation和areaPoints的坐标值,然后调用OnCompleteArea(true),依然显示区域的轮廓。

void OnDirectionKeyDown(short keyCode)
{
    int x = keyCode == VK_LEFT ? -2 : (keyCode == VK_RIGHT ? 2 : 0);
    int y = keyCode == VK_UP ? -2 : (keyCode == VK_DOWN ? 2 : 0);
    maskLocation.x += x;
    maskLocation.y += y;
    for (int i = 0; i < areaPoints.size(); i++)
    {
        areaPoints[i].x += x;
        areaPoints[i].y += y;
    }
    OnCompleteArea(true);
    cv::imshow(winName, resultImg);
}

将上面函数在主函数的按键循环中调用,方向按键通过key的高16位判断,在windows下可以使用虚拟键码宏表示。 同时为了能看到最终合成的图片加入Enter按键消息处理,将图像合成并去掉轮廓。

int key = cv::waitKeyEx(0);
short lowKey = key;
short highKey = key >> 16;
while (key != VK_ESCAPE)
{
    if (key == VK_RETURN)//Enter
    {
        OnCompleteArea(false);
        cv::imshow(winName, resultImg);
    }
    else if (lowKey == 0 && (highKey == VK_UP || highKey == VK_DOWN || highKey == VK_LEFT || highKey == VK_RIGHT))
    {
        OnDirectionKeyDown(highKey);
    }
    key = cv::waitKeyEx(0);
    lowKey = key;
    highKey = key >> 16;
}

这样一个简单的套索工具功能就做好了(上面的代码都是简化处理,还有很多可以优化的地方,从而使编辑更加流畅)

完整代码

#include<iOStream>
#include"opencv2/opencv.hpp"
#include<windows.h>
const char* winName = "TaoSuoTool";
cv::Point maskLocation;
cv::Mat resultImg;
cv::Mat foregroundImg;
cv::Mat areaMask;
std::vector<cv::Point> drawingPoints;
std::vector<cv::Point> areaPoints;
void MergeImages(cv::Mat& destImg, const cv::Mat& srcImg, const cv::Mat& maskImg, int x, int y)
{
    int top = y > 0 ? y : 0;
    int left = x > 0 ? x : 0;
    int right = destImg.cols > x + srcImg.cols ? x + srcImg.cols : destImg.cols;
    int bottom = destImg.rows > y + srcImg.rows ? y + srcImg.rows : destImg.rows;
    for (int i = top; i < bottom; i++)
    {
        for (int j = left; j < right; j++)
        {
            int destIndex = i * destImg.cols + j;
            int srcIndex = (i - top)*srcImg.cols + j - left;
            int channel = destImg.channels();
            if (maskImg.at<uchar>(i - y, j - x) > 0)
            {
                destImg.at<cv::Vec3b>(i, j)[0] = srcImg.at<cv::Vec3b>(i - y, j - x)[0];
                destImg.at<cv::Vec3b>(i, j)[1] = srcImg.at<cv::Vec3b>(i - y, j - x)[1];
                destImg.at<cv::Vec3b>(i, j)[2] = srcImg.at<cv::Vec3b>(i - y, j - x)[2];
            }
        }
    }
}
 
void OnCompleteArea(bool bDrawOutline)
{
    foregroundImg.copyTo(resultImg);
    MergeImages(resultImg, foregroundImg, areaMask, maskLocation.x, maskLocation.y);
    if (bDrawOutline)
    {
        if (areaPoints.size() >= 3)
        {
            std::vector<std::vector<cv::Point>> polys;
            polys.push_back(areaPoints);
            cv::polylines(resultImg, polys, true, cv::Scalar::all(255));
        }
    }
    else
    {
        resultImg.copyTo(foregroundImg);
        areaPoints.clear();
        memset(areaMask.data, 0, areaMask.rows*areaMask.cols*areaMask.elemSize());
        maskLocation.x = maskLocation.y = 0;
    }
}
void OnLeftMouseButtonDown(int x,int y)
{
    if (drawingPoints.empty() && areaPoints.size() > 0)
    {
        OnCompleteArea(false);
    }
    drawingPoints.push_back(cv::Point(x, y));
    cv::rectangle(resultImg, cv::Rect(x - 2, y - 2, 4, 4), CV_RGB(255, 0, 0), -1);
    if (drawingPoints.size() >= 2)
    {
        cv::line(resultImg, drawingPoints[drawingPoints.size() - 2], cv::Point(x, y), CV_RGB(0, 0, 255), 1);
    }
    cv::imshow(winName, resultImg);
}
void OnRightMouseButtonDown(int x,int y)
{
    if (drawingPoints.size() >= 3)
    {
        areaPoints = drawingPoints;
        std::vector<std::vector<cv::Point>> polys;
        polys.push_back(areaPoints);
        cv::fillPoly(areaMask, polys, cv::Scalar::all(255));
        OnCompleteArea(true);
    }
    else
    {
        foregroundImg.copyTo(resultImg);
    }
    drawingPoints.clear();
    cv::imshow(winName, resultImg);
}
void OnMouseMove(int x,int y)
{
    if (drawingPoints.size() > 0)
    {
        foregroundImg.copyTo(resultImg);
        for (int i = 0; i < drawingPoints.size() - 1; i++)
        {
            cv::rectangle(resultImg, cv::Rect(drawingPoints[i].x - 2, drawingPoints[i].y - 2, 4, 4), CV_RGB(255, 0, 0), -1);
            cv::line(resultImg, drawingPoints[i], drawingPoints[i + 1], CV_RGB(0, 0, 255), 1);
        }
        cv::rectangle(resultImg, cv::Rect(drawingPoints[drawingPoints.size() - 1].x - 2, drawingPoints[drawingPoints.size() - 1].y - 2, 4, 4), CV_RGB(255, 0, 0), -1);
        cv::line(resultImg, drawingPoints[drawingPoints.size() - 1], cv::Point(x, y), CV_RGB(0, 0, 255), 1);
        cv::imshow(winName, resultImg);
    }
}
void OnMouseEvent(int event, int x, int y, int flags, void* userdata)
{
    if (event == cv::EVENT_LBUTTONDOWN)
    {
        OnLeftMouseButtonDown(x,y);
    }
    else if (event == cv::EVENT_RBUTTONDOWN)
    {
        OnRightMouseButtonDown(x,y);
    }
    if (event == cv::EVENT_MOUSEMOVE)
    {
        OnMouseMove(x,y);
    }
}
 
void OnDirectionKeyDown(short keyCode)
{
    int x = keyCode == VK_LEFT ? -2 : (keyCode == VK_RIGHT ? 2 : 0);
    int y = keyCode == VK_UP ? -2 : (keyCode == VK_DOWN ? 2 : 0);
    maskLocation.x += x;
    maskLocation.y += y;
    for (int i = 0; i < areaPoints.size(); i++)
    {
        areaPoints[i].x += x;
        areaPoints[i].y += y;
    }
    OnCompleteArea(true);
    cv::imshow(winName, resultImg);
}
int main(int argc, char **arv)
{
    foregroundImg = cv::imread("test.jpg");
    foregroundImg.copyTo(resultImg);
    areaMask = cv::Mat::zeros(foregroundImg.size(), CV_8U);
    cv::imshow(winName, resultImg);
 
    maskLocation.x = maskLocation.y = 0;
    cv::setMouseCallback(winName, OnMouseEvent);
    int key = cv::waitKeyEx(0);
    short lowKey = key;
    short highKey = key >> 16;
    while (key != VK_ESCAPE)
    {
        if (key == VK_RETURN)//Enter
        {
            OnCompleteArea(false);
            cv::imshow(winName, resultImg);
        }
        else if (lowKey == 0 && (highKey == VK_UP || highKey == VK_DOWN || highKey == VK_LEFT || highKey == VK_RIGHT))
        {
            OnDirectionKeyDown(highKey);
        }
        key = cv::waitKeyEx(0);
        lowKey = key;
        highKey = key >> 16;
    }
    return 0;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

本文标题: OpenCV实现简单套索工具

本文链接: https://www.lsjlt.com/news/162970.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • OpenCV实现简单套索工具
    Photoshop中的套索工具通过鼠标多次点击可以选中一个任意多边形的区域,然后单独对这块区域进行编辑,下面就使用OpenCV实现一个简单的功能,模拟Photoshop中的套索工具。...
    99+
    2024-04-02
  • OpenCV是怎么实现简单套索工具
    这篇文章将为大家详细讲解有关OpenCV是怎么实现简单套索工具,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Photoshop中的套索工具通过鼠标多次点击可以选中一个任意多边形的区域,然后单...
    99+
    2023-06-26
  • Java实现简单QQ聊天工具
    Java实现简单的类似QQ聊天工具,供大家参考,具体内容如下 所使用到的知识点: java socket编程之TCP协议java Swing简单的java多线程 运行截图: 服务...
    99+
    2024-04-02
  • OpenCV实现抠图工具
    本文实例为大家分享了OpenCV实现抠图工具的具体代码,供大家参考,具体内容如下 在计算机图像领域,我们经常需要做一些抠图的工作,将图像中的目标感兴趣区域提取出来,剔除其他冗余的背景...
    99+
    2024-04-02
  • 怎么用Python实现基于Pyqt5的简单电影搜索工具
    这篇文章主要介绍“怎么用Python实现基于Pyqt5的简单电影搜索工具”,在日常操作中,相信很多人在怎么用Python实现基于Pyqt5的简单电影搜索工具问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用...
    99+
    2023-06-02
  • PS的套索工具有哪些
    今天小编给大家分享一下PS的套索工具有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。 ...
    99+
    2024-04-02
  • Python 小工具 -实现简单文件对比
    起因            历史遗留问题导致CMDB (配置管理数据库) 数据错误,内网机器200多台,逐一核对显然太不现实; (浪费人力);      2.解决问题思路 ;        读取docker 平台ip接口,和cmdb 平台接...
    99+
    2023-01-31
    小工具 简单 文件
  • Unity实现简单的多人聊天工具
    本文实例为大家分享了Unity实现多人聊天工具的具体代码,供大家参考,具体内容如下 代码1 : 服务端代码 using UnityEngine; using System.Net.S...
    99+
    2024-04-02
  • OpenCV实现简单录屏功能
    本文实例为大家分享了OpenCV实现简单录屏功能的具体代码,供大家参考,具体内容如下 OpenCV中VideoCapture和VideoWriter用于读写视频文件,这里的录屏功能用...
    99+
    2024-04-02
  • opencv C++模板匹配的简单实现
    目录一简单实现二函数及原理讲解1matchTemplate()参数详解2minMaxLoc()函数一 简单实现 #include <opencv2/opencv.hpp>...
    99+
    2024-04-02
  • 基于C#实现一个简单的FTP操作工具
    目录实现功能开发环境实现代码实现效果实现功能 实现使用FTP上传、下载、重命名、刷新、删除功能 开发环境 开发工具: Visual Studio 2013 .NET Framewor...
    99+
    2024-04-02
  • PHP实用工具:数字转大写功能的简单实现
    PHP实用工具:数字转大写功能的简单实现 在日常开发中,经常会遇到将数字转换成大写的需求,比如金额大写转换等。在PHP中,我们可以通过简单的代码实现这个功能,让我们来看看具体的实现方法...
    99+
    2024-04-02
  • PHP实用工具:数字转大写功能的简单实现
    PHP实用工具:数字转大写功能的简单实现 在日常开发中,经常会遇到将数字转换成大写的需求,比如金额大写转换等。在PHP中,我们可以通过简单的代码实现这个功能,让我们来看看具体的实现方法...
    99+
    2024-04-02
  • 基于Python制作一个简单的文章搜索工具
    目录 前言功能实现导入模块创建窗口背景图片搜索文本框 内容显示界面搜索内容效果代码展示内容效果代码点击搜索功能代码访问博客网页 前言 今天,我无聊的时候...
    99+
    2023-05-12
    Python实现文章搜索工具 Python文章搜索工具 Python文章搜索 Python 搜索工具
  • Golang 基于flag库实现一个简单命令行工具
    目录前言flag 库FlagSet需求拆解实现 weather flag天气数据打印获取源数据数据转换运行效果小结前言 Golang 标准库中的 flag 库提供了解析命令行选项的能...
    99+
    2024-04-02
  • 基于Python实现简单的汉字拼音转换工具
    目录1.准备2.基本使用3.高级使用将汉字转为拼音,可以用于批量汉字注音、文字排序、拼音检索文字等常见场景。 现在互联网上有许多拼音转换工具,基于Python的开源模块也不少,今天给...
    99+
    2024-04-02
  • 超级简单加解密工具
    方案:读取文件头,提取特定长度进行加密,加密后加这一部分写入源文件,解密可逆。 #! /usr/bin/env python #coding=utf-8 #Edit:Sandy #时间:2019年1月27日13:57:04 #功能:文件...
    99+
    2023-01-30
    简单 工具 加解密
  • C#实现简单工厂模式
    情景:有一个怪兽,HP是100,现在勇士有可以使用武器将其打败,有三种武器,木剑每次打击20血,铁剑每次50血,金刚剑每次100血,如果想要使用简单工厂方式,怎么设计? 一.啥是简单...
    99+
    2024-04-02
  • python实现简单的百度搜索
    #!/usr/bin/python # coding=utf-8 import urllib import urllib2 #实现百度关键字查询的小例子 #定义基础url url = "http://www.baidu.com/s" ...
    99+
    2023-01-31
    百度搜索 简单 python
  • OpenCV实现抠图工具的代码是什么
    今天就跟大家聊聊有关OpenCV实现抠图工具的代码是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。在计算机图像领域,我们经常需要做一些抠图的工作,将图像中的目标感兴趣区域提取出来...
    99+
    2023-06-26
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作