Project Virtual Painter

先上实验效果:

画笔实验结果

实现画笔操作,提取颜色目标,绘制轮廓,显示画笔三步骤。

1、提取颜色目标

要实现画笔操作,首先要提取颜色目标,也就是画笔,我们需要得到int hmin , smin, vmin,hmax, smax, vmax;六个值。我使用怡宝的瓶盖充当画笔,具体实现代码:

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>using namespace cv;
using namespace std;Color Detecction     /
int hmin = 0, smin = 110, vmin = 153;
int hmax = 19, smax = 240, vmax = 255;
/*黄色
int hmin = 15, smin = 119, vmin = 150;
int hmax = 38, smax = 221, vmax = 201;
//
*/
/*绿色
int hmin = 39, smin = 58, vmin = 27;
int hmax = 137, smax = 192, vmax = 116;
*/
VideoCapture cap(0);
Mat img,imgHSV, mask;;void main() {/*string path = "Learn-OpenCV-cpp-in-4-Hours-main\\Resources\\lambo.png";Mat img = imread(path),*/namedWindow("Trackbars", (640, 220));createTrackbar("Hue Min", "Trackbars", &hmin, 179);createTrackbar("Hue Max", "Trackbars", &hmax, 255);createTrackbar("Sat Min", "Trackbars", &smin, 255);createTrackbar("Sat Max", "Trackbars", &smax, 255);createTrackbar("Val Min", "Trackbars", &vmin, 255);createTrackbar("Val Max", "Trackbars", &vmax, 255);while (true) {cap.read(img);cvtColor(img, imgHSV, COLOR_BGR2HSV);Scalar lower(hmin, smin, vmin);Scalar upper(hmax, smax, vmax);inRange(imgHSV, lower, upper, mask);imshow("Image", img);imshow("imgHSV", imgHSV);imshow("imgMask", mask);waitKey(1);}destroyAllWindows();
}

利用createTrackbar函数创建调节的六个滑动按钮,以及inrange函数实现对掩膜的提取。

实验效果:

获取颜色值之后就是定位颜色、生成掩膜、绘制轮廓,并且返回轮廓中心点,中心点就是绘画笔的起点。实现代码:

vector<vector<int>> findColor(Mat img) {Mat imgHSV,mask;cvtColor(img, imgHSV, COLOR_BGR2HSV);for (int i = 0;i < myColors.size();i++) {Scalar lower(myColors[i][0], myColors[i][1], myColors[i][2]);Scalar upper(myColors[i][3], myColors[i][4], myColors[i][5]);inRange(imgHSV, lower, upper, mask);//imshow(str[i], mask);Point myPoint = getContours(mask);if (myPoint.x!= 0 && myPoint.y != 0) {Newpoint.push_back({ myPoint.x,myPoint.y,i });}}return Newpoint;
}

2、绘制轮廓

主要利用函数findContours(Dil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

contours定义为“vector<vector<Point>> contours”,是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;


    /*  hierarchy包含4个值的数组:[Next, Previous, First Child, Parent]
        Next:与当前轮廓处于同一层级的下一条轮廓
        举例来说,前面图中跟0处于同一层级的下一条轮廓是1,所以Next = 1;同理,对轮廓1来说,Next = 2;那么对于轮廓2呢?没有与它同一层级的下一条轮廓了,此时Next = -1。
        Previous:与当前轮廓处于同一层级的上一条轮廓
        跟前面一样,对于轮廓1来说,Previous = 0;对于轮廓2,Previous = 1;对于轮廓2a,没有上一条轮廓了,所以Previous = -1。
        First Child:当前轮廓的第一条子轮廓
        比如对于轮廓2,第一条子轮廓就是轮廓2a,所以First Child = 2a;对轮廓3,First Child = 3a。
        Parent:当前轮廓的父轮廓
        比如2a的父轮廓是2,Parent = 2;轮廓2没有父轮廓,所以Parent = -1。*/
    //RETR_EXTERNAL
    //这种方式只寻找最高层级的轮廓,也就是只寻找最外层轮廓:
    //CV_CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留;

具体实现代码:

Point getContours(Mat Dil) {vector<vector<Point>> contours;vector<Vec4i> hierarchy;//contours定义为“vector<vector<Point>> contours”,是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;/*  hierarchy包含4个值的数组:[Next, Previous, First Child, Parent]Next:与当前轮廓处于同一层级的下一条轮廓举例来说,前面图中跟0处于同一层级的下一条轮廓是1,所以Next = 1;同理,对轮廓1来说,Next = 2;那么对于轮廓2呢?没有与它同一层级的下一条轮廓了,此时Next = -1。Previous:与当前轮廓处于同一层级的上一条轮廓跟前面一样,对于轮廓1来说,Previous = 0;对于轮廓2,Previous = 1;对于轮廓2a,没有上一条轮廓了,所以Previous = -1。First Child:当前轮廓的第一条子轮廓比如对于轮廓2,第一条子轮廓就是轮廓2a,所以First Child = 2a;对轮廓3,First Child = 3a。Parent:当前轮廓的父轮廓比如2a的父轮廓是2,Parent = 2;轮廓2没有父轮廓,所以Parent = -1。*///RETR_EXTERNAL//这种方式只寻找最高层级的轮廓,也就是只寻找最外层轮廓://CV_CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留;findContours(Dil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//drawContours(img, contours, -1, Scalar(255, 0, 255),2);vector<vector<Point>>conPoly(contours.size());vector<Rect>boundRect(contours.size());Point myPoint(0, 0);//排除干扰for (int i = 0;i < contours.size();i++) {//计算轮廓面积 int area = contourArea(contours[i]);string objectType;cout << area << endl;if (area > 50) {//arcLength(contours[i], true);计算轮廓周长  //InputArray类型的curve,输入的向量,二维点(轮廓顶点),可以为std::vector或Mat类型。//bool类型的closed,用于指示曲线是否封闭的标识符,一般设置为true。float peri = arcLength(contours[i], true);对图像轮廓点进行多边形拟合approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);cout << conPoly[i].size() << endl;表示返回矩形边界左上角顶点的坐标值及矩形边界的宽和高boundRect[i] = boundingRect(conPoly[i]);myPoint.x = boundRect[i].x + boundRect[i].width / 2;myPoint.y = boundRect[i].y + boundRect[i].height/ 2;//绘制轮廓drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);//绘制矩形框rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);}}return myPoint;
}

 3、显示画笔

void drawOnCanvas(vector<vector<int>> Newpoint, vector<Scalar>myColorValues){for (int i = 0; i < Newpoint.size();i++) {circle(img, Point(Newpoint[i][0], Newpoint[i][1]), 10, myColorValues[Newpoint[i][2]],FILLED);}
}

全部实现代码:

#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <iostream>using namespace cv;
using namespace std;/    Project 1     //
Mat img;
vector<vector<int>> Newpoint;Point getContours(Mat Dil) {vector<vector<Point>> contours;vector<Vec4i> hierarchy;//contours定义为“vector<vector<Point>> contours”,是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;/*  hierarchy包含4个值的数组:[Next, Previous, First Child, Parent]Next:与当前轮廓处于同一层级的下一条轮廓举例来说,前面图中跟0处于同一层级的下一条轮廓是1,所以Next = 1;同理,对轮廓1来说,Next = 2;那么对于轮廓2呢?没有与它同一层级的下一条轮廓了,此时Next = -1。Previous:与当前轮廓处于同一层级的上一条轮廓跟前面一样,对于轮廓1来说,Previous = 0;对于轮廓2,Previous = 1;对于轮廓2a,没有上一条轮廓了,所以Previous = -1。First Child:当前轮廓的第一条子轮廓比如对于轮廓2,第一条子轮廓就是轮廓2a,所以First Child = 2a;对轮廓3,First Child = 3a。Parent:当前轮廓的父轮廓比如2a的父轮廓是2,Parent = 2;轮廓2没有父轮廓,所以Parent = -1。*///RETR_EXTERNAL//这种方式只寻找最高层级的轮廓,也就是只寻找最外层轮廓://CV_CHAIN_APPROX_SIMPLE:仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留;findContours(Dil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);//drawContours(img, contours, -1, Scalar(255, 0, 255),2);vector<vector<Point>>conPoly(contours.size());vector<Rect>boundRect(contours.size());Point myPoint(0, 0);//排除干扰for (int i = 0;i < contours.size();i++) {//计算轮廓面积 int area = contourArea(contours[i]);string objectType;cout << area << endl;if (area > 50) {//arcLength(contours[i], true);计算轮廓周长  //InputArray类型的curve,输入的向量,二维点(轮廓顶点),可以为std::vector或Mat类型。//bool类型的closed,用于指示曲线是否封闭的标识符,一般设置为true。float peri = arcLength(contours[i], true);对图像轮廓点进行多边形拟合approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);cout << conPoly[i].size() << endl;表示返回矩形边界左上角顶点的坐标值及矩形边界的宽和高boundRect[i] = boundingRect(conPoly[i]);myPoint.x = boundRect[i].x + boundRect[i].width / 2;myPoint.y = boundRect[i].y + boundRect[i].height/ 2;//绘制轮廓drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);//绘制矩形框rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);}}return myPoint;
}//smin,vmin,vmin,hmax,smax,vmax
vector<vector<int>>myColors{{15,119,150,38,221,201},//yello{39,58,27,137,192,116}};//greevector<Scalar>myColorValues{{0,255,255},//黄色{0,255,0}};//绿色
vector<string>str{ "黄色","绿色"};vector<vector<int>> findColor(Mat img) {Mat imgHSV,mask;cvtColor(img, imgHSV, COLOR_BGR2HSV);for (int i = 0;i < myColors.size();i++) {Scalar lower(myColors[i][0], myColors[i][1], myColors[i][2]);Scalar upper(myColors[i][3], myColors[i][4], myColors[i][5]);inRange(imgHSV, lower, upper, mask);//imshow(str[i], mask);Point myPoint = getContours(mask);if (myPoint.x!= 0 && myPoint.y != 0) {Newpoint.push_back({ myPoint.x,myPoint.y,i });}}return Newpoint;
}void drawOnCanvas(vector<vector<int>> Newpoint, vector<Scalar>myColorValues){for (int i = 0; i < Newpoint.size();i++) {circle(img, Point(Newpoint[i][0], Newpoint[i][1]), 10, myColorValues[Newpoint[i][2]],FILLED);}
}void main() {VideoCapture cap(0);while (true) {cap.read(img);Newpoint = findColor(img);drawOnCanvas(Newpoint,myColorValues);imshow("Image", img);waitKey(1);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/671909.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cmake进阶:变量的作用域说明三(从函数作用域方面)

一. 简介 前一篇文章从函数作用域方面学习了 变量的作用域。文章如下&#xff1a; cmake进阶&#xff1a;变量的作用域说明一&#xff08;从函数作用域方面&#xff09;-CSDN博客cmake进阶&#xff1a;变量的作用域说明二&#xff08;从函数作用域方面&#xff09;-CSDN博客…

VTK —— 三、简单操作 - 示例3 - 将点投影到平面上(附完整源码)

代码效果 本代码编译运行均在如下链接文章生成的库执行成功&#xff0c;若无VTK库则请先参考如下链接编译vtk源码&#xff1a; VTK —— 一、Windows10下编译VTK源码&#xff0c;并用Vs2017代码测试&#xff08;附编译流程、附编译好的库、vtk测试源码&#xff09; 教程描述 本…

leetcode-没有重复项的全排列-97

题目要求 思路 1.递归&#xff0c;如果num和n的元素个数一样就可以插入res中了&#xff0c;这个作为递归的结束条件 2.因为这个题是属于排列&#xff0c;并非组合&#xff0c;两者的区别是排列需要把之前插入的元素在回退会去&#xff0c;而组合不需要&#xff0c;因此会存在一…

【操作指南】银河麒麟高级服务器操作系统内核升级——基于4.19.90-17升级

1. 升级清单 升级包及依赖包清单如下。 kernel ARM架构 kernel-core-4.19.90-23.18.v2101.ky10.aarch64.rpm kernel-modules-4.19.90-23.18.v2101.ky10.aarch64.rpm kernel-4.19.90-23.18.v2101.ky10.aarch64.rpm kernel-modules-extra-4.19.90-23.18.v2101.ky10.aarch64.r…

【docker】常用的把springboot打包为docker镜像的maven插件

Spring Boot Maven Plugin: Spring Boot 自带的 Maven 插件 (spring-boot-maven-plugin) 支持直接生成 Docker 镜像。通过配置&#xff0c;可以在 Maven 构建过程中自动构建 Docker 镜像&#xff0c;而无需单独编写 Dockerfile。这种方法简化了将应用打包为 Docker 镜像的过程。…

设备树与/sys/bus/platform/devices与/sys/devices目录关系

设备树与sys/bus/platform/devices sysfs文件系统中/sys/bus/platform/devices下的设备是由设备树生成&#xff0c; 根节点下有compatible的子节点都会在/bus/platform/devices生成节点 总线 I2C、SPI 等控制器会在/bus/platform/devices生成节点 总线 I2C、SPI 节点下的子节点…

权益商城系统源码,支持多种支付方式

权益商城系统源码&#xff0c;支持多种支付方式&#xff0c;后台商品管理&#xff0c;订单管理&#xff0c;串货管理&#xff0c;分站管理&#xff0c; 会员列表&#xff0c;分销日志&#xff0c;应用配置。 上传到服务器&#xff0c;修改数据库信息&#xff0c;导入数据库&a…

LeetCode:滑动窗口最大值

文章收录于LeetCode专栏 LeetCode地址 滑动窗口最大值 题目 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。   返回 滑动窗口中的最大值 。   示例 1…

秋招后端开发面试题 - JVM运行时数据区

目录 运行时数据区前言面试题JVM 内存区域 / 运行时数据区&#xff1f;说一下 JDK1.6、1.7、1.8 内存区域的变化&#xff1f;为什么使用元空间替代永久代作为方法区的实现&#xff1f;Java 堆的内存分区了解吗&#xff1f;运行时常量池&#xff1f;字符串常量池了解吗&#xff…

【Unity】如何获得TMP Button下的text内容

【背景】 unity项目中使用了TMP命名空间的Button UI组件。脚本中需要获得Button下Text的内容,但是发现用TextMeshPro仍然无法获得button下的text对象。 【分析】 Hierarchy结构上看明确Button下是有Text组件的: 括号里是TMP,所以理论上用TextMeshPro类型去FindComponent…

限量背包问题

问题描述 限量背包问题&#xff1a;从m个物品中挑选出最多v个物品放入容量为n的背包。 问题分析 限量背包问题&#xff0c;可以用来解决许多问题&#xff0c;例如要求从n个物品中挑选出最多v个物品放入容量为m的背包使得背包最后的价值最大&#xff0c;或者总共有多少种放法…

这是用VS写的一个tcp客户端和服务端的demo

服务端&#xff1a; 客户端&#xff1a; 其实这里面的核心代码就两行。 客户端的核心代码&#xff1a; //套接字连接服务端 m_tcpSocket->connectToHost(_ip,_port);//通过套接字发送数据m_tcpSocket->write(ui.textEditSend->toPlainText().toUtf8());//如果收到信…