C++视觉开发 二.OpenCV基础

目录

本章记录OpenCV开发中的基本操作语法

一.基础

1.读取图像

2.显示图像

3.保存图像

二.图像

1.像素处理

2.彩色图像

三.滤波

1.高斯滤波(Gaussian Blur)

功能: 高斯滤波是一种常用的线性平滑滤波器,用于降低图像噪声和细节。

2.中值滤波(Median Blur)

功能: 中值滤波是一种非线性滤波方法,用于去除图像中的椒盐噪声或者斑点噪声。

四.形态学

1.cv::getStructuringElement 核函数

2.膨胀(Dilation)

3.腐蚀(Erosion)

4.开运算(Opening)

5.闭运算(Closing)

6.通用形态学操作

五. 二值化、轮廓、文本绘制

1.cv::cvtColor 颜色转换

2.cv::threshold 二值化

3.cv::findContours 查找轮廓

4. cv::drawContours 绘制轮廓

5.cv::moments 计算矩

6.cv::putText 绘制文本

7.cv::contourArea() 函数 轮廓面积

六.实战-物体计数 



本章记录OpenCV开发中的基本操作语法

首先导入库

#include <opencv2/opencv.hpp>
#include <iostream>

一.基础

1.读取图像

cv::Mat img = cv::imread("lenacolor.png",flags);

语法:cv::Mat 图像名 = cv::imread(“图像名称”,flags);

flag标记值如下表:

cv::IMREAD_UNCHANGED保持原格式不变
cv::IMREAD_GRAYSCALE单通道灰度图像
cv::IMREAD_COLOR三通道BGR格式

2.显示图像

cv::imshow("one", img);

语法:cv::imshow("窗口名称", 图像名);

默认加入两个函数:

cv::waitKey(0); //暂停运行
cv::destroyAllWindows(); //按任意键释放窗口

方便查看图像和释放。

3.保存图像

cv::imwrite("result",img)

语法:cv::imwrite("目标文件完成路径", 图像名);

二.图像

1.像素处理

例子:

cv::Mat img = cv::imread("lena.bmp", cv::IMREAD_GRAYSCALE);for (int i = 10; i < 100; ++i) {for (int j = 80; j < 100; ++j) {img.at<uchar>(i, j) = 255;}}cv::imshow("after", img);cv::waitKey(0);cv::destroyAllWindows();

读取灰度图像,并处理其中的像素点,案例中为改为白色。

2.彩色图像

例子:

#include <opencv2/opencv.hpp>
#include <iostream>int main() {// 读取图像cv::Mat img = cv::imread("lenacolor.png");// 检查图像是否加载成功if (img.empty()) {std::cerr << "Error: Could not open or find the image 'lenacolor.png'" << std::endl;return -1;}// 显示原始图像cv::imshow("before", img);// 访问并打印指定像素值//cv::Vec3b 类型用于表示一个包含3个无符号字符(即一个像素的BGR值)的向量。std::cout << "访问img[0,0] = " << img.at<cv::Vec3b>(0, 0) << std::endl;std::cout << "访问img[0,0,0] = " << (int)img.at<cv::Vec3b>(0, 0)[0] << std::endl;std::cout << "访问img[0,0,1] = " << (int)img.at<cv::Vec3b>(0, 0)[1] << std::endl;std::cout << "访问img[0,0,2] = " << (int)img.at<cv::Vec3b>(0, 0)[2] << std::endl;std::cout << "访问img[50,0] = " << img.at<cv::Vec3b>(50, 0) << std::endl;std::cout << "访问img[100,0] = " << img.at<cv::Vec3b>(100, 0) << std::endl;// 区域1:白色for (int i = 0; i < 50; ++i) {for (int j = 0; j < 100; ++j) {img.at<cv::Vec3b>(i, j) = cv::Vec3b(255, 255, 255);}}// 区域2:灰色for (int i = 50; i < 100; ++i) {for (int j = 0; j < 100; ++j) {img.at<cv::Vec3b>(i, j) = cv::Vec3b(128, 128, 128);}}// 区域3:黑色for (int i = 100; i < 150; ++i) {for (int j = 0; j < 100; ++j) {img.at<cv::Vec3b>(i, j) = cv::Vec3b(0, 0, 0);}}// 区域4:红色for (int i = 150; i < 200; ++i) {for (int j = 0; j < 100; ++j) {img.at<cv::Vec3b>(i, j) = cv::Vec3b(0, 0, 255);}}// 显示修改后的图像cv::imshow("after", img);// 打印修改后的像素值std::cout << "修改后img[0,0] = " << img.at<cv::Vec3b>(0, 0) << std::endl;std::cout << "修改后img[0,0,0] = " << (int)img.at<cv::Vec3b>(0, 0)[0] << std::endl;std::cout << "修改后img[0,0,1] = " << (int)img.at<cv::Vec3b>(0, 0)[1] << std::endl;std::cout << "修改后img[0,0,2] = " << (int)img.at<cv::Vec3b>(0, 0)[2] << std::endl;std::cout << "修改后img[50,0] = " << img.at<cv::Vec3b>(50, 0) << std::endl;std::cout << "修改后img[100,0] = " << img.at<cv::Vec3b>(100, 0) << std::endl;// 等待按键cv::waitKey(0);// 销毁所有窗口cv::destroyAllWindows();return 0;
}

这段代码主要展示了通道问题,指的是图像的颜色通道处理。在OpenCV中,图像以BGR格式存储,即每个像素由三个无符号字符(8位)组成,分别表示蓝色(B)、绿色(G)和红色(R)的强度。通过cv::Vec3b类型的向量可以访问和修改每个像素的BGR值,例如使用img.at<cv::Vec3b>(i, j)[0]访问蓝色通道的值,img.at<cv::Vec3b>(i, j)[1]访问绿色通道的值,img.at<cv::Vec3b>(i, j)[2]访问红色通道的值。在这段代码中,通过操作这些通道的数值,实现了对图像不同区域颜色的修改,从而展示了如何处理和操作图像的颜色通道信息。

三.滤波

1.高斯滤波(Gaussian Blur)

功能: 高斯滤波是一种常用的线性平滑滤波器,用于降低图像噪声和细节。

函数语法:

cv::GaussianBlur(src, dst, ksize, sigmaX, sigmaY, borderType);
  • src: 输入图像,通常是cv::Mat类型。
  • dst: 输出图像,与输入图像有相同的尺寸和类型。
  • ksize: 高斯内核的大小。可以指定一个正奇数,例如cv::Size(3, 3),表示3x3的内核。宽度和高度可以不同,但它们都必须是正的和奇数。
  • sigmaX: X方向的高斯核标准差。0
  • sigmaY: Y方向的高斯核标准差,如果为0,则默认与sigmaX相同。
  • borderType: 边界模式,默认为cv::BORDER_DEFAULT

2.中值滤波(Median Blur)

功能: 中值滤波是一种非线性滤波方法,用于去除图像中的椒盐噪声或者斑点噪声。

函数语法:

cv::medianBlur(src, dst, ksize);
  • src: 输入图像,通常是cv::Mat类型。
  • dst: 输出图像,与输入图像有相同的尺寸和类型。
  • ksize: 中值滤波核的大小,必须是大于1的奇数,例如3、5、7等。

四.形态学

1.cv::getStructuringElement 核函数

功能:用于生成形态学操作中使用的结构元素(或称为内核、核)。形态学操作主要应用于二值图像处理,如腐蚀、膨胀、开运算、闭运算等。结构元素是形态学操作的核心,它定义了操作的形状和大小。

语法:

cv::getStructuringElement(int shape, cv::Size ksize);
参数含义
shape

结构元素的形状,可以是以下值之一:

cv::MORPH_RECT:矩形

cv::MORPH_ELLIPSE:椭圆

cv::MORPH_CROSS:十字形

ksize

结构元素的大小,类型为 cv::Size,表示宽度和高度。

例如: cv::Size(5, 5)

 返回值为cv::Mat 类型。

示例:生成不同形状的结构元素并应用于形态学操作

#include <opencv2/opencv.hpp>
#include <iostream>int main() {// 读取图像cv::Mat image = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);if (image.empty()) {std::cerr << "Error: Could not open or find the image." << std::endl;return -1;}// 生成矩形结构元素cv::Mat rectElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));// 生成椭圆形结构元素cv::Mat ellipseElement = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5));// 生成十字形结构元素cv::Mat crossElement = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(5, 5));// 应用膨胀操作cv::Mat dilatedRect, dilatedEllipse, dilatedCross;cv::dilate(image, dilatedRect, rectElement);cv::dilate(image, dilatedEllipse, ellipseElement);cv::dilate(image, dilatedCross, crossElement);// 显示结果cv::imshow("Original Image", image);cv::imshow("Dilated with Rect", dilatedRect);cv::imshow("Dilated with Ellipse", dilatedEllipse);cv::imshow("Dilated with Cross", dilatedCross);// 等待按键cv::waitKey(0);cv::destroyAllWindows();return 0;
}

2.膨胀(Dilation)

膨胀操作将图像中的物体边界膨胀或扩展。它会将前景物体的像素值设为周围区域内的最大像素值。在图像中,膨胀可以填充物体内的空洞或连接物体,增加物体的大小。

示例代码语法:

cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::dilate(src, dst, element);

3.腐蚀(Erosion)

腐蚀操作与膨胀相反,它会将前景物体的边界向内侵蚀。它会将前景物体的像素值设为周围区域内的最小像素值。在图像中,腐蚀可以去除物体边界附近的像素,减小物体的大小或分离物体。

示例代码语法:

cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::erode(src, dst, element);

 注意:如果需要用到膨胀或者腐蚀的迭代次数,需要用到第五个参数

示例:腐蚀4次膨胀3次

cv::Mat erosion;
cv::erode(binary, erosion, kernel, cv::Point(-1, -1), 4);
cv::Mat dilation;
cv::dilate(erosion, dilation, kernel, cv::Point(-1, -1), 3);

4.开运算(Opening)

开运算是先进行腐蚀操作,然后进行膨胀操作的组合操作。它被用来去除图像中的噪声。开运算能够平滑物体的边界,消除细小的噪声。

示例代码语法:

cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::morphologyEx(src, dst, cv::MORPH_OPEN, element);

5.闭运算(Closing)

闭运算是先进行膨胀操作,然后进行腐蚀操作的组合操作。它能够填充物体内部的小洞,连接物体。闭运算可以平滑物体的轮廓,连接相邻的物体。

示例代码语法:

cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
cv::morphologyEx(src, dst, cv::MORPH_CLOSE, element);

6.通用形态学操作

在OpenCV中,形态学操作通常结合使用cv::Mat类型的图像和cv::morphologyEx函数来实现。

函数语法:

cv::morphologyEx(src, dst, op, kernel, anchor, iterations, borderType, borderValue);
  • src: 输入图像,通常是cv::Mat类型。
  • dst: 输出图像,与输入图像有相同的尺寸和类型。
  • op: 形态学操作类型,可以是以下几种:
    • cv::MORPH_ERODE: 腐蚀
    • cv::MORPH_DILATE: 膨胀
    • cv::MORPH_OPEN: 开运算
    • cv::MORPH_CLOSE: 闭运算
    • 其他自定义的形态学操作类型。
  • kernel: 结构元素(内核)大小和形状,通常是通过cv::getStructuringElement函数创建的,例如:
    cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5))
    创建一个5x5的矩形结构元素。

常用上面几个就够了,下面几个可以了解一下:

  • anchor: 结构元素的锚点位置,默认为(-1, -1)表示结构元素的中心。
  • iterations: 形态学操作的重复次数,默认为1。
  • borderType: 边界模式,默认为cv::BORDER_CONSTANT
  • borderValue: 边界值,默认为cv::morphologyDefaultBorderValue()

示例:

#include <opencv2/opencv.hpp>
#include <iostream>int main() {cv::Mat img = cv::imread("shapes.jpg", cv::IMREAD_GRAYSCALE);if (img.empty()) {std::cerr << "Error: Could not open or find the image 'shapes.jpg'" << std::endl;return -1;}// 创建结构元素(内核)cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));// 进行膨胀操作cv::Mat dilated_img;cv::morphologyEx(img, dilated_img, cv::MORPH_DILATE, kernel);// 进行开运算(先腐蚀后膨胀)cv::Mat opened_img;cv::morphologyEx(img, opened_img, cv::MORPH_OPEN, kernel);// 显示原始图像和处理后的图像cv::imshow("Original Image", img);cv::imshow("Dilated Image", dilated_img);cv::imshow("Opened Image", opened_img);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

五. 二值化、轮廓、文本绘制

先展示完整案例:

#include <opencv2/opencv.hpp>
#include <iostream>int main() {// 读取图像cv::Mat o = cv::imread("cat3.jpg", 1);// 检查图像是否加载成功if (o.empty()) {std::cerr << "Error: Could not open or find the image 'cat3.jpg'" << std::endl;return -1;}// 显示原始图像cv::imshow("original", o);// 转换为灰度图像cv::Mat gray;cv::cvtColor(o, gray, cv::COLOR_BGR2GRAY);// 二值化cv::Mat binary;cv::threshold(gray, binary, 127, 255, cv::THRESH_BINARY);// 查找轮廓std::vector<std::vector<cv::Point>> contours;std::vector<cv::Vec4i> hierarchy;cv::findContours(binary, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);// 绘制第一个轮廓cv::drawContours(o, contours, 0, cv::Scalar(0, 0, 255), 3);// 计算矩cv::Moments m = cv::moments(contours[0]);double m00 = m.m00; // 非0像素值的和double m10 = m.m10; // 非0像素值*x轴坐标值的和double m01 = m.m01; // 非0像素值*y轴坐标值的和// 计算质心int cx = static_cast<int>(m10 / m00);int cy = static_cast<int>(m01 / m00);// 在质心位置绘制文本cv::putText(o, "cat", cv::Point(cx, cy), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 3);// 显示结果图像cv::imshow("result", o);// 等待按键cv::waitKey(0);// 销毁所有窗口cv::destroyAllWindows();return 0;
}

 结果如图所示:

里面的重要函数:

1.cv::cvtColor 颜色转换

功能:将图像从一种颜色空间转换为另一种颜色空间。

语法:

cv::cvtColor(const cv::Mat& src, cv::Mat& dst, int code, int dstCn = 0)
参数含义
src输入图像
dst输出图像
code

颜色转换代码(常用 cv::COLOR_BGR2GRAY)

例如 cv::COLOR_BGR2GRAY 表示从BGR转换为灰度

dstCn目标图像的通道数(可选参数)

示例: 将彩色图像 o 转化为灰度图像 gray

cv::Mat gray;
cv::cvtColor(o, gray, cv::COLOR_BGR2GRAY);

2.cv::threshold 二值化

!!!!!!!重要!!!!!!!

功能:将灰度图像转换为二值图像。

语法

cv::threshold(const cv::Mat& src, cv::Mat& dst, double thresh, double maxval, int type)
参数含义
src输入图像(单通道,通常是灰度图像)
dst输出图像(与输入图像大小相同)
thresh阈值,用于比较像素值的基准值
maxval满足条件时分配给像素的最大值
type阈值类型,例如 cv::THRESH_BINARY

常用的type:     0纯黑,255纯白

  • cv::THRESH_BINARY:如果像素值大于阈值,则将其设置为 maxval,否则设置为0。
  • cv::THRESH_BINARY_INV:如果像素值大于阈值,则将其设置为0,否则设置为 maxval
  • cv::THRESH_TRUNC:如果像素值大于阈值,则将其设置为阈值,否则保持不变。
  • cv::THRESH_TOZERO:如果像素值大于阈值,则保持不变,否则设置为0。
  • cv::THRESH_TOZERO_INV:如果像素值大于阈值,则设置为0,否则保持不变。
  • cv::THRESH_BINARY_INV + cv::THRESH_OTSU:增加THRESH_OTSU方法,以自动确定最佳阈值

示例:将灰度图像 gray 转换为二值图像 binary,阈值为127,超过阈值的像素值设为255

cv::Mat binary;
cv::threshold(gray, binary, 127, 255, cv::THRESH_BINARY);
//自动最优阈值
cv::threshold(gray, binary,  0, 255, cv::THRESH_BINARY_INV+cv::THRESH_OTSU);

3.cv::findContours 查找轮廓

功能:查找图像中的轮廓。

语法:

cv::findContours(cv::Mat& image,std::vector<std::vector<cv::Point>>& contours,std::vector<cv::Vec4i>& hierarchy,int mode,int method
)
参数含义
image输入的单通道二值图像。此函数会修改输入图像,因此需要传递图像的副本。
contours输出的轮廓向量,每个轮廓是一个点的向量。
hierarchy输出的层次结构信息向量。
mode轮廓检索模式
method轮廓逼近方法

其中的mode和method:

  • mode(轮廓检索模式):

    • cv::RETR_EXTERNAL:只检索最外层的轮廓。
    • cv::RETR_LIST:检索所有的轮廓,不建立层次结构。
    • cv::RETR_CCOMP:检索所有的轮廓,并将它们组织成两层的层次结构。
    • cv::RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的完整层次结构。
  • method(轮廓逼近方法):

    • cv::CHAIN_APPROX_NONE:存储所有的轮廓点。
    • cv::CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角线段,仅保留它们的端点。
    • cv::CHAIN_APPROX_TC89_L1cv::CHAIN_APPROX_TC89_KCOS:使用Teh-Chin链逼近算法。

示例: 在二值图像中查找轮廓,并将结果存储在 contourshierarchy 中。

//二值化图像
cv::Mat binary;
cv::threshold(src, binary, 127, 255, cv::THRESH_BINARY);//查找轮廓
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(binary, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);

4. cv::drawContours 绘制轮廓

功能:在图像上绘制轮廓。

语法

cv::drawContours(cv::Mat& image,const std::vector<std::vector<cv::Point>>& contours,int contourIdx,const cv::Scalar& color,int thickness,int lineType,const std::vector<cv::Vec4i>& hierarchy = std::vector<cv::Vec4i>(),int maxLevel = INT_MAX,cv::Point offset = cv::Point()
)
参数含义
image目标图像,在该图像上绘制轮廓
contours

包含所有轮廓的向量,每个轮廓是一个点的向量。

使用 cv::findContours 函数从二值图像中提取出来,并存储在这个向量中。

contourIdx

轮廓的索引。

例如: -1表示绘制所有轮廓;0表示绘制第一个轮廓。

color

轮廓的颜色,用 cv::Scalar 定义。

例如: cv::Scalar(0, 0, 255) 表示红色。

thickness轮廓线的粗细,默认为 1。如果为负值,则填充轮廓。(以像素为单位)

以上为常用的参数,后面的几个为可选参数,通常不使用。

示例:(在上面的基础上)

cv::drawContours(o, contours, 0, cv::Scalar(0, 0, 255), 3);

5.cv::moments 计算矩

功能:计算给定轮廓或二值图像的空间矩。矩的计算是基于图像或轮廓中的像素值,通过数学公式来提取图像中的几何特征。

矩的具体定义和计算如下

1.零阶矩(m00): 表示图像中非零像素的总和,即图像中物体的面积。

其中,I(x,y)I(x,y)I(x,y)是图像中点(x,y)(x,y)(x,y)的像素值。

2.一阶矩(m10 和 m01): 分别表示图像中非零像素的x坐标和y坐标的加权和,用于计算质心。

3.质心(Centroid): 质心是图像中物体的重心位置,通过零阶矩和一阶矩计算得到。

语法:

cv::Moments m = cv::moments(const InputArray &array, bool binaryImage = false);
参数含义
array输入的二值图像或轮廓(可以是cv::Mat或者std::vector<cv::Point>
binaryImage

如果是二值图像,设为true;否则设为false

这个参数在处理二值图像时有助于提高计算效率。

示例:从cv::Moments对象中提取零阶矩和一阶矩,并计算质心坐标。

cv::Moments m = cv::moments(contours[0]);
double m00 = m.m00; // 非0像素值的和
double m10 = m.m10; // 非0像素值*x轴坐标值的和
double m01 = m.m01; // 非0像素值*y轴坐标值的和// 计算质心
int cx = static_cast<int>(m10 / m00);
int cy = static_cast<int>(m01 / m00);

6.cv::putText 绘制文本

功能:用于在图像上绘制指定的文本。

语法:

cv::putText(cv::Mat &img, const std::string &text, cv::Point org, int fontFace, double fontScale, cv::Scalar color, int thickness = 1, int lineType = 8, bool bottomLeftOrigin = false
);
img输入的图像,文本将绘制在这张图像上。
text要绘制的文本字符串
org文本起始点的坐标,用cv::Point表示,指定文本左下角的位置。
fontFace

字体类型,OpenCV支持多种字体类型。

例如cv::FONT_HERSHEY_SIMPLEXcv::FONT_HERSHEY_PLAIN等。

fontScale字体缩放系数,控制文本的大小。(设为1就行)
color

文本颜色,使用cv::Scalar表示,可以定义BGR颜色值。

例如cv::Scalar(0, 0, 255)表示红色。

thickness(可选参数)文本的线条粗细(可选),默认为1。
lineType

(可选参数)线条类型,默认为8,表示8连接线。

  其他选项包括4和cv::LINE_AA(抗锯齿线条)。

bottomLeftOrigin

(可选参数)布尔值。

如果为true:文本的原点将是图像的左下角。

否则:否则是左上角。默认为false

 示例:在图像的质心位置绘制文本"cat"

// 计算质心
int cx = static_cast<int>(m10 / m00);
int cy = static_cast<int>(m01 / m00);// 在质心位置绘制文本
cv::putText(o, "cat", cv::Point(cx, cy), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 3);

7.cv::contourArea() 函数 轮廓面积

功能:用于计算各轮廓的面积。

语法:

cv::contourArea(const InputArray &contour, bool oriented = false);
参数含义
contour输入的轮廓,是一个点的向量(std::vector<cv::Point>
oriented(可选参数)布尔值,如果为true,则返回有符号的面积,表示轮廓的方向(顺时针或逆时针)。默认值为false,返回绝对面积。

返回值:返回轮廓的面积(double类型)

完整示例:

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>using namespace cv;int main() {// 读取图像cv::Mat o = cv::imread("opencv.png", 1);if (o.empty()) {std::cerr << "Error: Could not open or find the image 'opencv.png'" << std::endl;return -1;}// 转换为灰度图像cv::Mat gray;cv::cvtColor(o, gray, cv::COLOR_BGR2GRAY);// 二值化cv::Mat binary;cv::threshold(gray, binary, 127, 255, cv::THRESH_BINARY);// 查找轮廓std::vector<std::vector<cv::Point>> contours;std::vector<cv::Vec4i> hierarchy;cv::findContours(binary, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);int n = contours.size();for (int i = 0; i < n; ++i) {double area = cv::contourArea(contours[i]);if (area > 1000) {// 绘制轮廓cv::drawContours(o, contours, i, cv::Scalar(0, 0, 255), 1);// 计算质心cv::Moments m = cv::moments(contours[i]);if (m.m00 != 0) { // 确保 m00 不为零int cx = static_cast<int>(m.m10 / m.m00);int cy = static_cast<int>(m.m01 / m.m00);// 在质心位置绘制文本std::string ii = std::to_string(i);cv::putText(o, ii, cv::Point(cx, cy), cv::FONT_HERSHEY_SCRIPT_SIMPLEX, 1, cv::Scalar(0, 0, 255));// 打印面积std::cout << "轮廓" << i << "的面积为" << area << std::endl;}}}// 显示结果图像cv::imshow("result", o);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

六.实战-物体计数 

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>using namespace std;int main() {// 读取图像cv::Mat o = cv::imread("count.jpg", 1);if (o.empty()) {cerr << "Error: Could not open or find the image." << endl;return -1;}// 转换为灰度图像cv::Mat gray;cv::cvtColor(o, gray, cv::COLOR_BGR2GRAY);// 二值化,使用 OTSU 方法自动确定阈值cv::Mat binary;cv::threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV + cv::THRESH_OTSU);// 生成结构元素cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5));// 腐蚀和膨胀操作cv::Mat erosion;cv::erode(binary, erosion, kernel, cv::Point(-1, -1), 4);cv::Mat dilation;cv::dilate(erosion, dilation, kernel, cv::Point(-1, -1), 3);// 高斯模糊cv::Mat gaussian;cv::GaussianBlur(dilation, gaussian, cv::Size(3, 3), 1);// 查找轮廓vector<vector<cv::Point>> contours;vector<cv::Vec4i> hierarchy;cv::findContours(gaussian, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);// 筛选符合要求的轮廓vector<vector<cv::Point>> contoursOK;for (size_t i = 0; i < contours.size(); ++i) {double area = cv::contourArea(contours[i]);if (area > 30) {contoursOK.push_back(contours[i]);}}// 绘制筛选后的轮廓cv::drawContours(o, contoursOK, -1, cv::Scalar(0, 255, 0), 1);// 标记质心for (size_t i = 0; i < contoursOK.size(); ++i) {cv::Moments m = cv::moments(contoursOK[i]);if (m.m00 != 0) {int cx = static_cast<int>(m.m10 / m.m00);int cy = static_cast<int>(m.m01 / m.m00);string ii = to_string(i + 1);cv::putText(o, ii, cv::Point(cx, cy), cv::FONT_HERSHEY_PLAIN, 1.5, cv::Scalar(0, 0, 255), 2);}}// 显示结果cv::imshow("result", o);cv::waitKey(0);cv::destroyAllWindows();return 0;
}

结果如图: 

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

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

相关文章

v-for中key的原理以及用法

在 Vue.js 中&#xff0c;v-for 指令用于基于源数据多次渲染元素或模板块。当使用 v-for 渲染列表时&#xff0c;为每个列表项提供一个唯一的 key 属性是非常重要的。key 的主要作用是帮助 Vue 跟踪每个节点的身份&#xff0c;从而重用和重新排序现有元素。 先来张原理图&#…

开机弹窗缺失OpenCL.dll如何解决?分享5种靠谱的解决方法

在电脑使用过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“开机提示找不到OpenCL.dll”。那么&#xff0c;这个错误提示到底是怎么回事呢&#xff1f;它又对电脑有什么影响&#xff1f;我们又该如何解决这个问题并预防OpenCL.dll再次丢失呢&#xff1…

基于Ollama Python的本地多模态大模型

0&#xff0c;背景 最近测试Ollama&#xff0c;发现之前直接下载开源模型在我电脑上都跑不动的模型&#xff0c;居然也能运行了&#xff08;AMD 7840HS核显/32GB内存&#xff09;&#xff0c;突发奇想那些多模态大模型能不能基于Python接口使用&#xff0c;所以决定尝试一下。…

网页用事件监听器播放声音

一、什么是监听器&#xff1a; 在前端页面中&#xff0c;事件监听器&#xff08;Event Listener&#xff09;是一种编程机制&#xff0c;它允许开发者指定当特定事件&#xff08;如用户点击按钮、鼠标悬停、页面加载完成等&#xff09;发生时执行特定的代码块。简而言之&#x…

服务器上VMWare Workstation虚拟机声卡支持

问题&#xff1a;联想服务器没有声卡&#xff0c;Windows 服务器安装了VMWare Workstation&#xff0c;里面的Windows 11虚拟机&#xff0c;我远程桌面上来&#xff0c;没有声卡&#xff0c;但是我想做 声音方面的测试就没办法。 解决办法&#xff1a; 服务器主机上安装虚拟机…

5.4符号三角形问题

#include<iostream> #include<stdio.h> using namespace std; int half; int ssum; int cnt0;//减号的个数 int n; int p[100][100]; int countt0; void BackTrack(int s) {if(cnt>half||s*(s-1)/2-cnt>half)return ;if(s>n){countt;return ;}for(int i0;…

RabbitMq教程【精细版一】

一、引言 模块之间的耦合度过高&#xff0c;导致一个模块宕机后&#xff0c;全部功能都不能用了&#xff0c;并且同步通讯的成本过高&#xff0c;用户体验差。 RabbitMQ引言 二、RabbitMQ介绍 MQ全称为Message Queue&#xff0c;消息队列是应用程序和应用程序之间的通信方法。…

在安装HDFS过程中遇见Permission denied

HDFS Shell命令权限不足问题解决 问题 想必有同学在实战Shell的时候&#xff0c;遇到了&#xff1a; Permission denied: userroot, accessWRITE, inode"/":hadoop:supergroup:drwxr-xr-x 这种类似的问题。 问题的原因就是没有权限&#xff0c;那么为什么呢&#…

功能强大的声音模拟合成软件Togu Audio Line TAL-Mod 1.9.7

Togu Audio Line TAL一个虚拟模拟合成器,具有卓越的声音和几乎无限的调制能力。其特殊的振荡器模型能够创建广泛的声音,从经典的单声道到丰富的立体声引线、效果器和焊盘。路由可以使用虚拟跳线电缆来完成。只需连接调制输出以达到调制的目的。之后,您可以调整调制强度。您不…

【SQLmap】常用命令

文章目录 实际使用案例常用命令基本命令数据库指纹识别用户信息用户权限数据库枚举数据导出密码哈希操作系统命令执行文件操作代理和网络参数指定保存恢复自动搜索注入智能模式等级设置自动注入WAF 绕过杂项帮助和支持 SQLmap 是一款开源的自动化 SQL 注入检测和利用工具&#…

你真的会udf提权???数据库权限到系统权限 内网学习 mysql的udf提权操作 ??msf你会用了吗???

我们在已经取得了数据库的账号密码过后&#xff0c;我们要进一步进行提取的操作&#xff0c;我们mysql有4钟提权的操作。 udf提权(最常用的)mof提权启动项提权反弹shell提权操作 怎么获取密码操作&#xff1a; 怎么获取密码&#xff0c;通过sql注入获取这个大家都应该知道了&a…

Python-数据分析组合可视化实例图【附完整源码】

数据分析组合可视化实例图 开篇&#xff1a;应女朋友的要求&#xff0c;于是写下了这篇详细的数据可视化代码及完整注释 一&#xff1a;柱状图、折线图横向组合网格布局 本段代码使用了pyecharts库来创建一个包含多个图表&#xff08;柱状图、折线图&#xff09;和网格布局的…

第一周:李宏毅机器学习笔记

第一周学习周报 摘要一、机器学习基础理论1. 什么是机器学习&#xff1f;2. 机器学习“寻找”的函数有哪些类型&#xff1f;3. 机器学习中机器如何“寻找”函数&#xff1f;三步走3.1 第一步&#xff1a;设定函数的未知量&#xff08;Function with Unknown Parameters&#xf…

Vue3 按钮根据屏幕宽度展示折叠按钮

文章目录 一、组件封装二、使用三、最终效果(参考)四、参考 一、组件封装 ButtonFold.vue 1、获取父组件的元素&#xff0c;根据元素创建动态插槽 2、插槽中插入父元素标签。默认效果和初始状态相同。 3、当屏幕宽度缩小时&#xff0c;部分按钮通过 dropdown 的方式展示出来&a…

springboot使用测试类报空指针异常

检查了Service注解&#xff0c;还有Autowired注解&#xff0c;还有其他注解&#xff0c;后面放心没能解决问题&#xff0c;最后使用RunWith(SpringRunner.class)解决了问题&#xff01;&#xff01; 真的是✓8了&#xff0c;烦死了这个✓8报错&#xff01;

C语言 | Leetcode C语言题解之第200题岛屿数量

题目&#xff1a; 题解&#xff1a; void cleanLand(char** grid, int gridSize, int ColSize,int row,int column) {if(grid[row][column] 1){//不等于1则清零grid[row][column] 0;}else{//不等于1则返回return ;}int newRow;int newColumn;//上if(row ! 0) //还能上{ne…

每天五分钟计算机视觉:人体姿势识别

本文重点 人体姿势识别是计算机视觉领域的一个重要研究方向,旨在通过图像或视频数据自动检测并识别出人体的各种姿势和动作。随着深度学习技术的快速发展,基于神经网络的方法在这一领域取得了显著进展。神经网络,特别是卷积神经网络(CNN)和循环神经网络(RNN),因其强大…

基于YOLOv9的PCB板缺陷检测

数据集 PCB缺陷检测&#xff0c;我们直接采用北京大学智能机器人开放实验室数据提供的数据集&#xff0c; 共六类缺陷 漏孔、鼠咬、开路、短路、杂散、杂铜 已经对数据进行了数据增强处理&#xff0c;同时按照YOLO格式配置好&#xff0c;数据内容如下 模型训练 ​ 采用YOLO…

idea乱码问题解决

乱码问题产生的根本原因 数据的编码和解码使用的不是同一个字符集 使用了不支持某个语言文字的字符集 Tomcat控制台乱码 在tomcat10.1.7这个版本中,修改 tomcat/conf/logging.properties中,所有的UTF-8为GBK即可 sout乱码问题,设置JVM加载.class文件时使用UTF-8字符集 设置虚…

RedisAtomicInteger并发案例

&#x1f370; 个人主页:__Aurora__ &#x1f35e;文章有不合理的地方请各位大佬指正。 &#x1f349;文章不定期持续更新&#xff0c;如果我的文章对你有帮助➡️ 关注&#x1f64f;&#x1f3fb; 点赞&#x1f44d; 收藏⭐️ RedisAtomicInteger 提供了对整数的原子性操作&a…