目录
本章记录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方向的高斯核标准差。0sigmaY
: 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 | 结构元素的形状,可以是以下值之一:
|
ksize | 结构元素的大小,类型为 例如: 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
函数创建的,例如:
创建一个5x5的矩形结构元素。cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5))
常用上面几个就够了,下面几个可以了解一下:
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 | 颜色转换代码(常用 例如 |
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_L1
和cv::CHAIN_APPROX_TC89_KCOS
:使用Teh-Chin链逼近算法。
示例: 在二值图像中查找轮廓,并将结果存储在 contours
和 hierarchy
中。
//二值化图像
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 | 包含所有轮廓的向量,每个轮廓是一个点的向量。 使用 |
contourIdx | 轮廓的索引。 例如: -1表示绘制所有轮廓;0表示绘制第一个轮廓。 |
color | 轮廓的颜色,用 例如: |
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 | 如果是二值图像,设为 这个参数在处理二值图像时有助于提高计算效率。 |
示例:从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支持多种字体类型。 例如 |
fontScale | 字体缩放系数,控制文本的大小。(设为1就行) |
color | 文本颜色,使用 例如 |
thickness | (可选参数)文本的线条粗细(可选),默认为1。 |
lineType | (可选参数)线条类型,默认为8,表示8连接线。 其他选项包括4和 |
bottomLeftOrigin | (可选参数)布尔值。 如果为 否则:否则是左上角。默认为 |
示例:在图像的质心位置绘制文本"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;
}
结果如图: