OpenCV 笔记(3):基本图形的绘制

news2024/9/28 19:19:37

Part11.  绘制简单的图形

绘图功能是 OpenCV 最基础的功能,OpenCV 提供了基础的绘制函数,用于帮助我们绘制一些基本的图形。通过这些函数的组合,我们也可以做一些高级的应用。

11.1 绘制点和圆

OpenCV 的绘制函数相对简单,而且很多参数很类似,所以介绍第一个函数时会详细地介绍各个参数的含义,后面就不做特别详细的介绍了。

我们先来看点和圆的绘制:

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat image = Mat::zeros(Size(800, 800), CV_8UC3);
    image.setTo(255);// 设置屏幕为白色

    Point p1(100, 100);
    Point p2(200, 200);
    Point p3(300, 300);
    Point p4(400, 400);
    Point p5(500, 500);
    Point p6(600, 600);
    Point p7(700, 700);

    circle(image, p1, 4, Scalar(0, 0, 255), -1);  // 画半径为4的圆(画点)

    circle(image, p2, 60, Scalar(255, 0, 0), 2);  // 画半径为60的圆

    circle(image, p3, 60, Scalar(0, 255, 0), -1);

    circle(image, p4, 60, Scalar(255, 255, 0), 5);

    circle(image, p5, 60, Scalar(255, 0, 255), -1);

    circle(image, p6, 60, Scalar(0, 255, 255), 2);

    circle(image, p7, 60, Scalar(0, 0, 0), -1);

    imshow("src", image);

    waitKey(0);
    return 0;
}
4e9526e0bbc5e2fcac4d19e6251d595e.jpeg
绘制点和圆.png

我们主要使用 circle() 函数来绘制点和圆。

CV_EXPORTS_W void circle(InputOutputArray img, Point center, int radius,
                       const Scalar& color, int thickness = 1,
                       int lineType = LINE_8, int shift = 0);

其各个参数的含义:

第一个参数 img:输入的源图像。 第二个参数 center:圆心的坐标。 第三个参数 radius:圆的半径。 第四个参数 color:圆形的颜色。 第五个参数 thickness:如果是正数,表示组成圆的线条的粗细程度。如果是负数,表示圆被填充。 第六个参数 lineType:线条的类型。OpenCV 提供了三种类型的线条,它们都是 LineTypes 枚举类型。

  • LINE_4 :4,表示四连接线。

  • LINE_8 :8,表示八连接线。

  • LINE_AA :16,表示抗锯齿线。使用它会产生更好的绘图质量,图像看起来会非常平滑,但是绘制速度较慢。

第七个参数 shift:圆心坐标点和半径值的小数点位数。

这里很多的参数,在本文后续的函数中都会用到。

21.2 绘制直线

直线跟圆的区别是,直线需要2个点来确定位置。下面是绘制直线的例子:

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat image = Mat::zeros(Size(800, 800), CV_8UC3);
    image.setTo(255);// 设置屏幕为白色

    Point p1(100, 100);
    Point p2(700, 700);
    Point p3(700, 100);
    Point p4(100, 700);

    line(image, p1, p2, Scalar(0, 0, 255), 2);
    line(image, p3, p4, Scalar(255, 0, 0), 2);

    imshow("src", image);

    waitKey(0);
    return 0;
}
7974271f7f1bb52906c40c3c76d886d2.jpeg
绘制直线.png

31.3 绘制矩形

矩形有两种绘制方式,一种是定义好矩形的左上角点位置和矩形长宽,然后在图像上绘制出来;另一种是通过确定矩形的左上角点右下角点来确定矩形的位置,然后在图像上绘制出来。

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat image = Mat::zeros(Size(800, 800), CV_8UC3);
    image.setTo(255);// 设置屏幕为白色

    Rect rect(150, 150, 120, 200);
    rectangle(image, rect, Scalar(0, 0, 255), 4);

    rectangle(image,Point(200,400),      //两个对角点
              Point(600,600),
              Scalar(255,0,0),
              -);

    imshow("src", image);

    waitKey(0);
    return 0;
}
9c763366baf96f8fa7d1d96146bcbfe6.jpeg
绘制矩形.png

41.4 绘制椭圆

椭圆的绘制稍微复杂一点,除了椭圆的中心位置以外,还需要确定椭圆的旋转角度、横轴长、纵轴长,这样才能绘制出椭圆。

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat image = Mat::zeros(Size(800, 800), CV_8UC3);
    image.setTo(255);// 设置屏幕为白色

    Point p1(100, 100);
    Point p2(200, 200);
    Point p3(300, 300);
    Point p4(400, 400);
    Point p5(600, 600);

    ellipse(image,p1,Size(60, 30),30,0,360,Scalar(255, 255, 0),4);

    ellipse(image,p2,Size(30, 60),0,0,360,Scalar(255, 0, 0),-1);

    ellipse(image,p3,Size(60, 30),120,0,360,Scalar(0, 255, 0),4);

    ellipse(image,p4,Size(100, 100),0,0,360,Scalar(0, 0, 255),-1);

    ellipse(image,p5,Size(120, 60),0,0,360,Scalar(255, 0, 255),4);

    imshow("src", image);

    waitKey(0);
    return 0;
}
c89b2f59ff16c91cf1cb525b95dc92a4.jpeg
绘制椭圆.png

绘制椭圆的 ellipse() 函数的定义:

CV_EXPORTS_W void ellipse(InputOutputArray img, Point center, Size axes,
                        double angle, double startAngle, double endAngle,
                        const Scalar& color, int thickness = 1,
                        int lineType = LINE_8, int shift = 0);

其中, 第三个参数 axes: Size 的两个参数分别是横轴的长度、纵轴的长度。当横轴和纵轴相等时,那就表示是圆形。 第四个参数 angle:椭圆旋转角度。 第五个参数 startAngle:从主轴顺时针方向测量的椭圆弧的起点。 第六个参数 endAngle:从主轴顺时针方向测量的椭圆弧的终点。当 startAngle 和 endAngle 的值为 0、360 才会绘制完整的椭圆。

51.5 绘制多边形

多面体相对于椭圆更加复杂一些,多面体的绘制本身也有两种函数可以实现。

polylines() 函数根据点集绘制多条相连的线段用以组成多面体,fillPoly() 函数绘制具有填充效果的多面体。因此, thickness 参数是否为负数无法对多面体的填充起作用。

#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat image = Mat::zeros(Size(800, 800), CV_8UC3);
    image.setTo(255);// 设置屏幕为白色

    Point p1(100, 100);
    Point p2(350, 100);
    Point p3(450, 280);
    Point p4(320, 450);
    Point p5(100, 400);

    std::vector<Point> pts;
    pts.push_back(p1);
    pts.push_back(p2);
    pts.push_back(p3);
    pts.push_back(p4);
    pts.push_back(p5);

    polylines(image, pts, true, Scalar(255, 0, 255), 4);

    Point p6(500, 500);
    Point p7(720, 650);
    Point p8(650, 780);
    Point p9(550, 700);
    Point p10(300, 700);

    pts.clear();
    pts.push_back(p6);
    pts.push_back(p7);
    pts.push_back(p8);
    pts.push_back(p9);
    pts.push_back(p10);

    fillPoly(image, pts, Scalar(0, 255, 255));

    imshow("src", image);

    waitKey(0);
    return 0;
}
25d352983006bcf2904933cb741d8dc1.jpeg
绘制多边形.png

简单介绍一下 polylines() 函数,另一个 fillPoly() 函数很类似。

CV_EXPORTS_W void polylines(InputOutputArray img, InputArrayOfArrays pts,
                            bool isClosed, const Scalar& color,
                            int thickness = 1, int lineType = LINE_8, int shift = 0 );

第二个参数 pts: 输入多边形的点的集合。 第三个参数 isClosed:是否把绘制的多条线段首尾相连,如果要绘制成多边形这个参数很重要,需要设置成 true。

61.6 图像中添加文字

OpenCV 提供了在原图上添加文字的 putText() 函数,支持字体、字号的设置。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

int main(int argc,char *argv[])
{
    Mat image = Mat::zeros(Size(800, 800), CV_8UC3);
    image.setTo(255);// 设置屏幕为白色

    string text = "Hello OpenCV!";

    putText(image, text, Point(0,100), FONT_HERSHEY_PLAIN, 2, cv::Scalar(0, 0, 255), 2);
    putText(image, text, Point(100,200), FONT_HERSHEY_PLAIN, 4, cv::Scalar(255, 0, 255), 4);

    //设置绘制文本的相关参数
    int fontFace = cv::FONT_HERSHEY_SIMPLEX;
    double fontScale = 2;
    int thickness = 8;
    int baseline;

    // 通过 getTextSize() 函数先获取待绘制文本的大小
    Size textSize = getTextSize(text, fontFace, fontScale, thickness, &baseline);

    // 计算出文本绘制到图片居中的位置
    Point point;
    point.x = image.cols / 2 - textSize.width / 2;
    point.y = image.rows / 2 + textSize.height / 2;

    putText(image, text, point, fontFace, fontScale, cv::Scalar(255, 255, 0), thickness);

    imshow("src", image);

    waitKey(0);
    return 0;
}
94efb18cba862eefd94a65ce3d2be053.jpeg
添加文字.png

Part22. 轮廓入门和绘制轮廓

72.1 轮廓入门

轮廓机器视觉的常用概念。它是由一系列相连的点组成的曲线,具有相同的颜色或灰度。轮廓常用于形状分析、物体检测、识别等任务。

一般情况下,为了得到精准的轮廓需要先对图像进行二值化处理,例如使用阈值分割或者 Canny 边缘检测等方式得到二值图像。然后,对二值图像进行轮廓发现和轮廓分析。

轮廓发现是利用 findContours() 函数检测图像中的对象边界,将每一个轮廓以点向量方式存储。因此,可以得到一个图像的拓扑信息,包含了一个轮廓的后一个轮廓、前一个轮廓、父轮廓和内嵌轮廓的索引编号。

在获取图像轮廓之后,我们就可以通过轮廓的属性(例如:轮廓的面积、质心、周长、几何矩、中心矩等等)来分析和筛选轮廓。轮廓具有很多属性和性质,我们会在后面的文章详细地介绍更多的内容,本文只是作为简单的入门介绍。

下面的例子,展示了获取手机的轮廓图,并获取其最小外接矩形以及截取 roi:

#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

bool ascendSort(vector<Point> a,vector<Point> b)
{
    return contourArea(a) > contourArea(b);
}

int main(int argc,char *argv[])
{
    string fileName = ...;
    Mat image = imread(fileName);
    if (image.empty()) {
        return -1;
    }

    imshow("src",image);

    Mat gray;
    cvtColor(image,gray,COLOR_BGR2GRAY);
    Mat thresh;
    threshold(gray, thresh, 0, 255, THRESH_BINARY | THRESH_OTSU);

    // 定义变量轮廓
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;

    findContours(thresh, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    sort(contours.begin(), contours.end(), ascendSort);//ascending sort

    RotatedRect rrt = minAreaRect(contours[0]);
    Rect bbox = rrt.boundingRect();

    Mat roi;
    try {
        roi = image(bbox);
    } catch (...) {
        return -1;
    }

    imshow("roi",roi);

    waitKey(0);

    return 0;
}
88217fc9318408c0819fcaae8fdf52a2.jpeg
查找ROI.png

上述代码,首先将原图转换成灰度图像,再进行阈值分割变成二值图像。然后,对轮廓进行查找并按照轮廓面积的大小进行排序。最后,对最大的轮廓获取其最小外接矩形以及截取这个最小外接矩形作为 roi,并将其展示。

在这段代码中,有些函数的作用和解释会在以后的小节中详细介绍。本文只详细解释如何进行轮廓发现和查找,主要使用的是 findContours() 函数。它包含很多参数,我们有必要简介绍一下各个参数的含义。

第一个参数 image: 输入的源图像。一个 CV_8UC1 的单通道图像。 第二个参数 contours: 输出轮廓图像。每个轮廓都存储为点向量 std::vector< cv::Point >,由多个轮廓组成输出的全部轮廓 std::vector<std::vector < cv::Point >>。 第三个参数 hierarchy:输出各个轮廓的继承关系。是 std::vector < cv::Vec4i > 类型的向量,长度跟 contours 的长度一致,每个元素和 contours 的元素对应,包含了有关图像拓扑的信息。 第四个参数 mode:轮廓检测的模式。包括以下四种:

  • RETR_EXTERNAL:只检测外轮廓,忽略轮廓内部的洞。

  • RETR_LIST:检测所有的轮廓,但不建立继承(包含)关系。

  • RETR_TREE:检测所有的轮廓,并且建立所有的继承(包含)关系。

  • RETR_CCOMP:检测所有轮廓,但是仅仅建立两层包含关系。

  • RETR_FLOODFILL:洪水填充法。采用这种模式时,输入的源图像也可以是 32 位的整型图像(CV_32SC1)。

第五个参数 method:每个轮廓的编码信息。包括以下四种:

  • CHAIN_APPROX_NONE:把轮廓上所有的点存储。

  • CHAIN_APPROX_SIMPLE:只存储轮廓上的拐点。

  • CHAIN_APPROX_TC89_L1:使用 teh-Chinl chain 近似算法。

  • CHAIN_APPROX_TC89_KCOS 使用 teh-Chinl chain 近似算法。

第六个参数 offset:每个轮廓点移动的偏移量。表示所有的轮廓信息相对于原始图像的偏移量,它是一个可选参数,cv::Point()类型。

82.2 绘制轮廓

在上述的代码找到了轮廓之后,绘制轮廓就变得很简单了。我们使用 drawContours() 函数就可以绘制轮廓。

drawContours(image,contours,0,Scalar(0,0,255),8);

imshow("contours",image);

再结合 imshow() 函数,可以直接在原图上展示绘制出来的手机轮廓。51beb6b77f66eb0abb8687c9f41ee5fa.jpeg

drawContours() 函数的参数就不一一解释了,我们只解释2个参数的含义。 第二个参数 contours: 输入全部的轮廓图像。 第三个参数 contourIdx:轮廓索引号,从 0 开始。-1 表示绘制所有轮廓。

通过这个函数,我们学会了绘制轮廓。在调试代码的时候,我经常会在原图上绘制一下查找到的相关轮廓,看看查找的内容是否准确。

Part33.  总结

本文主要分成两个部分。第一部分介绍了 OpenCV 基本的绘制函数以及使用,它们的使用比较简单只要明白每个函数中各个参数的含义即可。如果将这些函数组合起来使用,也可以做一些相对高级的应用。

第二部分介绍了轮廓的入门知识,主要是轮廓发现和轮廓绘制。轮廓是图像处理的核心内容之一,它包含了很多重要的信息和性质,我们会在后面的文章中重点学习。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Unity]将所有 TGA、TIFF、PSD 和 BMP(可自定义)纹理转换为 PNG,以减小项目大小,而不会在 Unity 中造成任何质量损失

如何使用 只需在“项目”窗口中创建一个名为“编辑器”的文件夹&#xff0c;然后在其中添加此脚本即可。然后&#xff0c;打开窗口-Convert Textures to PNG&#xff0c;配置参数并点击“Convert to PNG&#xff01; ”。 就我而言&#xff0c;它已将某些 3D 资源的总文件大小…

C语言汇总

汇总一&#xff08;linux环境&#xff09; /bin &#xff1a;bin是二进制&#xff08;binary&#xff09;英文缩写。 /boot&#xff1a;存放的都是系统启动时要用到的程序。 /dev&#xff1a;包含了所有Linux系统中使用的外部设备。 /etc&#xff1a;存放了系统管理时要用到的…

uniapp中 background-image 设置背景图片不展示问题

有问题 <view class"file-picker__box jsz" tap"jszxszUpload(jsz)"></view>.jsz {background-image: url(../../static/example_drive.png); }解决1 <view class"file-picker__box jsz" :style"{ background-image: url(…

Android APP 隐藏系统软键盘的方法

1.场景描述&#xff1a; 1) APP项目中经常会开发自定义软键盘&#xff1b;同时在使用EditText时&#xff0c;也会常常遇到自动弹出系统自带的软键盘&#xff0c;与自定义的软键盘产生冲突的情况&#xff1b;此时需要禁止EditText自动弹出系统软键盘&#xff0c;从而使自定义的…

汽车电子专有名词与相应技术

1.EEA &#xff08;Electronic & Electrical Architecture 电子电气架构&#xff09; EEA在宏观上概括为物理架构与逻辑架构的结合&#xff0c;微观上通过众多电子元器件的协同配合&#xff0c;或集成式或分布式的系统级电子电气架构&#xff0c;具体详见专栏 新能源汽车电…

volatile 关键字有什么用?它的实现原理是什么?

volatile volatile是Java中的一个关键字&#xff0c;用于修饰变量&#xff0c;表示该变量是“易变的”&#xff08;Volatile&#xff09;。 volatile 关键字有两个作用&#xff1a; 可以保证在多线程环境下共享变量的可见性。 通过增加内存屏障防止多个指令之间的重排序。 可见…

【图结构从入门到应用】图的表示和遍历,图搜索算法详解与示例

1图的概念 图是一种非常常见的数据结构&#xff0c;用于表示对象之间的关系。在计算机科学中&#xff0c;有许多不同的图类型&#xff0c;包括有向图&#xff08;Directed Graph&#xff09;和无向图&#xff08;Undirected Graph&#xff09;。图通常由节点&#xff08;顶点&a…

vscode json文件添加注释报错

在vscode中创建json文件&#xff0c;想要注释一波时&#xff0c;发现报了个错&#xff1a;Comments are not permitted in JSON. (521)&#xff0c;意思是JSON中不允许注释 以下为解决方法&#xff1a; 在vscode的右下角中找到这个&#xff0c;点击 在出现的弹窗中输入json wit…

Python 自动化(十五)请求和响应

准备工作 将不同day下的代码分目录管理&#xff0c;方便后续复习查阅 (testenv) [rootlocalhost projects]# ls mysite1 (testenv) [rootlocalhost projects]# mkdir day01 day02 (testenv) [rootlocalhost projects]# cp -rf mysite1/ day01/ (testenv) [rootlocalhost proj…

vue路径中“@/“代表什么

举例&#xff1a; <img src"/../static/imgNew/adv/tupian.jpg"/>其中&#xff0c;/是webpack设置的路径别名&#xff0c;代表什么路径&#xff0c;要看webpack的build文件夹下webpack.base.conf.js里面对于是如何配置&#xff1a; 上图中代表src,上述代码就…

KDChart3.0编译过程-使用QT5.15及QT6.x编译

文章目录 参考原文一、下载KDChart源文件二、下载安装CMake三、编译Qt5.15.0 编译Qt6.x 编译使用Qt6.X编译的直接看这最快 四、使用测试方法一&#xff1a;测试方法二&#xff1a; 参考原文 记录我的KDChart3.0编译过程 系统&#xff1a;win11&#xff0c;Qt5.15 &#xff0c;编…

Android View拖拽/拖放DragAndDrop自定义View.DragShadowBuilder,Kotlin(2)

Android View拖拽/拖放DragAndDrop自定义View.DragShadowBuilder&#xff0c;Kotlin&#xff08;2&#xff09; import android.graphics.Canvas import android.graphics.Point import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.util…

关于前端如何下载后端接口返回content-type为application/octet-stream的文件

关于前端如何下载后端接口返回response-type为application/octet-stream的文件 问题描述 后端接口定义为直接返回一个文件&#xff0c;如果带认证信息可以直接通过浏览器url下载&#xff0c;但是接口需要传headers认证信息&#xff0c;url上又不支持传相关信息 解决 前端…

如何用 JMeter 编写性能测试脚本?

Apache JMeter 应该是应用最广泛的性能测试工具。怎么用 JMeter 编写性能测试脚本&#xff1f; 1. 编写 HTTP 性能测试脚本 STEP 1. 添加 HTTP 请求 img STEP 2. 了解配置信息 HTTP 请求各项信息说明&#xff08;以 JMeter 5.1 为例&#xff09;。 如下图所示&#xff1a;…

kibana监控

采取方式 Elastic Agent &#xff1a;更完善的功能 Metricbeat&#xff1a;轻量级指标收集&#xff08;采用&#xff09; 传统收集方法&#xff1a;使用内部导出器收集指标&#xff0c;已不建议 安装 metricbeat Download Metricbeat • Ship Metrics to Elasticsearch | E…

硬件安全与机器学习的结合

文章目录 1. A HT Detection and Diagnosis Method for Gate-level Netlists based on Machine Learning摘要Introduction 2. 基于多维结构特征的硬件木马检测技术摘要Instruction 3. A Hardware Trojan Detection and Diagnosis Method for Gate-Level Netlists Based on Diff…

第一章 系统工程概述|系统建模语言SysML实用指南学习

仅供个人学习记录 系统工程起因 期望当今系统能力较之前有显著提升。 竞争压力要求系统提升技术先进性&#xff1a;提高性能&#xff0c;同时降低成本及缩短交付周期 能力增长驱动需求增长&#xff0c;包括功能、互操作性、性能、可靠性提升与小型化等。 系统不再是孤立的&…

【软考系统架构设计师】2022年系统架构师综合知识真题及解析

本篇文章主要讲解2022年系统架构师综合知识真题及解析 【01】云计算服务体系结构如下图所示&#xff0c;图中①、②、③分别与SaaS、PaaS、Iaas相对应&#xff0c;图中①、②、③应为( )。 解析&#xff1a;答案选择B 从上到下&#xff0c;依次是应用层——平台层——基础设施…

【网络原理】| 应用层协议与传输层协议 (UDP)

&#x1f397;️ 主页&#xff1a;小夜时雨 &#x1f397;️ 专栏&#xff1a;javaEE初阶 &#x1f397;️ 乾坤未定&#xff0c;你我皆黑马 目录 一、应用层协议二、传输层协议&#xff08;UDP协议&#xff09; 一、应用层协议 应用层是和代码直接相关的一层&#xff0c;决定…

Spark集群中一个Worker启动失败的排错记录

文章目录 1 检查失败节点worker启动日志2 检查正常节点worker启动日志3 查看正常节点spark环境配置4 又出现新的ERROR4.1 报错解释4.2 报错解决思路4.3 端口报错解决操作 集群下电停机后再次启动时&#xff0c;发现其中一台节点的worker启动失败。 1 检查失败节点worker启动日…