笔记(五)
c++的#include 和using namespace

#include 详解:代码的 “复制粘贴” 指令
- 核心作用
include 是 C++ 预处理指令,作用是将指定文件的全部内容原封不动地插入到当前代码文件中,解决 “代码复用” 和 “跨文件调用” 问题。
简单类比:你写代码时需要用标准库的 cout,但 cout 的定义不在你当前文件里,#include
2. 语法与分类
// 方式1:包含标准库头文件(<> 表示从系统库路径查找)
#include <iostream> // 标准输入输出库
#include <vector> // 容器库
// 方式2:包含自定义头文件("" 表示先从当前项目路径查找,再找系统路径)
#include "myfunc.h" // 自己写的头文件
#include "datatype.hpp"
- 新手必懂的关键规则
头文件保护:自定义头文件必须加 “防重复包含” 代码,否则多次 #include 会导致重复定义错误:
// myfunc.h 中必须加的保护代码(两种方式二选一)
// 方式1:预处理宏(兼容所有编译器)
#ifndef MYFUNC_H // 如果未定义该宏
#define MYFUNC_H // 定义宏
// 头文件内容(函数声明、结构体定义等)
int Add(int a, int b);
#endif // 结束保护
// 方式2:编译器指令(更简洁,C++11 支持)
#pragma once
int Add(int a, int b);
不要滥用 #include:只包含需要的头文件(比如用 vector 只加
using namespace 详解:命名空间的 “简写工具”
- 命名空间的背景
C++ 引入 “命名空间(namespace)” 是为了解决命名冲突问题。比如两个库都有 Print() 函数,通过命名空间 A::Print() 和 B::Print() 就能区分。
标准库的所有内容都放在 std 命名空间里(比如 std::cout、std::vector),如果不用 using namespace,就必须写完整前缀:
#include <iostream>
// 没有 using namespace std,必须写 std::cout
int main() {
std::cout << "Hello World" << std::endl;
return 0;
}
- using namespace 的作用
using namespace std; 表示 “将 std 命名空间中的所有名称引入当前作用域”,之后可以直接写 cout 而非 std::cout:
#include <iostream>
using namespace std; // 引入std命名空间
int main() {
cout << "Hello World" << endl; // 简化写法
return 0;
}
- 新手易踩坑的使用规范
❌ 不推荐的写法(全局引入)
在头文件中写 using namespace std;,或在代码开头全局引入:
// 头文件 myfunc.h(错误示例)
#include <iostream>
using namespace std; // 坑:所有包含该头文件的文件都会引入std,易引发命名冲突
✅ 推荐的写法(局部 / 精准引入)
方式 1:在函数内引入(仅作用于该函数)
#include <iostream>
int main() {
using namespace std; // 仅main函数内有效
cout << "Hello" << endl;
return 0;
}
方式 2:只引入需要的名称(最安全)
#include <iostream>
// 只引入 cout 和 endl,不引入std的其他内容
using std::cout;
using std::endl;
int main() {
cout << "Hello" << endl;
return 0;
}
完整示例:正确搭配使用
// 1. 包含必要的头文件(只包含需要的)
#include <iostream> // 用cout需要
#include <vector> // 用vector需要
#include "myfunc.h" // 用自定义Add函数需要
// 2. 精准引入命名空间(避免全局引入)
using std::cout;
using std::vector;
int main() {
// 使用标准库(无需std::前缀)
vector<int> nums = {1, 2, 3};
cout << "第一个数:" << nums[0] << endl;
// 使用自定义函数
int sum = Add(10, 20);
cout << "求和结果:" << sum << endl;
return 0;
}
总结
- include:是预处理阶段的 “复制粘贴”,必须写(用到外部代码时),自定义头文件要加防重复包含保护;
- using namespace:是编译阶段的 “简写工具”,非必须,禁止在头文件中全局引入,推荐函数内引入或精准引入单个名称;
- 核心原则:#include 按需包含,using namespace 按需引入,减少冗余和命名冲突。
& 的核心含义:引用(Reference)
在 C++ 中,& 放在变量类型后、变量名前时,代表引用(可以理解为 “变量的别名”),核心特点:
- 引用是对已有变量的 “绑定”,而非新变量(不占用额外内存);
- 引用必须在定义时初始化,且一旦绑定不能修改绑定对象;
- 对引用的操作,等价于对原变量的操作。
__declspec(dllexport) cv::Mat ImageU8C1ToMat(const ImageU8C1& img)

threshold(src,src,0,255,THRESH_BINARY_INV| THRESH_OTSU);


Otsu 算法(THRESH_OTSU)核心
Otsu(大津法)是一种自动计算最优阈值的算法,核心原理是:
- 分析图像的像素灰度直方图,找到一个阈值,使得 “前景像素” 和 “背景像素” 的类间方差最大;
- 这个阈值是区分前景 / 背景的最优值,无需手动调试(新手不用理解算法细节,会用即可)。
✅ 关键:只有当图像是单通道灰度图,且二值化类型包含 THRESH_OTSU 时,第 3 个参数(手动阈值)才会被忽略,算法会自动计算阈值。
完整执行流程(以 “黑色元件 + 白色背景” 为例)
Otsu 算法分析 src 的灰度直方图,自动算出最优阈值(比如 127);
对每个像素执行反向二值化:
像素值 ≥ 127(背景,白色)→ 设为 0(黑色);
像素值 < 127(元件,黑色)→ 设为 255(白色);
最终结果:元件变为白色,背景变为黑色(突出前景)。
Otsu 算法的适用场景:
仅适用于前景和背景对比明显的图像(直方图有两个明显峰值);如果图像光照不均,Otsu 效果差,需改用自适应阈值(adaptiveThreshold)。
开运算
Mat element = getStructuringElement(MORPH_ELLIPSE,Size(5,5));
morphologyEx(src, src, MORPH_OPEN, element);//开运算
先创建一个 5×5 的椭圆形结构元素,对二值化后的图像执行开运算(先腐蚀后膨胀),去除图像中的小噪声点、孤立的细小亮斑,同时保留元件 / 目标的整体轮廓,最后将处理后的图像保存为 BMP 格式文件。
getStructuringElement


开运算核心原理(新手易理解)
- 腐蚀(第一步):用结构元素扫描图像,只有当模板内所有像素都是白色(255)时,中心像素才保留白色,否则变为黑色(0)→ 作用:消除小的亮噪声、收缩目标边缘;
- 膨胀(第二步):用结构元素扫描图像,只要模板内有一个像素是白色(255),中心像素就设为白色 → 作用:将腐蚀后收缩的目标轮廓恢复到原有大小;
✅ 开运算的最终效果:去除小噪声、平滑目标边缘,不改变目标整体大小和形状(专门解决二值化后图像有细小白点噪声的问题)。

开运算的典型使用场景(为什么要做这一步)
二值化后的图像往往会有 “噪声白点”(比如元件背景中的细小亮斑),这些噪声会干扰后续的轮廓检测、目标识别,开运算就是为了解决这个问题:
- 工业检测:去除元件表面的细小反光点、背景杂点;
- 文字识别:消除文字周围的毛刺、孤立白点;
- 轮廓提取:让目标轮廓更平滑,避免噪声形成虚假小轮廓。
开运算的关键特性(新手必记)
- 只删 “小的亮区域”,不影响大目标
开运算只会去除比 “结构元素尺寸小” 的白色噪声点(比如 5×5 结构元素能删 ≤4×4 的白点),大于结构元素的目标(如元件)只会短暂收缩再恢复,整体形状不变。 - 平滑目标边缘,不改变整体位置
能消除目标边缘的细小毛刺、凸起,让轮廓更平滑,且目标的中心位置、整体尺寸基本不变。 - 不可逆性
被腐蚀掉的小噪声点,膨胀步骤无法恢复,因此噪声被永久去除(这是开运算的核心价值)。
开运算的典型使用场景
- 工业检测(最常用)
元件 / 工件二值化后,背景有细小的反光点、杂点,开运算能精准去除这些噪声,不影响元件轮廓(比如你之前的代码场景)。 - 文字识别
扫描的文字图像有墨水毛刺、纸张杂点,开运算能平滑文字边缘,消除孤立白点,提升识别准确率。 - 医学图像
细胞、病灶图像中,去除小的伪影、噪声点,保留病灶的完整轮廓。


浙公网安备 33010602011771号